summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xChangelog.txt16
-rwxr-xr-xFreeFileSync/Build/Help/FreeFileSync.hhc12
-rwxr-xr-xFreeFileSync/Build/Help/FreeFileSync.hhp5
-rwxr-xr-xFreeFileSync/Build/Help/html/command-line.html2
-rwxr-xr-xFreeFileSync/Build/Help/html/comparison-settings.html7
-rwxr-xr-xFreeFileSync/Build/Help/html/daylight-saving-time.html2
-rwxr-xr-xFreeFileSync/Build/Help/html/exclude-items.html2
-rwxr-xr-xFreeFileSync/Build/Help/html/ftp-setup.html79
-rwxr-xr-xFreeFileSync/Build/Help/html/performance.html72
-rwxr-xr-xFreeFileSync/Build/Help/html/realtimesync.html24
-rwxr-xr-xFreeFileSync/Build/Help/html/run-as-service.html2
-rwxr-xr-xFreeFileSync/Build/Help/html/schedule-batch-jobs.html (renamed from FreeFileSync/Build/Help/html/schedule-a-batch-job.html)4
-rwxr-xr-xFreeFileSync/Build/Help/html/synchronize-with-sftp.html75
-rwxr-xr-xFreeFileSync/Build/Help/html/volume-shadow-copy.html4
-rwxr-xr-xFreeFileSync/Build/Help/images/comparison-settings.pngbin13744 -> 23375 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/comparison-variant-double-click.pngbin4051 -> 5818 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/filter.pngbin18843 -> 25733 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/gnome-scheduler.pngbin46979 -> 49329 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/ignore-time-shift.pngbin11712 -> 16368 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/performance.pngbin0 -> 5457 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/realtimesync-schedule.pngbin8708 -> 12688 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/sftp-login.pngbin8127 -> 11307 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/sftp-performance.pngbin4676 -> 7369 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/synchronization-settings.pngbin28610 -> 36070 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/synchronization-variant-double-click.pngbin4033 -> 6422 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/windows-scheduler.pngbin9166 -> 13628 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/xp-scheduler.pngbin18530 -> 20957 bytes
-rwxr-xr-xFreeFileSync/Build/Languages/arabic.lng2059
-rwxr-xr-xFreeFileSync/Build/Languages/bulgarian.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/chinese_simple.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/chinese_traditional.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/croatian.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/czech.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/danish.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/dutch.lng102
-rwxr-xr-xFreeFileSync/Build/Languages/english_uk.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/finnish.lng1999
-rwxr-xr-xFreeFileSync/Build/Languages/french.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/german.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/greek.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/hebrew.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/hindi.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/hungarian.lng102
-rwxr-xr-xFreeFileSync/Build/Languages/italian.lng102
-rwxr-xr-xFreeFileSync/Build/Languages/japanese.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/korean.lng110
-rwxr-xr-xFreeFileSync/Build/Languages/lithuanian.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/norwegian.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/polish.lng197
-rwxr-xr-xFreeFileSync/Build/Languages/portuguese.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/portuguese_br.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/romanian.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/russian.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/slovak.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/slovenian.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/spanish.lng110
-rwxr-xr-xFreeFileSync/Build/Languages/swedish.lng100
-rwxr-xr-xFreeFileSync/Build/Languages/turkish.lng102
-rwxr-xr-xFreeFileSync/Build/Languages/ukrainian.lng100
-rwxr-xr-xFreeFileSync/Build/Resources.zipbin318587 -> 319981 bytes
-rwxr-xr-xFreeFileSync/Source/Makefile145
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/Makefile73
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/main_dlg.cpp10
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/xml_proc.cpp165
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/xml_proc.h6
-rwxr-xr-xFreeFileSync/Source/algorithm.cpp101
-rwxr-xr-xFreeFileSync/Source/algorithm.h2
-rwxr-xr-xFreeFileSync/Source/application.cpp22
-rwxr-xr-xFreeFileSync/Source/comparison.cpp92
-rwxr-xr-xFreeFileSync/Source/comparison.h3
-rwxr-xr-xFreeFileSync/Source/file_hierarchy.cpp2
-rwxr-xr-xFreeFileSync/Source/file_hierarchy.h5
-rwxr-xr-xFreeFileSync/Source/fs/abstract.cpp92
-rwxr-xr-xFreeFileSync/Source/fs/abstract.h210
-rwxr-xr-xFreeFileSync/Source/fs/native.cpp365
-rwxr-xr-xFreeFileSync/Source/lib/dir_exist_async.h60
-rwxr-xr-xFreeFileSync/Source/lib/dir_lock.cpp9
-rwxr-xr-xFreeFileSync/Source/lib/generate_logfile.cpp246
-rwxr-xr-xFreeFileSync/Source/lib/generate_logfile.h174
-rwxr-xr-xFreeFileSync/Source/lib/hard_filter.cpp6
-rwxr-xr-xFreeFileSync/Source/lib/hard_filter.h2
-rwxr-xr-xFreeFileSync/Source/lib/icon_buffer.cpp12
-rwxr-xr-xFreeFileSync/Source/lib/parallel_scan.cpp288
-rwxr-xr-xFreeFileSync/Source/lib/parallel_scan.h13
-rwxr-xr-xFreeFileSync/Source/lib/parse_lng.h6
-rwxr-xr-xFreeFileSync/Source/lib/process_xml.cpp110
-rwxr-xr-xFreeFileSync/Source/lib/status_handler.h14
-rwxr-xr-xFreeFileSync/Source/lib/status_handler_impl.h28
-rwxr-xr-xFreeFileSync/Source/lib/versioning.cpp99
-rwxr-xr-xFreeFileSync/Source/lib/versioning.h20
-rwxr-xr-xFreeFileSync/Source/process_callback.h23
-rwxr-xr-xFreeFileSync/Source/structures.cpp15
-rwxr-xr-xFreeFileSync/Source/structures.h18
-rwxr-xr-xFreeFileSync/Source/synchronization.cpp1504
-rwxr-xr-xFreeFileSync/Source/synchronization.h18
-rwxr-xr-xFreeFileSync/Source/ui/batch_config.cpp5
-rwxr-xr-xFreeFileSync/Source/ui/batch_status_handler.cpp82
-rwxr-xr-xFreeFileSync/Source/ui/batch_status_handler.h4
-rwxr-xr-xFreeFileSync/Source/ui/folder_selector.cpp13
-rwxr-xr-xFreeFileSync/Source/ui/folder_selector.h14
-rwxr-xr-xFreeFileSync/Source/ui/gui_generated.cpp569
-rwxr-xr-xFreeFileSync/Source/ui/gui_generated.h94
-rwxr-xr-xFreeFileSync/Source/ui/gui_status_handler.cpp16
-rwxr-xr-xFreeFileSync/Source/ui/gui_status_handler.h6
-rwxr-xr-xFreeFileSync/Source/ui/main_dlg.cpp356
-rwxr-xr-xFreeFileSync/Source/ui/main_dlg.h5
-rwxr-xr-xFreeFileSync/Source/ui/small_dlgs.cpp7
-rwxr-xr-xFreeFileSync/Source/ui/sync_cfg.cpp210
-rwxr-xr-xFreeFileSync/Source/ui/sync_cfg.h4
-rwxr-xr-xFreeFileSync/Source/ui/tree_grid.cpp6
-rwxr-xr-xFreeFileSync/Source/ui/version_check.cpp4
-rwxr-xr-xFreeFileSync/Source/version/version.h2
-rwxr-xr-xwx+/image_resources.cpp29
-rwxr-xr-xxBRZ/src/xbrz.cpp1262
-rwxr-xr-xxBRZ/src/xbrz.h80
-rwxr-xr-xxBRZ/src/xbrz_config.h35
-rwxr-xr-xxBRZ/src/xbrz_tools.h268
-rwxr-xr-xzen/deprecate.h4
-rwxr-xr-xzen/file_access.cpp69
-rwxr-xr-xzen/file_traverser.cpp25
-rwxr-xr-xzen/guid.h4
-rwxr-xr-xzen/scope_guard.h2
-rwxr-xr-xzen/stl_tools.h44
-rwxr-xr-xzen/thread.h72
-rwxr-xr-xzen/type_traits.h6
-rwxr-xr-xzen/warn_static.h2
-rwxr-xr-xzen/xml_io.cpp2
-rwxr-xr-xzen/xml_io.h2
-rwxr-xr-xzen/zstring.cpp6
-rwxr-xr-xzen/zstring.h4
130 files changed, 7336 insertions, 7424 deletions
diff --git a/Changelog.txt b/Changelog.txt
index 47476388..44d49196 100755
--- a/Changelog.txt
+++ b/Changelog.txt
@@ -1,3 +1,19 @@
+FreeFileSync 10.0
+-----------------
+The installer is now ad-free!
+Sync multiple files in parallel (Donation Edition)
+Compare multiple files in parallel within a single folder tree
+Aggregate worker threads per device during folder traversal
+Reset GUI layout configuration for high DPI displays
+Keep GUI responsive during synchronization
+Remember maximum number of visible folder pairs
+Fixed high DPI issues in installer
+Don't delay errors by callback interval during comparison
+Handle concurrent intermediate folder creation for versioning
+Sync all folder level items before recursion (avoid CWDs)
+Updated translation files
+
+
FreeFileSync 9.9 [2018-03-09]
-----------------------------
High DPI display support
diff --git a/FreeFileSync/Build/Help/FreeFileSync.hhc b/FreeFileSync/Build/Help/FreeFileSync.hhc
index 1b2ba311..637167ca 100755
--- a/FreeFileSync/Build/Help/FreeFileSync.hhc
+++ b/FreeFileSync/Build/Help/FreeFileSync.hhc
@@ -48,16 +48,20 @@
<param name="Local" value="html\macros.html">
</OBJECT>
<LI> <OBJECT type="text/sitemap">
- <param name="Name" value="Schedule a Batch Job">
- <param name="Local" value="html\schedule-a-batch-job.html">
+ <param name="Name" value="Performance">
+ <param name="Local" value="html\performance.html">
+ </OBJECT>
+ <LI> <OBJECT type="text/sitemap">
+ <param name="Name" value="Schedule Batch Jobs">
+ <param name="Local" value="html\schedule-batch-jobs.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">
+ <param name="Name" value="(S)FTP Setup">
+ <param name="Local" value="html\ftp-setup.html">
</OBJECT>
<LI> <OBJECT type="text/sitemap">
<param name="Name" value="Tips and Tricks">
diff --git a/FreeFileSync/Build/Help/FreeFileSync.hhp b/FreeFileSync/Build/Help/FreeFileSync.hhp
index 92006f89..45b4497c 100755
--- a/FreeFileSync/Build/Help/FreeFileSync.hhp
+++ b/FreeFileSync/Build/Help/FreeFileSync.hhp
@@ -18,9 +18,10 @@ html\expert-settings.html
html\external-applications.html
html\freefilesync.html
html\macros.html
-html\schedule-a-batch-job.html
+html\performance.html
+html\schedule-batch-jobs.html
html\synchronization-settings.html
-html\synchronize-with-sftp.html
+html\ftp-setup.html
html\tips-and-tricks.html
html\variable-drive-letters.html
html\versioning.html
diff --git a/FreeFileSync/Build/Help/html/command-line.html b/FreeFileSync/Build/Help/html/command-line.html
index 37ebf902..78a9fc75 100755
--- a/FreeFileSync/Build/Help/html/command-line.html
+++ b/FreeFileSync/Build/Help/html/command-line.html
@@ -20,7 +20,7 @@
<br>
<img src="../images/command-line-syntax.png" alt="Command line syntax">
- <br>
+ <br><br>
<h2>1. Run a FreeFileSync batch job</h2>
<p>
diff --git a/FreeFileSync/Build/Help/html/comparison-settings.html b/FreeFileSync/Build/Help/html/comparison-settings.html
index eb885f4b..d83877c8 100755
--- a/FreeFileSync/Build/Help/html/comparison-settings.html
+++ b/FreeFileSync/Build/Help/html/comparison-settings.html
@@ -108,10 +108,9 @@
the target of each link is copied during synchronization.<br>&nbsp;
<li><b>Direct:</b>
- Evaluate the symbolic link object
- directly. Symbolic links will be shown as separate entities.
- Links pointing to directories are not traversed and the link object
- is copied directly during synchronization.
+ Evaluate the symbolic link object directly. Symbolic links will be shown as separate entities.
+ Links pointing to directories are not traversed and the link object itself
+ is copied during synchronization.
</ol>
<br>
diff --git a/FreeFileSync/Build/Help/html/daylight-saving-time.html b/FreeFileSync/Build/Help/html/daylight-saving-time.html
index c627b45f..1b68d93e 100755
--- a/FreeFileSync/Build/Help/html/daylight-saving-time.html
+++ b/FreeFileSync/Build/Help/html/daylight-saving-time.html
@@ -59,7 +59,7 @@
</div>
<br>
- <li>Alternatively, you can avoid the problem in first place by only synchronizing from FAT to FAT or NTFS to NTFS file systems.
+ <li>Alternatively, you can avoid the problem in the first place by only synchronizing from FAT to FAT or NTFS to NTFS file systems.
Since most local disks are formatted with NTFS and USB memory sticks with FAT, this situation could be handled by formatting the USB stick with NTFS as well.
</ol>
</body>
diff --git a/FreeFileSync/Build/Help/html/exclude-items.html b/FreeFileSync/Build/Help/html/exclude-items.html
index 7e12a70c..0563f5ef 100755
--- a/FreeFileSync/Build/Help/html/exclude-items.html
+++ b/FreeFileSync/Build/Help/html/exclude-items.html
@@ -91,7 +91,7 @@
<b>Note</b>
<ul style="margin: 0">
<li>For simple exclusions, just right-click and exclude one item or a list
- of items directly via the context menu on main dialog.
+ of items directly via the <b>context menu</b> on main dialog.
<li>A filter phrase is compared against
<b>both</b> file and directory paths. If you want to consider directories
diff --git a/FreeFileSync/Build/Help/html/ftp-setup.html b/FreeFileSync/Build/Help/html/ftp-setup.html
new file mode 100755
index 00000000..e75c54d3
--- /dev/null
+++ b/FreeFileSync/Build/Help/html/ftp-setup.html
@@ -0,0 +1,79 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <link rel="stylesheet" type="text/css" href="base.css" >
+ <title>SFTP and FTP Setup</title>
+</head>
+
+<body>
+ <h1>SFTP and FTP Setup <span style="font-weight: normal">(Windows, macOS)</span></h1>
+
+ <p>
+ FreeFileSync supports synchronization with SFTP and FTP natively. Just enter your login information into the dialog shown for cloud folder selection:
+ <img src="../images/sftp-cloud-picker.png" alt="Cloud folder button"><br>
+ <br>
+ <img src="../images/sftp-login.png" alt="Enter SFTP login data">
+ </p>
+
+ <div class="bluebox">
+ <b>Note</b><br>In case the (S)FTP server sets file <b>modification times</b> to the <b>current time</b>
+ you can do a <a href="comparison-settings.html">Compare by File Size</a> as a workaround.
+ Another solution is to set up the <i>Two way</i> variant and have the files with the newer dates
+ be copied back from the server during the next synchronization.
+ </div>
+ <br>
+
+ <h2>Configure SFTP for best performance</h2>
+ <p>
+ By default, FreeFileSync creates one connection to the server and uses one SFTP channel, i.e. only a single SFTP command can be sent and received at a time.
+ Since most of this time is spent waiting due to the high latency of the remote connection, you can speed up reading large folder hierarchies
+ by increasing both the connection and channel count.<br>
+ <br>
+ <em>The folder reading time is reduced by a factor of <b>N</b> x <b>M</b> when using N connections with M channels each.</em>
+ </p>
+
+ <b>Example</b>: 10 connections using 2 channels each can yield a <b>20</b> times faster folder reading.<br>
+
+ <div class="half-line">&nbsp;</div>
+ <img src="../images/sftp-performance.png" class="screen-snippet" alt="Set up SFTP for best performance">
+ <div class="half-line">&nbsp;</div>
+
+ <ul style="margin: 0">
+ <li>The creation of additional connections and channels takes time. If you are only scanning a small remote folder,
+ setting up too many connections and channels might actually slow the overall process down.
+ Creating extra connections is slower than creating extra channels.<br>&nbsp;
+
+ <li>SFTP servers have internal limits on the number of allowed connections and channels.
+ Generally, servers expect one connection per user, so this number should be kept rather low.
+ If too many connections and channels are used, the server may decide to stop responding.<br>&nbsp;
+
+ <li>Unlike connections, additional SFTP channels are (currently) only used during folder reading (comparison), but not during synchronization.
+ </ul>
+ <br>
+ <div class="bluebox">
+ <b>Advice</b><br>Start with low numbers and make tests with different combinations of connections and channels for your
+ particular SFTP synchronization scenario to see what gives the highest speed.
+ Note, however, that FreeFileSync <b>reuses existing</b> SFTP connections/channels.
+ Therefore, you should <b>restart</b> FreeFileSync before measuring SFTP speed.
+ </div>
+ <br>
+
+ <h1>SFTP Setup <span style="font-weight: normal">(Linux)</span></h1>
+
+ <p>An SFTP share can be mapped to a local folder for use with FreeFileSync:</p>
+
+ <div class="greybox">
+ <ul style="margin: 0">
+ <li>Install:
+ <div class="command-line">sudo apt-get install sshfs</div><br>
+
+ <li>Mount SFTP share:
+ <div class="command-line">sshfs ssh-account@ssh-server:&lt;path&gt; mountpoint</div><br>
+
+ <li>Unmount:<br>
+ <div class="command-line">fusermount -u mountpoint</div>
+ </ul>
+ </div>
+</body>
+</html>
diff --git a/FreeFileSync/Build/Help/html/performance.html b/FreeFileSync/Build/Help/html/performance.html
new file mode 100755
index 00000000..37098ed3
--- /dev/null
+++ b/FreeFileSync/Build/Help/html/performance.html
@@ -0,0 +1,72 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <link rel="stylesheet" type="text/css" href="base.css" >
+ <title>Performance Improvements</title>
+</head>
+
+<body>
+ <h1>Performance Improvements</h1>
+
+ <p>
+ <img src="../images/performance.png" class="screen-snippet" alt="Performance settings" style="float:right; margin-left: 10px;">
+ FreeFileSync can be set up to issue multiple file accesses
+ in parallel. This speeds up synchronization times dramatically in
+ cases where single I/O operations have significant latency
+ (e.g. long response times on a slow network connection)
+ or they cannot use the full bandwidth available
+ (e.g. a SFTP server that has a speed limit for each connection).<br>
+ <br>
+ The number of parallel file operations that FreeFileSync should use
+ can be set up for each device individually
+ in the <b>Comparison Settings</b> dialog.
+ It is considered for all folder pairs of a configuration as follows:
+ </p>
+ <ul>
+ <li><b>During comparison</b> FreeFileSync first groups all folders by their root devices.<br>
+ <div class="half-line">&nbsp;</div>
+ For example, consider a configuration with two folder pairs and parallel file operations set up:
+ <div>
+ <table style="border-spacing:0; display: inline-block; vertical-align: middle;">
+ <tr><td><span class="file-path">C:\Source </span></td> <td>&harr;</td> <td><span class="file-path">D:\Target</span></td></tr>
+ <tr><td><span class="file-path">C:\Source2</span></td> <td>&harr;</td> <td><span class="file-path">E:\Target</span></td></tr>
+ </table>
+ <table style="border-spacing:0; text-align: center; display: inline-block; vertical-align: middle;">
+ <tr>
+ <td style="font-style: italic;">Parallel operations</td>
+ <td style="font-style: italic;">Device root</td>
+ </tr>
+ <tr><td>1</td> <td><span class="file-path">C:\</span></td></tr>
+ <tr><td>2</td> <td><span class="file-path">D:\</span></td></tr>
+ <tr><td>3</td> <td><span class="file-path">E:\</span></td></tr>
+ </table>
+ </div>
+ <div class="half-line">&nbsp;</div>
+ FreeFileSync will put the folders <span class="file-path">C:\Source</span> and
+ <span class="file-path">C:\Source2</span>
+ into the same group and allow only 1 file operation at a time.
+ Folder <span class="file-path">D:\Target</span> will be traversed using 2 operations,
+ and <span class="file-path">E:\Target</span> using 3 operations at a time.
+ In total FreeFileSync will be scanning all folders
+ employing 6 file operations in parallel.<br>
+ <br>
+
+ <li><b>When synchronizing</b> a folder pair FreeFileSync
+ will use the <b>maximum</b> of the number of parallel operations
+ that the two folders support.<br>
+ <br>
+ In the previous example the folder pair
+ <span class="file-path">C:\Source</span> &harr; <span class="file-path">D:\Target</span>
+ will be synchronized using 2 parallel operations, and
+ <span class="file-path">C:\Source2</span> &harr; <span class="file-path">E:\target</span>
+ will be using 3.
+ </ul>
+ <div class="bluebox">
+ <b>Note</b><br>
+ FreeFileSync implements parallel file operations by opening multiple connections to a device.
+ Some devices like SFTP servers have limits on how many connections they allow and will
+ fail if too many are attempted; see <a href="ftp-setup.html">(S)FTP Setup</a>.
+ </div>
+</body>
+</html>
diff --git a/FreeFileSync/Build/Help/html/realtimesync.html b/FreeFileSync/Build/Help/html/realtimesync.html
index 6053ba62..6abbdbb2 100755
--- a/FreeFileSync/Build/Help/html/realtimesync.html
+++ b/FreeFileSync/Build/Help/html/realtimesync.html
@@ -85,21 +85,19 @@
<br>
<h2>Example: <span style="font-weight:normal">Log names of changed files and directories (Windows)</span></h2>
- <p>
- <div class="greybox">
- Show which file or directory has triggered a change. Enter command line:<br>
- <div class="command-line">
- &nbsp;&nbsp;&nbsp;&nbsp;cmd /c echo %change_action% &quot;%change_path%&quot; &amp; pause
- </div>
- <br>
-
- Write a list of all changes to a log file:<br>
- <div class="command-line">
- &nbsp;&nbsp;&nbsp;&nbsp;cmd /c echo %change_action% &quot;%change_path%&quot; &gt;&gt; %csidl_Desktop%\log.txt
- </div>
+ <div class="greybox">
+ Show which file or directory has triggered a change. Enter command line:<br>
+ <div class="command-line">
+ &nbsp;&nbsp;&nbsp;&nbsp;cmd /c echo %change_action% &quot;%change_path%&quot; &amp; pause
</div>
- </p>
+ <br>
+ Write a list of all changes to a log file:<br>
+ <div class="command-line">
+ &nbsp;&nbsp;&nbsp;&nbsp;cmd /c echo %change_action% &quot;%change_path%&quot; &gt;&gt; %csidl_Desktop%\log.txt
+ </div>
+ </div>
+ <br>
<div class="bluebox">
<b>Note</b><br>
When RealTimeSync executes a Windows batch file (bat or cmd) a black console window is shown. You can hide it using the Visual Basic script
diff --git a/FreeFileSync/Build/Help/html/run-as-service.html b/FreeFileSync/Build/Help/html/run-as-service.html
index 27fc39ee..165ed84b 100755
--- a/FreeFileSync/Build/Help/html/run-as-service.html
+++ b/FreeFileSync/Build/Help/html/run-as-service.html
@@ -41,7 +41,7 @@
<li><p>
RealTimeSync should be monitoring while Windows is running, irrespective of currently logged in users:<br>
Create a new task in your operating systems's task scheduler and have it execute the command line above when the system starts.
- See <a href="schedule-a-batch-job.html">Schedule a Batch Job</a> for an example of how to add a task. Then change
+ See <a href="schedule-batch-jobs.html">Schedule Batch Jobs</a> for an example of how to add a task. Then change
the user which runs the task to <b>SYSTEM</b> - a special user account always running in the background.
</p>
<img src="../images/realtimesync-schedule.png" alt="Schedule RealTimeSync">
diff --git a/FreeFileSync/Build/Help/html/schedule-a-batch-job.html b/FreeFileSync/Build/Help/html/schedule-batch-jobs.html
index fa9a1e2d..c15dcf5a 100755
--- a/FreeFileSync/Build/Help/html/schedule-a-batch-job.html
+++ b/FreeFileSync/Build/Help/html/schedule-batch-jobs.html
@@ -3,11 +3,11 @@
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="base.css" >
- <title>Schedule a Batch Job</title>
+ <title>Schedule Batch Jobs</title>
</head>
<body>
- <h1>Schedule a Batch Job</h1>
+ <h1>Schedule Batch Jobs</h1>
<ol>
<li>Create a new batch job via FreeFileSync's main dialog: <b>Menu &rarr; File &rarr; Save as a batch job...</b><br>&nbsp;
diff --git a/FreeFileSync/Build/Help/html/synchronize-with-sftp.html b/FreeFileSync/Build/Help/html/synchronize-with-sftp.html
deleted file mode 100755
index f3d853bf..00000000
--- a/FreeFileSync/Build/Help/html/synchronize-with-sftp.html
+++ /dev/null
@@ -1,75 +0,0 @@
-<!doctype html>
-<html lang="en">
-<head>
- <meta charset="utf-8">
- <link rel="stylesheet" type="text/css" href="base.css" >
- <title>Synchronize Files with SFTP</title>
-</head>
-
-<body>
- <h1>Synchronize Files with SFTP <span style="font-weight: normal">(Windows, macOS)</span></h1>
-
- <p>
- FreeFileSync supports synchronization with SFTP natively. Just enter your SFTP login information into the dialog shown for cloud folder selection:
- <img src="../images/sftp-cloud-picker.png" alt="Cloud folder button"><br>
- <br>
- <img src="../images/sftp-login.png" alt="Enter SFTP login data">
- </p>
-
- <div class="bluebox">
- <b>Note</b><br>In case the SFTP server sets file modification times to the current time
- you can do a <a href="comparison-settings.html">Compare by File Size</a> as a workaround.
- Another solution is to set up the <i>Two way</i> variant and have the files with the newer dates
- be copied back from the server during the next synchronization.
- </div>
- <br>
-
- <h2>Set up SFTP for best performance</h2>
- <p>
- By default, FreeFileSync creates one connection to the server and uses one SFTP channel, i.e. only a single SFTP command can be sent and received at a time.
- Since most of this time is spent waiting due to the high latency of the remote connection, you can speed up reading large directory hierarchies
- by increasing both the connection and channel count.<br>
- <br>
- <em>The directory reading time is reduced by a factor of <b>N</b> x <b>M</b> when using N connections with M channels each.</em>
- <br><br>
- <b>Example</b>: 2 connections using 10 channels each can yield a <b>20</b> times faster directory reading.
- <br><br>
- <img src="../images/sftp-performance.png" class="screen-snippet" alt="Set up SFTP for best performance">
-
- <ul style="margin: 0">
- <li>The creation of additional connections and channels takes time. If you are only scanning a small remote directory,
- setting up too many connections and channels might actually slow the overall process down.
- Creating extra connections is slower than creating extra channels.<br>&nbsp;
-
- <li>SFTP servers have internal limits on the number of allowed connections and channels.
- Generally, servers expect one connection per user, so this number should be kept rather low.
- If too many connections and channels are used, the server may decide to stop responding.
- </ul>
- </p>
-
- <div class="bluebox">
- <b>Advice</b><br>Start with low numbers and make tests with different combinations of connections and channels for your
- particular SFTP synchronization scenario to see what gives the highest speed.
- Note, however, that FreeFileSync <b>reuses existing</b> SFTP connections/channels.
- Therefore, you should <b>restart</b> FreeFileSync before measuring SFTP speed.
- </div>
- <br>
-
- <h1>Synchronize with SFTP <span style="font-weight: normal">(Linux)</span></h1>
-
- <p>An SFTP share can be mapped to a local folder for use with FreeFileSync:</p>
-
- <div class="greybox">
- <ul style="margin: 0">
- <li>Install:
- <div class="command-line">sudo apt-get install sshfs</div><br>
-
- <li>Mount SFTP share:
- <div class="command-line">sshfs ssh-account@ssh-server:&lt;path&gt; mountpoint</div><br>
-
- <li>Unmount:<br>
- <div class="command-line">fusermount -u mountpoint</div>
- </ul>
- </div>
-</body>
-</html>
diff --git a/FreeFileSync/Build/Help/html/volume-shadow-copy.html b/FreeFileSync/Build/Help/html/volume-shadow-copy.html
index c9004c35..fa98238a 100755
--- a/FreeFileSync/Build/Help/html/volume-shadow-copy.html
+++ b/FreeFileSync/Build/Help/html/volume-shadow-copy.html
@@ -3,11 +3,11 @@
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="base.css" >
- <title>Shadow Copy Service</title>
+ <title>Volume Shadow Copy</title>
</head>
<body>
- <h1>Shadow Copy Service<span style="font-weight: normal"> (Windows only)</span></h1>
+ <h1>Volume Shadow Copy<span style="font-weight: normal"> (Windows only)</span></h1>
<p>
FreeFileSync supports copying locked or shared files by creating a Volume Shadow
diff --git a/FreeFileSync/Build/Help/images/comparison-settings.png b/FreeFileSync/Build/Help/images/comparison-settings.png
index 371b28ed..5ffa2f48 100755
--- a/FreeFileSync/Build/Help/images/comparison-settings.png
+++ b/FreeFileSync/Build/Help/images/comparison-settings.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/comparison-variant-double-click.png b/FreeFileSync/Build/Help/images/comparison-variant-double-click.png
index eade94b5..1d998755 100755
--- a/FreeFileSync/Build/Help/images/comparison-variant-double-click.png
+++ b/FreeFileSync/Build/Help/images/comparison-variant-double-click.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/filter.png b/FreeFileSync/Build/Help/images/filter.png
index 9e688264..a4d3a0f8 100755
--- a/FreeFileSync/Build/Help/images/filter.png
+++ b/FreeFileSync/Build/Help/images/filter.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/gnome-scheduler.png b/FreeFileSync/Build/Help/images/gnome-scheduler.png
index fee122bd..05d0f1f7 100755
--- a/FreeFileSync/Build/Help/images/gnome-scheduler.png
+++ b/FreeFileSync/Build/Help/images/gnome-scheduler.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/ignore-time-shift.png b/FreeFileSync/Build/Help/images/ignore-time-shift.png
index fe9361be..ceadf60a 100755
--- a/FreeFileSync/Build/Help/images/ignore-time-shift.png
+++ b/FreeFileSync/Build/Help/images/ignore-time-shift.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/performance.png b/FreeFileSync/Build/Help/images/performance.png
new file mode 100755
index 00000000..70bed081
--- /dev/null
+++ b/FreeFileSync/Build/Help/images/performance.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/realtimesync-schedule.png b/FreeFileSync/Build/Help/images/realtimesync-schedule.png
index cd67e71d..34ba5b88 100755
--- a/FreeFileSync/Build/Help/images/realtimesync-schedule.png
+++ b/FreeFileSync/Build/Help/images/realtimesync-schedule.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/sftp-login.png b/FreeFileSync/Build/Help/images/sftp-login.png
index b1d3f60b..d28b12a9 100755
--- a/FreeFileSync/Build/Help/images/sftp-login.png
+++ b/FreeFileSync/Build/Help/images/sftp-login.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/sftp-performance.png b/FreeFileSync/Build/Help/images/sftp-performance.png
index 4e2d1528..6125aa5e 100755
--- a/FreeFileSync/Build/Help/images/sftp-performance.png
+++ b/FreeFileSync/Build/Help/images/sftp-performance.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/synchronization-settings.png b/FreeFileSync/Build/Help/images/synchronization-settings.png
index c7bffa5f..4eab9306 100755
--- a/FreeFileSync/Build/Help/images/synchronization-settings.png
+++ b/FreeFileSync/Build/Help/images/synchronization-settings.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/synchronization-variant-double-click.png b/FreeFileSync/Build/Help/images/synchronization-variant-double-click.png
index 9e2597c7..cb6cd370 100755
--- a/FreeFileSync/Build/Help/images/synchronization-variant-double-click.png
+++ b/FreeFileSync/Build/Help/images/synchronization-variant-double-click.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/windows-scheduler.png b/FreeFileSync/Build/Help/images/windows-scheduler.png
index ff52b34a..bf214a8f 100755
--- a/FreeFileSync/Build/Help/images/windows-scheduler.png
+++ b/FreeFileSync/Build/Help/images/windows-scheduler.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/xp-scheduler.png b/FreeFileSync/Build/Help/images/xp-scheduler.png
index 6a643d78..cfb74050 100755
--- a/FreeFileSync/Build/Help/images/xp-scheduler.png
+++ b/FreeFileSync/Build/Help/images/xp-scheduler.png
Binary files differ
diff --git a/FreeFileSync/Build/Languages/arabic.lng b/FreeFileSync/Build/Languages/arabic.lng
deleted file mode 100755
index f77e5105..00000000
--- a/FreeFileSync/Build/Languages/arabic.lng
+++ /dev/null
@@ -1,2059 +0,0 @@
-<header>
- <language>العربية</language>
- <translator>Majed Alotaibi (ماجد العتيبي)</translator>
- <locale>ar</locale>
- <image>flag_arabic.png</image>
- <plural_count>6</plural_count>
- <plural_definition>n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5</plural_definition>
-</header>
-
-<source>Both sides have changed since last synchronization.</source>
-<target>كلا الجانبين قد تغير منذ المزامنة الأخيرة.</target>
-
-<source>Cannot determine sync-direction:</source>
-<target>لا يمكن تحديد اتجاه المزامنة:</target>
-
-<source>No change since last synchronization.</source>
-<target>لم يطرأ أي تغيير منذ المزامنة الأخيرة.</target>
-
-<source>The database entry is not in sync considering current settings.</source>
-<target>مدخلات قواعد البيانات غير متزامنة حسب إعدادات المزامنة الحالية.</target>
-
-<source>Setting default synchronization directions: Old files will be overwritten with newer files.</source>
-<target>تحديد الاتجاهات الاÙتراضية للمزامنة: ستتم الكتابة Ùوق الملÙات القديمة بالملÙات الأحدث.</target>
-
-<source>Creating file %x</source>
-<target>إنشاء المل٠%x</target>
-
-<source>Creating folder %x</source>
-<target>إنشاء مجلد %x</target>
-
-<source>Creating symbolic link %x</source>
-<target>إنشاء ارتباط رمزي %x</target>
-
-<source>Moving file %x to the recycle bin</source>
-<target>نقل المل٠%x إلى سلة المهملات</target>
-
-<source>Moving folder %x to the recycle bin</source>
-<target>نقل المجلد %x إلى سلة المهملات</target>
-
-<source>Moving symbolic link %x to the recycle bin</source>
-<target>نقل الارتباط الرمزي %x إلى سلة المهملات</target>
-
-<source>Deleting file %x</source>
-<target>حذ٠المل٠%x</target>
-
-<source>Deleting folder %x</source>
-<target>حذ٠المجلد %x</target>
-
-<source>Deleting symbolic link %x</source>
-<target>حذ٠الارتباط الرمزي %x</target>
-
-<source>Checking recycle bin availability for folder %x...</source>
-<target>التحقق من تواÙر سلة المحذوÙات من أجل المجلد %x...</target>
-
-<source>The recycle bin is not supported by the following folders. Deleted or overwritten files will not be able to be restored:</source>
-<target>سلة المهملات غير مدعومة بواسطة المجلدات التالية. لن تتمكن من استعادة الملÙات المحذوÙØ© أو التي يتم استبدالها:</target>
-
-<source>An exception occurred</source>
-<target>حدث استثناء</target>
-
-<source>A directory path is expected after %x.</source>
-<target>مسار متوقع بعد %x.</target>
-
-<source>Syntax error</source>
-<target>خطأ ÙÙŠ البنية</target>
-
-<source>A left and a right directory path are expected after %x.</source>
-<target></target>
-
-<source>Cannot find file %x.</source>
-<target>لا يمكن العثور على المجلد %x.</target>
-
-<source>Error</source>
-<target>خطأ</target>
-
-<source>File %x does not contain a valid configuration.</source>
-<target>لا يحتوي المل٠%x تكويناً صحيحاً.</target>
-
-<source>Unequal number of left and right directories specified.</source>
-<target>لم يتم تحديد عدد متساوي من المسارات على الطرÙين.</target>
-
-<source>The config file must not contain settings at directory pair level when directories are set via command line.</source>
-<target>يجب أن يحتوي مل٠الخيارات الخيارات على مستوى أزواج المسارات عند تحديد المسارات بواسطة سطر الأوامر.</target>
-
-<source>Directories cannot be set for more than one configuration file.</source>
-<target>لا يمكن اختيار المسارات لأكثر من مل٠خيارات واحد.</target>
-
-<source>Command line</source>
-<target>سطر الأوامر</target>
-
-<source>Syntax:</source>
-<target>بنية:</target>
-
-<source>config files:</source>
-<target>ملÙات الخيارات:</target>
-
-<source>directory</source>
-<target>مسار</target>
-
-<source>global config file:</source>
-<target>مل٠الخيارات العام:</target>
-
-<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source>
-<target>أي عدد من ملÙات تكوين FreeFileSync بامتداد .ffs_gui أو/Ùˆ .ffs_batch.</target>
-
-<source>Any number of alternative directory pairs for at most one config file.</source>
-<target>أي عدد من أزواج المسارات البديلة من أجل مل٠خيارات واحد.</target>
-
-<source>Open the selected configuration for editing only without executing it.</source>
-<target>Ùتح التكوين المحدد لتعديله Ùقط بدون تنÙيذه.</target>
-
-<source>Path to an alternate GlobalSettings.xml file.</source>
-<target>تحديد مسار مختل٠لمل٠GlobalSettings.xml.</target>
-
-<source>Cannot find the following folders:</source>
-<target>تعذر العثور على المجلدات التالية:</target>
-
-<source>If this error is ignored the folders will be considered empty. Missing folders are created automatically when needed.</source>
-<target>إذا تم تجاهل هذا الخطأ سيتم اعتبار المجلدات Ùارغة. يتم إنشاء المجلدات المÙقودة تلقائيا عند الحاجة.</target>
-
-<source>Comparison finished:</source>
-<target></target>
-
-<source>
-<pluralform>1 item found</pluralform>
-<pluralform>%x items found</pluralform>
-</source>
-<target>
-</target>
-
-<source>File %x has an invalid date.</source>
-<target>يحتوي المل٠%x تاريخ غير صالح.</target>
-
-<source>Date:</source>
-<target>التاريخ:</target>
-
-<source>Files have the same date but a different size.</source>
-<target>الملÙات لها Ù†Ùس التاريخ ولكن حجم مختلÙ.</target>
-
-<source>Size:</source>
-<target>الحجم:</target>
-
-<source>Content comparison was skipped for excluded files.</source>
-<target>تم تخطي مقارنة المحتوى لملÙات مستبعدة.</target>
-
-<source>Items differ in attributes only</source>
-<target>العناصر مختلÙØ© ÙÙŠ السمات Ùقط</target>
-
-<source>Resolving symbolic link %x</source>
-<target>جاري حل المسار الرمزي %x</target>
-
-<source>Comparing content of files %x</source>
-<target>مقارنة محتويات الملÙات %x</target>
-
-<source>Generating file list...</source>
-<target>إنشاء قائمة الملÙات...</target>
-
-<source>Fail-safe file copy</source>
-<target>نسخ ملÙات آمن من الÙشل</target>
-
-<source>Enabled</source>
-<target>ممكن</target>
-
-<source>Disabled</source>
-<target>معطل</target>
-
-<source>Copy locked files</source>
-<target>نسخ الملÙات المقÙلة</target>
-
-<source>Copy file access permissions</source>
-<target>نسخ أذونات الوصول إلى الملÙ</target>
-
-<source>File time tolerance</source>
-<target>التÙاوت ÙÙŠ وقت الملÙ</target>
-
-<source>Folder access timeout</source>
-<target>مهلة وصول المجلد</target>
-
-<source>Run with background priority</source>
-<target>تشغيل مع أولوية ÙÙŠ الخلÙية</target>
-
-<source>Lock directories during sync</source>
-<target>Ù‚ÙÙ„ المسارات أثناء المزامنة</target>
-
-<source>Verify copied files</source>
-<target>التحقق من الملÙات التي تم نسخها</target>
-
-<source>Using non-default global settings:</source>
-<target>استخدام إعدادات عامة غير اÙتراضية:</target>
-
-<source>A folder input field is empty.</source>
-<target>حقل إدخال خاص بمجلد Ùارغ.</target>
-
-<source>The corresponding folder will be considered as empty.</source>
-<target>سيتم اعتبار المجلد المواÙÙ‚ كمجلد Ùارغ.</target>
-
-<source>Exclude:</source>
-<target>استثناء:</target>
-
-<source>One base folder of a folder pair is contained in the other one.</source>
-<target>يوجد مجلد أساسي لزوج من المجلدات ÙÙŠ المجلد الآخر.</target>
-
-<source>The folder should be excluded from synchronization via filter.</source>
-<target>يجب استبعاد المجلد من المزامنة عبر المرشح.</target>
-
-<source>Calculating sync directions...</source>
-<target>جاري حساب اتجاهات المزامنة...</target>
-
-<source>Out of memory.</source>
-<target>Ù†Ùدت الذاكرة.</target>
-
-<source>Item exists on left side only</source>
-<target>العنصر موجود على الجانب الأيمن Ùقط</target>
-
-<source>Item exists on right side only</source>
-<target>العنصر موجود ÙÙŠ الجانب الأيسر Ùقط</target>
-
-<source>Left side is newer</source>
-<target>الجانب الأيمن أحدث</target>
-
-<source>Right side is newer</source>
-<target>الجانب الأيسر أحدث</target>
-
-<source>Items have different content</source>
-<target>العناصر مختلÙØ© بالمحتوى</target>
-
-<source>Both sides are equal</source>
-<target>كلا الجانبين متماثلان</target>
-
-<source>Conflict/item cannot be categorized</source>
-<target>الاختلاÙ\العنصر لا يمكن تصنيÙÙ‡</target>
-
-<source>Copy new item to left</source>
-<target>نسخ عنصر جديد إلى اليمين</target>
-
-<source>Copy new item to right</source>
-<target>نسخ عنصر جديد إلى اليسار</target>
-
-<source>Delete left item</source>
-<target>حذ٠العنصر الأيمن</target>
-
-<source>Delete right item</source>
-<target>حذ٠العنصر الأيسر</target>
-
-<source>Move file on left</source>
-<target>نقل مل٠على اليمين</target>
-
-<source>Move file on right</source>
-<target>نقل مل٠على اليسار</target>
-
-<source>Update left item</source>
-<target>تحديث العنصر اليميني</target>
-
-<source>Update right item</source>
-<target>تحديث العنصر اليساري</target>
-
-<source>Do nothing</source>
-<target>لا تÙعل شيئا</target>
-
-<source>Update attributes on left</source>
-<target>تحديث السمات على اليمين</target>
-
-<source>Update attributes on right</source>
-<target>تحديث السمات على اليسار</target>
-
-<source>Cannot read file %x.</source>
-<target>لا يمكن قراءة المل٠%x.</target>
-
-<source>
-Unexpected size of data stream.
-Expected: %x bytes
-Actual: %y bytes
-</source>
-<target>
-حجم غير متوقع لتدÙÙ‚ البيانات.
-المتوقع: ‎%x bytes
-الÙعلي: ‎%y bytes
-</target>
-
-<source>Cannot write permissions of %x.</source>
-<target>لا يمكن كتابة أذونات %x.</target>
-
-<source>Operation not supported for different base folder types.</source>
-<target>العملية غير مدعومة لأنواع المجلدات مختلÙØ© الأساس.</target>
-
-<source>Cannot write file %x.</source>
-<target>لا يمكن كتابة المل٠%x.</target>
-
-<source>Cannot move file %x to %y.</source>
-<target>لا يمكن نقل المل٠%x إلى %y.</target>
-
-<source>Cannot copy symbolic link %x to %y.</source>
-<target>لا يمكن نسخ الرابط الرمزي من %x إلى %y.</target>
-
-<source>Unable to connect to %x.</source>
-<target>لا يمكن الاتصال بـ %x.</target>
-
-<source>Failed to get information about server %x.</source>
-<target>Ùشل الحصول على معلومات حول الخادم %x.</target>
-
-<source>Cannot open directory %x.</source>
-<target>لا يمكن Ùتح المسار %x.</target>
-
-<source>Cannot read directory %x.</source>
-<target>لا يمكن قراءة الدليل %x.</target>
-
-<source>Cannot read file attributes of %x.</source>
-<target>لا يمكن قراءة سمات المل٠%x.</target>
-
-<source>Cannot create directory %x.</source>
-<target>لا يمكن إنشاء المسار %x.</target>
-
-<source>Cannot delete file %x.</source>
-<target>لا يمكن حذ٠المل٠%x.</target>
-
-<source>Cannot delete directory %x.</source>
-<target>لا يمكن حذ٠المسار %x.</target>
-
-<source>Cannot write modification time of %x.</source>
-<target>لا يمكن كتابة وقت تعديل %x.</target>
-
-<source>Cannot determine final path for %x.</source>
-<target>تعذر تحديد المسار النهائي لـ %x.</target>
-
-<source>Cannot resolve symbolic link %x.</source>
-<target>لا يمكن حل الارتباط الرمزي %x.</target>
-
-<source>Unable to move %x to the recycle bin.</source>
-<target>تعذر نقل %x إلى سلة المحذوÙات.</target>
-
-<source>Cannot find %x.</source>
-<target>لا يمكن العثور على %x.</target>
-
-<source>Cannot open file %x.</source>
-<target>تعذر Ùتح المل٠%x.</target>
-
-<source>Cannot find device %x.</source>
-<target>لا يمكن العثور على الجهاز %x.</target>
-
-<source>Type of item %x is not supported:</source>
-<target>نوع العنصر %x غير مدعوم:</target>
-
-<source>Cannot delete symbolic link %x.</source>
-<target>لا يمكن حذ٠الرابط الرمزي %x.</target>
-
-<source>Cannot determine free disk space for %x.</source>
-<target>لا يمكن تحديد مساحة القرص الحرة لـ %x.</target>
-
-<source>Incorrect command line:</source>
-<target>سطر أوامر خاطئ:</target>
-
-<source>Error Code %x</source>
-<target>رمز الخطأ %x</target>
-
-<source>The server does not support authentication via %x.</source>
-<target>لا يدعم الخادم المصادقة عبر %x.</target>
-
-<source>Required:</source>
-<target>مطلوب:</target>
-
-<source>Unable to access %x.</source>
-<target>لا يمكن الوصول إلى %x.</target>
-
-<source>
-<pluralform>Operation timed out after 1 second.</pluralform>
-<pluralform>Operation timed out after %x seconds.</pluralform>
-</source>
-<target>
-<pluralform>انقضت مهلة العملية بعد 0 ثانية.</pluralform>
-<pluralform>انقضت مهلة العملية بعد 1 ثانية.</pluralform>
-<pluralform>انقضت مهلة العملية بعد 2 ثانية.</pluralform>
-<pluralform>انقضت مهلة العملية بعد %x ثوان.</pluralform>
-<pluralform>انقضت مهلة العملية بعد %x ثانية.</pluralform>
-<pluralform>انقضت مهلة العملية بعد %x ثانية.</pluralform>
-</target>
-
-<source>
-<pluralform>Cannot wait on more than 1 connection at a time.</pluralform>
-<pluralform>Cannot wait on more than %x connections at a time.</pluralform>
-</source>
-<target>
-<pluralform>لا يمكن الانتظار على أكثر 0 اتصال ÙÙŠ وقت واحد.</pluralform>
-<pluralform>لا يمكن الانتظار على أكثر 1 اتصال ÙÙŠ وقت واحد.</pluralform>
-<pluralform>لا يمكن الانتظار على أكثر 2 اتصال ÙÙŠ وقت واحد.</pluralform>
-<pluralform>لا يمكن الانتظار على أكثر %x اتصالات ÙÙŠ وقت واحد.</pluralform>
-<pluralform>لا يمكن الانتظار على أكثر من %x اتصالات ÙÙŠ وقت واحد.</pluralform>
-<pluralform>لا يمكن الانتظار على أكثر من %x اتصالات ÙÙŠ وقت واحد.</pluralform>
-</target>
-
-<source>Active connections: %x</source>
-<target>الاتصال النشط: %x</target>
-
-<source>Failed to open SFTP channel number %x.</source>
-<target>Ùشل Ùتح قناة SFTP رقم %x.</target>
-
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>‎0 byte</pluralform>
-<pluralform>‎1 byte</pluralform>
-<pluralform>‎2 bytes</pluralform>
-<pluralform>‎%x bytes</pluralform>
-<pluralform>‎%x bytes</pluralform>
-<pluralform>‎%x bytes</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>‎%x MB</target>
-
-<source>%x KB</source>
-<target>‎%x KB</target>
-
-<source>%x GB</source>
-<target>‎%x GB</target>
-
-<source>Cannot load file %x.</source>
-<target>لا يمكن Ùتح المل٠%x.</target>
-
-<source>Database file %x is incompatible.</source>
-<target>مل٠قاعدة البيانات %x غير متواÙÙ‚.</target>
-
-<source>Initial synchronization:</source>
-<target>المزامنة الأولية:</target>
-
-<source>Database file %x does not yet exist.</source>
-<target>مل٠قاعدة البيانات %x غير موجود حتى الآن.</target>
-
-<source>Database file is corrupted:</source>
-<target>مل٠قاعدة البيانات تالÙ:</target>
-
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>لا تحتوي ملÙات قاعدة البيانات حتى الآن على معلومات حول المزامنة الأخيرة.</target>
-
-<source>Loading file %x...</source>
-<target>جار تحميل المل٠%x...</target>
-
-<source>Saving file %x...</source>
-<target>جاري Ø­Ùظ المل٠%x...</target>
-
-<source>Searching for folder %x...</source>
-<target>البحث عن المجلد %x...</target>
-
-<source>Timeout while searching for folder %x.</source>
-<target>انقضت المهلة أثناء البحث عن مجلد %x.</target>
-
-<source>Cannot get process information.</source>
-<target>لا يمكن الحصول على معلومات العملية.</target>
-
-<source>Waiting while directory is locked:</source>
-<target>انتظر بينما يتم إنشاء Ù‚ÙÙ„ للمسار:</target>
-
-<source>Lock owner:</source>
-<target>صاحب القÙÙ„:</target>
-
-<source>Detecting abandoned lock...</source>
-<target>اكتشا٠قÙÙ„ مهمل...</target>
-
-<source>
-<pluralform>1 sec</pluralform>
-<pluralform>%x sec</pluralform>
-</source>
-<target>
-<pluralform>0 ثانية</pluralform>
-<pluralform>1 ثانية واحدة</pluralform>
-<pluralform>2 ثانيتين</pluralform>
-<pluralform>%x ثواني</pluralform>
-<pluralform>%x ثانية</pluralform>
-<pluralform>%x ثانية</pluralform>
-</target>
-
-<source>Items processed:</source>
-<target>معالجة العناصر:</target>
-
-<source>Items remaining:</source>
-<target>العناصر المتبقية:</target>
-
-<source>Total time:</source>
-<target>مجموع الوقت:</target>
-
-<source>Error parsing file %x, row %y, column %z.</source>
-<target>حدث خطأ أثناء تحليل المل٠%x، الص٠%y، و العمود %z.</target>
-
-<source>Cannot set directory locks for the following folders:</source>
-<target></target>
-
-<source>
-<pluralform>1 thread</pluralform>
-<pluralform>%x threads</pluralform>
-</source>
-<target>
-<pluralform>0 بند</pluralform>
-<pluralform>1 بند واحد</pluralform>
-<pluralform>2 بندان</pluralform>
-<pluralform>%x بنود</pluralform>
-<pluralform>%x بنداً</pluralform>
-<pluralform>%x بند</pluralform>
-</target>
-
-<source>Scanning:</source>
-<target>الÙحص:</target>
-
-<source>/sec</source>
-<target>\ثانية</target>
-
-<source>%x items/sec</source>
-<target>%x عنصر\الثانية</target>
-
-<source>Show in Explorer</source>
-<target>إظهار ÙÙŠ المستكشÙ</target>
-
-<source>Open with default application</source>
-<target>Ùتح باستخدام التطبيق الاÙتراضي</target>
-
-<source>Browse directory</source>
-<target>تصÙØ­ المسار</target>
-
-<source>Cannot access the Volume Shadow Copy Service.</source>
-<target>لا يمكن الوصول إلى خدمة "نسخ الظل لوحدة التخزين".</target>
-
-<source>Please run the 64-bit version of FreeFileSync to create shadow copies on this system.</source>
-<target>الرجاء تشغيل نسخة 64-بت من FreeFileSync لإنشاء نسخ الظل على هذا النظام.</target>
-
-<source>Cannot determine volume name for %x.</source>
-<target>تعذر تحديد اسم الوسط %x.</target>
-
-<source>Volume name %x is not part of file path %y.</source>
-<target>اسم وحدة التخزين %x ليس جزءا٠من اسم المل٠%y.</target>
-
-<source>Unable to create time stamp for versioning:</source>
-<target>تعذر إنشاء بصمة زمنية من أجل المÙاضلة الزمنية:</target>
-
-<source>Drag && drop</source>
-<target>سحب Ùˆ Ø¥Ùلات</target>
-
-<source>Cannot find folder %x.</source>
-<target>تعذر العثور على المجلد %x.</target>
-
-<source>Select a folder</source>
-<target>تحديد مجلد</target>
-
-<source>&New</source>
-<target>&جديد</target>
-
-<source>&Open...</source>
-<target>&Ùتح...</target>
-
-<source>Save &as...</source>
-<target>&Ø­Ùظ باسم...</target>
-
-<source>E&xit</source>
-<target>&إغلاق</target>
-
-<source>&File</source>
-<target>&ملÙ</target>
-
-<source>&View help</source>
-<target>إ&ظهار المساعدة</target>
-
-<source>&About</source>
-<target>&حول</target>
-
-<source>&Help</source>
-<target>&تعليمات</target>
-
-<source>Usage:</source>
-<target>الاستخدام:</target>
-
-<source>1. Select folders to watch.</source>
-<target>1. حدد المجلدات للمتابعة.</target>
-
-<source>2. Enter a command line.</source>
-<target>2. إدخال سطر أوامر.</target>
-
-<source>3. Press 'Start'.</source>
-<target>3. اضغط على 'ابدأ'.</target>
-
-<source>To get started just import a .ffs_batch file.</source>
-<target>للبدء قم باستيراد مل٠.ffs_batch.</target>
-
-<source>Folders to watch:</source>
-<target>المجلدات للمتابعة:</target>
-
-<source>Add folder</source>
-<target>إضاÙØ© مجلد</target>
-
-<source>Remove folder</source>
-<target>إزالة مجلد</target>
-
-<source>Browse</source>
-<target>تصÙØ­</target>
-
-<source>Idle time (in seconds):</source>
-<target>وقت الخمول (بالثانية):</target>
-
-<source>Idle time between last detected change and execution of command</source>
-<target>وقت الخمول بين آخر تغيير تم الكش٠عنه وتنÙيذ الأوامر</target>
-
-<source>Command line:</source>
-<target>سطر الأوامر:</target>
-
-<source>
-The command is triggered if:
-- files or subfolders change
-- new folders arrive (e.g. USB stick insert)
-</source>
-<target>
-يتم تشغيل الأمر إذا:
--حدوث تغيير ÙÙŠ الملÙات أو المجلدات الÙرعية
--ظهور مجلدات جديدة (مثال: إدخال USB stick)
-</target>
-
-<source>Start</source>
-<target>بدء</target>
-
-<source>About</source>
-<target>حول</target>
-
-<source>Build: %x</source>
-<target>بناء: %x</target>
-
-<source>All files</source>
-<target>جميع الملÙات</target>
-
-<source>Automated Synchronization</source>
-<target>مزامنة تلقائية</target>
-
-<source>The %x protocol does not support directory monitoring:</source>
-<target>بروتوكول %x لا يدعم مراقبة المجلدات:</target>
-
-<source>Directory monitoring active</source>
-<target>مراقبة المسارات Ùعالة</target>
-
-<source>Waiting until all directories are available...</source>
-<target>انتظر حتى توÙر جميع المسارات...</target>
-
-<source>&Restore</source>
-<target>&استعادة</target>
-
-<source>&Show error</source>
-<target>إ&ظهار الخطأ</target>
-
-<source>&Quit</source>
-<target>إ&نهاء</target>
-
-<source>&Retry</source>
-<target>إ&عادة المحاولة</target>
-
-<source>File time and size</source>
-<target>تاريخ المل٠و حجمه</target>
-
-<source>File content</source>
-<target>محتوى الملÙ</target>
-
-<source>File size</source>
-<target>حجم الملÙ</target>
-
-<source>Two way</source>
-<target>بالاتجاهين</target>
-
-<source>Mirror</source>
-<target>انعكاس</target>
-
-<source>Update</source>
-<target>تحديث</target>
-
-<source>Custom</source>
-<target>مخصص</target>
-
-<source>Multiple...</source>
-<target>متعددة...</target>
-
-<source>Moving file %x to %y</source>
-<target>نقل المل٠%x إلى %y</target>
-
-<source>Moving folder %x to %y</source>
-<target>نقل المجلد %x إلى %y</target>
-
-<source>Moving symbolic link %x to %y</source>
-<target>نقل الارتباط الرمزي %x إلى %y</target>
-
-<source>Removing old versions...</source>
-<target>إزالة الإصدارات القديمة...</target>
-
-<source>Updating file %x</source>
-<target>جاري تحديث المل٠%x</target>
-
-<source>Updating symbolic link %x</source>
-<target>جاري تحديث المسار الرمزي %x</target>
-
-<source>Verifying file %x</source>
-<target>التحقق من المل٠%x</target>
-
-<source>Updating attributes of %x</source>
-<target>تحديث سمات %x</target>
-
-<source>Cannot write file attributes of %x.</source>
-<target>لا يمكن كتابة سمات المل٠%x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x Ùˆ %y لديهما محتوى مختلÙ.</target>
-
-<source>Data verification error:</source>
-<target>خطأ ÙÙŠ التحقق من البيانات:</target>
-
-<source>Creating a Volume Shadow Copy for %x...</source>
-<target>جاري إنشاء نسخة ظل وسيطة لـ %x...</target>
-
-<source>Target folder %x already existing.</source>
-<target>المجلد الهد٠%x موجود سابقاً.</target>
-
-<source>Target folder input field must not be empty.</source>
-<target>يجب أن لا يكون حقل إدخال المجلد الهد٠Ùارغاً.</target>
-
-<source>Source folder %x not found.</source>
-<target>لم يتم العثور على المجلد المصدر %x.</target>
-
-<source>Please enter a target folder for versioning.</source>
-<target>الرجاء تحديد مجلد هد٠من أجل الوسم حسب الإصدار.</target>
-
-<source>The following items have unresolved conflicts and will not be synchronized:</source>
-<target>العناصر التالية لم تحل اختلاÙاتها، Ùˆ لن يتم مزامنتها:</target>
-
-<source>The following folders are significantly different. Please check that the correct folders are selected for synchronization.</source>
-<target>المجلدات التالية تختل٠اختلاÙا كبيرا. يرجى التحقق من تحديد المجلدات الصحيحة لإجراء التزامن.</target>
-
-<source>Not enough free disk space available in:</source>
-<target>المساحة الحرة المتوÙرة على القرص غير كاÙية:</target>
-
-<source>Available:</source>
-<target>متاح:</target>
-
-<source>Some files will be synchronized as part of multiple base folders.</source>
-<target>ستتم مزامنة بعض الملÙات كجزء من مجلدات قاعدة متعددة.</target>
-
-<source>To avoid conflicts, set up exclude filters so that each updated file is considered by only one base folder.</source>
-<target>لتجنب التعارضات، قم بإعداد مرشحات استبعاد بحيث يتم النظر ÙÙŠ كل مل٠محدث من قبل مجلد أساسي واحد Ùقط.</target>
-
-<source>Versioning folder:</source>
-<target>مجلد الإصدار:</target>
-
-<source>Base folder:</source>
-<target>المجلد الأساس:</target>
-
-<source>The versioning folder is contained in a base folder.</source>
-<target>مجلد الإصدار موجود داخل مجلد أساس.</target>
-
-<source>Synchronizing folder pair:</source>
-<target>مزامنة زوج مجلدات:</target>
-
-<source>Generating database...</source>
-<target>إنشاء قاعدة بيانات...</target>
-
-<source>Loading...</source>
-<target>تحميل...</target>
-
-<source>job name</source>
-<target>اسم المهمة</target>
-
-<source>System: Sleep</source>
-<target></target>
-
-<source>System: Shut down</source>
-<target></target>
-
-<source>Cleaning up old log files...</source>
-<target>جاري تنظي٠ملÙات المتابعة القديمة...</target>
-
-<source>Stopped</source>
-<target>توقÙ</target>
-
-<source>Completed with errors</source>
-<target></target>
-
-<source>Completed with warnings</source>
-<target></target>
-
-<source>Warning</source>
-<target>تحذير</target>
-
-<source>Nothing to synchronize</source>
-<target>لا يوجد شيء للمزامنة</target>
-
-<source>Completed successfully</source>
-<target></target>
-
-<source>Executing command %x</source>
-<target>تنÙيذ الأمر %x</target>
-
-<source>You can switch to FreeFileSync's main window to resolve this issue.</source>
-<target>بإمكانك العودة إلى ناÙذة FreeFileSync الرئيسية لحل هذه المشكلة.</target>
-
-<source>&Don't show this warning again</source>
-<target>&لا تظهر هذا التنبيه مرة ثانية</target>
-
-<source>&Ignore</source>
-<target>&تجاهل</target>
-
-<source>&Switch</source>
-<target>&تبديل</target>
-
-<source>Switching to FreeFileSync's main window</source>
-<target>العودة إلى ناÙذة FreeFileSync الرئيسية</target>
-
-<source>Automatic retry</source>
-<target></target>
-
-<source>Ignore &all</source>
-<target>تجاهل &الكل</target>
-
-<source>Retrying operation...</source>
-<target>إعادة محاولة العملية...</target>
-
-<source>Serious Error</source>
-<target>خطأ Ùادح</target>
-
-<source>Last session</source>
-<target>مصدر الجلسة</target>
-
-<source>Today</source>
-<target>اليوم</target>
-
-<source>
-<pluralform>1 day</pluralform>
-<pluralform>%x days</pluralform>
-</source>
-<target>
-<pluralform>0 يوم</pluralform>
-<pluralform>1 يوم واحد</pluralform>
-<pluralform>2 يومان</pluralform>
-<pluralform>%x أيام</pluralform>
-<pluralform>%x يوماً</pluralform>
-<pluralform>%x يوم</pluralform>
-</target>
-
-<source>Name</source>
-<target>الاسم</target>
-
-<source>Last sync</source>
-<target></target>
-
-<source>Folder</source>
-<target>المجلد</target>
-
-<source>Symlink</source>
-<target>ارتباط-رمزي</target>
-
-<source>Full path</source>
-<target>المسار الكامل</target>
-
-<source>Relative path</source>
-<target>المسار النسبي</target>
-
-<source>Item name</source>
-<target>اسم العنصر</target>
-
-<source>Size</source>
-<target>الحجم</target>
-
-<source>Date</source>
-<target>تاريخ</target>
-
-<source>Extension</source>
-<target>اللاحقة</target>
-
-<source>Category</source>
-<target>الÙئة</target>
-
-<source>Action</source>
-<target>التصرÙ</target>
-
-<source>Local comparison settings</source>
-<target>إعدادات المقارنة المحلية</target>
-
-<source>Local synchronization settings</source>
-<target>إعدادات المزامنة المحلية</target>
-
-<source>Local filter</source>
-<target>Ùلتر محلي</target>
-
-<source>Active</source>
-<target>Ù…Ùعل</target>
-
-<source>None</source>
-<target>لا شيء</target>
-
-<source>Remove local settings</source>
-<target>إزالة الإعدادات المحلية</target>
-
-<source>Clear local filter</source>
-<target>إزالة الÙلتر المحلي</target>
-
-<source>Copy</source>
-<target>نسخ</target>
-
-<source>Paste</source>
-<target>لصق</target>
-
-<source>The selected folder %x cannot be used with FreeFileSync.</source>
-<target>المجلد المحدد %x لا يمكن استخدامه مع FreeFileSync.</target>
-
-<source>Please select a folder on a local file system, network or an MTP device.</source>
-<target>الرجاء تحديد مجلد على نظام الملÙات المحلي، الشبكة أو جهاز MTP.</target>
-
-<source>&Save</source>
-<target>&Ø­Ùظ</target>
-
-<source>Save as &batch job...</source>
-<target>&Ø­Ùظ كمهمة دÙعية...</target>
-
-<source>Start &comparison</source>
-<target>بدأ الم&قارنة</target>
-
-<source>C&omparison settings</source>
-<target>&مقارنة</target>
-
-<source>&Filter settings</source>
-<target>إعدادات ال&Ùلتر</target>
-
-<source>S&ynchronization settings</source>
-<target>إ&عدادات</target>
-
-<source>Start &synchronization</source>
-<target>بدأ الم&زامنة</target>
-
-<source>&Actions</source>
-<target>&مهام</target>
-
-<source>&Preferences</source>
-<target>&التÙضيلات</target>
-
-<source>&Language</source>
-<target>الل&غة</target>
-
-<source>&Find...</source>
-<target>&بحث...</target>
-
-<source>&Export file list...</source>
-<target>&تصدير قائمة الملÙات...</target>
-
-<source>&Reset layout</source>
-<target>إعادة ال&تنسيق إلى الإÙتراضي</target>
-
-<source>&Tools</source>
-<target>أ&دوات</target>
-
-<source>&Check for updates now</source>
-<target>&تحقق من وجود تحديثات الآن</target>
-
-<source>Check &automatically once a week</source>
-<target>&تحقق أوتوماتيكياً بشكل أسبوعي</target>
-
-<source>Cancel</source>
-<target>إلغاء الأمر</target>
-
-<source>Compare</source>
-<target>قارن</target>
-
-<source>Synchronize</source>
-<target>مزامنة</target>
-
-<source>Add folder pair</source>
-<target>إضاÙØ© زوج مجلدات</target>
-
-<source>Remove folder pair</source>
-<target>إزالة زوج مجلدات</target>
-
-<source>Access online storage</source>
-<target>الوصول إلى التخزين عبر الإنترنت</target>
-
-<source>Swap sides</source>
-<target>مبادلة الجانبين</target>
-
-<source>Close search bar</source>
-<target>إغلاق شريط البحث</target>
-
-<source>Find:</source>
-<target>بحث:</target>
-
-<source>Match case</source>
-<target>مطابقة الحالة</target>
-
-<source>New</source>
-<target>جديد</target>
-
-<source>Open...</source>
-<target>Ùتح...</target>
-
-<source>Save</source>
-<target>Ø­Ùظ</target>
-
-<source>Save as...</source>
-<target>Ø­Ùظ كـ...</target>
-
-<source>View type:</source>
-<target>عرض النوع:</target>
-
-<source>Select view:</source>
-<target>اختيار نمط العرض:</target>
-
-<source>Statistics:</source>
-<target>إحصائيات:</target>
-
-<source>Number of files and folders that will be deleted</source>
-<target>عدد الملÙات Ùˆ المجلدات التي سيتم حذÙها</target>
-
-<source>Number of files that will be updated</source>
-<target>عدد الملÙات التي سيتم تحديثها</target>
-
-<source>Number of files and folders that will be created</source>
-<target>عدد الملÙات Ùˆ المجلدات التي سيتم إنشاؤها</target>
-
-<source>Total bytes to copy</source>
-<target>إجمالي عدد الـ bytes التي سيتم نسخها</target>
-
-<source>Arrange folder pair</source>
-<target>ترتيب زوج المجلدات</target>
-
-<source>Folder pair:</source>
-<target>زوج المجلدات:</target>
-
-<source>Main settings:</source>
-<target>الإعدادات الرئيسية:</target>
-
-<source>Use local settings:</source>
-<target>استخدام الإعدادات المحلية:</target>
-
-<source>Select a variant:</source>
-<target>اختيار بديل:</target>
-
-<source>Include &symbolic links:</source>
-<target>تضمين &الروابط الرمزية:</target>
-
-<source>&Follow</source>
-<target>&متابعة</target>
-
-<source>&Direct</source>
-<target>&مباشر</target>
-
-<source>More information</source>
-<target>المزيد من المعلومات</target>
-
-<source>&Ignore time shift [hh:mm]</source>
-<target>&تجاهل تغيير الوقت [hh:mm]</target>
-
-<source>List of file time offsets to ignore</source>
-<target>قائمة إزاحات وقت للملÙات ليتم تجاهلها</target>
-
-<source>Example:</source>
-<target>مثال:</target>
-
-<source>Handle daylight saving time</source>
-<target>تعامل مع التوقيت الصيÙÙŠ</target>
-
-<source>Local settings:</source>
-<target>الإعدادات المحلية:</target>
-
-<source>Include:</source>
-<target>إدخال:</target>
-
-<source>Show examples</source>
-<target>إظهار أمثلة</target>
-
-<source>Time span:</source>
-<target>المجال الزمني:</target>
-
-<source>File size:</source>
-<target>حجم الملÙ:</target>
-
-<source>Minimum:</source>
-<target>الحد الأدنى:</target>
-
-<source>Maximum:</source>
-<target>الحد الأقصى:</target>
-
-<source>Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair.</source>
-<target>اختيار قوانين Ùلترة لاستثناء ملÙات معينة من المزامنة. أدخل مسارات الملÙات منسوبة إلى زوج المجلدات المقابل.</target>
-
-<source>C&lear</source>
-<target>إ&زالة</target>
-
-<source>Detect moved files</source>
-<target>اكتشا٠الملÙات المنقولة</target>
-
-<source>
-- Not supported by all file systems
-- Requires and creates database files
-- Detection not available for first sync
-</source>
-<target>
-- غير مدعوم من كل أنظمة الملÙات
-- يتطلب وينشىء ملÙات قواعد البيانات
-- الاكتشا٠غير متاح للمزامنة الأولى
-</target>
-
-<source>Delete files:</source>
-<target>حذ٠الملÙات:</target>
-
-<source>&Recycle bin</source>
-<target>&سلة المهملات</target>
-
-<source>&Permanent</source>
-<target>&دائم</target>
-
-<source>&Versioning</source>
-<target>ال&وسم حسب الإصدار</target>
-
-<source>Naming convention:</source>
-<target>اصطلاح التسمية:</target>
-
-<source>Ignore errors</source>
-<target></target>
-
-<source>Retry count:</source>
-<target>تعداد محاولات الإعادة:</target>
-
-<source>Delay (in seconds):</source>
-<target>التأخير (بالثواني):</target>
-
-<source>Run a command after synchronization:</source>
-<target>تشغيل أمر بعد المزامنة:</target>
-
-<source>OK</source>
-<target>مواÙÙ‚</target>
-
-<source>Enter your login details:</source>
-<target>أدخل تÙاصيل تسجيل الدخول:</target>
-
-<source>Connection type:</source>
-<target>نوع الاتصال:</target>
-
-<source>Server name or IP address:</source>
-<target>اسم الخادم أو عنوان IP:</target>
-
-<source>Port:</source>
-<target>المنÙØ°:</target>
-
-<source>Encryption:</source>
-<target>التشÙير:</target>
-
-<source>&Disabled</source>
-<target>&معطل</target>
-
-<source>&Explicit SSL/TLS</source>
-<target>&Explicit SSL/TLS</target>
-
-<source>Authentication:</source>
-<target>المصادقة:</target>
-
-<source>&Password</source>
-<target>&كلمة المرو</target>
-
-<source>&Key file</source>
-<target>&مل٠المÙتاح</target>
-
-<source>&SSH agent</source>
-<target>&عميل بروتوكول SSH</target>
-
-<source>User name:</source>
-<target>اسم المستخدم:</target>
-
-<source>Private key file:</source>
-<target>مل٠المÙتاح الخاص:</target>
-
-<source>&Show password</source>
-<target>&إظهار كلمة المرور</target>
-
-<source>Directory on server:</source>
-<target>المسار على الخادم:</target>
-
-<source>Performance improvements:</source>
-<target>تحسينات الأداء:</target>
-
-<source>How to get best performance?</source>
-<target>كيÙية الحصول على Ø£Ùضل أداء؟</target>
-
-<source>Connections for directory reading:</source>
-<target>عدد الاتصالات لقراءة المجلد:</target>
-
-<source>SFTP channels per connection:</source>
-<target>قنوات SFTP لكل اتصال:</target>
-
-<source>Detect server limit</source>
-<target>اكتشا٠حد الخادم</target>
-
-<source>Select a directory on the server:</source>
-<target>تحديد دليل على الخادم:</target>
-
-<source>Select Folder</source>
-<target>اختر مجلد</target>
-
-<source>Start synchronization now?</source>
-<target>بدأ المزامنة الآن؟</target>
-
-<source>Variant:</source>
-<target>بديل:</target>
-
-<source>&Don't show this dialog again</source>
-<target>&لا تظهر ناÙذة الحوار هذه مرة ثانية</target>
-
-<source>Items found:</source>
-<target>العناصر التي تم العثور عليها:</target>
-
-<source>Time remaining:</source>
-<target>الوقت المتبقي:</target>
-
-<source>Time elapsed:</source>
-<target>الوقت المنقضي:</target>
-
-<source>Bytes</source>
-<target>بايت</target>
-
-<source>Items</source>
-<target>العناصر</target>
-
-<source>Synchronizing...</source>
-<target>مزامنة...</target>
-
-<source>Minimize to notification area</source>
-<target>تصغير إلى منطقة التنبيهات</target>
-
-<source>When finished:</source>
-<target>عند الانتهاء:</target>
-
-<source>Auto-close</source>
-<target></target>
-
-<source>Close</source>
-<target>إغلاق</target>
-
-<source>&Pause</source>
-<target>إ&يقا٠مؤقت</target>
-
-<source>Stop</source>
-<target>توقÙ</target>
-
-<source>Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x</source>
-<target>إنشاء مل٠دÙعي من أجل عمليات المزامنة غير المحضورة. للبدأ, انقر نقراً مزدوجاً على المل٠أو المهمة المجدولة ÙÙŠ منظم المهام: %x</target>
-
-<source>Progress dialog:</source>
-<target></target>
-
-<source>Run minimized</source>
-<target>تشغيل بوضع التصغير</target>
-
-<source>&Show error dialog</source>
-<target>&إظهار ناÙذة الأخطاء</target>
-
-<source>Show pop-up on errors or warnings</source>
-<target>إظهار إطارات منبثقة عند حصول أخطاء أو تحذيرات</target>
-
-<source>&Cancel</source>
-<target>&إلغاء</target>
-
-<source>Stop synchronization at first error</source>
-<target>إحباط المزامنة عند أول خطأ</target>
-
-<source>Save log:</source>
-<target>Ø­Ùظ السجل:</target>
-
-<source>Limit:</source>
-<target>حدود:</target>
-
-<source>Limit maximum number of log files</source>
-<target>تقييد الحد الأقصى لعدد ملÙات السجل</target>
-
-<source>How can I schedule a batch job?</source>
-<target>كي٠يمكنني جدولة مهمة دÙعية؟</target>
-
-<source>&Keep relative paths</source>
-<target>&إبقاء المسارات النسبية</target>
-
-<source>&Overwrite existing files</source>
-<target>&الكتابة Ùوق الملÙات الموجودة</target>
-
-<source>The following settings are used for all synchronization jobs.</source>
-<target>هذه الإعدادات مستخدمة لجميع مهمات المزامنة.</target>
-
-<source>
-Copy to a temporary file (*.ffs_tmp) before overwriting target.
-This guarantees a consistent state even in case of a serious error.
-</source>
-<target>
-قم بالنسخ إلى مل٠مؤقت (*.ffs_tmp) قبل استبدال المل٠الهدÙ.
-هذه العملية تضمن حالة مستقرة حتى ÙÙŠ حال حدوث خطأ Ùادح.
-</target>
-
-<source>recommended</source>
-<target>â€Ù…نصوح بهâ€</target>
-
-<source>Copy shared or locked files using the Volume Shadow Copy Service.</source>
-<target>نسخ الملÙات المشتركة مع مستخدمين آخرين أو المقÙولة باستخدام خدمة نسخ الظل الوسيط.</target>
-
-<source>requires administrator rights</source>
-<target>يتطلب صلاحيات مسؤول</target>
-
-<source>Transfer file and folder permissions.</source>
-<target>نقل أذونات الملÙات Ùˆ المجلدات.</target>
-
-<source>Show hidden dialogs again</source>
-<target>إظهار التنبهات Ùˆ نواÙØ° الحوار المخÙية</target>
-
-<source>Show all permanently hidden dialogs and warning messages again</source>
-<target>إعادة إظهار جميع التنبهات Ùˆ نواÙØ° الحوار التي تم إخÙاؤها</target>
-
-<source>Customize context menu:</source>
-<target>تخصيص القائمة المحلية:</target>
-
-<source>Description</source>
-<target>الوصÙ</target>
-
-<source>&Default</source>
-<target>الا&Ùتراضي</target>
-
-<source>Source code written in C++ using:</source>
-<target>الرماز المصدري مكتوب بلغة C++‎ باستخدام:</target>
-
-<source>If you like FreeFileSync:</source>
-<target>إذا أعجبك FreeFileSync:</target>
-
-<source>Support with a donation</source>
-<target>دعم عبر التبرع</target>
-
-<source>Donation details</source>
-<target>تÙاصيل التبرع</target>
-
-<source>The auto updater was disabled by the administrator.</source>
-<target>تم تعطيل المحدث التلقائي بواسطة المسؤول.</target>
-
-<source>Feedback and suggestions are welcome</source>
-<target>التعليقات و الاقتراحات موضع ترحيب</target>
-
-<source>Home page</source>
-<target>الصÙحة الرئيسية</target>
-
-<source>Email</source>
-<target>البريد الإلكتروني</target>
-
-<source>Published under the GNU General Public License</source>
-<target>نشر تحت رخصة GNU General Public License</target>
-
-<source>Many thanks for localization:</source>
-<target>شكرا جزيلا للترجمة:</target>
-
-<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
-<target>قم بتنشيط FreeFileSync Donation Edition بإحدى الطرق التالية:</target>
-
-<source>1. Activate via internet now:</source>
-<target>1. تنشيط عبر الإنترنت الآن:</target>
-
-<source>Activate online</source>
-<target>التنشيط عبر الإنترنت</target>
-
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. استرداد Ù…Ùتاح تنشيط دون اتصال من الرابط التالي:</target>
-
-<source>&Copy to clipboard</source>
-<target>&نسخ من الذاكرة</target>
-
-<source>Enter activation key:</source>
-<target>أدخل Ù…Ùتاح التنشيط:</target>
-
-<source>Activate offline</source>
-<target>تنشيط دون اتصال</target>
-
-<source>Highlight configurations that have not been run for more than the following number of days:</source>
-<target></target>
-
-<source>Synchronization Settings</source>
-<target>إعدادات المزامنة</target>
-
-<source>Access Online Storage</source>
-<target></target>
-
-<source>Save as a Batch Job</source>
-<target>Ø­Ùظ كمهمة دÙعية</target>
-
-<source>Delete Items</source>
-<target>حذ٠العناصر</target>
-
-<source>Copy Items</source>
-<target></target>
-
-<source>Options</source>
-<target>خيارات</target>
-
-<source>Select Time Span</source>
-<target>اختيار المطال الزمني</target>
-
-<source>FreeFileSync Donation Edition</source>
-<target>FreeFileSync Donation Edition</target>
-
-<source>Highlight Configurations</source>
-<target></target>
-
-<source>&Options</source>
-<target>&خيارات</target>
-
-<source>Main Bar</source>
-<target>الشريط الرئيسي</target>
-
-<source>Folder Pairs</source>
-<target>أزواج المجلدات</target>
-
-<source>Find</source>
-<target>بحث</target>
-
-<source>View Settings</source>
-<target>عرض الإعدادات</target>
-
-<source>Configuration</source>
-<target>التكوين</target>
-
-<source>Overview</source>
-<target>نظرة عامة</target>
-
-<source>Show "%x"</source>
-<target>إظهار "%x"</target>
-
-<source>&Show details</source>
-<target>&عرض التÙاصيل</target>
-
-<source>FreeFileSync %x is available!</source>
-<target>FreeFileSync %x متوÙر!</target>
-
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>ملÙات التثبيت تالÙØ©. الرجاء إعادة تثبيت FreeFileSync.</target>
-
-<source>Local path not available for %x.</source>
-<target>المسار المحلي غير متوÙر لـ %x.</target>
-
-<source>Confirm</source>
-<target>تأكيد</target>
-
-<source>
-<pluralform>Do you really want to execute the command %y for one item?</pluralform>
-<pluralform>Do you really want to execute the command %y for %x items?</pluralform>
-</source>
-<target>
-<pluralform>هل تريد حقاً تنÙيذ العملية %y من أجل عنصر 0</pluralform>
-<pluralform>هل تريد حقاً تنÙيذ العملية %y من أجل عنصر وحيد 1</pluralform>
-<pluralform>هل تريد حقاً تنÙيذ العملية %y من أجل عنصرين اثنين 2</pluralform>
-<pluralform>هل تريد حقاً تنÙيذ العملية %y من أجل %x عناصر</pluralform>
-<pluralform>هل تريد حقاً تنÙيذ العملية %y من أجل %x عنصراً</pluralform>
-<pluralform>هل تريد حقاً تنÙيذ العملية %y من أجل %x عنصر</pluralform>
-</target>
-
-<source>&Execute</source>
-<target>&تنÙيذ</target>
-
-<source>
-<pluralform>1 directory</pluralform>
-<pluralform>%x directories</pluralform>
-</source>
-<target>
-<pluralform>0 مسار</pluralform>
-<pluralform>1 مسار واحد</pluralform>
-<pluralform>2 مساران</pluralform>
-<pluralform>%x مسارات</pluralform>
-<pluralform>%x مساراً</pluralform>
-<pluralform>%x مسار</pluralform>
-</target>
-
-<source>
-<pluralform>1 file</pluralform>
-<pluralform>%x files</pluralform>
-</source>
-<target>
-<pluralform>0 ملÙ</pluralform>
-<pluralform>1 مل٠واحد</pluralform>
-<pluralform>2 ملÙان</pluralform>
-<pluralform>%x ملÙات</pluralform>
-<pluralform>%x ملÙاً</pluralform>
-<pluralform>%x ملÙ</pluralform>
-</target>
-
-<source>
-<pluralform>Showing %y of 1 row</pluralform>
-<pluralform>Showing %y of %x rows</pluralform>
-</source>
-<target>
-<pluralform>الظاهر %y من أصل سطر 0</pluralform>
-<pluralform>الظاهر %y من أصل سطر وحيد 1</pluralform>
-<pluralform>الظاهر %y من أصل سطرين اثنين 2</pluralform>
-<pluralform>الظاهر %y من أصل %x سطور</pluralform>
-<pluralform>الظاهر %y من أصل %x سطراً</pluralform>
-<pluralform>الظاهر %y من أصل %x سطر</pluralform>
-</target>
-
-<source>Set direction:</source>
-<target>تحديد الاتجاه:</target>
-
-<source>multiple selection</source>
-<target>تحديد متعدد</target>
-
-<source>Include via filter:</source>
-<target>تضمن عن طريق Ùلتر:</target>
-
-<source>Exclude via filter:</source>
-<target>استبعاد باستخدام عامل الÙلترة:</target>
-
-<source>Include temporarily</source>
-<target>شمول مؤقتاً</target>
-
-<source>Exclude temporarily</source>
-<target>استبعاد مؤقتاً</target>
-
-<source>&Copy to...</source>
-<target>&نسخ إلى...</target>
-
-<source>&Delete</source>
-<target>&حذÙ</target>
-
-<source>Include all</source>
-<target>شمول الكل</target>
-
-<source>Exclude all</source>
-<target>استبعاد الكل</target>
-
-<source>Show icons:</source>
-<target>إظهار الأيقونات:</target>
-
-<source>Small</source>
-<target>صغيرة</target>
-
-<source>Medium</source>
-<target>متوسطة</target>
-
-<source>Large</source>
-<target>كبيرة</target>
-
-<source>Select time span...</source>
-<target>حدد المجال الزمني...</target>
-
-<source>Folder Comparison and Synchronization</source>
-<target>مقارنة و مزامنة المجلد</target>
-
-<source>Configuration saved</source>
-<target>تم Ø­Ùظ التكوين</target>
-
-<source>FreeFileSync batch</source>
-<target>دÙعة FreeFileSync</target>
-
-<source>Do you want to save changes to %x?</source>
-<target>هل تريد Ø­Ùظ التغييرات إلى %xØŸ</target>
-
-<source>Never save &changes</source>
-<target>&لا تقم بحÙظ التغيرات</target>
-
-<source>Do&n't save</source>
-<target>&لا تحÙظ</target>
-
-<source>Hide configuration</source>
-<target></target>
-
-<source>Highlight...</source>
-<target></target>
-
-<source>Clear filter</source>
-<target>إزالة الÙلاتر الحالية</target>
-
-<source>Show files that exist on left side only</source>
-<target>إظهار الملÙات الموجودة ÙÙŠ الجانب الأيمن Ùقط</target>
-
-<source>Show files that exist on right side only</source>
-<target>إظهار الملÙات الموجودة ÙÙŠ الجانب الأيسر Ùقط</target>
-
-<source>Show files that are newer on left</source>
-<target>إظهار الملÙات الأحدث ÙÙŠ اليمين</target>
-
-<source>Show files that are newer on right</source>
-<target>إظهار الملÙات الأحدث ÙÙŠ اليسار</target>
-
-<source>Show files that are equal</source>
-<target>إظهار الملÙات المتماثلة على الطرÙين</target>
-
-<source>Show files that are different</source>
-<target>إظهار الملÙات المختلÙØ© على الطرÙين</target>
-
-<source>Show conflicts</source>
-<target>إظهار الاختلاÙات</target>
-
-<source>Show files that will be created on the left side</source>
-<target>إظهار الملÙات التي سيتم إنشاؤها ÙÙŠ الجانب الأيمن</target>
-
-<source>Show files that will be created on the right side</source>
-<target>إظهار الملÙات التي سيتم إنشاؤها ÙÙŠ الجانب الأيسر</target>
-
-<source>Show files that will be deleted on the left side</source>
-<target>إظهار الملÙات التي سيتم حذÙها من من الجانب الأيمن</target>
-
-<source>Show files that will be deleted on the right side</source>
-<target>إظهار الملÙات التي سيتم حذÙها من الجانب الأيسر</target>
-
-<source>Show files that will be updated on the left side</source>
-<target>إظهار الملÙات التي سيتم تحديثها ÙÙŠ الجانب الأيسر</target>
-
-<source>Show files that will be updated on the right side</source>
-<target>إظهار الملÙات التي سيتم تحديثها ÙÙŠ الجانب الأيمن</target>
-
-<source>Show files that won't be copied</source>
-<target>إظهار الملÙات التي لن يتم نسخها</target>
-
-<source>Show filtered or temporarily excluded files</source>
-<target>إظهار الملÙات التي تم Ùلترتها أو استبعادها بشكل مؤقت</target>
-
-<source>Save as default</source>
-<target>Ø­Ùظ كاÙتراضي</target>
-
-<source>Filter</source>
-<target>عامل الÙلترة</target>
-
-<source>All files are in sync</source>
-<target>جميع الملÙات ÙÙŠ تزامن كامل</target>
-
-<source>Cannot find %x</source>
-<target>لا يمكن العثور على %x</target>
-
-<source>Move up</source>
-<target>تحريك لأعلى</target>
-
-<source>Move down</source>
-<target>تحريك لأسÙÙ„</target>
-
-<source>Comma-separated values</source>
-<target>قائمة قيم Ù…Ùصولة بÙواصل</target>
-
-<source>File list exported</source>
-<target>تم تصدير قائمة الملÙات</target>
-
-<source>Searching for program updates...</source>
-<target>جاري البحث عن تحديثات للبرنامج...</target>
-
-<source>Paused</source>
-<target>تم الإيقا٠مؤقتاً</target>
-
-<source>Stop requested...</source>
-<target></target>
-
-<source>Initializing...</source>
-<target>التجهيز للبدأ...</target>
-
-<source>Scanning...</source>
-<target>جاري الÙحص...</target>
-
-<source>Comparing content...</source>
-<target>مقارنة المحتوى...</target>
-
-<source>Info</source>
-<target>معلومات</target>
-
-<source>Select all</source>
-<target>اختيار الجميع</target>
-
-<source>&Continue</source>
-<target>&مواصلة</target>
-
-<source>Progress</source>
-<target>التقدم</target>
-
-<source>Log</source>
-<target>السجل</target>
-
-<source>Thank you, %x, for your donation and support!</source>
-<target>شكرا لك، %x, للتبرع والدعم.</target>
-
-<source>Recommended range:</source>
-<target>النطاق المستحسن:</target>
-
-<source>Password:</source>
-<target>كلمة المرور:</target>
-
-<source>Key password:</source>
-<target>كلمة المرور الرئيسية:</target>
-
-<source>Please enter a file path.</source>
-<target>الرجاء إدخال مسار الملÙ.</target>
-
-<source>
-<pluralform>Copy the following item to another folder?</pluralform>
-<pluralform>Copy the following %x items to another folder?</pluralform>
-</source>
-<target>
-<pluralform>نسخ العنصر 0 التالي إلى مجلد آخر؟</pluralform>
-<pluralform>نسخ العنصر 1 التالي إلى مجلد آخر؟</pluralform>
-<pluralform>نسخ العنصران التاليان إلى مجلد آخر؟</pluralform>
-<pluralform>نسخ الـ %x عناصر التالية إلى مجلد آخر؟</pluralform>
-<pluralform>نسخ الـ %x عنصراً التالية إلى مجلد آخر؟</pluralform>
-<pluralform>نسخ الـ %x عنصر التالية إلى مجلد آخر؟</pluralform>
-</target>
-
-<source>Please enter a target folder.</source>
-<target>الرجاء إدخال المجلد الهدÙ.</target>
-
-<source>
-<pluralform>Do you really want to move the following item to the recycle bin?</pluralform>
-<pluralform>Do you really want to move the following %x items to the recycle bin?</pluralform>
-</source>
-<target>
-<pluralform>هل تريد حقاً نقل 0 مل٠إلى سلة المهملات ؟</pluralform>
-<pluralform>هل تريد حقاً نقل هذا المل٠الوحيد 1 إلى سلة المهملات ؟</pluralform>
-<pluralform>هل تريد حقاً نقل هذين الملÙبن 2 إلى سلة المهملات ØŸ</pluralform>
-<pluralform>هل تريد حقاً نقل %x ملÙات إلى سلة المهملات ØŸ</pluralform>
-<pluralform>هل تريد حقاً نقل %x ملÙاً إلى سلة المهملات ØŸ</pluralform>
-<pluralform>هل تريد حقاً نقل %x مل٠إلى سلة المهملات ؟</pluralform>
-</target>
-
-<source>Move</source>
-<target>نقل</target>
-
-<source>
-<pluralform>Do you really want to delete the following item?</pluralform>
-<pluralform>Do you really want to delete the following %x items?</pluralform>
-</source>
-<target>
-<pluralform>هل تريد حقاً حذ٠0 مل٠التالي ؟</pluralform>
-<pluralform>هل تريد حقاً حذ٠المل٠1 التالي ؟</pluralform>
-<pluralform>هل تريد حقاً حذ٠الملÙين 2 التاليين ØŸ</pluralform>
-<pluralform>هل تريد حقاً حذ٠الـ %x ملÙات التالية ØŸ</pluralform>
-<pluralform>هل تريد حقاً حذ٠الـ %x ملÙاً التالية ØŸ</pluralform>
-<pluralform>هل تريد حقاً حذ٠الـ %x مل٠التالية ؟</pluralform>
-</target>
-
-<source>Copy DACL, SACL, Owner, Group</source>
-<target>نسخ DACL, SACL, Owner, Group</target>
-
-<source>Integrate external applications into context menu. The following macros are available:</source>
-<target>دمج تطبيقات خارجية ÙÙŠ قائمة السياق. تتوÙر وحدات الماكرو التالية:</target>
-
-<source>Full file or folder path</source>
-<target>المسار الكامل للمل٠أو المجلد</target>
-
-<source>Parent folder path</source>
-<target>مسار المجلد الحاوي</target>
-
-<source>Temporary local copy for SFTP and MTP storage</source>
-<target>نسخة محلية مؤقتة لتخزين SFTP و MTP</target>
-
-<source>Parameters for opposite side</source>
-<target>معلمات الجانب المعاكس</target>
-
-<source>Downloading update...</source>
-<target>جار تحميل التحديث...</target>
-
-<source>Identify equal files by comparing modification time and size.</source>
-<target>التعر٠على الملÙات المتساوية عن طريق مقارنة التاريخ Ùˆ الحجم.</target>
-
-<source>Identify equal files by comparing the file content.</source>
-<target>التعر٠على الملÙات المتساوية عن طريق مقارنة محتوى الملÙ.</target>
-
-<source>Identify equal files by comparing their file size.</source>
-<target>تحديد الملÙات المتساوية بمقارنة حجم المل٠لها.</target>
-
-<source>Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database.</source>
-<target>تحديد التغيرات Ùˆ مواكبتها على الجانبين. عمليات الحذÙ, النقل Ùˆ المشاكل المكتشÙØ© باستخدام قواعد البيانات.</target>
-
-<source>Create a mirror backup of the left folder by adapting the right folder to match.</source>
-<target>إنشاء نسخة احتياطية من الجانب الأيمن عن طريق تعديل الطر٠الأيسر ليطابق الأيمن.</target>
-
-<source>Copy new and updated files to the right folder.</source>
-<target>نسخ الملÙات المحدثة إلى المجلد المناسب.</target>
-
-<source>Configure your own synchronization rules.</source>
-<target>تحديد قواعد المزامنة الخاصة بك.</target>
-
-<source>Comparison</source>
-<target>المقارنة</target>
-
-<source>Synchronization</source>
-<target>المزامنة</target>
-
-<source>This week</source>
-<target>هذا الأسبوع</target>
-
-<source>This month</source>
-<target>هذا الشهر</target>
-
-<source>This year</source>
-<target>هذه السنة</target>
-
-<source>Last x days</source>
-<target>الأيام x الماضية</target>
-
-<source>Byte</source>
-<target>Byte</target>
-
-<source>KB</source>
-<target>KB</target>
-
-<source>MB</source>
-<target>MB</target>
-
-<source>Retain deleted and overwritten files in the recycle bin</source>
-<target>الاحتÙاظ بالملÙات المحذوÙØ© والمستبدلة ÙÙŠ سلة المهملات</target>
-
-<source>Delete and overwrite files permanently</source>
-<target>حذ٠واستبدال الملÙات نهائيا</target>
-
-<source>Move files to a user-defined folder</source>
-<target>نقل الملÙات إلى المجلد المحدد من قبل المستخدم</target>
-
-<source>Replace</source>
-<target>استبدال</target>
-
-<source>Move files and replace if existing</source>
-<target>نقل الملÙات Ùˆ استبدال الموجودة بها إن وجدت</target>
-
-<source>Time stamp</source>
-<target>البصمة الزمنية</target>
-
-<source>Append a time stamp to each file name</source>
-<target>إلحاق ختم زمني بكل اسم ملÙ</target>
-
-<source>On completion:</source>
-<target>عند الانتهاء:</target>
-
-<source>On errors:</source>
-<target>عند حصول أخطاء:</target>
-
-<source>On success:</source>
-<target>عند النجاح:</target>
-
-<source>Main config</source>
-<target>التكوين الرئيسي</target>
-
-<source>empty</source>
-<target>Ùارغ</target>
-
-<source>Leave as unresolved conflict</source>
-<target>ترك كاختلاÙات من دون حل</target>
-
-<source>File</source>
-<target>ملÙ</target>
-
-<source>YYYY-MM-DD hhmmss</source>
-<target>YYYY-MM-DD hhmmss</target>
-
-<source>Files</source>
-<target>ملÙات</target>
-
-<source>Percentage</source>
-<target>النسبة المئوية</target>
-
-<source>Failed to retrieve update information.</source>
-<target>Ùشل استرداد معلومات التحديث.</target>
-
-<source>Automatic updates:</source>
-<target>التحديثات التلقائية:</target>
-
-<source>Requires FreeFileSync Donation Edition</source>
-<target>يتطلب FreeFileSync Donation Edition</target>
-
-<source>Check for Program Updates</source>
-<target>تÙقد وجود تحديثات للبرنامج</target>
-
-<source>Auto-update now or download manually from the FreeFileSync home page?</source>
-<target>التحديث التلقائي الآن أو تحميل يدويا من الصÙحة الرئيسية لـ FreeFileSyncØŸ</target>
-
-<source>&Auto-update</source>
-<target>&تحديث تلقائي</target>
-
-<source>&Home page</source>
-<target>&الصÙحة الرئيسية</target>
-
-<source>Download now?</source>
-<target>تنزيل الآن؟</target>
-
-<source>&Download</source>
-<target>&تنزيل</target>
-
-<source>FreeFileSync is up to date.</source>
-<target>FreeFileSync بأحدث إصدار بالÙعل.</target>
-
-<source>Cannot find current FreeFileSync version number online. A newer version is likely available. Check manually now?</source>
-<target>لا يمكن إيجاد رقم النسخة الحالية لـ FreeFileSync عبر الإنترنت. ربما تتوÙر نسخة جديدة، هل تريد Ùعل ذلك يدويا؟</target>
-
-<source>&Check</source>
-<target>&تحقق</target>
-
-<source>Consistency check failed for %x.</source>
-<target>Ùشل Ùحص التناسق لـ %x.</target>
-
-<source>Installation was registered on a different operating system.</source>
-<target>تم تسجيل التثبيت على نظام تشغيل مختلÙ.</target>
-
-<source>Failed to activate FreeFileSync Donation Edition.</source>
-<target>Ùشل تÙعيل FreeFileSync Donation Edition.</target>
-
-<source>Incorrect activation key.</source>
-<target>Ù…Ùتاح التÙعيل غير صحيح.</target>
-
-<source>Unable to register to receive system messages.</source>
-<target>تعذر التسجيل لاستقبال رسائل النظام.</target>
-
-<source>Cannot find system function %x.</source>
-<target>لا يمكن العثور على وظيÙØ© نظام %x.</target>
-
-<source>Unable to register device notifications for %x.</source>
-<target>تعذر تسجيل تنبيهات الجهاز لـ %x.</target>
-
-<source>Cannot monitor directory %x.</source>
-<target>لا يمكن مراقبة المسار %x.</target>
-
-<source>The file is locked by another process:</source>
-<target>المل٠مقÙول من قبل عملية أخرى:</target>
-
-<source>Cannot read security context of %x.</source>
-<target>لا يمكن قراءة سياق الأمان %x.</target>
-
-<source>Cannot write security context of %x.</source>
-<target>لا يمكن كتابة سياق الأمان %x.</target>
-
-<source>Cannot read permissions of %x.</source>
-<target>لا يمكن قراءة أذونات %x.</target>
-
-<source>Cannot copy permissions from %x to %y.</source>
-<target>لا يمكن نسخ الأذونات من %x إلى %y.</target>
-
-<source>%x is not a regular directory name.</source>
-<target>%x ليس اسم دليل اعتيادي.</target>
-
-<source>Cannot copy file %x to %y.</source>
-<target>لا يمكن نسخ المل٠%x إلى %y.</target>
-
-<source>Cannot copy attributes from %x to %y.</source>
-<target>لا يمكن نسخ السمات من %x إلى %y.</target>
-
-<source>%x TB</source>
-<target>‎%x TB</target>
-
-<source>%x PB</source>
-<target>‎%x PB</target>
-
-<source>
-<pluralform>1 min</pluralform>
-<pluralform>%x min</pluralform>
-</source>
-<target>
-<pluralform>0 دقيقة</pluralform>
-<pluralform>1 دقيقة واحدة</pluralform>
-<pluralform>2 دقيقتان</pluralform>
-<pluralform>%x دقائق</pluralform>
-<pluralform>%x دقيقة</pluralform>
-<pluralform>%x دقيقة</pluralform>
-</target>
-
-<source>
-<pluralform>1 hour</pluralform>
-<pluralform>%x hours</pluralform>
-</source>
-<target>
-<pluralform>0 ساعة</pluralform>
-<pluralform>1 ساعة واحدة</pluralform>
-<pluralform>2 ساعتين</pluralform>
-<pluralform>%x ساعات</pluralform>
-<pluralform>%x ساعة</pluralform>
-<pluralform>%x ساعة</pluralform>
-</target>
-
-<source>Cannot set privilege %x.</source>
-<target>لا يمكن تعيين امتيازات %x.</target>
-
-<source>Unable to suspend system sleep mode.</source>
-<target>تعذر تعليق وضع النوم للنظام.</target>
-
-<source>Cannot change process I/O priorities.</source>
-<target>تعذر تغيير أولويات I/O للعملية.</target>
-
-<source>Unable to shut down the system.</source>
-<target>غير Ùادر على إيقا٠تشغيل النظام.</target>
-
-<source>Checking recycle bin failed for folder %x.</source>
-<target>Ùشل تصÙØ­ سلة المهملات من أجل المل٠%x.</target>
-
-<source>The following XML elements could not be read:</source>
-<target>عناصر XML التالية لا يمكن قراءتها:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>مل٠التكوين %x غير مكتمل. سيتم إعادة تعيين العناصر المÙقودة إلى قيمها الاÙتراضية.</target>
-
-<source>Prepare installation</source>
-<target>الاستعداد للتثبيت</target>
-
-<source>Choose which components you want to install.</source>
-<target>اختر المكونات التي تريد تنصيبها.</target>
-
-<source>Select installation type:</source>
-<target>اختيار نوع التنصيب:</target>
-
-<source>Local</source>
-<target>محلي</target>
-
-<source>Portable</source>
-<target>متنقل</target>
-
-<source>Save settings to "%APPDATA%\FreeFileSync"</source>
-<target>Ø­Ùظ الإعدادات إلى â€â€ª"%APPDATA%\FreeFileSync"</target>
-
-<source>Register FreeFileSync file extensions</source>
-<target>تسجيل امتدادات الملÙات لـ FreeFileSync</target>
-
-<source>Create Explorer context menu entries</source>
-<target>إنشاء مدخلات القوائم المحلية ÙÙŠ متصÙØ­ الملÙات</target>
-
-<source>Save settings in installation directory</source>
-<target>احÙظ إعدادات التثبيت ÙÙŠ مسار التثبيت</target>
-
-<source>Do not write to Registry</source>
-<target>لا تسجل ÙÙŠ ملÙات الرجيستري</target>
-
-<source>Just copy the files</source>
-<target>انسخ الملÙات Ùقط</target>
-
-<source>Choose a directory for installation:</source>
-<target>اختيار مسار التثبيت:</target>
-
-<source>Create shortcuts:</source>
-<target>إنشاء اختصار:</target>
-
-<source>Desktop</source>
-<target>سطح المكتب</target>
-
-<source>Start Menu</source>
-<target></target>
-
-<source>Send To</source>
-<target></target>
-
-<source>Registering FreeFileSync file extensions</source>
-<target>جار تسجيل امتدادات الملÙات لـ FreeFileSync</target>
-
-<source>Unregistering FreeFileSync file extensions</source>
-<target>جار إلغاء تسجيل امتدادات الملÙات لـ FreeFileSync</target>
-
-<source>FreeFileSync Configuration</source>
-<target>تضبيطات FreeFileSync</target>
-
-<source>FreeFileSync Batch File</source>
-<target>المل٠الدÙعي الخاص بـ FreeFileSync</target>
-
-<source>FreeFileSync Synchronization Database</source>
-<target>قاعدة بيانات المزامنة الخاصة بـ FreeFileSync</target>
-
-<source>RealTimeSync Configuration</source>
-<target>تضبيطات RealTimeSync</target>
-
-<source>Edit with FreeFileSync</source>
-<target>تعديل بواسطة FreeFileSync</target>
-
-<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
-<target>لا يمكن تثبيت النسخة المحمولة من FreeFileSync ÙÙŠ مجلد Ùرعي داخل %x.</target>
-
-<source>Please choose the local installation type or select a different folder for installation.</source>
-<target>الرجاء اختيار نوع التثبيت المحلي أو اختيار مجلد آخر للتثبيت.</target>
-
-<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
-<target></target>
-
diff --git a/FreeFileSync/Build/Languages/bulgarian.lng b/FreeFileSync/Build/Languages/bulgarian.lng
index 4d30763f..f016e045 100755
--- a/FreeFileSync/Build/Languages/bulgarian.lng
+++ b/FreeFileSync/Build/Languages/bulgarian.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Път до алтернативен файл GlobalSettings.xml.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>ИнÑталационните файлове Ñа повредени. МолÑ, преинÑталирайте FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>Ðе Ñа намерени Ñледните папки:</target>
@@ -333,12 +336,12 @@ Actual: %y bytes
<source>Cannot find %x.</source>
<target>Ðе е намерен %x.</target>
-<source>Cannot open file %x.</source>
-<target>Ðе може да отвори файл %x.</target>
-
<source>Cannot find device %x.</source>
<target>Ðе е намерено уÑтройÑтво %x.</target>
+<source>Cannot open file %x.</source>
+<target>Ðе може да отвори файл %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>Типа на елемент %x не Ñе поддържа:</target>
@@ -465,6 +468,9 @@ Actual: %y bytes
<source>Total time:</source>
<target>Общо време:</target>
+<source>Cleaning up old log files...</source>
+<target>ИзчиÑтва Ñтарите протоколни файлове...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Грешка при анализ на файл %x, ред %y, колона %z.</target>
@@ -653,6 +659,15 @@ The command is triggered if:
<source>Multiple...</source>
<target>Разни...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Ðе може да запише файловите атрибути на %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x и %y имат различно Ñъдържание.</target>
+
+<source>Data verification error:</source>
+<target>Грешка при Ð²ÐµÑ€Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Ð½Ð° данните:</target>
+
<source>Moving file %x to %y</source>
<target>ПремеÑтва файл %x към %y</target>
@@ -677,14 +692,8 @@ The command is triggered if:
<source>Updating attributes of %x</source>
<target>Ðктуализира атрибутите на %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Ðе може да запише файловите атрибути на %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x и %y имат различно Ñъдържание.</target>
-
-<source>Data verification error:</source>
-<target>Грешка при Ð²ÐµÑ€Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Ð½Ð° данните:</target>
+<source>Source item %x not found</source>
+<target>Изходен елемент %x не е намерен</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>Създава Ñе Volume Shadow Copy за %x...</target>
@@ -746,9 +755,6 @@ The command is triggered if:
<source>System: Shut down</source>
<target>СиÑтема: Изключване</target>
-<source>Cleaning up old log files...</source>
-<target>ИзчиÑтва Ñтарите протоколни файлове...</target>
-
<source>Stopped</source>
<target>СпрÑно</target>
@@ -881,6 +887,12 @@ The command is triggered if:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>МолÑ, изберете папка от локална файлова ÑиÑтема, мрежа или MTP-уÑтройÑтво.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>ИзиÑква FreeFileSync ДарителÑко Издание</target>
+
<source>&Save</source>
<target>&Запази</target>
@@ -1031,6 +1043,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>Отчети лÑтното време</target>
+<source>Performance improvements:</source>
+<target>ПодобрÑване на производителноÑтта:</target>
+
+<source>Parallel file operations:</source>
+<target>Паралелни файлови операции:</target>
+
+<source>How to get best performance?</source>
+<target>Как да Ñе получи най-добра производителноÑÑ‚?</target>
+
<source>Local settings:</source>
<target>Локални наÑтройки:</target>
@@ -1147,15 +1168,6 @@ The command is triggered if:
<source>Directory on server:</source>
<target>Ð”Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ Ð½Ð° Ñървъра:</target>
-<source>Performance improvements:</source>
-<target>ПодобрÑване на производителноÑтта:</target>
-
-<source>How to get best performance?</source>
-<target>Как да Ñе получи най-добра производителноÑÑ‚?</target>
-
-<source>Connections for directory reading:</source>
-<target>Връзки за четене на папка:</target>
-
<source>SFTP channels per connection:</source>
<target>SFTP-канали на връзка:</target>
@@ -1291,8 +1303,17 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>&По подразбиране</target>
-<source>Source code written in C++ using:</source>
-<target>Ð˜Ð·Ñ…Ð¾Ð´Ð½Ð¸Ñ ÐºÐ¾Ð´ е напиÑан на C++ ÑÑŠÑ:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Забележки и Ð¿Ñ€ÐµÐ´Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ñа добре дошли</target>
+
+<source>Home page</source>
+<target>Домашна Ñтраница</target>
+
+<source>FreeFileSync Forum</source>
+<target>FreeFileSync-Форум</target>
+
+<source>Email</source>
+<target>Ел.поща</target>
<source>If you like FreeFileSync:</source>
<target>Ðко хареÑвате FreeFileSync:</target>
@@ -1300,20 +1321,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Support with a donation</source>
<target>Подкрепете Ñ Ð´Ð°Ñ€ÐµÐ½Ð¸Ðµ</target>
-<source>Donation details</source>
-<target>ПодробноÑти за дарение</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>Ðвтоматичното обновÑване бе деактивирано от админиÑтратора.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Забележки и Ð¿Ñ€ÐµÐ´Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ñа добре дошли</target>
-
-<source>Home page</source>
-<target>Домашна Ñтраница</target>
+<source>Donation details</source>
+<target>ПодробноÑти за дарение</target>
-<source>Email</source>
-<target>Ел.поща</target>
+<source>Source code written in C++ using:</source>
+<target>Ð˜Ð·Ñ…Ð¾Ð´Ð½Ð¸Ñ ÐºÐ¾Ð´ е напиÑан на C++ ÑÑŠÑ:</target>
<source>Published under the GNU General Public License</source>
<target>Публикува Ñе по лиценза GNU General Public License</target>
@@ -1402,9 +1417,6 @@ This guarantees a consistent state even in case of a serious error.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x е наличен!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>ИнÑталационните файлове Ñа повредени. МолÑ, преинÑталирайте FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>ÐÑма наличен локален път за %x.</target>
@@ -1627,6 +1639,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Thank you, %x, for your donation and support!</source>
<target>%x, Ð±Ð»Ð°Ð³Ð¾Ð´Ð°Ñ€Ñ Ð·Ð° Вашето дарение и подкрепа!</target>
+<source>Connections</source>
+<target>СвързваниÑ</target>
+
<source>Recommended range:</source>
<target>Препоръчителен обхват:</target>
@@ -1798,9 +1813,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Automatic updates:</source>
<target>Ðвтоматично актуализиране:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>ИзиÑква FreeFileSync ДарителÑко Издание</target>
-
<source>Check for Program Updates</source>
<target>Проверка за нова верÑÐ¸Ñ Ð½Ð° програмата</target>
@@ -1990,6 +2002,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Edit with FreeFileSync</source>
<target>Редактиране Ñ FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>ВмеÑто реклама, ето една животинка.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>ПреноÑимата верÑÐ¸Ñ Ð½Ð° FreeFileSync не може да Ñе инÑталира в подпапка на %x.</target>
@@ -1999,3 +2014,6 @@ This guarantees a consistent state even in case of a serious error.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>ОпциÑта %x инÑталиране е възможна Ñамо за FreeFileSync ДарителÑко Издание.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Вземете ДарителÑко Издание Ñ Ð±Ð¾Ð½ÑƒÑи и помогнете FreeFileSync да оÑтане Ñвободна.</target>
+
diff --git a/FreeFileSync/Build/Languages/chinese_simple.lng b/FreeFileSync/Build/Languages/chinese_simple.lng
index 03a647de..4bef3554 100755
--- a/FreeFileSync/Build/Languages/chinese_simple.lng
+++ b/FreeFileSync/Build/Languages/chinese_simple.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>指å‘一个替代的 GlobalSettings.xml 文件的路径.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>安装文件已æŸå, 请é‡æ–°å®‰è£…FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>无法找到如下文件夹:</target>
@@ -332,12 +335,12 @@ Actual: %y bytes
<source>Cannot find %x.</source>
<target>无法找到 %x.</target>
-<source>Cannot open file %x.</source>
-<target>无法打开文件 %x.</target>
-
<source>Cannot find device %x.</source>
<target>无法找到设备 %x.</target>
+<source>Cannot open file %x.</source>
+<target>无法打开文件 %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>项目 %x 的类型ä¸è¢«æ”¯æŒ:</target>
@@ -460,6 +463,9 @@ Actual: %y bytes
<source>Total time:</source>
<target>总共时间:</target>
+<source>Cleaning up old log files...</source>
+<target>正在清ç†æ—§æ—¥å¿—文件...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>当分æžæ–‡ä»¶ %x , è¡Œ %y, 列 %z 时出错.</target>
@@ -647,6 +653,15 @@ The command is triggered if:
<source>Multiple...</source>
<target>并è”...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>无法写入 %x 的文件属性.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x å’Œ %y 有ç€ä¸åŒçš„内容.</target>
+
+<source>Data verification error:</source>
+<target>æ•°æ®æ ¡éªŒå‡ºé”™:</target>
+
<source>Moving file %x to %y</source>
<target>正在移动文件 %x 到 %y</target>
@@ -671,14 +686,8 @@ The command is triggered if:
<source>Updating attributes of %x</source>
<target>正在更新 %x 的属性</target>
-<source>Cannot write file attributes of %x.</source>
-<target>无法写入 %x 的文件属性.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x å’Œ %y 有ç€ä¸åŒçš„内容.</target>
-
-<source>Data verification error:</source>
-<target>æ•°æ®æ ¡éªŒå‡ºé”™:</target>
+<source>Source item %x not found</source>
+<target>æ¥æºé¡¹ç›® %x 未找到</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>正在为 %x 创建一个å·å½±å‰¯æœ¬...</target>
@@ -740,9 +749,6 @@ The command is triggered if:
<source>System: Shut down</source>
<target>系统: 关机</target>
-<source>Cleaning up old log files...</source>
-<target>正在清ç†æ—§æ—¥å¿—文件...</target>
-
<source>Stopped</source>
<target>å·²åœæ­¢</target>
@@ -874,6 +880,12 @@ The command is triggered if:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>请选择在本地文件系统, 网络或MTP设备上的文件夹.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>需è¦FreeFileSyncæ赠版</target>
+
<source>&Save</source>
<target>ä¿å­˜(&S)</target>
@@ -1024,6 +1036,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>处ç†å¤ä»¤æ—¶</target>
+<source>Performance improvements:</source>
+<target>性能改进:</target>
+
+<source>Parallel file operations:</source>
+<target>并行文件æ“作:</target>
+
+<source>How to get best performance?</source>
+<target>如何å–得最佳性能?</target>
+
<source>Local settings:</source>
<target>本地设置:</target>
@@ -1140,15 +1161,6 @@ The command is triggered if:
<source>Directory on server:</source>
<target>æœåŠ¡å™¨ä¸Šçš„目录:</target>
-<source>Performance improvements:</source>
-<target>性能改进:</target>
-
-<source>How to get best performance?</source>
-<target>如何å–得最佳性能?</target>
-
-<source>Connections for directory reading:</source>
-<target>用于目录读å–的连接:</target>
-
<source>SFTP channels per connection:</source>
<target>æ¯ä¸ªè¿žæŽ¥çš„SFTP通é“æ•°:</target>
@@ -1281,8 +1293,17 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>默认(&D)</target>
-<source>Source code written in C++ using:</source>
-<target>æºä»£ç ä½¿ç”¨å¦‚下工具由C++写æˆ:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>欢迎å馈æ„è§å’Œæ出建议</target>
+
+<source>Home page</source>
+<target>主页</target>
+
+<source>FreeFileSync Forum</source>
+<target>FreeFileSync 论å›</target>
+
+<source>Email</source>
+<target>邮箱</target>
<source>If you like FreeFileSync:</source>
<target>如果你喜欢 FreeFileSync:</target>
@@ -1290,20 +1311,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Support with a donation</source>
<target>通过æ款æ¥æ”¯æŒæˆ‘们</target>
-<source>Donation details</source>
-<target>æ款详情</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>自动更新程åºå·²ç”±ç®¡ç†å‘˜ç¦ç”¨.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>欢迎å馈æ„è§å’Œæ出建议</target>
-
-<source>Home page</source>
-<target>主页</target>
+<source>Donation details</source>
+<target>æ款详情</target>
-<source>Email</source>
-<target>邮箱</target>
+<source>Source code written in C++ using:</source>
+<target>æºä»£ç ä½¿ç”¨å¦‚下工具由C++写æˆ:</target>
<source>Published under the GNU General Public License</source>
<target>在GNU通用公共许å¯ä¸‹å‘布</target>
@@ -1392,9 +1407,6 @@ This guarantees a consistent state even in case of a serious error.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x å¯ç”¨!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>安装文件已æŸå, 请é‡æ–°å®‰è£…FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>本地路径ä¸é€‚用于 %x.</target>
@@ -1613,6 +1625,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Thank you, %x, for your donation and support!</source>
<target>谢谢你, %x , 对我们的æ款和支æŒ!</target>
+<source>Connections</source>
+<target>连接数</target>
+
<source>Recommended range:</source>
<target>建议的范围:</target>
@@ -1781,9 +1796,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Automatic updates:</source>
<target>自动更新:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>需è¦FreeFileSyncæ赠版</target>
-
<source>Check for Program Updates</source>
<target>检查程åºçš„æ›´æ–°</target>
@@ -1971,6 +1983,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Edit with FreeFileSync</source>
<target>使用 FreeFileSync 编辑</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>这里是一åªåŠ¨ç‰©, 而ä¸æ˜¯å¹¿å‘Š.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>FreeFileSync 便æºç‰ˆæ— æ³•å®‰è£…到 %x 的一个å­ç›®å½•.</target>
@@ -1980,3 +1995,6 @@ This guarantees a consistent state even in case of a serious error.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>安装选项 %x åªåœ¨ FreeFileSync æ赠版有效.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>获å–有é¢å¤–功能的æ赠版本, 并帮助ä¿æŒ FreeFileSync 无广告.</target>
+
diff --git a/FreeFileSync/Build/Languages/chinese_traditional.lng b/FreeFileSync/Build/Languages/chinese_traditional.lng
index defe3748..6550e726 100755
--- a/FreeFileSync/Build/Languages/chinese_traditional.lng
+++ b/FreeFileSync/Build/Languages/chinese_traditional.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>備用的GlobalSettings.xml檔案路徑。</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>安è£æª”å·²æ壞。請é‡æ–°å®‰è£FreeFileSync。</target>
+
<source>Cannot find the following folders:</source>
<target>找ä¸åˆ°ä¸‹åˆ—資料夾:</target>
@@ -332,12 +335,12 @@ Actual: %y bytes
<source>Cannot find %x.</source>
<target>找ä¸åˆ° %x。</target>
-<source>Cannot open file %x.</source>
-<target>無法開啟檔案 %x。</target>
-
<source>Cannot find device %x.</source>
<target>找ä¸åˆ°è£ç½® %x。</target>
+<source>Cannot open file %x.</source>
+<target>無法開啟檔案 %x。</target>
+
<source>Type of item %x is not supported:</source>
<target>項目類型 %x ä¸è¢«æ”¯æ´ï¼š</target>
@@ -460,6 +463,9 @@ Actual: %y bytes
<source>Total time:</source>
<target>全部時間:</target>
+<source>Cleaning up old log files...</source>
+<target>清ç†èˆŠæ—¥èªŒæª”…</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>è§£æž %x 檔案,第 %y 列,第 %z 行出ç¾éŒ¯èª¤ã€‚</target>
@@ -647,6 +653,15 @@ The command is triggered if:
<source>Multiple...</source>
<target>多個…</target>
+<source>Cannot write file attributes of %x.</source>
+<target>無法寫入 %x 的檔案屬性。</target>
+
+<source>%x and %y have different content.</source>
+<target>%x å’Œ %y 具有ä¸åŒçš„內容。</target>
+
+<source>Data verification error:</source>
+<target>資料驗證錯誤:</target>
+
<source>Moving file %x to %y</source>
<target>正在移動檔案 %x 到 %y</target>
@@ -671,14 +686,8 @@ The command is triggered if:
<source>Updating attributes of %x</source>
<target>正在更新 %x 的屬性</target>
-<source>Cannot write file attributes of %x.</source>
-<target>無法寫入 %x 的檔案屬性。</target>
-
-<source>%x and %y have different content.</source>
-<target>%x å’Œ %y 具有ä¸åŒçš„內容。</target>
-
-<source>Data verification error:</source>
-<target>資料驗證錯誤:</target>
+<source>Source item %x not found</source>
+<target>找ä¸åˆ°ä¾†æºé …ç›® %x</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>正在建立ç£ç¢Ÿå€é™°å½±è¤‡è£½ç‚º %x…</target>
@@ -740,9 +749,6 @@ The command is triggered if:
<source>System: Shut down</source>
<target>系統:關機</target>
-<source>Cleaning up old log files...</source>
-<target>清ç†èˆŠæ—¥èªŒæª”…</target>
-
<source>Stopped</source>
<target>å·²åœæ­¢</target>
@@ -874,6 +880,12 @@ The command is triggered if:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>è«‹é¸æ“‡ä¸€å€‹æœ¬æ©Ÿæª”案系統ã€ç¶²è·¯æˆ–MTPè£ç½®ä¸Šçš„資料夾。</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>需è¦FreeFileSync贊助版</target>
+
<source>&Save</source>
<target>儲存(&S)</target>
@@ -1024,6 +1036,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>處ç†æ—¥å…‰ç¯€ç´„時間</target>
+<source>Performance improvements:</source>
+<target>效能改善:</target>
+
+<source>Parallel file operations:</source>
+<target>並列檔案æ“作:</target>
+
+<source>How to get best performance?</source>
+<target>如何ç²å¾—最佳效能?</target>
+
<source>Local settings:</source>
<target>本機設定:</target>
@@ -1140,15 +1161,6 @@ The command is triggered if:
<source>Directory on server:</source>
<target>在伺æœå™¨ä¸Šçš„目錄:</target>
-<source>Performance improvements:</source>
-<target>效能改善:</target>
-
-<source>How to get best performance?</source>
-<target>如何ç²å¾—最佳效能?</target>
-
-<source>Connections for directory reading:</source>
-<target>用於讀å–目錄的連接:</target>
-
<source>SFTP channels per connection:</source>
<target>æ¯å€‹é€£æŽ¥SFTP通é“數:</target>
@@ -1284,8 +1296,17 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>é è¨­(&D)</target>
-<source>Source code written in C++ using:</source>
-<target>使用C++編寫的原始碼:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>æ­¡è¿Žå映æ„見和建議</target>
+
+<source>Home page</source>
+<target>主é </target>
+
+<source>FreeFileSync Forum</source>
+<target>FreeFileSync論壇</target>
+
+<source>Email</source>
+<target>ä¿¡ç®±</target>
<source>If you like FreeFileSync:</source>
<target>如果您喜歡FreeFileSync:</target>
@@ -1293,20 +1314,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Support with a donation</source>
<target>贊助與支æŒ</target>
-<source>Donation details</source>
-<target>贊助詳情</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>自動更新程å¼å·²è¢«ç®¡ç†å“¡ç¦ç”¨ã€‚</target>
-<source>Feedback and suggestions are welcome</source>
-<target>æ­¡è¿Žå映æ„見和建議</target>
-
-<source>Home page</source>
-<target>主é </target>
+<source>Donation details</source>
+<target>贊助詳情</target>
-<source>Email</source>
-<target>ä¿¡ç®±</target>
+<source>Source code written in C++ using:</source>
+<target>使用C++編寫的原始碼:</target>
<source>Published under the GNU General Public License</source>
<target>在GNU通用公共許å¯è­‰ä¸‹ç™¼ä½ˆ</target>
@@ -1395,9 +1410,6 @@ This guarantees a consistent state even in case of a serious error.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x å¯ç”¨ï¼</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>安è£æª”å·²æ壞。請é‡æ–°å®‰è£FreeFileSync。</target>
-
<source>Local path not available for %x.</source>
<target>本機路徑ä¸é©ç”¨æ–¼ %x。</target>
@@ -1616,6 +1628,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Thank you, %x, for your donation and support!</source>
<target>%x, æ„Ÿè¬æ‚¨çš„贊助與支æŒï¼</target>
+<source>Connections</source>
+<target>連線數</target>
+
<source>Recommended range:</source>
<target>建議範åœï¼š</target>
@@ -1784,9 +1799,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Automatic updates:</source>
<target>自動更新:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>需è¦FreeFileSync贊助版</target>
-
<source>Check for Program Updates</source>
<target>檢查程å¼æ›´æ–°</target>
@@ -1974,6 +1986,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Edit with FreeFileSync</source>
<target>使用FreeFileSync進行編輯</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>這是一隻動物,而ä¸æ˜¯å»£å‘Šã€‚</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>FreeFileSyncå¯æ”œå¼ç‰ˆæœ¬ç„¡æ³•å®‰è£åˆ° %x çš„å­è³‡æ–™å¤¾ã€‚</target>
@@ -1983,3 +1998,6 @@ This guarantees a consistent state even in case of a serious error.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>%x 安è£é¸é …僅在FreeFileSync贊助版å¯ç”¨ã€‚</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>ç²å¾—贊助版的çŽå‹µåŠŸèƒ½ï¼Œä¸¦å”助維æŒFreeFileSync無廣告。</target>
+
diff --git a/FreeFileSync/Build/Languages/croatian.lng b/FreeFileSync/Build/Languages/croatian.lng
index 7748df23..dce705ac 100755
--- a/FreeFileSync/Build/Languages/croatian.lng
+++ b/FreeFileSync/Build/Languages/croatian.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Putanja do alternativne GlobalSettings.xml datoteke.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Instalacijske datoteke su oštećene. Molimo ponovno instalirajte FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>Ne mogu pronaći slijedeće mape:</target>
@@ -334,12 +337,12 @@ Stvarno: %y bajta
<source>Cannot find %x.</source>
<target>Nije moguće pronaći %x.</target>
-<source>Cannot open file %x.</source>
-<target>Ne mogu otvoriti datoteku %x.</target>
-
<source>Cannot find device %x.</source>
<target>Nije moguće pronaći uređaj %x.</target>
+<source>Cannot open file %x.</source>
+<target>Ne mogu otvoriti datoteku %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>Vrsta stavke %x koji nije podržan:</target>
@@ -470,6 +473,9 @@ Stvarno: %y bajta
<source>Total time:</source>
<target>Ukupno vrijeme:</target>
+<source>Cleaning up old log files...</source>
+<target>Čistim stare datoteke o izvješću...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Greška u analizi datoteke %x, red %y, stupac %z.</target>
@@ -659,6 +665,15 @@ Naredba će biti pokrenuta ako se:
<source>Multiple...</source>
<target>Mnogostruko...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Ne mogu zapisati svojstva od %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x. i %y imaju razliÄit sadržaj.</target>
+
+<source>Data verification error:</source>
+<target>Pogreška verificiranja podataka:</target>
+
<source>Moving file %x to %y</source>
<target>Premještanje datoteke %x u %y</target>
@@ -683,14 +698,8 @@ Naredba će biti pokrenuta ako se:
<source>Updating attributes of %x</source>
<target>Obnavljam atribute od %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Ne mogu zapisati svojstva od %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x. i %y imaju razliÄit sadržaj.</target>
-
-<source>Data verification error:</source>
-<target>Pogreška verificiranja podataka:</target>
+<source>Source item %x not found</source>
+<target>Izvorna datoteka %x nije pronađena</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>Kreiranje Volume Shadow Copy za %x...</target>
@@ -752,9 +761,6 @@ Naredba će biti pokrenuta ako se:
<source>System: Shut down</source>
<target>Sistem: IskljuÄivanje</target>
-<source>Cleaning up old log files...</source>
-<target>Čistim stare datoteke o izvješću...</target>
-
<source>Stopped</source>
<target>Zaustavljeno</target>
@@ -888,6 +894,12 @@ Naredba će biti pokrenuta ako se:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Molimo odaberite mapu na lokalnom disku, mreži ili MTP uređaju.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Zahtjeva FreeFileSync Donatorsku Verziju</target>
+
<source>&Save</source>
<target>&Spremi</target>
@@ -1038,6 +1050,15 @@ Naredba će biti pokrenuta ako se:
<source>Handle daylight saving time</source>
<target>Upravljaj ljetnim raÄunanjem vremena</target>
+<source>Performance improvements:</source>
+<target>Poboljšanje perfomansi:</target>
+
+<source>Parallel file operations:</source>
+<target>Paralelne operacije datotekama:</target>
+
+<source>How to get best performance?</source>
+<target>Kako dobiti najbolje perfomanse?</target>
+
<source>Local settings:</source>
<target>Lokalne postavke:</target>
@@ -1154,15 +1175,6 @@ Naredba će biti pokrenuta ako se:
<source>Directory on server:</source>
<target>Mapa na serveru:</target>
-<source>Performance improvements:</source>
-<target>Poboljšanje perfomansi:</target>
-
-<source>How to get best performance?</source>
-<target>Kako dobiti najbolje perfomanse?</target>
-
-<source>Connections for directory reading:</source>
-<target>Broj veza za Äitanje mapa:</target>
-
<source>SFTP channels per connection:</source>
<target>SFTP kanala po konekciji:</target>
@@ -1298,8 +1310,17 @@ Ovo garantira stabilno stanje Äak u sluÄaju ozbiljne greÅ¡ke.
<source>&Default</source>
<target>&Zadano</target>
-<source>Source code written in C++ using:</source>
-<target>Izvorni kod napisan u C++ uz korištenje:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Povratne informacije i prijedlozi su dobrodošli</target>
+
+<source>Home page</source>
+<target>Web stranica</target>
+
+<source>FreeFileSync Forum</source>
+<target>FreeFileSync Forum</target>
+
+<source>Email</source>
+<target>Email</target>
<source>If you like FreeFileSync:</source>
<target>Ako volite FreeFileSync:</target>
@@ -1307,20 +1328,14 @@ Ovo garantira stabilno stanje Äak u sluÄaju ozbiljne greÅ¡ke.
<source>Support with a donation</source>
<target>Podržite nas donacijom</target>
-<source>Donation details</source>
-<target>Detalji donacije</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>Auto updater je onemogućen od strane administratora.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Povratne informacije i prijedlozi su dobrodošli</target>
-
-<source>Home page</source>
-<target>Web stranica</target>
+<source>Donation details</source>
+<target>Detalji donacije</target>
-<source>Email</source>
-<target>Email</target>
+<source>Source code written in C++ using:</source>
+<target>Izvorni kod napisan u C++ uz korištenje:</target>
<source>Published under the GNU General Public License</source>
<target>Objavljeno pod licencom GNU General Public</target>
@@ -1409,9 +1424,6 @@ Ovo garantira stabilno stanje Äak u sluÄaju ozbiljne greÅ¡ke.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x je dostupan!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Instalacijske datoteke su oštećene. Molimo ponovno instalirajte FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>Nije dostupna lokalna putanja za %x.</target>
@@ -1638,6 +1650,9 @@ Ovo garantira stabilno stanje Äak u sluÄaju ozbiljne greÅ¡ke.
<source>Thank you, %x, for your donation and support!</source>
<target>Hvala Vam, %x, na vašoj donaciji i podršci!</target>
+<source>Connections</source>
+<target>Konekcije</target>
+
<source>Recommended range:</source>
<target>Predloženi raspon:</target>
@@ -1812,9 +1827,6 @@ Ovo garantira stabilno stanje Äak u sluÄaju ozbiljne greÅ¡ke.
<source>Automatic updates:</source>
<target>Automatsko ažuriranje:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Zahtjeva FreeFileSync Donatorsku Verziju</target>
-
<source>Check for Program Updates</source>
<target>Provjeri za nadogradnje programa</target>
@@ -2006,6 +2018,9 @@ Ovo garantira stabilno stanje Äak u sluÄaju ozbiljne greÅ¡ke.
<source>Edit with FreeFileSync</source>
<target>Uredi pomoću FreeFileSynca</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>Umjesto reklama, prikazujemo vam životinje.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>FreeFileSync prijenosna verzija se ne može instalirati u podfolder od %x.</target>
@@ -2015,3 +2030,6 @@ Ovo garantira stabilno stanje Äak u sluÄaju ozbiljne greÅ¡ke.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>%x opcija instalacije je dostupna samo u FreeFileSync Donatorskoj verziji.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Preuzmite donatorsku verziju sa bonus opcijama i pomognite da FreeFileSync ostane bez reklama.</target>
+
diff --git a/FreeFileSync/Build/Languages/czech.lng b/FreeFileSync/Build/Languages/czech.lng
index 499fe4e3..6c04eadc 100755
--- a/FreeFileSync/Build/Languages/czech.lng
+++ b/FreeFileSync/Build/Languages/czech.lng
@@ -7,6 +7,9 @@
<plural_definition>n==1 ? 0 : n>=2 && n<=4 ? 1 : 2</plural_definition>
</header>
+<source>Defined by context of use</source>
+<target></target>
+
<source>Both sides have changed since last synchronization.</source>
<target>Došlo ke změně obou stran od poslední synchronizace.</target>
@@ -112,6 +115,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Cesta k alternativnímu souboru GlobalSettings.xml.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Instalace programu je poškozena. Prosím nainstalujte znovu FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>Nelze najít následující složky:</target>
@@ -334,12 +340,12 @@ Aktuálně: %y b
<source>Cannot find %x.</source>
<target>Nelze najít %x.</target>
-<source>Cannot open file %x.</source>
-<target>Nelze otevřít soubor %x.</target>
-
<source>Cannot find device %x.</source>
<target>Nelze nalézt zařízení %x.</target>
+<source>Cannot open file %x.</source>
+<target>Nelze otevřít soubor %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>Typ položky %x není podporován:</target>
@@ -470,6 +476,9 @@ Aktuálně: %y b
<source>Total time:</source>
<target>Celkový Äas:</target>
+<source>Cleaning up old log files...</source>
+<target>Odstraňování starých žurnálů...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Chyba zpracování souboru %x: na řádku %y ve sloupci %z.</target>
@@ -659,6 +668,15 @@ Příkaz je spuštěn když:
<source>Multiple...</source>
<target>Různé...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Nelze zapsat vlastnosti souboru %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x a %y mají odlišný obsah.</target>
+
+<source>Data verification error:</source>
+<target>Chyba verifikace dat:</target>
+
<source>Moving file %x to %y</source>
<target>Přesouvání souboru %x do %y</target>
@@ -683,14 +701,8 @@ Příkaz je spuštěn když:
<source>Updating attributes of %x</source>
<target>Aktualizace vlastností souboru %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Nelze zapsat vlastnosti souboru %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x a %y mají odlišný obsah.</target>
-
-<source>Data verification error:</source>
-<target>Chyba verifikace dat:</target>
+<source>Source item %x not found</source>
+<target>Zdrojová položka %x nenalezena.</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>Vytváření Stínové kopie pro %x...</target>
@@ -752,9 +764,6 @@ Příkaz je spuštěn když:
<source>System: Shut down</source>
<target>Vypnutí poÄítaÄe</target>
-<source>Cleaning up old log files...</source>
-<target>Odstraňování starých žurnálů...</target>
-
<source>Stopped</source>
<target>Zastaveno</target>
@@ -888,6 +897,9 @@ Příkaz je spuštěn když:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Prosím vyberte složku na místním souborovém systému, síti nebo multimediální zařízení.</target>
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Je vyžadována předplacená verze FreeFileSync Donation Edition</target>
+
<source>&Save</source>
<target>&Uložit</target>
@@ -1038,6 +1050,15 @@ Příkaz je spuštěn když:
<source>Handle daylight saving time</source>
<target>Používat letní Äas</target>
+<source>Performance improvements:</source>
+<target>Vylepšení rychlosti:</target>
+
+<source>Parallel file operations:</source>
+<target>Paralelní operace se soubory:</target>
+
+<source>How to get best performance?</source>
+<target>Jak získat nejlepší rychlost?</target>
+
<source>Local settings:</source>
<target>Nastavení:</target>
@@ -1154,15 +1175,6 @@ Příkaz je spuštěn když:
<source>Directory on server:</source>
<target>Adresář na serveru:</target>
-<source>Performance improvements:</source>
-<target>Vylepšení rychlosti:</target>
-
-<source>How to get best performance?</source>
-<target>Jak získat nejlepší rychlost?</target>
-
-<source>Connections for directory reading:</source>
-<target>Spojení pro Ätení adresáře:</target>
-
<source>SFTP channels per connection:</source>
<target>SFTP kanálů na spojení:</target>
@@ -1295,8 +1307,17 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>&Předdefinované</target>
-<source>Source code written in C++ using:</source>
-<target>Zdrojový kód byl napsán kompletně v C++ pomocí:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Komentáře a náměty jsou vždy vítány</target>
+
+<source>Home page</source>
+<target>Navštivte</target>
+
+<source>FreeFileSync Forum</source>
+<target>Diskuzní fórum FreeFileSync</target>
+
+<source>Email</source>
+<target>Napište</target>
<source>If you like FreeFileSync:</source>
<target>Pokud se Vám FreeFileSync líbí:</target>
@@ -1304,20 +1325,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Support with a donation</source>
<target>Podpořit darem</target>
-<source>Donation details</source>
-<target>Podrobnosti</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>Automatická aktualizace byla správcem zakázána.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Komentáře a náměty jsou vždy vítány</target>
-
-<source>Home page</source>
-<target>Navštivte</target>
+<source>Donation details</source>
+<target>Podrobnosti</target>
-<source>Email</source>
-<target>Napište</target>
+<source>Source code written in C++ using:</source>
+<target>Zdrojový kód byl napsán kompletně v C++ pomocí:</target>
<source>Published under the GNU General Public License</source>
<target>Vydáno pod GNU General Public License (GPL)</target>
@@ -1406,9 +1421,6 @@ This guarantees a consistent state even in case of a serious error.
<source>FreeFileSync %x is available!</source>
<target>Je k dostupná verze FreeFileSync %x!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Instalace programu je poškozena. Prosím nainstalujte znovu FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>Místní cesta není k dispozici pro %x.</target>
@@ -1635,6 +1647,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Thank you, %x, for your donation and support!</source>
<target>Děkuji, %x, za příspěvek a podporu!</target>
+<source>Connections</source>
+<target>Připojení</target>
+
<source>Recommended range:</source>
<target>DoporuÄený rozsah:</target>
@@ -1809,9 +1824,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Automatic updates:</source>
<target>Automatická aktualizace:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Je vyžadována předplacená verze FreeFileSync Donation Edition</target>
-
<source>Check for Program Updates</source>
<target>Hledání aktualizací programu</target>
@@ -2003,6 +2015,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Edit with FreeFileSync</source>
<target>Upravit v FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>Místo reklamy zvířátko.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>Přenosná verze FreeFileSync nemůže být instalována do složky %x.</target>
@@ -2012,3 +2027,6 @@ This guarantees a consistent state even in case of a serious error.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>%x instalace je k dispozici pouze v předplacené verzi FreeFileSync Donation Edition.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Získat předplacenou verzi s funikcemi navíc a pomoci tím udržet FreeFileSync bez reklam.</target>
+
diff --git a/FreeFileSync/Build/Languages/danish.lng b/FreeFileSync/Build/Languages/danish.lng
index a0822e1d..20764c0c 100755
--- a/FreeFileSync/Build/Languages/danish.lng
+++ b/FreeFileSync/Build/Languages/danish.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Sti til alternativ GlobalSettings.xml fil.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Ødelagte installationsfiler. Geninstaller FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>Kan ikke finde følgende mapper:</target>
@@ -333,12 +336,12 @@ Aktuel: %y byte
<source>Cannot find %x.</source>
<target>Kan ikke finde %x.</target>
-<source>Cannot open file %x.</source>
-<target>Filen %x kan ikke åbnes.</target>
-
<source>Cannot find device %x.</source>
<target>Kan ikke finde enheden %x.</target>
+<source>Cannot open file %x.</source>
+<target>Filen %x kan ikke åbnes.</target>
+
<source>Type of item %x is not supported:</source>
<target>Filtypen %x understøttes ikke:</target>
@@ -465,6 +468,9 @@ Aktuel: %y byte
<source>Total time:</source>
<target>Samlet tid:</target>
+<source>Cleaning up old log files...</source>
+<target>Fjerner gamle logfiler...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Behandlingsfejl i filen %x, række %y, kolonne %z.</target>
@@ -653,6 +659,15 @@ Kommandoen udføres hvis:
<source>Multiple...</source>
<target>Flere...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Kan ikke skrive filattributter til %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x og %y har forskelligt indhold.</target>
+
+<source>Data verification error:</source>
+<target>Verifikationsfejl:</target>
+
<source>Moving file %x to %y</source>
<target>Flytter filen %x til %y</target>
@@ -677,14 +692,8 @@ Kommandoen udføres hvis:
<source>Updating attributes of %x</source>
<target>Opdaterer attributter for %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Kan ikke skrive filattributter til %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x og %y har forskelligt indhold.</target>
-
-<source>Data verification error:</source>
-<target>Verifikationsfejl:</target>
+<source>Source item %x not found</source>
+<target>Kildeemne %x ikke fundet</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>Opretter VSS kopi for %x...</target>
@@ -746,9 +755,6 @@ Kommandoen udføres hvis:
<source>System: Shut down</source>
<target>System: Luk</target>
-<source>Cleaning up old log files...</source>
-<target>Fjerner gamle logfiler...</target>
-
<source>Stopped</source>
<target>Afbrudt</target>
@@ -881,6 +887,12 @@ Kommandoen udføres hvis:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Vælg lokal mappe, netværksmappe eller mappe på MTP enhed.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Kræver FreeFileSync donationsudgave</target>
+
<source>&Save</source>
<target>&Gem</target>
@@ -1031,6 +1043,15 @@ Kommandoen udføres hvis:
<source>Handle daylight saving time</source>
<target>Tag hensyn til sommertid</target>
+<source>Performance improvements:</source>
+<target>Ydelsesforbedring:</target>
+
+<source>Parallel file operations:</source>
+<target>Parallelle filhandlinger:</target>
+
+<source>How to get best performance?</source>
+<target>Hvordan får du bedste ydelse?</target>
+
<source>Local settings:</source>
<target>Lokale indstillinger:</target>
@@ -1147,15 +1168,6 @@ Kommandoen udføres hvis:
<source>Directory on server:</source>
<target>Servermappe:</target>
-<source>Performance improvements:</source>
-<target>Ydelsesforbedring:</target>
-
-<source>How to get best performance?</source>
-<target>Hvordan får du bedste ydelse?</target>
-
-<source>Connections for directory reading:</source>
-<target>Forbindelser til mappelæsning:</target>
-
<source>SFTP channels per connection:</source>
<target>SFTP kanaler pr. forbindelse:</target>
@@ -1291,8 +1303,17 @@ Sikrer processen ved alvorlige fejl.
<source>&Default</source>
<target>S&tandard</target>
-<source>Source code written in C++ using:</source>
-<target>Kildekoden er skrevet i C++ med hjælp fra:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Kritik og forslag er meget velkomne</target>
+
+<source>Home page</source>
+<target>Hjemmeside</target>
+
+<source>FreeFileSync Forum</source>
+<target>FreeFileSync forum</target>
+
+<source>Email</source>
+<target>Email</target>
<source>If you like FreeFileSync:</source>
<target>Er du glad for FreeFileSync:</target>
@@ -1300,20 +1321,14 @@ Sikrer processen ved alvorlige fejl.
<source>Support with a donation</source>
<target>Donér for at støtte</target>
-<source>Donation details</source>
-<target>Donationsdetaljer</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>Autoopdatering deaktiveret af administrator.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Kritik og forslag er meget velkomne</target>
-
-<source>Home page</source>
-<target>Hjemmeside</target>
+<source>Donation details</source>
+<target>Donationsdetaljer</target>
-<source>Email</source>
-<target>Email</target>
+<source>Source code written in C++ using:</source>
+<target>Kildekoden er skrevet i C++ med hjælp fra:</target>
<source>Published under the GNU General Public License</source>
<target>Udgivet under GNU General Public Licence</target>
@@ -1402,9 +1417,6 @@ Sikrer processen ved alvorlige fejl.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x er udkommet!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Ødelagte installationsfiler. Geninstaller FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>Placeringen er ikke tilgængelig for %x.</target>
@@ -1627,6 +1639,9 @@ Sikrer processen ved alvorlige fejl.
<source>Thank you, %x, for your donation and support!</source>
<target>Tak %x for donationen og støtten!</target>
+<source>Connections</source>
+<target>Forbindelser</target>
+
<source>Recommended range:</source>
<target>Anbefalet interval:</target>
@@ -1798,9 +1813,6 @@ Sikrer processen ved alvorlige fejl.
<source>Automatic updates:</source>
<target>Automatiske opdateringer:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Kræver FreeFileSync donationsudgave</target>
-
<source>Check for Program Updates</source>
<target>Søg efter opdatering</target>
@@ -1990,6 +2002,9 @@ Sikrer processen ved alvorlige fejl.
<source>Edit with FreeFileSync</source>
<target>Rediger med FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>Her er et dyr, i stedet for annoncer.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>FreeFileSync portable kan ikke installeres i en undermappe til %x.</target>
@@ -1999,3 +2014,6 @@ Sikrer processen ved alvorlige fejl.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>Installationsmuligheden %x er kun mulig i FreeFileSync Donation Edition.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Hent donationsudgave med flere funktioner, og hold FreeFileSync fri for annoncer.</target>
+
diff --git a/FreeFileSync/Build/Languages/dutch.lng b/FreeFileSync/Build/Languages/dutch.lng
index 99875a32..321314c7 100755
--- a/FreeFileSync/Build/Languages/dutch.lng
+++ b/FreeFileSync/Build/Languages/dutch.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Bestandspad naar plaatsvervangend GlobalSettings.xml bestand.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Installatiebestanden zijn beschadigd. Installeer FreeFileSync opnieuw.</target>
+
<source>Cannot find the following folders:</source>
<target>De volgende mappen konden niet worden gevonden:</target>
@@ -333,12 +336,12 @@ Werkelijk: %y bytes
<source>Cannot find %x.</source>
<target>Kan %x niet vinden.</target>
-<source>Cannot open file %x.</source>
-<target>Het bestand %x kan niet geopend worden.</target>
-
<source>Cannot find device %x.</source>
<target>Kan apparaat %x niet vinden.</target>
+<source>Cannot open file %x.</source>
+<target>Het bestand %x kan niet geopend worden.</target>
+
<source>Type of item %x is not supported:</source>
<target>Dit itemtype %x wordt niet ondersteund:</target>
@@ -465,6 +468,9 @@ Werkelijk: %y bytes
<source>Total time:</source>
<target>Totale tijd:</target>
+<source>Cleaning up old log files...</source>
+<target>Opruimen oude logboekbestanden...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Fout bij het analyseren van bestand %x, rij %y, kolom %z.</target>
@@ -653,6 +659,15 @@ De opdracht wordt geactiveerd als:
<source>Multiple...</source>
<target>Meerdere...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>De bestandskenmerken voor %x kunnen niet geschreven worden.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x en %y hebben verschillende inhoud.</target>
+
+<source>Data verification error:</source>
+<target>Gegevens verificatiefout:</target>
+
<source>Moving file %x to %y</source>
<target>Bestand %x verplaatsen naar %y</target>
@@ -677,14 +692,8 @@ De opdracht wordt geactiveerd als:
<source>Updating attributes of %x</source>
<target>Kenmerken van %x bijwerken</target>
-<source>Cannot write file attributes of %x.</source>
-<target>De bestandskenmerken voor %x kunnen niet geschreven worden.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x en %y hebben verschillende inhoud.</target>
-
-<source>Data verification error:</source>
-<target>Gegevens verificatiefout:</target>
+<source>Source item %x not found</source>
+<target>Bronitem %x niet gevonden</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>Maken van een volume schaduwkopie voor %x...</target>
@@ -746,9 +755,6 @@ De opdracht wordt geactiveerd als:
<source>System: Shut down</source>
<target>Systeem: Uitschakelen</target>
-<source>Cleaning up old log files...</source>
-<target>Opruimen oude logboekbestanden...</target>
-
<source>Stopped</source>
<target>Gestopt</target>
@@ -881,6 +887,12 @@ De opdracht wordt geactiveerd als:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Selecteer een map op een lokaal bestandssysteem, netwerk of een MTP-apparaat.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Vereist FreeFileSync donatie Editie</target>
+
<source>&Save</source>
<target>&Opslaan</target>
@@ -1031,6 +1043,15 @@ De opdracht wordt geactiveerd als:
<source>Handle daylight saving time</source>
<target>Zomertijd gebruiken</target>
+<source>Performance improvements:</source>
+<target>Prestatieverbeteringen:</target>
+
+<source>Parallel file operations:</source>
+<target>Parallelle bestandsbewerkingen:</target>
+
+<source>How to get best performance?</source>
+<target>Hoe kom je aan de beste prestaties?</target>
+
<source>Local settings:</source>
<target>Lokale instellingen:</target>
@@ -1147,15 +1168,6 @@ De opdracht wordt geactiveerd als:
<source>Directory on server:</source>
<target>Map op de server:</target>
-<source>Performance improvements:</source>
-<target>Prestatieverbeteringen:</target>
-
-<source>How to get best performance?</source>
-<target>Hoe kom je aan de beste prestaties?</target>
-
-<source>Connections for directory reading:</source>
-<target>Verbindingen voor het lezen van mappen:</target>
-
<source>SFTP channels per connection:</source>
<target>SFTP kanalen per verbinding:</target>
@@ -1199,7 +1211,7 @@ De opdracht wordt geactiveerd als:
<target>Minimaliseren naar systeemvak</target>
<source>When finished:</source>
-<target>Wanneer je gereed bent:</target>
+<target>Indien gereed:</target>
<source>Auto-close</source>
<target>Automatisch sluiten</target>
@@ -1291,8 +1303,17 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<source>&Default</source>
<target>&Standaard</target>
-<source>Source code written in C++ using:</source>
-<target>Broncode werd in C++ geschreven met:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Terugkoppeling en suggesties zijn welkom</target>
+
+<source>Home page</source>
+<target>Startpagina</target>
+
+<source>FreeFileSync Forum</source>
+<target>FreeFileSync Forum</target>
+
+<source>Email</source>
+<target>Email</target>
<source>If you like FreeFileSync:</source>
<target>Als FreeFileSync je bevalt:</target>
@@ -1300,20 +1321,14 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<source>Support with a donation</source>
<target>Steunen met een donatie</target>
-<source>Donation details</source>
-<target>Donatie details</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>De automatische updater werd uitgeschakeld door de beheerder.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Terugkoppeling en suggesties zijn welkom</target>
-
-<source>Home page</source>
-<target>Startpagina</target>
+<source>Donation details</source>
+<target>Donatie details</target>
-<source>Email</source>
-<target>Email</target>
+<source>Source code written in C++ using:</source>
+<target>Broncode werd in C++ geschreven met:</target>
<source>Published under the GNU General Public License</source>
<target>Gepubliceerd onder de GNU General Public License</target>
@@ -1402,9 +1417,6 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x is beschikbaar!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Installatiebestanden zijn beschadigd. Installeer FreeFileSync opnieuw.</target>
-
<source>Local path not available for %x.</source>
<target>Lokaal pad niet beschikbaar voor %x.</target>
@@ -1627,6 +1639,9 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<source>Thank you, %x, for your donation and support!</source>
<target>Dank u, %x, voor uw donatie en ondersteuning!</target>
+<source>Connections</source>
+<target>Verbindingen</target>
+
<source>Recommended range:</source>
<target>Aanbevolen bereik:</target>
@@ -1798,9 +1813,6 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<source>Automatic updates:</source>
<target>Automatische updates:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Vereist FreeFileSync donatie Editie</target>
-
<source>Check for Program Updates</source>
<target>Controleer voor programma-updates</target>
@@ -1990,6 +2002,9 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<source>Edit with FreeFileSync</source>
<target>Bewerken met FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>In plaats van een advertentie is hier een dier.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>De draagbare versie van FreeFileSync kan niet worden geïnstalleerd in een submap van %x.</target>
@@ -1999,3 +2014,6 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>De %x installatieoptie is alleen beschikbaar in de FreeFileSync Donatie-editie.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Ontvang de donatie uitgave met bonuseigenschappen en help FreeFileSync advertentie-vrij te houden.</target>
+
diff --git a/FreeFileSync/Build/Languages/english_uk.lng b/FreeFileSync/Build/Languages/english_uk.lng
index 1f3fcaf0..07f178b9 100755
--- a/FreeFileSync/Build/Languages/english_uk.lng
+++ b/FreeFileSync/Build/Languages/english_uk.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Path to an alternate GlobalSettings.xml file.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Installation files are corrupted. Please reinstall FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>Cannot find the following folders:</target>
@@ -333,12 +336,12 @@ Actual: %y bytes
<source>Cannot find %x.</source>
<target>Cannot find %x.</target>
-<source>Cannot open file %x.</source>
-<target>Cannot open file %x.</target>
-
<source>Cannot find device %x.</source>
<target>Cannot find device %x.</target>
+<source>Cannot open file %x.</source>
+<target>Cannot open file %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>Type of item %x is not supported:</target>
@@ -465,6 +468,9 @@ Actual: %y bytes
<source>Total time:</source>
<target>Total time:</target>
+<source>Cleaning up old log files...</source>
+<target>Cleaning up old log files...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Error parsing file %x, row %y, column %z.</target>
@@ -653,6 +659,15 @@ The command is triggered if:
<source>Multiple...</source>
<target>Multiple...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Cannot write file attributes of %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x and %y have different content.</target>
+
+<source>Data verification error:</source>
+<target>Data verification error:</target>
+
<source>Moving file %x to %y</source>
<target>Moving file %x to %y</target>
@@ -677,14 +692,8 @@ The command is triggered if:
<source>Updating attributes of %x</source>
<target>Updating attributes of %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Cannot write file attributes of %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x and %y have different content.</target>
-
-<source>Data verification error:</source>
-<target>Data verification error:</target>
+<source>Source item %x not found</source>
+<target>Source item %x not found</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>Creating a Volume Shadow Copy for %x...</target>
@@ -746,9 +755,6 @@ The command is triggered if:
<source>System: Shut down</source>
<target>System: Shut down</target>
-<source>Cleaning up old log files...</source>
-<target>Cleaning up old log files...</target>
-
<source>Stopped</source>
<target>Stopped</target>
@@ -881,6 +887,12 @@ The command is triggered if:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Please select a folder on a local file system, network or an MTP device.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Requires FreeFileSync Donation Edition</target>
+
<source>&Save</source>
<target>&Save</target>
@@ -1031,6 +1043,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>Handle daylight saving time</target>
+<source>Performance improvements:</source>
+<target>Performance improvements:</target>
+
+<source>Parallel file operations:</source>
+<target>Parallel file operations:</target>
+
+<source>How to get best performance?</source>
+<target>How to get best performance?</target>
+
<source>Local settings:</source>
<target>Local settings:</target>
@@ -1147,15 +1168,6 @@ The command is triggered if:
<source>Directory on server:</source>
<target>Directory on server:</target>
-<source>Performance improvements:</source>
-<target>Performance improvements:</target>
-
-<source>How to get best performance?</source>
-<target>How to get best performance?</target>
-
-<source>Connections for directory reading:</source>
-<target>Connections for directory reading:</target>
-
<source>SFTP channels per connection:</source>
<target>SFTP channels per connection:</target>
@@ -1291,8 +1303,17 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>&Default</target>
-<source>Source code written in C++ using:</source>
-<target>Source code written in C++ using:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Feedback and suggestions are welcome</target>
+
+<source>Home page</source>
+<target>Home page</target>
+
+<source>FreeFileSync Forum</source>
+<target>FreeFileSync Forum</target>
+
+<source>Email</source>
+<target>E-mail</target>
<source>If you like FreeFileSync:</source>
<target>If you like FreeFileSync:</target>
@@ -1300,20 +1321,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Support with a donation</source>
<target>Support with a donation</target>
-<source>Donation details</source>
-<target>Donation details</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>The auto updater was disabled by the administrator.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Feedback and suggestions are welcome</target>
-
-<source>Home page</source>
-<target>Home page</target>
+<source>Donation details</source>
+<target>Donation details</target>
-<source>Email</source>
-<target>E-mail</target>
+<source>Source code written in C++ using:</source>
+<target>Source code written in C++ using:</target>
<source>Published under the GNU General Public License</source>
<target>Published under the GNU General Public Licence</target>
@@ -1402,9 +1417,6 @@ This guarantees a consistent state even in case of a serious error.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x is available!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Installation files are corrupted. Please reinstall FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>Local path not available for %x.</target>
@@ -1627,6 +1639,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Thank you, %x, for your donation and support!</source>
<target>Thank you, %x, for your donation and support!</target>
+<source>Connections</source>
+<target>Connections</target>
+
<source>Recommended range:</source>
<target>Recommended range:</target>
@@ -1798,9 +1813,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Automatic updates:</source>
<target>Automatic updates:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Requires FreeFileSync Donation Edition</target>
-
<source>Check for Program Updates</source>
<target>Check for Program Updates</target>
@@ -1990,6 +2002,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Edit with FreeFileSync</source>
<target>Edit with FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>Instead of an ad, here's an animal.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>The FreeFileSync portable version cannot install into a subfolder of %x.</target>
@@ -1999,3 +2014,6 @@ This guarantees a consistent state even in case of a serious error.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>The %x installation option is only available in the FreeFileSync Donation Edition.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</target>
+
diff --git a/FreeFileSync/Build/Languages/finnish.lng b/FreeFileSync/Build/Languages/finnish.lng
deleted file mode 100755
index dfdf1145..00000000
--- a/FreeFileSync/Build/Languages/finnish.lng
+++ /dev/null
@@ -1,1999 +0,0 @@
-<header>
- <language>Suomi</language>
- <translator>Nalle Juslén</translator>
- <locale>fi_FI</locale>
- <image>flag_finland.png</image>
- <plural_count>2</plural_count>
- <plural_definition>n == 1 ? 0 : 1</plural_definition>
-</header>
-
-<source>Both sides have changed since last synchronization.</source>
-<target>Molemmat puolet muuttuneet edellisestä täsmäytyksestä.</target>
-
-<source>Cannot determine sync-direction:</source>
-<target>Tuntematon suunta täsmäytykselle:</target>
-
-<source>No change since last synchronization.</source>
-<target>Ei muutoksia edellisen täsmäytyksen jälkeen.</target>
-
-<source>The database entry is not in sync considering current settings.</source>
-<target>Tietokanta ei vastaa nykyisiä asetuksia.</target>
-
-<source>Setting default synchronization directions: Old files will be overwritten with newer files.</source>
-<target>Asetetaan oletussuunta täsmäytykselle: vanhat tiedostot korvataan uudemilla tiedostoilla.</target>
-
-<source>Creating file %x</source>
-<target>Luodaan tiedosto %x</target>
-
-<source>Creating folder %x</source>
-<target>Luodaan hakemisto %x</target>
-
-<source>Creating symbolic link %x</source>
-<target>Luodaan pikakuvake %x</target>
-
-<source>Moving file %x to the recycle bin</source>
-<target>Siirretään tiedosto %x Roskakoriin</target>
-
-<source>Moving folder %x to the recycle bin</source>
-<target>Siirretään hakemisto %x Roskakoriin</target>
-
-<source>Moving symbolic link %x to the recycle bin</source>
-<target>Siirretään pikakuvike %x Roskakoriin</target>
-
-<source>Deleting file %x</source>
-<target>Poistetaan tiedosto %x</target>
-
-<source>Deleting folder %x</source>
-<target>Poistetaan hakemisto %x</target>
-
-<source>Deleting symbolic link %x</source>
-<target>Pistetaan pikakuvake %x</target>
-
-<source>Checking recycle bin availability for folder %x...</source>
-<target>Tarkistetaan hakemisto %x:n käyttöoikeus Roskakoriin ...</target>
-
-<source>The recycle bin is not supported by the following folders. Deleted or overwritten files will not be able to be restored:</source>
-<target>Roskakoria ei tueta näissä hakemistoissa. Poistettuja tai muutettuja tiedostoja ei voida palauttaa:</target>
-
-<source>An exception occurred</source>
-<target>Poikkeama havaittu</target>
-
-<source>A directory path is expected after %x.</source>
-<target>%x:n jälkeen kuuluisi olla hakemistopolku.</target>
-
-<source>Syntax error</source>
-<target>Muotovirhe</target>
-
-<source>A left and a right directory path are expected after %x.</source>
-<target></target>
-
-<source>Cannot find file %x.</source>
-<target>Tiedosto %x ei löydy.</target>
-
-<source>Error</source>
-<target>Virhe</target>
-
-<source>File %x does not contain a valid configuration.</source>
-<target>Tiedosto %x ei sisällä kelvollista kokoonpanoa.</target>
-
-<source>Unequal number of left and right directories specified.</source>
-<target>Oikealla ja vasemmalla eri määrä hakemistoja.</target>
-
-<source>The config file must not contain settings at directory pair level when directories are set via command line.</source>
-<target>Määritystiedosto ei saa sisältää hakemistoparitason asetuksia, jos hakemistot määritellään komentoriviltä.</target>
-
-<source>Directories cannot be set for more than one configuration file.</source>
-<target>Hakemistot voi asettaa vain yhtä määrittelyä varten.</target>
-
-<source>Command line</source>
-<target>Komentokehote</target>
-
-<source>Syntax:</source>
-<target>Syntaksi:</target>
-
-<source>config files:</source>
-<target>määritystiedostot:</target>
-
-<source>directory</source>
-<target>hakemisto</target>
-
-<source>global config file:</source>
-<target>yleinen määritystiedosto:</target>
-
-<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source>
-<target>Vapaa määrä FreeFileSync .ffs_gui ja/tai .ffs_batch määrittelytiedostoja.</target>
-
-<source>Any number of alternative directory pairs for at most one config file.</source>
-<target>Vapaa määrä eri hakemistopareja yhdelle tietylle määritystiedostolle.</target>
-
-<source>Open the selected configuration for editing only without executing it.</source>
-<target>Avaa asetus editointia varten, ei suoriteta.</target>
-
-<source>Path to an alternate GlobalSettings.xml file.</source>
-<target>Polku toiseen GlobalSettings.xml tiedostolle.</target>
-
-<source>Cannot find the following folders:</source>
-<target>Seuraavia hakemistoja ei löydy:</target>
-
-<source>If this error is ignored the folders will be considered empty. Missing folders are created automatically when needed.</source>
-<target>Hakemistot luodaan tyhjinä jos virhe ohitetaan. Puuttuvat hakemistot luodaan tarvittaessa.</target>
-
-<source>Comparison finished:</source>
-<target></target>
-
-<source>
-<pluralform>1 item found</pluralform>
-<pluralform>%x items found</pluralform>
-</source>
-<target>
-</target>
-
-<source>File %x has an invalid date.</source>
-<target>Tiedostolla %x on virheellinen päiväys.</target>
-
-<source>Date:</source>
-<target>Pvm:</target>
-
-<source>Files have the same date but a different size.</source>
-<target>Tiedostojen päiväys täsmäämutta koke ei.</target>
-
-<source>Size:</source>
-<target>Koko:</target>
-
-<source>Content comparison was skipped for excluded files.</source>
-<target>Sisällön tarkastus ohitettiin, tiedosto suljettu pois.</target>
-
-<source>Items differ in attributes only</source>
-<target>Kohteet eroavat vain ominaisuuksiltaan</target>
-
-<source>Resolving symbolic link %x</source>
-<target>Tulkitaan pikakuvike %x</target>
-
-<source>Comparing content of files %x</source>
-<target>Verrataan tiedostojen %x sisältöä</target>
-
-<source>Generating file list...</source>
-<target>Luodaan tiedostoluettelo...</target>
-
-<source>Fail-safe file copy</source>
-<target>Varmennettu tiedostokopiointi</target>
-
-<source>Enabled</source>
-<target>Käytössä</target>
-
-<source>Disabled</source>
-<target>Estetty</target>
-
-<source>Copy locked files</source>
-<target>Kopioi lukitut tiedostot</target>
-
-<source>Copy file access permissions</source>
-<target>Kopioi tiedoston käyttöoikeudet</target>
-
-<source>File time tolerance</source>
-<target>Tiedoston toleranssiaika</target>
-
-<source>Folder access timeout</source>
-<target>Hakemiston aikakatkaisu</target>
-
-<source>Run with background priority</source>
-<target>Aja tausta-ajo prioriteetillä</target>
-
-<source>Lock directories during sync</source>
-<target>Lukitse hakemistot täsmäytyksen ajaksi</target>
-
-<source>Verify copied files</source>
-<target>Tarkista monistetut tiedostot</target>
-
-<source>Using non-default global settings:</source>
-<target>Käytä mukautettuja yleisasetuksia:</target>
-
-<source>A folder input field is empty.</source>
-<target>Hakemiston syötekenttä on tyhjä.</target>
-
-<source>The corresponding folder will be considered as empty.</source>
-<target>Vastaava hakemisto tulkitaan tyhjäksi.</target>
-
-<source>Exclude:</source>
-<target>Sulje pois:</target>
-
-<source>One base folder of a folder pair is contained in the other one.</source>
-<target>Hakemiston yksi juurihakemisto sisältyy jo toiseen.</target>
-
-<source>The folder should be excluded from synchronization via filter.</source>
-<target>Sulje hakemisto pois käyttämällä suodinta.</target>
-
-<source>Calculating sync directions...</source>
-<target>Lasketaan täsmäytyksen suuntaa...</target>
-
-<source>Out of memory.</source>
-<target>Muisti loppui.</target>
-
-<source>Item exists on left side only</source>
-<target>Kohde on vain vasemmalla</target>
-
-<source>Item exists on right side only</source>
-<target>Kohde on vain oikealla</target>
-
-<source>Left side is newer</source>
-<target>Uudempi vasemmalla</target>
-
-<source>Right side is newer</source>
-<target>Uudempi oikealla</target>
-
-<source>Items have different content</source>
-<target>Kohteilla on eri sisältö</target>
-
-<source>Both sides are equal</source>
-<target>Puolet ovat identtiset</target>
-
-<source>Conflict/item cannot be categorized</source>
-<target>Ristiriita/kohde ei ole luokiteltavissa</target>
-
-<source>Copy new item to left</source>
-<target>Kopioi uusi kohde vasemmalle</target>
-
-<source>Copy new item to right</source>
-<target>Kopioi uusi kohde oikealle</target>
-
-<source>Delete left item</source>
-<target>Poista vasen kohde</target>
-
-<source>Delete right item</source>
-<target>Poista oikea kohde</target>
-
-<source>Move file on left</source>
-<target>Siirrä vasen tiedosto</target>
-
-<source>Move file on right</source>
-<target>Siirrä oikea tiedosto</target>
-
-<source>Update left item</source>
-<target>Päivitä vasen kohde</target>
-
-<source>Update right item</source>
-<target>Päivitä oikea kohde</target>
-
-<source>Do nothing</source>
-<target>Älä tee mitään</target>
-
-<source>Update attributes on left</source>
-<target>Päivitä oikeudet vasemmalla</target>
-
-<source>Update attributes on right</source>
-<target>Päivitä määritteet oikealla</target>
-
-<source>Cannot read file %x.</source>
-<target>Tiedosto %x on lukukelvoton.</target>
-
-<source>
-Unexpected size of data stream.
-Expected: %x bytes
-Actual: %y bytes
-</source>
-<target>
-Tietovuon suuruus yllätti.
-Oletettu: %x tavua
-Todellinen: %y tavua
-</target>
-
-<source>Cannot write permissions of %x.</source>
-<target>Ei voi tallentaa %x:n oikeuksia.</target>
-
-<source>Operation not supported for different base folder types.</source>
-<target>Toiminto ei tue erilaisia lähdehakemistoja.</target>
-
-<source>Cannot write file %x.</source>
-<target>Tiedoston %x kirjoittaminen ei onnistu.</target>
-
-<source>Cannot move file %x to %y.</source>
-<target>Tiedostoa %x ei voida siirtää kohtaan %y.</target>
-
-<source>Cannot copy symbolic link %x to %y.</source>
-<target>Pikakuvike %x - %y kopiointi epäonnistui.</target>
-
-<source>Unable to connect to %x.</source>
-<target>Yhteyttä %x:n ei voi luoda.</target>
-
-<source>Failed to get information about server %x.</source>
-<target>Palvelimen %x tietoja ei saatu.</target>
-
-<source>Cannot open directory %x.</source>
-<target>Hakemistoa %x ei voi avata.</target>
-
-<source>Cannot read directory %x.</source>
-<target>Hekemisto %x on lukukelvoton.</target>
-
-<source>Cannot read file attributes of %x.</source>
-<target>Tiedoston %x määritteitä ei voitu lukea.</target>
-
-<source>Cannot create directory %x.</source>
-<target>Hakemistoa %x ei voitu luoda.</target>
-
-<source>Cannot delete file %x.</source>
-<target>Ei voi poistaa tiedostoa %x.</target>
-
-<source>Cannot delete directory %x.</source>
-<target>Hakemistoa %x ei voida poistaa.</target>
-
-<source>Cannot write modification time of %x.</source>
-<target>Tiedoston %x aikaleimaa ei voida kirjoittaa.</target>
-
-<source>Cannot determine final path for %x.</source>
-<target>%x:n lopullista polkua ei voida määrittää.</target>
-
-<source>Cannot resolve symbolic link %x.</source>
-<target>Pikakuvike %x on virheellinen.</target>
-
-<source>Unable to move %x to the recycle bin.</source>
-<target>%x:n siirto Roskakoriin epäonnistui.</target>
-
-<source>Cannot find %x.</source>
-<target>%x ei löydy.</target>
-
-<source>Cannot open file %x.</source>
-<target>Tiedosto %x ei aukea.</target>
-
-<source>Cannot find device %x.</source>
-<target>Laite %x ei löydy.</target>
-
-<source>Type of item %x is not supported:</source>
-<target>Kohteen %x tyyppiä ei tueta:</target>
-
-<source>Cannot delete symbolic link %x.</source>
-<target>Pikakuvike %x:n poisto ei onnistu.</target>
-
-<source>Cannot determine free disk space for %x.</source>
-<target>%x:n vaapaan tilan määrittely ei onnistu.</target>
-
-<source>Incorrect command line:</source>
-<target>Virheellinen komento:</target>
-
-<source>Error Code %x</source>
-<target>Virhe %x.</target>
-
-<source>The server does not support authentication via %x.</source>
-<target>Palvelin ei salli tunnistautumista %x:n kautta.</target>
-
-<source>Required:</source>
-<target>Vaadittu:</target>
-
-<source>Unable to access %x.</source>
-<target>Ei saatu yhteyttä %x:n.</target>
-
-<source>
-<pluralform>Operation timed out after 1 second.</pluralform>
-<pluralform>Operation timed out after %x seconds.</pluralform>
-</source>
-<target>
-<pluralform>Toiminto keskeytyy 1 sekunnin kuluttua.</pluralform>
-<pluralform>Toiminto keskeytyy %x sekunnin kuluttua.</pluralform>
-</target>
-
-<source>
-<pluralform>Cannot wait on more than 1 connection at a time.</pluralform>
-<pluralform>Cannot wait on more than %x connections at a time.</pluralform>
-</source>
-<target>
-<pluralform>Odottaminen sallittu vain 1 yhteydelle.</pluralform>
-<pluralform>Odottaminen sallittu vain %x yhtäaikaiselle yhteydelle.</pluralform>
-</target>
-
-<source>Active connections: %x</source>
-<target>Aktiivinen yhteys: %x</target>
-
-<source>Failed to open SFTP channel number %x.</source>
-<target>SFTP kanava %x ei voitu avata.</target>
-
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>1 tavu</pluralform>
-<pluralform>%x tavua</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x Mt</target>
-
-<source>%x KB</source>
-<target>%x kt</target>
-
-<source>%x GB</source>
-<target>%x Gt</target>
-
-<source>Cannot load file %x.</source>
-<target>Tiedostoa %x ei voida ladata.</target>
-
-<source>Database file %x is incompatible.</source>
-<target>Tietokanta %x ei ole yhteensopiva.</target>
-
-<source>Initial synchronization:</source>
-<target>Ensimmäinen täsmäytys:</target>
-
-<source>Database file %x does not yet exist.</source>
-<target>Tietokantaa %x ei vielä ole.</target>
-
-<source>Database file is corrupted:</source>
-<target>Tietokanta on tuhoutunut:</target>
-
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Tietokannasa ei vielä ole tietoja viimeisestä täsmäyksestä.</target>
-
-<source>Loading file %x...</source>
-<target>Ladataan tiedostoa %x...</target>
-
-<source>Saving file %x...</source>
-<target>Tallennetaan tiedostoa %x...</target>
-
-<source>Searching for folder %x...</source>
-<target>Etsitään hakemistoa %x...</target>
-
-<source>Timeout while searching for folder %x.</source>
-<target>Aikaviive hakiessa hakemistoa %x.</target>
-
-<source>Cannot get process information.</source>
-<target>Prosessin tietoja ei saada.</target>
-
-<source>Waiting while directory is locked:</source>
-<target>Odotetaan, kunnes hakemiston lukitus aukeaa:</target>
-
-<source>Lock owner:</source>
-<target>Lukitsija:</target>
-
-<source>Detecting abandoned lock...</source>
-<target>Hylätyn lukituksen etsintä...</target>
-
-<source>
-<pluralform>1 sec</pluralform>
-<pluralform>%x sec</pluralform>
-</source>
-<target>
-<pluralform>1 s</pluralform>
-<pluralform>%x s</pluralform>
-</target>
-
-<source>Items processed:</source>
-<target>Osioita käsitelty:</target>
-
-<source>Items remaining:</source>
-<target>Osioita jäljellä:</target>
-
-<source>Total time:</source>
-<target>Kokonaisaika:</target>
-
-<source>Error parsing file %x, row %y, column %z.</source>
-<target>Jäsennysvirhe - tiedosto: %x, rivi: %y, sarake: %z.</target>
-
-<source>Cannot set directory locks for the following folders:</source>
-<target></target>
-
-<source>
-<pluralform>1 thread</pluralform>
-<pluralform>%x threads</pluralform>
-</source>
-<target>
-<pluralform>1 säije</pluralform>
-<pluralform>%x säijettä</pluralform>
-</target>
-
-<source>Scanning:</source>
-<target>Skannaus:</target>
-
-<source>/sec</source>
-<target>/s</target>
-
-<source>%x items/sec</source>
-<target>%x kohteita/s</target>
-
-<source>Show in Explorer</source>
-<target>Näytä Explorerissa</target>
-
-<source>Open with default application</source>
-<target>Avaa oletussovelluksessa</target>
-
-<source>Browse directory</source>
-<target>Selaa hakemistoa</target>
-
-<source>Cannot access the Volume Shadow Copy Service.</source>
-<target>Aseman tilannevedospalvelu ei vastaa.</target>
-
-<source>Please run the 64-bit version of FreeFileSync to create shadow copies on this system.</source>
-<target>Käytä FreeFileSync:n 64-bittinen versio jos haluat luoda järjestelmästä tilannevedoksia.</target>
-
-<source>Cannot determine volume name for %x.</source>
-<target>Aseman %x määrittäminen ei onnistu.</target>
-
-<source>Volume name %x is not part of file path %y.</source>
-<target>Asema %x ei esiinny tiedostopolussa %y.</target>
-
-<source>Unable to create time stamp for versioning:</source>
-<target>Versionhallinnan aikaleimaa ei voida luoda:</target>
-
-<source>Drag && drop</source>
-<target>Vedä ja pudota</target>
-
-<source>Cannot find folder %x.</source>
-<target>Hakemistoa %x ei löydy.</target>
-
-<source>Select a folder</source>
-<target>Valitse hakemisto</target>
-
-<source>&New</source>
-<target>&Uusi</target>
-
-<source>&Open...</source>
-<target>&Avaa...</target>
-
-<source>Save &as...</source>
-<target>Tallenna n&imellä...</target>
-
-<source>E&xit</source>
-<target>&Lopeta</target>
-
-<source>&File</source>
-<target>&Tiedosto</target>
-
-<source>&View help</source>
-<target>&Näytä ohje</target>
-
-<source>&About</source>
-<target>&Ohje</target>
-
-<source>&Help</source>
-<target>&Ohje</target>
-
-<source>Usage:</source>
-<target>Käyttö:</target>
-
-<source>1. Select folders to watch.</source>
-<target>1. Valitse seurattavat hakemistot.</target>
-
-<source>2. Enter a command line.</source>
-<target>2. Anna komentokehote.</target>
-
-<source>3. Press 'Start'.</source>
-<target>3. Paina 'Käynnistä'.</target>
-
-<source>To get started just import a .ffs_batch file.</source>
-<target>Aloita lataamalla joku .ffs_batch-tiedosto.</target>
-
-<source>Folders to watch:</source>
-<target>Seurattavat hakemistot:</target>
-
-<source>Add folder</source>
-<target>Lisää hakemisto</target>
-
-<source>Remove folder</source>
-<target>Poista hakemisto</target>
-
-<source>Browse</source>
-<target>Selaa</target>
-
-<source>Idle time (in seconds):</source>
-<target>Joutoaika (s):</target>
-
-<source>Idle time between last detected change and execution of command</source>
-<target>Joutoaika edellisen havaitun muutoksen ja käskyn suorittamisen välillä</target>
-
-<source>Command line:</source>
-<target>Komentokehoite:</target>
-
-<source>
-The command is triggered if:
-- files or subfolders change
-- new folders arrive (e.g. USB stick insert)
-</source>
-<target>
-Käsky suoritetaan jos:
-- tiedosto tai alihakemisto muuttuu
-- uusi hakemisto ilmestyy (esim. muistitikku liitetään)
-</target>
-
-<source>Start</source>
-<target>Aloita</target>
-
-<source>About</source>
-<target>Ohje</target>
-
-<source>Build: %x</source>
-<target>Versio: %x</target>
-
-<source>All files</source>
-<target>Kaikki tiedostot</target>
-
-<source>Automated Synchronization</source>
-<target>Automaattinen täsmäytys</target>
-
-<source>The %x protocol does not support directory monitoring:</source>
-<target>Valittu protokolla %x ei tue seurantaa:</target>
-
-<source>Directory monitoring active</source>
-<target>Hakemistovalvonta päällä</target>
-
-<source>Waiting until all directories are available...</source>
-<target>Odota kunnes kaikki hakemistot ovat saatavilla...</target>
-
-<source>&Restore</source>
-<target>&Palauta</target>
-
-<source>&Show error</source>
-<target>&Näytä virhe</target>
-
-<source>&Quit</source>
-<target>&Lopeta</target>
-
-<source>&Retry</source>
-<target>&Uudestaan</target>
-
-<source>File time and size</source>
-<target>Tiedoston aika ja koko</target>
-
-<source>File content</source>
-<target>Tiedoston sisältö</target>
-
-<source>File size</source>
-<target>Tiedostokoko</target>
-
-<source>Two way</source>
-<target>Kaksisuuntainen</target>
-
-<source>Mirror</source>
-<target>Peilaava</target>
-
-<source>Update</source>
-<target>Päivittävä</target>
-
-<source>Custom</source>
-<target>Oma määritelmä</target>
-
-<source>Multiple...</source>
-<target>Moninkertainen...</target>
-
-<source>Moving file %x to %y</source>
-<target>Siirretään tiedosto %x -> %y</target>
-
-<source>Moving folder %x to %y</source>
-<target>Siirretään hakemisto %x -> %y</target>
-
-<source>Moving symbolic link %x to %y</source>
-<target>Siirrä pikakuvike %x -> %y</target>
-
-<source>Removing old versions...</source>
-<target>Vanhat versiot poistetaan...</target>
-
-<source>Updating file %x</source>
-<target>Tiedostoa %x päivitetään</target>
-
-<source>Updating symbolic link %x</source>
-<target>Pikakuvitetta %x päivitetään</target>
-
-<source>Verifying file %x</source>
-<target>Tarkistetaan tiedostoa %x</target>
-
-<source>Updating attributes of %x</source>
-<target>Päivitetään %x:n ominaisuuksia</target>
-
-<source>Cannot write file attributes of %x.</source>
-<target>Tiedoston %x ominaisuuksia ei voitu tallentaa.</target>
-
-<source>%x and %y have different content.</source>
-<target>Eri sisältö %x:llä ja %y:llä.</target>
-
-<source>Data verification error:</source>
-<target>Tiedon tarkistusvirhe:</target>
-
-<source>Creating a Volume Shadow Copy for %x...</source>
-<target>Luodaan %x:n tilannevedos ...</target>
-
-<source>Target folder %x already existing.</source>
-<target>Kohdehakemisto %x on jo olemassa.</target>
-
-<source>Target folder input field must not be empty.</source>
-<target>Kohde hakemiston kenttä on annettava.</target>
-
-<source>Source folder %x not found.</source>
-<target>Lähdehakemistoa %x ei löydy.</target>
-
-<source>Please enter a target folder for versioning.</source>
-<target>Anna versioinnin kohdehakemisto.</target>
-
-<source>The following items have unresolved conflicts and will not be synchronized:</source>
-<target>Näissä kohteissa on selvittämättömiä ristiriitoja, niitä ei täsmäytetä:</target>
-
-<source>The following folders are significantly different. Please check that the correct folders are selected for synchronization.</source>
-<target>Seuraavat hakemistot eroavat oleellisesti. Varmista valitut täsmäytettävät hakemistot.</target>
-
-<source>Not enough free disk space available in:</source>
-<target>Vapaa levytila ei riitä:</target>
-
-<source>Available:</source>
-<target>Saatavilla:</target>
-
-<source>Some files will be synchronized as part of multiple base folders.</source>
-<target>Jotkut tiedostot täsmäytetään osana useampaa juurihakemistoa.</target>
-
-<source>To avoid conflicts, set up exclude filters so that each updated file is considered by only one base folder.</source>
-<target>Ristiriidan välttämiseksi, aseta poisulkeva suodin jotta tiedosto käsitellään vain yhdessä juurihakemistossa.</target>
-
-<source>Versioning folder:</source>
-<target>Versiointi hakemisto:</target>
-
-<source>Base folder:</source>
-<target>Juurihakemisto:</target>
-
-<source>The versioning folder is contained in a base folder.</source>
-<target>Versionnin hakemisto löytyy peruskansiosta.</target>
-
-<source>Synchronizing folder pair:</source>
-<target>Täsmäytetään hakemistoparia:</target>
-
-<source>Generating database...</source>
-<target>Tietokantaa luodaan...</target>
-
-<source>Loading...</source>
-<target>Lataan...</target>
-
-<source>job name</source>
-<target>työnnimi</target>
-
-<source>System: Sleep</source>
-<target></target>
-
-<source>System: Shut down</source>
-<target></target>
-
-<source>Cleaning up old log files...</source>
-<target>Siivotaan vanhat lokitiedostot...</target>
-
-<source>Stopped</source>
-<target>Keskeytys</target>
-
-<source>Completed with errors</source>
-<target></target>
-
-<source>Completed with warnings</source>
-<target></target>
-
-<source>Warning</source>
-<target>Varoitus</target>
-
-<source>Nothing to synchronize</source>
-<target>Ei mitään täsmäytettävää</target>
-
-<source>Completed successfully</source>
-<target></target>
-
-<source>Executing command %x</source>
-<target>Suorita komentua %x</target>
-
-<source>You can switch to FreeFileSync's main window to resolve this issue.</source>
-<target>Voit siirtyä FreeFileSyncin pääikkunaan korjataksesi tämä virhe.</target>
-
-<source>&Don't show this warning again</source>
-<target>&Älä enää näytä tätä varoitusta</target>
-
-<source>&Ignore</source>
-<target>&Sivuta</target>
-
-<source>&Switch</source>
-<target>&Vaihda</target>
-
-<source>Switching to FreeFileSync's main window</source>
-<target>Vaihdetaan FreeFileSyncin pääikkunaan</target>
-
-<source>Automatic retry</source>
-<target></target>
-
-<source>Ignore &all</source>
-<target>Ohita &kaikki</target>
-
-<source>Retrying operation...</source>
-<target>Yritetään uudestaan...</target>
-
-<source>Serious Error</source>
-<target>Vakava virhe</target>
-
-<source>Last session</source>
-<target>Viime istunto</target>
-
-<source>Today</source>
-<target>Tänään</target>
-
-<source>
-<pluralform>1 day</pluralform>
-<pluralform>%x days</pluralform>
-</source>
-<target>
-<pluralform>1 päivä</pluralform>
-<pluralform>%x päivää</pluralform>
-</target>
-
-<source>Name</source>
-<target>Nimi</target>
-
-<source>Last sync</source>
-<target></target>
-
-<source>Folder</source>
-<target>Hakemisto</target>
-
-<source>Symlink</source>
-<target>Pikakuvake</target>
-
-<source>Full path</source>
-<target>Koko polku</target>
-
-<source>Relative path</source>
-<target>Suhteellinen polku</target>
-
-<source>Item name</source>
-<target>Kohteen nimi</target>
-
-<source>Size</source>
-<target>Koko</target>
-
-<source>Date</source>
-<target>Päiväys</target>
-
-<source>Extension</source>
-<target>Tunniste</target>
-
-<source>Category</source>
-<target>Luokka</target>
-
-<source>Action</source>
-<target>Toiminto</target>
-
-<source>Local comparison settings</source>
-<target>Vertailun paikalliset asetukset</target>
-
-<source>Local synchronization settings</source>
-<target>Täsmäytyksen paikalliset asetukset</target>
-
-<source>Local filter</source>
-<target>Paikallinen suodin</target>
-
-<source>Active</source>
-<target>Aktiivinen</target>
-
-<source>None</source>
-<target>Ei ole</target>
-
-<source>Remove local settings</source>
-<target>Poista paikalliset asetukset</target>
-
-<source>Clear local filter</source>
-<target>Nollaa paikallinen suodin</target>
-
-<source>Copy</source>
-<target>Monista</target>
-
-<source>Paste</source>
-<target>Liitä</target>
-
-<source>The selected folder %x cannot be used with FreeFileSync.</source>
-<target>Kansio %x ei toimi FreeFileSync:n kanssa.</target>
-
-<source>Please select a folder on a local file system, network or an MTP device.</source>
-<target>Valitse kansio joka on paikallinen, verkossa tai MTP laitteella.</target>
-
-<source>&Save</source>
-<target>&Tallenna</target>
-
-<source>Save as &batch job...</source>
-<target>Tallenna &eräajona...</target>
-
-<source>Start &comparison</source>
-<target>Aloita &vertailu</target>
-
-<source>C&omparison settings</source>
-<target>&Vertailu asetukset</target>
-
-<source>&Filter settings</source>
-<target>&Suodin asetukset</target>
-
-<source>S&ynchronization settings</source>
-<target>&Täsmäytyksen asetukset</target>
-
-<source>Start &synchronization</source>
-<target>Aloita &täsmäytys</target>
-
-<source>&Actions</source>
-<target>&Toinnot</target>
-
-<source>&Preferences</source>
-<target>&Ominaisuudet</target>
-
-<source>&Language</source>
-<target>&Kieli</target>
-
-<source>&Find...</source>
-<target>&Etsi...</target>
-
-<source>&Export file list...</source>
-<target>&Vie tiedostojoukko...</target>
-
-<source>&Reset layout</source>
-<target>&Nollaa asettelu</target>
-
-<source>&Tools</source>
-<target>&Asetukset</target>
-
-<source>&Check for updates now</source>
-<target>&Etsi päivityksiä nyt</target>
-
-<source>Check &automatically once a week</source>
-<target>Tarkista &viikoittain</target>
-
-<source>Cancel</source>
-<target>Lopeta</target>
-
-<source>Compare</source>
-<target>Vertaile</target>
-
-<source>Synchronize</source>
-<target>Täsmäytä</target>
-
-<source>Add folder pair</source>
-<target>Lisää hakemistopari</target>
-
-<source>Remove folder pair</source>
-<target>Poista hakemistopari</target>
-
-<source>Access online storage</source>
-<target>Osoita online taltiota</target>
-
-<source>Swap sides</source>
-<target>Puoltenvaihto</target>
-
-<source>Close search bar</source>
-<target>Sulje hakukenttä</target>
-
-<source>Find:</source>
-<target>Etsi:</target>
-
-<source>Match case</source>
-<target>Täsmäytä kirjainkoko</target>
-
-<source>New</source>
-<target>Uusi</target>
-
-<source>Open...</source>
-<target>Avaa...</target>
-
-<source>Save</source>
-<target>Tallenna</target>
-
-<source>Save as...</source>
-<target>Tallenna nimellä...</target>
-
-<source>View type:</source>
-<target>Näytä tyyppi:</target>
-
-<source>Select view:</source>
-<target>Valitse näkymä:</target>
-
-<source>Statistics:</source>
-<target>Tilastot:</target>
-
-<source>Number of files and folders that will be deleted</source>
-<target>Poistettavien tiedostojen ja hakemistojen määrä</target>
-
-<source>Number of files that will be updated</source>
-<target>Päitettävien tiedostojen määrä</target>
-
-<source>Number of files and folders that will be created</source>
-<target>Luotavien tiedostojen ja hakemistojen määrä</target>
-
-<source>Total bytes to copy</source>
-<target>Kopioitava määrä tavuja</target>
-
-<source>Arrange folder pair</source>
-<target>Järjestä hakemistoparit</target>
-
-<source>Folder pair:</source>
-<target>Hakemistopari:</target>
-
-<source>Main settings:</source>
-<target>Pääasetukset:</target>
-
-<source>Use local settings:</source>
-<target>Käytä paikallisia asetuksia:</target>
-
-<source>Select a variant:</source>
-<target>Valitse vaihtoehto:</target>
-
-<source>Include &symbolic links:</source>
-<target>Sisällytä &pikalinkit:</target>
-
-<source>&Follow</source>
-<target>&Seuraa</target>
-
-<source>&Direct</source>
-<target>S&uoraan</target>
-
-<source>More information</source>
-<target>Lisää tietoa</target>
-
-<source>&Ignore time shift [hh:mm]</source>
-<target>&Jätä aikasiirtymä huomiotta [hh:mm]</target>
-
-<source>List of file time offsets to ignore</source>
-<target>Aikasiirtymät joita ei huomioida</target>
-
-<source>Example:</source>
-<target>Esim.:</target>
-
-<source>Handle daylight saving time</source>
-<target>Käsittele kesäaika</target>
-
-<source>Local settings:</source>
-<target>Paikalliset aseukset:</target>
-
-<source>Include:</source>
-<target>Sisällytä:</target>
-
-<source>Show examples</source>
-<target>Näytä esimerkkejä</target>
-
-<source>Time span:</source>
-<target>Aikajakso:</target>
-
-<source>File size:</source>
-<target>Tiedoston koko:</target>
-
-<source>Minimum:</source>
-<target>Vähintään:</target>
-
-<source>Maximum:</source>
-<target>Enintään:</target>
-
-<source>Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair.</source>
-<target>Määrittele suodatussäännöt täsmäyksestä pois jätettäville tiedostoille. Anna hakemistopariin nähden suhteellinen osoite.</target>
-
-<source>C&lear</source>
-<target>&Nollaa</target>
-
-<source>Detect moved files</source>
-<target>Tunnista siirretyt tiedostot</target>
-
-<source>
-- Not supported by all file systems
-- Requires and creates database files
-- Detection not available for first sync
-</source>
-<target>
-- Ei tueta kaikissa tiedostojärjestelmissä
-- Vaatii ja luo tarvittavat tietokannat
-- Tunnistus alkaa ensimmäisestä täsmäyksestä
-</target>
-
-<source>Delete files:</source>
-<target>Poista tiedosto:</target>
-
-<source>&Recycle bin</source>
-<target>&Roskakori</target>
-
-<source>&Permanent</source>
-<target>&Pysyvä</target>
-
-<source>&Versioning</source>
-<target>&Versiointi</target>
-
-<source>Naming convention:</source>
-<target>Nimeämiskäytäntö:</target>
-
-<source>Ignore errors</source>
-<target></target>
-
-<source>Retry count:</source>
-<target>Uusintayrityksiä:</target>
-
-<source>Delay (in seconds):</source>
-<target>Viive (sekunneissa):</target>
-
-<source>Run a command after synchronization:</source>
-<target>Suorita käsky kun täsmäytys on tehty:</target>
-
-<source>OK</source>
-<target>Kyllä</target>
-
-<source>Enter your login details:</source>
-<target>Anna kirjautumistietosi:</target>
-
-<source>Connection type:</source>
-<target>Yhteystapa:</target>
-
-<source>Server name or IP address:</source>
-<target>Palvelimen nimi tai IP:</target>
-
-<source>Port:</source>
-<target>Portti:</target>
-
-<source>Encryption:</source>
-<target>Salaus:</target>
-
-<source>&Disabled</source>
-<target>&ei käytössä</target>
-
-<source>&Explicit SSL/TLS</source>
-<target>&Valikoiva SSL/TLS</target>
-
-<source>Authentication:</source>
-<target>Todennus:</target>
-
-<source>&Password</source>
-<target>&Salasana</target>
-
-<source>&Key file</source>
-<target>&Avaintiedosto</target>
-
-<source>&SSH agent</source>
-<target>&SSH agentti</target>
-
-<source>User name:</source>
-<target>Käyttäjänimi:</target>
-
-<source>Private key file:</source>
-<target>Avaintiedosto:</target>
-
-<source>&Show password</source>
-<target>&Näytä salasana</target>
-
-<source>Directory on server:</source>
-<target>Hakemisto palvelimella:</target>
-
-<source>Performance improvements:</source>
-<target>Suorituskyvyn tehostus:</target>
-
-<source>How to get best performance?</source>
-<target>Miten saan parhaan suorituskyvyn?</target>
-
-<source>Connections for directory reading:</source>
-<target>Yhteyksiä hakemistojen lukemiseen:</target>
-
-<source>SFTP channels per connection:</source>
-<target>SFTP kanavia per yhteys:</target>
-
-<source>Detect server limit</source>
-<target>Tunnista palvelimen rajat</target>
-
-<source>Select a directory on the server:</source>
-<target>Valitse palvelimelta hakemisto:</target>
-
-<source>Select Folder</source>
-<target>Valitse kansio</target>
-
-<source>Start synchronization now?</source>
-<target>Käynnistetäänkö täsmäytys?</target>
-
-<source>Variant:</source>
-<target>Vaihtoehto:</target>
-
-<source>&Don't show this dialog again</source>
-<target>&Älä näytä tätä uudestaan</target>
-
-<source>Items found:</source>
-<target>Kohteita löytyi:</target>
-
-<source>Time remaining:</source>
-<target>Aikaa jäljellä:</target>
-
-<source>Time elapsed:</source>
-<target>Aikaa kulunut:</target>
-
-<source>Bytes</source>
-<target>Tavua</target>
-
-<source>Items</source>
-<target>Kohteita</target>
-
-<source>Synchronizing...</source>
-<target>Täsmäytetään...</target>
-
-<source>Minimize to notification area</source>
-<target>Pienennä ilmaisinalueelle</target>
-
-<source>When finished:</source>
-<target>Kun valmis:</target>
-
-<source>Auto-close</source>
-<target></target>
-
-<source>Close</source>
-<target>Sulje</target>
-
-<source>&Pause</source>
-<target>&Keskeytä</target>
-
-<source>Stop</source>
-<target>Lopeta</target>
-
-<source>Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x</source>
-<target>Luo eräajotiedosto automaattista täsmäytystä varten. Käynnistä kaksoisnapsauttamalla tai ajasta tehtävä: %x</target>
-
-<source>Progress dialog:</source>
-<target></target>
-
-<source>Run minimized</source>
-<target>Suorita pienennettynä</target>
-
-<source>&Show error dialog</source>
-<target>&Näytä virheilmoitus</target>
-
-<source>Show pop-up on errors or warnings</source>
-<target>Näytä ponnahdusikkuna virheiden tai varoituksien kohdalla</target>
-
-<source>&Cancel</source>
-<target>&Peruuta</target>
-
-<source>Stop synchronization at first error</source>
-<target>Lopeta täsmäytys ensimmäiseen virheeseen</target>
-
-<source>Save log:</source>
-<target>Tallenna loki:</target>
-
-<source>Limit:</source>
-<target>Raja:</target>
-
-<source>Limit maximum number of log files</source>
-<target>Rajoita lokien maksimimäärä</target>
-
-<source>How can I schedule a batch job?</source>
-<target>Miten ajastan eräajon?</target>
-
-<source>&Keep relative paths</source>
-<target>&Säilytä suhteelliset polut</target>
-
-<source>&Overwrite existing files</source>
-<target>&Korvaa olemassa olevat tiedostot</target>
-
-<source>The following settings are used for all synchronization jobs.</source>
-<target>Näitä asetuksia käytetään kaikkiin täsmäyksiin.</target>
-
-<source>
-Copy to a temporary file (*.ffs_tmp) before overwriting target.
-This guarantees a consistent state even in case of a serious error.
-</source>
-<target>
-Kopioi väliaikaistiedostoon (*.ffs_tmp) ennen kohteen korvaamista.
-Tällä varmistetaan eheys, vaikka vakava virhe tapahtuisi.
-</target>
-
-<source>recommended</source>
-<target>suositus</target>
-
-<source>Copy shared or locked files using the Volume Shadow Copy Service.</source>
-<target>Jaettujen tai lukittujen tiedostojen kopiointi käyttäen aseman tilannevedospalvelua.</target>
-
-<source>requires administrator rights</source>
-<target>edellyttää järjestelmänvalvojan oikeuksia</target>
-
-<source>Transfer file and folder permissions.</source>
-<target>Siirrä tiedostojen ja hakemistojen käyttöoikeudet.</target>
-
-<source>Show hidden dialogs again</source>
-<target>Näytä piiloitetut valintaikkunat uudestaan</target>
-
-<source>Show all permanently hidden dialogs and warning messages again</source>
-<target>Näytä pysyvästi piiloitetut valintaikkunat ja varoitukset uudestaan</target>
-
-<source>Customize context menu:</source>
-<target>Muokkaa pikavalikkoa:</target>
-
-<source>Description</source>
-<target>Kuvaus</target>
-
-<source>&Default</source>
-<target>&Oletus</target>
-
-<source>Source code written in C++ using:</source>
-<target>Koodattu C++-kielellä käyttäen:</target>
-
-<source>If you like FreeFileSync:</source>
-<target>Jos pidät FreeFileSyncistä:</target>
-
-<source>Support with a donation</source>
-<target>Tue meitä lahjoituksella</target>
-
-<source>Donation details</source>
-<target>Lahjoituksen tiedot</target>
-
-<source>The auto updater was disabled by the administrator.</source>
-<target>Järjestelmävalvoka keskeytti päivityksen.</target>
-
-<source>Feedback and suggestions are welcome</source>
-<target>Palaute ja uudet ehdotukset ovat tervetulleita</target>
-
-<source>Home page</source>
-<target>Kotisivu</target>
-
-<source>Email</source>
-<target>S-posti</target>
-
-<source>Published under the GNU General Public License</source>
-<target>Julkaistu noudattaen GNU General Public Licensen ehtoja:</target>
-
-<source>Many thanks for localization:</source>
-<target>Paljon kiitoksia kääntäjille:</target>
-
-<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
-<target>Käynnistä FreeFileSyncin Lahjoittajan versiota käyttäen:</target>
-
-<source>1. Activate via internet now:</source>
-<target>1. Internetin kautta:</target>
-
-<source>Activate online</source>
-<target>Aktivoi netissä</target>
-
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Hae erillinen aktivointiavain sivulta:</target>
-
-<source>&Copy to clipboard</source>
-<target>&Kopioi leikepöydälle</target>
-
-<source>Enter activation key:</source>
-<target>Syötä aktivointiavain:</target>
-
-<source>Activate offline</source>
-<target>Aktivoi yhteyttä</target>
-
-<source>Highlight configurations that have not been run for more than the following number of days:</source>
-<target></target>
-
-<source>Synchronization Settings</source>
-<target>Täsmäyksen asetukset</target>
-
-<source>Access Online Storage</source>
-<target></target>
-
-<source>Save as a Batch Job</source>
-<target>Tallenna eräajona></target>
-
-<source>Delete Items</source>
-<target>Poista kohteet</target>
-
-<source>Copy Items</source>
-<target></target>
-
-<source>Options</source>
-<target>Asetukset</target>
-
-<source>Select Time Span</source>
-<target>Valitse aikajakso</target>
-
-<source>FreeFileSync Donation Edition</source>
-<target>FreeFileSync Lahjoittajan versio</target>
-
-<source>Highlight Configurations</source>
-<target></target>
-
-<source>&Options</source>
-<target>&Asetukset</target>
-
-<source>Main Bar</source>
-<target>Pääpalkki</target>
-
-<source>Folder Pairs</source>
-<target>Hakemistoparit</target>
-
-<source>Find</source>
-<target>Etsi</target>
-
-<source>View Settings</source>
-<target>Näytä asetukset</target>
-
-<source>Configuration</source>
-<target>Asetukset</target>
-
-<source>Overview</source>
-<target>Yleiskatsaus</target>
-
-<source>Show "%x"</source>
-<target>Näytä "%x"</target>
-
-<source>&Show details</source>
-<target>&Näytä tarkenteet</target>
-
-<source>FreeFileSync %x is available!</source>
-<target>FreeFileSync %x on saatavilla!</target>
-
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Asennustiedostot ovat viottuneet. Yritä asentaa FreeFileSync uudestaan.</target>
-
-<source>Local path not available for %x.</source>
-<target>Paikallinen polku %x puuttuu.</target>
-
-<source>Confirm</source>
-<target>Vahvista</target>
-
-<source>
-<pluralform>Do you really want to execute the command %y for one item?</pluralform>
-<pluralform>Do you really want to execute the command %y for %x items?</pluralform>
-</source>
-<target>
-<pluralform>Haluatko varmasti suorittaa %y tälle kohteelle?</pluralform>
-<pluralform>Haluatko varmasti suorittaa %y näille %x kohteelle?</pluralform>
-</target>
-
-<source>&Execute</source>
-<target>&Suorita</target>
-
-<source>
-<pluralform>1 directory</pluralform>
-<pluralform>%x directories</pluralform>
-</source>
-<target>
-<pluralform>1 hakemisto</pluralform>
-<pluralform>%x hakemistoja</pluralform>
-</target>
-
-<source>
-<pluralform>1 file</pluralform>
-<pluralform>%x files</pluralform>
-</source>
-<target>
-<pluralform>1 tiedosto</pluralform>
-<pluralform>%x tiedostoja</pluralform>
-</target>
-
-<source>
-<pluralform>Showing %y of 1 row</pluralform>
-<pluralform>Showing %y of %x rows</pluralform>
-</source>
-<target>
-<pluralform>Näytetään %y (1 rivi)</pluralform>
-<pluralform>Näytetään %y (%x riviä)</pluralform>
-</target>
-
-<source>Set direction:</source>
-<target>Aseta suunta:</target>
-
-<source>multiple selection</source>
-<target>monivalinta</target>
-
-<source>Include via filter:</source>
-<target>Sisällytä suotimella:</target>
-
-<source>Exclude via filter:</source>
-<target>Suodata suotimella:</target>
-
-<source>Include temporarily</source>
-<target>Sisällytä, tilapäisesti</target>
-
-<source>Exclude temporarily</source>
-<target>Suodat tilapäisesti</target>
-
-<source>&Copy to...</source>
-<target>&Monista...</target>
-
-<source>&Delete</source>
-<target>&Poista</target>
-
-<source>Include all</source>
-<target>Valitse kaikki</target>
-
-<source>Exclude all</source>
-<target>Suodata kaikki</target>
-
-<source>Show icons:</source>
-<target>Näytä kuvakkeet:</target>
-
-<source>Small</source>
-<target>Pieni</target>
-
-<source>Medium</source>
-<target>Keskikoko</target>
-
-<source>Large</source>
-<target>Iso</target>
-
-<source>Select time span...</source>
-<target>Valitse aikajana...</target>
-
-<source>Folder Comparison and Synchronization</source>
-<target>Hakemistojen vertailu ja täsmäytys</target>
-
-<source>Configuration saved</source>
-<target>Asetukset tallennettu</target>
-
-<source>FreeFileSync batch</source>
-<target>FreeFileSync eräajo</target>
-
-<source>Do you want to save changes to %x?</source>
-<target>Haluatko tallentaa muutokset: %x?</target>
-
-<source>Never save &changes</source>
-<target>Älä tallenna &muutoksia</target>
-
-<source>Do&n't save</source>
-<target>Äl&ä tallenna</target>
-
-<source>Hide configuration</source>
-<target></target>
-
-<source>Highlight...</source>
-<target></target>
-
-<source>Clear filter</source>
-<target>Nollaa suodin</target>
-
-<source>Show files that exist on left side only</source>
-<target>Näytä vain vasemmalla esiintyvät tiedostot</target>
-
-<source>Show files that exist on right side only</source>
-<target>Näytä vain oikealla esiintyvät tiedostot</target>
-
-<source>Show files that are newer on left</source>
-<target>Näytä vasemmalla olevat uudemmat tiedostot</target>
-
-<source>Show files that are newer on right</source>
-<target>Näytä oikealla olevat uudemmat tiedostot</target>
-
-<source>Show files that are equal</source>
-<target>Näytä yhteneväiset tiedostot</target>
-
-<source>Show files that are different</source>
-<target>Näytä erilaiset tiedostot</target>
-
-<source>Show conflicts</source>
-<target>Näytä ristiriidat</target>
-
-<source>Show files that will be created on the left side</source>
-<target>Näytä vasemmalle luotavat tiedostot</target>
-
-<source>Show files that will be created on the right side</source>
-<target>Näytä oikealle luotavat tiedostot</target>
-
-<source>Show files that will be deleted on the left side</source>
-<target>Näytä vasemmalta poistettavat tiedostot</target>
-
-<source>Show files that will be deleted on the right side</source>
-<target>Näytä oikealta poistettavat tiedostot</target>
-
-<source>Show files that will be updated on the left side</source>
-<target>Näytä ne tiedostot vasemmalla joita päivitetään</target>
-
-<source>Show files that will be updated on the right side</source>
-<target>Näytä ne tiedostot oikealla joita päivitetään</target>
-
-<source>Show files that won't be copied</source>
-<target>Näytä kopioimatta jäävät tiedostot</target>
-
-<source>Show filtered or temporarily excluded files</source>
-<target>Näytä suodatetut tai tilapäisesti pois suljetut tiedostot</target>
-
-<source>Save as default</source>
-<target>Tallenna oletukseksi</target>
-
-<source>Filter</source>
-<target>Suodin</target>
-
-<source>All files are in sync</source>
-<target>Kaikki tiedostot ovat ajantasalla</target>
-
-<source>Cannot find %x</source>
-<target>En löydä %x</target>
-
-<source>Move up</source>
-<target>Siirrä ylös</target>
-
-<source>Move down</source>
-<target>Siirrä alas</target>
-
-<source>Comma-separated values</source>
-<target>Pilkulla erotetut arvot</target>
-
-<source>File list exported</source>
-<target>Tiedostolista viety</target>
-
-<source>Searching for program updates...</source>
-<target>Ohjelmapäivytystä haetaan...</target>
-
-<source>Paused</source>
-<target>Pysäytetty</target>
-
-<source>Stop requested...</source>
-<target></target>
-
-<source>Initializing...</source>
-<target>Alustetaan...</target>
-
-<source>Scanning...</source>
-<target>Tiedostoja haetaan...</target>
-
-<source>Comparing content...</source>
-<target>Sisältöä vertaillaan...</target>
-
-<source>Info</source>
-<target>Info</target>
-
-<source>Select all</source>
-<target>Valitse kaikki</target>
-
-<source>&Continue</source>
-<target>&Jatka</target>
-
-<source>Progress</source>
-<target>Etenemä</target>
-
-<source>Log</source>
-<target>Loki</target>
-
-<source>Thank you, %x, for your donation and support!</source>
-<target>Kiitos %x, arvostan tukeasi ja lahjoitustasi!</target>
-
-<source>Recommended range:</source>
-<target>Suositeltu alue:</target>
-
-<source>Password:</source>
-<target>Salasana:</target>
-
-<source>Key password:</source>
-<target>Salasana:</target>
-
-<source>Please enter a file path.</source>
-<target>Anna tiedostopolku.</target>
-
-<source>
-<pluralform>Copy the following item to another folder?</pluralform>
-<pluralform>Copy the following %x items to another folder?</pluralform>
-</source>
-<target>
-<pluralform>Monistetaanko kohde toiseen kansioon?</pluralform>
-<pluralform>Monista %x kohteet toiseen kansioon?</pluralform>
-</target>
-
-<source>Please enter a target folder.</source>
-<target>Kerro kohde kansio.</target>
-
-<source>
-<pluralform>Do you really want to move the following item to the recycle bin?</pluralform>
-<pluralform>Do you really want to move the following %x items to the recycle bin?</pluralform>
-</source>
-<target>
-<pluralform>Haluatko varmasti siirtää kohteen Roskakoriin?</pluralform>
-<pluralform>Haluatko varmasti siirtää nämä %x kohteet Roskakoriin?</pluralform>
-</target>
-
-<source>Move</source>
-<target>Siirrä</target>
-
-<source>
-<pluralform>Do you really want to delete the following item?</pluralform>
-<pluralform>Do you really want to delete the following %x items?</pluralform>
-</source>
-<target>
-<pluralform>Haluatko todella poistaa tämän kohteen?</pluralform>
-<pluralform>Haluatko todella poistaa nämä %x kohdetta?</pluralform>
-</target>
-
-<source>Copy DACL, SACL, Owner, Group</source>
-<target>Monista DACL, SACL, omistaja, ryhmä</target>
-
-<source>Integrate external applications into context menu. The following macros are available:</source>
-<target>Liitä ulkoinen sovellus pikavalikkoon. Seuraavat makrot ovat valittavissa:</target>
-
-<source>Full file or folder path</source>
-<target>Koko tiedosto/hakemisto-polku</target>
-
-<source>Parent folder path</source>
-<target>Ylätason kansio</target>
-
-<source>Temporary local copy for SFTP and MTP storage</source>
-<target>Tilapäinen paikalliskopio SFTP/MTP:lle</target>
-
-<source>Parameters for opposite side</source>
-<target>Toisen puolen parametrit</target>
-
-<source>Downloading update...</source>
-<target>Ladataan päivitystä...</target>
-
-<source>Identify equal files by comparing modification time and size.</source>
-<target>Tunnista identtiset tiedostot aikaleimoja ja kokoa vertailemalla.</target>
-
-<source>Identify equal files by comparing the file content.</source>
-<target>Tunnista identtiset tiedostot sisältöä vertailemalla.</target>
-
-<source>Identify equal files by comparing their file size.</source>
-<target>Löydä vastaavat tiedostot tiedostokoon mukaan.</target>
-
-<source>Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database.</source>
-<target>Löydä ja monista muutokset molemmille puolille. Poistot, siirrot ja eroavuudet tunnistetaan tietokannan avulla.</target>
-
-<source>Create a mirror backup of the left folder by adapting the right folder to match.</source>
-<target>Luo peilikuva vasemmasta kansiosta muokkaamalla oikeata kansiota samanlaiseksi.</target>
-
-<source>Copy new and updated files to the right folder.</source>
-<target>Monista uudet ja päivitetyt tiedostot oikealle.</target>
-
-<source>Configure your own synchronization rules.</source>
-<target>Määritä omat täsmäytyssäännöt.</target>
-
-<source>Comparison</source>
-<target>Vertailu</target>
-
-<source>Synchronization</source>
-<target>Täsmäytys</target>
-
-<source>This week</source>
-<target>Tällä viikolla</target>
-
-<source>This month</source>
-<target>Tässä kuussa</target>
-
-<source>This year</source>
-<target>Tänä vuonna</target>
-
-<source>Last x days</source>
-<target>Viimeiset x päivää</target>
-
-<source>Byte</source>
-<target>tavua</target>
-
-<source>KB</source>
-<target>kt</target>
-
-<source>MB</source>
-<target>Mt</target>
-
-<source>Retain deleted and overwritten files in the recycle bin</source>
-<target>Säilytä poistetut ja korjatut tiedostot roskakorissa</target>
-
-<source>Delete and overwrite files permanently</source>
-<target>Poista ja korvaa tiedostot suoraan</target>
-
-<source>Move files to a user-defined folder</source>
-<target>Siirrä tiedostot itse määräämäsi hakemistoon</target>
-
-<source>Replace</source>
-<target>Korvaa</target>
-
-<source>Move files and replace if existing</source>
-<target>Siirrä tiedostot ja korvaa olemassaolevat</target>
-
-<source>Time stamp</source>
-<target>Aikaleima</target>
-
-<source>Append a time stamp to each file name</source>
-<target>Lisää tiedostoihin aikaleimat</target>
-
-<source>On completion:</source>
-<target>Kun valmis:</target>
-
-<source>On errors:</source>
-<target>Kun virhe:</target>
-
-<source>On success:</source>
-<target>Kun onnistui:</target>
-
-<source>Main config</source>
-<target>Pääasetus</target>
-
-<source>empty</source>
-<target>tyhjä</target>
-
-<source>Leave as unresolved conflict</source>
-<target>Jätä ratkaisemattomana ristiriitana</target>
-
-<source>File</source>
-<target>Tiedosto</target>
-
-<source>YYYY-MM-DD hhmmss</source>
-<target>VVVV-KK-PP ttmmss</target>
-
-<source>Files</source>
-<target>Tiedostot</target>
-
-<source>Percentage</source>
-<target>Prosenttia</target>
-
-<source>Failed to retrieve update information.</source>
-<target>Päivytyksen noutaminen epäonnistui.</target>
-
-<source>Automatic updates:</source>
-<target>Automaattinen päivitys:</target>
-
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Edellyttää FreeFileSyncin Lahjoittajan versiota</target>
-
-<source>Check for Program Updates</source>
-<target>Etsi ohjelmaan päivitystä</target>
-
-<source>Auto-update now or download manually from the FreeFileSync home page?</source>
-<target>Päivitä automaattisesti tai lataa päivitys FreeFileSyncin kotisivulta?</target>
-
-<source>&Auto-update</source>
-<target>&Automattisesti</target>
-
-<source>&Home page</source>
-<target>&Kotisivu</target>
-
-<source>Download now?</source>
-<target>Ladataanko nyt?</target>
-
-<source>&Download</source>
-<target>&Lataa</target>
-
-<source>FreeFileSync is up to date.</source>
-<target>FreeFileSync on ajan tasalla.</target>
-
-<source>Cannot find current FreeFileSync version number online. A newer version is likely available. Check manually now?</source>
-<target>Tätä FreeFileSync versiota ei lödy. Uudempi versio luultavasti löytyy, tarkistetaanko manuaalisesti?</target>
-
-<source>&Check</source>
-<target>&Tarkista</target>
-
-<source>Consistency check failed for %x.</source>
-<target>%x:n yhtenäisyystarkistus epäonnistui.</target>
-
-<source>Installation was registered on a different operating system.</source>
-<target>Asennus on rekisteröity toiselle käyttöjärjestelmälle.</target>
-
-<source>Failed to activate FreeFileSync Donation Edition.</source>
-<target>FreeFileSyncin Lahjoittajan versio ei aktivoitunut.</target>
-
-<source>Incorrect activation key.</source>
-<target>Väärä aktivointiavain.</target>
-
-<source>Unable to register to receive system messages.</source>
-<target>Kirjautuminen järjestelmäviestien vastaanottamiseksi ei onnistu.</target>
-
-<source>Cannot find system function %x.</source>
-<target>Järjestelmäfunktiota %x ei löydy.</target>
-
-<source>Unable to register device notifications for %x.</source>
-<target>%x laiteviestien kirjaaminen ei onnistu.</target>
-
-<source>Cannot monitor directory %x.</source>
-<target>Hakemistoa %x ei voida tarkkaila.</target>
-
-<source>The file is locked by another process:</source>
-<target>Tiedosto on toisen prosessin lukitsema:</target>
-
-<source>Cannot read security context of %x.</source>
-<target>Ei voi lukea %x:n suojauskontekstia.</target>
-
-<source>Cannot write security context of %x.</source>
-<target>Ei voi tallentaa %x:n suojauskontekstia.</target>
-
-<source>Cannot read permissions of %x.</source>
-<target>Ei voi lukea %x:n oikeuksia.</target>
-
-<source>Cannot copy permissions from %x to %y.</source>
-<target>Oikeuksien monistus %x:ltä %y:lle ei onnistu.</target>
-
-<source>%x is not a regular directory name.</source>
-<target>%x on epämääräinen hakemisto nimi.</target>
-
-<source>Cannot copy file %x to %y.</source>
-<target>Tiedostoa %x ei voida kopioida kohtaan %y.</target>
-
-<source>Cannot copy attributes from %x to %y.</source>
-<target>Ominaisuuksien monistus %x:ltä %y:lle ei onnistu.</target>
-
-<source>%x TB</source>
-<target>%x Tt</target>
-
-<source>%x PB</source>
-<target>%x Pt</target>
-
-<source>
-<pluralform>1 min</pluralform>
-<pluralform>%x min</pluralform>
-</source>
-<target>
-<pluralform>1 min</pluralform>
-<pluralform>%x min</pluralform>
-</target>
-
-<source>
-<pluralform>1 hour</pluralform>
-<pluralform>%x hours</pluralform>
-</source>
-<target>
-<pluralform>1 tunti</pluralform>
-<pluralform>%x tuntia</pluralform>
-</target>
-
-<source>Cannot set privilege %x.</source>
-<target>Oikeutta %x ei voitu asettaa.</target>
-
-<source>Unable to suspend system sleep mode.</source>
-<target>Lepotilan keskeyttäminen ei onnistu.</target>
-
-<source>Cannot change process I/O priorities.</source>
-<target>Prosessin I/O-prioriteetin muutos ei onnistu.</target>
-
-<source>Unable to shut down the system.</source>
-<target>Järjestelmää ei voi sammuttaa.</target>
-
-<source>Checking recycle bin failed for folder %x.</source>
-<target>Roskakorin tarkistus hakemistolle %x epäonnistui.</target>
-
-<source>The following XML elements could not be read:</source>
-<target>Nämä XML elementit ovat lukukelvottimia:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Kokoonpanotiedosto %x ladattu vain osittain. Puuttuvat asetetaan oletusarvoihin.</target>
-
-<source>Prepare installation</source>
-<target>Alusta asennus</target>
-
-<source>Choose which components you want to install.</source>
-<target>Valitse asennettavat osiot.</target>
-
-<source>Select installation type:</source>
-<target>Valitse asennustapa:</target>
-
-<source>Local</source>
-<target>Paikallinen</target>
-
-<source>Portable</source>
-<target>Siirrettävä</target>
-
-<source>Save settings to "%APPDATA%\FreeFileSync"</source>
-<target>Tallenna asetukset "%APPDATA%\FreeFileSync"</target>
-
-<source>Register FreeFileSync file extensions</source>
-<target>Rekisteröi FreeFileSync tiedostotunnisteet</target>
-
-<source>Create Explorer context menu entries</source>
-<target>Luo merkinnät Explorer-pikavalikoon</target>
-
-<source>Save settings in installation directory</source>
-<target>Tallenna asetukset asennus hakemistoon</target>
-
-<source>Do not write to Registry</source>
-<target>Älä kirjoita rekisteritietoja</target>
-
-<source>Just copy the files</source>
-<target>Kopio vain tiedostot</target>
-
-<source>Choose a directory for installation:</source>
-<target>Valitse asennushakemisto:</target>
-
-<source>Create shortcuts:</source>
-<target>Luodaan pikavalinnat:</target>
-
-<source>Desktop</source>
-<target>Työpöydälle</target>
-
-<source>Start Menu</source>
-<target></target>
-
-<source>Send To</source>
-<target></target>
-
-<source>Registering FreeFileSync file extensions</source>
-<target>Liitään FreeFileSync tiedostotunnisteet</target>
-
-<source>Unregistering FreeFileSync file extensions</source>
-<target>Poistetaan liitos, FreeFileSync tiedostotunnisteet</target>
-
-<source>FreeFileSync Configuration</source>
-<target>FreeFileSync määrittelyt</target>
-
-<source>FreeFileSync Batch File</source>
-<target>FreeFileSync eräajo</target>
-
-<source>FreeFileSync Synchronization Database</source>
-<target>FreeFileSync täsmäytyksen tietokanta</target>
-
-<source>RealTimeSync Configuration</source>
-<target>RealTimeSync määrittelyt</target>
-
-<source>Edit with FreeFileSync</source>
-<target>Muokkaa FreeFileSync:llä</target>
-
-<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
-<target>FreeFileSync:n siirrettävä versio ei voi asentaa alikansioon %x.</target>
-
-<source>Please choose the local installation type or select a different folder for installation.</source>
-<target>Valitse paikallinen asennus tai vaihda asennushakemistoa.</target>
-
-<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
-<target></target>
-
diff --git a/FreeFileSync/Build/Languages/french.lng b/FreeFileSync/Build/Languages/french.lng
index 67627ec3..915bbc94 100755
--- a/FreeFileSync/Build/Languages/french.lng
+++ b/FreeFileSync/Build/Languages/french.lng
@@ -7,6 +7,9 @@
<plural_definition>n <= 1 ? 0 : 1</plural_definition>
</header>
+<source>Defined by context of use</source>
+<target></target>
+
<source>Both sides have changed since last synchronization.</source>
<target>Les deux côtés ont changé depuis la dernière synchronisation.</target>
@@ -112,6 +115,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Chemin d'un fichier GlobalSettings.xml distinct.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Un ou plusieurs fichiers installés sont abîmés. Veuillez réinstaller FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>Impossible de trouver les dossiers suivants :</target>
@@ -333,12 +339,12 @@ Trouvé : %y octets
<source>Cannot find %x.</source>
<target>Impossible de trouver %x.</target>
-<source>Cannot open file %x.</source>
-<target>Impossible d'ouvrir le fichier %x.</target>
-
<source>Cannot find device %x.</source>
<target>Impossible de trouver le périphérique %x.</target>
+<source>Cannot open file %x.</source>
+<target>Impossible d'ouvrir le fichier %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>Le type de l'élément %x n'est pas accepté :</target>
@@ -465,6 +471,9 @@ Trouvé : %y octets
<source>Total time:</source>
<target>Durée totale :</target>
+<source>Cleaning up old log files...</source>
+<target>Nettoyage des anciens fichiers log ...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Erreur lors de l'analyse du fichier %x, ligne %y, colonne %z.</target>
@@ -653,6 +662,15 @@ La commande est déclenchée si :
<source>Multiple...</source>
<target>Multiple ...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Impossible d'écrire les attributs de fichier de %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x et %y ont des contenus différents.</target>
+
+<source>Data verification error:</source>
+<target>Erreur de contrôle des données :</target>
+
<source>Moving file %x to %y</source>
<target>Déplacement du fichier %x vers %y</target>
@@ -677,14 +695,8 @@ La commande est déclenchée si :
<source>Updating attributes of %x</source>
<target>Mise à jour des attributs de %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Impossible d'écrire les attributs de fichier de %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x et %y ont des contenus différents.</target>
-
-<source>Data verification error:</source>
-<target>Erreur de contrôle des données :</target>
+<source>Source item %x not found</source>
+<target>L'élément source %x n'a pas été trouvé</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>Création d'un Volume Shadow Copy pour %x ...</target>
@@ -746,9 +758,6 @@ La commande est déclenchée si :
<source>System: Shut down</source>
<target>Sustème : Arrêt</target>
-<source>Cleaning up old log files...</source>
-<target>Nettoyage des anciens fichiers log ...</target>
-
<source>Stopped</source>
<target>Arrêté</target>
@@ -881,6 +890,9 @@ La commande est déclenchée si :
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Veuillez choisir un dossier local, en réseau ou un périphérique MTP.</target>
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Nécessite FreeFileSync Donation Edition</target>
+
<source>&Save</source>
<target>&Sauvegarder</target>
@@ -1031,6 +1043,15 @@ La commande est déclenchée si :
<source>Handle daylight saving time</source>
<target>Gérer l'heure d'été</target>
+<source>Performance improvements:</source>
+<target>Amélioration des performances :</target>
+
+<source>Parallel file operations:</source>
+<target>Opérations sur les fichiers parallèles :</target>
+
+<source>How to get best performance?</source>
+<target>Comment améliorer les performances ?</target>
+
<source>Local settings:</source>
<target>Paramètres locaux :</target>
@@ -1147,15 +1168,6 @@ La commande est déclenchée si :
<source>Directory on server:</source>
<target>Répertoire sur le serveur :</target>
-<source>Performance improvements:</source>
-<target>Amélioration des performances :</target>
-
-<source>How to get best performance?</source>
-<target>Comment améliorer les performances ?</target>
-
-<source>Connections for directory reading:</source>
-<target>Connexions pour la lecture des répertoires :</target>
-
<source>SFTP channels per connection:</source>
<target>Ports SFTP par connexion :</target>
@@ -1291,8 +1303,17 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave.
<source>&Default</source>
<target>&Défaut</target>
-<source>Source code written in C++ using:</source>
-<target>Code source écrit en C++ utilisant :</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Vos commentaires et vos suggestions sont les bienvenus</target>
+
+<source>Home page</source>
+<target>Page d'accueil</target>
+
+<source>FreeFileSync Forum</source>
+<target>Forum FreeFileSync</target>
+
+<source>Email</source>
+<target>Email</target>
<source>If you like FreeFileSync:</source>
<target>Si vous aimez FreeFileSync :</target>
@@ -1300,20 +1321,14 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave.
<source>Support with a donation</source>
<target>Soutenir avec un don</target>
-<source>Donation details</source>
-<target>Détails pour les donations</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>La mise à jour automatique a été désactivée par l'administrateur.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Vos commentaires et vos suggestions sont les bienvenus</target>
-
-<source>Home page</source>
-<target>Page d'accueil</target>
+<source>Donation details</source>
+<target>Détails pour les donations</target>
-<source>Email</source>
-<target>Email</target>
+<source>Source code written in C++ using:</source>
+<target>Code source écrit en C++ utilisant :</target>
<source>Published under the GNU General Public License</source>
<target>Publié sous licence GNU General Public License</target>
@@ -1402,9 +1417,6 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x est disponible !</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Un ou plusieurs fichiers installés sont abîmés. Veuillez réinstaller FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>Chemin local invalide pour %x.</target>
@@ -1627,6 +1639,9 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave.
<source>Thank you, %x, for your donation and support!</source>
<target>Merci %x pour votre don et votre aide !</target>
+<source>Connections</source>
+<target>Connexions</target>
+
<source>Recommended range:</source>
<target>Plage recommandée :</target>
@@ -1798,9 +1813,6 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave.
<source>Automatic updates:</source>
<target>Mise à jour automatique :</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Nécessite FreeFileSync Donation Edition</target>
-
<source>Check for Program Updates</source>
<target>Recherche des Mises à Jour</target>
@@ -1990,6 +2002,9 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave.
<source>Edit with FreeFileSync</source>
<target>Modification avec FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>A la place d'une publicité, voici un animal.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>La version portable de FreeFileSync ne peut pas être intallée dans le sous-dossier de %x.</target>
@@ -1999,3 +2014,6 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>L'option d'installation %x n'est disponible que dans FreeFileSync Donation Edition.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Obtenez l'Édition Don avec des fonctionnalités bonus et aidez FreeFileSync à rester sans publicité.</target>
+
diff --git a/FreeFileSync/Build/Languages/german.lng b/FreeFileSync/Build/Languages/german.lng
index b993aab4..2039e816 100755
--- a/FreeFileSync/Build/Languages/german.lng
+++ b/FreeFileSync/Build/Languages/german.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Pfad zu alternativer GlobalSettings.xml Datei.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Die Installationsdateien sind beschädigt. Bitte installieren Sie FreeFileSync neu.</target>
+
<source>Cannot find the following folders:</source>
<target>Die folgenden Ordner wurden nicht gefunden:</target>
@@ -333,12 +336,12 @@ Tatsächlich: %y bytes
<source>Cannot find %x.</source>
<target>%x wurde nicht gefunden.</target>
-<source>Cannot open file %x.</source>
-<target>Die Datei %x kann nicht geöffnet werden.</target>
-
<source>Cannot find device %x.</source>
<target>Das Gerät %x wurde nicht gefunden.</target>
+<source>Cannot open file %x.</source>
+<target>Die Datei %x kann nicht geöffnet werden.</target>
+
<source>Type of item %x is not supported:</source>
<target>Der Typ des Elements %x wird nicht unterstützt:</target>
@@ -465,6 +468,9 @@ Tatsächlich: %y bytes
<source>Total time:</source>
<target>Gesamtzeit:</target>
+<source>Cleaning up old log files...</source>
+<target>Bereinige alte Protokolldateien...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Fehler beim Auswerten der Datei %x, Zeile %y, Spalte %z.</target>
@@ -653,6 +659,15 @@ Die Befehlszeile wird ausgelöst, wenn:
<source>Multiple...</source>
<target>Verschiedene...</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>
+
+<source>Data verification error:</source>
+<target>Verifizierungsfehler:</target>
+
<source>Moving file %x to %y</source>
<target>Verschiebe Datei %x nach %y</target>
@@ -677,14 +692,8 @@ 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>
-
-<source>Data verification error:</source>
-<target>Verifizierungsfehler:</target>
+<source>Source item %x not found</source>
+<target>Das Quellelement %x wurde nicht gefunden.</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>Erstelle eine Volumenschattenkopie für %x...</target>
@@ -746,9 +755,6 @@ Die Befehlszeile wird ausgelöst, wenn:
<source>System: Shut down</source>
<target>System: Herunterfahren</target>
-<source>Cleaning up old log files...</source>
-<target>Bereinige alte Protokolldateien...</target>
-
<source>Stopped</source>
<target>Gestoppt</target>
@@ -881,6 +887,12 @@ Die Befehlszeile wird ausgelöst, wenn:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Bitte wählen Sie einen Ordner auf einem lokalen Dateisystem, Netzwerk oder MTP Gerät.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Benötigt FreeFileSync Spendenversion</target>
+
<source>&Save</source>
<target>&Speichern</target>
@@ -1031,6 +1043,15 @@ Die Befehlszeile wird ausgelöst, wenn:
<source>Handle daylight saving time</source>
<target>Sommerzeit berücksichtigen</target>
+<source>Performance improvements:</source>
+<target>Leistungsverbesserungen:</target>
+
+<source>Parallel file operations:</source>
+<target>Parallele Dateioperationen:</target>
+
+<source>How to get best performance?</source>
+<target>Wie erhält man die beste Leistung?</target>
+
<source>Local settings:</source>
<target>Lokale Einstellungen:</target>
@@ -1147,15 +1168,6 @@ Die Befehlszeile wird ausgelöst, wenn:
<source>Directory on server:</source>
<target>Verzeichnis auf Server:</target>
-<source>Performance improvements:</source>
-<target>Leistungsverbesserungen:</target>
-
-<source>How to get best performance?</source>
-<target>Wie erhält man die beste Leistung?</target>
-
-<source>Connections for directory reading:</source>
-<target>Verbindungen zum Verzeichnislesen:</target>
-
<source>SFTP channels per connection:</source>
<target>SFTP Kanäle je Verbindung:</target>
@@ -1291,8 +1303,17 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>&Default</source>
<target>&Standard</target>
-<source>Source code written in C++ using:</source>
-<target>Der Quellcode wurde in C++ geschrieben mit:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Feedback und Vorschläge sind willkommen</target>
+
+<source>Home page</source>
+<target>Homepage</target>
+
+<source>FreeFileSync Forum</source>
+<target>FreeFileSync Forum</target>
+
+<source>Email</source>
+<target>Email</target>
<source>If you like FreeFileSync:</source>
<target>Wenn Sie FreeFileSync mögen:</target>
@@ -1300,20 +1321,14 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Support with a donation</source>
<target>Mit einer Spende unterstützen</target>
-<source>Donation details</source>
-<target>Spendendetails</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>Die automatische Aktualisierung wurde vom Administrator deaktiviert.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Feedback und Vorschläge sind willkommen</target>
-
-<source>Home page</source>
-<target>Homepage</target>
+<source>Donation details</source>
+<target>Spendendetails</target>
-<source>Email</source>
-<target>Email</target>
+<source>Source code written in C++ using:</source>
+<target>Der Quellcode wurde in C++ geschrieben mit:</target>
<source>Published under the GNU General Public License</source>
<target>Veröffentlicht unter der Allgemeinen Öffentlichen GNU-Lizenz</target>
@@ -1402,9 +1417,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x ist verfügbar!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Die Installationsdateien sind beschädigt. Bitte installieren Sie FreeFileSync neu.</target>
-
<source>Local path not available for %x.</source>
<target>Lokaler Pfad ist nicht verfügbar für %x.</target>
@@ -1627,6 +1639,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Thank you, %x, for your donation and support!</source>
<target>Danke %x für die Spende und Unterstützung!</target>
+<source>Connections</source>
+<target>Verbindungen</target>
+
<source>Recommended range:</source>
<target>Empfohlener Bereich:</target>
@@ -1798,9 +1813,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Automatic updates:</source>
<target>Automatische Aktualisierung:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Benötigt FreeFileSync Spendenversion</target>
-
<source>Check for Program Updates</source>
<target>Suche nach neuer Programmversion</target>
@@ -1990,6 +2002,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Edit with FreeFileSync</source>
<target>Mit FreeFileSync editieren</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>Hier ein Tierbild statt Werbung.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>Die portable Version von FreeFileSync kann nicht in einen Unterordner von %x installiert werden.</target>
@@ -1999,3 +2014,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>Die %x Installationsoption ist nur in der FreeFileSync Spendenversion verfügbar.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Jetzt die Spendenversion mit Bonusfunktionen holen und mithelfen, FreeFileSync werbefrei zu halten.</target>
+
diff --git a/FreeFileSync/Build/Languages/greek.lng b/FreeFileSync/Build/Languages/greek.lng
index d611c0df..81494217 100755
--- a/FreeFileSync/Build/Languages/greek.lng
+++ b/FreeFileSync/Build/Languages/greek.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>ΔιαδÏομή σε ένα εναλλακτικό αÏχείο GlobalSettings.xml.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Τα αÏχεία εγκατάστασης είναι κατεστÏαμμένα. ΠαÏακαλοÏμε επανεγκαταστείστε το FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>Οι ακόλουθοι φάκελοι δεν ήταν δυνατό να βÏεθοÏν:</target>
@@ -333,12 +336,12 @@ Actual: %y bytes
<source>Cannot find %x.</source>
<target>Το %x δεν μποÏεί να βÏεθεί.</target>
-<source>Cannot open file %x.</source>
-<target>Δεν είναι δυνατό το άνοιγμα του αÏχείου %x.</target>
-
<source>Cannot find device %x.</source>
<target>Η συσκευή %x δεν μποÏεί να βÏεθεί.</target>
+<source>Cannot open file %x.</source>
+<target>Δεν είναι δυνατό το άνοιγμα του αÏχείου %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>Ο Ï„Ïπος του στοιχείου %x δεν υποστηÏίζεται:</target>
@@ -465,6 +468,9 @@ Actual: %y bytes
<source>Total time:</source>
<target>Συνολική διάÏκεια:</target>
+<source>Cleaning up old log files...</source>
+<target>ΚαθαÏισμός των παλιών αÏχείων καταγÏαφής...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Σφάλμα κατά την ανάλυση του αÏχείου %x, γÏαμμή %y, στήλη %z.</target>
@@ -653,6 +659,15 @@ The command is triggered if:
<source>Multiple...</source>
<target>Πολλαπλές Ïυθμίσεις...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Δεν μποÏεί να γίνει εγγÏαφή των χαÏακτηÏιστικών αÏχείου του %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>Τα %x και %y έχουν διαφοÏετικό πεÏιεχόμενο.</target>
+
+<source>Data verification error:</source>
+<target>Σφάλμα επαλήθευσης δεδομένων:</target>
+
<source>Moving file %x to %y</source>
<target>ΜεταφοÏά του αÏχείου %x στο %y</target>
@@ -677,14 +692,8 @@ The command is triggered if:
<source>Updating attributes of %x</source>
<target>ΕνημέÏωση των χαÏακτηÏιστικών αÏχείου του %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Δεν μποÏεί να γίνει εγγÏαφή των χαÏακτηÏιστικών αÏχείου του %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>Τα %x και %y έχουν διαφοÏετικό πεÏιεχόμενο.</target>
-
-<source>Data verification error:</source>
-<target>Σφάλμα επαλήθευσης δεδομένων:</target>
+<source>Source item %x not found</source>
+<target>Το στοιχείο Ï€Ïοέλευσης %x δεν βÏέθηκε</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>ΔημιουÏγία Σκιώδους ΑντίγÏαφου Τόμου για το %x...</target>
@@ -746,9 +755,6 @@ The command is triggered if:
<source>System: Shut down</source>
<target>ΣÏστημα: ΤεÏματισμός λειτουÏγίας</target>
-<source>Cleaning up old log files...</source>
-<target>ΚαθαÏισμός των παλιών αÏχείων καταγÏαφής...</target>
-
<source>Stopped</source>
<target>Διακοπή</target>
@@ -881,6 +887,12 @@ The command is triggered if:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>ΠαÏακαλοÏμε επιλέξτε ένα φάκελο σε ένα τοπικό σÏστημα αÏχείων, σε ένα δίκτυο ή σε μια συσκευή MTP.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Απαιτεί την Έκδοση ΔωÏητή του FreeFileSync</target>
+
<source>&Save</source>
<target>&Αποθήκευση</target>
@@ -1031,6 +1043,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>ΔιαχείÏιση θεÏινής ÏŽÏας</target>
+<source>Performance improvements:</source>
+<target>Βελτιώσεις της απόδοσης:</target>
+
+<source>Parallel file operations:</source>
+<target>ΠαÏάλληλες λειτουÏγίες αÏχείων:</target>
+
+<source>How to get best performance?</source>
+<target>Πώς να έχετε την καλÏτεÏη απόδοση;</target>
+
<source>Local settings:</source>
<target>Τοπικές Ïυθμίσεις:</target>
@@ -1147,15 +1168,6 @@ The command is triggered if:
<source>Directory on server:</source>
<target>Υποκατάλογος στον διακομιστή:</target>
-<source>Performance improvements:</source>
-<target>Βελτιώσεις της απόδοσης:</target>
-
-<source>How to get best performance?</source>
-<target>Πώς να έχετε την καλÏτεÏη απόδοση;</target>
-
-<source>Connections for directory reading:</source>
-<target>Συνδέσεις για ανάγνωση καταλόγου:</target>
-
<source>SFTP channels per connection:</source>
<target>ΑÏιθμός καναλιών SFTP ανά σÏνδεση:</target>
@@ -1291,8 +1303,17 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>&ΠÏοεπιλογή</target>
-<source>Source code written in C++ using:</source>
-<target>Ο πηγαίος κώδικας γÏάφτηκε σε C++ χÏησιμοποιώντας τα:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Τα σχόλια και οι Ï€Ïοτάσεις σας είναι ευπÏόσδεκτα</target>
+
+<source>Home page</source>
+<target>ΑÏχική σελίδα</target>
+
+<source>FreeFileSync Forum</source>
+<target>FreeFileSync Forum</target>
+
+<source>Email</source>
+<target>Email</target>
<source>If you like FreeFileSync:</source>
<target>Αν σας αÏέσει το FreeFileSync:</target>
@@ -1300,20 +1321,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Support with a donation</source>
<target>ΥποστηÏίξτε με μια δωÏεά</target>
-<source>Donation details</source>
-<target>ΛεπτομέÏειες δωÏεάς</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>Η αυτόματη ενημέÏωση απενεÏγοποιήθηκε από το διαχειÏιστή.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Τα σχόλια και οι Ï€Ïοτάσεις σας είναι ευπÏόσδεκτα</target>
-
-<source>Home page</source>
-<target>ΑÏχική σελίδα</target>
+<source>Donation details</source>
+<target>ΛεπτομέÏειες δωÏεάς</target>
-<source>Email</source>
-<target>Email</target>
+<source>Source code written in C++ using:</source>
+<target>Ο πηγαίος κώδικας γÏάφτηκε σε C++ χÏησιμοποιώντας τα:</target>
<source>Published under the GNU General Public License</source>
<target>Διανέμεται υπό την Γενική Άδεια Δημόσιας ΧÏήσης GNU</target>
@@ -1402,9 +1417,6 @@ This guarantees a consistent state even in case of a serious error.
<source>FreeFileSync %x is available!</source>
<target>Το FreeFileSync %x είναι διαθέσιμο!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Τα αÏχεία εγκατάστασης είναι κατεστÏαμμένα. ΠαÏακαλοÏμε επανεγκαταστείστε το FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>Η τοπική διαδÏομή δεν είναι διαθέσιμη για το %x.</target>
@@ -1627,6 +1639,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Thank you, %x, for your donation and support!</source>
<target>Σας ευχαÏιστοÏμε, %x, για τη δωÏεά και την υποστήÏιξή σας!</target>
+<source>Connections</source>
+<target>Συνδέσεις</target>
+
<source>Recommended range:</source>
<target>ΠÏοτεινόμενο εÏÏος:</target>
@@ -1798,9 +1813,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Automatic updates:</source>
<target>Αυτόματες ενημεÏώσεις:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Απαιτεί την Έκδοση ΔωÏητή του FreeFileSync</target>
-
<source>Check for Program Updates</source>
<target>Έλεγχος για ενημεÏώσεις του Ï€ÏογÏάμματος</target>
@@ -1990,6 +2002,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Edit with FreeFileSync</source>
<target>ΕπεξεÏγασία με FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>Αντί για διαφήμιση, δείτε ένα ζωάκι.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>Η φοÏητή έκδοση του FreeFileSync δεν μποÏεί να εγκατασταθεί σε υποφάκελο του %x.</target>
@@ -1999,3 +2014,6 @@ This guarantees a consistent state even in case of a serious error.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>Η επιλογή εγκατάστασης %x είναι διαθέσιμη μόνο στην Έκδοση ΔωÏητή του FreeFileSync.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>ΑγοÏάστε την Έκδοση ΔωÏητή με πεÏισσότεÏες δυνατότητες και βοηθήστε να μείνει το FreeFileSync χωÏίς διαφημίσεις.</target>
+
diff --git a/FreeFileSync/Build/Languages/hebrew.lng b/FreeFileSync/Build/Languages/hebrew.lng
index a8157cc9..a3a23712 100755
--- a/FreeFileSync/Build/Languages/hebrew.lng
+++ b/FreeFileSync/Build/Languages/hebrew.lng
@@ -7,6 +7,9 @@
<plural_definition>n == 1 ? 0 : 1</plural_definition>
</header>
+<source>Defined by context of use</source>
+<target></target>
+
<source>Both sides have changed since last synchronization.</source>
<target>שני ×”×¦×“×“×™× ×©×•× ×• מ××– הסנכרון ×”×חרון.</target>
@@ -112,6 +115,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>נתיב ×ל קובץ GlobalSettingd.xml ×לטרנטיבי.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>קבצי ההתקנה פגומי×. בבקשה התקן מחדש ×ת FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>×œ× ×™×›×•×œ ×œ×ž×¦×•× ×ת התיקיות הב×ות:</target>
@@ -333,12 +339,12 @@ Actual: %y bytes
<source>Cannot find %x.</source>
<target>×œ× ×ž×•×¦× %x.</target>
-<source>Cannot open file %x.</source>
-<target>×œ× ×™×›×•×œ לפתוח קובץ %x.</target>
-
<source>Cannot find device %x.</source>
<target>×œ× ×ž×•×¦× ×”×ª×§×Ÿ %x.</target>
+<source>Cannot open file %x.</source>
+<target>×œ× ×™×›×•×œ לפתוח קובץ %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>סוג של פריט %x ×ינו נתמך:</target>
@@ -465,6 +471,9 @@ Actual: %y bytes
<source>Total time:</source>
<target>זמן כולל:</target>
+<source>Cleaning up old log files...</source>
+<target>מנקה קבצי יומן ישני×...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>שגי××” בפענוח קובץ %x, שורה %y, טור %z.</target>
@@ -653,6 +662,15 @@ The command is triggered if:
<source>Multiple...</source>
<target>הכפל...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>×œ× ×™×›×•×œ לכתוב תכונות קובץ של %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x ו- %y ×”× ×‘×¢×œ×™ צוכן שונה.</target>
+
+<source>Data verification error:</source>
+<target>שגי×ת בדיקת מידע:</target>
+
<source>Moving file %x to %y</source>
<target>מעביר קובץ %x ×ל %y</target>
@@ -677,14 +695,8 @@ The command is triggered if:
<source>Updating attributes of %x</source>
<target>מעדכן תכונות של %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>×œ× ×™×›×•×œ לכתוב תכונות קובץ של %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x ו- %y ×”× ×‘×¢×œ×™ צוכן שונה.</target>
-
-<source>Data verification error:</source>
-<target>שגי×ת בדיקת מידע:</target>
+<source>Source item %x not found</source>
+<target>פריט מקור %x ×œ× × ×ž×¦×</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>מייצר Volume Shadow Copy עבור %x...</target>
@@ -746,9 +758,6 @@ The command is triggered if:
<source>System: Shut down</source>
<target>מערכת: כיבוי</target>
-<source>Cleaning up old log files...</source>
-<target>מנקה קבצי יומן ישני×...</target>
-
<source>Stopped</source>
<target>נעצר</target>
@@ -881,6 +890,9 @@ The command is triggered if:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>×× × ×‘×—×¨ תיקייה במערכת ×”×§×‘×¦×™× ×”×ž×§×•×ž×™×ª, ברשת ×ו בהתקן MTP.</target>
+<source>Requires FreeFileSync Donation Edition</source>
+<target>נדרשת מהדורת התרומות של FreeFileSync</target>
+
<source>&Save</source>
<target>&שמור</target>
@@ -1031,6 +1043,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>התמודד ×¢× ×©×¢×•×Ÿ קיץ</target>
+<source>Performance improvements:</source>
+<target>×©×™×¤×•×¨×™× ×‘×‘×™×¦×•×¢×™×:</target>
+
+<source>Parallel file operations:</source>
+<target>פעולות קובץ מקבילות:</target>
+
+<source>How to get best performance?</source>
+<target>כיצד לקבל ×ת ×”×‘×™×¦×•×¢×™× ×”×˜×•×‘×™× ×‘×™×•×ª×¨?</target>
+
<source>Local settings:</source>
<target>הגדרות מקומיות:</target>
@@ -1147,15 +1168,6 @@ The command is triggered if:
<source>Directory on server:</source>
<target>מחיצה על שרת:</target>
-<source>Performance improvements:</source>
-<target>×©×™×¤×•×¨×™× ×‘×‘×™×¦×•×¢×™×:</target>
-
-<source>How to get best performance?</source>
-<target>כיצד לקבל ×ת ×”×‘×™×¦×•×¢×™× ×”×˜×•×‘×™× ×‘×™×•×ª×¨?</target>
-
-<source>Connections for directory reading:</source>
-<target>×—×™×‘×•×¨×™× ×œ×§×¨×™×ת מחיצה:</target>
-
<source>SFTP channels per connection:</source>
<target>ערוצי SFTP לחיבור:</target>
@@ -1291,8 +1303,17 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>&ברירת מחדל</target>
-<source>Source code written in C++ using:</source>
-<target>קוד מקור נכתב ב- C++‎ ב×מצעות:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>משוב והצעות יתקבלו בברכה</target>
+
+<source>Home page</source>
+<target>דף הבית</target>
+
+<source>FreeFileSync Forum</source>
+<target>×¤×•×¨×•× FreeFileSync</target>
+
+<source>Email</source>
+<target>דו×"ל</target>
<source>If you like FreeFileSync:</source>
<target>במידה ו-FreeFileSync מוצ×ת חן בעינכ×:</target>
@@ -1300,20 +1321,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Support with a donation</source>
<target>תמיכה ב×מצ×ות תרומה</target>
-<source>Donation details</source>
-<target>פרטי תרומה:</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>עידכון ×וטומטי הושבת על ידי מנהל מערכת.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>משוב והצעות יתקבלו בברכה</target>
-
-<source>Home page</source>
-<target>דף הבית</target>
+<source>Donation details</source>
+<target>פרטי תרומה:</target>
-<source>Email</source>
-<target>דו×"ל</target>
+<source>Source code written in C++ using:</source>
+<target>קוד מקור נכתב ב- C++‎ ב×מצעות:</target>
<source>Published under the GNU General Public License</source>
<target>×ž×¤×•×¨×¡× ×‘×ž×¡×’×¨×ª GNU General Public License</target>
@@ -1402,9 +1417,6 @@ This guarantees a consistent state even in case of a serious error.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x זמין !</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>קבצי ההתקנה פגומי×. בבקשה התקן מחדש ×ת FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>נתיב מקומי ×œ× ×–×ž×™×Ÿ עבור %x.</target>
@@ -1627,6 +1639,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Thank you, %x, for your donation and support!</source>
<target>תודה לך %x, עבור תרומתך ותמיכתך!</target>
+<source>Connections</source>
+<target>קשרי×</target>
+
<source>Recommended range:</source>
<target>טווח מומלץ:</target>
@@ -1798,9 +1813,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Automatic updates:</source>
<target>×¢×“×›×•× ×™× ×וטומטיי×:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>נדרשת מהדורת התרומות של FreeFileSync</target>
-
<source>Check for Program Updates</source>
<target>בדוק ×§×™×•× ×¢×“×›×•× ×™ תוכנה</target>
@@ -1990,6 +2002,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Edit with FreeFileSync</source>
<target>ערוך ×¢× FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>×‘×ž×§×•× ×¤×¨×¡×•×ž×ª, להלן ×—×™×”.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>הגירסה הניידת של FreeFileSync ××™× ×” ניתנת להתקנה בתת תיקייה של %x.</target>
@@ -1999,3 +2014,6 @@ This guarantees a consistent state even in case of a serious error.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>×פשרת התקנה %x זמינה רק במהדורת תרומות של FreeFileSync.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>קבל ×ת מהדורת התרומות ×¢× ×ª×›×•× ×•×ª נוספות ועזור לשמר ×ת FreeFileSync נטול-פרסומות.</target>
+
diff --git a/FreeFileSync/Build/Languages/hindi.lng b/FreeFileSync/Build/Languages/hindi.lng
index be7a4d4a..d575a013 100755
--- a/FreeFileSync/Build/Languages/hindi.lng
+++ b/FreeFileSync/Build/Languages/hindi.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>वैकलà¥à¤ªà¤¿à¤• GlobalSettings.xml फ़ाइल का पथ।</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>सà¥à¤¥à¤¾à¤ªà¤¨à¤¾ फ़ाइलें दूषित हैं। FreeFileSync पà¥à¤¨à¤°à¥à¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करें।</target>
+
<source>Cannot find the following folders:</source>
<target>निमà¥à¤¨ फ़ोलडरà¥à¤¸ नहीं मिले:</target>
@@ -333,12 +336,12 @@ Actual: %y bytes
<source>Cannot find %x.</source>
<target>%x नहीं मिला।</target>
-<source>Cannot open file %x.</source>
-<target>%x फ़ाइल को खोलने में असमरà¥à¤¥à¥¤</target>
-
<source>Cannot find device %x.</source>
<target>डिवाइस %x नहीं मिला।</target>
+<source>Cannot open file %x.</source>
+<target>%x फ़ाइल को खोलने में असमरà¥à¤¥à¥¤</target>
+
<source>Type of item %x is not supported:</source>
<target>%x पà¥à¤°à¤•à¤¾à¤° का आइटम समरà¥à¤¥à¤¿à¤¤ नहीं है:</target>
@@ -465,6 +468,9 @@ Actual: %y bytes
<source>Total time:</source>
<target>कà¥à¤² समय:</target>
+<source>Cleaning up old log files...</source>
+<target>पà¥à¤°à¤¾à¤¨à¥‡ लॉग फ़ाइलà¥à¤¸ की सफाई हो रही है...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>फ़ाइल %x, पंकà¥à¤¤à¤¿ %y, सà¥à¤¤à¤‚भ %z पदचà¥à¤›à¥‡à¤¦à¤¨ में तà¥à¤°à¥à¤Ÿà¤¿à¥¤</target>
@@ -653,6 +659,15 @@ The command is triggered if:
<source>Multiple...</source>
<target>à¤à¤•à¤¾à¤§à¤¿à¤•...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>%x के फ़ाइल गà¥à¤£ लिख नहीं सकते।</target>
+
+<source>%x and %y have different content.</source>
+<target>%x और %y की सामगà¥à¤°à¥€ भिनà¥à¤¨ है।</target>
+
+<source>Data verification error:</source>
+<target>डेटा सतà¥à¤¯à¤¾à¤ªà¤¨ तà¥à¤°à¥à¤Ÿà¤¿:</target>
+
<source>Moving file %x to %y</source>
<target>फ़ाइल %x को %y यहाठले जाया जा रहा है</target>
@@ -677,14 +692,8 @@ The command is triggered if:
<source>Updating attributes of %x</source>
<target>%x के गà¥à¤£ अदà¥à¤¯à¤¤à¤¿à¤¤ हो रहे हैं</target>
-<source>Cannot write file attributes of %x.</source>
-<target>%x के फ़ाइल गà¥à¤£ लिख नहीं सकते।</target>
-
-<source>%x and %y have different content.</source>
-<target>%x और %y की सामगà¥à¤°à¥€ भिनà¥à¤¨ है।</target>
-
-<source>Data verification error:</source>
-<target>डेटा सतà¥à¤¯à¤¾à¤ªà¤¨ तà¥à¤°à¥à¤Ÿà¤¿:</target>
+<source>Source item %x not found</source>
+<target>सà¥à¤°à¥‹à¤¤ आइटम %x नहीं मिला</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>%x के लिठवॉलà¥à¤¯à¥‚म शॅडो पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¤¿ बनाई जा रही है...</target>
@@ -746,9 +755,6 @@ The command is triggered if:
<source>System: Shut down</source>
<target>सिसà¥à¤Ÿà¤®: बंद करें</target>
-<source>Cleaning up old log files...</source>
-<target>पà¥à¤°à¤¾à¤¨à¥‡ लॉग फ़ाइलà¥à¤¸ की सफाई हो रही है...</target>
-
<source>Stopped</source>
<target>रà¥à¤•à¤¾</target>
@@ -881,6 +887,12 @@ The command is triggered if:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>सà¥à¤¥à¤¾à¤¨à¥€à¤¯ फ़ाइल सिसà¥à¤Ÿà¤®, नेटवरà¥à¤• या MTP डिवà¥à¤¹à¤¾à¤‡à¤¸ पर कोई निरà¥à¤¦à¥‡à¤¶à¤¿à¤•à¤¾ चà¥à¤¨à¥‡à¤‚।</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>FreeFileSync दान संसà¥â€à¤•à¤°à¤£ आवशà¥à¤¯à¤•</target>
+
<source>&Save</source>
<target>सहेजें (&S)</target>
@@ -1031,6 +1043,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>दिवालोक बचत समय (डेलाईट सेविंग टाइम) पà¥à¤°à¤¹à¤¸à¥à¤¤à¤¨ करें</target>
+<source>Performance improvements:</source>
+<target>पà¥à¤°à¤¦à¤°à¥à¤¶à¤¨ सà¥à¤§à¤¾à¤°:</target>
+
+<source>Parallel file operations:</source>
+<target>समांतर फ़ाइल कारà¥à¤°à¤µà¤¾à¤ˆ:</target>
+
+<source>How to get best performance?</source>
+<target>सरà¥à¤µà¤¶à¥à¤°à¥‡à¤·à¥à¤  पà¥à¤°à¤¦à¤°à¥à¤¶à¤¨ कैसे पाà¤à¤‚?</target>
+
<source>Local settings:</source>
<target>सà¥à¤¥à¤¾à¤¨à¥€à¤¯ सेटिंगà¥à¤¸:</target>
@@ -1147,15 +1168,6 @@ The command is triggered if:
<source>Directory on server:</source>
<target>सरà¥à¤µà¤° पर निरà¥à¤¦à¥‡à¤¶à¤¿à¤•à¤¾:</target>
-<source>Performance improvements:</source>
-<target>पà¥à¤°à¤¦à¤°à¥à¤¶à¤¨ सà¥à¤§à¤¾à¤°:</target>
-
-<source>How to get best performance?</source>
-<target>सरà¥à¤µà¤¶à¥à¤°à¥‡à¤·à¥à¤  पà¥à¤°à¤¦à¤°à¥à¤¶à¤¨ कैसे पाà¤à¤‚?</target>
-
-<source>Connections for directory reading:</source>
-<target>निरà¥à¤¦à¥‡à¤¶à¤¿à¤•à¤¾ पठन के लिठकनेकà¥à¤¶à¤¨à¥à¤¸:</target>
-
<source>SFTP channels per connection:</source>
<target>SFTP चैनलà¥à¤¸ पà¥à¤°à¤¤à¤¿ कनेकà¥à¤¶à¤¨:</target>
@@ -1291,8 +1303,17 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>डिफ़ॉलà¥à¤Ÿ (&D)</target>
-<source>Source code written in C++ using:</source>
-<target>सà¥à¤°à¥‹à¤¤ कोड C++ में लिखा गया इनके पà¥à¤°à¤¯à¥‹à¤— से:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>पà¥à¤°à¤¤à¤¿à¤•à¥à¤°à¤¿à¤¯à¤¾ और सà¥à¤à¤¾à¤µà¥‹à¤‚ का सà¥à¤µà¤¾à¤—त है</target>
+
+<source>Home page</source>
+<target>मà¥à¤– पृषà¥à¤ </target>
+
+<source>FreeFileSync Forum</source>
+<target>FreeFileSync फ़ोरम</target>
+
+<source>Email</source>
+<target>ई-मेल</target>
<source>If you like FreeFileSync:</source>
<target>यदि आपको FreeFileSync पसंद है:</target>
@@ -1300,20 +1321,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Support with a donation</source>
<target>दान के साथ समरà¥à¤¥à¤¨ करें</target>
-<source>Donation details</source>
-<target>दान विवरण</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>सà¥à¤µà¤šà¤¾à¤²à¤¿à¤¤ अदà¥à¤¯à¤¤à¤¨ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤• दà¥à¤µà¤¾à¤°à¤¾ अकà¥à¤·à¤® किया गया है।</target>
-<source>Feedback and suggestions are welcome</source>
-<target>पà¥à¤°à¤¤à¤¿à¤•à¥à¤°à¤¿à¤¯à¤¾ और सà¥à¤à¤¾à¤µà¥‹à¤‚ का सà¥à¤µà¤¾à¤—त है</target>
-
-<source>Home page</source>
-<target>मà¥à¤– पृषà¥à¤ </target>
+<source>Donation details</source>
+<target>दान विवरण</target>
-<source>Email</source>
-<target>ई-मेल</target>
+<source>Source code written in C++ using:</source>
+<target>सà¥à¤°à¥‹à¤¤ कोड C++ में लिखा गया इनके पà¥à¤°à¤¯à¥‹à¤— से:</target>
<source>Published under the GNU General Public License</source>
<target>जीà¤à¤¨à¤¯à¥‚ जनरल पबà¥à¤²à¤¿à¤• लाइसेंस (GNU General Public License) के अंतरà¥à¤—त पà¥à¤°à¤•à¤¾à¤¶à¤¿à¤¤</target>
@@ -1402,9 +1417,6 @@ This guarantees a consistent state even in case of a serious error.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x उपलबà¥à¤§ है!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>सà¥à¤¥à¤¾à¤ªà¤¨à¤¾ फ़ाइलें दूषित हैं। FreeFileSync पà¥à¤¨à¤°à¥à¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करें।</target>
-
<source>Local path not available for %x.</source>
<target>%x के लिठसà¥à¤¥à¤¾à¤¨à¥€à¤¯ पथ उपलबà¥à¤§ नहीं है।</target>
@@ -1627,6 +1639,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Thank you, %x, for your donation and support!</source>
<target>धनà¥à¤¯à¤µà¤¾à¤¦, %x, आपके दान और समरà¥à¤¥à¤¨ के लिà¤!</target>
+<source>Connections</source>
+<target>कनेकà¥à¤¶à¤¨à¥à¤¸</target>
+
<source>Recommended range:</source>
<target>अनà¥à¤¶à¤‚सित शà¥à¤°à¥‡à¤£à¥€:</target>
@@ -1798,9 +1813,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Automatic updates:</source>
<target>सà¥à¤µà¤šà¤¾à¤²à¤¿à¤¤ अदà¥à¤¯à¤¤à¤¨:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>FreeFileSync दान संसà¥â€à¤•à¤°à¤£ आवशà¥à¤¯à¤•</target>
-
<source>Check for Program Updates</source>
<target>पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® अदà¥à¤¯à¤¤à¤¨à¥‹à¤‚ के लिठजाà¤à¤š करें</target>
@@ -1990,6 +2002,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Edit with FreeFileSync</source>
<target>FreeFileSync के साथ संपादित करें</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>कोई विजà¥à¤žà¤¾à¤ªà¤¨ के बजाय, यहाठà¤à¤• पशॠहै।</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>FreeFileSync पोरà¥à¤Ÿà¥‡à¤¬à¤² संसà¥à¤•à¤°à¤£ %x के उप-निरà¥à¤¦à¥‡à¤¶à¤¿à¤•à¤¾ में सà¥à¤¥à¤¾à¤ªà¤¿à¤¤ नहीं किया जा सकता।</target>
@@ -1999,3 +2014,6 @@ This guarantees a consistent state even in case of a serious error.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>%x सà¥à¤¥à¤¾à¤ªà¤¨à¤¾ विकलà¥à¤ª केवल FreeFileSync दान संसà¥â€à¤•à¤°à¤£ में ही उपलबà¥à¤§ है।</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>दान संसà¥â€à¤•à¤°à¤£ बोनस सà¥à¤µà¤¿à¤§à¤¾à¤“ं के साथ पà¥à¤°à¤¾à¤ªà¥à¤¤ करें और FreeFileSync विजà¥à¤žà¤¾à¤ªà¤¨-रहित रखने में मदद करें।</target>
+
diff --git a/FreeFileSync/Build/Languages/hungarian.lng b/FreeFileSync/Build/Languages/hungarian.lng
index 307ff95e..7ba7daa5 100755
--- a/FreeFileSync/Build/Languages/hungarian.lng
+++ b/FreeFileSync/Build/Languages/hungarian.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Útvonal egy alternatív GlobalSettings.xml állományhoz.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>A telepítő állományok sérültek. Installálja újra a FreeFileSync-et.</target>
+
<source>Cannot find the following folders:</source>
<target>A következő könyvtárak nem találhatóak:</target>
@@ -333,12 +336,12 @@ Tényleges: %y bájt
<source>Cannot find %x.</source>
<target>%x nem található.</target>
-<source>Cannot open file %x.</source>
-<target>%x állomány nem nyitható meg.</target>
-
<source>Cannot find device %x.</source>
<target>%x eszköz nem található.</target>
+<source>Cannot open file %x.</source>
+<target>%x állomány nem nyitható meg.</target>
+
<source>Type of item %x is not supported:</source>
<target>%x elem típusa nem támogatott:</target>
@@ -465,6 +468,9 @@ Tényleges: %y bájt
<source>Total time:</source>
<target>Összes időszükséglet:</target>
+<source>Cleaning up old log files...</source>
+<target>Régi log állományok törlése...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Hiba történt a feldolgozás közben: %x állomány, %y sor, %z oszlop.</target>
@@ -653,6 +659,15 @@ A parancs végrehajtódik, ha:
<source>Multiple...</source>
<target>Sokszoroz...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>%x állomány attribútumainak írása nem sikerült.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x és %y tartalma különböző.</target>
+
+<source>Data verification error:</source>
+<target>Adat ellenőrzési hiba:</target>
+
<source>Moving file %x to %y</source>
<target>%x állomány mozgatása ide: %y</target>
@@ -677,14 +692,8 @@ A parancs végrehajtódik, ha:
<source>Updating attributes of %x</source>
<target>%x attribútumainak frissítése</target>
-<source>Cannot write file attributes of %x.</source>
-<target>%x állomány attribútumainak írása nem sikerült.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x és %y tartalma különböző.</target>
-
-<source>Data verification error:</source>
-<target>Adat ellenőrzési hiba:</target>
+<source>Source item %x not found</source>
+<target>%x forrás állományt nem találom</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>%x számára árnyékmásolat-kötetet készítése...</target>
@@ -746,9 +755,6 @@ A parancs végrehajtódik, ha:
<source>System: Shut down</source>
<target>Rendszer: Leállítva</target>
-<source>Cleaning up old log files...</source>
-<target>Régi log állományok törlése...</target>
-
<source>Stopped</source>
<target>Leállítva</target>
@@ -881,6 +887,12 @@ A parancs végrehajtódik, ha:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Kérem válasszon egy könyvtárat a helyi fájlrendszerben, a hálózaton vagy egy MTP eszközön.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>A FreeFileSync támogatói kiadása szükséges</target>
+
<source>&Save</source>
<target>&Ment</target>
@@ -1031,6 +1043,15 @@ A parancs végrehajtódik, ha:
<source>Handle daylight saving time</source>
<target>Kezelje a nyári időszámítás különbségét</target>
+<source>Performance improvements:</source>
+<target>Teljesítmény növelése:</target>
+
+<source>Parallel file operations:</source>
+<target>Párhuzamos fájl-műveletek:</target>
+
+<source>How to get best performance?</source>
+<target>Hogyan lehet elérni a legjobb teljesítményt?</target>
+
<source>Local settings:</source>
<target>Helyi beállítások:</target>
@@ -1147,15 +1168,6 @@ A parancs végrehajtódik, ha:
<source>Directory on server:</source>
<target>Könyvtár az alábbi szerveren:</target>
-<source>Performance improvements:</source>
-<target>Teljesítmény növelése:</target>
-
-<source>How to get best performance?</source>
-<target>Hogyan lehet elérni a legjobb teljesítményt?</target>
-
-<source>Connections for directory reading:</source>
-<target>Kapcsolatok a könyvtár olvasásához:</target>
-
<source>SFTP channels per connection:</source>
<target>SFTP csatornák száma kapcsolatonként:</target>
@@ -1291,8 +1303,17 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is.
<source>&Default</source>
<target>&Alapértelmezett</target>
-<source>Source code written in C++ using:</source>
-<target>A programot C++-ban fejlesztették a következők felhasználásával:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Várjuk a visszajelzéseket és az ötleteket</target>
+
+<source>Home page</source>
+<target>Honlap</target>
+
+<source>FreeFileSync Forum</source>
+<target>FreeFileSync Fórum</target>
+
+<source>Email</source>
+<target>E-mail</target>
<source>If you like FreeFileSync:</source>
<target>Ha szereted a FreeFileSync-et:</target>
@@ -1300,20 +1321,14 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is.
<source>Support with a donation</source>
<target>Támogassa adománnyal</target>
-<source>Donation details</source>
-<target>Támogatás részletei</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>Az automatikus frissítést a rendszergazda kapcsolta ki.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Várjuk a visszajelzéseket és az ötleteket</target>
-
-<source>Home page</source>
-<target>Honlap</target>
+<source>Donation details</source>
+<target>Támogatás részletei</target>
-<source>Email</source>
-<target>E-mail</target>
+<source>Source code written in C++ using:</source>
+<target>A programot C++-ban fejlesztették a következők felhasználásával:</target>
<source>Published under the GNU General Public License</source>
<target>Közzétéve a GNU General Public License alatt</target>
@@ -1402,9 +1417,6 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x elérhető!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>A telepítő állományok sérültek. Installálja újra a FreeFileSync-et.</target>
-
<source>Local path not available for %x.</source>
<target>%x-hez a helyi útvonal nem érhető el.</target>
@@ -1627,6 +1639,9 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is.
<source>Thank you, %x, for your donation and support!</source>
<target>%x, köszönöm Önnek adományát és támogatását!</target>
+<source>Connections</source>
+<target>Kapcsolatok</target>
+
<source>Recommended range:</source>
<target>A javasolt tartomány:</target>
@@ -1798,9 +1813,6 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is.
<source>Automatic updates:</source>
<target>Automatikus frissítések:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>A FreeFileSync támogatói kiadása szükséges</target>
-
<source>Check for Program Updates</source>
<target>Program-frissítések ellenőrzése</target>
@@ -1990,6 +2002,9 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is.
<source>Edit with FreeFileSync</source>
<target>Szerkesztés FreeFileSync-kel</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>A reklám helyett itt egy állat.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>A FreeFileSync hordozható változatát nem lehet telepíteni %x egyik alkönyvtárába.</target>
@@ -1997,5 +2012,8 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is.
<target>Válassza a helyi telepítési módot vagy válasszon másik könytárat a telepítéshez.</target>
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
-<target>%x telepítési opció csak a FreeFileSync adományozói kiadásában érhető el.</target>
+<target>%x telepítési opció csak a FreeFileSync Támogatói Kiadásában érhető el.</target>
+
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Használd a Támogatói Kiadást bónusz szolgáltatásokkal és segits reklám-mentesen tartani a FreeFileSync-et.</target>
diff --git a/FreeFileSync/Build/Languages/italian.lng b/FreeFileSync/Build/Languages/italian.lng
index 707c244f..0a50b5e6 100755
--- a/FreeFileSync/Build/Languages/italian.lng
+++ b/FreeFileSync/Build/Languages/italian.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Percorso di un file GlobalSettings.xml alternativo.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>I file di installazione sono danneggiati. Si prega di reinstallare FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>Impossibile trovare le seguenti cartelle:</target>
@@ -333,12 +336,12 @@ Attuale: %y byte
<source>Cannot find %x.</source>
<target>Impossibile trovare %x.</target>
-<source>Cannot open file %x.</source>
-<target>Impossibile aprire il file %x.</target>
-
<source>Cannot find device %x.</source>
<target>Impossibile trovare dispositivo %x.</target>
+<source>Cannot open file %x.</source>
+<target>Impossibile aprire il file %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>Il tipo di oggetto %x non è supportato:</target>
@@ -465,6 +468,9 @@ Attuale: %y byte
<source>Total time:</source>
<target>Tempo totale:</target>
+<source>Cleaning up old log files...</source>
+<target>Pulizia vecchi file di log ...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Errore nel parsing del file %x, riga %y, colonna %z.</target>
@@ -653,6 +659,15 @@ Il comando è attivato se:
<source>Multiple...</source>
<target>Multiplo...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Impossibile scrivere gli attributi del file %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x e %y hanno un contenuto diverso.</target>
+
+<source>Data verification error:</source>
+<target>Errore di verifica dei dati:</target>
+
<source>Moving file %x to %y</source>
<target>Spostamento file %x in %y</target>
@@ -677,14 +692,8 @@ Il comando è attivato se:
<source>Updating attributes of %x</source>
<target>Aggiornamento attributi di %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Impossibile scrivere gli attributi del file %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x e %y hanno un contenuto diverso.</target>
-
-<source>Data verification error:</source>
-<target>Errore di verifica dei dati:</target>
+<source>Source item %x not found</source>
+<target>Elemento di origine %x non trovato</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>Creazione di un Volume Shadow Copy per %x...</target>
@@ -746,9 +755,6 @@ Il comando è attivato se:
<source>System: Shut down</source>
<target>Sistema: Spento</target>
-<source>Cleaning up old log files...</source>
-<target>Pulizia vecchi file di log ...</target>
-
<source>Stopped</source>
<target>Arrestato</target>
@@ -881,6 +887,12 @@ Il comando è attivato se:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Si prega di selezionare una cartella su un file system locale, di rete o un dispositivo MTP.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Richiede FreeFileSync Donation Edition</target>
+
<source>&Save</source>
<target>&Salva</target>
@@ -1031,6 +1043,15 @@ Il comando è attivato se:
<source>Handle daylight saving time</source>
<target>Maneggiare l'ora legale</target>
+<source>Performance improvements:</source>
+<target>Miglioramenti delle prestazioni:</target>
+
+<source>Parallel file operations:</source>
+<target>Operazioni parallele sul file:</target>
+
+<source>How to get best performance?</source>
+<target>Come ottenere le migliori prestazioni?</target>
+
<source>Local settings:</source>
<target>Impostazioni Locali:</target>
@@ -1147,15 +1168,6 @@ Il comando è attivato se:
<source>Directory on server:</source>
<target>Directory su server:</target>
-<source>Performance improvements:</source>
-<target>Miglioramenti delle prestazioni:</target>
-
-<source>How to get best performance?</source>
-<target>Come ottenere le migliori prestazioni?</target>
-
-<source>Connections for directory reading:</source>
-<target>Collegamenti per la lettura delle directory:</target>
-
<source>SFTP channels per connection:</source>
<target>Canali SFTP per il collegamento:</target>
@@ -1260,7 +1272,7 @@ Copy to a temporary file (*.ffs_tmp) before overwriting target.
This guarantees a consistent state even in case of a serious error.
</source>
<target>
-Copiare in un file temporaneo ( *.ffs_tmp ) prima di sovrascrivere il bersaglio.
+Copiare in un file temporaneo (*.ffs_tmp) prima di sovrascrivere il bersaglio.
Questo garantisce uno stato consistente anche in caso di errore grave.
</target>
@@ -1291,8 +1303,17 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<source>&Default</source>
<target>&Predefinito</target>
-<source>Source code written in C++ using:</source>
-<target>Codice sorgente scritto in C++ utilizzando:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Ogni commento o suggerimento è ben accetto</target>
+
+<source>Home page</source>
+<target>Pagina Iniziale</target>
+
+<source>FreeFileSync Forum</source>
+<target>Forum di FreeFileSync</target>
+
+<source>Email</source>
+<target>Email</target>
<source>If you like FreeFileSync:</source>
<target>Se ti piace FreeFileSync:</target>
@@ -1300,20 +1321,14 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<source>Support with a donation</source>
<target>Supporto con una donazione</target>
-<source>Donation details</source>
-<target>dettagli Donazione</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>L'aggiornamento automatico è stato disabilitato dall'amministratore.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Ogni commento o suggerimento è ben accetto</target>
-
-<source>Home page</source>
-<target>Pagina Iniziale</target>
+<source>Donation details</source>
+<target>dettagli Donazione</target>
-<source>Email</source>
-<target>Email</target>
+<source>Source code written in C++ using:</source>
+<target>Codice sorgente scritto in C++ utilizzando:</target>
<source>Published under the GNU General Public License</source>
<target>Pubblicato con licenza GNU General Public</target>
@@ -1402,9 +1417,6 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x è disponibile!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>I file di installazione sono danneggiati. Si prega di reinstallare FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>Percorso locale non disponibile per %x.</target>
@@ -1627,6 +1639,9 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<source>Thank you, %x, for your donation and support!</source>
<target>Grazie, %x, per la vostra donazione e il supporto!</target>
+<source>Connections</source>
+<target>Connessioni</target>
+
<source>Recommended range:</source>
<target>Intervallo consigliato:</target>
@@ -1798,9 +1813,6 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<source>Automatic updates:</source>
<target>Aggiornamenti automatici:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Richiede FreeFileSync Donation Edition</target>
-
<source>Check for Program Updates</source>
<target>Controlla Aggiornamenti del Programma</target>
@@ -1990,6 +2002,9 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<source>Edit with FreeFileSync</source>
<target>Modifica con FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>Invece di un annuncio, ecco un animale.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>La versione portatile FreeFileSync non può essere installata in una sottocartella di %x.</target>
@@ -1999,3 +2014,6 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>L'opzione di installazione %x è disponibile solo nell'edizione di donazione FreeFileSync.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Ottieni l'Edizione Donazione con funzioni bonus e aiuto per mantenere FreeFileSync senza pubblicità.</target>
+
diff --git a/FreeFileSync/Build/Languages/japanese.lng b/FreeFileSync/Build/Languages/japanese.lng
index cbbc32ac..0776af38 100755
--- a/FreeFileSync/Build/Languages/japanese.lng
+++ b/FreeFileSync/Build/Languages/japanese.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>代替グローãƒãƒ«è¨­å®š.xml ファイルã®ãƒ‘ス.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>インストール ファイルãŒç ´æã—ã¦ã„ã¾ã™ã€FreeFileSync ã‚’å†ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã—ã¦ãã ã•ã„.</target>
+
<source>Cannot find the following folders:</source>
<target>以下ã®ãƒ•ã‚©ãƒ«ãƒ€ãŒã¿ã¤ã‹ã‚Šã¾ã›ã‚“:</target>
@@ -332,12 +335,12 @@ Actual: %y bytes
<source>Cannot find %x.</source>
<target>%x ãŒã¿ã¤ã‹ã‚Šã¾ã›ã‚“.</target>
-<source>Cannot open file %x.</source>
-<target>ファイル %x ã‚’é–‹ã‘ã¾ã›ã‚“.</target>
-
<source>Cannot find device %x.</source>
<target>デãƒã‚¤ã‚¹ %x ãŒã¿ã¤ã‹ã‚Šã¾ã›ã‚“.</target>
+<source>Cannot open file %x.</source>
+<target>ファイル %x ã‚’é–‹ã‘ã¾ã›ã‚“.</target>
+
<source>Type of item %x is not supported:</source>
<target>é …ç›® %x ã«ã¯å¯¾å¿œã—ã¦ã„ã¾ã›ã‚“:</target>
@@ -460,6 +463,9 @@ Actual: %y bytes
<source>Total time:</source>
<target>åˆè¨ˆæ™‚é–“:</target>
+<source>Cleaning up old log files...</source>
+<target>å¤ã„ログファイルをクリーン...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>ファイル %x ã®æ§‹æ–‡è§£æžã‚¨ãƒ©ãƒ¼, è¡Œ %y, 列 %z.</target>
@@ -647,6 +653,15 @@ The command is triggered if:
<source>Multiple...</source>
<target>複数処ç†...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>%x ã®ãƒ•ã‚¡ã‚¤ãƒ«å±žæ€§ã‚’書ãè¾¼ã‚ã¾ã›ã‚“.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x 㨠%y ã®å†…容ã¯ç•°ãªã‚Šã¾ã™.</target>
+
+<source>Data verification error:</source>
+<target>データ検証エラー:</target>
+
<source>Moving file %x to %y</source>
<target>ファイル %x ã‚’ %y ã«ç§»å‹•ä¸­</target>
@@ -671,14 +686,8 @@ The command is triggered if:
<source>Updating attributes of %x</source>
<target>%x ã®å±žæ€§ã‚’æ›´æ–°</target>
-<source>Cannot write file attributes of %x.</source>
-<target>%x ã®ãƒ•ã‚¡ã‚¤ãƒ«å±žæ€§ã‚’書ãè¾¼ã‚ã¾ã›ã‚“.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x 㨠%y ã®å†…容ã¯ç•°ãªã‚Šã¾ã™.</target>
-
-<source>Data verification error:</source>
-<target>データ検証エラー:</target>
+<source>Source item %x not found</source>
+<target>ソース項目 %x ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>ボリュームシャドウコピーを作æˆä¸­ %x...</target>
@@ -740,9 +749,6 @@ The command is triggered if:
<source>System: Shut down</source>
<target>システム: シャットダウン</target>
-<source>Cleaning up old log files...</source>
-<target>å¤ã„ログファイルをクリーン...</target>
-
<source>Stopped</source>
<target>åœæ­¢</target>
@@ -874,6 +880,12 @@ The command is triggered if:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>ローカルファイルシステムã€ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã¾ãŸã¯ MTP デãƒã‚¤ã‚¹ä¸Šã®ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠž.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>FreeFileSync 寄付版ãŒå¿…è¦ã§ã™</target>
+
<source>&Save</source>
<target>ä¿å­˜(&S)</target>
@@ -1024,6 +1036,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>å¤æ™‚é–“ã®å–り扱ã„</target>
+<source>Performance improvements:</source>
+<target>パフォーマンスå‘上:</target>
+
+<source>Parallel file operations:</source>
+<target>並列ファイルæ“作:</target>
+
+<source>How to get best performance?</source>
+<target>最é©ãªãƒ‘フォーマンスã¨ã¯?</target>
+
<source>Local settings:</source>
<target>ローカル設定:</target>
@@ -1140,15 +1161,6 @@ The command is triggered if:
<source>Directory on server:</source>
<target>サーãƒä¸Šã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª:</target>
-<source>Performance improvements:</source>
-<target>パフォーマンスå‘上:</target>
-
-<source>How to get best performance?</source>
-<target>最é©ãªãƒ‘フォーマンスã¨ã¯?</target>
-
-<source>Connections for directory reading:</source>
-<target>ディレクトリã®èª­ã¿å–り接続数:</target>
-
<source>SFTP channels per connection:</source>
<target>接続当ãŸã‚Šã® SFTP ãƒãƒ£ãƒ³ãƒãƒ«æ•°:</target>
@@ -1284,8 +1296,17 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>デフォルト(&D)</target>
-<source>Source code written in C++ using:</source>
-<target>ソースコード㯠C++ ã§æ›¸ã‹ã‚Œã¦ã„ã¾ã™:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>フィードãƒãƒƒã‚¯ã€æ案ãªã©ã¯ã“ã¡ã‚‰ã‹ã‚‰</target>
+
+<source>Home page</source>
+<target>ホーム ページ</target>
+
+<source>FreeFileSync Forum</source>
+<target>FreeFileSync フォーラム</target>
+
+<source>Email</source>
+<target>E-メール</target>
<source>If you like FreeFileSync:</source>
<target>FreeFileSync ã‚’æ°—ã«å…¥ã£ã¦ãã‚ŒãŸæ–¹ã¸:</target>
@@ -1293,20 +1314,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Support with a donation</source>
<target>寄付ã«ã‚ˆã‚‹ã‚µãƒãƒ¼ãƒˆ</target>
-<source>Donation details</source>
-<target>寄付ã®è©³ç´°</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>自動アップデートã¯ç®¡ç†è€…ã«ã‚ˆã£ã¦ç„¡åŠ¹ã«ã•ã‚Œã¦ã„ã¾ã™.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>フィードãƒãƒƒã‚¯ã€æ案ãªã©ã¯ã“ã¡ã‚‰ã‹ã‚‰</target>
-
-<source>Home page</source>
-<target>ホーム ページ</target>
+<source>Donation details</source>
+<target>寄付ã®è©³ç´°</target>
-<source>Email</source>
-<target>E-メール</target>
+<source>Source code written in C++ using:</source>
+<target>ソースコード㯠C++ ã§æ›¸ã‹ã‚Œã¦ã„ã¾ã™:</target>
<source>Published under the GNU General Public License</source>
<target>GNU 一般共有使用許諾ã«åŸºã¥ã公開</target>
@@ -1395,9 +1410,6 @@ This guarantees a consistent state even in case of a serious error.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x ãŒåˆ©ç”¨ã§ãã¾ã™!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>インストール ファイルãŒç ´æã—ã¦ã„ã¾ã™ã€FreeFileSync ã‚’å†ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã—ã¦ãã ã•ã„.</target>
-
<source>Local path not available for %x.</source>
<target>利用ã§ããªã„ローカスパス %x.</target>
@@ -1616,6 +1628,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Thank you, %x, for your donation and support!</source>
<target>ã‚ã‚ŠãŒã¨ã† %x, ã‚ãªãŸã®å¯„付ã¨æ”¯æ´ã«æ„Ÿè¬ã—ã¾ã™!!</target>
+<source>Connections</source>
+<target>接続</target>
+
<source>Recommended range:</source>
<target>推奨ã•ã‚Œã‚‹ç¯„囲:</target>
@@ -1784,9 +1799,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Automatic updates:</source>
<target>自動アップデート:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>FreeFileSync 寄付版ãŒå¿…è¦ã§ã™</target>
-
<source>Check for Program Updates</source>
<target>プログラムã®ã‚¢ãƒƒãƒ—デートを確èª</target>
@@ -1974,6 +1986,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Edit with FreeFileSync</source>
<target>FreeFileSync ã§ç·¨é›†</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>ã“ã‚Œã¯åºƒå‘Šã®ä»£ã‚ã‚Šã§ã™.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>FreeFileSync ãƒãƒ¼ã‚¿ãƒ–ル版㯠%x ã®ã‚µãƒ–フォルダã«ã¯ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã§ãã¾ã›ã‚“.</target>
@@ -1983,3 +1998,6 @@ This guarantees a consistent state even in case of a serious error.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>インストール オプション %x ã¯ã€FreeFileSync 寄付版ã§ã®ã¿åˆ©ç”¨å¯èƒ½ã§ã™.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>寄付をã™ã‚‹ã“ã¨ã§åºƒå‘ŠãŒä¸€åˆ‡ç„¡ãã€ãƒœãƒ¼ãƒŠã‚¹æ©Ÿèƒ½ãŒä»˜ã„㟠FreeFileSync を使用ã§ãã¾ã™.</target>
+
diff --git a/FreeFileSync/Build/Languages/korean.lng b/FreeFileSync/Build/Languages/korean.lng
index b12f0763..1225d556 100755
--- a/FreeFileSync/Build/Languages/korean.lng
+++ b/FreeFileSync/Build/Languages/korean.lng
@@ -7,6 +7,9 @@
<plural_definition>0</plural_definition>
</header>
+<source>Defined by context of use</source>
+<target></target>
+
<source>Both sides have changed since last synchronization.</source>
<target>마지막 ë™ê¸°í™” ìž‘ì—… ì´í›„, 양측 ëª¨ë‘ ë³€ê²½ ë˜ì—ˆìŠµë‹ˆë‹¤.</target>
@@ -112,6 +115,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>GlobalSettings.xml 대체 파ì¼ì— 대한 경로.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>설치 파ì¼ì´ ì†ìƒë˜ì—ˆìŠµë‹ˆë‹¤. FreeFileSync를 다시 설치하십시오.</target>
+
<source>Cannot find the following folders:</source>
<target>ë‹¤ìŒ í´ë”를 ì°¾ì„ ìˆ˜ 없습니다:</target>
@@ -332,12 +338,12 @@ Actual: %y bytes
<source>Cannot find %x.</source>
<target>%xì„(를) ì°¾ì„ ìˆ˜ 없습니다.</target>
-<source>Cannot open file %x.</source>
-<target>íŒŒì¼ %xì„(를) ì—´ 수 없습니다.</target>
-
<source>Cannot find device %x.</source>
<target>장치 %xì„(를) ì°¾ì„ ìˆ˜ 없습니다.</target>
+<source>Cannot open file %x.</source>
+<target>íŒŒì¼ %xì„(를) ì—´ 수 없습니다.</target>
+
<source>Type of item %x is not supported:</source>
<target>항목 %xì˜ í˜•ì‹ì€ 지ì›ë˜ì§€ 않습니다:</target>
@@ -460,6 +466,9 @@ Actual: %y bytes
<source>Total time:</source>
<target>전체 시간:</target>
+<source>Cleaning up old log files...</source>
+<target>ì´ì „ 로그 íŒŒì¼ ì •ë¦¬ 중...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>ë¶„ì„ ì˜¤ë¥˜ - 파ì¼: %x; í–‰: %y; ì—´: %z.</target>
@@ -647,6 +656,15 @@ The command is triggered if:
<source>Multiple...</source>
<target>다중처리 (멀티플) 작업...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>%xì˜ íŒŒì¼ ì†ì„±ì„ 쓸 수 없습니다.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x와(ê³¼) %yì˜ ì½˜í…츠가 다릅니다.</target>
+
+<source>Data verification error:</source>
+<target>ë°ì´í„° í™•ì¸ ì˜¤ë¥˜:</target>
+
<source>Moving file %x to %y</source>
<target>íŒŒì¼ %xì„(를) %y(으)ë¡œ ì´ë™ 중</target>
@@ -671,14 +689,8 @@ The command is triggered if:
<source>Updating attributes of %x</source>
<target>%x ì†ì„± ì—…ë°ì´íŠ¸ 중</target>
-<source>Cannot write file attributes of %x.</source>
-<target>%xì˜ íŒŒì¼ ì†ì„±ì„ 쓸 수 없습니다.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x와(ê³¼) %yì˜ ì½˜í…츠가 다릅니다.</target>
-
-<source>Data verification error:</source>
-<target>ë°ì´í„° í™•ì¸ ì˜¤ë¥˜:</target>
+<source>Source item %x not found</source>
+<target>ì›ë³¸ 항목 %xì„(를) ì°¾ì„ ìˆ˜ ì—†ìŒ</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>%xì„(를) 위한 Volume Shadow Copy ìƒì„± 중...</target>
@@ -740,9 +752,6 @@ The command is triggered if:
<source>System: Shut down</source>
<target>시스템: 종료</target>
-<source>Cleaning up old log files...</source>
-<target>ì´ì „ 로그 íŒŒì¼ ì •ë¦¬ 중...</target>
-
<source>Stopped</source>
<target>중단</target>
@@ -874,6 +883,9 @@ The command is triggered if:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>로컬 íŒŒì¼ ì‹œìŠ¤í…œ, ë„¤íŠ¸ì›Œí¬ ë˜ëŠ” MTP 장치ì—ì„œì˜ í´ë” 하나를 ì„ íƒí•˜ì‹­ì‹œì˜¤.</target>
+<source>Requires FreeFileSync Donation Edition</source>
+<target>FreeFileSync ê¸°ë¶€ìž ì—ë””ì…˜ì´ í•„ìš”í•©ë‹ˆë‹¤.</target>
+
<source>&Save</source>
<target>저장(&S)</target>
@@ -1024,6 +1036,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>서머타임 설정</target>
+<source>Performance improvements:</source>
+<target>성능 í–¥ìƒ:</target>
+
+<source>Parallel file operations:</source>
+<target>병렬 íŒŒì¼ ìž‘ì—…:</target>
+
+<source>How to get best performance?</source>
+<target>최ìƒì˜ ì„±ëŠ¥ì„ ì–»ëŠ” 방법ì€?</target>
+
<source>Local settings:</source>
<target>로컬 설정:</target>
@@ -1140,15 +1161,6 @@ The command is triggered if:
<source>Directory on server:</source>
<target>서버 디렉터리:</target>
-<source>Performance improvements:</source>
-<target>성능 í–¥ìƒ:</target>
-
-<source>How to get best performance?</source>
-<target>최ìƒì˜ ì„±ëŠ¥ì„ ì–»ëŠ” 방법ì€?</target>
-
-<source>Connections for directory reading:</source>
-<target>디렉토리 ì½ê¸°ë¥¼ 위한 ì—°ê²°:</target>
-
<source>SFTP channels per connection:</source>
<target>ì—°ê²° 당 SFTP ì±„ë„ ìˆ˜:</target>
@@ -1284,8 +1296,17 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>기본 설정/값(&D)</target>
-<source>Source code written in C++ using:</source>
-<target>소스코드는 C++ 언어로 아래 íˆ´ì„ ì‚¬ìš©í•˜ì—¬ 작성ë˜ì—ˆìŠµë‹ˆë‹¤:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>모든 ì˜ê²¬ ë° ê±´ì˜/ì œì•ˆì„ í™˜ì˜í•©ë‹ˆë‹¤</target>
+
+<source>Home page</source>
+<target>홈페ì´ì§€</target>
+
+<source>FreeFileSync Forum</source>
+<target>FreeFileSync í¬ëŸ¼</target>
+
+<source>Email</source>
+<target>ì´ë©”ì¼</target>
<source>If you like FreeFileSync:</source>
<target>FreeFileSync를 위한 기부:</target>
@@ -1293,20 +1314,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Support with a donation</source>
<target>기부금 지ì›</target>
-<source>Donation details</source>
-<target>기부 관련 세부정보</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>관리ìžê°€ ìžë™ ì—…ë°ì´í„°ë¥¼ 비활성화했습니다.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>모든 ì˜ê²¬ ë° ê±´ì˜/ì œì•ˆì„ í™˜ì˜í•©ë‹ˆë‹¤</target>
-
-<source>Home page</source>
-<target>홈페ì´ì§€</target>
+<source>Donation details</source>
+<target>기부 관련 세부정보</target>
-<source>Email</source>
-<target>ì´ë©”ì¼</target>
+<source>Source code written in C++ using:</source>
+<target>소스코드는 C++ 언어로 아래 íˆ´ì„ ì‚¬ìš©í•˜ì—¬ 작성ë˜ì—ˆìŠµë‹ˆë‹¤:</target>
<source>Published under the GNU General Public License</source>
<target>GNU ì¼ë°˜ 공용 ë¼ì´ì„¼ìŠ¤ì— ì˜í•œ 출시</target>
@@ -1315,7 +1330,7 @@ This guarantees a consistent state even in case of a serious error.
<target>현지화 ìž‘ì—…ì— ê¹Šì€ ê°ì‚¬ 드립니다:</target>
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
-<target>ë‹¤ìŒ ì¤‘ í•œ ë°©ë²•ì„ ì‚¬ìš©í•˜ì—¬ FreeFileSync 유료 ë²„ì „ì„ í™œì„±í™”í•˜ì„¸ìš”:</target>
+<target>ë‹¤ìŒ ì¤‘ í•œ ë°©ë²•ì„ ì‚¬ìš©í•˜ì—¬ FreeFileSync ê¸°ë¶€ìž ì—ë””ì…˜ì„ í™œì„±í™”í•˜ì„¸ìš”:</target>
<source>1. Activate via internet now:</source>
<target>1. ì¸í„°ë„·ì„ 통해 지금 바로 활성화:</target>
@@ -1360,7 +1375,7 @@ This guarantees a consistent state even in case of a serious error.
<target>시간간격(타임스팬) ì„ íƒ</target>
<source>FreeFileSync Donation Edition</source>
-<target>FreeFileSync 유료 버전</target>
+<target>FreeFileSync ê¸°ë¶€ìž ì—디션</target>
<source>Highlight Configurations</source>
<target>강조 표시 구성</target>
@@ -1395,9 +1410,6 @@ This guarantees a consistent state even in case of a serious error.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x 버전 출시!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>설치 파ì¼ì´ ì†ìƒë˜ì—ˆìŠµë‹ˆë‹¤. FreeFileSync를 다시 설치하십시오.</target>
-
<source>Local path not available for %x.</source>
<target>%xì—ì„œ 로컬 경로를 사용할 수 없습니다.</target>
@@ -1616,6 +1628,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Thank you, %x, for your donation and support!</source>
<target>%xë‹˜ì˜ ê¸°ë¶€ì™€ 지ì›ì— ê°ì‚¬ë“œë¦½ë‹ˆë‹¤!</target>
+<source>Connections</source>
+<target>ì—°ê²°</target>
+
<source>Recommended range:</source>
<target>권장 범위:</target>
@@ -1784,9 +1799,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Automatic updates:</source>
<target>ìžë™ ì—…ë°ì´íŠ¸:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>FreeFileSync 유료 ë²„ì „ì´ í•„ìš”í•©ë‹ˆë‹¤.</target>
-
<source>Check for Program Updates</source>
<target>프로그램 ì—…ë°ì´íŠ¸ 확ì¸</target>
@@ -1806,7 +1818,7 @@ This guarantees a consistent state even in case of a serious error.
<target>다운로드(&D)</target>
<source>FreeFileSync is up to date.</source>
-<target>FreeFileSync는 현재 최신버전 ìƒíƒœìž…니다.</target>
+<target>현재 FreeFileSync는 최신 버전입니다.</target>
<source>Cannot find current FreeFileSync version number online. A newer version is likely available. Check manually now?</source>
<target>현재 사용 ì¤‘ì¸ FreeFileSync 버전 번호를 온ë¼ì¸ì—ì„œ ì°¾ì„ ìˆ˜ 없습니다. 새로운 ë²„ì „ì´ ìžˆì„ ìˆ˜ 있으니 수ë™ìœ¼ë¡œ 확ì¸í•´ 보시겠습니까?</target>
@@ -1821,7 +1833,7 @@ This guarantees a consistent state even in case of a serious error.
<target>설치가 다른 ìš´ì˜ì²´ì œì— 등ë¡ë˜ì—ˆìŠµë‹ˆë‹¤.</target>
<source>Failed to activate FreeFileSync Donation Edition.</source>
-<target>FreeFileSync 유료 버전 í™œì„±í™”ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.</target>
+<target>FreeFileSync ê¸°ë¶€ìž ì—디션 í™œì„±í™”ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.</target>
<source>Incorrect activation key.</source>
<target>ìž˜ëª»ëœ í™œì„±í™” 키.</target>
@@ -1974,6 +1986,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Edit with FreeFileSync</source>
<target>FreeFileSync로 편집</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>ê´‘ê³  ëŒ€ì‹ ì— ì—¬ê¸° ë™ë¬¼ 한마리가 있어요.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>FreeFileSync 휴대용 ë²„ì „ì€ %xì˜ í•˜ìœ„ í´ë”ì— ì„¤ì¹˜í•  수 없습니다.</target>
@@ -1981,5 +1996,8 @@ This guarantees a consistent state even in case of a serious error.
<target>로컬 설치 ìœ í˜•ì„ ì„ íƒí•˜ê±°ë‚˜ 설치할 다른 í´ë”를 ì„ íƒí•˜ì‹­ì‹œì˜¤.</target>
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
-<target>%x 설치 ì˜µì…˜ì€ FreeFileSync 유료 버전ì—서만 사용 가능합니다.</target>
+<target>%x 설치 ì˜µì…˜ì€ FreeFileSync ê¸°ë¶€ìž ì—디션ì—서만 사용 가능합니다.</target>
+
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>보너스 ê¸°ëŠ¥ì´ ìžˆëŠ” ê¸°ë¶€ìž ì—ë””ì…˜ì„ ì‚¬ìš©í•˜ì‹œì–´ 광고없는 FreeFileSyncê°€ 유지ë˜ë„ë¡ ë„와주세요.</target>
diff --git a/FreeFileSync/Build/Languages/lithuanian.lng b/FreeFileSync/Build/Languages/lithuanian.lng
index 97c435fe..beceddd9 100755
--- a/FreeFileSync/Build/Languages/lithuanian.lng
+++ b/FreeFileSync/Build/Languages/lithuanian.lng
@@ -7,6 +7,9 @@
<plural_definition>n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2</plural_definition>
</header>
+<source>Defined by context of use</source>
+<target></target>
+
<source>Both sides have changed since last synchronization.</source>
<target>Abi pusÄ—s buvo pakeistos nuo paskutinio suvienodinimo.</target>
@@ -112,6 +115,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Kelias iki alternatyvaus GlobalSettings.xml failo.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Įdiegimo failai yra pažeisti. Prašome įdiegti iš naujo FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>Nepavyksta rasti šių aplankų:</target>
@@ -334,12 +340,12 @@ Esamas: %y baitai
<source>Cannot find %x.</source>
<target>Negalima surasti %x.</target>
-<source>Cannot open file %x.</source>
-<target>%x failo nepavyko atidaryti.</target>
-
<source>Cannot find device %x.</source>
<target>Negalima surasti įrenginio %x.</target>
+<source>Cannot open file %x.</source>
+<target>%x failo nepavyko atidaryti.</target>
+
<source>Type of item %x is not supported:</source>
<target>Elemento tipas %x nepalaikomas:</target>
@@ -470,6 +476,9 @@ Esamas: %y baitai
<source>Total time:</source>
<target>Visas laikas:</target>
+<source>Cleaning up old log files...</source>
+<target>Išvalomi seni žurnalo įrašai...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Klaida trinant failÄ… %x, eilÄ— %y, stulpelis %z.</target>
@@ -659,6 +668,15 @@ Komanda inicijuojama jei:
<source>Multiple...</source>
<target>Keletas...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Nepavyksta įrašyti atributų failui %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x ir %y turi skirtingą turinį.</target>
+
+<source>Data verification error:</source>
+<target>Duomenų tikrinimo klaida:</target>
+
<source>Moving file %x to %y</source>
<target>Perkeliamas failas %x į %y</target>
@@ -683,14 +701,8 @@ Komanda inicijuojama jei:
<source>Updating attributes of %x</source>
<target>Atnaujinami atributai %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Nepavyksta įrašyti atributų failui %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x ir %y turi skirtingą turinį.</target>
-
-<source>Data verification error:</source>
-<target>Duomenų tikrinimo klaida:</target>
+<source>Source item %x not found</source>
+<target>Å altinio elementas %x nerastas:</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>%x kuriamas Duomenų Šešėlinė Kopija...</target>
@@ -752,9 +764,6 @@ Komanda inicijuojama jei:
<source>System: Shut down</source>
<target>Sistema: IÅ¡jungti</target>
-<source>Cleaning up old log files...</source>
-<target>Išvalomi seni žurnalo įrašai...</target>
-
<source>Stopped</source>
<target>Sustabdyta</target>
@@ -888,6 +897,9 @@ Komanda inicijuojama jei:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Prašome pasirinkti aplanką vietinėje failų sistemoje, tinkle arba MTP įrenginyje.</target>
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Reikalinga FreeFileSync Donoro Versija</target>
+
<source>&Save</source>
<target>&IÅ¡saugoti</target>
@@ -1038,6 +1050,15 @@ Komanda inicijuojama jei:
<source>Handle daylight saving time</source>
<target>Naudoti vasaros laikÄ…</target>
+<source>Performance improvements:</source>
+<target>Veiklos gerinimai:</target>
+
+<source>Parallel file operations:</source>
+<target>LygiagreÄios failų operacijos:</target>
+
+<source>How to get best performance?</source>
+<target>Kaip gauti geriausius rezultatus?</target>
+
<source>Local settings:</source>
<target>Vietiniai nustatymai:</target>
@@ -1154,15 +1175,6 @@ Komanda inicijuojama jei:
<source>Directory on server:</source>
<target>Katalogas serveryje:</target>
-<source>Performance improvements:</source>
-<target>Veiklos gerinimai:</target>
-
-<source>How to get best performance?</source>
-<target>Kaip gauti geriausius rezultatus?</target>
-
-<source>Connections for directory reading:</source>
-<target>Prisijungimai katalogų nuskaitymui:</target>
-
<source>SFTP channels per connection:</source>
<target>SFTP kanalai per jungtį:</target>
@@ -1298,8 +1310,17 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<source>&Default</source>
<target>&Numatyta</target>
-<source>Source code written in C++ using:</source>
-<target>Šaltinio kodas parašytas su C++ naudojant:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>NuomonÄ— ir patarimai laukiami</target>
+
+<source>Home page</source>
+<target>Pagrindinis puslapis</target>
+
+<source>FreeFileSync Forum</source>
+<target>FreeFileSync Diskusijos</target>
+
+<source>Email</source>
+<target>El. paštas</target>
<source>If you like FreeFileSync:</source>
<target>Jei Jums patinka FreeFileSync:</target>
@@ -1307,20 +1328,14 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<source>Support with a donation</source>
<target>Pailaikymas su paaukojimu</target>
-<source>Donation details</source>
-<target>IÅ¡samiau apie aukojimÄ…</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>Automatinis atnaujinimas yra išjungtas administratoriaus.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>NuomonÄ— ir patarimai laukiami</target>
-
-<source>Home page</source>
-<target>Pagrindinis puslapis</target>
+<source>Donation details</source>
+<target>IÅ¡samiau apie aukojimÄ…</target>
-<source>Email</source>
-<target>El. paštas</target>
+<source>Source code written in C++ using:</source>
+<target>Šaltinio kodas parašytas su C++ naudojant:</target>
<source>Published under the GNU General Public License</source>
<target>Platinama su GNU General Public licenzija</target>
@@ -1409,9 +1424,6 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x yra pasiekiamas!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Įdiegimo failai yra pažeisti. Prašome įdiegti iš naujo FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>Nerastas %x vietinis kelias.</target>
@@ -1638,6 +1650,9 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<source>Thank you, %x, for your donation and support!</source>
<target>AÄiÅ«, %x, už jÅ«sų aukÄ…, bei parÄ—mimÄ…!</target>
+<source>Connections</source>
+<target>Prisijungimai</target>
+
<source>Recommended range:</source>
<target>Rekomenduojamas intervalas:</target>
@@ -1812,9 +1827,6 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<source>Automatic updates:</source>
<target>Automatiniai atnaujinimai:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Reikalinga FreeFileSync Donoro Versija</target>
-
<source>Check for Program Updates</source>
<target>Tikrinti programos atnaujinimus</target>
@@ -2006,6 +2018,9 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<source>Edit with FreeFileSync</source>
<target>Koreguoti su FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>Vietoje reklamos, Å¡tai Äia yra gyvÅ«nas.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>FreeFileSync kilnojama versija negali būti įdiegta į poaplankį %x.</target>
@@ -2015,3 +2030,6 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>%x įdiegiamoji parinktis yra tiktai FreeFileSync Donoro Versijoje.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Pasirinkti Parėmimo Versiją su papildomomis funkcijomis ir padėkite išlaikyti FreeFileSync be reklamų.</target>
+
diff --git a/FreeFileSync/Build/Languages/norwegian.lng b/FreeFileSync/Build/Languages/norwegian.lng
index f7829a75..5f806a08 100755
--- a/FreeFileSync/Build/Languages/norwegian.lng
+++ b/FreeFileSync/Build/Languages/norwegian.lng
@@ -7,6 +7,9 @@
<plural_definition>n == 1 ? 0 : 1</plural_definition>
</header>
+<source>Defined by context of use</source>
+<target></target>
+
<source>Both sides have changed since last synchronization.</source>
<target>Begge sider er endret siden siste synkronisering.</target>
@@ -112,6 +115,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Sti til en alternativ GlobalSettings.xml fil.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Installasjonsfilene er ødelagte. Installer FreeFileSync på nytt.</target>
+
<source>Cannot find the following folders:</source>
<target>Kan ikke finne følgende mapper:</target>
@@ -333,12 +339,12 @@ Faktisk: %y bytes
<source>Cannot find %x.</source>
<target>Kan ikke finne %x.</target>
-<source>Cannot open file %x.</source>
-<target>Kan ikke åpne filen %x.</target>
-
<source>Cannot find device %x.</source>
<target>Kan ikke finne enhet %x.</target>
+<source>Cannot open file %x.</source>
+<target>Kan ikke åpne filen %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>Elementtypen %x støttes ikke:</target>
@@ -465,6 +471,9 @@ Faktisk: %y bytes
<source>Total time:</source>
<target>Samlet tid:</target>
+<source>Cleaning up old log files...</source>
+<target>Fjerner gamle loggfiler...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Behandlingsfeil i filen %x, rad %y, kolonne %z.</target>
@@ -653,6 +662,15 @@ Kommandoen utføres hvis:
<source>Multiple...</source>
<target>Flere...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Kan ikke skrive filattributter til %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x og %y har forskjellig innhold.</target>
+
+<source>Data verification error:</source>
+<target>Data verifikasjons-feil:</target>
+
<source>Moving file %x to %y</source>
<target>Flytter filen %x til %y</target>
@@ -677,14 +695,8 @@ Kommandoen utføres hvis:
<source>Updating attributes of %x</source>
<target>Oppdaterer attributter for %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Kan ikke skrive filattributter til %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x og %y har forskjellig innhold.</target>
-
-<source>Data verification error:</source>
-<target>Data verifikasjons-feil:</target>
+<source>Source item %x not found</source>
+<target>Kildeelementet %x ikke funnet</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>Oppretter en Volume Shadow-kopi for %x...</target>
@@ -746,9 +758,6 @@ Kommandoen utføres hvis:
<source>System: Shut down</source>
<target>System: Avslutt</target>
-<source>Cleaning up old log files...</source>
-<target>Fjerner gamle loggfiler...</target>
-
<source>Stopped</source>
<target>Stoppet</target>
@@ -881,6 +890,9 @@ Kommandoen utføres hvis:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Velg en mappe på lokalt filsystem, nettverk eller på en MTP-enhet.</target>
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Behøver FreeFileSync Donation Edition</target>
+
<source>&Save</source>
<target>&Lagre</target>
@@ -1031,6 +1043,15 @@ Kommandoen utføres hvis:
<source>Handle daylight saving time</source>
<target>Behandle sommertid</target>
+<source>Performance improvements:</source>
+<target>Ytelsesforbedringer:</target>
+
+<source>Parallel file operations:</source>
+<target>Parallelle fil-operasjoner:</target>
+
+<source>How to get best performance?</source>
+<target>Hvordan få best mulig ytelse?</target>
+
<source>Local settings:</source>
<target>Lokale innstillinger:</target>
@@ -1147,15 +1168,6 @@ Kommandoen utføres hvis:
<source>Directory on server:</source>
<target>Katalog på server:</target>
-<source>Performance improvements:</source>
-<target>Ytelsesforbedringer:</target>
-
-<source>How to get best performance?</source>
-<target>Hvordan få best mulig ytelse?</target>
-
-<source>Connections for directory reading:</source>
-<target>Tilkoblinger for kataloglesing:</target>
-
<source>SFTP channels per connection:</source>
<target>SFTP kanaler per tilkobling:</target>
@@ -1291,8 +1303,17 @@ Sikrer prosessen ved alvorlige feil.
<source>&Default</source>
<target>&Standard</target>
-<source>Source code written in C++ using:</source>
-<target>Kildekoden er skrevet i C++ med hjelp fra:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Tilbakemeldinger og forslag er ønsket</target>
+
+<source>Home page</source>
+<target>Hjemmeside</target>
+
+<source>FreeFileSync Forum</source>
+<target>FreeFileSync sitt forum</target>
+
+<source>Email</source>
+<target>Epost</target>
<source>If you like FreeFileSync:</source>
<target>Hvis du liker FreeFileSync:</target>
@@ -1300,20 +1321,14 @@ Sikrer prosessen ved alvorlige feil.
<source>Support with a donation</source>
<target>Støtte med en donasjon</target>
-<source>Donation details</source>
-<target>Donasjon detaljer</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>Den automatiske oppdateringen ble deaktivert av administratoren.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Tilbakemeldinger og forslag er ønsket</target>
-
-<source>Home page</source>
-<target>Hjemmeside</target>
+<source>Donation details</source>
+<target>Donasjon detaljer</target>
-<source>Email</source>
-<target>Epost</target>
+<source>Source code written in C++ using:</source>
+<target>Kildekoden er skrevet i C++ med hjelp fra:</target>
<source>Published under the GNU General Public License</source>
<target>Utgitt under GNU General Public Licence</target>
@@ -1402,9 +1417,6 @@ Sikrer prosessen ved alvorlige feil.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x er tilgjengelig!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Installasjonsfilene er ødelagte. Installer FreeFileSync på nytt.</target>
-
<source>Local path not available for %x.</source>
<target>Lokal sti er ikke tilgjengelig for %x.</target>
@@ -1627,6 +1639,9 @@ Sikrer prosessen ved alvorlige feil.
<source>Thank you, %x, for your donation and support!</source>
<target>Takk, %x, for ditt bidrag og støtte!</target>
+<source>Connections</source>
+<target>Tilkoblinger</target>
+
<source>Recommended range:</source>
<target>Anbefalt område:</target>
@@ -1798,9 +1813,6 @@ Sikrer prosessen ved alvorlige feil.
<source>Automatic updates:</source>
<target>Automatiske oppdateringer:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Behøver FreeFileSync Donation Edition</target>
-
<source>Check for Program Updates</source>
<target>Søk etter program-oppdateringer</target>
@@ -1990,6 +2002,9 @@ Sikrer prosessen ved alvorlige feil.
<source>Edit with FreeFileSync</source>
<target>Rediger med FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>I stedet for en annonse er her et dyr.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>Den bærbare versjonen av FreeFileSync kan ikke installeres i en undermappe av %x.</target>
@@ -1999,3 +2014,6 @@ Sikrer prosessen ved alvorlige feil.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>Installasjonsalternativet %x er bare tilgjengelig i FreeFileSync Donation Edition.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Få Donation Edition med bonus-tillegg og hjelp til med å holde FreeFileSync annonsefri.</target>
+
diff --git a/FreeFileSync/Build/Languages/polish.lng b/FreeFileSync/Build/Languages/polish.lng
index 077c5115..e31c349d 100755
--- a/FreeFileSync/Build/Languages/polish.lng
+++ b/FreeFileSync/Build/Languages/polish.lng
@@ -1,6 +1,6 @@
<header>
<language>Polski</language>
- <translator>Wojciech Pietruszewski</translator>
+ <translator>Józef Åuszczek</translator>
<locale>pl_PL</locale>
<image>flag_poland.png</image>
<plural_count>3</plural_count>
@@ -26,7 +26,7 @@
<target>Tworzenie pliku %x</target>
<source>Creating folder %x</source>
-<target>Tworzenie folderu %x</target>
+<target>Tworzenie katalogu %x</target>
<source>Creating symbolic link %x</source>
<target>Tworzenie dowiÄ…zania symbolicznego %x</target>
@@ -44,7 +44,7 @@
<target>Usuwanie pliku %x</target>
<source>Deleting folder %x</source>
-<target>Usuwanie folderu %x</target>
+<target>Usuwanie katalogu %x</target>
<source>Deleting symbolic link %x</source>
<target>Usuwanie dowiÄ…zania symbolicznego %x</target>
@@ -65,7 +65,7 @@
<target>Błąd składni</target>
<source>A left and a right directory path are expected after %x.</source>
-<target></target>
+<target>Po %x należy określić lewy i prawy katalog.</target>
<source>Cannot find file %x.</source>
<target>Nie można odnaleźć pliku %x.</target>
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Alternatywna ścieżka do pliku GlobalSettings.xml.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Pliki instalacyjne sÄ… uszkodzone. Przeinstaluj program FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>Nie można znaleźć następujących katalogów:</target>
@@ -119,13 +122,16 @@
<target>Jeżeli ten błąd zostanie zignorowany, katalogi zostaną uznane za puste. Brakujące katalogi będą w razie potrzeby utworzone automatycznie.</target>
<source>Comparison finished:</source>
-<target></target>
+<target>Porównywanie zakończone:</target>
<source>
<pluralform>1 item found</pluralform>
<pluralform>%x items found</pluralform>
</source>
<target>
+<pluralform>Znaleziono 1 element</pluralform>
+<pluralform>Znaleziono %x elementy</pluralform>
+<pluralform>Znaleziono %x elementów</pluralform>
</target>
<source>File %x has an invalid date.</source>
@@ -331,12 +337,12 @@ Przesłany: %y bajtów
<source>Cannot find %x.</source>
<target>Nie można odnaleźć %x.</target>
-<source>Cannot open file %x.</source>
-<target>Nie można otworzyć pliku %x.</target>
-
<source>Cannot find device %x.</source>
<target>Nie można odnaleźć urządzenia %x.</target>
+<source>Cannot open file %x.</source>
+<target>Nie można otworzyć pliku %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>Element typu %x nie jest wspierany:</target>
@@ -422,7 +428,7 @@ Przesłany: %y bajtów
<target>Plik bazy danych jest uszkodzony:</target>
<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Pliki bazy danych nie posiadają żadnych informacji o poprzednich synchronizacjach.</target>
+<target>Pliki bazy danych nie posiadają żadnych informacji o ostatniej synchronizacji.</target>
<source>Loading file %x...</source>
<target>Wczytywanie pliku %x...</target>
@@ -467,11 +473,14 @@ Przesłany: %y bajtów
<source>Total time:</source>
<target>Całkowity czas:</target>
+<source>Cleaning up old log files...</source>
+<target>Usuwanie starych plików logów...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>BÅ‚Ä…d podczas parsowania pliku %x, rzÄ…d %y, kolumna %z.</target>
<source>Cannot set directory locks for the following folders:</source>
-<target></target>
+<target>Nie można zablokować katalogów dla poniższych folderów:</target>
<source>
<pluralform>1 thread</pluralform>
@@ -505,16 +514,16 @@ Przesłany: %y bajtów
<target>Nie można uzyskać dostępu do usługi Volume Shadow Copy.</target>
<source>Please run the 64-bit version of FreeFileSync to create shadow copies on this system.</source>
-<target>Uruchom 64-bitową wersję FreeFileSync aby utworzyć Shadow Copies na tym systemie.</target>
+<target>Uruchom 64-bitową wersję FreeFileSync aby utworzyć Shadow Copies w tym systemie.</target>
<source>Cannot determine volume name for %x.</source>
<target>Nie można określić nazwy dysku dla %x.</target>
<source>Volume name %x is not part of file path %y.</source>
-<target>Nazwa zasobu %x ni jest częścią ścieżki %y.</target>
+<target>Nazwa wolumenu %x nie jest częścią ścieżki %y.</target>
<source>Unable to create time stamp for versioning:</source>
-<target>Nie można utworzyć znacznika czasu:</target>
+<target>Nie można utworzyć znacznika czasu dla wersjonowania:</target>
<source>Drag && drop</source>
<target>Drag && Drop</target>
@@ -593,7 +602,7 @@ The command is triggered if:
<target>
Komenda jest wykonywana gdy:
- pliki lub podkatalogi ulegnÄ… zmianie
-- zostaną utworzony nowe katalogi (n.p włożenie pamięci USB)
+- zostaną utworzone nowe katalogi (np. włożenie pamięci USB)
</target>
<source>Start</source>
@@ -612,7 +621,7 @@ Komenda jest wykonywana gdy:
<target>Automatyczna synchronizacja</target>
<source>The %x protocol does not support directory monitoring:</source>
-<target>Protokuł %x nie wspiera monitorowania katalogów:</target>
+<target>Protokół %x nie wspiera monitorowania katalogów:</target>
<source>Directory monitoring active</source>
<target>Monitorowanie katalogów aktywne</target>
@@ -656,6 +665,15 @@ Komenda jest wykonywana gdy:
<source>Multiple...</source>
<target>Wiele...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Nie można zapisać atrybutów %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x i %y mają różną zawartość.</target>
+
+<source>Data verification error:</source>
+<target>BÅ‚Ä…d weryfikacji danych:</target>
+
<source>Moving file %x to %y</source>
<target>Przenoszenie pliku %x do %y</target>
@@ -680,14 +698,8 @@ Komenda jest wykonywana gdy:
<source>Updating attributes of %x</source>
<target>Aktualizowanie atrybutów %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Nie można zapisać atrybutów %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x i %y mają różną zawartość.</target>
-
-<source>Data verification error:</source>
-<target>BÅ‚Ä…d weryfikacji danych:</target>
+<source>Source item %x not found</source>
+<target>Nie znaleziono elementu źródłowego %x</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>Tworzenie Volume Shadow Copy dla %x...</target>
@@ -696,13 +708,13 @@ Komenda jest wykonywana gdy:
<target>Katalog docelowy %x już istnieje.</target>
<source>Target folder input field must not be empty.</source>
-<target>Pole katalog docelowy nie może być puste.</target>
+<target>Pole katalogu docelowego nie może być puste.</target>
<source>Source folder %x not found.</source>
-<target>Nie znaleziono katalogu docelowego %x.</target>
+<target>Nie znaleziono katalogu źródłowego %x.</target>
<source>Please enter a target folder for versioning.</source>
-<target>Określ katalog do wersjonowania.</target>
+<target>Określ katalog docelowy do wersjonowania.</target>
<source>The following items have unresolved conflicts and will not be synchronized:</source>
<target>Te elementy znajdują się w konflikcie, którego nie można rozwiązać. Pliki nie zostaną zsynchronizowane:</target>
@@ -720,7 +732,7 @@ Komenda jest wykonywana gdy:
<target>Niektóre pliki będą zsynchronizowane jako część kilku katalogów źródłowych.</target>
<source>To avoid conflicts, set up exclude filters so that each updated file is considered by only one base folder.</source>
-<target>Aby uniknąc konfliktów ustaw filtry tak aby wykluczyć przynależność pliku do wielu katalogów źródłowych.</target>
+<target>Aby uniknąć konfliktów ustaw filtry tak aby wykluczyć przynależność pliku do wielu katalogów źródłowych.</target>
<source>Versioning folder:</source>
<target>Wersjonowanie katalogów:</target>
@@ -732,7 +744,7 @@ Komenda jest wykonywana gdy:
<target>Katalog wersjonowania znajduje siÄ™ w katalogu bazowym.</target>
<source>Synchronizing folder pair:</source>
-<target>Synchronizacja katalgów:</target>
+<target>Synchronizacja katalogów parami:</target>
<source>Generating database...</source>
<target>Generowanie bazy danych...</target>
@@ -744,22 +756,19 @@ Komenda jest wykonywana gdy:
<target>nazwa zadania</target>
<source>System: Sleep</source>
-<target></target>
+<target>System: Uśpienie</target>
<source>System: Shut down</source>
-<target></target>
-
-<source>Cleaning up old log files...</source>
-<target>Usuwanie starych plików logów...</target>
+<target>System: Wyłączenie</target>
<source>Stopped</source>
<target>Zatrzymana</target>
<source>Completed with errors</source>
-<target></target>
+<target>Zakończono z błędami</target>
<source>Completed with warnings</source>
-<target></target>
+<target>Zakończono z ostrzeżeniami</target>
<source>Warning</source>
<target>Ostrzeżenie</target>
@@ -768,7 +777,7 @@ Komenda jest wykonywana gdy:
<target>Brak plików do synchronizacji</target>
<source>Completed successfully</source>
-<target></target>
+<target>Zakończono bez błędów</target>
<source>Executing command %x</source>
<target>Wykonywanie polecenia %x</target>
@@ -789,7 +798,7 @@ Komenda jest wykonywana gdy:
<target>Przejdź do głównego okna FreeFileSync.</target>
<source>Automatic retry</source>
-<target></target>
+<target>Automatyczne ponowienie</target>
<source>Ignore &all</source>
<target>Ignoruj &wszystkie</target>
@@ -820,7 +829,7 @@ Komenda jest wykonywana gdy:
<target>Nazwa</target>
<source>Last sync</source>
-<target></target>
+<target>Ostatnia synchronizacja</target>
<source>Folder</source>
<target>Katalog</target>
@@ -885,6 +894,12 @@ Komenda jest wykonywana gdy:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Okreś katalog lokalny, sieciowy bądź urządzenie MTP.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Wymaga FreeFileSync Donation Edition</target>
+
<source>&Save</source>
<target>&Zapisz</target>
@@ -1035,6 +1050,15 @@ Komenda jest wykonywana gdy:
<source>Handle daylight saving time</source>
<target>Uwzględniaj przesunięcie czasu</target>
+<source>Performance improvements:</source>
+<target>Polepszenie wydajności:</target>
+
+<source>Parallel file operations:</source>
+<target>Równoległe operacje plikowe:</target>
+
+<source>How to get best performance?</source>
+<target>Jak uzyskać lepszą wydajność?</target>
+
<source>Local settings:</source>
<target>Ustawienia lokalne:</target>
@@ -1042,7 +1066,7 @@ Komenda jest wykonywana gdy:
<target>Dołącz:</target>
<source>Show examples</source>
-<target>Przykład</target>
+<target>Pokaż przykłady</target>
<source>Time span:</source>
<target>Przedział czasu:</target>
@@ -1092,7 +1116,7 @@ Komenda jest wykonywana gdy:
<target>Konwencja nazewnictwa:</target>
<source>Ignore errors</source>
-<target></target>
+<target>Ignoruj błędy</target>
<source>Retry count:</source>
<target>Liczba prób:</target>
@@ -1113,7 +1137,7 @@ Komenda jest wykonywana gdy:
<target>Typ połączenia:</target>
<source>Server name or IP address:</source>
-<target>Nazwa serwera, lub adres IP:</target>
+<target>Nazwa serwera lub adres IP:</target>
<source>Port:</source>
<target>Port:</target>
@@ -1151,15 +1175,6 @@ Komenda jest wykonywana gdy:
<source>Directory on server:</source>
<target>Katalog na serwerze:</target>
-<source>Performance improvements:</source>
-<target>Polepszenie wydajności:</target>
-
-<source>How to get best performance?</source>
-<target>Jak uzyskać lepszą wydajność?</target>
-
-<source>Connections for directory reading:</source>
-<target>Połączenie do odczytu katalogów:</target>
-
<source>SFTP channels per connection:</source>
<target>Liczba kanałów dla połączenia SFTP:</target>
@@ -1206,7 +1221,7 @@ Komenda jest wykonywana gdy:
<target>Gdy zakończono:</target>
<source>Auto-close</source>
-<target></target>
+<target>Auto-zamykanie</target>
<source>Close</source>
<target>Zamknij</target>
@@ -1218,10 +1233,10 @@ Komenda jest wykonywana gdy:
<target>Przerwij</target>
<source>Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x</source>
-<target>Utwórz plik wsadowy do zautomatyzowania procesu synchronizacji. Aby rozpocząć, klknij dwa razy plik lub zaplanuj zadanie w harmonogramie zadań: %x</target>
+<target>Utwórz plik wsadowy do zautomatyzowania procesu synchronizacji. Aby rozpocząć, kliknij dwa razy plik lub zaplanuj zadanie w harmonogramie zadań: %x</target>
<source>Progress dialog:</source>
-<target></target>
+<target>Okno postępu:</target>
<source>Run minimized</source>
<target>Uruchom zminimalizowane</target>
@@ -1295,8 +1310,17 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<source>&Default</source>
<target>&Domyślne</target>
-<source>Source code written in C++ using:</source>
-<target>Kod stworzony w C++ z wykorzystaniem:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Wszelkie opinie i sugestie mile widziane</target>
+
+<source>Home page</source>
+<target>Strona domowa:</target>
+
+<source>FreeFileSync Forum</source>
+<target>Forum FreeFileSync</target>
+
+<source>Email</source>
+<target>Poczta</target>
<source>If you like FreeFileSync:</source>
<target>Jeżeli lubisz FreeFileSync:</target>
@@ -1304,20 +1328,14 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<source>Support with a donation</source>
<target>Wesprzyj dotacjÄ…</target>
-<source>Donation details</source>
-<target>Szczegóły dotacji</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>Auto aktualizacja została wyłączona przez administratora.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Wszelkie opinie i sugestie mile widziane</target>
-
-<source>Home page</source>
-<target>Strona domowa:</target>
+<source>Donation details</source>
+<target>Szczegóły dotacji</target>
-<source>Email</source>
-<target>Poczta</target>
+<source>Source code written in C++ using:</source>
+<target>Kod stworzony w C++ z wykorzystaniem:</target>
<source>Published under the GNU General Public License</source>
<target>Udostępnione na zasadach licencji GNU General Public License</target>
@@ -1338,7 +1356,7 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<target>2. Pobierz offline'owy klucz aktywacyjny z poniższego adresu:</target>
<source>&Copy to clipboard</source>
-<target>&Kopuj do schowka</target>
+<target>&Kopiuj do schowka</target>
<source>Enter activation key:</source>
<target>Wprowadź klucz aktywacyjny:</target>
@@ -1347,13 +1365,13 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<target>Aktywuj offline</target>
<source>Highlight configurations that have not been run for more than the following number of days:</source>
-<target></target>
+<target>Pokaż konfiguracje, które nie były uruchamiane więcej niż następująca liczba dni:</target>
<source>Synchronization Settings</source>
<target>Ustawienia synchronizacji</target>
<source>Access Online Storage</source>
-<target></target>
+<target>Dostęp do Online Storage</target>
<source>Save as a Batch Job</source>
<target>Zapisz jako plik wsadowy.</target>
@@ -1362,19 +1380,19 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<target>Usuń elementy</target>
<source>Copy Items</source>
-<target></target>
+<target>Kopiowanie elementów</target>
<source>Options</source>
<target>Opcje</target>
<source>Select Time Span</source>
-<target>Określ</target>
+<target>Określ przedział czasu</target>
<source>FreeFileSync Donation Edition</source>
<target>FreeFileSync Donation Edition</target>
<source>Highlight Configurations</source>
-<target></target>
+<target>Pokaż konfiguracje</target>
<source>&Options</source>
<target>&Opcje</target>
@@ -1406,9 +1424,6 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x jest już dostępny!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Pliki instalacyjne sÄ… uszkodzone. Przeinstaluj program FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>Lokalna ścieżka nie jest dostępna dla %x.</target>
@@ -1522,10 +1537,10 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<target>&Nie zapisuj</target>
<source>Hide configuration</source>
-<target></target>
+<target>Ukryj konfiguracje</target>
<source>Highlight...</source>
-<target></target>
+<target>Pokaż...</target>
<source>Clear filter</source>
<target>Wyczyść filtr</target>
@@ -1573,7 +1588,7 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<target>Pokaż pliki, które nie będą kopiowane</target>
<source>Show filtered or temporarily excluded files</source>
-<target>Pokaż pliki wykluczone tymczasowo, lub pliki wykluczone tymczasowo</target>
+<target>Pokaż pliki wyfiltrowane lub wykluczone tymczasowo</target>
<source>Save as default</source>
<target>Zapisz jako domyślne</target>
@@ -1606,7 +1621,7 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<target>Pauza</target>
<source>Stop requested...</source>
-<target></target>
+<target>Zatrzymaj...</target>
<source>Initializing...</source>
<target>Inicjalizacja...</target>
@@ -1635,6 +1650,9 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<source>Thank you, %x, for your donation and support!</source>
<target>Dziękujemy %x za Twoje wsparcie!</target>
+<source>Connections</source>
+<target>Połączenia</target>
+
<source>Recommended range:</source>
<target>Rekomendowany zakres:</target>
@@ -1753,7 +1771,7 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<target>MB</target>
<source>Retain deleted and overwritten files in the recycle bin</source>
-<target>Zachowaj pliku usunięte i nadpisane w koszy systemowym</target>
+<target>Zachowaj pliku usunięte i nadpisane w koszu systemowym</target>
<source>Delete and overwrite files permanently</source>
<target>Usuwaj i nadpisuj pliki permanentnie</target>
@@ -1809,9 +1827,6 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<source>Automatic updates:</source>
<target>Automatyczne aktualizacje:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Wymaga FreeFileSync Donation Edition</target>
-
<source>Check for Program Updates</source>
<target>Sprawdź dostępne aktualizacje.</target>
@@ -1849,7 +1864,7 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<target>BÅ‚Ä…d aktywacji FreeFileSync Donation Edition.</target>
<source>Incorrect activation key.</source>
-<target>Nipoprawny klucz aktywacyjny.</target>
+<target>Niepoprawny klucz aktywacyjny.</target>
<source>Unable to register to receive system messages.</source>
<target>Błąd podczas rejestrowania do odbioru komunikatów systemowych.</target>
@@ -1932,7 +1947,7 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<target>Poniższy element XML nie może zostać odczytany:</target>
<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Plik konfiguracyjny %x jest niekompletny. Zostaną ustawione domyśle wartości dla brakujących elementów.</target>
+<target>Plik konfiguracyjny %x jest niekompletny. Zostaną ustawione domyślne wartości dla brakujących elementów.</target>
<source>Prepare installation</source>
<target>Przygotuj instalacjÄ™</target>
@@ -1941,7 +1956,7 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<target>Określ komponenty do instalacji.</target>
<source>Select installation type:</source>
-<target>Typ instalacji:</target>
+<target>Wybierz typ instalacji:</target>
<source>Local</source>
<target>Lokalna</target>
@@ -1977,10 +1992,10 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<target>Na Pulpicie</target>
<source>Start Menu</source>
-<target></target>
+<target>Start Menu</target>
<source>Send To</source>
-<target></target>
+<target>Wyślij Do</target>
<source>Registering FreeFileSync file extensions</source>
<target>Rejestrowanie rozszerzeń plików FreeFileSync</target>
@@ -2003,6 +2018,9 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<source>Edit with FreeFileSync</source>
<target>Edytuj przy użyciu FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>Zamiast reklamy, oto jest zwierzÄ™.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>Przenośna wersja FreeFileSync nie może być zainstalowana w podkatalogu w %x.</target>
@@ -2010,5 +2028,8 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<target>Wybierz lokalny typ instalacji lub określ inny katalog.</target>
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
-<target></target>
+<target>Opcja instalacyjna %x jest dostępna wyłącznie w wersji FreeFileSync Donation Edition.</target>
+
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Pobierz Donation Edition z funkcjami dodatkowymi i pomóż utrzymać FreeFileSync bez reklam.</target>
diff --git a/FreeFileSync/Build/Languages/portuguese.lng b/FreeFileSync/Build/Languages/portuguese.lng
index 493c0a0b..77bd1d3b 100755
--- a/FreeFileSync/Build/Languages/portuguese.lng
+++ b/FreeFileSync/Build/Languages/portuguese.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Caminho para o ficheiro GlobalSettings.xml alternativo.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Os ficheiros de instalação estão corrompidos. Por favor, reinstale o FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>Não é possível encontrar as seguintes pastas:</target>
@@ -333,12 +336,12 @@ Actual: %y bytes
<source>Cannot find %x.</source>
<target>Não é possível encontrar %x.</target>
-<source>Cannot open file %x.</source>
-<target>Não é possível abrir o ficheiro %x.</target>
-
<source>Cannot find device %x.</source>
<target>Não é possível encontrar o dispositivo %x.</target>
+<source>Cannot open file %x.</source>
+<target>Não é possível abrir o ficheiro %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>Tipo de item %x não é suportado:</target>
@@ -465,6 +468,9 @@ Actual: %y bytes
<source>Total time:</source>
<target>Tempo total:</target>
+<source>Cleaning up old log files...</source>
+<target>A limpar ficheiros de log antigos...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Erro ao analisar ficheiro %x, linha %y, coluna %z.</target>
@@ -653,6 +659,15 @@ O comando é executado se:
<source>Multiple...</source>
<target>Múltiplo...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Não é possível escrever os atributos de %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x e %y tem conteúdos distintos.</target>
+
+<source>Data verification error:</source>
+<target>Erro de verificação dos dados:</target>
+
<source>Moving file %x to %y</source>
<target>A mover ficheiro %x para %y</target>
@@ -677,14 +692,8 @@ O comando é executado se:
<source>Updating attributes of %x</source>
<target>A actualizar atributos de %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Não é possível escrever os atributos de %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x e %y tem conteúdos distintos.</target>
-
-<source>Data verification error:</source>
-<target>Erro de verificação dos dados:</target>
+<source>Source item %x not found</source>
+<target>Item de origem %x não encontrado</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>A criar Volume Shadow Copy para %x...</target>
@@ -746,9 +755,6 @@ O comando é executado se:
<source>System: Shut down</source>
<target>Desligar</target>
-<source>Cleaning up old log files...</source>
-<target>A limpar ficheiros de log antigos...</target>
-
<source>Stopped</source>
<target>Parado</target>
@@ -881,6 +887,12 @@ O comando é executado se:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Seleccione a pasta no sistema de ficheiros local, rede ou dispositivo MTP.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Requer o FreeFileSync Donation Edition</target>
+
<source>&Save</source>
<target>G&uardar</target>
@@ -1031,6 +1043,15 @@ O comando é executado se:
<source>Handle daylight saving time</source>
<target>Lidar com horário de verão</target>
+<source>Performance improvements:</source>
+<target>Melhorias de desempenho:</target>
+
+<source>Parallel file operations:</source>
+<target>Operações de arquivos paralelos:</target>
+
+<source>How to get best performance?</source>
+<target>Como obter um melhor desempenho?</target>
+
<source>Local settings:</source>
<target>Opções locais:</target>
@@ -1147,15 +1168,6 @@ O comando é executado se:
<source>Directory on server:</source>
<target>Directório no servidor:</target>
-<source>Performance improvements:</source>
-<target>Melhorias de desempenho:</target>
-
-<source>How to get best performance?</source>
-<target>Como obter um melhor desempenho?</target>
-
-<source>Connections for directory reading:</source>
-<target>Conexões para ler directório:</target>
-
<source>SFTP channels per connection:</source>
<target>Canais SFTP por conexão:</target>
@@ -1291,8 +1303,17 @@ Isto garante um estado consistente mesmo em caso de falha grave.
<source>&Default</source>
<target>&Config. Iniciais</target>
-<source>Source code written in C++ using:</source>
-<target>Código fonte escrito em C++ utilizando:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Comentários e sugestões são apreciados</target>
+
+<source>Home page</source>
+<target>Sítio da web</target>
+
+<source>FreeFileSync Forum</source>
+<target>Fórum do FreeFileSync</target>
+
+<source>Email</source>
+<target>Email</target>
<source>If you like FreeFileSync:</source>
<target>Se gosta do FreeFileSync:</target>
@@ -1300,20 +1321,14 @@ Isto garante um estado consistente mesmo em caso de falha grave.
<source>Support with a donation</source>
<target>Apoiar com uma doação</target>
-<source>Donation details</source>
-<target>Detalhes da doação</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>A actualização automática foi desactivada pelo administrador.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Comentários e sugestões são apreciados</target>
-
-<source>Home page</source>
-<target>Sítio da web</target>
+<source>Donation details</source>
+<target>Detalhes da doação</target>
-<source>Email</source>
-<target>Email</target>
+<source>Source code written in C++ using:</source>
+<target>Código fonte escrito em C++ utilizando:</target>
<source>Published under the GNU General Public License</source>
<target>Publicado sobre GNU General Public License</target>
@@ -1402,9 +1417,6 @@ Isto garante um estado consistente mesmo em caso de falha grave.
<source>FreeFileSync %x is available!</source>
<target>O FreeFileSync %x está disponível!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Os ficheiros de instalação estão corrompidos. Por favor, reinstale o FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>Caminho local não disponível para %x.</target>
@@ -1627,6 +1639,9 @@ Isto garante um estado consistente mesmo em caso de falha grave.
<source>Thank you, %x, for your donation and support!</source>
<target>Obrigado, %x, pela sua doação e suporte!</target>
+<source>Connections</source>
+<target>Conexões</target>
+
<source>Recommended range:</source>
<target>Gama recomendada:</target>
@@ -1798,9 +1813,6 @@ Isto garante um estado consistente mesmo em caso de falha grave.
<source>Automatic updates:</source>
<target>Actualizações automáticas:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Requer o FreeFileSync Donation Edition</target>
-
<source>Check for Program Updates</source>
<target>Procurar actualizações do programa</target>
@@ -1990,6 +2002,9 @@ Isto garante um estado consistente mesmo em caso de falha grave.
<source>Edit with FreeFileSync</source>
<target>Editar com FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>Em vez de um anúncio, aqui está um animal.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>A versão portátil do FreeFileSync não pode ser instalada em uma sub-pasta de %x.</target>
@@ -1999,3 +2014,6 @@ Isto garante um estado consistente mesmo em caso de falha grave.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>A opção de instalação %x está disponível somente no FreeFileSync Donation Edition.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Obtenha a Donation Edition com recursos bônus e ajude a manter o FreeFileSync sem anúncios.</target>
+
diff --git a/FreeFileSync/Build/Languages/portuguese_br.lng b/FreeFileSync/Build/Languages/portuguese_br.lng
index 8639ec65..393d94eb 100755
--- a/FreeFileSync/Build/Languages/portuguese_br.lng
+++ b/FreeFileSync/Build/Languages/portuguese_br.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Caminho para um arquivo GlobalSettings.xml alternativo.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Os arquivos de instalação estão corrompidos. Por favor, reinstale o FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>Não é possível localizar as seguintes pastas:</target>
@@ -333,12 +336,12 @@ Atual: %y bytes
<source>Cannot find %x.</source>
<target>Não é possível encontrar %x.</target>
-<source>Cannot open file %x.</source>
-<target>Não é possível abrir o arquivo %x.</target>
-
<source>Cannot find device %x.</source>
<target>Não é possível encontrar o dispositivo %x.</target>
+<source>Cannot open file %x.</source>
+<target>Não é possível abrir o arquivo %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>Tipo de item %x não é suportado:</target>
@@ -465,6 +468,9 @@ Atual: %y bytes
<source>Total time:</source>
<target>Tempo total:</target>
+<source>Cleaning up old log files...</source>
+<target>Limpando arquivo de log antigo...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Erro analisando o arquivo %x, linha %y, coluna %z.</target>
@@ -653,6 +659,15 @@ O comando é disparado se:
<source>Multiple...</source>
<target>Múltiplo...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Não é possível escrever os atributos de arquivo de %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x e %y têm conteúdos diferentes.</target>
+
+<source>Data verification error:</source>
+<target>Erro na verificação dos dados:</target>
+
<source>Moving file %x to %y</source>
<target>Movendo arquivo %x para %y</target>
@@ -677,14 +692,8 @@ O comando é disparado se:
<source>Updating attributes of %x</source>
<target>Atualizando atributos de %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Não é possível escrever os atributos de arquivo de %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x e %y têm conteúdos diferentes.</target>
-
-<source>Data verification error:</source>
-<target>Erro na verificação dos dados:</target>
+<source>Source item %x not found</source>
+<target>Item de origem %x não encontrado</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>Criando uma Cópia de Sombra de Volume para %x...</target>
@@ -746,9 +755,6 @@ O comando é disparado se:
<source>System: Shut down</source>
<target>Sistema: Desligar</target>
-<source>Cleaning up old log files...</source>
-<target>Limpando arquivo de log antigo...</target>
-
<source>Stopped</source>
<target>Interrompido</target>
@@ -881,6 +887,12 @@ O comando é disparado se:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Selecione uma pasta em um sistema de arquivos local, de rede ou de um dispositivo MTP.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Requer FreeFileSync Edição do Doador</target>
+
<source>&Save</source>
<target>&Salvar</target>
@@ -1031,6 +1043,15 @@ O comando é disparado se:
<source>Handle daylight saving time</source>
<target>Como lidar com horário de verão</target>
+<source>Performance improvements:</source>
+<target>Melhorias de desempenho:</target>
+
+<source>Parallel file operations:</source>
+<target>Operações de arquivos em paralelo:</target>
+
+<source>How to get best performance?</source>
+<target>Como obter o melhor desempenho?</target>
+
<source>Local settings:</source>
<target>Configurações locais:</target>
@@ -1147,15 +1168,6 @@ O comando é disparado se:
<source>Directory on server:</source>
<target>Diretório no servidor:</target>
-<source>Performance improvements:</source>
-<target>Melhorias de desempenho:</target>
-
-<source>How to get best performance?</source>
-<target>Como obter o melhor desempenho?</target>
-
-<source>Connections for directory reading:</source>
-<target>Conexões para leitura de diretório:</target>
-
<source>SFTP channels per connection:</source>
<target>Canais SFTP por conexão:</target>
@@ -1291,8 +1303,17 @@ Isto garante um estado consistente mesmo em caso de erro grave.
<source>&Default</source>
<target>&Config. Padrão</target>
-<source>Source code written in C++ using:</source>
-<target>Código-fonte escrito em C++ utilizando:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Críticas e sugestões são bem-vindas</target>
+
+<source>Home page</source>
+<target>Página web</target>
+
+<source>FreeFileSync Forum</source>
+<target>Fórum do FreeFileSync</target>
+
+<source>Email</source>
+<target>E-mail</target>
<source>If you like FreeFileSync:</source>
<target>Se você gosta do FreeFileSync:</target>
@@ -1300,20 +1321,14 @@ Isto garante um estado consistente mesmo em caso de erro grave.
<source>Support with a donation</source>
<target>Apoie com uma doação</target>
-<source>Donation details</source>
-<target>Detalhes da doação</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>A atualização automática foi desabilitada pelo administrador.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Críticas e sugestões são bem-vindas</target>
-
-<source>Home page</source>
-<target>Página web</target>
+<source>Donation details</source>
+<target>Detalhes da doação</target>
-<source>Email</source>
-<target>E-mail</target>
+<source>Source code written in C++ using:</source>
+<target>Código-fonte escrito em C++ utilizando:</target>
<source>Published under the GNU General Public License</source>
<target>Publicado sob a GNU General Public License</target>
@@ -1402,9 +1417,6 @@ Isto garante um estado consistente mesmo em caso de erro grave.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x está disponível!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Os arquivos de instalação estão corrompidos. Por favor, reinstale o FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>Caminho local não disponível para %x.</target>
@@ -1627,6 +1639,9 @@ Isto garante um estado consistente mesmo em caso de erro grave.
<source>Thank you, %x, for your donation and support!</source>
<target>Obrigado, %x, pela sua doação e suporte!</target>
+<source>Connections</source>
+<target>Conexões</target>
+
<source>Recommended range:</source>
<target>Intervalo recomendado:</target>
@@ -1798,9 +1813,6 @@ Isto garante um estado consistente mesmo em caso de erro grave.
<source>Automatic updates:</source>
<target>Atualizações automáticas:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Requer FreeFileSync Edição do Doador</target>
-
<source>Check for Program Updates</source>
<target>Verificar se existem atualizações</target>
@@ -1990,6 +2002,9 @@ Isto garante um estado consistente mesmo em caso de erro grave.
<source>Edit with FreeFileSync</source>
<target>Editar com FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>Ao invés de um anúncio, veja um animal.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>A versão portátil do FreeFileSync não pode ser instalada em uma subpasta de %x.</target>
@@ -1999,3 +2014,6 @@ Isto garante um estado consistente mesmo em caso de erro grave.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>A opção de instalação %x está disponível apenas na Edição do Doador do FreeFileSync.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Obtenha a Edição do Doador com recursos adicionais e ajude a manter o FreeFileSync livre de anúncios.</target>
+
diff --git a/FreeFileSync/Build/Languages/romanian.lng b/FreeFileSync/Build/Languages/romanian.lng
index aa9255ec..f36eea79 100755
--- a/FreeFileSync/Build/Languages/romanian.lng
+++ b/FreeFileSync/Build/Languages/romanian.lng
@@ -7,6 +7,9 @@
<plural_definition>n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2</plural_definition>
</header>
+<source>Defined by context of use</source>
+<target></target>
+
<source>Both sides have changed since last synchronization.</source>
<target>Ambele părți s-au modificat de la ultima sincronizare.</target>
@@ -112,6 +115,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Calea către o filă GlobalSettings.xml alternativă.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Filele de instalare sînt stricate (corupte). Reinstalează FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>Nu pot găsi dosarele următoare:</target>
@@ -334,12 +340,12 @@ Actuală: %y baiți
<source>Cannot find %x.</source>
<target>Nu pot găsi %x.</target>
-<source>Cannot open file %x.</source>
-<target>Nu pot deschide fila %x.</target>
-
<source>Cannot find device %x.</source>
<target>Nu pot găsi dispozitivul %x.</target>
+<source>Cannot open file %x.</source>
+<target>Nu pot deschide fila %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>Tipul de element %x nu e suportat:</target>
@@ -470,6 +476,9 @@ Actuală: %y baiți
<source>Total time:</source>
<target>Timp Total:</target>
+<source>Cleaning up old log files...</source>
+<target>Curăț filele de jurnalizare vechi...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Eroare la parsarea filei %x, rîndul %y, coloana %z.</target>
@@ -659,6 +668,15 @@ Comanda este declanșată dacă:
<source>Multiple...</source>
<target>Multiplu...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Nu pot scrie atributele de filă ale lui %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x și %y au conținut diferit.</target>
+
+<source>Data verification error:</source>
+<target>Eroare de verificare a datei:</target>
+
<source>Moving file %x to %y</source>
<target>Mut fila %x în %y</target>
@@ -683,14 +701,8 @@ Comanda este declanșată dacă:
<source>Updating attributes of %x</source>
<target>Actualizez atributele lui %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Nu pot scrie atributele de filă ale lui %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x și %y au conținut diferit.</target>
-
-<source>Data verification error:</source>
-<target>Eroare de verificare a datei:</target>
+<source>Source item %x not found</source>
+<target>Itemul sursă %x n-a fost găsit</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>Creez o Conservare a Volumului [Volume Shadow Copy] pentru %x...</target>
@@ -752,9 +764,6 @@ Comanda este declanșată dacă:
<source>System: Shut down</source>
<target>Sistem: ÃŽnchide PC-ul [Shut down]</target>
-<source>Cleaning up old log files...</source>
-<target>Curăț filele de jurnalizare vechi...</target>
-
<source>Stopped</source>
<target>Oprită</target>
@@ -888,6 +897,9 @@ Comanda este declanșată dacă:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Selectează un dosar de pe un sistem de file local, din rețea sau de pe un dispozitiv MTP (dispozitiv media portabil).</target>
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Necesită FreeFileSync Ediția pentru Donatori</target>
+
<source>&Save</source>
<target>&Salvează</target>
@@ -1038,6 +1050,15 @@ Comanda este declanșată dacă:
<source>Handle daylight saving time</source>
<target>Gestionarea timpului de vară (DST)</target>
+<source>Performance improvements:</source>
+<target>Îmbunătățiri de Performanță:</target>
+
+<source>Parallel file operations:</source>
+<target>Operațiuni paralele cu file:</target>
+
+<source>How to get best performance?</source>
+<target>Cum obții cea mai bună performanță?</target>
+
<source>Local settings:</source>
<target>Setări Locale:</target>
@@ -1154,15 +1175,6 @@ Comanda este declanșată dacă:
<source>Directory on server:</source>
<target>Dosarul de pe server:</target>
-<source>Performance improvements:</source>
-<target>Îmbunătățiri de Performanță:</target>
-
-<source>How to get best performance?</source>
-<target>Cum obții cea mai bună performanță?</target>
-
-<source>Connections for directory reading:</source>
-<target>Conexiuni pentru citirea dosarelor:</target>
-
<source>SFTP channels per connection:</source>
<target>Canale SFTP per conexiune:</target>
@@ -1298,8 +1310,17 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției
<source>&Default</source>
<target>Coloanele &Implicite</target>
-<source>Source code written in C++ using:</source>
-<target>Cod sursă scris în C++ folosind:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Opiniile și sugestiile despre program sînt binevenite.</target>
+
+<source>Home page</source>
+<target>Pagina Sitului</target>
+
+<source>FreeFileSync Forum</source>
+<target>Forumul FreeFileSync</target>
+
+<source>Email</source>
+<target>Adresa Autorului</target>
<source>If you like FreeFileSync:</source>
<target>Donează pentru FreeFileSync:</target>
@@ -1307,20 +1328,14 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției
<source>Support with a donation</source>
<target>Sprijină cu o donație</target>
-<source>Donation details</source>
-<target>Detaliile Donăriii</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>Actualizatorul automat a fost dezactivat de administrator.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Opiniile și sugestiile despre program sînt binevenite.</target>
-
-<source>Home page</source>
-<target>Pagina Sitului</target>
+<source>Donation details</source>
+<target>Detaliile Donăriii</target>
-<source>Email</source>
-<target>Adresa Autorului</target>
+<source>Source code written in C++ using:</source>
+<target>Cod sursă scris în C++ folosind:</target>
<source>Published under the GNU General Public License</source>
<target>Publicat sub licența GNU GPL</target>
@@ -1409,9 +1424,6 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x este disponibil!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Filele de instalare sînt stricate (corupte). Reinstalează FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>Calea locală nu este disponibilă pentru %x.</target>
@@ -1638,6 +1650,9 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției
<source>Thank you, %x, for your donation and support!</source>
<target>Mulțumesc %x, pentru donație și sprijin!</target>
+<source>Connections</source>
+<target>Conexiuni</target>
+
<source>Recommended range:</source>
<target>Interval recomandat:</target>
@@ -1812,9 +1827,6 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției
<source>Automatic updates:</source>
<target>Actualizări Automate:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Necesită FreeFileSync Ediția pentru Donatori</target>
-
<source>Check for Program Updates</source>
<target>Caută Actualizări ale Programului</target>
@@ -2006,6 +2018,9 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției
<source>Edit with FreeFileSync</source>
<target>Editează cu FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>ÃŽn loc de o publicitate, uite un animal.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>Versiunea portabilă de FreeFileSync nu poate fi instalată într-un subdosar al %x.</target>
@@ -2015,3 +2030,6 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>Opțiunea de instalare %x e disponibilă doar pentru FreeFileSync Ediția pentru Donatori.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Obține Ediția pentru Donatori cu funcții suplimentare și ajută la menținerea FreeFileSync fără publicitate.</target>
+
diff --git a/FreeFileSync/Build/Languages/russian.lng b/FreeFileSync/Build/Languages/russian.lng
index 4273a175..67606f38 100755
--- a/FreeFileSync/Build/Languages/russian.lng
+++ b/FreeFileSync/Build/Languages/russian.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Путь к альтернативному файлу GlobalSettings.xml.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>УÑтановочные файлы повреждены. ПереуÑтановите FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>Ðевозможно найти Ñледующие папки:</target>
@@ -334,12 +337,12 @@ Actual: %y bytes
<source>Cannot find %x.</source>
<target>Ðевозможно найти %x.</target>
-<source>Cannot open file %x.</source>
-<target>Ðевозможно открыть файл %x.</target>
-
<source>Cannot find device %x.</source>
<target>Ðевозможно найти уÑтройÑтво %x.</target>
+<source>Cannot open file %x.</source>
+<target>Ðевозможно открыть файл %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>Тип Ñлемента %x не поддерживаетÑÑ:</target>
@@ -470,6 +473,9 @@ Actual: %y bytes
<source>Total time:</source>
<target>Общее времÑ:</target>
+<source>Cleaning up old log files...</source>
+<target>ОчиÑтка Ñтарых лог-файлов (журналов)...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Ошибка при разборе файла %x, Ñтрока %y, колонка %z.</target>
@@ -659,6 +665,15 @@ The command is triggered if:
<source>Multiple...</source>
<target>Различные варианты Ñинхронизации...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Ðевозможно запиÑать атрибуты файла %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x и %y имеют разное Ñодержание.</target>
+
+<source>Data verification error:</source>
+<target>Ошибка верификации данных:</target>
+
<source>Moving file %x to %y</source>
<target>Перемещение файла %x в %y</target>
@@ -683,14 +698,8 @@ The command is triggered if:
<source>Updating attributes of %x</source>
<target>Обновление атрибутов %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Ðевозможно запиÑать атрибуты файла %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x и %y имеют разное Ñодержание.</target>
-
-<source>Data verification error:</source>
-<target>Ошибка верификации данных:</target>
+<source>Source item %x not found</source>
+<target>ИÑходный Ñлемент %x не найден</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>Создание Тома Теневого ÐšÐ¾Ð¿Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð´Ð»Ñ %x...</target>
@@ -752,9 +761,6 @@ The command is triggered if:
<source>System: Shut down</source>
<target>СиÑтема: Завершение работы</target>
-<source>Cleaning up old log files...</source>
-<target>ОчиÑтка Ñтарых лог-файлов (журналов)...</target>
-
<source>Stopped</source>
<target>ОÑтановлено</target>
@@ -888,6 +894,12 @@ The command is triggered if:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>ПожалуйÑта, выберите папку в локальной файловой ÑиÑтеме, Ñети или MTP уÑтройÑтве.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>ТребуетÑÑ Ð¿Ð»Ð°Ñ‚Ð½Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ FreeFileSync</target>
+
<source>&Save</source>
<target>&Сохранить</target>
@@ -1038,6 +1050,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>Ручной переход на летнее времÑ</target>
+<source>Performance improvements:</source>
+<target>Повышение производительноÑти:</target>
+
+<source>Parallel file operations:</source>
+<target>Параллельные операции Ñ Ñ„Ð°Ð¹Ð»Ð°Ð¼Ð¸:</target>
+
+<source>How to get best performance?</source>
+<target>Как получить лучшую производительноÑÑ‚ÑŒ?</target>
+
<source>Local settings:</source>
<target>Локальные наÑтройки:</target>
@@ -1154,15 +1175,6 @@ The command is triggered if:
<source>Directory on server:</source>
<target>Папка на Ñервере:</target>
-<source>Performance improvements:</source>
-<target>Повышение производительноÑти:</target>
-
-<source>How to get best performance?</source>
-<target>Как получить лучшую производительноÑÑ‚ÑŒ?</target>
-
-<source>Connections for directory reading:</source>
-<target>КоличеÑтво Ñоединений Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð¿Ð°Ð¿Ð¾Ðº:</target>
-
<source>SFTP channels per connection:</source>
<target>КоличеÑтво SFTP каналов на одно Ñоединение:</target>
@@ -1298,8 +1310,17 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>&По умолчанию</target>
-<source>Source code written in C++ using:</source>
-<target>ИÑходный код напиÑан на C++ Ñ Ð¸Ñпользованием:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Ð—Ð°Ð¼ÐµÑ‡Ð°Ð½Ð¸Ñ Ð¸ Ð¿Ñ€ÐµÐ´Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸Ð²ÐµÑ‚ÑтвуютÑÑ</target>
+
+<source>Home page</source>
+<target>ДомашнÑÑ Ñтраница</target>
+
+<source>FreeFileSync Forum</source>
+<target>Форум FreeFileSync</target>
+
+<source>Email</source>
+<target>Почта</target>
<source>If you like FreeFileSync:</source>
<target>ЕÑли Вам понравилÑÑ FreeFileSync:</target>
@@ -1307,20 +1328,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Support with a donation</source>
<target>Помочь пожертвованием</target>
-<source>Donation details</source>
-<target>Детали оплаты</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>ÐвтоматичеÑкое обновление было отключено админиÑтратором.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Ð—Ð°Ð¼ÐµÑ‡Ð°Ð½Ð¸Ñ Ð¸ Ð¿Ñ€ÐµÐ´Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸Ð²ÐµÑ‚ÑтвуютÑÑ</target>
-
-<source>Home page</source>
-<target>ДомашнÑÑ Ñтраница</target>
+<source>Donation details</source>
+<target>Детали оплаты</target>
-<source>Email</source>
-<target>Почта</target>
+<source>Source code written in C++ using:</source>
+<target>ИÑходный код напиÑан на C++ Ñ Ð¸Ñпользованием:</target>
<source>Published under the GNU General Public License</source>
<target>ИздаетÑÑ Ð¿Ð¾Ð´ лицензией GNU General Public License</target>
@@ -1409,9 +1424,6 @@ This guarantees a consistent state even in case of a serious error.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x доÑтупен!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>УÑтановочные файлы повреждены. ПереуÑтановите FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>Локальный путь не доÑтупен Ð´Ð»Ñ %x.</target>
@@ -1638,6 +1650,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Thank you, %x, for your donation and support!</source>
<target>Благодарим ваÑ, %x, за пожертвование и поддержку!</target>
+<source>Connections</source>
+<target>СоединениÑ</target>
+
<source>Recommended range:</source>
<target>Рекомендуемый диапазон:</target>
@@ -1812,9 +1827,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Automatic updates:</source>
<target>ÐвтоматичеÑкие обновлениÑ:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>ТребуетÑÑ Ð¿Ð»Ð°Ñ‚Ð½Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ FreeFileSync</target>
-
<source>Check for Program Updates</source>
<target>Проверка Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¼Ñ‹</target>
@@ -2006,6 +2018,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Edit with FreeFileSync</source>
<target>Редактировать Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>Вот вам картинка животного, вмеÑто рекламы.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>ÐŸÐ¾Ñ€Ñ‚Ð°Ñ‚Ð¸Ð²Ð½Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ FreeFileSync не может быть уÑтановлена в подпапку %x.</target>
@@ -2015,3 +2030,6 @@ This guarantees a consistent state even in case of a serious error.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>%x Ð¾Ð¿Ñ†Ð¸Ñ ÑƒÑтановки доÑтупна только в платной верÑии FreeFileSync.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Приобретите платную верÑию FreeFileSync Ñ Ð±Ð¾Ð½ÑƒÑными функциÑми и помогите Ñохранить FreeFileSync Ñвободным от рекламы.</target>
+
diff --git a/FreeFileSync/Build/Languages/slovak.lng b/FreeFileSync/Build/Languages/slovak.lng
index 8ff29202..769d28dc 100755
--- a/FreeFileSync/Build/Languages/slovak.lng
+++ b/FreeFileSync/Build/Languages/slovak.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Cesta k alternatívnemu súboru GlobalSettings.xml.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>InÅ¡talaÄný súbor je poÅ¡kodený. Prosím preinÅ¡talujte FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>Nie je možné nájsÅ¥ následujúce prieÄinky:</target>
@@ -334,12 +337,12 @@ Aktuálne: %y b
<source>Cannot find %x.</source>
<target>Nie je možné nájsť %x.</target>
-<source>Cannot open file %x.</source>
-<target>Nie je možné otvoriť súbor %x.</target>
-
<source>Cannot find device %x.</source>
<target>Nie je možné nájsť zariadenie %x.</target>
+<source>Cannot open file %x.</source>
+<target>Nie je možné otvoriť súbor %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>Typ položky %x nie je podporovaný:</target>
@@ -470,6 +473,9 @@ Aktuálne: %y b
<source>Total time:</source>
<target>Celkový Äas:</target>
+<source>Cleaning up old log files...</source>
+<target>Odstráňovanie starých log súborov...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Chyba spracovania súboru %x: na riadku %y v stĺpci %z.</target>
@@ -659,6 +665,15 @@ Príkaz bude spustení ak:
<source>Multiple...</source>
<target>Rôzne...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Nie je možné zapísať atribúty súboru %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x a %y majú odlišný obsah.</target>
+
+<source>Data verification error:</source>
+<target>Chyba verifikácie údajov:</target>
+
<source>Moving file %x to %y</source>
<target>Presúvanie súboru %x do %y</target>
@@ -683,14 +698,8 @@ Príkaz bude spustení ak:
<source>Updating attributes of %x</source>
<target>Aktualizácia atribútov súboru %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Nie je možné zapísať atribúty súboru %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x a %y majú odlišný obsah.</target>
-
-<source>Data verification error:</source>
-<target>Chyba verifikácie údajov:</target>
+<source>Source item %x not found</source>
+<target>Zdrojová položka %x sa nenašla</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>Vytváranie Tieňovej kópie zväzkov pre %x...</target>
@@ -752,9 +761,6 @@ Príkaz bude spustení ak:
<source>System: Shut down</source>
<target>Systém: Vypnúť</target>
-<source>Cleaning up old log files...</source>
-<target>Odstráňovanie starých log súborov...</target>
-
<source>Stopped</source>
<target>Zastavené</target>
@@ -888,6 +894,12 @@ Príkaz bude spustení ak:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Prosím vyberte prieÄinok v lokálnom súborovom systéme, sieti alebo multimediálnom zariadení.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Je potrebná FreeFileSync Donation Edition</target>
+
<source>&Save</source>
<target>&Uložiť</target>
@@ -1038,6 +1050,15 @@ Príkaz bude spustení ak:
<source>Handle daylight saving time</source>
<target>PoužívaÅ¥ letný Äas</target>
+<source>Performance improvements:</source>
+<target>Zlepšenie výkonu:</target>
+
+<source>Parallel file operations:</source>
+<target>Paralélne operácie súborov:</target>
+
+<source>How to get best performance?</source>
+<target>Ako získať najlepší výkon:</target>
+
<source>Local settings:</source>
<target>Lokálne Nastavenia:</target>
@@ -1154,15 +1175,6 @@ Príkaz bude spustení ak:
<source>Directory on server:</source>
<target>Adresár na servery:</target>
-<source>Performance improvements:</source>
-<target>Zlepšenie výkonu:</target>
-
-<source>How to get best performance?</source>
-<target>Ako získať najlepší výkon:</target>
-
-<source>Connections for directory reading:</source>
-<target>Pripojenia pri naÄítaní adresárov:</target>
-
<source>SFTP channels per connection:</source>
<target>SFTP kanály na pripojenie:</target>
@@ -1295,8 +1307,17 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>&Predvolené</target>
-<source>Source code written in C++ using:</source>
-<target>Zdrojový kód bol napísaný kompletne v C++ pomocou:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Komentáre a námety sú vždy vítané</target>
+
+<source>Home page</source>
+<target>Domovská stránka</target>
+
+<source>FreeFileSync Forum</source>
+<target>FreeFileSync fórum</target>
+
+<source>Email</source>
+<target>Email</target>
<source>If you like FreeFileSync:</source>
<target>Pokiaľ sa Vám FreeFileSync páÄi:</target>
@@ -1304,20 +1325,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Support with a donation</source>
<target>Podporiť darom</target>
-<source>Donation details</source>
-<target>Detajly darovania</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>Automatická aktualizácia bola zakázaná správcom.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Komentáre a námety sú vždy vítané</target>
-
-<source>Home page</source>
-<target>Domovská stránka</target>
+<source>Donation details</source>
+<target>Detajly darovania</target>
-<source>Email</source>
-<target>Email</target>
+<source>Source code written in C++ using:</source>
+<target>Zdrojový kód bol napísaný kompletne v C++ pomocou:</target>
<source>Published under the GNU General Public License</source>
<target>Vydané pod GNU General Public License (GPL)</target>
@@ -1406,9 +1421,6 @@ This guarantees a consistent state even in case of a serious error.
<source>FreeFileSync %x is available!</source>
<target>Je dostupná FreeFileSync verzia %x!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>InÅ¡talaÄný súbor je poÅ¡kodený. Prosím preinÅ¡talujte FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>Lokálna cesta pre %x nie je k dispozícií.</target>
@@ -1635,6 +1647,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Thank you, %x, for your donation and support!</source>
<target>ÄŽakujem, %x, za dar a podporu!</target>
+<source>Connections</source>
+<target>Pripojenia</target>
+
<source>Recommended range:</source>
<target>OdporúÄaný rozsah:</target>
@@ -1809,9 +1824,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Automatic updates:</source>
<target>Automatická aktualizácia:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Je potrebná FreeFileSync Donation Edition</target>
-
<source>Check for Program Updates</source>
<target>Hľadanie aktualizácií programu</target>
@@ -2003,6 +2015,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Edit with FreeFileSync</source>
<target>Upraviť vo FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>Namiesto reklami je tu obrázok zvieraťa.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>Prenosnú verziu FreeFileSync nie je možné inÅ¡talovaÅ¥ do podprieÄinka %x.</target>
@@ -2012,3 +2027,6 @@ This guarantees a consistent state even in case of a serious error.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>InÅ¡talaÄná možnosÅ¥ %x je dostupná iba pri FreeFileSync Donation Edition.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Získajte Donation Edition s bonusovými funkciami a pomôžte, aby ostal FreeFileSync bez reklami.</target>
+
diff --git a/FreeFileSync/Build/Languages/slovenian.lng b/FreeFileSync/Build/Languages/slovenian.lng
index b958ef19..63121a80 100755
--- a/FreeFileSync/Build/Languages/slovenian.lng
+++ b/FreeFileSync/Build/Languages/slovenian.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Pot do alternativne datoteke GlobalSettings.xml.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Namestitvena datoteka je poškodovana. Prosim ponovno naložite FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>Ne najdem naslednjih map:</target>
@@ -335,12 +338,12 @@ Dejansko: %y bajtov
<source>Cannot find %x.</source>
<target>Ne najdem %x.</target>
-<source>Cannot open file %x.</source>
-<target>Ne morem odpreti datoteke %x.</target>
-
<source>Cannot find device %x.</source>
<target>Ne najdem naprave %x.</target>
+<source>Cannot open file %x.</source>
+<target>Ne morem odpreti datoteke %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>Vrsta postavke %x ni podprta:</target>
@@ -475,6 +478,9 @@ Dejansko: %y bajtov
<source>Total time:</source>
<target>Celoten Äas:</target>
+<source>Cleaning up old log files...</source>
+<target>ÄŒiÅ¡Äenje starih datotek dnevnika...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Napaka pri razÄlenjevanju datoteke %x, vrstica %y, stolpec %z.</target>
@@ -665,6 +671,15 @@ Ukaz se sproži Äe:
<source>Multiple...</source>
<target>VeÄkratno...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Ne morem zapisati datoteÄnih atributov od %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x in %y imata razliÄno vsebino.</target>
+
+<source>Data verification error:</source>
+<target>Napaka pri preverjanju podatkov:</target>
+
<source>Moving file %x to %y</source>
<target>Premikam datoteko %x v %y</target>
@@ -689,14 +704,8 @@ Ukaz se sproži Äe:
<source>Updating attributes of %x</source>
<target>Posodabljam atribute od %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Ne morem zapisati datoteÄnih atributov od %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x in %y imata razliÄno vsebino.</target>
-
-<source>Data verification error:</source>
-<target>Napaka pri preverjanju podatkov:</target>
+<source>Source item %x not found</source>
+<target>Izvorna postavka %x ni bila najdena</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>Ustvarjam Volume Shadow Copy za %x...</target>
@@ -758,9 +767,6 @@ Ukaz se sproži Äe:
<source>System: Shut down</source>
<target>Sistem: IzkljuÄi raÄunalnik</target>
-<source>Cleaning up old log files...</source>
-<target>ÄŒiÅ¡Äenje starih datotek dnevnika...</target>
-
<source>Stopped</source>
<target>Ustavljeno</target>
@@ -895,6 +901,12 @@ Ukaz se sproži Äe:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Prosim izberite mapo na lokalnem datoteÄnem sistemu, mreži ali na MTP napravi.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Zahteva FreeFileSync Donation Edition</target>
+
<source>&Save</source>
<target>&Shrani</target>
@@ -1045,6 +1057,15 @@ Ukaz se sproži Äe:
<source>Handle daylight saving time</source>
<target>UpoÅ¡tevaj poletni in zimski Äas</target>
+<source>Performance improvements:</source>
+<target>Izboljšave zmogljivosti:</target>
+
+<source>Parallel file operations:</source>
+<target>Vzporedne operacije datoteke:</target>
+
+<source>How to get best performance?</source>
+<target>Kako doseÄi najboljÅ¡o uÄinkovitost?</target>
+
<source>Local settings:</source>
<target>Lokalne nastavitve:</target>
@@ -1161,15 +1182,6 @@ Ukaz se sproži Äe:
<source>Directory on server:</source>
<target>Imenik na strežniku:</target>
-<source>Performance improvements:</source>
-<target>Izboljšave zmogljivosti:</target>
-
-<source>How to get best performance?</source>
-<target>Kako doseÄi najboljÅ¡o uÄinkovitost?</target>
-
-<source>Connections for directory reading:</source>
-<target>Povezave za branje imenika:</target>
-
<source>SFTP channels per connection:</source>
<target>SFTP kanali za povezavo:</target>
@@ -1305,8 +1317,17 @@ To zagotavlja dosledno stanje tudi v primeru resne napake.
<source>&Default</source>
<target>&Privzeto</target>
-<source>Source code written in C++ using:</source>
-<target>Izvorna koda napisana v C++ z uporabo:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Povratne informacije in predlogi so dobrodošli</target>
+
+<source>Home page</source>
+<target>DomaÄa stran</target>
+
+<source>FreeFileSync Forum</source>
+<target>FreeFileSync Forum</target>
+
+<source>Email</source>
+<target>Elektronska pošta</target>
<source>If you like FreeFileSync:</source>
<target>ÄŒe vam je FreeFileSync vÅ¡eÄ:</target>
@@ -1314,20 +1335,14 @@ To zagotavlja dosledno stanje tudi v primeru resne napake.
<source>Support with a donation</source>
<target>Podpora z donacijo</target>
-<source>Donation details</source>
-<target>Podrobnosti o donaciji</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>Skrbnik je onemogoÄil samodejno posodabljanje.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Povratne informacije in predlogi so dobrodošli</target>
-
-<source>Home page</source>
-<target>DomaÄa stran</target>
+<source>Donation details</source>
+<target>Podrobnosti o donaciji</target>
-<source>Email</source>
-<target>Elektronska pošta</target>
+<source>Source code written in C++ using:</source>
+<target>Izvorna koda napisana v C++ z uporabo:</target>
<source>Published under the GNU General Public License</source>
<target>Objavljeno pod licenco GNU General Public</target>
@@ -1416,9 +1431,6 @@ To zagotavlja dosledno stanje tudi v primeru resne napake.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x je na voljo</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Namestitvena datoteka je poškodovana. Prosim ponovno naložite FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>Lokalna pot ni na voljo za %x.</target>
@@ -1649,6 +1661,9 @@ To zagotavlja dosledno stanje tudi v primeru resne napake.
<source>Thank you, %x, for your donation and support!</source>
<target>Najlepša hvala, %x, za vašo donacijo in podporo!</target>
+<source>Connections</source>
+<target>Povezave</target>
+
<source>Recommended range:</source>
<target>PriporoÄeni obseg:</target>
@@ -1826,9 +1841,6 @@ To zagotavlja dosledno stanje tudi v primeru resne napake.
<source>Automatic updates:</source>
<target>Samodejne posodobitve:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Zahteva FreeFileSync Donation Edition</target>
-
<source>Check for Program Updates</source>
<target>Preveri obstoj posodobitve programa</target>
@@ -2022,6 +2034,9 @@ To zagotavlja dosledno stanje tudi v primeru resne napake.
<source>Edit with FreeFileSync</source>
<target>Uredi z FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>Namesto oglasa je tukaj žival.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>Prenosne razliÄice FreeFileSync ni mogoÄe namestiti v podmapo %x.</target>
@@ -2031,3 +2046,6 @@ To zagotavlja dosledno stanje tudi v primeru resne napake.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>Možnost namestitve %x je na voljo samo v FreeFileSync Donation Edition.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Pridobite donacijsko izdajo s bonusnimi funkcijami in pomagajte ohraniti FreeFileSync brez oglasov.</target>
+
diff --git a/FreeFileSync/Build/Languages/spanish.lng b/FreeFileSync/Build/Languages/spanish.lng
index d440e564..dea4aaac 100755
--- a/FreeFileSync/Build/Languages/spanish.lng
+++ b/FreeFileSync/Build/Languages/spanish.lng
@@ -29,7 +29,7 @@
<target>Creando carpeta %x</target>
<source>Creating symbolic link %x</source>
-<target>Creando el vínculo simbólico %x</target>
+<target>Creando vínculo simbólico %x</target>
<source>Moving file %x to the recycle bin</source>
<target>Mover archivo %x a la papelera de reciclaje</target>
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Ruta a un archivo alternativo GlobalSettings.xml.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Los archivos de instalación están dañados. Reinstale FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>No se pudieron encontrar las siguiente carpetas:</target>
@@ -149,7 +152,7 @@
<target>Los elementos sólo se diferencian en los atributos</target>
<source>Resolving symbolic link %x</source>
-<target>Resolviendo el vínculo simbólico %x</target>
+<target>Resolviendo vínculo simbólico %x</target>
<source>Comparing content of files %x</source>
<target>Comparación del contenido de los archivos %x</target>
@@ -333,17 +336,17 @@ Reales: %y bytes
<source>Cannot find %x.</source>
<target>No se encuentra %x.</target>
-<source>Cannot open file %x.</source>
-<target>No se puede abrir el archivo %x.</target>
-
<source>Cannot find device %x.</source>
<target>No se encuentra el dispositivo %x.</target>
+<source>Cannot open file %x.</source>
+<target>No se puede abrir el archivo %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>El tipo de objeto %x no esta soportado:</target>
<source>Cannot delete symbolic link %x.</source>
-<target>No se puede eliminar el el vínculo simbólico %x.</target>
+<target>No se puede eliminar el vínculo simbólico %x.</target>
<source>Cannot determine free disk space for %x.</source>
<target>No se puede determinar el espacio libre en disco para %x.</target>
@@ -465,6 +468,9 @@ Reales: %y bytes
<source>Total time:</source>
<target>Tiempo total:</target>
+<source>Cleaning up old log files...</source>
+<target>Limpiando antiguos archivos de registro...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Error analizando archivo %x, fila %y, columna %z.</target>
@@ -653,6 +659,15 @@ El comando es disparado si:
<source>Multiple...</source>
<target>Múltiple...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>No se pueden escribir los atributos de archivo de %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x y %y tienen contenidos diferentes.</target>
+
+<source>Data verification error:</source>
+<target>Error de verificación de datos:</target>
+
<source>Moving file %x to %y</source>
<target>Mover archivo de %x a %y</target>
@@ -669,7 +684,7 @@ El comando es disparado si:
<target>Actualizando archivo %x</target>
<source>Updating symbolic link %x</source>
-<target>Actualizando el vínculo simbólico %x</target>
+<target>Actualizando vínculo simbólico %x</target>
<source>Verifying file %x</source>
<target>Verificación del archivo %x</target>
@@ -677,14 +692,8 @@ El comando es disparado si:
<source>Updating attributes of %x</source>
<target>Actualizar atributos de %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>No se pueden escribir los atributos de archivo de %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x y %y tienen contenidos diferentes.</target>
-
-<source>Data verification error:</source>
-<target>Error de verificación de datos:</target>
+<source>Source item %x not found</source>
+<target>No se encontró el archivo de origen %x</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>Creando una Instantánea de volumen para %x...</target>
@@ -696,7 +705,7 @@ El comando es disparado si:
<target>El campo de entrada de la carpeta de destino no debe estar vacío.</target>
<source>Source folder %x not found.</source>
-<target>El archivo de origen %x no ha sido encontrado.</target>
+<target>No se encontró la carpeta de origen %x.</target>
<source>Please enter a target folder for versioning.</source>
<target>Indique una carpeta de destino para la versión.</target>
@@ -746,9 +755,6 @@ El comando es disparado si:
<source>System: Shut down</source>
<target>Sistema: apagar</target>
-<source>Cleaning up old log files...</source>
-<target>Limpiando antiguos archivos de registro...</target>
-
<source>Stopped</source>
<target>Detenido</target>
@@ -881,6 +887,12 @@ El comando es disparado si:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Seleccione otra carpeta del systema de archivos local, en red o en un dispositivo MTP.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Requiere FreeFileSync Donation Edition</target>
+
<source>&Save</source>
<target>&Guardar</target>
@@ -1031,6 +1043,15 @@ El comando es disparado si:
<source>Handle daylight saving time</source>
<target>Respetar el horario de verano</target>
+<source>Performance improvements:</source>
+<target>Mejoras en el rendimiento:</target>
+
+<source>Parallel file operations:</source>
+<target>Operaciones paralelas sobre archivos:</target>
+
+<source>How to get best performance?</source>
+<target>¿Cómo obtener el mejor rendimiento?</target>
+
<source>Local settings:</source>
<target>Opciones locales:</target>
@@ -1147,15 +1168,6 @@ El comando es disparado si:
<source>Directory on server:</source>
<target>Directorio del servidor:</target>
-<source>Performance improvements:</source>
-<target>Mejoras en el rendimiento:</target>
-
-<source>How to get best performance?</source>
-<target>¿Cómo obtener el mejor rendimiento?</target>
-
-<source>Connections for directory reading:</source>
-<target>Conexiones para lectura de directorios:</target>
-
<source>SFTP channels per connection:</source>
<target>Canales SFTP por conexión:</target>
@@ -1291,8 +1303,17 @@ Esto garantiza un estado coherente incluso en caso de error grave.
<source>&Default</source>
<target>&Configuración predeterminada</target>
-<source>Source code written in C++ using:</source>
-<target>Código fuente C++ con soporte de :</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Tus comentarios y sugerencias son bienvenidos :</target>
+
+<source>Home page</source>
+<target>Página de inicio</target>
+
+<source>FreeFileSync Forum</source>
+<target>Foro de FreeFileSync</target>
+
+<source>Email</source>
+<target>Correo electrónico</target>
<source>If you like FreeFileSync:</source>
<target>Si te resulta útil FreeFileSync:</target>
@@ -1300,20 +1321,14 @@ Esto garantiza un estado coherente incluso en caso de error grave.
<source>Support with a donation</source>
<target>¡Contribuye con una donación!</target>
-<source>Donation details</source>
-<target>Detalles para donación</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>La actualización automática ha sido desactivada por el administrador.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Tus comentarios y sugerencias son bienvenidos :</target>
-
-<source>Home page</source>
-<target>Página de inicio</target>
+<source>Donation details</source>
+<target>Detalles para donación</target>
-<source>Email</source>
-<target>Correo electrónico</target>
+<source>Source code written in C++ using:</source>
+<target>Código fuente C++ con soporte de :</target>
<source>Published under the GNU General Public License</source>
<target>Publicado bajo régimen de derechos GNU General Public License :</target>
@@ -1402,9 +1417,6 @@ Esto garantiza un estado coherente incluso en caso de error grave.
<source>FreeFileSync %x is available!</source>
<target>¡Ya está disponible FreeFileSync %x!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Los archivos de instalación están dañados. Reinstale FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>Ruta local no disponible para %x.</target>
@@ -1627,6 +1639,9 @@ Esto garantiza un estado coherente incluso en caso de error grave.
<source>Thank you, %x, for your donation and support!</source>
<target>¡Muchas gracias, %x, por su contribución y ayuda!</target>
+<source>Connections</source>
+<target>Conexiones</target>
+
<source>Recommended range:</source>
<target>Rango recomendado:</target>
@@ -1798,9 +1813,6 @@ Esto garantiza un estado coherente incluso en caso de error grave.
<source>Automatic updates:</source>
<target>Actualizaciones automáticas:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Requiere FreeFileSync Donation Edition</target>
-
<source>Check for Program Updates</source>
<target>Buscar actualizaciones del programa</target>
@@ -1990,6 +2002,9 @@ Esto garantiza un estado coherente incluso en caso de error grave.
<source>Edit with FreeFileSync</source>
<target>Modificar con FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>En lugar de un anuncio, verá una imagen como ésta.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>No se puede instalar la versión portátil de FreeFileSync en una subcarpeta de %x.</target>
@@ -1999,3 +2014,6 @@ Esto garantiza un estado coherente incluso en caso de error grave.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>La opción %x de instalación sólo está disponible para la versión FreeFileSync Donation Edition.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Obtenga la FreeFileSync Donation Edition con sus características extra y ayude a mantener FreeFileSync libre de anuncios.</target>
+
diff --git a/FreeFileSync/Build/Languages/swedish.lng b/FreeFileSync/Build/Languages/swedish.lng
index 6963e231..e7879ed7 100755
--- a/FreeFileSync/Build/Languages/swedish.lng
+++ b/FreeFileSync/Build/Languages/swedish.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Sökväg till en alternativ GlobalSettings.xml-fil.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Installationsfilerna är skadade. Installera om FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>Kan inte hitta följande mappar:</target>
@@ -333,12 +336,12 @@ Aktuell: %y byte
<source>Cannot find %x.</source>
<target>Kan inte hitta %x.</target>
-<source>Cannot open file %x.</source>
-<target>Kan inte öppna %x.</target>
-
<source>Cannot find device %x.</source>
<target>Kan inte hitta enheten %x.</target>
+<source>Cannot open file %x.</source>
+<target>Kan inte öppna %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>Objekttyp %x stöds ej:</target>
@@ -465,6 +468,9 @@ Aktuell: %y byte
<source>Total time:</source>
<target>Total tid:</target>
+<source>Cleaning up old log files...</source>
+<target>Rensar ut gamla loggfiler...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Tolkningsfel på filen %x, rad %y, kolumn %z.</target>
@@ -653,6 +659,15 @@ Kommandot triggas om:
<source>Multiple...</source>
<target>Flera...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Kan inte skriva filattribut för %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x och %y har olika innehåll.</target>
+
+<source>Data verification error:</source>
+<target>Dataverifieringsfel:</target>
+
<source>Moving file %x to %y</source>
<target>Flyttar filen %x till %y</target>
@@ -677,14 +692,8 @@ Kommandot triggas om:
<source>Updating attributes of %x</source>
<target>Uppdaterar attribut för %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Kan inte skriva filattribut för %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x och %y har olika innehåll.</target>
-
-<source>Data verification error:</source>
-<target>Dataverifieringsfel:</target>
+<source>Source item %x not found</source>
+<target>Källobjektet %x hittades inte</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>Skapar en 'Volume Shadow Copy' för %x...</target>
@@ -746,9 +755,6 @@ Kommandot triggas om:
<source>System: Shut down</source>
<target>System: Stäng av</target>
-<source>Cleaning up old log files...</source>
-<target>Rensar ut gamla loggfiler...</target>
-
<source>Stopped</source>
<target>Stoppad</target>
@@ -881,6 +887,12 @@ Kommandot triggas om:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Välj en mapp på i ett lokalt filsystem, nätverk eller en MTP-enhet.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Kräver FreeFileSync Donation Edition</target>
+
<source>&Save</source>
<target>&Spara</target>
@@ -1031,6 +1043,15 @@ Kommandot triggas om:
<source>Handle daylight saving time</source>
<target>Hantera sommartid</target>
+<source>Performance improvements:</source>
+<target>Prestandaförbättringar:</target>
+
+<source>Parallel file operations:</source>
+<target>Parallella filoperationer:</target>
+
+<source>How to get best performance?</source>
+<target>Hur får man ut bästa prestanda?</target>
+
<source>Local settings:</source>
<target>Lokala inställningar:</target>
@@ -1147,15 +1168,6 @@ Kommandot triggas om:
<source>Directory on server:</source>
<target>Målmapp på servern:</target>
-<source>Performance improvements:</source>
-<target>Prestandaförbättringar:</target>
-
-<source>How to get best performance?</source>
-<target>Hur får man ut bästa prestanda?</target>
-
-<source>Connections for directory reading:</source>
-<target>Anslutningar för mappläsning:</target>
-
<source>SFTP channels per connection:</source>
<target>SFTP-kanaler per anslutning:</target>
@@ -1291,8 +1303,17 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel.
<source>&Default</source>
<target>&Standard</target>
-<source>Source code written in C++ using:</source>
-<target>Källkod skriven i C++ med hjälp av:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Återkoppling och förslag är välkommna</target>
+
+<source>Home page</source>
+<target>Hemsida</target>
+
+<source>FreeFileSync Forum</source>
+<target>FreeFileSync Forum</target>
+
+<source>Email</source>
+<target>E-post</target>
<source>If you like FreeFileSync:</source>
<target>Om du gillar FreeFileSync:</target>
@@ -1300,20 +1321,14 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel.
<source>Support with a donation</source>
<target>Stöd oss med en donation</target>
-<source>Donation details</source>
-<target>Donationsuppgifter</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>Automatisk uppdatering inaktiverades av administratören.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Återkoppling och förslag är välkommna</target>
-
-<source>Home page</source>
-<target>Hemsida</target>
+<source>Donation details</source>
+<target>Donationsuppgifter</target>
-<source>Email</source>
-<target>E-post</target>
+<source>Source code written in C++ using:</source>
+<target>Källkod skriven i C++ med hjälp av:</target>
<source>Published under the GNU General Public License</source>
<target>Publiserad under GNU General Public License</target>
@@ -1402,9 +1417,6 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x finns tillgänglig!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Installationsfilerna är skadade. Installera om FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>Lokal sökväg ej tillgänglig för %x.</target>
@@ -1627,6 +1639,9 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel.
<source>Thank you, %x, for your donation and support!</source>
<target>Tack %x, för din donation och ditt stöd!</target>
+<source>Connections</source>
+<target>Anslutningar</target>
+
<source>Recommended range:</source>
<target>Rekommenderat intervall:</target>
@@ -1798,9 +1813,6 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel.
<source>Automatic updates:</source>
<target>Automatiska uppdateringar:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Kräver FreeFileSync Donation Edition</target>
-
<source>Check for Program Updates</source>
<target>Sök efter programuppdateringar</target>
@@ -1990,6 +2002,9 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel.
<source>Edit with FreeFileSync</source>
<target>Redigera med FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>Här är ett djur, istället för reklam.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>FreeFileSync portabel version kan inte installeras i en undermapp till %x.</target>
@@ -1999,3 +2014,6 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>Installationsalternativet %x är endast tillgängligt i FreeFileSync Donation Edition.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Hämta donationsversionen med bonusfunktioner och hjälp till att hålla FreeFileSync reklamfri.</target>
+
diff --git a/FreeFileSync/Build/Languages/turkish.lng b/FreeFileSync/Build/Languages/turkish.lng
index 8a87fc93..1bdbf530 100755
--- a/FreeFileSync/Build/Languages/turkish.lng
+++ b/FreeFileSync/Build/Languages/turkish.lng
@@ -112,6 +112,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Alternatif GlobalSettings.xml dosyasının yolu.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Kurulum dosyaları bozulmuş. Lütfen FreeFileSync uygulamasını yeniden kurun.</target>
+
<source>Cannot find the following folders:</source>
<target>Aşağıdaki klasörler bulunamadı:</target>
@@ -333,12 +336,12 @@ Gerçekleşen: %y bayt
<source>Cannot find %x.</source>
<target>%x bulunamadı.</target>
-<source>Cannot open file %x.</source>
-<target>%x dosyası açılamadı.</target>
-
<source>Cannot find device %x.</source>
<target>%x aygıtı bulunamadı.</target>
+<source>Cannot open file %x.</source>
+<target>%x dosyası açılamadı.</target>
+
<source>Type of item %x is not supported:</source>
<target>%x ögesi türü desteklenmiyor:</target>
@@ -465,6 +468,9 @@ Gerçekleşen: %y bayt
<source>Total time:</source>
<target>Toplam süre:</target>
+<source>Cleaning up old log files...</source>
+<target>Eski günlük dosyaları temizleniyor...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>%x dosyası işlenirken sorun çıktı, satır %y, sütun %z.</target>
@@ -653,6 +659,15 @@ Komut şu durumlarda yürütülür:
<source>Multiple...</source>
<target>Çoklu...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>%x dosya öznitelikleri yazılamadı.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x ve %y farklı içeriklere sahip.</target>
+
+<source>Data verification error:</source>
+<target>Veri doÄŸrulama sorunu:</target>
+
<source>Moving file %x to %y</source>
<target>%x dosyası %y içine taşınıyor</target>
@@ -677,14 +692,8 @@ Komut şu durumlarda yürütülür:
<source>Updating attributes of %x</source>
<target>%x öznitelikleri güncelleniyor</target>
-<source>Cannot write file attributes of %x.</source>
-<target>%x dosya öznitelikleri yazılamadı.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x ve %y farklı içeriklere sahip.</target>
-
-<source>Data verification error:</source>
-<target>Veri doÄŸrulama sorunu:</target>
+<source>Source item %x not found</source>
+<target>%x kaynak ögesi bulunamadı</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>%x için Birim Gölge Hizmeti oluşturuluyor...</target>
@@ -746,9 +755,6 @@ Komut şu durumlarda yürütülür:
<source>System: Shut down</source>
<target>Sistem: Kapat</target>
-<source>Cleaning up old log files...</source>
-<target>Eski günlük dosyaları temizleniyor...</target>
-
<source>Stopped</source>
<target>Durduruldu</target>
@@ -881,6 +887,12 @@ Komut şu durumlarda yürütülür:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Lütfen yerel dosya sistemi, ağ ya da MTP aygıtı üzerinde bulunan bir klasör seçin.</target>
+<source>Defined by context of use</source>
+<target></target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>FreeFileSync Donation Edition gereklidir</target>
+
<source>&Save</source>
<target>&Kaydet</target>
@@ -1031,6 +1043,15 @@ Komut şu durumlarda yürütülür:
<source>Handle daylight saving time</source>
<target>Yaz Saati Hakkında Bilgiler</target>
+<source>Performance improvements:</source>
+<target>Başarım İyileştirmeleri:</target>
+
+<source>Parallel file operations:</source>
+<target>Paralel dosya iÅŸlemleri:</target>
+
+<source>How to get best performance?</source>
+<target>En iyi başarım nasıl elde edilir?</target>
+
<source>Local settings:</source>
<target>Yerel ayarlar:</target>
@@ -1147,15 +1168,6 @@ Komut şu durumlarda yürütülür:
<source>Directory on server:</source>
<target>Sunucudaki Klasör:</target>
-<source>Performance improvements:</source>
-<target>Başarım İyileştirmeleri:</target>
-
-<source>How to get best performance?</source>
-<target>En iyi başarım nasıl elde edilir?</target>
-
-<source>Connections for directory reading:</source>
-<target>Klasör okuma bağlantıları:</target>
-
<source>SFTP channels per connection:</source>
<target>Bir Bağlantı için SFTP Kanalı Sayısı:</target>
@@ -1291,8 +1303,17 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y
<source>&Default</source>
<target>&Varsayılan</target>
-<source>Source code written in C++ using:</source>
-<target>Kaynak kodu C++ kullanılarak yazılmıştır:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Öneri ve geri bildirimlerinizi bekleriz</target>
+
+<source>Home page</source>
+<target>Ana Sayfa</target>
+
+<source>FreeFileSync Forum</source>
+<target>FreeFileSync Forumu</target>
+
+<source>Email</source>
+<target>E-posta</target>
<source>If you like FreeFileSync:</source>
<target>FreeFileSync hoÅŸunuza gittiyse:</target>
@@ -1300,20 +1321,14 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y
<source>Support with a donation</source>
<target>Bağış yaparak destek olun</target>
-<source>Donation details</source>
-<target>Bağış Bilgileri</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>Otomatik güncelleme yönetici tarafından devre dışı bırakılmış.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Öneri ve geri bildirimlerinizi bekleriz</target>
-
-<source>Home page</source>
-<target>Ana Sayfa</target>
+<source>Donation details</source>
+<target>Bağış Bilgileri</target>
-<source>Email</source>
-<target>E-posta</target>
+<source>Source code written in C++ using:</source>
+<target>Kaynak kodu C++ kullanılarak yazılmıştır:</target>
<source>Published under the GNU General Public License</source>
<target>GNU Genel Kamu Lisansı koşulları altında yayınlanmıştır</target>
@@ -1402,9 +1417,6 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x sürümü yayınlanmış!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Kurulum dosyaları bozulmuş. Lütfen FreeFileSync uygulamasını yeniden kurun.</target>
-
<source>Local path not available for %x.</source>
<target>%x için yerel yol bulunamadı.</target>
@@ -1627,6 +1639,9 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y
<source>Thank you, %x, for your donation and support!</source>
<target>Sevgili %x, bağışın ve desteğin için teşekkürler!</target>
+<source>Connections</source>
+<target>Bağlantılar</target>
+
<source>Recommended range:</source>
<target>Önerilen Aralık:</target>
@@ -1798,9 +1813,6 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y
<source>Automatic updates:</source>
<target>Otomatik güncellemeler:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>FreeFileSync Donation Edition gereklidir</target>
-
<source>Check for Program Updates</source>
<target>Güncelleme Denetimi</target>
@@ -1958,7 +1970,7 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y
<target>Kurulum Klasörünü Seçin:</target>
<source>Create shortcuts:</source>
-<target>Kısayollar Oluşturulsun:</target>
+<target>Oluşturulacak Kısayollar:</target>
<source>Desktop</source>
<target>Masaüstü</target>
@@ -1990,6 +2002,9 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y
<source>Edit with FreeFileSync</source>
<target>FreeFileSync ile Düzenlensin</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>Bir reklam yerine burada bir hayvan var.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>FreeFileSync taşınabilir sürümü bir %x alt klasörüne yüklenemez.</target>
@@ -1999,3 +2014,6 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>%x kurulumu yalnız FreeFileSync Donation Sürümü ile yapılabilir.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Hediye özellikleri edinmek ve FreeFileSync yazılımını reklamsız kullanmak için Bağış Sürümünü alın.</target>
+
diff --git a/FreeFileSync/Build/Languages/ukrainian.lng b/FreeFileSync/Build/Languages/ukrainian.lng
index 855507d3..215d0ef7 100755
--- a/FreeFileSync/Build/Languages/ukrainian.lng
+++ b/FreeFileSync/Build/Languages/ukrainian.lng
@@ -7,6 +7,9 @@
<plural_definition>n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2</plural_definition>
</header>
+<source>Defined by context of use</source>
+<target></target>
+
<source>Both sides have changed since last synchronization.</source>
<target>З моменту оÑтанньої Ñинхронізації з обох Ñторін відбулиÑÑ Ð·Ð¼Ñ–Ð½Ð¸.</target>
@@ -112,6 +115,9 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>ШлÑÑ… до альтернативного файлу GlobalSettings.xml.</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Файли вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¿Ð¾ÑˆÐºÐ¾Ð´Ð¶ÐµÐ½Ñ–. Будь лаÑка, перевÑтановіть FreeFileSync.</target>
+
<source>Cannot find the following folders:</source>
<target>Ðе вдаєтьÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ такі папки:</target>
@@ -334,12 +340,12 @@ Actual: %y bytes
<source>Cannot find %x.</source>
<target>Ðе вдаєтьÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ %x.</target>
-<source>Cannot open file %x.</source>
-<target>Ðе вдаєтьÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ файл %x.</target>
-
<source>Cannot find device %x.</source>
<target>Ðе вдаєтьÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ приÑтрій %x.</target>
+<source>Cannot open file %x.</source>
+<target>Ðе вдаєтьÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ файл %x.</target>
+
<source>Type of item %x is not supported:</source>
<target>Тип елемента %x не підтримуєтьÑÑ:</target>
@@ -470,6 +476,9 @@ Actual: %y bytes
<source>Total time:</source>
<target>Загальний чаÑ:</target>
+<source>Cleaning up old log files...</source>
+<target>ОчиÑтка Ñтарих журналів...</target>
+
<source>Error parsing file %x, row %y, column %z.</source>
<target>Помилка розбору файлу %x, Ñ€Ñдок %y, колонка %z.</target>
@@ -659,6 +668,15 @@ The command is triggered if:
<source>Multiple...</source>
<target>Різні варіанти...</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Ðе вдаєтьÑÑ Ð·Ð°Ð¿Ð¸Ñати атрибути файлу %x.</target>
+
+<source>%x and %y have different content.</source>
+<target>%x Ñ– %y мають різний вміÑÑ‚.</target>
+
+<source>Data verification error:</source>
+<target>Помилка перевірки даних:</target>
+
<source>Moving file %x to %y</source>
<target>ÐŸÐµÑ€ÐµÐ¼Ñ–Ñ‰ÐµÐ½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ %x до %y</target>
@@ -683,14 +701,8 @@ The command is triggered if:
<source>Updating attributes of %x</source>
<target>ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð°Ñ‚Ñ€Ð¸Ð±ÑƒÑ‚Ñ–Ð² %x</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Ðе вдаєтьÑÑ Ð·Ð°Ð¿Ð¸Ñати атрибути файлу %x.</target>
-
-<source>%x and %y have different content.</source>
-<target>%x Ñ– %y мають різний вміÑÑ‚.</target>
-
-<source>Data verification error:</source>
-<target>Помилка перевірки даних:</target>
+<source>Source item %x not found</source>
+<target>Вихідний елемент %x не знайдено</target>
<source>Creating a Volume Shadow Copy for %x...</source>
<target>Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¢Ñ–Ð½ÑŒÐ¾Ð²Ð¾Ñ— Копії Ð´Ð»Ñ %x...</target>
@@ -752,9 +764,6 @@ The command is triggered if:
<source>System: Shut down</source>
<target>СиÑтема: Ð—Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸</target>
-<source>Cleaning up old log files...</source>
-<target>ОчиÑтка Ñтарих журналів...</target>
-
<source>Stopped</source>
<target>Зупинено</target>
@@ -888,6 +897,9 @@ The command is triggered if:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Будь лаÑка, виберіть папку на локальній файловій ÑиÑтемі, в мережі чи на MTP приÑтрої.</target>
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Потрібна FreeFileSync Donation Edition</target>
+
<source>&Save</source>
<target>&Зберегти</target>
@@ -1038,6 +1050,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>Перехід на літній Ñ‡Ð°Ñ Ð²Ñ€ÑƒÑ‡Ð½Ñƒ</target>
+<source>Performance improvements:</source>
+<target>ÐŸÑ–Ð´Ð²Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð´ÑƒÐºÑ‚Ð¸Ð²Ð½Ð¾ÑÑ‚Ñ–:</target>
+
+<source>Parallel file operations:</source>
+<target>Паралельні файлові операції:</target>
+
+<source>How to get best performance?</source>
+<target>Як отримати найкращу швидкодію?</target>
+
<source>Local settings:</source>
<target>Локальні налаштуваннÑ:</target>
@@ -1154,15 +1175,6 @@ The command is triggered if:
<source>Directory on server:</source>
<target>Папка на Ñервері:</target>
-<source>Performance improvements:</source>
-<target>ÐŸÑ–Ð´Ð²Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð´ÑƒÐºÑ‚Ð¸Ð²Ð½Ð¾ÑÑ‚Ñ–:</target>
-
-<source>How to get best performance?</source>
-<target>Як отримати найкращу швидкодію?</target>
-
-<source>Connections for directory reading:</source>
-<target>З'єднань Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð¿Ð°Ð¿Ð¾Ðº:</target>
-
<source>SFTP channels per connection:</source>
<target>SFTP канали на з'єднаннÑ:</target>
@@ -1298,8 +1310,17 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>&За замовчуваннÑм</target>
-<source>Source code written in C++ using:</source>
-<target>Код програми напиÑаний на C++ з викориÑтаннÑм:</target>
+<source>Feedback and suggestions are welcome</source>
+<target>Відгуки та пропозиції вітаютьÑÑ</target>
+
+<source>Home page</source>
+<target>Ð”Ð¾Ð¼Ð°ÑˆÐ½Ñ Ñторінка</target>
+
+<source>FreeFileSync Forum</source>
+<target>Форум FreeFileSync</target>
+
+<source>Email</source>
+<target>Пошта</target>
<source>If you like FreeFileSync:</source>
<target>Якщо Вам ÑподобавÑÑ FreeFileSync:</target>
@@ -1307,20 +1328,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Support with a donation</source>
<target>Підтримати пожертвуваннÑм.</target>
-<source>Donation details</source>
-<target>Докладно про пожертвуваннÑ</target>
-
<source>The auto updater was disabled by the administrator.</source>
<target>Ðвтоматичне Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð±ÑƒÐ»Ð¾ відключене адмініÑтратором.</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Відгуки та пропозиції вітаютьÑÑ</target>
-
-<source>Home page</source>
-<target>Ð”Ð¾Ð¼Ð°ÑˆÐ½Ñ Ñторінка</target>
+<source>Donation details</source>
+<target>Докладно про пожертвуваннÑ</target>
-<source>Email</source>
-<target>Пошта</target>
+<source>Source code written in C++ using:</source>
+<target>Код програми напиÑаний на C++ з викориÑтаннÑм:</target>
<source>Published under the GNU General Public License</source>
<target>Видано за ліцензією GNU General Public License</target>
@@ -1409,9 +1424,6 @@ This guarantees a consistent state even in case of a serious error.
<source>FreeFileSync %x is available!</source>
<target>FreeFileSync %x доÑтупний!</target>
-<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
-<target>Файли вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¿Ð¾ÑˆÐºÐ¾Ð´Ð¶ÐµÐ½Ñ–. Будь лаÑка, перевÑтановіть FreeFileSync.</target>
-
<source>Local path not available for %x.</source>
<target>Локальний шлÑÑ… не доÑтупний Ð´Ð»Ñ %x.</target>
@@ -1638,6 +1650,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Thank you, %x, for your donation and support!</source>
<target>ДÑкуємо Вам, %x, за ваше Ð¿Ð¾Ð¶ÐµÑ€Ñ‚Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð° підтримку!</target>
+<source>Connections</source>
+<target>З'єднаннÑ</target>
+
<source>Recommended range:</source>
<target>Рекомендований діапазон:</target>
@@ -1812,9 +1827,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Automatic updates:</source>
<target>Ðвтоматичні оновленнÑ:</target>
-<source>Requires FreeFileSync Donation Edition</source>
-<target>Потрібна FreeFileSync Donation Edition</target>
-
<source>Check for Program Updates</source>
<target>Перевірка Оновлень Програми</target>
@@ -2006,6 +2018,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Edit with FreeFileSync</source>
<target>Редагувати за допомогою FreeFileSync</target>
+<source>Instead of an ad, here's an animal.</source>
+<target>ЗаміÑÑ‚ÑŒ реклами, оÑÑŒ тварина.</target>
+
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>Портативна верÑÑ–Ñ FreeFileSync не може бути вÑтановлена в підпапку %x.</target>
@@ -2015,3 +2030,6 @@ This guarantees a consistent state even in case of a serious error.
<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
<target>Варіант уÑтановки %x доÑтупний тільки у FreeFileSync Donation Edition.</target>
+<source>Get the Donation Edition with bonus features and help keep FreeFileSync ad-free.</source>
+<target>Отримайте Donation Edition з бонуÑними функціÑми та допоможіть зберегти FreeFileSync без реклами.</target>
+
diff --git a/FreeFileSync/Build/Resources.zip b/FreeFileSync/Build/Resources.zip
index 28e925b0..a1d93d01 100755
--- a/FreeFileSync/Build/Resources.zip
+++ b/FreeFileSync/Build/Resources.zip
Binary files differ
diff --git a/FreeFileSync/Source/Makefile b/FreeFileSync/Source/Makefile
index 33197267..dffa2308 100755
--- a/FreeFileSync/Source/Makefile
+++ b/FreeFileSync/Source/Makefile
@@ -5,9 +5,11 @@ SHAREDIR = $(DESTDIR)$(prefix)/share
APPSHAREDIR = $(SHAREDIR)/$(APPNAME)
DOCSHAREDIR = $(SHAREDIR)/doc/$(APPNAME)
-CXXFLAGS = -std=c++14 -pipe -DWXINTL_NO_GETTEXT_MACRO -I../.. -I../../zenXml -include "zen/i18n.h" -Wall -O3 -DNDEBUG `wx-config --cxxflags --debug=no` -pthread
+CXXFLAGS = -std=c++17 -pipe -DWXINTL_NO_GETTEXT_MACRO -I../.. -I../../zenXml -I../../boost -include "zen/i18n.h" -include "zen/warn_static.h" \
+-Wall -Wfatal-errors -Winit-self -Wmissing-include-dirs -Wswitch-enum -Wmain -Wnon-virtual-dtor -Wcast-align -Wshadow -Wno-deprecated-declarations \
+-O3 -DNDEBUG `wx-config --cxxflags --debug=no` -pthread
-LINKFLAGS = -s `wx-config --libs std, aui --debug=no` -lboost_thread -lboost_chrono -lboost_system -lz -pthread
+LINKFLAGS = -s -no-pie `wx-config --libs std, aui --debug=no` -pthread
#Gtk - support recycler/icon loading/no button border/grid scrolling
CXXFLAGS += `pkg-config --cflags gtk+-2.0`
@@ -27,87 +29,86 @@ CXXFLAGS += `pkg-config --cflags unity` -DHAVE_UBUNTU_UNITY
LINKFLAGS += `pkg-config --libs unity`
endif
-CPP_LIST=
-CPP_LIST+=algorithm.cpp
-CPP_LIST+=application.cpp
-CPP_LIST+=comparison.cpp
-CPP_LIST+=structures.cpp
-CPP_LIST+=synchronization.cpp
-CPP_LIST+=fs/abstract.cpp
-CPP_LIST+=fs/concrete.cpp
-CPP_LIST+=fs/native.cpp
-CPP_LIST+=file_hierarchy.cpp
-CPP_LIST+=ui/cfg_grid.cpp
-CPP_LIST+=ui/file_grid.cpp
-CPP_LIST+=ui/folder_history_box.cpp
-CPP_LIST+=ui/command_box.cpp
-CPP_LIST+=ui/folder_selector.cpp
-CPP_LIST+=ui/batch_config.cpp
-CPP_LIST+=ui/batch_status_handler.cpp
-CPP_LIST+=ui/version_check.cpp
-CPP_LIST+=ui/file_view.cpp
-CPP_LIST+=ui/tree_grid.cpp
-CPP_LIST+=ui/gui_generated.cpp
-CPP_LIST+=ui/gui_status_handler.cpp
-CPP_LIST+=ui/main_dlg.cpp
-CPP_LIST+=ui/progress_indicator.cpp
-CPP_LIST+=ui/search.cpp
-CPP_LIST+=ui/small_dlgs.cpp
-CPP_LIST+=ui/sync_cfg.cpp
-CPP_LIST+=ui/taskbar.cpp
-CPP_LIST+=ui/triple_splitter.cpp
-CPP_LIST+=ui/tray_icon.cpp
-CPP_LIST+=lib/binary.cpp
-CPP_LIST+=lib/db_file.cpp
-CPP_LIST+=lib/dir_lock.cpp
-CPP_LIST+=lib/hard_filter.cpp
-CPP_LIST+=lib/icon_buffer.cpp
-CPP_LIST+=lib/icon_loader.cpp
-CPP_LIST+=lib/localization.cpp
-CPP_LIST+=lib/parallel_scan.cpp
-CPP_LIST+=lib/process_xml.cpp
-CPP_LIST+=lib/resolve_path.cpp
-CPP_LIST+=lib/perf_check.cpp
-CPP_LIST+=lib/status_handler.cpp
-CPP_LIST+=lib/versioning.cpp
-CPP_LIST+=lib/ffs_paths.cpp
-CPP_LIST+=../../zen/xml_io.cpp
-CPP_LIST+=../../zen/recycler.cpp
-CPP_LIST+=../../zen/file_access.cpp
-CPP_LIST+=../../zen/file_io.cpp
-CPP_LIST+=../../zen/file_traverser.cpp
-CPP_LIST+=../../zen/zstring.cpp
-CPP_LIST+=../../zen/format_unit.cpp
-CPP_LIST+=../../zen/process_priority.cpp
-CPP_LIST+=../../zen/shutdown.cpp
-CPP_LIST+=../../wx+/file_drop.cpp
-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
-CPP_LIST+=../../wx+/zlib_wrap.cpp
+CPP_FILES=
+CPP_FILES+=algorithm.cpp
+CPP_FILES+=application.cpp
+CPP_FILES+=comparison.cpp
+CPP_FILES+=structures.cpp
+CPP_FILES+=synchronization.cpp
+CPP_FILES+=fs/abstract.cpp
+CPP_FILES+=fs/concrete.cpp
+CPP_FILES+=fs/native.cpp
+CPP_FILES+=file_hierarchy.cpp
+CPP_FILES+=ui/batch_config.cpp
+CPP_FILES+=ui/batch_status_handler.cpp
+CPP_FILES+=ui/cfg_grid.cpp
+CPP_FILES+=ui/command_box.cpp
+CPP_FILES+=ui/folder_history_box.cpp
+CPP_FILES+=ui/folder_selector.cpp
+CPP_FILES+=ui/file_grid.cpp
+CPP_FILES+=ui/file_view.cpp
+CPP_FILES+=ui/tree_grid.cpp
+CPP_FILES+=ui/gui_generated.cpp
+CPP_FILES+=ui/gui_status_handler.cpp
+CPP_FILES+=ui/main_dlg.cpp
+CPP_FILES+=ui/progress_indicator.cpp
+CPP_FILES+=ui/search.cpp
+CPP_FILES+=ui/small_dlgs.cpp
+CPP_FILES+=ui/sync_cfg.cpp
+CPP_FILES+=ui/taskbar.cpp
+CPP_FILES+=ui/tray_icon.cpp
+CPP_FILES+=ui/triple_splitter.cpp
+CPP_FILES+=ui/version_check.cpp
+CPP_FILES+=lib/binary.cpp
+CPP_FILES+=lib/db_file.cpp
+CPP_FILES+=lib/dir_lock.cpp
+CPP_FILES+=lib/ffs_paths.cpp
+CPP_FILES+=lib/generate_logfile.cpp
+CPP_FILES+=lib/hard_filter.cpp
+CPP_FILES+=lib/icon_buffer.cpp
+CPP_FILES+=lib/icon_loader.cpp
+CPP_FILES+=lib/localization.cpp
+CPP_FILES+=lib/parallel_scan.cpp
+CPP_FILES+=lib/process_xml.cpp
+CPP_FILES+=lib/resolve_path.cpp
+CPP_FILES+=lib/perf_check.cpp
+CPP_FILES+=lib/status_handler.cpp
+CPP_FILES+=lib/versioning.cpp
+CPP_FILES+=../../zen/xml_io.cpp
+CPP_FILES+=../../zen/recycler.cpp
+CPP_FILES+=../../zen/file_access.cpp
+CPP_FILES+=../../zen/file_io.cpp
+CPP_FILES+=../../zen/file_traverser.cpp
+CPP_FILES+=../../zen/zstring.cpp
+CPP_FILES+=../../zen/format_unit.cpp
+CPP_FILES+=../../zen/process_priority.cpp
+CPP_FILES+=../../zen/shutdown.cpp
+CPP_FILES+=../../wx+/file_drop.cpp
+CPP_FILES+=../../wx+/grid.cpp
+CPP_FILES+=../../wx+/image_tools.cpp
+CPP_FILES+=../../wx+/graph.cpp
+CPP_FILES+=../../wx+/http.cpp
+CPP_FILES+=../../wx+/tooltip.cpp
+CPP_FILES+=../../wx+/image_resources.cpp
+CPP_FILES+=../../wx+/popup_dlg.cpp
+CPP_FILES+=../../wx+/popup_dlg_generated.cpp
+CPP_FILES+=../../wx+/zlib_wrap.cpp
+CPP_FILES+=../../xBRZ/src/xbrz.cpp
-OBJECT_LIST = $(CPP_LIST:%.cpp=../Obj/FFS_GCC_Make_Release/ffs/src/%.o)
+OBJECT_LIST = $(CPP_FILES:%.cpp=../Obj/FFS_GCC_Make_Release/ffs/src/%.o)
-all: launchpad
+all: ../Build/$(APPNAME)
-launchpad: FreeFileSync
+../Build/$(APPNAME): $(OBJECT_LIST)
+ g++ -o $@ $^ $(LINKFLAGS)
../Obj/FFS_GCC_Make_Release/ffs/src/%.o : %.cpp
mkdir -p $(dir $@)
g++ $(CXXFLAGS) -c $< -o $@
-FreeFileSync: $(OBJECT_LIST)
- g++ -o ../Build/$(APPNAME) $(OBJECT_LIST) $(LINKFLAGS)
-
clean:
rm -rf ../Obj/FFS_GCC_Make_Release
rm -f ../Build/$(APPNAME)
- rm -f ../../wx+/pch.h.gch
install:
mkdir -p $(BINDIR)
diff --git a/FreeFileSync/Source/RealTimeSync/Makefile b/FreeFileSync/Source/RealTimeSync/Makefile
index baf4ef62..e4915afa 100755
--- a/FreeFileSync/Source/RealTimeSync/Makefile
+++ b/FreeFileSync/Source/RealTimeSync/Makefile
@@ -2,58 +2,55 @@ APPNAME = RealTimeSync
prefix = /usr
BINDIR = $(DESTDIR)$(prefix)/bin
-CXXFLAGS = -std=c++14 -pipe -DWXINTL_NO_GETTEXT_MACRO -I../../.. -I../../../zenXml -include "zen/i18n.h" -Wall -O3 -DNDEBUG `wx-config --cxxflags --debug=no` -pthread
+CXXFLAGS = -std=c++17 -pipe -DWXINTL_NO_GETTEXT_MACRO -I../../.. -I../../../zenXml -I../../../boost -include "zen/i18n.h" -include "zen/warn_static.h" \
+-Wall -Wfatal-errors -Winit-self -Wmissing-include-dirs -Wswitch-enum -Wmain -Wnon-virtual-dtor -Wcast-align -Wshadow -Wno-deprecated-declarations \
+-O3 -DNDEBUG `wx-config --cxxflags --debug=no` -pthread
-LINKFLAGS = -s `wx-config --libs std, aui --debug=no` -lboost_thread -lboost_chrono -lboost_system -lz -pthread
+LINKFLAGS = -s -no-pie `wx-config --libs std, aui --debug=no` -pthread
#Gtk - support "no button border"
CXXFLAGS += `pkg-config --cflags gtk+-2.0`
LINKFLAGS += `pkg-config --libs gtk+-2.0`
-CPP_LIST=
-CPP_LIST+=application.cpp
-CPP_LIST+=gui_generated.cpp
-CPP_LIST+=main_dlg.cpp
-CPP_LIST+=tray_menu.cpp
-CPP_LIST+=monitor.cpp
-CPP_LIST+=xml_proc.cpp
-CPP_LIST+=folder_selector2.cpp
-CPP_LIST+=../structures.cpp
-CPP_LIST+=../lib/localization.cpp
-CPP_LIST+=../lib/process_xml.cpp
-CPP_LIST+=../lib/resolve_path.cpp
-CPP_LIST+=../lib/ffs_paths.cpp
-CPP_LIST+=../lib/hard_filter.cpp
-CPP_LIST+=../../../zen/xml_io.cpp
-CPP_LIST+=../../../zen/dir_watcher.cpp
-CPP_LIST+=../../../zen/file_access.cpp
-CPP_LIST+=../../../zen/file_io.cpp
-CPP_LIST+=../../../zen/file_traverser.cpp
-CPP_LIST+=../../../zen/zstring.cpp
-CPP_LIST+=../../../zen/format_unit.cpp
-CPP_LIST+=../../../wx+/file_drop.cpp
-CPP_LIST+=../../../wx+/image_tools.cpp
-CPP_LIST+=../../../wx+/image_resources.cpp
-CPP_LIST+=../../../wx+/popup_dlg.cpp
-CPP_LIST+=../../../wx+/popup_dlg_generated.cpp
-
-OBJECT_LIST=$(CPP_LIST:%.cpp=../../Obj/RTS_GCC_Make_Release/ffs/src/rts/%.o)
-
-all: launchpad
-
-launchpad: RealTimeSync
+CPP_FILES=
+CPP_FILES+=application.cpp
+CPP_FILES+=gui_generated.cpp
+CPP_FILES+=main_dlg.cpp
+CPP_FILES+=tray_menu.cpp
+CPP_FILES+=monitor.cpp
+CPP_FILES+=xml_proc.cpp
+CPP_FILES+=folder_selector2.cpp
+CPP_FILES+=../lib/localization.cpp
+CPP_FILES+=../lib/resolve_path.cpp
+CPP_FILES+=../lib/ffs_paths.cpp
+CPP_FILES+=../../../zen/xml_io.cpp
+CPP_FILES+=../../../zen/dir_watcher.cpp
+CPP_FILES+=../../../zen/file_access.cpp
+CPP_FILES+=../../../zen/file_io.cpp
+CPP_FILES+=../../../zen/file_traverser.cpp
+CPP_FILES+=../../../zen/zstring.cpp
+CPP_FILES+=../../../zen/format_unit.cpp
+CPP_FILES+=../../../wx+/file_drop.cpp
+CPP_FILES+=../../../wx+/image_tools.cpp
+CPP_FILES+=../../../wx+/image_resources.cpp
+CPP_FILES+=../../../wx+/popup_dlg.cpp
+CPP_FILES+=../../../wx+/popup_dlg_generated.cpp
+CPP_FILES+=../../../xBRZ/src/xbrz.cpp
+
+OBJECT_LIST=$(CPP_FILES:%.cpp=../../Obj/RTS_GCC_Make_Release/ffs/src/rts/%.o)
+
+all: ../../Build/$(APPNAME)
+
+../../Build/$(APPNAME): $(OBJECT_LIST)
+ g++ -o $@ $^ $(LINKFLAGS)
../../Obj/RTS_GCC_Make_Release/ffs/src/rts/%.o : %.cpp
mkdir -p $(dir $@)
g++ $(CXXFLAGS) -c $< -o $@
-RealTimeSync: $(OBJECT_LIST)
- g++ -o ../../Build/$(APPNAME) $(OBJECT_LIST) $(LINKFLAGS)
-
clean:
rm -rf ../../Obj/RTS_GCC_Make_Release
rm -f ../../Build/$(APPNAME)
- rm -f ../../../wx+/pch.h.gch
install:
mkdir -p $(BINDIR)
diff --git a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
index 3f009a1a..67fdde0a 100755
--- a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
+++ b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
@@ -33,6 +33,14 @@ namespace
static const size_t MAX_ADD_FOLDERS = 6;
+std::wstring extractJobName(const Zstring& cfgFilePath)
+{
+ const Zstring shortName = afterLast(cfgFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
+ const Zstring jobName = beforeLast(shortName, Zstr('.'), IF_MISSING_RETURN_ALL);
+ return utfTo<std::wstring>(jobName);
+}
+
+
}
@@ -207,7 +215,7 @@ void MainDialog::OnStart(wxCommandEvent& event)
XmlRealConfig currentCfg = getConfiguration();
const Zstring activeCfgFilePath = !equalFilePath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstring();
- switch (rts::startDirectoryMonitor(currentCfg, fff::extractJobName(activeCfgFilePath)))
+ switch (rts::startDirectoryMonitor(currentCfg, ::extractJobName(activeCfgFilePath)))
{
case rts::EXIT_APP:
Close();
diff --git a/FreeFileSync/Source/RealTimeSync/xml_proc.cpp b/FreeFileSync/Source/RealTimeSync/xml_proc.cpp
index 9d022b5d..f4762540 100755
--- a/FreeFileSync/Source/RealTimeSync/xml_proc.cpp
+++ b/FreeFileSync/Source/RealTimeSync/xml_proc.cpp
@@ -6,66 +6,95 @@
#include "xml_proc.h"
#include <zen/file_access.h>
-#include <wx/filefn.h>
-#include "../lib/process_xml.h"
+#include <wx/intl.h>
#include "../lib/ffs_paths.h"
+#include "../lib/localization.h"
using namespace zen;
using namespace rts;
-namespace
+namespace zen
{
-void readConfig(const XmlIn& in, XmlRealConfig& config)
+template <> inline
+bool readText(const std::string& input, wxLanguage& value)
{
- in["Directories"](config.directories);
- in["Delay" ](config.delay);
- in["Commandline"](config.commandline);
+ if (const wxLanguageInfo* lngInfo = wxLocale::FindLanguageInfo(utfTo<wxString>(input)))
+ {
+ value = static_cast<wxLanguage>(lngInfo->Language);
+ return true;
+ }
+ return false;
+}
}
-bool isXmlTypeRTS(const XmlDoc& doc) //throw()
+namespace
+{
+enum class RtsXmlType
+{
+ REAL,
+ BATCH,
+ GLOBAL,
+ OTHER
+};
+RtsXmlType getXmlTypeNoThrow(const XmlDoc& doc) //throw()
{
if (doc.root().getNameAs<std::string>() == "FreeFileSync")
{
std::string type;
if (doc.root().getAttribute("XmlType", type))
- return type == "REAL";
+ {
+ if (type == "REAL")
+ return RtsXmlType::REAL;
+ else if (type == "BATCH")
+ return RtsXmlType::BATCH;
+ else if (type == "GLOBAL")
+ return RtsXmlType::GLOBAL;
+ }
}
- return false;
+ return RtsXmlType::OTHER;
}
+
+
+void readConfig(const XmlIn& in, XmlRealConfig& config)
+{
+ in["Directories"](config.directories);
+ in["Delay" ](config.delay);
+ in["Commandline"](config.commandline);
+}
+
+void writeConfig(const XmlRealConfig& config, XmlOut& out)
+{
+ out["Directories"](config.directories);
+ out["Delay" ](config.delay);
+ out["Commandline"](config.commandline);
}
-void rts::readConfig(const Zstring& filepath, XmlRealConfig& config, std::wstring& warningMsg) //throw FileError
+template <class ConfigType>
+void readConfig(const Zstring& filePath, RtsXmlType type, ConfigType& cfg, std::wstring& warningMsg) //throw FileError
{
- XmlDoc doc = loadXmlDocument(filepath); //throw FileError
+ XmlDoc doc = loadXmlDocument(filePath); //throw FileError
- if (!isXmlTypeRTS(doc))
- throw FileError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtPath(filepath)));
+ if (getXmlTypeNoThrow(doc) != type) //noexcept
+ throw FileError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtPath(filePath)));
XmlIn in(doc);
- ::readConfig(in, config);
+ ::readConfig(in, cfg);
try
{
- checkForMappingErrors(in, filepath); //throw FileError
- }
- catch (const FileError& e)
- {
- warningMsg = e.toString();
+ checkForMappingErrors(in, filePath); //throw FileError
}
+ catch (const FileError& e) { warningMsg = e.toString(); }
+}
}
-namespace
+void rts::readConfig(const Zstring& filePath, XmlRealConfig& config, std::wstring& warningMsg) //throw FileError
{
-void writeConfig(const XmlRealConfig& config, XmlOut& out)
-{
- out["Directories"](config.directories);
- out["Delay" ](config.delay);
- out["Commandline"](config.commandline);
-}
+ ::readConfig(filePath, RtsXmlType::REAL, config, warningMsg); //throw FileError
}
@@ -81,56 +110,70 @@ void rts::writeConfig(const XmlRealConfig& config, const Zstring& filepath) //th
}
-namespace
-{
-XmlRealConfig convertBatchToReal(const fff::XmlBatchConfig& batchCfg, const Zstring& batchFilePath)
+void rts::readRealOrBatchConfig(const Zstring& filePath, XmlRealConfig& config, std::wstring& warningMsg) //throw FileError
{
- std::set<Zstring, LessFilePath> uniqueFolders;
+ //do NOT use zen::loadStream as it will needlessly load even huge files!
+ XmlDoc doc = loadXmlDocument(filePath); //throw FileError; quick exit if file is not an FFS XML
- //add main folders
- uniqueFolders.insert(batchCfg.mainCfg.firstPair.folderPathPhraseLeft);
- uniqueFolders.insert(batchCfg.mainCfg.firstPair.folderPathPhraseRight);
+ const RtsXmlType xmlType = ::getXmlTypeNoThrow(doc);
- //additional folders
- for (const fff::LocalPairConfig& lpc : batchCfg.mainCfg.additionalPairs)
+ //convert batch config to RealTimeSync config
+ if (xmlType == RtsXmlType::BATCH)
{
- uniqueFolders.insert(lpc.folderPathPhraseLeft);
- uniqueFolders.insert(lpc.folderPathPhraseRight);
- }
+ XmlIn in(doc);
- erase_if(uniqueFolders, [](const Zstring& str) { return trimCpy(str).empty(); });
+ //read folder pairs
+ std::set<Zstring, LessFilePath> uniqueFolders;
- XmlRealConfig output;
- output.directories.assign(uniqueFolders.begin(), uniqueFolders.end());
- output.commandline = Zstr("\"") + fff::getFreeFileSyncLauncherPath() + Zstr("\" \"") + batchFilePath + Zstr("\"");
- return output;
-}
-}
+ for (XmlIn inPair = in["FolderPairs"]["Pair"]; inPair; inPair.next())
+ {
+ Zstring folderPathPhraseLeft;
+ Zstring folderPathPhraseRight;
+ inPair["Left" ](folderPathPhraseLeft);
+ inPair["Right"](folderPathPhraseRight);
+ uniqueFolders.insert(folderPathPhraseLeft);
+ uniqueFolders.insert(folderPathPhraseRight);
+ }
-void rts::readRealOrBatchConfig(const Zstring& filepath, XmlRealConfig& config, std::wstring& warningMsg) //throw FileError
-{
- if (fff::getXmlType(filepath) != fff::XML_TYPE_BATCH) //throw FileError
- return readConfig(filepath, config, warningMsg); //throw FileError
+ //don't consider failure a warning only:
+ checkForMappingErrors(in, filePath); //throw FileError
- //convert batch config to RealTimeSync config
- fff::XmlBatchConfig batchCfg;
- readConfig(filepath, batchCfg, warningMsg); //throw FileError
- //<- redirect batch config warnings
+ //---------------------------------------------------------------------------------------
- config = convertBatchToReal(batchCfg, filepath);
+ erase_if(uniqueFolders, [](const Zstring& str) { return trimCpy(str).empty(); });
+ config.directories.assign(uniqueFolders.begin(), uniqueFolders.end());
+ config.commandline = Zstr("\"") + fff::getFreeFileSyncLauncherPath() + Zstr("\" \"") + filePath + Zstr("\"");
+ }
+ else
+ return readConfig(filePath, config, warningMsg); //throw FileError
}
-wxLanguage rts::getProgramLanguage()
+wxLanguage rts::getProgramLanguage() //throw FileError
{
- fff::XmlGlobalSettings settings;
- std::wstring warningMsg;
+ const Zstring& filePath = fff::getConfigDirPathPf() + Zstr("GlobalSettings.xml");
+
+ XmlDoc doc;
try
{
- fff::readConfig(fff::getGlobalConfigFile(), settings, warningMsg); //throw FileError
+ doc = loadXmlDocument(filePath); //throw FileError
}
- catch (const FileError&) {} //use default language if error occurred
+ catch (FileError&)
+ {
+ if (!itemNotExisting(filePath)) //existing or access error
+ throw;
+ return fff::getSystemLanguage();
+ }
+
+ if (getXmlTypeNoThrow(doc) != RtsXmlType::GLOBAL) //noexcept
+ throw FileError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtPath(filePath)));
+
+ XmlIn in(doc);
+
+ wxLanguage lng = wxLANGUAGE_UNKNOWN;
+ in["General"]["Language"].attribute("Name", lng);
- return settings.programLanguage;
+ checkForMappingErrors(in, filePath); //throw FileError
+ return lng;
}
diff --git a/FreeFileSync/Source/RealTimeSync/xml_proc.h b/FreeFileSync/Source/RealTimeSync/xml_proc.h
index afbd010c..305ca76d 100755
--- a/FreeFileSync/Source/RealTimeSync/xml_proc.h
+++ b/FreeFileSync/Source/RealTimeSync/xml_proc.h
@@ -21,14 +21,14 @@ struct XmlRealConfig
unsigned int delay = 10;
};
-void readConfig(const Zstring& filepath, XmlRealConfig& config, std::wstring& warningMsg); //throw FileError
+void readConfig(const Zstring& filePath, XmlRealConfig& config, std::wstring& warningMsg); //throw FileError
void writeConfig(const XmlRealConfig& config, const Zstring& filepath); //throw FileError
//reuse (some of) FreeFileSync's xml files
-void readRealOrBatchConfig(const Zstring& filepath, XmlRealConfig& config, std::wstring& warningMsg); //throw FileError
+void readRealOrBatchConfig(const Zstring& filePath, XmlRealConfig& config, std::wstring& warningMsg); //throw FileError
-wxLanguage getProgramLanguage();
+wxLanguage getProgramLanguage(); //throw FileError
}
#endif //XML_PROC_H_0813748158321813490
diff --git a/FreeFileSync/Source/algorithm.cpp b/FreeFileSync/Source/algorithm.cpp
index faab26af..a7d21ccf 100755
--- a/FreeFileSync/Source/algorithm.cpp
+++ b/FreeFileSync/Source/algorithm.cpp
@@ -1130,7 +1130,7 @@ Opt<PathDependency> fff::getPathDependency(const AbstractPath& basePathL, const
{
const AFS::PathComponents compL = AFS::getPathComponents(basePathL);
const AFS::PathComponents compR = AFS::getPathComponents(basePathR);
- if (AFS::compareAbstractPath(compL.rootPath, compR.rootPath) == 0)
+ if (compL.rootPath == compR.rootPath)
{
const bool leftParent = compL.relPath.size() <= compR.relPath.size();
@@ -1205,7 +1205,7 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT
const std::wstring txtCreatingFolder(_("Creating folder %x" ));
const std::wstring txtCreatingLink (_("Creating symbolic link %x"));
- auto copyItem = [overwriteIfExists](const AbstractPath& targetPath, //throw FileError
+ auto copyItem = [overwriteIfExists](const AbstractPath& targetPath, ItemStatReporter& statReporter, //throw FileError
const std::function<void(const std::function<void()>& deleteTargetItem)>& copyItemPlain) //throw FileError
{
//start deleting existing target as required by copyFileTransactional():
@@ -1225,27 +1225,28 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT
}
catch (FileError&)
{
- Opt<AFS::PathStatus> pd;
- try { pd = AFS::getPathStatus(targetPath); /*throw FileError*/ }
- catch (FileError&) {} //previous exception is more relevant
+ const AFS::PathStatus ps = AFS::getPathStatus(targetPath); //throw FileError
- if (pd)
+ if (ps.relPath.empty()) //already existing
{
- if (pd->relPath.empty()) //already existing
+ if (deletionError)
+ throw* deletionError;
+ }
+ else if (ps.relPath.size() > 1) //parent folder missing
+ {
+ AbstractPath intermediatePath = ps.existingPath;
+ for (const Zstring& itemName : std::vector<Zstring>(ps.relPath.begin(), ps.relPath.end() - 1))
{
- if (deletionError)
- throw* deletionError;
+ AFS::createFolderPlain(intermediatePath = AFS::appendRelPath(intermediatePath, itemName)); //throw FileError
+ statReporter.reportDelta(1, 0);
}
- else if (pd->relPath.size() > 1) //parent folder missing
- {
- AbstractPath intermediatePath = pd->existingPath;
- for (const Zstring& itemName : std::vector<Zstring>(pd->relPath.begin(), pd->relPath.end() - 1))
- AFS::createFolderPlain(intermediatePath = AFS::appendRelPath(intermediatePath, itemName)); //throw FileError
+ //potential future issue when adding multithreading support: intermediate folders might already exist
+ //potential future issue 2: folder created by parallel thread just after failure => ps->relPath.size() == 1, but need retry!
+ //see abstract.cpp; AFS::createFolderIfMissingRecursion()
- //retry:
- copyItemPlain(nullptr /*deleteTargetItem*/); //throw FileError
- return;
- }
+ //retry:
+ copyItemPlain(nullptr /*deleteTargetItem*/); //throw FileError
+ return;
}
throw;
}
@@ -1260,7 +1261,7 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT
visitFSObject(*fsObj, [&](const FolderPair& folder)
{
- StatisticsReporter statReporter(1, 0, callback);
+ ItemStatReporter statReporter(1, 0, callback);
notifyItemCopy(txtCreatingFolder, AFS::getDisplayPath(targetPath));
try
{
@@ -1270,40 +1271,36 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT
}
catch (FileError&)
{
- Opt<AFS::PathStatus> pd;
- try { pd = AFS::getPathStatus(targetPath); /*throw FileError*/ }
- catch (FileError&) {} //previous exception is more relevant
+ const AFS::PathStatus ps = AFS::getPathStatus(targetPath); //throw FileError
+ if (ps.existingType == AFS::ItemType::FILE)
+ throw;
- if (pd)
- {
- if (pd->relPath.empty()) //already existing
- {
- if (pd->existingType != AFS::ItemType::FILE)
- return; //folder might already exist: see creation of intermediate directories below
- }
- else if (pd->relPath.size() > 1) //parent folder missing
- {
- AbstractPath intermediatePath = pd->existingPath;
- for (const Zstring& itemName : pd->relPath)
- AFS::createFolderPlain(intermediatePath = AFS::appendRelPath(intermediatePath, itemName)); //throw FileError
+ if (ps.relPath.size() == 1) //don't repeat the very same createFolderPlain() call from above!
+ throw;
- statReporter.reportDelta(1, 0);
- return;
- }
+ //folder might already exist: see creation of intermediate directories below
+
+ AbstractPath intermediatePath = ps.existingPath;
+ for (const Zstring& itemName : ps.relPath)
+ {
+ AFS::createFolderPlain(intermediatePath = AFS::appendRelPath(intermediatePath, itemName)); //throw FileError
+ statReporter.reportDelta(1, 0);
}
- throw;
+ //potential future issue when adding multithreading support: intermediate folders might already exist
+ //potential future issue 2: parent folder created by parallel thread just after failure => ps->relPath.size() == 1, but need retry!
+ //see abstract.cpp; AFS::createFolderIfMissingRecursion()
}
},
[&](const FilePair& file)
{
- StatisticsReporter statReporter(1, file.getFileSize<side>(), callback);
+ ItemStatReporter statReporter(1, file.getFileSize<side>(), callback);
notifyItemCopy(txtCreatingFile, AFS::getDisplayPath(targetPath));
const FileAttributes attr = file.getAttributes<side>();
const AFS::StreamAttributes sourceAttr{ attr.modTime, attr.fileSize, attr.fileId };
- copyItem(targetPath, [&](const std::function<void()>& deleteTargetItem) //throw FileError
+ copyItem(targetPath, statReporter, [&](const std::function<void()>& deleteTargetItem) //throw FileError
{
auto notifyUnbufferedIO = [&](int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); };
/*const AFS::FileCopyResult result =*/ AFS::copyFileTransactional(sourcePath, sourceAttr, targetPath, //throw FileError, ErrorFileLocked
@@ -1315,17 +1312,17 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT
[&](const SymlinkPair& symlink)
{
- StatisticsReporter statReporter(1, 0, callback);
+ ItemStatReporter statReporter(1, 0, callback);
notifyItemCopy(txtCreatingLink, AFS::getDisplayPath(targetPath));
- copyItem(targetPath, [&](const std::function<void()>& deleteTargetItem) //throw FileError
+ copyItem(targetPath, statReporter, [&](const std::function<void()>& deleteTargetItem) //throw FileError
{
deleteTargetItem(); //throw FileError
AFS::copySymlink(sourcePath, targetPath, false /*copyFilePermissions*/); //throw FileError
});
statReporter.reportDelta(1, 0);
});
- }, callback); //throw X?
+ }, callback); //throw X
}
}
@@ -1399,7 +1396,7 @@ void deleteFromGridAndHDOneSide(std::vector<FileSystemObject*>& rowsToDelete,
for (FileSystemObject* fsObj : rowsToDelete) //all pointers are required(!) to be bound
tryReportingError([&]
{
- StatisticsReporter statReporter(1, 0, callback);
+ ItemStatReporter statReporter(1, 0, callback);
if (!fsObj->isEmpty<side>()) //element may be implicitly deleted, e.g. if parent folder was deleted first
{
@@ -1453,7 +1450,7 @@ void deleteFromGridAndHDOneSide(std::vector<FileSystemObject*>& rowsToDelete,
fsObj->removeObject<side>(); //if directory: removes recursively!
}
- }, callback); //throw X?
+ }, callback); //throw X
}
@@ -1462,7 +1459,7 @@ void categorize(const std::vector<FileSystemObject*>& rows,
std::vector<FileSystemObject*>& deletePermanent,
std::vector<FileSystemObject*>& deleteRecyler,
bool useRecycleBin,
- std::map<AbstractPath, bool, AFS::LessAbstractPath>& recyclerSupported,
+ std::map<AbstractPath, bool>& recyclerSupported,
ProcessCallback& callback)
{
auto hasRecycler = [&](const AbstractPath& baseFolderPath) -> bool
@@ -1476,7 +1473,7 @@ void categorize(const std::vector<FileSystemObject*>& rows,
bool recSupported = false;
tryReportingError([&]{
recSupported = AFS::supportsRecycleBin(baseFolderPath, [&] { callback.reportStatus(msg); /*may throw*/ }); //throw FileError
- }, callback); //throw X?
+ }, callback); //throw X
recyclerSupported.emplace(baseFolderPath, recSupported);
return recSupported;
@@ -1568,7 +1565,7 @@ void fff::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete
std::vector<FileSystemObject*> deleteRecylerLeft;
std::vector<FileSystemObject*> deleteRecylerRight;
- std::map<AbstractPath, bool, AFS::LessAbstractPath> recyclerSupported;
+ std::map<AbstractPath, bool> recyclerSupported;
categorize< LEFT_SIDE>(deleteLeft, deletePermanentLeft, deleteRecylerLeft, useRecycleBin, recyclerSupported, callback);
categorize<RIGHT_SIDE>(deleteRight, deletePermanentRight, deleteRecylerRight, useRecycleBin, recyclerSupported, callback);
@@ -1608,7 +1605,7 @@ bool fff::operator<(const FileDescriptor& lhs, const FileDescriptor& rhs)
if (lhs.attr.isFollowedSymlink != rhs.attr.isFollowedSymlink)
return lhs.attr.isFollowedSymlink < rhs.attr.isFollowedSymlink;
- return AFS::LessAbstractPath()(lhs.path, rhs.path);
+ return lhs.path < rhs.path;
}
@@ -1659,7 +1656,7 @@ void TempFileBuffer::createTempFiles(const std::set<FileDescriptor>& workLoad, P
createDirectoryIfMissingRecursion(tempPathTmp); //throw FileError
tempFolderPath_ = tempPathTmp;
- }, callback); //throw X?
+ }, callback); //throw X
if (errMsg) return;
}
@@ -1687,7 +1684,7 @@ void TempFileBuffer::createTempFiles(const std::set<FileDescriptor>& workLoad, P
tryReportingError([&]
{
- StatisticsReporter statReporter(1, descr.attr.fileSize, callback);
+ ItemStatReporter statReporter(1, descr.attr.fileSize, callback);
callback.reportInfo(replaceCpy(_("Creating file %x"), L"%x", fmtPath(tempFilePath)));
@@ -1701,6 +1698,6 @@ void TempFileBuffer::createTempFiles(const std::set<FileDescriptor>& workLoad, P
statReporter.reportDelta(1, 0);
tempFilePaths_[descr] = tempFilePath;
- }, callback); //throw X?
+ }, callback); //throw X
}
}
diff --git a/FreeFileSync/Source/algorithm.h b/FreeFileSync/Source/algorithm.h
index 719d0f9b..f3a81e12 100755
--- a/FreeFileSync/Source/algorithm.h
+++ b/FreeFileSync/Source/algorithm.h
@@ -46,7 +46,7 @@ struct PathDependency
{
AbstractPath basePathParent;
AbstractPath basePathChild;
- Zstring relPath; //filled if child path is sub folder of parent path; empty if child path == parent path
+ Zstring relPath; //filled if child path is subfolder of parent path; empty if child path == parent path
};
zen::Opt<PathDependency> getPathDependency(const AbstractPath& basePathL, const HardFilter& filterL,
const AbstractPath& basePathR, const HardFilter& filterR);
diff --git a/FreeFileSync/Source/application.cpp b/FreeFileSync/Source/application.cpp
index 5a6e717a..f4519639 100755
--- a/FreeFileSync/Source/application.cpp
+++ b/FreeFileSync/Source/application.cpp
@@ -326,9 +326,9 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
auto hasNonDefaultConfig = [](const LocalPairConfig& lpc)
{
- return lpc != LocalPairConfig(lpc.folderPathPhraseLeft,
- lpc.folderPathPhraseRight,
- NoValue(), NoValue(), FilterConfig());
+ return lpc != LocalPairConfig{ lpc.folderPathPhraseLeft,
+ lpc.folderPathPhraseRight,
+ NoValue(), NoValue(), FilterConfig() };
};
auto replaceDirectories = [&](MainConfiguration& mainCfg)
@@ -350,8 +350,8 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
mainCfg.firstPair.folderPathPhraseRight = dirPathPhrasePairs[0].second;
}
else
- mainCfg.additionalPairs.emplace_back(dirPathPhrasePairs[i].first, dirPathPhrasePairs[i].second,
- NoValue(), NoValue(), FilterConfig());
+ mainCfg.additionalPairs.push_back({ dirPathPhrasePairs[i].first, dirPathPhrasePairs[i].second,
+ NoValue(), NoValue(), FilterConfig() });
}
return true;
};
@@ -567,11 +567,13 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat
logNonDefaultSettings(globalCfg, statusHandler); //inform about (important) non-default global settings
- const std::vector<FolderPairCfg> cmpConfig = extractCompareCfg(batchCfg.mainCfg);
+ const std::vector<FolderPairCfg> fpCfgList = extractCompareCfg(batchCfg.mainCfg);
//batch mode: place directory locks on directories during both comparison AND synchronization
std::unique_ptr<LockHolder> dirLocks;
+ const std::map<AbstractPath, size_t>& deviceParallelOps = batchCfg.mainCfg.deviceParallelOps;
+
//COMPARE DIRECTORIES
FolderComparison cmpResult = compare(globalCfg.warnDlgs,
globalCfg.fileTimeTolerance,
@@ -580,8 +582,9 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat
globalCfg.folderAccessTimeout,
globalCfg.createLockFile,
dirLocks,
- cmpConfig,
- statusHandler); //throw ?
+ fpCfgList,
+ deviceParallelOps,
+ statusHandler); //throw X
//START SYNCHRONIZATION
const std::vector<FolderPairSyncCfg> syncProcessCfg = extractSyncCfg(batchCfg.mainCfg);
@@ -597,8 +600,9 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat
globalCfg.folderAccessTimeout,
syncProcessCfg,
cmpResult,
+ deviceParallelOps,
globalCfg.warnDlgs,
- statusHandler); //throw ?
+ statusHandler); //throw X
//not cancelled? => update last sync date for the selected cfg file
for (ConfigFileItem& cfi : globalCfg.gui.mainDlg.cfgFileHistory)
diff --git a/FreeFileSync/Source/comparison.cpp b/FreeFileSync/Source/comparison.cpp
index 61001120..b0e7bcec 100755
--- a/FreeFileSync/Source/comparison.cpp
+++ b/FreeFileSync/Source/comparison.cpp
@@ -22,22 +22,26 @@ using namespace fff;
std::vector<FolderPairCfg> fff::extractCompareCfg(const MainConfiguration& mainCfg)
{
//merge first and additional pairs
- std::vector<LocalPairConfig> allPairs = { mainCfg.firstPair };
- append(allPairs, mainCfg.additionalPairs);
+ std::vector<LocalPairConfig> localCfgs = { mainCfg.firstPair };
+ append(localCfgs, mainCfg.additionalPairs);
std::vector<FolderPairCfg> output;
- std::transform(allPairs.begin(), allPairs.end(), std::back_inserter(output),
- [&](const LocalPairConfig& lpc) -> FolderPairCfg
- {
- return FolderPairCfg(lpc.folderPathPhraseLeft, lpc.folderPathPhraseRight,
- lpc.localCmpCfg ? lpc.localCmpCfg->compareVar : mainCfg.cmpConfig.compareVar,
- lpc.localCmpCfg ? lpc.localCmpCfg->handleSymlinks : mainCfg.cmpConfig.handleSymlinks,
- lpc.localCmpCfg ? lpc.localCmpCfg->ignoreTimeShiftMinutes : mainCfg.cmpConfig.ignoreTimeShiftMinutes,
- normalizeFilters(mainCfg.globalFilter, lpc.localFilter),
+ for (const LocalPairConfig& lpc : localCfgs)
+ {
+ const CompConfig cmpCfg = lpc.localCmpCfg ? *lpc.localCmpCfg : mainCfg.cmpCfg;
+ const SyncConfig syncCfg = lpc.localSyncCfg ? *lpc.localSyncCfg : mainCfg.syncCfg;
- lpc.localSyncCfg ? lpc.localSyncCfg->directionCfg : mainCfg.syncCfg.directionCfg);
- });
+ output.push_back(
+ {
+ lpc.folderPathPhraseLeft, lpc.folderPathPhraseRight,
+ cmpCfg.compareVar,
+ cmpCfg.handleSymlinks,
+ cmpCfg.ignoreTimeShiftMinutes,
+ normalizeFilters(mainCfg.globalFilter, lpc.localFilter),
+ syncCfg.directionCfg
+ });
+ }
return output;
}
@@ -54,11 +58,11 @@ struct ResolvedFolderPair
struct ResolvedBaseFolders
{
std::vector<ResolvedFolderPair> resolvedPairs;
- std::set<AbstractPath, AFS::LessAbstractPath> existingBaseFolders;
+ std::set<AbstractPath> existingBaseFolders;
};
-ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& cfgList,
+ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& fpCfgList, const std::map<AbstractPath, size_t>& deviceParallelOps,
int folderAccessTimeout,
bool allowUserInteraction,
ProcessCallback& callback)
@@ -67,11 +71,11 @@ ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& cfgL
tryReportingError([&]
{
- std::set<AbstractPath, AFS::LessAbstractPath> uniqueBaseFolders;
+ std::set<AbstractPath> uniqueBaseFolders;
//support "retry" for environment variable and and variable driver letter resolution!
output.resolvedPairs.clear();
- for (const FolderPairCfg& fpCfg : cfgList)
+ for (const FolderPairCfg& fpCfg : fpCfgList)
{
AbstractPath folderPathLeft = createAbstractPath(fpCfg.folderPathPhraseLeft_);
AbstractPath folderPathRight = createAbstractPath(fpCfg.folderPathPhraseRight_);
@@ -82,7 +86,8 @@ ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& cfgL
output.resolvedPairs.push_back({ folderPathLeft, folderPathRight });
}
- const FolderStatus status = getFolderStatusNonBlocking(uniqueBaseFolders, folderAccessTimeout, allowUserInteraction, callback); //re-check *all* directories on each try!
+ const FolderStatus status = getFolderStatusNonBlocking(uniqueBaseFolders, deviceParallelOps,
+ folderAccessTimeout, allowUserInteraction, callback); //re-check *all* directories on each try!
output.existingBaseFolders = status.existing;
if (!status.notExisting.empty() || !status.failedChecks.empty())
@@ -107,7 +112,7 @@ ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& cfgL
throw FileError(msg);
}
- }, callback); //throw X?
+ }, callback); //throw X
return output;
}
@@ -117,7 +122,10 @@ ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& cfgL
class ComparisonBuffer
{
public:
- ComparisonBuffer(const std::set<DirectoryKey>& keysToRead, int fileTimeTolerance, ProcessCallback& callback);
+ ComparisonBuffer(const std::set<DirectoryKey>& foldersToRead,
+ const std::map<AbstractPath, size_t>& deviceParallelOps,
+ 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;
@@ -139,7 +147,10 @@ private:
};
-ComparisonBuffer::ComparisonBuffer(const std::set<DirectoryKey>& keysToRead, int fileTimeTolerance, ProcessCallback& callback) :
+ComparisonBuffer::ComparisonBuffer(const std::set<DirectoryKey>& foldersToRead,
+ const std::map<AbstractPath, size_t>& deviceParallelOps,
+ int fileTimeTolerance,
+ ProcessCallback& callback) :
fileTimeTolerance_(fileTimeTolerance), callback_(callback)
{
class CbImpl : public FillBufferCallback
@@ -149,7 +160,7 @@ ComparisonBuffer::ComparisonBuffer(const std::set<DirectoryKey>& keysToRead, int
void reportStatus(const std::wstring& statusMsg, int itemsTotal) override
{
- callback_.updateProcessedData(itemsTotal - itemsReported_, 0); //processed bytes are reported in subfunctions!
+ callback_.updateDataProcessed(itemsTotal - itemsReported_, 0); //processed bytes are reported in subfunctions!
itemsReported_ = itemsTotal;
callback_.reportStatus(statusMsg); //may throw
@@ -177,8 +188,9 @@ ComparisonBuffer::ComparisonBuffer(const std::set<DirectoryKey>& keysToRead, int
int itemsReported_ = 0;
} cb(callback);
- fillBuffer(keysToRead, //in
+ fillBuffer(foldersToRead, //in
directoryBuffer_, //out
+ deviceParallelOps,
cb,
UI_UPDATE_INTERVAL / 2); //every ~50 ms
@@ -338,7 +350,7 @@ void categorizeSymlinkByContent(SymlinkPair& symlink, ProcessCallback& callback)
callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtPath(AFS::getDisplayPath(symlink.getAbstractPath<RIGHT_SIDE>()))));
binaryContentR = AFS::getSymlinkBinaryContent(symlink.getAbstractPath<RIGHT_SIDE>()); //throw FileError
- }, callback); //throw X?
+ }, callback); //throw X
if (errMsg)
symlink.setCategoryConflict(*errMsg);
@@ -400,6 +412,7 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::compareBySize(const ResolvedFo
std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(const std::vector<std::pair<ResolvedFolderPair, FolderPairCfg>>& workLoad) const
{
+ warn_static("perf: make parallel")
std::list<std::shared_ptr<BaseFolderPair>> output;
if (workLoad.empty())
return output;
@@ -460,14 +473,14 @@ std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(co
bool haveSameContent = false;
Opt<std::wstring> errMsg = tryReportingError([&]
{
- StatisticsReporter statReporter(1, file->getFileSize<LEFT_SIDE>(), callback_);
+ ItemStatReporter statReporter(1, file->getFileSize<LEFT_SIDE>(), callback_);
auto notifyUnbufferedIO = [&](int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); };
haveSameContent = filesHaveSameContent(file->getAbstractPath<LEFT_SIDE>(),
file->getAbstractPath<RIGHT_SIDE>(), notifyUnbufferedIO); //throw FileError
statReporter.reportDelta(1, 0);
- }, callback_); //throw X?
+ }, callback_); //throw X
if (errMsg)
file->setCategoryConflict(*errMsg);
@@ -481,7 +494,7 @@ std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(co
//3. harmonize with "bool stillInSync()" in algorithm.cpp, FilePair::setSyncedTo() in file_hierarchy.h
if (file->getItemName<LEFT_SIDE>() != file->getItemName<RIGHT_SIDE>())
file->setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(*file));
-#if 0 //don't synchronize modtime only see SynchronizeFolderPair::synchronizeFileInt(), SO_COPY_METADATA_TO_*
+#if 0 //don't synchronize modtime only see FolderPairSyncer::synchronizeFileInt(), SO_COPY_METADATA_TO_*
else if (!sameFileTime(file->getLastWriteTime<LEFT_SIDE>(),
file->getLastWriteTime<RIGHT_SIDE>(), file->base().getFileTimeTolerance(), file->base().getIgnoredTimeShift()))
file->setCategoryDiffMetadata(getDescrDiffMetaDate(*file));
@@ -820,7 +833,8 @@ FolderComparison fff::compare(WarningDialogs& warnings,
int folderAccessTimeout,
bool createDirLocks,
std::unique_ptr<LockHolder>& dirLocks,
- const std::vector<FolderPairCfg>& cfgList,
+ const std::vector<FolderPairCfg>& fpCfgList,
+ const std::map<AbstractPath, size_t>& deviceParallelOps,
ProcessCallback& callback)
{
//PERF_START;
@@ -855,17 +869,17 @@ FolderComparison fff::compare(WarningDialogs& warnings,
callback.reportInfo(e.toString()); //may throw!
}
- const ResolvedBaseFolders& resInfo = initializeBaseFolders(cfgList, folderAccessTimeout, allowUserInteraction, callback);
+ const ResolvedBaseFolders& resInfo = initializeBaseFolders(fpCfgList, deviceParallelOps, folderAccessTimeout, allowUserInteraction, callback);
//directory existence only checked *once* to avoid race conditions!
- if (resInfo.resolvedPairs.size() != cfgList.size())
+ if (resInfo.resolvedPairs.size() != fpCfgList.size())
throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
auto basefolderExisting = [&](const AbstractPath& folderPath) { return resInfo.existingBaseFolders.find(folderPath) != resInfo.existingBaseFolders.end(); };
std::vector<std::pair<ResolvedFolderPair, FolderPairCfg>> workLoad;
- for (size_t i = 0; i < cfgList.size(); ++i)
- workLoad.emplace_back(resInfo.resolvedPairs[i], cfgList[i]);
+ for (size_t i = 0; i < fpCfgList.size(); ++i)
+ workLoad.emplace_back(resInfo.resolvedPairs[i], fpCfgList[i]);
//-----------execute basic checks all at once before starting comparison----------
@@ -922,14 +936,14 @@ FolderComparison fff::compare(WarningDialogs& warnings,
try
{
//------------------- fill directory buffer ---------------------------------------------------
- std::set<DirectoryKey> dirsToRead;
+ std::set<DirectoryKey> foldersToRead;
for (const auto& w : workLoad)
{
if (basefolderExisting(w.first.folderPathLeft)) //only traverse *currently existing* folders: at this point user is aware that non-ex + empty string are seen as empty folder!
- dirsToRead.insert({ w.first.folderPathLeft, w.second.filter.nameFilter, w.second.handleSymlinks });
+ foldersToRead.emplace(DirectoryKey({ w.first.folderPathLeft, w.second.filter.nameFilter, w.second.handleSymlinks }));
if (basefolderExisting(w.first.folderPathRight))
- dirsToRead.insert({ w.first.folderPathRight, w.second.filter.nameFilter, w.second.handleSymlinks });
+ foldersToRead.emplace(DirectoryKey({ w.first.folderPathRight, w.second.filter.nameFilter, w.second.handleSymlinks }));
}
FolderComparison output;
@@ -938,7 +952,7 @@ FolderComparison fff::compare(WarningDialogs& warnings,
{
//------------ traverse/read folders -----------------------------------------------------
//PERF_START;
- ComparisonBuffer cmpBuff(dirsToRead, fileTimeTolerance, callback);
+ ComparisonBuffer cmpBuff(foldersToRead, deviceParallelOps, fileTimeTolerance, callback);
//PERF_STOP;
//process binary comparison as one junk
@@ -969,13 +983,13 @@ FolderComparison fff::compare(WarningDialogs& warnings,
break;
}
}
- assert(output.size() == cfgList.size());
+ assert(output.size() == fpCfgList.size());
//--------- set initial sync-direction --------------------------------------------------
for (auto it = begin(output); it != end(output); ++it)
{
- const FolderPairCfg& fpCfg = cfgList[it - output.begin()];
+ const FolderPairCfg& fpCfg = fpCfgList[it - output.begin()];
callback.reportStatus(_("Calculating sync directions..."));
callback.forceUiRefresh(); //throw X
@@ -985,7 +999,7 @@ FolderComparison fff::compare(WarningDialogs& warnings,
redetermineSyncDirection(fpCfg.directionCfg, *it, //throw FileError
[&](const std::wstring& msg) { callback.reportStatus(msg); }); //throw X
- }, callback); //throw X?
+ }, callback); //throw X
}
return output;
@@ -993,7 +1007,7 @@ FolderComparison fff::compare(WarningDialogs& warnings,
catch (const std::bad_alloc& e)
{
callback.reportFatalError(_("Out of memory.") + L" " + utfTo<std::wstring>(e.what()));
- //we need to maintain the "output.size() == cfgList.size()" contract in ALL cases! => abort
+ //we need to maintain the "output.size() == fpCfgList.size()" contract in ALL cases! => abort
callback.abortProcessNow(); //throw X
throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
}
diff --git a/FreeFileSync/Source/comparison.h b/FreeFileSync/Source/comparison.h
index 5bc5cd9d..4e9e7b5a 100755
--- a/FreeFileSync/Source/comparison.h
+++ b/FreeFileSync/Source/comparison.h
@@ -58,7 +58,8 @@ FolderComparison compare(WarningDialogs& warnings,
int folderAccessTimeout,
bool createDirLocks,
std::unique_ptr<LockHolder>& dirLocks, //out
- const std::vector<FolderPairCfg>& cfgList,
+ const std::vector<FolderPairCfg>& fpCfgList,
+ const std::map<AbstractPath, size_t>& deviceParallelOps,
ProcessCallback& callback);
}
diff --git a/FreeFileSync/Source/file_hierarchy.cpp b/FreeFileSync/Source/file_hierarchy.cpp
index 7ef11e37..fd0e8beb 100755
--- a/FreeFileSync/Source/file_hierarchy.cpp
+++ b/FreeFileSync/Source/file_hierarchy.cpp
@@ -457,7 +457,7 @@ std::wstring fff::getSyncOpDescription(const FileSystemObject& fsObj)
case SO_COPY_METADATA_TO_LEFT:
case SO_COPY_METADATA_TO_RIGHT:
- //harmonize with synchronization.cpp::SynchronizeFolderPair::synchronizeFileInt, ect!!
+ //harmonize with synchronization.cpp::FolderPairSyncer::synchronizeFileInt, ect!!
{
Zstring shortNameOld = fsObj.getItemName<RIGHT_SIDE>();
Zstring shortNameNew = fsObj.getItemName< LEFT_SIDE>();
diff --git a/FreeFileSync/Source/file_hierarchy.h b/FreeFileSync/Source/file_hierarchy.h
index f25d7684..482cf50a 100755
--- a/FreeFileSync/Source/file_hierarchy.h
+++ b/FreeFileSync/Source/file_hierarchy.h
@@ -24,8 +24,6 @@
namespace fff
{
-using AFS = AbstractFileSystem;
-
struct FileAttributes
{
FileAttributes() {}
@@ -416,6 +414,9 @@ private:
static std::unordered_set<const ObjectMgr*>& activeObjects()
{
+ //our global ObjectMgr is not thread-safe (and currently does not need to be!)
+ //assert(std::this_thread::get_id() == mainThreadId); -> still, may be accessed by synchronization worker thread, one-at-a-time
+
static std::unordered_set<const ObjectMgr*> inst;
return inst; //external linkage (even in header file!)
}
diff --git a/FreeFileSync/Source/fs/abstract.cpp b/FreeFileSync/Source/fs/abstract.cpp
index 6442008d..1b857161 100755
--- a/FreeFileSync/Source/fs/abstract.cpp
+++ b/FreeFileSync/Source/fs/abstract.cpp
@@ -47,7 +47,7 @@ int AFS::compareAbstractPath(const AbstractPath& lhs, const AbstractPath& rhs)
AFS::PathComponents AFS::getPathComponents(const AbstractPath& ap)
{
- return { AbstractPath(ap.afs, AfsPath(Zstring())), split(ap.afsPath.value, FILE_NAME_SEPARATOR, SplitType::SKIP_EMPTY) };
+ return { AbstractPath(ap.afs, AfsPath()), split(ap.afsPath.value, FILE_NAME_SEPARATOR, SplitType::SKIP_EMPTY) };
}
@@ -69,6 +69,28 @@ Opt<AfsPath> AFS::getParentAfsPath(const AfsPath& afsPath)
}
+void AFS::traverseFolderParallel(const AbstractPath& rootPath, const AFS::TraverserWorkload& workload, size_t parallelOps)
+{
+ warn_static("just glue to rootPath!")
+ assert(rootPath.afsPath.value.empty());
+
+ TraverserWorkloadImpl wlImpl;
+ for (const auto& item : workload)
+ {
+ AfsPath afsPath;
+ for (const Zstring& itemName : item.first)
+ {
+ assert(!contains(itemName, FILE_NAME_SEPARATOR));
+ if (!afsPath.value.empty())
+ afsPath.value += FILE_NAME_SEPARATOR;
+ afsPath.value += itemName;
+ }
+ wlImpl.emplace_back(afsPath, item.second);
+ }
+ rootPath.afs->traverseFolderParallel(wlImpl, parallelOps); //throw
+}
+
+
//target existing: undefined behavior! (fail/overwrite/auto-rename)
AFS::FileCopyResult AFS::copyFileAsStream(const AfsPath& afsPathSource, const StreamAttributes& attrSource, //throw FileError, ErrorFileLocked
const AbstractPath& apTarget, const IOCallback& notifyUnbufferedIO) const
@@ -229,20 +251,28 @@ void AFS::createFolderIfMissingRecursion(const AbstractPath& ap) //throw FileErr
}
catch (FileError&)
{
- Opt<PathStatus> pd;
- try { pd = getPathStatus(ap); /*throw FileError*/ }
- catch (FileError&) {} //previous exception is more relevant
+ const PathStatus ps = getPathStatus(ap); //throw FileError
+ if (ps.existingType == ItemType::FILE)
+ throw;
- if (pd &&
- pd->existingType != ItemType::FILE &&
- pd->relPath.size() != 1) //don't repeat the very same createFolderPlain() call from above!
- {
- AbstractPath intermediatePath = pd->existingPath;
- for (const Zstring& itemName : pd->relPath)
+ //ps.relPath.size() == 1 => same createFolderPlain() call from above? Maybe parent folder was created by parallel thread shortly after failure!
+ AbstractPath intermediatePath = ps.existingPath;
+ for (const Zstring& itemName : ps.relPath)
+ try
+ {
createFolderPlain(intermediatePath = appendRelPath(intermediatePath, itemName)); //throw FileError
- return;
- }
- throw;
+ }
+ catch (FileError&)
+ {
+ try //already existing => possible, if createFolderIfMissingRecursion() is run in parallel
+ {
+ if (getItemType(intermediatePath) != ItemType::FILE) //throw FileError
+ continue;
+ }
+ catch (FileError&) {}
+
+ throw;
+ }
}
}
@@ -254,7 +284,7 @@ struct ItemSearchCallback: public AFS::TraverserCallback
ItemSearchCallback(const Zstring& itemName) : itemName_(itemName) {}
void onFile (const FileInfo& fi) override { if (equalFilePath(fi.itemName, itemName_)) throw AFS::ItemType::FILE; }
- std::unique_ptr<TraverserCallback> onFolder (const FolderInfo& fi) override { if (equalFilePath(fi.itemName, itemName_)) throw AFS::ItemType::FOLDER; return nullptr; }
+ std::shared_ptr<TraverserCallback> onFolder (const FolderInfo& fi) override { if (equalFilePath(fi.itemName, itemName_)) throw AFS::ItemType::FOLDER; return nullptr; }
HandleLink onSymlink(const SymlinkInfo& si) override { if (equalFilePath(si.itemName, itemName_)) throw AFS::ItemType::SYMLINK; return TraverserCallback::LINK_SKIP; }
HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override { throw FileError(msg); }
HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zstring& itemName) override { throw FileError(msg); }
@@ -289,8 +319,8 @@ AFS::PathStatusImpl AFS::getPathStatusViaFolderTraversal(const AfsPath& afsPath)
ps.existingType != ItemType::FILE) //obscure, but possible (and not an error)
try
{
- ItemSearchCallback iscb(itemName);
- traverseFolder(*parentAfsPath, iscb); //throw FileError, ItemType
+ auto iscb = std::make_shared<ItemSearchCallback>(itemName);
+ traverseFolderParallel({{ *parentAfsPath, iscb }}, 1 /*parallelOps*/); //throw FileError, ItemType
}
catch (const ItemType& type) { return { type, afsPath, {} }; } //yes, exceptions for control-flow are bad design... but, but...
//we're not CPU-bound here and finding the item after getItemType() previously failed is exceptional (even C:\pagefile.sys should be found)
@@ -302,17 +332,17 @@ AFS::PathStatusImpl AFS::getPathStatusViaFolderTraversal(const AfsPath& afsPath)
Opt<AFS::ItemType> AFS::getItemTypeIfExists(const AbstractPath& ap) //throw FileError
{
- const PathStatus pd = getPathStatus(ap); //throw FileError
- if (pd.relPath.empty())
- return pd.existingType;
+ const PathStatus ps = getPathStatus(ap); //throw FileError
+ if (ps.relPath.empty())
+ return ps.existingType;
return NoValue();
}
AFS::PathStatus AFS::getPathStatus(const AbstractPath& ap) //throw FileError
{
- const PathStatusImpl pdi = ap.afs->getPathStatus(ap.afsPath); //throw FileError
- return { pdi.existingType, AbstractPath(ap.afs, pdi.existingAfsPath), pdi.relPath };
+ const PathStatusImpl psi = ap.afs->getPathStatus(ap.afsPath); //throw FileError
+ return { psi.existingType, AbstractPath(ap.afs, psi.existingAfsPath), psi.relPath };
}
@@ -323,7 +353,7 @@ struct FlatTraverserCallback: public AFS::TraverserCallback
FlatTraverserCallback(const AbstractPath& folderPath) : folderPath_(folderPath) {}
void onFile (const FileInfo& fi) override { fileNames_ .push_back(fi.itemName); }
- std::unique_ptr<TraverserCallback> onFolder (const FolderInfo& fi) override { folderNames_ .push_back(fi.itemName); return nullptr; }
+ std::shared_ptr<TraverserCallback> onFolder (const FolderInfo& fi) override { folderNames_ .push_back(fi.itemName); return nullptr; }
HandleLink onSymlink(const SymlinkInfo& si) override { symlinkNames_.push_back(si.itemName); return TraverserCallback::LINK_SKIP; }
HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override { throw FileError(msg); }
HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zstring& itemName) override { throw FileError(msg); }
@@ -345,10 +375,12 @@ void removeFolderIfExistsRecursionImpl(const AbstractPath& folderPath, //throw F
const std::function<void (const std::wstring& displayPath)>& onBeforeFolderDeletion) //one call for each *existing* object!
{
- FlatTraverserCallback ft(folderPath); //deferred recursion => save stack space and allow deletion of extremely deep hierarchies!
- AFS::traverseFolder(folderPath, ft); //throw FileError
+ //deferred recursion => save stack space and allow deletion of extremely deep hierarchies!
+ auto ft = std::make_shared<FlatTraverserCallback>(folderPath);
+ const AFS::PathComponents pc = AFS::getPathComponents(folderPath);
+ AFS::traverseFolderParallel(pc.rootPath, {{ pc.relPath, ft }}, 1 /*parallelOps*/); //throw FileError
- for (const Zstring& fileName : ft.refFileNames())
+ for (const Zstring& fileName : ft->refFileNames())
{
const AbstractPath filePath = AFS::appendRelPath(folderPath, fileName);
if (onBeforeFileDeletion)
@@ -357,7 +389,7 @@ void removeFolderIfExistsRecursionImpl(const AbstractPath& folderPath, //throw F
AFS::removeFilePlain(filePath); //throw FileError
}
- for (const Zstring& symlinkName : ft.refSymlinkNames())
+ for (const Zstring& symlinkName : ft->refSymlinkNames())
{
const AbstractPath linkPath = AFS::appendRelPath(folderPath, symlinkName);
if (onBeforeFileDeletion)
@@ -366,7 +398,7 @@ void removeFolderIfExistsRecursionImpl(const AbstractPath& folderPath, //throw F
AFS::removeSymlinkPlain(linkPath); //throw FileError
}
- for (const Zstring& folderName : ft.refFolderNames())
+ for (const Zstring& folderName : ft->refFolderNames())
removeFolderIfExistsRecursionImpl(AFS::appendRelPath(folderPath, folderName), //throw FileError
onBeforeFileDeletion, onBeforeFolderDeletion);
@@ -380,8 +412,9 @@ void removeFolderIfExistsRecursionImpl(const AbstractPath& folderPath, //throw F
void AFS::removeFolderIfExistsRecursion(const AbstractPath& ap, //throw FileError
const std::function<void (const std::wstring& displayPath)>& onBeforeFileDeletion, //optional
- const std::function<void (const std::wstring& displayPath)>& onBeforeFolderDeletion) //one call for each *existing* object!
+ const std::function<void (const std::wstring& displayPath)>& onBeforeFolderDeletion) //one call for each object!
{
+ //no error situation if directory is not existing! manual deletion relies on it!
if (Opt<ItemType> type = AFS::getItemTypeIfExists(ap)) //throw FileError
{
if (*type == AFS::ItemType::SYMLINK)
@@ -394,7 +427,8 @@ void AFS::removeFolderIfExistsRecursion(const AbstractPath& ap, //throw FileErro
else
removeFolderIfExistsRecursionImpl(ap, onBeforeFileDeletion, onBeforeFolderDeletion); //throw FileError
}
- //no error situation if directory is not existing! manual deletion relies on it!
+ else //even if the folder did not exist anymore, significant I/O work was done => report
+ if (onBeforeFolderDeletion) onBeforeFolderDeletion(AFS::getDisplayPath(ap));
}
diff --git a/FreeFileSync/Source/fs/abstract.h b/FreeFileSync/Source/fs/abstract.h
index 233d4faa..9774aead 100755
--- a/FreeFileSync/Source/fs/abstract.h
+++ b/FreeFileSync/Source/fs/abstract.h
@@ -11,6 +11,7 @@
#include <zen/file_error.h>
#include <zen/zstring.h>
#include <zen/optional.h>
+#include <zen/thread.h>
#include <zen/serialize.h> //InputStream/OutputStream support buffered stream concept
#include <wx+/image_holder.h> //NOT a wxWidgets dependency!
@@ -22,8 +23,9 @@ struct AbstractFileSystem;
bool isValidRelPath(const Zstring& relPath);
//==============================================================================================================
-struct AfsPath //= path relative (no leading/traling separator) to the file system root folder
+struct AfsPath //= path relative to the file system root folder (no leading/traling separator)
{
+ AfsPath() {}
explicit AfsPath(const Zstring& p) : value(p) { assert(isValidRelPath(value)); }
Zstring value;
};
@@ -42,17 +44,13 @@ private:
std::shared_ptr<const AbstractFileSystem> afs; //always bound; "const AbstractFileSystem" => all accesses expected to be thread-safe!!!
AfsPath afsPath;
};
+
//==============================================================================================================
struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model thread-safe access!
{
static int compareAbstractPath(const AbstractPath& lhs, const AbstractPath& rhs);
- struct LessAbstractPath
- {
- bool operator()(const AbstractPath& lhs, const AbstractPath& rhs) const { return compareAbstractPath(lhs, rhs) < 0; }
- };
-
static bool equalAbstractPath(const AbstractPath& lhs, const AbstractPath& rhs) { return compareAbstractPath(lhs, rhs) == 0; }
static Zstring getInitPathPhrase(const AbstractPath& ap) { return ap.afs->getInitPathPhrase(ap.afsPath); }
@@ -107,7 +105,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
static bool removeSymlinkIfExists(const AbstractPath& ap); //
static void removeFolderIfExistsRecursion(const AbstractPath& ap, //throw FileError
const std::function<void (const std::wstring& displayPath)>& onBeforeFileDeletion, //optional
- const std::function<void (const std::wstring& displayPath)>& onBeforeFolderDeletion); //one call for each *existing* object!
+ const std::function<void (const std::wstring& displayPath)>& onBeforeFolderDeletion); //one call for each object!
static void removeFilePlain (const AbstractPath& ap) { ap.afs->removeFilePlain (ap.afsPath); } //throw FileError
static void removeSymlinkPlain(const AbstractPath& ap) { ap.afs->removeSymlinkPlain(ap.afsPath); } //throw FileError
@@ -218,15 +216,18 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
virtual void onFile (const FileInfo& fi) = 0; //
virtual HandleLink onSymlink(const SymlinkInfo& si) = 0; //throw X
- virtual std::unique_ptr<TraverserCallback> onFolder (const FolderInfo& fi) = 0; //
+ virtual std::shared_ptr<TraverserCallback> onFolder (const FolderInfo& fi) = 0; //
//nullptr: ignore directory, non-nullptr: traverse into, using the (new) callback
virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) = 0; //failed directory traversal -> consider directory data at current level as incomplete!
virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zstring& itemName) = 0; //failed to get data for single file/dir/symlink only!
};
+ using TraverserWorkload = std::vector<std::pair<std::vector<Zstring> /*relPath*/, std::shared_ptr<TraverserCallback> /*throw X*/>>;
+
//- client needs to handle duplicate file reports! (FilePlusTraverser fallback, retrying to read directory contents, ...)
- static void traverseFolder(const AbstractPath& ap, TraverserCallback& sink /*throw X*/) { ap.afs->traverseFolder(ap.afsPath, sink); } //throw X
+ static void traverseFolderParallel(const AbstractPath& rootPath, const TraverserWorkload& workload, size_t parallelOps);
+
//----------------------------------------------------------------------------------------------------------------
static bool supportPermissionCopy(const AbstractPath& apSource, const AbstractPath& apTarget); //throw FileError
@@ -275,7 +276,10 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
struct RecycleSession
{
virtual ~RecycleSession() {}
- virtual bool recycleItem(const AbstractPath& itemPath, const Zstring& logicalRelPath) = 0; //throw FileError; return true if item existed
+ //- return true if item existed
+ //- multi-threaded access: internally synchronized!
+ virtual bool recycleItem(const AbstractPath& itemPath, const Zstring& logicalRelPath) = 0; //throw FileError;
+
virtual void tryCleanup(const std::function<void (const std::wstring& displayPath)>& notifyDeletionStatus /*optional; currentItem may be empty*/) = 0; //throw FileError
};
@@ -310,6 +314,8 @@ protected: //grant derived classes access to AbstractPath:
FileCopyResult copyFileAsStream(const AfsPath& afsPathSource, const StreamAttributes& attrSource, //throw FileError, ErrorFileLocked
const AbstractPath& apTarget, const zen::IOCallback& notifyUnbufferedIO) const; //may be nullptr; throw X!
+ using TraverserWorkloadImpl = std::vector<std::pair<AfsPath, std::shared_ptr<TraverserCallback> /*throw X*/>>;
+
private:
virtual zen::Opt<Zstring> getNativeItemPath(const AfsPath& afsPath) const { return zen::NoValue(); };
@@ -346,7 +352,8 @@ private:
const uint64_t* streamSize, //optional
const zen::IOCallback& notifyUnbufferedIO) const = 0; //
//----------------------------------------------------------------------------------------------------------------
- virtual void traverseFolder(const AfsPath& afsPath, TraverserCallback& sink /*throw X*/) const = 0; //throw X
+ virtual void traverseFolderParallel(const TraverserWorkloadImpl& workload /*throw X*/, size_t parallelOps) const = 0;
+
//----------------------------------------------------------------------------------------------------------------
virtual bool supportsPermissions(const AfsPath& afsPath) const = 0; //throw FileError
@@ -381,6 +388,11 @@ private:
};
+inline bool operator< (const AbstractPath& lhs, const AbstractPath& rhs) { return AbstractFileSystem::compareAbstractPath(lhs, rhs) < 0; }
+inline bool operator==(const AbstractPath& lhs, const AbstractPath& rhs) { return AbstractFileSystem::compareAbstractPath(lhs, rhs) == 0; }
+inline bool operator!=(const AbstractPath& lhs, const AbstractPath& rhs) { return !(lhs == rhs); }
+
+
//implement "retry" in a generic way:
template <class Command> inline //function object expecting to throw FileError if operation fails
bool tryReportingDirError(Command cmd, AbstractFileSystem::TraverserCallback& callback) //throw X, return "true" on success, "false" if error was ignored
@@ -426,6 +438,182 @@ bool tryReportingItemError(Command cmd, AbstractFileSystem::TraverserCallback& c
}
+#if __GNUC__ < 6 //support for "fold expressions" requires GCC 6.0 or later: http://en.cppreference.com/w/cpp/compiler_support
+ #define ZEN_LINUX_TRAVERSER_LEGACY
+#else
+ #define ZEN_LINUX_TRAVERSER_MODERN
+#endif
+
+#if __GNUC__ == 6 //std::apply available with GCC 7
+}
+namespace std
+{
+template <class F, class T0, class T1, class T2>
+constexpr decltype(auto) apply(F&& f, std::tuple<T0, T1, T2>& t) { return std::invoke(std::forward<F>(f), std::get<0>(t), std::get<1>(t), std::get<2>(t)); }
+}
+namespace fff
+{
+#endif
+
+
+#if !defined ZEN_LINUX || defined ZEN_LINUX_TRAVERSER_MODERN
+template <class Function>
+struct WorkItem
+{
+ Function getResult; //throw FileError
+ Zstring errorItemName; //empty if all items affected
+ size_t errorRetryCount = 0;
+ std::shared_ptr<AbstractFileSystem::TraverserCallback> cb; //call by controlling thread only! => don't require traverseFolderParallel() callbacks to be thread-safe!
+};
+
+template <class Function>
+struct ResultItem
+{
+ WorkItem<Function> wi;
+ std::exception_ptr error; //mutually exclusive
+ decltype(wi.getResult()) value; //
+};
+
+
+template <class... Functions> //avoid std::function memory alloc + virtual calls
+class AsyncTraverserWorkload
+{
+public:
+ AsyncTraverserWorkload() {}
+
+ //context of controlling thread, non-blocking:
+ //NOTE: AsyncTraverserWorkload must out-live threadGroup!!!
+ template <class Function>
+ void run(WorkItem<Function>&& wi, zen::ThreadGroup<std::function<void()>>& threadGroup)
+ {
+ threadGroup.run([this, wi]
+ {
+ try { this->returnResult<Function>({ wi, nullptr, wi.getResult() }); } //throw FileError
+ catch (...) { this->returnResult<Function>({ wi, std::current_exception(), {} }); }
+ });
+
+ std::lock_guard<std::mutex> dummy(lockWork_);
+ ++workItemsInProcess_;
+ }
+
+ //context of controlling thread, blocking:
+ bool getResults(std::tuple<std::vector<ResultItem<Functions>>...>& results) //returns false when traversal is finished
+ {
+ std::apply([](auto&... r) { (..., r.clear()); }, results);
+
+ std::unique_lock<std::mutex> dummy(lockWork_);
+
+ auto resultsReady = [&]
+ {
+ bool ready = false;
+ std::apply([&ready](const auto&... r) { ready = (... || !r.empty()); }, results_);
+ return ready;
+ };
+
+ if (!resultsReady() && workItemsInProcess_ == 0)
+ return false;
+
+ conditionNewResult_.wait(dummy, [&resultsReady] { return resultsReady(); });
+
+ results.swap(results_); //reuse memory + avoid needless item-level mutex locking
+ return true;
+ }
+
+private:
+ AsyncTraverserWorkload (const AsyncTraverserWorkload&) = delete;
+ AsyncTraverserWorkload& operator=(const AsyncTraverserWorkload&) = delete;
+
+ //context of worker threads, non-blocking:
+ template <class Function>
+ void returnResult(ResultItem<Function>&& r)
+ {
+ {
+ std::lock_guard<std::mutex> dummy(lockWork_);
+
+ std::get<std::vector<ResultItem<Function>>>(results_).push_back(std::move(r));
+ --workItemsInProcess_;
+ }
+ conditionNewResult_.notify_all();
+ }
+
+ std::mutex lockWork_;
+ size_t workItemsInProcess_ = 0;
+ std::tuple<std::vector<ResultItem<Functions>>...> results_;
+ std::condition_variable conditionNewResult_;
+};
+
+
+template <class... Functions>
+class GenericDirTraverser
+{
+public:
+ using Function1 = typename zen::GetFirstOf<Functions...>::Type;
+
+ GenericDirTraverser(std::vector<WorkItem<Function1>>&& initialWorkItems, size_t parallelOps, const std::string& threadGroupName) :
+ threadGroup_(std::max<size_t>(1, parallelOps), threadGroupName)
+ {
+ //set the initial work load
+ for (auto& item : initialWorkItems)
+ asyncWorkload_.template run<Function1>(std::move(item), threadGroup_);
+
+ //run loop
+ std::tuple<std::vector<ResultItem<Functions>>...> results; //avoid per-getNextResults() memory allocations (=> swap instead!)
+
+ while (asyncWorkload_.getResults(results))
+ std::apply([&](auto&... r) { (..., this->evalResultList(r)); }, results); //throw X
+ }
+
+private:
+ GenericDirTraverser (const GenericDirTraverser&) = delete;
+ GenericDirTraverser& operator=(const GenericDirTraverser&) = delete;
+
+ template <class Function>
+ void evalResultList(std::vector<ResultItem<Function>>& results) //throw X
+ {
+ for (ResultItem<Function>& result : results)
+ evalResult(result); //throw X
+ }
+
+ template <class Function>
+ void evalResult(ResultItem<Function>& result); //throw X
+
+ //specialize!
+ template <class Function>
+ void evalResultValue(const typename Function::Result& r, std::shared_ptr<AbstractFileSystem::TraverserCallback>& cb); //throw X
+
+ AsyncTraverserWorkload<Functions...> asyncWorkload_; //ATTENTION: asyncWorkload_ must out-live threadGroup_!!!
+ zen::ThreadGroup<std::function<void()>> threadGroup_; //
+};
+
+
+template <class... Functions>
+template <class Function>
+void GenericDirTraverser<Functions...>::evalResult(ResultItem<Function>& result) //throw X
+{
+ auto& cb = result.wi.cb;
+ try
+ {
+ if (result.error)
+ std::rethrow_exception(result.error); //throw FileError
+ }
+ catch (const zen::FileError& e)
+ {
+ switch (result.wi.errorItemName.empty() ?
+ cb->reportDirError (e.toString(), result.wi.errorRetryCount) : //throw X
+ cb->reportItemError(e.toString(), result.wi.errorRetryCount, result.wi.errorItemName)) //throw X
+ {
+ case AbstractFileSystem::TraverserCallback::ON_ERROR_RETRY:
+ asyncWorkload_.template run<Function>({ std::move(result.wi.getResult), result.wi.errorItemName, result.wi.errorRetryCount + 1, cb }, threadGroup_);
+ return;
+
+ case AbstractFileSystem::TraverserCallback::ON_ERROR_CONTINUE:
+ return;
+ }
+ }
+
+ evalResultValue<Function>(result.value, result.wi.cb); //throw X
+}
+#endif
diff --git a/FreeFileSync/Source/fs/native.cpp b/FreeFileSync/Source/fs/native.cpp
index 5ea0c9bb..864b1369 100755
--- a/FreeFileSync/Source/fs/native.cpp
+++ b/FreeFileSync/Source/fs/native.cpp
@@ -11,6 +11,7 @@
#include <zen/file_id_def.h>
#include <zen/stl_tools.h>
#include <zen/recycler.h>
+#include <zen/thread.h>
#include <zen/guid.h>
#include <zen/crc.h>
#include "../lib/resolve_path.h"
@@ -29,8 +30,6 @@ using AFS = AbstractFileSystem;
namespace
{
-
-
void initComForThread() //throw FileError
{
}
@@ -50,40 +49,270 @@ AFS::FileId convertToAbstractFileId(const zen::FileId& fid)
}
-class DirTraverser
+#if !defined ZEN_LINUX || defined ZEN_LINUX_TRAVERSER_MODERN
+struct FsItemRaw
{
-public:
- static void execute(const Zstring& baseDirPath, AFS::TraverserCallback& sink)
+ Zstring itemName;
+ Zstring itemPath;
+};
+std::vector<FsItemRaw> getDirContentFlat(const Zstring& dirPath) //throw FileError
+{
+ //no need to check for endless recursion:
+ //1. Linux has a fixed limit on the number of symbolic links in a path
+ //2. fails with "too many open files" or "path too long" before reaching stack overflow
+
+ DIR* folder = ::opendir(dirPath.c_str()); //directory must NOT end with path separator, except "/"
+ if (!folder)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), L"opendir");
+ ZEN_ON_SCOPE_EXIT(::closedir(folder)); //never close nullptr handles! -> crash
+
+ std::vector<FsItemRaw> output;
+ for (;;)
+ {
+ /*
+ Linux:
+ http://man7.org/linux/man-pages/man3/readdir_r.3.html
+ "It is recommended that applications use readdir(3) instead of readdir_r"
+ "... in modern implementations (including the glibc implementation), concurrent calls to readdir(3) that specify different directory streams are thread-safe"
+
+ macOS:
+ - libc: readdir thread-safe already in code from 2000: https://opensource.apple.com/source/Libc/Libc-166/gen.subproj/readdir.c.auto.html
+ - and in the latest version from 2017: https://opensource.apple.com/source/Libc/Libc-1244.30.3/gen/FreeBSD/readdir.c.auto.html
+ */
+ errno = 0;
+ const struct ::dirent* dirEntry = ::readdir(folder);
+ if (!dirEntry)
+ {
+ if (errno == 0) //errno left unchanged => no more items
+ return output;
+
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir");
+ //don't retry but restart dir traversal on error! https://blogs.msdn.microsoft.com/oldnewthing/20140612-00/?p=753/
+ }
+
+ const char* itemNameRaw = dirEntry->d_name; //evaluate dirEntry *before* going into recursion
+
+ //skip "." and ".."
+ if (itemNameRaw[0] == '.' &&
+ (itemNameRaw[1] == 0 || (itemNameRaw[1] == '.' && itemNameRaw[2] == 0)))
+ continue;
+ const Zstring& itemName = itemNameRaw;
+ if (itemName.empty()) //checks result of normalizeUtfForPosix, too!
+ throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir: Data corruption; item with empty name.");
+
+ const Zstring& itemPath = appendSeparator(dirPath) + itemName;
+
+ output.push_back({ itemName, itemPath});
+ }
+}
+
+
+struct ItemDetailsRaw
+{
+ ItemType type;
+ time_t modTime; //number of seconds since Jan. 1st 1970 UTC
+ uint64_t fileSize; //unit: bytes!
+ FileId fileId;
+};
+ItemDetailsRaw getItemDetails(const Zstring& itemPath) //throw FileError
+{
+ struct ::stat statData = {};
+ if (::lstat(itemPath.c_str(), &statData) != 0) //lstat() does not resolve symlinks
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"lstat");
+
+ if (S_ISLNK(statData.st_mode)) //on Linux there is no distinction between file and directory symlinks!
+ return { ItemType::SYMLINK, statData.st_mtime, 0, extractFileId(statData) };
+
+ else if (S_ISDIR(statData.st_mode)) //a directory
+ return { ItemType::FOLDER, statData.st_mtime, 0, extractFileId(statData) };
+
+ else //a file or named pipe, ect. => dont't check using S_ISREG(): see comment in file_traverser.cpp
+ return { ItemType::FILE, statData.st_mtime, makeUnsigned(statData.st_size), extractFileId(statData) };
+}
+
+ItemDetailsRaw getSymlinkTargetDetails(const Zstring& linkPath) //throw FileError
+{
+ struct ::stat statData = {};
+ if (::stat(linkPath.c_str(), &statData) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), L"stat");
+
+ if (S_ISDIR(statData.st_mode)) //a directory
+ return { ItemType::FOLDER, statData.st_mtime, 0, extractFileId(statData) };
+ else //a file or named pipe, ect.
+ return { ItemType::FILE, statData.st_mtime, makeUnsigned(statData.st_size), extractFileId(statData) };
+}
+
+
+struct GetDirDetails
+{
+ GetDirDetails(const Zstring& dirPath) : dirPath_(dirPath) {}
+
+ using Result = std::vector<FsItemRaw>;
+ Result operator()() const
{
- DirTraverser(baseDirPath, sink); //throw X
+ return getDirContentFlat(dirPath_); //throw FileError
}
private:
- DirTraverser(const Zstring& baseDirPath, AFS::TraverserCallback& sink)
+ Zstring dirPath_;
+};
+
+
+struct GetItemDetails //details not already retrieved by raw folder traversal
+{
+ GetItemDetails(const FsItemRaw& rawItem) : rawItem_(rawItem) {}
+
+ struct Result
+ {
+ FsItemRaw raw;
+ ItemDetailsRaw details;
+ };
+ Result operator()() const
{
- /* quote: "Since POSIX.1 does not specify the size of the d_name field, and other nonstandard fields may precede
- that field within the dirent structure, portable applications that use readdir_r() should allocate
- the buffer whose address is passed in entry as follows:
- len = offsetof(struct dirent, d_name) + pathconf(dirPath, _PC_NAME_MAX) + 1
- entryp = malloc(len); */
- const size_t nameMax = std::max<long>(::pathconf(baseDirPath.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return long(-1)
- buffer_.resize(offsetof(struct ::dirent, d_name) + nameMax + 1);
+ return { rawItem_, getItemDetails(rawItem_.itemPath) }; //throw FileError
+ }
+
+private:
+ FsItemRaw rawItem_;
+};
+
+
+struct GetLinkTargetDetails
+{
+ GetLinkTargetDetails(const FsItemRaw& rawItem, const ItemDetailsRaw& linkDetails) : rawItem_(rawItem), linkDetails_(linkDetails) {}
- traverse(baseDirPath, sink); //throw X
+ struct Result
+ {
+ FsItemRaw raw;
+ ItemDetailsRaw link;
+ ItemDetailsRaw target;
+ };
+ Result operator()() const
+ {
+ return { rawItem_, linkDetails_, getSymlinkTargetDetails(rawItem_.itemPath) }; //throw FileError
}
- DirTraverser (const DirTraverser&) = delete;
- DirTraverser& operator=(const DirTraverser&) = delete;
+private:
+ FsItemRaw rawItem_;
+ ItemDetailsRaw linkDetails_;
+};
+
+
+void traverseFolderParallelNative(const std::vector<std::pair<Zstring, std::shared_ptr<AFS::TraverserCallback>>>& initialWorkItems, size_t parallelOps) //throw X
+{
+ std::vector<WorkItem<GetDirDetails>> genItems;
+
+ for (const auto& item : initialWorkItems)
+ genItems.push_back({ GetDirDetails(item.first),
+ Zstring() /*errorItemName*/, 0 /*errorRetryCount*/, item.second /*TraverserCallback*/ });
+
+ GenericDirTraverser<GetDirDetails, GetItemDetails, GetLinkTargetDetails>(std::move(genItems), parallelOps, "Native Traverser"); //throw X
+}
+}
- void traverse(const Zstring& dirPath, AFS::TraverserCallback& sink) //throw X
+namespace fff //specialization in original namespace needed by GCC 6
+{
+template <>
+template <>
+void GenericDirTraverser<GetDirDetails, GetItemDetails, GetLinkTargetDetails>::evalResultValue<GetDirDetails>(const GetDirDetails::Result& r, std::shared_ptr<AFS::TraverserCallback>& cb) //throw X
+{
+ for (const FsItemRaw& rawItem : r)
+ asyncWorkload_.run<GetItemDetails>({ GetItemDetails(rawItem),
+ rawItem.itemName, 0 /*errorRetryCount*/, cb }, threadGroup_);
+}
+
+
+template <>
+template <>
+void GenericDirTraverser<GetDirDetails, GetItemDetails, GetLinkTargetDetails>::evalResultValue<GetItemDetails>(const GetItemDetails::Result& r, std::shared_ptr<AFS::TraverserCallback>& cb) //throw X
+{
+ switch (r.details.type)
{
- tryReportingDirError([&] //throw X
+ case ItemType::FILE:
+ cb->onFile({ r.raw.itemName, r.details.fileSize, r.details.modTime, convertToAbstractFileId(r.details.fileId), nullptr /*symlinkInfo*/ }); //throw X
+ break;
+
+ case ItemType::FOLDER:
+ if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb->onFolder({ r.raw.itemName, nullptr /*symlinkInfo*/ })) //throw X
+ asyncWorkload_.run<GetDirDetails>({ GetDirDetails(r.raw.itemPath),
+ Zstring() /*errorItemName*/, 0 /*errorRetryCount*/, std::move(cbSub) }, threadGroup_);
+ break;
+
+ case ItemType::SYMLINK:
+ switch (cb->onSymlink({ r.raw.itemName, r.details.modTime })) //throw X
+ {
+ case AFS::TraverserCallback::LINK_FOLLOW:
+ asyncWorkload_.run<GetLinkTargetDetails>({ GetLinkTargetDetails(r.raw, r.details),
+ r.raw.itemName, 0 /*errorRetryCount*/, cb }, threadGroup_);
+ break;
+
+ case AFS::TraverserCallback::LINK_SKIP:
+ break;
+ }
+ break;
+ }
+}
+
+
+template <>
+template <>
+void GenericDirTraverser<GetDirDetails, GetItemDetails, GetLinkTargetDetails>::evalResultValue<GetLinkTargetDetails>(const GetLinkTargetDetails::Result& r, std::shared_ptr<AFS::TraverserCallback>& cb) //throw X
+{
+ assert(r.link.type == ItemType::SYMLINK && r.target.type != ItemType::SYMLINK);
+
+ const AFS::TraverserCallback::SymlinkInfo linkInfo = { r.raw.itemName, r.link.modTime };
+
+ if (r.target.type == ItemType::FOLDER)
+ {
+ if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb->onFolder({ r.raw.itemName, &linkInfo })) //throw X
+ asyncWorkload_.run<GetDirDetails>({ GetDirDetails(r.raw.itemPath),
+ Zstring() /*errorItemName*/, 0 /*errorRetryCount*/, std::move(cbSub) }, threadGroup_);
+ }
+ else //a file or named pipe, ect.
+ cb->onFile({ r.raw.itemName, r.target.fileSize, r.target.modTime, convertToAbstractFileId(r.target.fileId), &linkInfo }); //throw X
+}
+}
+
+namespace
+{
+#elif defined ZEN_LINUX && defined ZEN_LINUX_TRAVERSER_LEGACY //support legacy GCC versions < 6
+static_assert(__GNUC__ < 6, "");
+
+class LegacyDirTraverser
+{
+public:
+ static void execute(const std::vector<std::pair<Zstring, std::shared_ptr<AFS::TraverserCallback>>>& initialWorkItems, size_t parallelOps)
+ {
+ assert(parallelOps == 1);
+ //Parallel operations > 1 are not supported because FreeFileSync was built with GCC < version 6.",
+ // => at least parallel_scan.h will show the correct number of threads
+
+ for (const auto& item : initialWorkItems)
+ LegacyDirTraverser(item.first, *item.second); //throw X
+ }
+
+private:
+ LegacyDirTraverser (const LegacyDirTraverser&) = delete;
+ LegacyDirTraverser& operator=(const LegacyDirTraverser&) = delete;
+
+ LegacyDirTraverser(const Zstring& baseDirPath, AFS::TraverserCallback& cb)
+ {
+ //set the initial work load
+ workload_.push_back({ baseDirPath, std::shared_ptr<AFS::TraverserCallback>(&cb, [](AFS::TraverserCallback*) {}) });
+
+ while (!workload_.empty())
{
- traverseWithException(dirPath, sink); //throw FileError, X
- }, sink);
+ WorkItem wi = std::move(workload_. back()); //yes, no strong exception guarantee (std::bad_alloc)
+ /**/ workload_.pop_back(); //
+
+ tryReportingDirError([&] //throw X
+ {
+ traverseWithException(wi.dirPath, *wi.cb); //throw FileError, X
+ }, *wi.cb);
+ }
}
- void traverseWithException(const Zstring& dirPath, AFS::TraverserCallback& sink) //throw FileError, X
+ void traverseWithException(const Zstring& dirPath, AFS::TraverserCallback& cb) //throw FileError, X
{
//no need to check for endless recursion:
//1. Linux has a fixed limit on the number of symbolic links in a path
@@ -96,23 +325,37 @@ private:
for (;;)
{
- struct ::dirent* dirEntry = nullptr;
- if (::readdir_r(folder, reinterpret_cast< ::dirent*>(&buffer_[0]), &dirEntry) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r");
- //don't retry but restart dir traversal on error! https://blogs.msdn.microsoft.com/oldnewthing/20140612-00/?p=753/
+ /*
+ Linux:
+ http://man7.org/linux/man-pages/man3/readdir_r.3.html
+ "It is recommended that applications use readdir(3) instead of readdir_r"
+ "... in modern implementations (including the glibc implementation), concurrent calls to readdir(3) that specify different directory streams are thread-safe"
+
+ macOS:
+ - libc: readdir thread-safe already in code from 2000: https://opensource.apple.com/source/Libc/Libc-166/gen.subproj/readdir.c.auto.html
+ - and in the latest version from 2017: https://opensource.apple.com/source/Libc/Libc-1244.30.3/gen/FreeBSD/readdir.c.auto.html
+ */
+ errno = 0;
+ const struct ::dirent* dirEntry = ::readdir(folder);
+ if (!dirEntry)
+ {
+ if (errno == 0) //errno left unchanged => no more items
+ return;
- if (!dirEntry) //no more items
- return;
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir");
+ //don't retry but restart dir traversal on error! https://blogs.msdn.microsoft.com/oldnewthing/20140612-00/?p=753/
+ }
- const char* itemNameRaw = dirEntry->d_name; //evaluate dirEntry *before* going into recursion => we use a single "buffer"!
+ const char* itemNameRaw = dirEntry->d_name; //evaluate dirEntry *before* going into recursion
//skip "." and ".."
if (itemNameRaw[0] == '.' &&
(itemNameRaw[1] == 0 || (itemNameRaw[1] == '.' && itemNameRaw[2] == 0)))
continue;
+
const Zstring& itemName = itemNameRaw;
if (itemName.empty()) //checks result of normalizeUtfForPosix, too!
- throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item with empty name.");
+ throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir: Data corruption; item with empty name.");
const Zstring& itemPath = appendSeparator(dirPath) + itemName;
@@ -121,14 +364,14 @@ private:
{
if (::lstat(itemPath.c_str(), &statData) != 0) //lstat() does not resolve symlinks
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"lstat");
- }, sink, itemName))
+ }, cb, itemName))
continue; //ignore error: skip file
if (S_ISLNK(statData.st_mode)) //on Linux there is no distinction between file and directory symlinks!
{
const AFS::TraverserCallback::SymlinkInfo linkInfo = { itemName, statData.st_mtime };
- switch (sink.onSymlink(linkInfo)) //throw X
+ switch (cb.onSymlink(linkInfo)) //throw X
{
case AFS::TraverserCallback::LINK_FOLLOW:
{
@@ -139,19 +382,19 @@ private:
{
if (::stat(itemPath.c_str(), &statDataTrg) != 0)
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(itemPath)), L"stat");
- }, sink, itemName);
+ }, cb, itemName);
if (validLink)
{
if (S_ISDIR(statDataTrg.st_mode)) //a directory
{
- if (std::unique_ptr<AFS::TraverserCallback> trav = sink.onFolder({ itemName, &linkInfo })) //throw X
- traverse(itemPath, *trav); //throw X
+ if (std::shared_ptr<AFS::TraverserCallback> trav = cb.onFolder({ itemName, &linkInfo })) //throw X
+ workload_.push_back({ itemPath, std::move(trav) });
}
else //a file or named pipe, ect.
{
AFS::TraverserCallback::FileInfo fi = { itemName, makeUnsigned(statDataTrg.st_size), statDataTrg.st_mtime, convertToAbstractFileId(extractFileId(statDataTrg)), &linkInfo };
- sink.onFile(fi); //throw X
+ cb.onFile(fi); //throw X
}
}
// else //broken symlink -> ignore: it's client's responsibility to handle error!
@@ -164,27 +407,34 @@ private:
}
else if (S_ISDIR(statData.st_mode)) //a directory
{
- if (std::unique_ptr<AFS::TraverserCallback> trav = sink.onFolder({ itemName, nullptr })) //throw X
- traverse(itemPath, *trav); //throw X
+ if (std::shared_ptr<AFS::TraverserCallback> trav = cb.onFolder({ itemName, nullptr })) //throw X
+ workload_.push_back({ itemPath, std::move(trav) });
+ warn_static("workload_ item order is reversed => fix!?")
}
- else //a file or named pipe, ect.
+ else //a file or named pipe, ect. => dont't check using S_ISREG(): see comment in file_traverser.cpp
{
AFS::TraverserCallback::FileInfo fi = { itemName, makeUnsigned(statData.st_size), statData.st_mtime, convertToAbstractFileId(extractFileId(statData)), nullptr /*symlinkInfo*/ };
- sink.onFile(fi); //throw X
+ cb.onFile(fi); //throw X
}
- /*
- It may be a good idea to not check "S_ISREG(statData.st_mode)" explicitly and to not issue an error message on other types to support these scenarios:
- - RTS setup watch (essentially wants to read directories only)
- - removeDirectory (wants to delete everything; pipes can be deleted just like files via "unlink")
-
- However an "open" on a pipe will block (https://sourceforge.net/p/freefilesync/bugs/221/), so the copy routines need to be smarter!!
- */
}
}
- std::vector<char> buffer_;
+ struct WorkItem
+ {
+ Zstring dirPath;
+ std::shared_ptr<AFS::TraverserCallback> cb;
+ };
+
+ std::vector<WorkItem> workload_;
};
+
+void traverseFolderParallelNative(const std::vector<std::pair<Zstring, std::shared_ptr<AFS::TraverserCallback>>>& initialWorkItems, size_t parallelOps) //throw X
+{
+ LegacyDirTraverser::execute(initialWorkItems, parallelOps); //throw X
+}
+#endif
+
//====================================================================================================
//====================================================================================================
@@ -389,9 +639,15 @@ private:
}
//----------------------------------------------------------------------------------------------------------------
- void traverseFolder(const AfsPath& afsPath, TraverserCallback& sink /*throw X*/) const override //throw X
+ void traverseFolderParallel(const TraverserWorkloadImpl& workload /*throw X*/, size_t parallelOps) const override
{
- DirTraverser::execute(getNativePath(afsPath), sink); //throw X
+ //initComForThread() -> done on traverser worker threads
+
+ std::vector<std::pair<Zstring, std::shared_ptr<TraverserCallback>>> initialWorkItems;
+ for (const auto& item : workload)
+ initialWorkItems.emplace_back(getNativePath(item.first), item.second);
+
+ traverseFolderParallelNative(initialWorkItems, parallelOps); //throw X
}
//----------------------------------------------------------------------------------------------------------------
@@ -525,6 +781,8 @@ private:
+//- return true if item existed
+//- multi-threaded access: internally synchronized!
bool RecycleSessionNative::recycleItem(const AbstractPath& itemPath, const Zstring& logicalRelPath) //throw FileError
{
assert(!startsWith(logicalRelPath, FILE_NAME_SEPARATOR));
@@ -572,9 +830,6 @@ AbstractPath fff::createItemPathNativeNoFormatting(const Zstring& nativePath) //
{
if (const Opt<PathComponents> comp = parsePathComponents(nativePath))
return AbstractPath(std::make_shared<NativeFileSystem>(comp->rootPath), AfsPath(comp->relPath));
- else
- {
- // assert(nativePath.empty());
- return AbstractPath(std::make_shared<NativeFileSystem>(nativePath), AfsPath(Zstring()));
- }
+ else //path syntax broken
+ return AbstractPath(std::make_shared<NativeFileSystem>(nativePath), AfsPath());
}
diff --git a/FreeFileSync/Source/lib/dir_exist_async.h b/FreeFileSync/Source/lib/dir_exist_async.h
index ea518316..4daee747 100755
--- a/FreeFileSync/Source/lib/dir_exist_async.h
+++ b/FreeFileSync/Source/lib/dir_exist_async.h
@@ -22,38 +22,65 @@ namespace
//directory existence checking may hang for non-existent network drives => run asynchronously and update UI!
//- check existence of all directories in parallel! (avoid adding up search times if multiple network drives are not reachable)
//- add reasonable time-out time!
-//- avoid checking duplicate entries by design: std::set
+//- avoid checking duplicate entries => std::set
struct FolderStatus
{
- std::set<AbstractPath, AFS::LessAbstractPath> existing;
- std::set<AbstractPath, AFS::LessAbstractPath> notExisting;
- std::map<AbstractPath, zen::FileError, AFS::LessAbstractPath> failedChecks;
+ std::set<AbstractPath> existing;
+ std::set<AbstractPath> notExisting;
+ std::map<AbstractPath, zen::FileError> failedChecks;
};
-FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath, AFS::LessAbstractPath>& folderPaths, int folderAccessTimeout,
- bool allowUserInteraction, ProcessCallback& procCallback)
+FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath>& folderPaths, const std::map<AbstractPath, size_t>& deviceParallelOps,
+ int folderAccessTimeout, bool allowUserInteraction,
+ ProcessCallback& procCallback)
{
using namespace zen;
- FolderStatus output;
-
- std::list<std::pair<AbstractPath, std::future<bool>>> futureInfo;
+ //aggregate folder paths that are on the same root device: see parallel_scan.h
+ std::map<AbstractPath, std::set<AbstractPath>> perDevicePaths;
for (const AbstractPath& folderPath : folderPaths)
if (!AFS::isNullPath(folderPath)) //skip empty dirs
- futureInfo.emplace_back(folderPath, runAsync([folderPath, allowUserInteraction] //AbstractPath is thread-safe like an int! :)
+ perDevicePaths[AFS::getPathComponents(folderPath).rootPath].insert(folderPath);
+ warn_static("relax for native paths? consider hanging network share!?")
+
+ std::list<std::pair<AbstractPath, std::future<bool>>> futureInfo;
+
+ std::list<ThreadGroup<std::packaged_task<bool()>>> perDeviceThreads;
+ for (const auto& item : perDevicePaths)
+ {
+ const AbstractPath& rootPath = item.first;
+
+ auto itParOps = deviceParallelOps.find(rootPath);
+ const size_t parallelOps = std::max<size_t>(itParOps != deviceParallelOps.end() ? itParOps->second : 1, 1);
+ const size_t threadCount = std::min(parallelOps, item.second.size());
+
+ perDeviceThreads.emplace_back(threadCount, "DirExist Device: " + utfTo<std::string>(AFS::getDisplayPath(rootPath)));
+ auto& threadGroup = perDeviceThreads.back();
+
+ for (const AbstractPath& folderPath : item.second)
{
- //1. login to network share, open FTP connection, ect.
- AFS::connectNetworkFolder(folderPath, allowUserInteraction); //throw FileError
+ std::packaged_task<bool()> pt([folderPath, allowUserInteraction] //AbstractPath is thread-safe like an int! :)
+ {
+ //1. login to network share, open FTP connection, ect.
+ AFS::connectNetworkFolder(folderPath, allowUserInteraction); //throw FileError
- //2. check dir existence
- return static_cast<bool>(AFS::getItemTypeIfExists(folderPath)); //throw FileError
- //TODO: consider ItemType:FILE a failure instead? In any case: return "false" IFF nothing (of any type) exists
- }));
+ //2. check dir existence
+ return static_cast<bool>(AFS::getItemTypeIfExists(folderPath)); //throw FileError
+ //TODO: consider ItemType:FILE a failure instead? In any case: return "false" IFF nothing (of any type) exists
+ });
+ auto fut = pt.get_future();
+ threadGroup.run(std::move(pt));
+
+ futureInfo.emplace_back(folderPath, std::move(fut));
+ }
+ }
//don't wait (almost) endlessly like Win32 would on non-existing network shares:
const auto startTime = std::chrono::steady_clock::now();
+ FolderStatus output;
+
for (auto& fi : futureInfo)
{
const std::wstring& displayPathFmt = fmtPath(AFS::getDisplayPath(fi.first));
@@ -79,7 +106,6 @@ FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath, AFS::LessAb
else
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 ab41804f..68ce72f4 100755
--- a/FreeFileSync/Source/lib/dir_lock.cpp
+++ b/FreeFileSync/Source/lib/dir_lock.cpp
@@ -41,12 +41,15 @@ const int LOCK_FORMAT_VER = 2; //lock file format version
class LifeSigns
{
public:
- LifeSigns(const Zstring& lockFilePath) : lockFilePath_(lockFilePath) {}
+ LifeSigns(const Zstring& lockFilePath) : lockFilePath_(lockFilePath)
+ {
+ }
void operator()() const //throw ThreadInterruption
{
- setCurrentThreadName("DirLock: Life Signs");
-
+ const Opt<Zstring> parentDirPath = getParentFolderPath(lockFilePath_);
+ setCurrentThreadName(("DirLock: " + (parentDirPath ? utfTo<std::string>(*parentDirPath) : "")).c_str());
+ warn_static("set nice name for setCurrentThreadName in other places, too")
try
{
for (;;)
diff --git a/FreeFileSync/Source/lib/generate_logfile.cpp b/FreeFileSync/Source/lib/generate_logfile.cpp
new file mode 100755
index 00000000..2ee7f309
--- /dev/null
+++ b/FreeFileSync/Source/lib/generate_logfile.cpp
@@ -0,0 +1,246 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "generate_logfile.h"
+#include <zen/file_io.h>
+#include <wx/datetime.h>
+
+using namespace zen;
+using namespace fff;
+
+
+namespace
+{
+std::wstring generateLogHeader(const LogSummary& s)
+{
+ assert(s.itemsProcessed <= s.itemsTotal);
+ assert(s.bytesProcessed <= s.bytesTotal);
+
+ std::wstring output;
+
+ //write header
+ std::wstring headerLine = formatTime<std::wstring>(FORMAT_DATE);
+ if (!s.jobName.empty())
+ headerLine += L" | " + s.jobName;
+ headerLine += L" | " + s.finalStatus;
+
+ //assemble results box
+ std::vector<std::wstring> results;
+ results.push_back(headerLine);
+ results.push_back(L"");
+
+ const wchar_t tabSpace[] = L" ";
+
+ std::wstring itemsProc = tabSpace + _("Items processed:") + L" " + formatNumber(s.itemsProcessed); //show always, even if 0!
+ if (s.itemsProcessed != 0 || s.bytesProcessed != 0) //[!] don't show 0 bytes processed if 0 items were processed
+ itemsProc += + L" (" + formatFilesizeShort(s.bytesProcessed) + L")";
+ results.push_back(itemsProc);
+
+ if (s.itemsTotal != 0 || s.bytesTotal != 0) //=: sync phase was reached and there were actual items to sync
+ {
+ if (s.itemsProcessed != s.itemsTotal ||
+ s.bytesProcessed != s.bytesTotal)
+ results.push_back(tabSpace + _("Items remaining:") + L" " + formatNumber(s.itemsTotal - s.itemsProcessed) + L" (" + formatFilesizeShort(s.bytesTotal - s.bytesProcessed) + L")");
+ }
+
+ results.push_back(tabSpace + _("Total time:") + L" " + copyStringTo<std::wstring>(wxTimeSpan::Seconds(s.totalTime).Format()));
+
+ //calculate max width, this considers UTF-16 only, not true Unicode...but maybe good idea? those 2-char-UTF16 codes are usually wider than fixed width chars anyway!
+ size_t sepLineLen = 0;
+ for (const std::wstring& str : results) sepLineLen = std::max(sepLineLen, str.size());
+
+ output.resize(output.size() + sepLineLen + 1, L'_');
+ output += L'\n';
+
+ for (const std::wstring& str : results) { output += L'|'; output += str; output += L'\n'; }
+
+ output += L'|';
+ output.resize(output.size() + sepLineLen, L'_');
+ output += L'\n';
+
+ return output;
+}
+}
+
+
+void fff::streamToLogFile(const LogSummary& summary, //throw FileError
+ const zen::ErrorLog& log,
+ AFS::OutputStream& streamOut)
+{
+ const std::string header = replaceCpy(utfTo<std::string>(generateLogHeader(summary)), '\n', LINE_BREAK); //don't replace line break any earlier
+
+ streamOut.write(&header[0], header.size()); //throw FileError, X
+
+ //write log items in blocks instead of creating one big string: memory allocation might fail; think 1 million entries!
+ std::string buffer;
+ buffer += LINE_BREAK;
+
+ for (const LogEntry& entry : log)
+ {
+ buffer += replaceCpy(utfTo<std::string>(formatMessage<std::wstring>(entry)), '\n', LINE_BREAK);
+ buffer += LINE_BREAK;
+
+ streamOut.write(&buffer[0], buffer.size()); //throw FileError, X
+ buffer.clear();
+ }
+}
+
+
+void fff::saveToLastSyncsLog(const LogSummary& summary, //throw FileError
+ const zen::ErrorLog& log,
+ size_t maxBytesToWrite, //log may be *huge*, e.g. 1 million items; LastSyncs.log *must not* create performance problems!
+ const std::function<void(const std::wstring& msg)>& notifyStatus)
+{
+ const Zstring filePath = getConfigDirPathPf() + Zstr("LastSyncs.log");
+
+ Utf8String newStream = utfTo<Utf8String>(generateLogHeader(summary));
+ replace(newStream, '\n', LINE_BREAK); //don't replace line break any earlier
+ newStream += LINE_BREAK;
+
+ //check size of "newStream": memory allocation might fail - think 1 million entries!
+ for (const LogEntry& entry : log)
+ {
+ newStream += replaceCpy(utfTo<Utf8String>(formatMessage<std::wstring>(entry)), '\n', LINE_BREAK);
+ newStream += LINE_BREAK;
+
+ if (newStream.size() > maxBytesToWrite)
+ {
+ newStream += "[...]";
+ newStream += LINE_BREAK;
+ break;
+ }
+ }
+
+ auto notifyUnbufferedIOLoad = [notifyStatus,
+ bytesRead_ = int64_t(0),
+ msg_ = replaceCpy(_("Loading file %x..."), L"%x", fmtPath(filePath))]
+ (int64_t bytesDelta) mutable
+ {
+ if (notifyStatus)
+ notifyStatus(msg_ + L" (" + formatFilesizeShort(bytesRead_ += bytesDelta) + L")"); /*throw X*/
+ };
+
+ auto notifyUnbufferedIOSave = [notifyStatus,
+ bytesWritten_ = int64_t(0),
+ msg_ = replaceCpy(_("Saving file %x..."), L"%x", fmtPath(filePath))]
+ (int64_t bytesDelta) mutable
+ {
+ if (notifyStatus)
+ notifyStatus(msg_ + L" (" + formatFilesizeShort(bytesWritten_ += bytesDelta) + L")"); /*throw X*/
+ };
+
+ //fill up the rest of permitted space by appending old log
+ if (newStream.size() < maxBytesToWrite)
+ {
+ Utf8String oldStream;
+ try
+ {
+ oldStream = loadBinContainer<Utf8String>(filePath, notifyUnbufferedIOLoad); //throw FileError, X
+ //Note: we also report the loaded bytes via onUpdateSaveStatus()!
+ }
+ catch (FileError&) {}
+
+ if (!oldStream.empty())
+ {
+ newStream += LINE_BREAK;
+ newStream += LINE_BREAK;
+ newStream += oldStream; //implicitly limited by "maxBytesToWrite"!
+
+ //truncate size if required
+ if (newStream.size() > maxBytesToWrite)
+ {
+ //but do not cut in the middle of a row
+ auto it = std::search(newStream.cbegin() + maxBytesToWrite, newStream.cend(), std::begin(LINE_BREAK), std::end(LINE_BREAK) - 1);
+ if (it != newStream.cend())
+ {
+ newStream.resize(it - newStream.cbegin());
+ newStream += LINE_BREAK;
+
+ newStream += "[...]";
+ newStream += LINE_BREAK;
+ }
+ }
+ }
+ }
+
+ saveBinContainer(filePath, newStream, notifyUnbufferedIOSave); //throw FileError, X
+}
+
+
+namespace
+{
+struct LogTraverserCallback: public AFS::TraverserCallback
+{
+ LogTraverserCallback(const Zstring& prefix, const std::function<void()>& onUpdateStatus) :
+ prefix_(prefix),
+ onUpdateStatus_(onUpdateStatus) {}
+
+ void onFile(const FileInfo& fi) override
+ {
+ if (startsWith(fi.itemName, prefix_, CmpFilePath() /*even on Linux!*/) && endsWith(fi.itemName, Zstr(".log"), CmpFilePath()))
+ logFileNames_.push_back(fi.itemName);
+
+ if (onUpdateStatus_) onUpdateStatus_();
+ }
+ std::shared_ptr<TraverserCallback> onFolder (const FolderInfo& fi) override { return nullptr; }
+ HandleLink onSymlink(const SymlinkInfo& si) override { return TraverserCallback::LINK_SKIP; }
+
+ HandleError reportDirError (const std::wstring& msg, size_t retryNumber ) override { setError(msg); return ON_ERROR_CONTINUE; }
+ HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zstring& itemName) override { setError(msg); return ON_ERROR_CONTINUE; }
+
+ const std::vector<Zstring>& refFileNames() const { return logFileNames_; }
+ const Opt<FileError>& getLastError() const { return lastError_; }
+
+private:
+ void setError(const std::wstring& msg) //implement late failure
+ {
+ if (!lastError_)
+ lastError_ = FileError(msg);
+ }
+
+ const Zstring prefix_;
+ const std::function<void()> onUpdateStatus_;
+ std::vector<Zstring> logFileNames_; //out
+ Opt<FileError> lastError_;
+};
+}
+
+
+void fff::limitLogfileCount(const AbstractPath& logFolderPath, const std::wstring& jobname, size_t maxCount, //throw FileError
+ const std::function<void(const std::wstring& msg)>& notifyStatus)
+{
+ const Zstring prefix = utfTo<Zstring>(jobname);
+ const std::wstring cleaningMsg = _("Cleaning up old log files...");;
+
+ //traverse source directory one level deep
+ auto lt = std::make_shared<LogTraverserCallback>(prefix, [&] { if (notifyStatus) notifyStatus(cleaningMsg); });
+ const AFS::PathComponents pc = AFS::getPathComponents(logFolderPath);
+ AFS::traverseFolderParallel(pc.rootPath, {{ pc.relPath, lt }}, 1 /*parallelOps*/); //throw FileError
+
+ std::vector<Zstring> logFileNames = lt->refFileNames();
+ Opt<FileError> lastError = lt->getLastError();
+
+ if (logFileNames.size() > maxCount)
+ {
+ //delete oldest logfiles: take advantage of logfile naming convention to find them
+ std::nth_element(logFileNames.begin(), logFileNames.end() - maxCount, logFileNames.end(), LessFilePath());
+
+ std::for_each(logFileNames.begin(), logFileNames.end() - maxCount, [&](const Zstring& logFileName)
+ {
+ const AbstractPath filePath = AFS::appendRelPath(logFolderPath, logFileName);
+ if (notifyStatus) notifyStatus(cleaningMsg + L" " + fmtPath(AFS::getDisplayPath(filePath)));
+
+ try
+ {
+ AFS::removeFilePlain(filePath); //throw FileError
+ }
+ catch (const FileError& e) { if (!lastError) lastError = e; };
+ });
+ }
+
+ if (lastError) //late failure!
+ throw* lastError;
+}
diff --git a/FreeFileSync/Source/lib/generate_logfile.h b/FreeFileSync/Source/lib/generate_logfile.h
index 0eb85762..ecd1db1f 100755
--- a/FreeFileSync/Source/lib/generate_logfile.h
+++ b/FreeFileSync/Source/lib/generate_logfile.h
@@ -8,16 +8,13 @@
#define GENERATE_LOGFILE_H_931726432167489732164
#include <zen/error_log.h>
-#include <zen/file_io.h>
-#include <zen/format_unit.h>
#include "ffs_paths.h"
-#include "../fs/abstract.h"
#include "../file_hierarchy.h"
namespace fff
{
-struct SummaryInfo
+struct LogSummary
{
std::wstring jobName; //may be empty
std::wstring finalStatus;
@@ -28,11 +25,11 @@ struct SummaryInfo
int64_t totalTime = 0; //unit: [sec]
};
-void streamToLogFile(const SummaryInfo& summary, //throw FileError
+void streamToLogFile(const LogSummary& summary, //throw FileError
const zen::ErrorLog& log,
AFS::OutputStream& streamOut);
-void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError
+void saveToLastSyncsLog(const LogSummary& summary, //throw FileError
const zen::ErrorLog& log,
size_t maxBytesToWrite,
const std::function<void(const std::wstring& msg)>& notifyStatus);
@@ -40,169 +37,8 @@ void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError
inline Zstring getDefaultLogFolderPath() { return getConfigDirPathPf() + Zstr("Logs") ; }
-
-//####################### implementation #######################
-namespace
-{
-std::wstring generateLogHeader(const SummaryInfo& s)
-{
- using namespace zen;
- assert(s.itemsProcessed <= s.itemsTotal);
- assert(s.bytesProcessed <= s.bytesTotal);
-
- std::wstring output;
-
- //write header
- std::wstring headerLine = formatTime<std::wstring>(FORMAT_DATE);
- if (!s.jobName.empty())
- headerLine += L" | " + s.jobName;
- headerLine += L" | " + s.finalStatus;
-
- //assemble results box
- std::vector<std::wstring> results;
- results.push_back(headerLine);
- results.push_back(L"");
-
- const wchar_t tabSpace[] = L" ";
-
- std::wstring itemsProc = tabSpace + _("Items processed:") + L" " + formatNumber(s.itemsProcessed); //show always, even if 0!
- if (s.itemsProcessed != 0 || s.bytesProcessed != 0) //[!] don't show 0 bytes processed if 0 items were processed
- itemsProc += + L" (" + formatFilesizeShort(s.bytesProcessed) + L")";
- results.push_back(itemsProc);
-
- if (s.itemsTotal != 0 || s.bytesTotal != 0) //=: sync phase was reached and there were actual items to sync
- {
- if (s.itemsProcessed != s.itemsTotal ||
- s.bytesProcessed != s.bytesTotal)
- results.push_back(tabSpace + _("Items remaining:") + L" " + formatNumber(s.itemsTotal - s.itemsProcessed) + L" (" + formatFilesizeShort(s.bytesTotal - s.bytesProcessed) + L")");
- }
-
- results.push_back(tabSpace + _("Total time:") + L" " + copyStringTo<std::wstring>(wxTimeSpan::Seconds(s.totalTime).Format()));
-
- //calculate max width, this considers UTF-16 only, not true Unicode...but maybe good idea? those 2-char-UTF16 codes are usually wider than fixed width chars anyway!
- size_t sepLineLen = 0;
- for (const std::wstring& str : results) sepLineLen = std::max(sepLineLen, str.size());
-
- output.resize(output.size() + sepLineLen + 1, L'_');
- output += L'\n';
-
- for (const std::wstring& str : results) { output += L'|'; output += str; output += L'\n'; }
-
- output += L'|';
- output.resize(output.size() + sepLineLen, L'_');
- output += L'\n';
-
- return output;
-}
-}
-
-
-inline
-void streamToLogFile(const SummaryInfo& summary, //throw FileError
- const zen::ErrorLog& log,
- AFS::OutputStream& streamOut)
-{
- using namespace zen;
- const std::string header = replaceCpy(utfTo<std::string>(generateLogHeader(summary)), '\n', LINE_BREAK); //don't replace line break any earlier
-
- streamOut.write(&header[0], header.size()); //throw FileError, X
-
- //write log items in blocks instead of creating one big string: memory allocation might fail; think 1 million entries!
- std::string buffer;
- buffer += LINE_BREAK;
-
- for (const LogEntry& entry : log)
- {
- buffer += replaceCpy(utfTo<std::string>(formatMessage<std::wstring>(entry)), '\n', LINE_BREAK);
- buffer += LINE_BREAK;
-
- streamOut.write(&buffer[0], buffer.size()); //throw FileError, X
- buffer.clear();
- }
-}
-
-
-inline
-void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError
- const zen::ErrorLog& log,
- size_t maxBytesToWrite, //log may be *huge*, e.g. 1 million items; LastSyncs.log *must not* create performance problems!
- const std::function<void(const std::wstring& msg)>& notifyStatus)
-{
- using namespace zen;
- const Zstring filePath = getConfigDirPathPf() + Zstr("LastSyncs.log");
-
- Utf8String newStream = utfTo<Utf8String>(generateLogHeader(summary));
- replace(newStream, '\n', LINE_BREAK); //don't replace line break any earlier
- newStream += LINE_BREAK;
-
- //check size of "newStream": memory allocation might fail - think 1 million entries!
- for (const LogEntry& entry : log)
- {
- newStream += replaceCpy(utfTo<Utf8String>(formatMessage<std::wstring>(entry)), '\n', LINE_BREAK);
- newStream += LINE_BREAK;
-
- if (newStream.size() > maxBytesToWrite)
- {
- newStream += "[...]";
- newStream += LINE_BREAK;
- break;
- }
- }
-
- auto notifyUnbufferedIOLoad = [notifyStatus,
- bytesRead_ = int64_t(0),
- msg_ = replaceCpy(_("Loading file %x..."), L"%x", fmtPath(filePath))]
- (int64_t bytesDelta) mutable
- {
- if (notifyStatus)
- notifyStatus(msg_ + L" (" + formatFilesizeShort(bytesRead_ += bytesDelta) + L")"); /*throw X*/
- };
-
- auto notifyUnbufferedIOSave = [notifyStatus,
- bytesWritten_ = int64_t(0),
- msg_ = replaceCpy(_("Saving file %x..."), L"%x", fmtPath(filePath))]
- (int64_t bytesDelta) mutable
- {
- if (notifyStatus)
- notifyStatus(msg_ + L" (" + formatFilesizeShort(bytesWritten_ += bytesDelta) + L")"); /*throw X*/
- };
-
- //fill up the rest of permitted space by appending old log
- if (newStream.size() < maxBytesToWrite)
- {
- Utf8String oldStream;
- try
- {
- oldStream = loadBinContainer<Utf8String>(filePath, notifyUnbufferedIOLoad); //throw FileError, X
- //Note: we also report the loaded bytes via onUpdateSaveStatus()!
- }
- catch (FileError&) {}
-
- if (!oldStream.empty())
- {
- newStream += LINE_BREAK;
- newStream += LINE_BREAK;
- newStream += oldStream; //implicitly limited by "maxBytesToWrite"!
-
- //truncate size if required
- if (newStream.size() > maxBytesToWrite)
- {
- //but do not cut in the middle of a row
- auto it = std::search(newStream.cbegin() + maxBytesToWrite, newStream.cend(), std::begin(LINE_BREAK), std::end(LINE_BREAK) - 1);
- if (it != newStream.cend())
- {
- newStream.resize(it - newStream.cbegin());
- newStream += LINE_BREAK;
-
- newStream += "[...]";
- newStream += LINE_BREAK;
- }
- }
- }
- }
-
- saveBinContainer(filePath, newStream, notifyUnbufferedIOSave); //throw FileError, X
-}
+void limitLogfileCount(const AbstractPath& logFolderPath, const std::wstring& jobname, size_t maxCount, //throw FileError
+ const std::function<void(const std::wstring& msg)>& notifyStatus);
}
#endif //GENERATE_LOGFILE_H_931726432167489732164
diff --git a/FreeFileSync/Source/lib/hard_filter.cpp b/FreeFileSync/Source/lib/hard_filter.cpp
index a24dc65f..3008aa24 100755
--- a/FreeFileSync/Source/lib/hard_filter.cpp
+++ b/FreeFileSync/Source/lib/hard_filter.cpp
@@ -291,9 +291,9 @@ bool NameFilter::passDirFilter(const Zstring& relDirPath, bool* childItemMightMa
This is not a problem for folder traversal which stops at the first *childItemMightMatch == false anyway, but other code continues recursing further,
e.g. the database update code in db_file.cpp recurses unconditionally without filter check! It's possible to construct edge cases with incorrect
behavior if "childItemMightMatch" were not optional:
- 1. two folders including a sub folder with some files are in sync with up-to-date database files
- 2. deny access to this sub folder on both sides and start sync ignoring errors
- 3. => database entries of this sub folder are incorrectly deleted! (if sub-folder is excluded, but child items are not!)
+ 1. two folders including a subfolder with some files are in sync with up-to-date database files
+ 2. deny access to this subfolder on both sides and start sync ignoring errors
+ 3. => database entries of this subfolder are incorrectly deleted! (if sub-folder is excluded, but child items are not!)
*/
return false;
}
diff --git a/FreeFileSync/Source/lib/hard_filter.h b/FreeFileSync/Source/lib/hard_filter.h
index 0f312ea6..f829761e 100755
--- a/FreeFileSync/Source/lib/hard_filter.h
+++ b/FreeFileSync/Source/lib/hard_filter.h
@@ -42,7 +42,7 @@ public:
virtual bool isNull() const = 0; //filter is equivalent to NullFilter, but may be technically slower
- using FilterRef = std::shared_ptr<const HardFilter>; //always bound by design!
+ using FilterRef = std::shared_ptr<const HardFilter>; //always bound by design! Thread-safety: internally synchronized!
virtual FilterRef copyFilterAddingExclusion(const Zstring& excludePhrase) const = 0;
diff --git a/FreeFileSync/Source/lib/icon_buffer.cpp b/FreeFileSync/Source/lib/icon_buffer.cpp
index 7efe713c..15bd6f93 100755
--- a/FreeFileSync/Source/lib/icon_buffer.cpp
+++ b/FreeFileSync/Source/lib/icon_buffer.cpp
@@ -111,16 +111,16 @@ public:
interruptibleWait(conditionNewWork_, dummy, [this] { return !workLoad_.empty(); }); //throw ThreadInterruption
- AbstractPath filePath = workLoad_.back(); //
- workLoad_.pop_back(); //yes, no strong exception guarantee (std::bad_alloc)
- return filePath; //
+ AbstractPath filePath = workLoad_. back(); //yes, no strong exception guarantee (std::bad_alloc)
+ /**/ workLoad_.pop_back(); //
+ return filePath;
}
private:
//AbstractPath is thread-safe like an int!
- std::vector<AbstractPath> workLoad_; //processes last elements of vector first!
std::mutex lockFiles_;
std::condition_variable conditionNewWork_; //signal event: data for processing available
+ std::vector<AbstractPath> workLoad_; //processes last elements of vector first!
};
@@ -189,11 +189,11 @@ private:
struct IconData;
#ifdef __clang__ //workaround libc++ limitation for incomplete types: http://llvm.org/bugs/show_bug.cgi?id=17701
- using FileIconMap = std::map<AbstractPath, std::unique_ptr<IconData>, AFS::LessAbstractPath>;
+ using FileIconMap = std::map<AbstractPath, std::unique_ptr<IconData>>;
static IconData& refData(FileIconMap::iterator it) { return *(it->second); }
static std::unique_ptr<IconData> makeValueObject() { return std::make_unique<IconData>(); }
#else
- using FileIconMap = std::map<AbstractPath, IconData, AFS::LessAbstractPath>;
+ using FileIconMap = std::map<AbstractPath, IconData>;
IconData& refData(FileIconMap::iterator it) { return it->second; }
static IconData makeValueObject() { return IconData(); }
#endif
diff --git a/FreeFileSync/Source/lib/parallel_scan.cpp b/FreeFileSync/Source/lib/parallel_scan.cpp
index 702b7aac..954b6b07 100755
--- a/FreeFileSync/Source/lib/parallel_scan.cpp
+++ b/FreeFileSync/Source/lib/parallel_scan.cpp
@@ -154,56 +154,72 @@ std::vector<std::set<DirectoryKey>> separateByDistinctDisk(const std::set<Direct
*/
//------------------------------------------------------------------------------------------
-using BasicWString = Zbase<wchar_t>; //thread-safe string class for UI texts
-
class AsyncCallback //actor pattern
{
public:
- AsyncCallback(std::chrono::milliseconds cbInterval) : cbInterval_(cbInterval) {}
+ AsyncCallback(size_t threadsToFinish, std::chrono::milliseconds cbInterval) : threadsToFinish_(threadsToFinish), cbInterval_(cbInterval) {}
//blocking call: context of worker thread
FillBufferCallback::HandleError reportError(const std::wstring& msg, size_t retryNumber) //throw ThreadInterruption
{
assert(std::this_thread::get_id() != mainThreadId);
- std::unique_lock<std::mutex> dummy(lockErrorInfo_);
- interruptibleWait(conditionCanReportError_, dummy, [this] { return !errorInfo_ && !errorResponse_; }); //throw ThreadInterruption
+ std::unique_lock<std::mutex> dummy(lockRequest_);
+ interruptibleWait(conditionReadyForNewRequest_, dummy, [this] { return !errorRequest_ && !errorResponse_; }); //throw ThreadInterruption
- errorInfo_ = std::make_pair(copyStringTo<BasicWString>(msg), retryNumber);
+ errorRequest_ = std::make_pair(msg, retryNumber);
+ conditionNewRequest.notify_all();
- interruptibleWait(conditionGotResponse_, dummy, [this] { return static_cast<bool>(errorResponse_); }); //throw ThreadInterruption
+ interruptibleWait(conditionHaveResponse_, dummy, [this] { return static_cast<bool>(errorResponse_); }); //throw ThreadInterruption
FillBufferCallback::HandleError rv = *errorResponse_;
- errorInfo_ = NoValue();
+ errorRequest_ = NoValue();
errorResponse_ = NoValue();
dummy.unlock(); //optimization for condition_variable::notify_all()
- conditionCanReportError_.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
+ conditionReadyForNewRequest_.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
return rv;
}
- //context of main thread, call repreatedly
- void processErrors(FillBufferCallback& callback)
+ //context of main thread
+ void waitUntilDone(std::chrono::milliseconds duration, FillBufferCallback& callback) //throw X
{
assert(std::this_thread::get_id() == mainThreadId);
- std::unique_lock<std::mutex> dummy(lockErrorInfo_);
- if (errorInfo_ && !errorResponse_)
+ for (;;)
{
- errorResponse_ = callback.reportError(copyStringTo<std::wstring>(errorInfo_->first), errorInfo_->second); //throw!
+ const std::chrono::steady_clock::time_point callbackTime = std::chrono::steady_clock::now() + duration;
+
+ for (std::unique_lock<std::mutex> dummy(lockRequest_) ;;) //process all errors without delay
+ {
+ const bool rv = conditionNewRequest.wait_until(dummy, callbackTime, [this] { return (errorRequest_ && !errorResponse_) || (threadsToFinish_ == 0); });
+ if (!rv) //time-out + condition not met
+ break;
+
+ if (errorRequest_ && !errorResponse_)
+ {
+ assert(threadsToFinish_ != 0);
+ errorResponse_ = callback.reportError(errorRequest_->first, errorRequest_->second); //throw X
+ conditionHaveResponse_.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
+ }
+ if (threadsToFinish_ == 0)
+ {
+ dummy.unlock();
+ callback.reportStatus(getCurrentStatus(), itemsScanned_); //throw X; one last call for accurate stat-reporting!
+ return;
+ }
+ }
- dummy.unlock(); //optimization for condition_variable::notify_all()
- conditionGotResponse_.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
+ //call member functions outside of mutex scope:
+ callback.reportStatus(getCurrentStatus(), itemsScanned_); //throw X
}
}
- void incrementNotifyingThreadId() {assert(std::this_thread::get_id() == mainThreadId); ++notifyingThreadID_; } //context of main thread
-
- //perf optimization: comparison phase is 7% faster by avoiding needless std::wstring contstruction for reportCurrentFile()
- bool mayReportCurrentFile(int threadID, std::chrono::steady_clock::time_point& lastReportTime) const
+ //perf optimization: comparison phase is 7% faster by avoiding needless std::wstring construction for reportCurrentFile()
+ bool mayReportCurrentFile(int threadIdx, std::chrono::steady_clock::time_point& lastReportTime) const
{
- if (threadID != notifyingThreadID_) //only one thread at a time may report status
+ if (threadIdx != notifyingThreadIdx_) //only one thread at a time may report status: the first in sequential order
return false;
const auto now = std::chrono::steady_clock::now(); //0 on error
@@ -217,63 +233,88 @@ public:
return false;
}
- void reportCurrentFile(const std::wstring& filepath) //context of worker thread
+ void reportCurrentFile(const std::wstring& filePath) //context of worker thread
{
assert(std::this_thread::get_id() != mainThreadId);
std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
- currentFile_ = copyStringTo<BasicWString>(filepath);
+ currentFile_ = filePath;
}
- std::wstring getCurrentStatus() //context of main thread, call repreatedly
+ void incItemsScanned() { ++itemsScanned_; } //perf: irrelevant! scanning is almost entirely file I/O bound, not CPU bound! => no prob having multiple threads poking at the same variable!
+
+ void notifyWorkBegin(int threadIdx, const size_t parallelOps)
+ {
+ std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
+
+ const auto it = activeThreadIdxs_.emplace(threadIdx, parallelOps);
+ assert(it.second);
+ (void)it;
+
+ notifyingThreadIdx_ = activeThreadIdxs_.begin()->first;
+ }
+
+ void notifyWorkEnd(int threadIdx)
{
- assert(std::this_thread::get_id() == mainThreadId);
- std::wstring filepath;
{
std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
- filepath = copyStringTo<std::wstring>(currentFile_);
- }
- if (filepath.empty())
- return std::wstring();
+ const size_t no = activeThreadIdxs_.erase(threadIdx);
+ assert(no == 1);
+ (void)no;
- std::wstring statusText = copyStringTo<std::wstring>(textScanning_);
+ notifyingThreadIdx_ = activeThreadIdxs_.empty() ? 0 : activeThreadIdxs_.begin()->first;
+ }
+ {
+ std::lock_guard<std::mutex> dummy(lockRequest_);
+ assert(threadsToFinish_ > 0);
+ if (--threadsToFinish_ == 0)
+ conditionNewRequest.notify_all(); //perf: should unlock mutex before notify!? (insignificant)
+ }
+ }
- const long activeCount = activeWorker_;
- if (activeCount >= 2)
- statusText += L" [" + _P("1 thread", "%x threads", activeCount) + L"]";
+private:
+ std::wstring getCurrentStatus() //context of main thread, call repreatedly
+ {
+ assert(std::this_thread::get_id() == mainThreadId);
- statusText += L" ";
- statusText += filepath;
- return statusText;
- }
+ size_t parallelOpsTotal = 0;
+ std::wstring filePath;
+ {
+ std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
- void incItemsScanned() { ++itemsScanned_; } //perf: irrelevant! scanning is almost entirely file I/O bound, not CPU bound! => no prob having multiple threads poking at the same variable!
- long getItemsScanned() const { return itemsScanned_; }
+ for (const auto& item : activeThreadIdxs_)
+ parallelOpsTotal += item.second;
- void incActiveWorker() { ++activeWorker_; }
- void decActiveWorker() { --activeWorker_; }
- long getActiveWorker() const { return activeWorker_; }
+ filePath = currentFile_;
+ }
-private:
- //---- error handling ----
- std::mutex lockErrorInfo_;
- std::condition_variable conditionCanReportError_;
- std::condition_variable conditionGotResponse_;
- Opt<std::pair<BasicWString, size_t>> errorInfo_; //error message + retry number
+ std::wstring output = textScanning_;
+ if (parallelOpsTotal >= 2)
+ output += L"[" + _P("1 thread", "%x threads", parallelOpsTotal) + L"] ";
+ output += filePath;
+ return output;
+ }
+
+ //---- main <-> worker communication channel ----
+ std::mutex lockRequest_;
+ std::condition_variable conditionReadyForNewRequest_;
+ std::condition_variable conditionNewRequest;
+ std::condition_variable conditionHaveResponse_;
+ Opt<std::pair<std::wstring, size_t>> errorRequest_; //error message + retry number
Opt<FillBufferCallback::HandleError> errorResponse_;
+ size_t threadsToFinish_; //!= activeThreadIdxs_.size(), which may be 0 during worker thread construction!
//---- status updates ----
- std::atomic<int> notifyingThreadID_ { 0 }; //CAVEAT: do NOT use boost::thread::id: https://svn.boost.org/trac/boost/ticket/5754
-
std::mutex lockCurrentStatus_; //use a different lock for current file: continue traversing while some thread may process an error
- BasicWString currentFile_;
- const std::chrono::milliseconds cbInterval_;
+ std::wstring currentFile_;
+ std::map<int /*threadIdx*/, size_t /*parallelOps*/> activeThreadIdxs_;
- const BasicWString textScanning_ { copyStringTo<BasicWString>(_("Scanning:")) }; //this one is (currently) not shared and could be made a std::wstring, but we stay consistent and use thread-safe variables in this class only!
+ std::atomic<int> notifyingThreadIdx_ { 0 }; //CAVEAT: do NOT use boost::thread::id: https://svn.boost.org/trac/boost/ticket/5754
+ const std::chrono::milliseconds cbInterval_;
+ const std::wstring textScanning_ = _("Scanning:") + L" "; //this one is (currently) not shared
- //---- status updates II (lock free) ----
+ //---- status updates II (lock-free) ----
std::atomic<int> itemsScanned_{ 0 }; //std:atomic is uninitialized by default!
- std::atomic<int> activeWorker_{ 0 }; //
};
//-------------------------------------------------------------------------------------------------
@@ -288,8 +329,8 @@ struct TraverserConfig
std::map<Zstring, std::wstring, LessFilePath>& failedItemReads;
AsyncCallback& acb;
- const int threadID;
- std::chrono::steady_clock::time_point lastReportTime;
+ const int threadIdx;
+ std::chrono::steady_clock::time_point& lastReportTime; //thread-level
};
@@ -303,10 +344,10 @@ public:
cfg_(cfg),
parentRelPathPf_(parentRelPathPf),
output_(output),
- level_(level) {}
+ level_(level) {} //MUST NOT use cfg_ during construction! see BaseDirCallback()
virtual void onFile (const FileInfo& fi) override; //
- virtual std::unique_ptr<TraverserCallback> onFolder (const FolderInfo& fi) override; //throw ThreadInterruption
+ virtual std::shared_ptr<TraverserCallback> onFolder (const FolderInfo& fi) override; //throw ThreadInterruption
virtual HandleLink onSymlink(const SymlinkInfo& li) override; //
HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override; //throw ThreadInterruption
@@ -320,6 +361,33 @@ private:
};
+class BaseDirCallback : public DirCallback
+{
+public:
+ BaseDirCallback(const DirectoryKey& baseFolderKey, DirectoryValue& output,
+ AsyncCallback& acb, int threadIdx, std::chrono::steady_clock::time_point& lastReportTime) :
+ DirCallback(travCfg_ /*not yet constructed!!!*/, Zstring(), output.folderCont, 0 /*level*/),
+ travCfg_
+ {
+ baseFolderKey.folderPath,
+ baseFolderKey.filter,
+ baseFolderKey.handleSymlinks,
+ output.failedFolderReads,
+ output.failedItemReads,
+ acb,
+ threadIdx,
+ lastReportTime
+ }
+ {
+ if (acb.mayReportCurrentFile(threadIdx, lastReportTime))
+ acb.reportCurrentFile(AFS::getDisplayPath(baseFolderKey.folderPath)); //just in case first directory access is blocking
+ }
+
+private:
+ TraverserConfig travCfg_;
+};
+
+
void DirCallback::onFile(const FileInfo& fi) //throw ThreadInterruption
{
interruptionPoint(); //throw ThreadInterruption
@@ -332,7 +400,7 @@ void DirCallback::onFile(const FileInfo& fi) //throw ThreadInterruption
const Zstring fileRelPath = parentRelPathPf_ + fi.itemName;
//update status information no matter whether item is excluded or not!
- if (cfg_.acb.mayReportCurrentFile(cfg_.threadID, cfg_.lastReportTime))
+ if (cfg_.acb.mayReportCurrentFile(cfg_.threadIdx, cfg_.lastReportTime))
cfg_.acb.reportCurrentFile(AFS::getDisplayPath(AFS::appendRelPath(cfg_.baseFolderPath, fileRelPath)));
//------------------------------------------------------------------------------------
@@ -357,14 +425,14 @@ void DirCallback::onFile(const FileInfo& fi) //throw ThreadInterruption
}
-std::unique_ptr<AFS::TraverserCallback> DirCallback::onFolder(const FolderInfo& fi) //throw ThreadInterruption
+std::shared_ptr<AFS::TraverserCallback> DirCallback::onFolder(const FolderInfo& fi) //throw ThreadInterruption
{
interruptionPoint(); //throw ThreadInterruption
const Zstring& folderRelPath = parentRelPathPf_ + fi.itemName;
//update status information no matter whether item is excluded or not!
- if (cfg_.acb.mayReportCurrentFile(cfg_.threadID, cfg_.lastReportTime))
+ if (cfg_.acb.mayReportCurrentFile(cfg_.threadIdx, cfg_.lastReportTime))
cfg_.acb.reportCurrentFile(AFS::getDisplayPath(AFS::appendRelPath(cfg_.baseFolderPath, folderRelPath)));
//------------------------------------------------------------------------------------
@@ -388,7 +456,7 @@ std::unique_ptr<AFS::TraverserCallback> DirCallback::onFolder(const FolderInfo&
}, *this, fi.itemName))
return nullptr;
- return std::make_unique<DirCallback>(cfg_, folderRelPath + FILE_NAME_SEPARATOR, subFolder, level_ + 1);
+ return std::make_shared<DirCallback>(cfg_, folderRelPath + FILE_NAME_SEPARATOR, subFolder, level_ + 1);
}
@@ -399,7 +467,7 @@ DirCallback::HandleLink DirCallback::onSymlink(const SymlinkInfo& si) //throw Th
const Zstring& linkRelPath = parentRelPathPf_ + si.itemName;
//update status information no matter whether item is excluded or not!
- if (cfg_.acb.mayReportCurrentFile(cfg_.threadID, cfg_.lastReportTime))
+ if (cfg_.acb.mayReportCurrentFile(cfg_.threadIdx, cfg_.lastReportTime))
cfg_.acb.reportCurrentFile(AFS::getDisplayPath(AFS::appendRelPath(cfg_.baseFolderPath, linkRelPath)));
switch (cfg_.handleSymlinks)
@@ -417,7 +485,7 @@ DirCallback::HandleLink DirCallback::onSymlink(const SymlinkInfo& si) //throw Th
case SymLinkHandling::FOLLOW:
//filter symlinks before trying to follow them: handle user-excluded broken symlinks!
- //since we don't know yet what type the symlink will resolve to, only do this when both variants agree:
+ //since we don't know yet what type the symlink will resolve to, only do this when both filter variants agree:
if (!cfg_.filter->passFileFilter(linkRelPath))
{
bool childItemMightMatch = true;
@@ -466,69 +534,67 @@ DirCallback::HandleError DirCallback::reportItemError(const std::wstring& msg, s
}
-void fff::fillBuffer(const std::set<DirectoryKey>& keysToRead, //in
+void fff::fillBuffer(const std::set<DirectoryKey>& foldersToRead, //in
std::map<DirectoryKey, DirectoryValue>& buf, //out
+ const std::map<AbstractPath, size_t>& deviceParallelOps,
FillBufferCallback& callback,
std::chrono::milliseconds cbInterval)
{
buf.clear();
+ //aggregate folder paths that are on the same root device:
+ // => one worker thread *per device*: avoid excessive parallelism
+ // => parallel folder traversal considers "parallel file operations" as specified by user
+ // => (S)FTP: avoid hitting connection limits inadvertently
+ std::map<AbstractPath, std::set<DirectoryKey>> perDeviceFolders;
+
+ for (const DirectoryKey& key : foldersToRead)
+ perDeviceFolders[AFS::getPathComponents(key.folderPath).rootPath].insert(key);
+
//communication channel used by threads
- AsyncCallback acb(cbInterval); //manage life time: enclose InterruptibleThread's!!!
+ AsyncCallback acb(perDeviceFolders.size() /*threadsToFinish*/, cbInterval); //manage life time: enclose InterruptibleThread's!!!
FixedList<InterruptibleThread> worker;
-
- ZEN_ON_SCOPE_FAIL
- (
- for (InterruptibleThread& wt : worker)
- wt.interrupt(); //interrupt all first, then join
- for (InterruptibleThread& wt : worker)
- if (wt.joinable()) //= precondition of thread::join(), which throws an exception if violated!
- wt.join(); //in this context it is possible a thread is *not* joinable anymore due to the tryJoinFor() below!
- );
+ ZEN_ON_SCOPE_EXIT( for (InterruptibleThread& wt : worker) wt.join(); );
+ ZEN_ON_SCOPE_FAIL( for (InterruptibleThread& wt : worker) wt.interrupt(); ); //interrupt all first, then join
//init worker threads
- for (const DirectoryKey& key : keysToRead)
+ for (const auto& item : perDeviceFolders)
{
- DirectoryValue& dirOutput = buf[key];
-
- const int threadId = static_cast<int>(worker.size());
- worker.emplace_back([&outputContainer = dirOutput.folderCont,
- travCfg = TraverserConfig //shared by all(!) instances of DirCallback while traversing a folder hierarchy
- {
- key.folderPath,
- key.filter,
- key.handleSymlinks,
- dirOutput.failedFolderReads,
- dirOutput.failedItemReads,
- acb,
- threadId,
- }]() mutable
+ const AbstractPath& rootPath = item.first;
+ const int threadIdx = static_cast<int>(worker.size());
+
+#if !defined ZEN_LINUX || defined ZEN_LINUX_TRAVERSER_MODERN
+ auto itParOps = deviceParallelOps.find(rootPath);
+ const size_t parallelOps = std::max<size_t>(itParOps != deviceParallelOps.end() ? itParOps->second : 1, 1); //sanitize early for correct status display
+#elif defined ZEN_LINUX && defined ZEN_LINUX_TRAVERSER_LEGACY
+ const size_t parallelOps = 1;
+#endif
+ std::map<DirectoryKey, DirectoryValue*> workload;
+
+ for (const DirectoryKey& key : item.second)
+ workload.emplace(key, &buf[key]); //=> DirectoryValue* unshared for lock-free worker-thread access
+
+ worker.emplace_back([rootPath, workload, threadIdx, &acb, parallelOps]() mutable
{
- setCurrentThreadName("Folder Traverser");
+ setCurrentThreadName(("Traverser[" + numberTo<std::string>(threadIdx) + "]").c_str());
- travCfg.acb.incActiveWorker();
- ZEN_ON_SCOPE_EXIT(travCfg.acb.decActiveWorker());
+ acb.notifyWorkBegin(threadIdx, parallelOps);
+ ZEN_ON_SCOPE_EXIT(acb.notifyWorkEnd(threadIdx));
- if (travCfg.acb.mayReportCurrentFile(travCfg.threadID, travCfg.lastReportTime))
- travCfg.acb.reportCurrentFile(AFS::getDisplayPath(travCfg.baseFolderPath)); //just in case first directory access is blocking
+ std::chrono::steady_clock::time_point lastReportTime; //keep at thread-level!
- DirCallback cb(travCfg, Zstring(), outputContainer, 0);
+ AFS::TraverserWorkload travWorkload;
- AFS::traverseFolder(travCfg.baseFolderPath, cb); //throw ThreadInterruption
+ for (auto& wl : workload)
+ {
+ const AFS::PathComponents pc = AFS::getPathComponents(wl.first.folderPath);
+ assert(pc.rootPath == rootPath);
+ travWorkload.emplace_back(pc.relPath, std::make_shared<BaseDirCallback>(wl.first, *wl.second, acb, threadIdx, lastReportTime));
+ }
+ AFS::traverseFolderParallel(rootPath, travWorkload, parallelOps); //throw ThreadInterruption
});
}
- //wait until done
- for (InterruptibleThread& wt : worker)
- {
- do
- {
- callback.reportStatus(acb.getCurrentStatus(), acb.getItemsScanned()); //throw!
- acb.processErrors(callback);
- }
- while (!wt.tryJoinFor(cbInterval));
-
- acb.incrementNotifyingThreadId(); //process info messages of one thread at a time only
- }
+ acb.waitUntilDone(cbInterval, callback); //throw X
}
diff --git a/FreeFileSync/Source/lib/parallel_scan.h b/FreeFileSync/Source/lib/parallel_scan.h
index d28bb9d9..7e54428a 100755
--- a/FreeFileSync/Source/lib/parallel_scan.h
+++ b/FreeFileSync/Source/lib/parallel_scan.h
@@ -31,10 +31,9 @@ bool operator<(const DirectoryKey& lhs, const DirectoryKey& rhs)
if (lhs.handleSymlinks != rhs.handleSymlinks)
return lhs.handleSymlinks < rhs.handleSymlinks;
- if (AFS::LessAbstractPath()(lhs.folderPath, rhs.folderPath))
- return true;
- if (AFS::LessAbstractPath()(rhs.folderPath, lhs.folderPath))
- return false;
+ const int cmp = AbstractFileSystem::compareAbstractPath(lhs.folderPath, rhs.folderPath);
+ if (cmp != 0)
+ return cmp < 0;
return *lhs.filter < *rhs.filter;
}
@@ -43,6 +42,7 @@ bool operator<(const DirectoryKey& lhs, const DirectoryKey& rhs)
struct DirectoryValue
{
FolderContainer folderCont;
+
//relative names (or empty string for root) for directories that could not be read (completely), e.g. access denied, or temporal network drop
std::map<Zstring, std::wstring, LessFilePath> failedFolderReads; //with corresponding error message
@@ -64,10 +64,11 @@ struct FillBufferCallback
virtual void reportStatus(const std::wstring& msg, int itemsTotal ) = 0; //
};
-//attention: ensure directory filtering is applied later to exclude filtered directories which have been kept as parent folders
+//attention: ensure directory filtering is applied later to exclude filtered folders which have been kept as parent folders
-void fillBuffer(const std::set<DirectoryKey>& keysToRead, //in
+void fillBuffer(const std::set<DirectoryKey>& foldersToRead, //in
std::map<DirectoryKey, DirectoryValue>& buf, //out
+ const std::map<AbstractPath, size_t>& deviceParallelOps,
FillBufferCallback& callback,
std::chrono::milliseconds cbInterval);
}
diff --git a/FreeFileSync/Source/lib/parse_lng.h b/FreeFileSync/Source/lib/parse_lng.h
index 86faca28..b6d486e2 100755
--- a/FreeFileSync/Source/lib/parse_lng.h
+++ b/FreeFileSync/Source/lib/parse_lng.h
@@ -412,7 +412,7 @@ private:
translation = token().text;
nextToken();
}
- validateTranslation(original, translation); //throw throw ParsingError
+ validateTranslation(original, translation); //throw ParsingError
consumeToken(Token::TK_TRG_END);
out.emplace(original, translation);
@@ -725,8 +725,8 @@ std::string generateLng(const TranslationUnorderedList& in, const TransHeader& h
out += tokens.text(Token::TK_SRC_END) + '\n';
out += tokens.text(Token::TK_TRG_BEGIN);
- if (!forms.empty()) //translators will be searching for "<target></target>"
- out += '\n';
+ if (!forms.empty()) //translators will be searching for "<target></target>"
+ out += '\n';
for (std::string plForm : forms)
{
formatMultiLineText(plForm);
diff --git a/FreeFileSync/Source/lib/process_xml.cpp b/FreeFileSync/Source/lib/process_xml.cpp
index d7db65df..95cac76e 100755
--- a/FreeFileSync/Source/lib/process_xml.cpp
+++ b/FreeFileSync/Source/lib/process_xml.cpp
@@ -12,6 +12,7 @@
#include <zen/optional.h>
#include <wx/intl.h>
#include "ffs_paths.h"
+#include "../fs/concrete.h"
using namespace zen;
@@ -21,8 +22,8 @@ using namespace fff; //functionally needed for correct overload resolution!!!
namespace
{
//-------------------------------------------------------------------------------------------------------------------------------
-const int XML_FORMAT_VER_GLOBAL = 8; //2018-02-01
-const int XML_FORMAT_VER_FFS_CFG = 10; //2018-02-24
+const int XML_FORMAT_VER_GLOBAL = 9; //2018-03-14
+const int XML_FORMAT_VER_FFS_CFG = 11; //2018-04-14
//-------------------------------------------------------------------------------------------------------------------------------
}
@@ -955,12 +956,50 @@ void readConfig(const XmlIn& in, FilterConfig& filter, int formatVer)
}
-void readConfig(const XmlIn& in, LocalPairConfig& lpc, int formatVer)
+void readConfig(const XmlIn& in, LocalPairConfig& lpc, std::map<AbstractPath, size_t>& deviceParallelOps, int formatVer)
{
//read folder pairs
in["Left" ](lpc.folderPathPhraseLeft);
in["Right"](lpc.folderPathPhraseRight);
+ size_t parallelOpsL = 0;
+ size_t parallelOpsR = 0;
+
+ //TODO: remove old parameter after migration! 2018-04-14
+ if (formatVer < 11)
+ {
+ auto getParallelOps = [&](const Zstring& folderPathPhrase, size_t& parallelOps)
+ {
+ if (startsWith(folderPathPhrase, Zstr("sftp:"), CmpAsciiNoCase()) ||
+ startsWith(folderPathPhrase, Zstr( "ftp:"), CmpAsciiNoCase()))
+ {
+ for (const Zstring& optPhrase : split(folderPathPhrase, Zstr("|"), SplitType::SKIP_EMPTY))
+ if (startsWith(optPhrase, Zstr("con=")))
+ parallelOps = stringTo<int>(afterFirst(optPhrase, Zstr("con="), IF_MISSING_RETURN_NONE));
+ }
+ };
+ getParallelOps(lpc.folderPathPhraseLeft, parallelOpsL);
+ getParallelOps(lpc.folderPathPhraseRight, parallelOpsR);
+ }
+ else
+ {
+ if (const XmlElement* e = in["Left" ].get()) e->getAttribute("Threads", parallelOpsL); //try to get attributes:
+ if (const XmlElement* e = in["Right"].get()) e->getAttribute("Threads", parallelOpsR); // => *no error* if not available
+ //in["Left" ].attribute("Threads", parallelOpsL);
+ //in["Right"].attribute("Threads", parallelOpsR);
+ }
+
+ auto setParallelOps = [&](const Zstring& folderPathPhrase, size_t parallelOps)
+ {
+ if (parallelOps > 1)
+ {
+ const AbstractPath& rootPath = AFS::getPathComponents(createAbstractPath(folderPathPhrase)).rootPath;
+ deviceParallelOps[rootPath] = std::max(deviceParallelOps[rootPath], parallelOps);
+ }
+ };
+ setParallelOps(lpc.folderPathPhraseLeft, parallelOpsL);
+ setParallelOps(lpc.folderPathPhraseRight, parallelOpsR);
+
//TODO: remove after migration - 2016-07-24
auto ciReplace = [](Zstring& pathPhrase, const Zstring& oldTerm, const Zstring& newTerm) { pathPhrase = ciReplaceCpy(pathPhrase, oldTerm, newTerm); };
ciReplace(lpc.folderPathPhraseLeft, Zstr("%csidl_MyDocuments%"), Zstr("%csidl_Documents%"));
@@ -1023,9 +1062,9 @@ void readConfig(const XmlIn& in, MainConfiguration& mainCfg, int formatVer)
XmlIn inMain = formatVer < 10 ? in["MainConfig"] : in; //TODO: remove if parameter migration after some time! 2018-02-25
if (formatVer < 10) //TODO: remove if parameter migration after some time! 2018-02-25
- readConfig(inMain["Comparison"], mainCfg.cmpConfig);
+ readConfig(inMain["Comparison"], mainCfg.cmpCfg);
else
- readConfig(inMain["Compare"], mainCfg.cmpConfig);
+ readConfig(inMain["Compare"], mainCfg.cmpCfg);
//###########################################################
//read sync configuration
@@ -1043,22 +1082,21 @@ void readConfig(const XmlIn& in, MainConfiguration& mainCfg, int formatVer)
readConfig(inMain["Filter"], mainCfg.globalFilter, formatVer);
//###########################################################
- //read all folder pairs
- mainCfg.additionalPairs.clear();
-
+ //read folder pairs
bool firstItem = true;
for (XmlIn inPair = inMain["FolderPairs"]["Pair"]; inPair; inPair.next())
{
LocalPairConfig lpc;
- readConfig(inPair, lpc, formatVer);
+ readConfig(inPair, lpc, mainCfg.deviceParallelOps, formatVer);
if (firstItem)
{
firstItem = false;
- mainCfg.firstPair = lpc; //set first folder pair
+ mainCfg.firstPair = lpc;
+ mainCfg.additionalPairs.clear();
}
else
- mainCfg.additionalPairs.push_back(lpc); //set additional folder pairs
+ mainCfg.additionalPairs.push_back(lpc);
}
//TODO: remove if parameter migration after some time! 2017-10-24
@@ -1470,6 +1508,20 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& cfg, int formatVer)
//batch specific global settings
//XmlIn inBatch = in["Batch"];
+
+
+ //TODO: remove parameter migration after some time! 2018-03-14
+ if (formatVer < 9)
+ if (fastFromDIP(96) > 96) //high-DPI monitor => one-time migration
+ {
+ const XmlGlobalSettings defaultCfg;
+ cfg.gui.mainDlg.dlgSize = defaultCfg.gui.mainDlg.dlgSize;
+ cfg.gui.mainDlg.guiPerspectiveLast = defaultCfg.gui.mainDlg.guiPerspectiveLast;
+ cfg.gui.mainDlg.cfgGridColumnAttribs = defaultCfg.gui.mainDlg.cfgGridColumnAttribs;
+ cfg.gui.mainDlg.treeGridColumnAttribs = defaultCfg.gui.mainDlg.treeGridColumnAttribs;
+ cfg.gui.mainDlg.columnAttribLeft = defaultCfg.gui.mainDlg.columnAttribLeft;
+ cfg.gui.mainDlg.columnAttribRight = defaultCfg.gui.mainDlg.columnAttribRight;
+ }
}
@@ -1499,14 +1551,11 @@ void readConfig(const Zstring& filePath, XmlType type, ConfigType& cfg, int curr
checkForMappingErrors(in, filePath); //throw FileError
//(try to) migrate old configuration automatically
- if (formatVer< currentXmlFormatVer)
+ if (formatVer < currentXmlFormatVer)
try { fff::writeConfig(cfg, filePath); /*throw FileError*/ }
catch (FileError&) { assert(false); } //don't bother user!
}
- catch (const FileError& e)
- {
- warningMsg = e.toString();
- }
+ catch (const FileError& e) { warningMsg = e.toString(); }
}
}
@@ -1659,7 +1708,7 @@ void writeConfig(const FilterConfig& filter, XmlOut& out)
}
-void writeConfig(const LocalPairConfig& lpc, XmlOut& out)
+void writeConfig(const LocalPairConfig& lpc, const std::map<AbstractPath, size_t>& deviceParallelOps, XmlOut& out)
{
XmlOut outPair = out.ref().addChild("Pair");
@@ -1667,6 +1716,21 @@ void writeConfig(const LocalPairConfig& lpc, XmlOut& out)
outPair["Left" ](lpc.folderPathPhraseLeft);
outPair["Right"](lpc.folderPathPhraseRight);
+ auto getParallelOps = [&](const Zstring& folderPathPhrase)
+ {
+ const AbstractPath& rootPath = AFS::getPathComponents(createAbstractPath(folderPathPhrase)).rootPath;
+ auto itParOps = deviceParallelOps.find(rootPath);
+ return std::max<size_t>(itParOps != deviceParallelOps.end() ? itParOps->second : 1, 1);
+ };
+ const size_t parallelOpsL = getParallelOps(lpc.folderPathPhraseLeft);
+ const size_t parallelOpsR = getParallelOps(lpc.folderPathPhraseRight);
+
+ if (parallelOpsL > 1) outPair["Left" ].attribute("Threads", parallelOpsL);
+ if (parallelOpsR > 1) outPair["Right"].attribute("Threads", parallelOpsR);
+
+ //avoid "fake" changed configs by only storing "real" parallel-enabled devices in deviceParallelOps
+ assert(std::all_of(deviceParallelOps.begin(), deviceParallelOps.end(), [](const auto& item) { return item.second > 1; }));
+
//###########################################################
//alternate comp configuration (optional)
if (lpc.localCmpCfg)
@@ -1698,7 +1762,7 @@ void writeConfig(const MainConfiguration& mainCfg, XmlOut& out)
XmlOut outCmp = outMain["Compare"];
- writeConfig(mainCfg.cmpConfig, outCmp);
+ writeConfig(mainCfg.cmpCfg, outCmp);
//###########################################################
XmlOut outSync = outMain["Synchronize"];
@@ -1711,16 +1775,12 @@ void writeConfig(const MainConfiguration& mainCfg, XmlOut& out)
writeConfig(mainCfg.globalFilter, outFilter);
//###########################################################
- //write all folder pairs
-
XmlOut outFp = outMain["FolderPairs"];
+ //write folder pairs
+ writeConfig(mainCfg.firstPair, mainCfg.deviceParallelOps, outFp);
- //write first folder pair
- writeConfig(mainCfg.firstPair, outFp);
-
- //write additional folder pairs
for (const LocalPairConfig& lpc : mainCfg.additionalPairs)
- writeConfig(lpc, outFp);
+ writeConfig(lpc, mainCfg.deviceParallelOps, outFp);
outMain["Errors"].attribute("Ignore", mainCfg.ignoreErrors);
outMain["Errors"].attribute("Retry", mainCfg.automaticRetryCount);
diff --git a/FreeFileSync/Source/lib/status_handler.h b/FreeFileSync/Source/lib/status_handler.h
index 779c2c60..c84396ec 100755
--- a/FreeFileSync/Source/lib/status_handler.h
+++ b/FreeFileSync/Source/lib/status_handler.h
@@ -77,8 +77,8 @@ public:
refNumbers(numbersTotal_, currentPhase_) = { itemsTotal, bytesTotal };
}
- void updateProcessedData(int itemsDelta, int64_t bytesDelta) override { updateData(numbersCurrent_, itemsDelta, bytesDelta); } //note: these methods MUST NOT throw in order
- void updateTotalData (int itemsDelta, int64_t bytesDelta) override { updateData(numbersTotal_, itemsDelta, bytesDelta); } //to allow usage within destructors!
+ void updateDataProcessed(int itemsDelta, int64_t bytesDelta) override { updateData(numbersCurrent_, itemsDelta, bytesDelta); } //note: these methods MUST NOT throw in order
+ void updateDataTotal (int itemsDelta, int64_t bytesDelta) override { updateData(numbersTotal_, itemsDelta, bytesDelta); } //to allow usage within destructors!
void requestUiRefresh() override final //throw X
{
@@ -107,20 +107,12 @@ public:
void reportStatus(const std::wstring& text) override final //throw X
{
+ warn_static("std::wstring statusText_: perf issue?")
//assert(!text.empty()); -> possible: start of parallel scan
-
statusText_ = text; //update text *before* running operations that can throw
requestUiRefresh(); //throw X
}
- void reportInfo(const std::wstring& text) override //throw X
- {
- assert(!text.empty());
- statusText_ = text;
- requestUiRefresh(); //throw X
- //log text in derived class
- }
-
void abortProcessNow() override
{
if (!abortRequested_) abortRequested_ = AbortTrigger::PROGRAM;
diff --git a/FreeFileSync/Source/lib/status_handler_impl.h b/FreeFileSync/Source/lib/status_handler_impl.h
index eab46960..6404c915 100755
--- a/FreeFileSync/Source/lib/status_handler_impl.h
+++ b/FreeFileSync/Source/lib/status_handler_impl.h
@@ -15,7 +15,7 @@
namespace fff
{
template <typename Function> inline
-zen::Opt<std::wstring> tryReportingError(Function cmd, ProcessCallback& handler /*throw X*/) //return ignored error message if available
+zen::Opt<std::wstring> tryReportingError(Function cmd, ProcessCallback& cb /*throw X*/) //return ignored error message if available
{
for (size_t retryNumber = 0;; ++retryNumber)
try
@@ -25,7 +25,7 @@ zen::Opt<std::wstring> tryReportingError(Function cmd, ProcessCallback& handler
}
catch (zen::FileError& error)
{
- switch (handler.reportError(error.toString(), retryNumber)) //throw X
+ switch (cb.reportError(error.toString(), retryNumber)) //throw X
{
case ProcessCallback::IGNORE_ERROR:
return error.toString();
@@ -37,44 +37,46 @@ zen::Opt<std::wstring> tryReportingError(Function cmd, ProcessCallback& handler
//manage statistics reporting for a single item of work
-class StatisticsReporter
+class ItemStatReporter
{
public:
- StatisticsReporter(int itemsExpected, int64_t bytesExpected, ProcessCallback& cb) :
+ ItemStatReporter(int itemsExpected, int64_t bytesExpected, ProcessCallback& cb) :
itemsExpected_(itemsExpected),
bytesExpected_(bytesExpected),
cb_(cb) {}
- ~StatisticsReporter()
+ ~ItemStatReporter()
{
const bool scopeFail = getUncaughtExceptionCount() > exeptionCount_;
if (scopeFail)
- cb_.updateTotalData(itemsReported_, bytesReported_); //=> unexpected increase of total workload
+ cb_.updateDataTotal(itemsReported_, bytesReported_); //=> unexpected increase of total workload
else
//update statistics to consider the real amount of data, e.g. more than the "file size" for ADS streams,
//less for sparse and compressed files, or file changed in the meantime!
- cb_.updateTotalData(itemsReported_ - itemsExpected_, bytesReported_ - bytesExpected_); //noexcept!
+ cb_.updateDataTotal(itemsReported_ - itemsExpected_, bytesReported_ - bytesExpected_); //noexcept!
}
- void reportDelta(int itemsDelta, int64_t bytesDelta) //may throw!
+ void reportStatus(const std::wstring& text) { cb_.reportStatus(text); } //throw X
+
+ void reportDelta(int itemsDelta, int64_t bytesDelta) //throw X
{
- cb_.updateProcessedData(itemsDelta, bytesDelta); //nothrow! -> ensure client and service provider are in sync!
+ cb_.updateDataProcessed(itemsDelta, bytesDelta); //nothrow!
itemsReported_ += itemsDelta;
- bytesReported_ += bytesDelta; //
+ bytesReported_ += bytesDelta;
//special rule: avoid temporary statistics mess up, even though they are corrected anyway below:
if (itemsReported_ > itemsExpected_)
{
- cb_.updateTotalData(itemsReported_ - itemsExpected_, 0);
+ cb_.updateDataTotal(itemsReported_ - itemsExpected_, 0);
itemsReported_ = itemsExpected_;
}
if (bytesReported_ > bytesExpected_)
{
- cb_.updateTotalData(0, bytesReported_ - bytesExpected_); //=> everything above "bytesExpected" adds to both "processed" and "total" data
+ cb_.updateDataTotal(0, bytesReported_ - bytesExpected_); //=> everything above "bytesExpected" adds to both "processed" and "total" data
bytesReported_ = bytesExpected_;
}
- cb_.requestUiRefresh(); //may throw!
+ cb_.requestUiRefresh(); //throw X
}
private:
diff --git a/FreeFileSync/Source/lib/versioning.cpp b/FreeFileSync/Source/lib/versioning.cpp
index 9033bea8..9fb2804f 100755
--- a/FreeFileSync/Source/lib/versioning.cpp
+++ b/FreeFileSync/Source/lib/versioning.cpp
@@ -21,28 +21,28 @@ bool fff::impl::isMatchingVersion(const Zstring& shortname, const Zstring& short
auto it = shortnameVersioned.begin();
auto itLast = shortnameVersioned.end();
- auto nextDigit = [&]() -> bool
+ auto nextDigit = [&]()
{
if (it == itLast || !isDigit(*it))
return false;
++it;
return true;
};
- auto nextDigits = [&](size_t count) -> bool
+ auto nextDigits = [&](size_t count)
{
while (count-- > 0)
if (!nextDigit())
return false;
return true;
};
- auto nextChar = [&](Zchar c) -> bool
+ auto nextChar = [&](Zchar c)
{
if (it == itLast || *it != c)
return false;
++it;
return true;
};
- auto nextStringI = [&](const Zstring& str) -> bool //windows: ignore case!
+ auto nextStringI = [&](const Zstring& str) //windows: ignore case!
{
if (itLast - it < static_cast<ptrdiff_t>(str.size()) || !equalFilePath(str, Zstring(&*it, str.size())))
return false;
@@ -106,33 +106,42 @@ void moveExistingItemToVersioning(const AbstractPath& sourcePath, const Abstract
catch (FileError&) { deletionError = std::current_exception(); } //probably "not existing" error, defer evaluation
//overwrite AFS::ItemType::FOLDER with FILE? => highly dubious, do not allow
- auto fixedTargetPathIssues = [&] //throw FileError
+ auto fixTargetPathIssues = [&](const FileError& prevEx) //throw FileError
{
- Opt<AFS::PathStatus> pd;
- try { pd = AFS::getPathStatus(targetPath); /*throw FileError*/ }
- catch (FileError&)
+ Opt<AFS::PathStatus> psTmp;
+ try { psTmp = AFS::getPathStatus(targetPath); /*throw FileError*/ }
+ catch (const FileError& e) { throw FileError(prevEx.toString(), e.toString()); }
+ const AFS::PathStatus& ps = *psTmp;
+ //previous exception contains context-level information, but current exception is the immediate problem => combine both
+ //=> e.g. prevEx might be about missing parent folder; FFS considers session faulty and tries to create a new one,
+ //which might fail with: LIBSSH2_ERROR_AUTHENTICATION_FAILED (due to limit on #sessions?) https://www.freefilesync.org/forum/viewtopic.php?t=4765#p16016
+
+ if (ps.relPath.empty()) //already existing
{
- //previous exception is more relevant in general
- //BUT, we might be hiding a second unrelated issue: https://www.freefilesync.org/forum/viewtopic.php?t=4765#p16016
- //=> FFS considers session faulty and tries to create a new one, which might fail with: LIBSSH2_ERROR_AUTHENTICATION_FAILED
+ if (deletionError)
+ std::rethrow_exception(deletionError);
+ throw prevEx; //yes, slicing, but not relevant here
}
- if (pd)
- {
- if (pd->relPath.empty()) //already existing
+ //parent folder missing => create + retry
+ //parent folder existing => maybe created shortly after move attempt by parallel thread! => retry
+ AbstractPath intermediatePath = ps.existingPath;
+ for (const Zstring& itemName : std::vector<Zstring>(ps.relPath.begin(), ps.relPath.end() - 1))
+ try
{
- if (deletionError)
- std::rethrow_exception(deletionError);
+ AFS::createFolderPlain(intermediatePath = AFS::appendRelPath(intermediatePath, itemName)); //throw FileError
}
- else if (pd->relPath.size() > 1) //parent folder missing
+ catch (FileError&)
{
- AbstractPath intermediatePath = pd->existingPath;
- for (const Zstring& itemName : std::vector<Zstring>(pd->relPath.begin(), pd->relPath.end() - 1))
- AFS::createFolderPlain(intermediatePath = AFS::appendRelPath(intermediatePath, itemName)); //throw FileError
- return true;
+ try //already existing => possible, if moveExistingItemToVersioning() is run in parallel
+ {
+ if (AFS::getItemType(intermediatePath) != AFS::ItemType::FILE) //throw FileError
+ continue;
+ }
+ catch (FileError&) {}
+
+ throw;
}
- }
- return false;
};
try //first try to move directly without copying
@@ -146,20 +155,19 @@ void moveExistingItemToVersioning(const AbstractPath& sourcePath, const Abstract
{
copyNewItemPlain(); //throw FileError
}
- catch (FileError&)
+ catch (const FileError& e)
{
- if (!fixedTargetPathIssues()) //throw FileError
- throw;
+ fixTargetPathIssues(e); //throw FileError
+
//retry
copyNewItemPlain(); //throw FileError
}
//[!] remove source file AFTER handling target path errors!
AFS::removeFilePlain(sourcePath); //throw FileError
}
- catch (FileError&)
+ catch (const FileError& e)
{
- if (!fixedTargetPathIssues()) //throw FileError
- throw;
+ fixTargetPathIssues(e); //throw FileError
try //retry
{
@@ -184,7 +192,7 @@ struct FlatTraverserCallback: public AFS::TraverserCallback
private:
void onFile (const FileInfo& fi) override { files_ .push_back(fi); }
- std::unique_ptr<TraverserCallback> onFolder (const FolderInfo& fi) override { folders_ .push_back(fi); return nullptr; }
+ std::shared_ptr<TraverserCallback> onFolder (const FolderInfo& fi) override { folders_ .push_back(fi); return nullptr; }
HandleLink onSymlink(const SymlinkInfo& si) override { symlinks_.push_back(si); return TraverserCallback::LINK_SKIP; }
HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override { throw FileError(msg); }
@@ -198,7 +206,7 @@ private:
}
-bool FileVersioner::revisionFile(const FileDescriptor& fileDescr, const Zstring& relativePath, const IOCallback& notifyUnbufferedIO) //throw FileError
+bool FileVersioner::revisionFile(const FileDescriptor& fileDescr, const Zstring& relativePath, const IOCallback& notifyUnbufferedIO) const //throw FileError
{
const AbstractPath& filePath = fileDescr.path;
const AFS::StreamAttributes fileAttr{ fileDescr.attr.modTime, fileDescr.attr.fileSize, fileDescr.attr.fileId };
@@ -212,10 +220,10 @@ bool FileVersioner::revisionFile(const FileDescriptor& fileDescr, const Zstring&
else
moveExistingItemToVersioning(filePath, targetPath, [&] //throw FileError
{
- //target existing: copyFileTransactional() undefined behavior! (fail/overwrite/auto-rename) => not expected here:
+ //target existing: copyFileTransactional() undefined behavior! (fail/overwrite/auto-rename) => not expected, but possible if target deletion failed
/*const AFS::FileCopyResult result =*/ AFS::copyFileTransactional(filePath, fileAttr, targetPath, //throw FileError, ErrorFileLocked
false, //copyFilePermissions
- false, //transactionalCopy: not needed for versioning!
+ false, //transactionalCopy: not needed for versioning! partial copy will be overwritten next time
nullptr /*onDeleteTargetFile*/, notifyUnbufferedIO);
//result.errorModTime? => irrelevant for versioning!
});
@@ -226,7 +234,7 @@ bool FileVersioner::revisionFile(const FileDescriptor& fileDescr, const Zstring&
}
-bool FileVersioner::revisionSymlink(const AbstractPath& linkPath, const Zstring& relativePath) //throw FileError
+bool FileVersioner::revisionSymlink(const AbstractPath& linkPath, const Zstring& relativePath) const //throw FileError
{
if (AFS::getItemTypeIfExists(linkPath)) //throw FileError
{
@@ -242,8 +250,9 @@ bool FileVersioner::revisionSymlink(const AbstractPath& linkPath, const Zstring&
void FileVersioner::revisionFolder(const AbstractPath& folderPath, const Zstring& relativePath, //throw FileError
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFileMove,
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFolderMove,
- const IOCallback& notifyUnbufferedIO)
+ const IOCallback& notifyUnbufferedIO) const
{
+ //no error situation if directory is not existing! manual deletion relies on it!
if (Opt<AFS::ItemType> type = AFS::getItemTypeIfExists(folderPath)) //throw FileError
{
if (*type == AFS::ItemType::SYMLINK) //on Linux there is just one type of symlink, and since we do revision file symlinks, we should revision dir symlinks as well!
@@ -257,24 +266,26 @@ void FileVersioner::revisionFolder(const AbstractPath& folderPath, const Zstring
else
revisionFolderImpl(folderPath, relativePath, onBeforeFileMove, onBeforeFolderMove, notifyUnbufferedIO); //throw FileError
}
- //no error situation if directory is not existing! manual deletion relies on it!
+ else //even if the folder did not exist anymore, significant I/O work was done => report
+ if (onBeforeFolderMove) onBeforeFolderMove(AFS::getDisplayPath(folderPath), AFS::getDisplayPath(AFS::appendRelPath(versioningFolderPath_, relativePath)));
}
void FileVersioner::revisionFolderImpl(const AbstractPath& folderPath, const Zstring& relativePath, //throw FileError
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFileMove,
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFolderMove,
- const IOCallback& notifyUnbufferedIO)
+ const IOCallback& notifyUnbufferedIO) const
{
//create target directories only when needed in moveFileToVersioning(): avoid empty directories!
- FlatTraverserCallback ft(folderPath); //traverse source directory one level deep
- AFS::traverseFolder(folderPath, ft); //throw FileError
+ auto ft = std::make_shared<FlatTraverserCallback>(folderPath); //traverse source directory one level deep
+ const AFS::PathComponents pc = AFS::getPathComponents(folderPath);
+ AFS::traverseFolderParallel(pc.rootPath, {{ pc.relPath, ft }}, 1 /*parallelOps*/); //throw FileError
const Zstring relPathPf = appendSeparator(relativePath);
- for (const auto& fileInfo: ft.refFiles())
+ for (const auto& fileInfo: ft->refFiles())
{
const AbstractPath sourcePath = AFS::appendRelPath(folderPath, fileInfo.itemName);
const AbstractPath targetPath = generateVersionedPath(relPathPf + fileInfo.itemName);
@@ -288,13 +299,13 @@ void FileVersioner::revisionFolderImpl(const AbstractPath& folderPath, const Zst
//target existing: copyFileTransactional() undefined behavior! (fail/overwrite/auto-rename) => not expected here:
/*const AFS::FileCopyResult result =*/ AFS::copyFileTransactional(sourcePath, sourceAttr, targetPath, //throw FileError, ErrorFileLocked
false, //copyFilePermissions
- false, //transactionalCopy: not needed for versioning!
+ false, //transactionalCopy: not needed for versioning! partial copy will be overwritten next time
nullptr /*onDeleteTargetFile*/, notifyUnbufferedIO);
//result.errorModTime? => irrelevant for versioning!
});
}
- for (const auto& linkInfo: ft.refSymlinks())
+ for (const auto& linkInfo: ft->refSymlinks())
{
const AbstractPath sourcePath = AFS::appendRelPath(folderPath, linkInfo.itemName);
const AbstractPath targetPath = generateVersionedPath(relPathPf + linkInfo.itemName);
@@ -306,7 +317,7 @@ void FileVersioner::revisionFolderImpl(const AbstractPath& folderPath, const Zst
}
//move folders recursively
- for (const auto& folderInfo : ft.refFolders())
+ for (const auto& folderInfo : ft->refFolders())
revisionFolderImpl(AFS::appendRelPath(folderPath, folderInfo.itemName), //throw FileError
relPathPf + folderInfo.itemName,
onBeforeFileMove, onBeforeFolderMove, notifyUnbufferedIO);
@@ -319,7 +330,7 @@ void FileVersioner::revisionFolderImpl(const AbstractPath& folderPath, const Zst
/*
-void FileVersioner::limitVersions(std::function<void()> updateUI) //throw FileError
+void FileVersioner::limitVersions(const std::function<void()>& updateUI) //throw FileError
{
if (versionCountLimit_ < 0) //no limit!
return;
diff --git a/FreeFileSync/Source/lib/versioning.h b/FreeFileSync/Source/lib/versioning.h
index 4236aff2..78a031a0 100755
--- a/FreeFileSync/Source/lib/versioning.h
+++ b/FreeFileSync/Source/lib/versioning.h
@@ -24,9 +24,10 @@ namespace fff
- creates missing intermediate directories
- does not create empty directories
- handles symlinks
+ - multi-threading: internally synchronized
- replaces already existing target files/dirs (supports retry)
=> (unlikely) risk of data loss for naming convention "versioning":
- race-condition if two FFS instances start at the very same second OR multiple folder pairs process the same filepath!!
+ race-condition if multiple folder pairs process the same filepath!!
*/
class FileVersioner
@@ -48,28 +49,33 @@ public:
throw FileError(_("Unable to create time stamp for versioning:") + L" \"" + utfTo<std::wstring>(timeStamp_) + L"\"");
}
+ //multi-threaded access: internally synchronized!
bool revisionFile(const FileDescriptor& fileDescr, //throw FileError; return "false" if file is not existing
const Zstring& relativePath,
//called frequently if move has to revert to copy + delete => see zen::copyFile for limitations when throwing exceptions!
- const zen::IOCallback& notifyUnbufferedIO); //may be nullptr
+ const zen::IOCallback& notifyUnbufferedIO) const; //may be nullptr
- bool revisionSymlink(const AbstractPath& linkPath, const Zstring& relativePath); //throw FileError; return "false" if file is not existing
+ bool revisionSymlink(const AbstractPath& linkPath, const Zstring& relativePath) const; //throw FileError; return "false" if file is not existing
void revisionFolder(const AbstractPath& folderPath, const Zstring& relativePath, //throw FileError
//optional callbacks: may be nullptr
- const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFileMove, //one call for each *existing* object!
+ const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFileMove, //one call for each object!
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFolderMove, //
//called frequently if move has to revert to copy + delete => see zen::copyFile for limitations when throwing exceptions!
- const zen::IOCallback& notifyUnbufferedIO);
+ const zen::IOCallback& notifyUnbufferedIO) const;
+ //multi-threaded access: ?
//void limitVersions(std::function<void()> updateUI); //throw FileError; call when done revisioning!
private:
+ FileVersioner (const FileVersioner&) = delete;
+ FileVersioner& operator=(const FileVersioner&) = delete;
+
void revisionFolderImpl(const AbstractPath& folderPath, const Zstring& relativePath,
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFileMove,
const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFolderMove,
- const zen::IOCallback& notifyUnbufferedIO); //throw FileError
+ const zen::IOCallback& notifyUnbufferedIO) const; //throw FileError
AbstractPath generateVersionedPath(const Zstring& relativePath) const;
@@ -77,7 +83,7 @@ private:
const VersioningStyle versioningStyle_;
const Zstring timeStamp_;
- //std::vector<Zstring> fileRelNames; //store list of revisioned file and symlink relative names for limitVersions()
+ //Protected<std::vector<Zstring>> fileRelNames_; //list of revisioned file and symlink relative names for limitVersions()
};
namespace impl //declare for unit tests:
diff --git a/FreeFileSync/Source/process_callback.h b/FreeFileSync/Source/process_callback.h
index 7a792917..0a8f487e 100755
--- a/FreeFileSync/Source/process_callback.h
+++ b/FreeFileSync/Source/process_callback.h
@@ -38,8 +38,8 @@ struct ProcessCallback
//note: this one must NOT throw in order to properly allow undoing setting of statistics!
//it is in general paired with a call to requestUiRefresh() to compensate!
- virtual void updateProcessedData(int itemsDelta, int64_t bytesDelta) = 0; //noexcept!!
- virtual void updateTotalData (int itemsDelta, int64_t bytesDelta) = 0; //
+ virtual void updateDataProcessed(int itemsDelta, int64_t bytesDelta) = 0; //noexcept!!
+ virtual void updateDataTotal (int itemsDelta, int64_t bytesDelta) = 0; //
/* the estimated and actual total workload may change *during* sync:
1. file cannot be moved -> fallback to copy + delete
2. file copy, actual size changed after comparison
@@ -53,22 +53,29 @@ struct ProcessCallback
10. Error during file copy, retry: bytes were copied => increases total workload!
*/
- //opportunity to abort must be implemented in a frequently executed method like requestUiRefresh()
+ //opportunity to abort must be implemented in a frequently-executed method like requestUiRefresh()
virtual void requestUiRefresh() = 0; //throw X
virtual void forceUiRefresh () = 0; //throw X - called before starting long running tasks which don't update regularly
- //called periodically after data was processed: expected(!) to request GUI update
- virtual void reportStatus(const std::wstring& text) = 0; //throw X; UI info only, should not be logged!
+ //UI info only, should not be logged: called periodically after data was processed: expected(!) to request GUI update
+ virtual void reportStatus(const std::wstring& msg) = 0; //throw X
- //called periodically after data was processed: expected(!) to request GUI update
- virtual void reportInfo(const std::wstring& text) = 0; //throw X
+ //logging only, no status update!
+ virtual void logInfo(const std::wstring& msg) = 0;
+
+ //called periodically after data was processed
+ void reportInfo(const std::wstring& msg) //throw X
+ {
+ logInfo(msg);
+ reportStatus(msg); //throw X
+ }
virtual void reportWarning(const std::wstring& warningMessage, bool& warningActive) = 0; //throw X
//error handling:
enum Response
{
- IGNORE_ERROR = 10,
+ IGNORE_ERROR,
RETRY
};
virtual Response reportError (const std::wstring& errorMessage, size_t retryNumber) = 0; //throw X; recoverable error situation
diff --git a/FreeFileSync/Source/structures.cpp b/FreeFileSync/Source/structures.cpp
index 79c3029e..a882807f 100755
--- a/FreeFileSync/Source/structures.cpp
+++ b/FreeFileSync/Source/structures.cpp
@@ -187,14 +187,14 @@ std::wstring fff::getCompVariantName(const MainConfiguration& mainCfg)
{
const CompareVariant firstVariant = mainCfg.firstPair.localCmpCfg ?
mainCfg.firstPair.localCmpCfg->compareVar :
- mainCfg.cmpConfig.compareVar; //fallback to main sync cfg
+ mainCfg.cmpCfg.compareVar; //fallback to main sync cfg
//test if there's a deviating variant within the additional folder pairs
for (const LocalPairConfig& lpc : mainCfg.additionalPairs)
{
const CompareVariant thisVariant = lpc.localCmpCfg ?
lpc.localCmpCfg->compareVar :
- mainCfg.cmpConfig.compareVar; //fallback to main sync cfg
+ mainCfg.cmpCfg.compareVar; //fallback to main sync cfg
if (thisVariant != firstVariant)
return _("Multiple...");
}
@@ -493,7 +493,7 @@ MainConfiguration fff::merge(const std::vector<MainConfiguration>& mainCfgs)
for (LocalPairConfig& lpc : tmpCfgs)
{
if (!lpc.localCmpCfg)
- lpc.localCmpCfg = mainCfg.cmpConfig;
+ lpc.localCmpCfg = mainCfg.cmpCfg;
if (!lpc.localSyncCfg)
lpc.localSyncCfg = mainCfg.syncCfg;
@@ -568,13 +568,20 @@ MainConfiguration fff::merge(const std::vector<MainConfiguration>& mainCfgs)
lpc.localFilter = FilterConfig();
}
+ std::map<AbstractPath, size_t> mergedParallelOps;
+ for (const MainConfiguration& mainCfg : mainCfgs)
+ for (const auto& item : mainCfg.deviceParallelOps)
+ mergedParallelOps[item.first] = std::max(mergedParallelOps[item.first], item.second);
+
//final assembly
MainConfiguration cfgOut;
- cfgOut.cmpConfig = cmpCfgHead;
+ cfgOut.cmpCfg = cmpCfgHead;
cfgOut.syncCfg = syncCfgHead;
cfgOut.globalFilter = globalFilter;
cfgOut.firstPair = mergedCfgs[0];
cfgOut.additionalPairs.assign(mergedCfgs.begin() + 1, mergedCfgs.end());
+ cfgOut.deviceParallelOps = mergedParallelOps;
+
cfgOut.ignoreErrors = std::all_of(mainCfgs.begin(), mainCfgs.end(), [](const MainConfiguration& mainCfg) { return mainCfg.ignoreErrors; });
cfgOut.automaticRetryCount = std::max_element(mainCfgs.begin(), mainCfgs.end(),
diff --git a/FreeFileSync/Source/structures.h b/FreeFileSync/Source/structures.h
index 298eda76..2403cf8d 100755
--- a/FreeFileSync/Source/structures.h
+++ b/FreeFileSync/Source/structures.h
@@ -11,10 +11,13 @@
#include <memory>
#include <zen/zstring.h>
#include <zen/optional.h>
+#include "fs/abstract.h"
namespace fff
{
+using AFS = AbstractFileSystem;
+
enum class CompareVariant
{
TIME_SIZE,
@@ -331,13 +334,13 @@ struct LocalPairConfig //enhanced folder pairs with (optional) alternate configu
LocalPairConfig(const Zstring& phraseLeft,
const Zstring& phraseRight,
- const zen::Opt<CompConfig>& cmpConfig,
- const zen::Opt<SyncConfig>& syncConfig,
+ const zen::Opt<CompConfig>& cmpCfg,
+ const zen::Opt<SyncConfig>& syncCfg,
const FilterConfig& filter) :
folderPathPhraseLeft (phraseLeft),
folderPathPhraseRight(phraseRight),
- localCmpCfg(cmpConfig),
- localSyncCfg(syncConfig),
+ localCmpCfg(cmpCfg),
+ localSyncCfg(syncCfg),
localFilter(filter) {}
Zstring folderPathPhraseLeft; //unresolved directory names as entered by user!
@@ -371,13 +374,15 @@ enum class PostSyncCondition
struct MainConfiguration
{
- CompConfig cmpConfig; //global compare settings: may be overwritten by folder pair settings
+ CompConfig cmpCfg; //global compare settings: may be overwritten by folder pair settings
SyncConfig syncCfg; //global synchronisation settings: may be overwritten by folder pair settings
FilterConfig globalFilter; //global filter settings: combined with folder pair settings
LocalPairConfig firstPair; //there needs to be at least one pair!
std::vector<LocalPairConfig> additionalPairs;
+ std::map<AbstractPath /*device root*/, size_t /*parallel operations*/> deviceParallelOps; //should only include devices with >= 2 parallel ops
+
bool ignoreErrors = false; //true: errors will still be logged
size_t automaticRetryCount = 0;
size_t automaticRetryDelay = 5; //unit: [sec]
@@ -393,11 +398,12 @@ std::wstring getSyncVariantName(const MainConfiguration& mainCfg);
inline
bool operator==(const MainConfiguration& lhs, const MainConfiguration& rhs)
{
- return lhs.cmpConfig == rhs.cmpConfig &&
+ return lhs.cmpCfg == rhs.cmpCfg &&
lhs.syncCfg == rhs.syncCfg &&
lhs.globalFilter == rhs.globalFilter &&
lhs.firstPair == rhs.firstPair &&
lhs.additionalPairs == rhs.additionalPairs &&
+ lhs.deviceParallelOps == rhs.deviceParallelOps &&
lhs.ignoreErrors == rhs.ignoreErrors &&
lhs.automaticRetryCount == rhs.automaticRetryCount &&
lhs.automaticRetryDelay == rhs.automaticRetryDelay &&
diff --git a/FreeFileSync/Source/synchronization.cpp b/FreeFileSync/Source/synchronization.cpp
index a4d064d7..89c9684c 100755
--- a/FreeFileSync/Source/synchronization.cpp
+++ b/FreeFileSync/Source/synchronization.cpp
@@ -253,17 +253,19 @@ std::vector<FolderPairSyncCfg> fff::extractSyncCfg(const MainConfiguration& main
std::vector<FolderPairSyncCfg> output;
- //process all pairs
for (const LocalPairConfig& lpc : localCfgs)
{
- SyncConfig syncCfg = lpc.localSyncCfg ? *lpc.localSyncCfg : mainCfg.syncCfg;
+ //const CompConfig cmpCfg = lpc.localCmpCfg ? *lpc.localCmpCfg : mainCfg.cmpCfg;
+ const SyncConfig syncCfg = lpc.localSyncCfg ? *lpc.localSyncCfg : mainCfg.syncCfg;
output.push_back(
- FolderPairSyncCfg(syncCfg.directionCfg.var == DirectionConfig::TWO_WAY || detectMovedFilesEnabled(syncCfg.directionCfg),
- syncCfg.handleDeletion,
- syncCfg.versioningStyle,
- syncCfg.versioningFolderPhrase,
- syncCfg.directionCfg.var));
+ {
+ syncCfg.directionCfg.var == DirectionConfig::TWO_WAY || detectMovedFilesEnabled(syncCfg.directionCfg),
+ syncCfg.handleDeletion,
+ syncCfg.versioningStyle,
+ syncCfg.versioningFolderPhrase,
+ syncCfg.directionCfg.var
+ });
}
return output;
}
@@ -323,36 +325,466 @@ bool significantDifferenceDetected(const SyncStatistics& folderPairStat)
//#################################################################################################################
-class DeletionHandling //abstract deletion variants: permanently, recycle bin, user-defined directory
+//--------------------- data verification -------------------------
+void flushFileBuffers(const Zstring& nativeFilePath) //throw FileError
+{
+ const int fileHandle = ::open(nativeFilePath.c_str(), O_WRONLY | O_APPEND);
+ if (fileHandle == -1)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open file %x."), L"%x", fmtPath(nativeFilePath)), L"open");
+ ZEN_ON_SCOPE_EXIT(::close(fileHandle));
+
+ if (::fsync(fileHandle) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(nativeFilePath)), L"fsync");
+}
+
+
+void verifyFiles(const AbstractPath& sourcePath, const AbstractPath& targetPath, const IOCallback& notifyUnbufferedIO) //throw FileError
+{
+ try
+ {
+ //do like "copy /v": 1. flush target file buffers, 2. read again as usual (using OS buffers)
+ // => it seems OS buffers are not invalidated by this: snake oil???
+ if (Opt<Zstring> nativeTargetPath = AFS::getNativeItemPath(targetPath))
+ flushFileBuffers(*nativeTargetPath); //throw FileError
+
+ if (!filesHaveSameContent(sourcePath, targetPath, notifyUnbufferedIO)) //throw FileError
+ throw FileError(replaceCpy(replaceCpy(_("%x and %y have different content."),
+ L"%x", L"\n" + fmtPath(AFS::getDisplayPath(sourcePath))),
+ L"%y", L"\n" + fmtPath(AFS::getDisplayPath(targetPath))));
+ }
+ catch (const FileError& e) //add some context to error message
+ {
+ throw FileError(_("Data verification error:"), e.toString());
+ }
+}
+
+//#################################################################################################################
+//#################################################################################################################
+
+/* ________________________________________________________________
+ | |
+ | Multithreaded File Copy: Parallel API for expensive file I/O |
+ |______________________________________________________________| */
+
+namespace parallel
+{
+template <class Function> inline
+auto parallelScope(Function fun, std::mutex& singleThread) //throw X
+{
+ singleThread.unlock();
+ ZEN_ON_SCOPE_EXIT(singleThread.lock());
+
+ return fun(); //throw X
+}
+
+
+inline
+AFS::ItemType getItemType(const AbstractPath& ap, std::mutex& singleThread) //throw FileError
+{ return parallelScope([ap] { return AFS::getItemType(ap); /*throw FileError*/ }, singleThread); }
+
+inline
+Opt<AFS::ItemType> getItemTypeIfExists(const AbstractPath& ap, std::mutex& singleThread) //throw FileError
+{ return parallelScope([ap] { return AFS::getItemTypeIfExists(ap); /*throw FileError*/ }, singleThread); }
+
+inline
+bool removeFileIfExists(const AbstractPath& ap, std::mutex& singleThread) //throw FileError
+{ return parallelScope([ap] { return AFS::removeFileIfExists(ap); /*throw FileError*/ }, singleThread); }
+
+inline
+bool removeSymlinkIfExists(const AbstractPath& ap, std::mutex& singleThread) //throw FileError
+{ return parallelScope([ap] { return AFS::removeSymlinkIfExists(ap); /*throw FileError*/ }, singleThread); }
+
+inline
+void renameItem(const AbstractPath& apSource, const AbstractPath& apTarget, std::mutex& singleThread) //throw FileError, ErrorDifferentVolume
+{ parallelScope([apSource, apTarget] { AFS::renameItem(apSource, apTarget); /*throw FileError, ErrorDifferentVolume*/ }, singleThread); }
+
+inline
+AbstractPath getSymlinkResolvedPath(const AbstractPath& ap, std::mutex& singleThread) //throw FileError
+{ return parallelScope([ap] { return AFS::getSymlinkResolvedPath(ap); /*throw FileError*/ }, singleThread); }
+
+inline
+void copySymlink(const AbstractPath& apSource, const AbstractPath& apTarget, bool copyFilePermissions, std::mutex& singleThread) //throw FileError
+{ parallelScope([apSource, apTarget, copyFilePermissions] { AFS::copySymlink(apSource, apTarget, copyFilePermissions); /*throw FileError*/ }, singleThread); }
+
+inline
+void copyNewFolder(const AbstractPath& apSource, const AbstractPath& apTarget, bool copyFilePermissions, std::mutex& singleThread) //throw FileError
+{ parallelScope([apSource, apTarget, copyFilePermissions] { AFS::copyNewFolder(apSource, apTarget, copyFilePermissions); /*throw FileError*/ }, singleThread); }
+
+inline
+void removeFilePlain(const AbstractPath& ap, std::mutex& singleThread) //throw FileError
+{ parallelScope([ap] { AFS::removeFilePlain(ap); /*throw FileError*/ }, singleThread); }
+
+//--------------------------------------------------------------
+//ATTENTION CALLBACKS: they also run asynchronously *outside* the singleThread lock!
+//--------------------------------------------------------------
+inline
+void removeFolderIfExistsRecursion(const AbstractPath& ap, //throw FileError
+ const std::function<void (const std::wstring& displayPath)>& onBeforeFileDeletion, //optional
+ const std::function<void (const std::wstring& displayPath)>& onBeforeFolderDeletion, //one call for each object!
+ std::mutex& singleThread)
+{ parallelScope([ap, onBeforeFileDeletion, onBeforeFolderDeletion] { AFS::removeFolderIfExistsRecursion(ap, onBeforeFileDeletion, onBeforeFolderDeletion); /*throw FileError*/ }, singleThread); }
+
+
+inline
+AFS::FileCopyResult copyFileTransactional(const AbstractPath& apSource, const AFS::StreamAttributes& attrSource, //throw FileError, ErrorFileLocked
+ const AbstractPath& apTarget,
+ bool copyFilePermissions,
+ bool transactionalCopy,
+ const std::function<void()>& onDeleteTargetFile,
+ const IOCallback& notifyUnbufferedIO,
+ std::mutex& singleThread)
+{
+ return parallelScope([=]
+ {
+ return AFS::copyFileTransactional(apSource, attrSource, apTarget, copyFilePermissions, transactionalCopy, onDeleteTargetFile, notifyUnbufferedIO); //throw FileError, ErrorFileLocked
+ }, singleThread);
+}
+
+inline //RecycleSession::recycleItem() is internally synchronized!
+bool recycleItem(AFS::RecycleSession& recyclerSession, const AbstractPath& ap, const Zstring& logicalRelPath, std::mutex& singleThread) //throw FileError
+{ return parallelScope([=, &recyclerSession] { return recyclerSession.recycleItem(ap, logicalRelPath); /*throw FileError*/ }, singleThread); }
+
+inline //FileVersioner::revisionFile() is internally synchronized!
+bool revisionFile(FileVersioner& versioner, const FileDescriptor& fileDescr, const Zstring& relativePath, const IOCallback& notifyUnbufferedIO, std::mutex& singleThread) //throw FileError
+{ return parallelScope([=, &versioner] { return versioner.revisionFile(fileDescr, relativePath, notifyUnbufferedIO); /*throw FileError*/ }, singleThread); }
+
+inline //FileVersioner::revisionSymlink() is internally synchronized!
+bool revisionSymlink(FileVersioner& versioner, const AbstractPath& linkPath, const Zstring& relativePath, std::mutex& singleThread) //throw FileError
+{ return parallelScope([=, &versioner] { return versioner.revisionSymlink(linkPath, relativePath); /*throw FileError*/ }, singleThread); }
+
+inline //FileVersioner::revisionFolder() is internally synchronized!
+void revisionFolder(FileVersioner& versioner,
+ const AbstractPath& folderPath, const Zstring& relativePath, //throw FileError
+ const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFileMove,
+ const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFolderMove,
+ const IOCallback& notifyUnbufferedIO,
+ std::mutex& singleThread)
+{ parallelScope([=, &versioner] { versioner.revisionFolder(folderPath, relativePath, onBeforeFileMove, onBeforeFolderMove, notifyUnbufferedIO); /*throw FileError*/ }, singleThread); }
+
+inline
+void verifyFiles(const AbstractPath& apSource, const AbstractPath& apTarget, const IOCallback& notifyUnbufferedIO, std::mutex& singleThread) //throw FileError
+{ parallelScope([=] { ::verifyFiles(apSource, apTarget, notifyUnbufferedIO); /*throw FileError*/ }, singleThread); }
+
+}
+
+
+namespace
+{
+class AsyncCallback //actor pattern
{
public:
- DeletionHandling(const AbstractPath& baseFolderPath,
- DeletionPolicy handleDel, //nothrow!
- const Zstring& versioningFolderPhrase,
- VersioningStyle versioningStyle,
- const TimeComp& timeStamp,
- ProcessCallback& procCallback);
- ~DeletionHandling()
+ AsyncCallback(size_t threadCount) : threadStatus_(threadCount), totalThreadCount_(threadCount) {}
+
+ //non-blocking: context of worker thread
+ void updateDataProcessed(int itemsDelta, int64_t bytesDelta) //noexcept!!
{
- //always (try to) clean up, even if synchronization is aborted!
+ itemsDeltaProcessed_ += itemsDelta;
+ bytesDeltaProcessed_ += bytesDelta;
+ }
+ void updateDataTotal(int itemsDelta, int64_t bytesDelta) //noexcept!!
+ {
+ itemsDeltaTotal_ += itemsDelta;
+ bytesDeltaTotal_ += bytesDelta;
+ }
+
+ //context of main thread
+ void reportStats(ProcessCallback& cb)
+ {
+ assert(std::this_thread::get_id() == mainThreadId);
+
+ const std::pair<int, int64_t> deltaProcessed(itemsDeltaProcessed_, bytesDeltaProcessed_);
+ if (deltaProcessed.first != 0 || deltaProcessed.second != 0)
+ {
+ updateDataProcessed (-deltaProcessed.first, -deltaProcessed.second); //careful with these atomics: don't just set to 0
+ cb.updateDataProcessed( deltaProcessed.first, deltaProcessed.second); //noexcept!!
+ }
+ const std::pair<int, int64_t> deltaTotal(itemsDeltaTotal_, bytesDeltaTotal_);
+ if (deltaTotal.first != 0 || deltaTotal.second != 0)
+ {
+ updateDataTotal (-deltaTotal.first, -deltaTotal.second);
+ cb.updateDataTotal( deltaTotal.first, deltaTotal.second); //noexcept!!
+ }
+ }
+
+ //context of worker thread
+ void reportStatus(const std::wstring& msg, size_t threadIdx) //throw ThreadInterruption
+ {
+ assert(std::this_thread::get_id() != mainThreadId);
+ std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
+
+ assert(threadStatus_[threadIdx].active);
+ threadStatus_[threadIdx].statusMsg = msg;
+
+ interruptionPoint(); //throw ThreadInterruption
+ }
+
+ //context of main thread, call repreatedly
+ std::wstring getCurrentStatus()
+ {
+ assert(std::this_thread::get_id() == mainThreadId);
+
+ int activeThreadCount = 0;
+ std::wstring statusMsg;
+ {
+ std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
+
+ for (const ThreadStatus& ts : threadStatus_)
+ if (ts.active)
+ {
+ ++activeThreadCount;
+ if (statusMsg.empty())
+ statusMsg = ts.statusMsg;
+ }
+ }
+
+ std::wstring output;
+ if (activeThreadCount >= 2)
+ output = L"[" + _P("1 thread", "%x threads", activeThreadCount) + L"] ";
+ output += statusMsg;
+ return output;
+ }
+
+ //blocking call: context of worker thread
+ //=> indirect support for "pause": reportInfo() is called under singleThread lock,
+ // so all other worker threads will wait when coming out of parallel I/O (trying to lock singleThread)
+ void reportInfo(const std::wstring& msg, size_t threadIdx) //throw ThreadInterruption
+ {
+ reportStatus(msg, threadIdx); //throw ThreadInterruption
+ logInfo (msg, threadIdx); //
+ }
+
+ //blocking call: context of worker thread
+ void logInfo(const std::wstring& msg, size_t threadIdx) //throw ThreadInterruption
+ {
+ assert(std::this_thread::get_id() != mainThreadId);
+ std::unique_lock<std::mutex> dummy(lockRequest_);
+ interruptibleWait(conditionReadyForNewRequest_, dummy, [this] { return !logInfoRequest_; }); //throw ThreadInterruption
+
+ logInfoRequest_ = (totalThreadCount_ > 1 ? L"[" + numberTo<std::wstring>(threadIdx + 1) + L"] " : L"") + msg;
+
+ dummy.unlock(); //optimization for condition_variable::notify_all()
+ conditionNewRequest.notify_all();
+ }
+
+ //blocking call: context of worker thread
+ ProcessCallback::Response reportError(const std::wstring& msg, size_t retryNumber, size_t threadIdx) //throw ThreadInterruption
+ {
+ assert(std::this_thread::get_id() != mainThreadId);
+ std::unique_lock<std::mutex> dummy(lockRequest_);
+ interruptibleWait(conditionReadyForNewRequest_, dummy, [this] { return !errorRequest_ && !errorResponse_; }); //throw ThreadInterruption
+
+ errorRequest_ = ErrorInfo({ (totalThreadCount_ > 1 ? L"[" + numberTo<std::wstring>(threadIdx + 1) + L"] " : L"") + msg, retryNumber });
+ conditionNewRequest.notify_all();
+
+ interruptibleWait(conditionHaveResponse_, dummy, [this] { return static_cast<bool>(errorResponse_); }); //throw ThreadInterruption
+
+ ProcessCallback::Response rv = *errorResponse_;
+
+ errorRequest_ = NoValue();
+ errorResponse_ = NoValue();
+
+ dummy.unlock(); //optimization for condition_variable::notify_all()
+ conditionReadyForNewRequest_.notify_all(); //=> spurious wake-up for AsyncCallback::logInfo()
+ return rv;
+ }
+
+ //context of main thread
+ void waitUntilDone(std::chrono::milliseconds duration, ProcessCallback& cb) //throw X
+ {
+ assert(std::this_thread::get_id() == mainThreadId);
+ for (;;)
+ {
+ const std::chrono::steady_clock::time_point callbackTime = std::chrono::steady_clock::now() + duration;
+
+ for (std::unique_lock<std::mutex> dummy(lockRequest_) ;;) //process all errors without delay
+ {
+ const bool rv = conditionNewRequest.wait_until(dummy, callbackTime, [this] { return (errorRequest_ && !errorResponse_) || logInfoRequest_ || finishNowRequest_; });
+ if (!rv) //time-out + condition not met
+ break;
+
+ if (errorRequest_ && !errorResponse_)
+ {
+ assert(!finishNowRequest_);
+ errorResponse_ = cb.reportError(errorRequest_->msg, errorRequest_->retryNumber); //throw X
+ conditionHaveResponse_.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
+ }
+ if (logInfoRequest_)
+ {
+ cb.logInfo(*logInfoRequest_);
+ logInfoRequest_ = NoValue();
+ conditionReadyForNewRequest_.notify_all(); //=> spurious wake-up for AsyncCallback::reportError()
+ }
+ if (finishNowRequest_)
+ {
+ dummy.unlock(); //call member functions outside of mutex scope:
+ reportStats(cb); //one last call for accurate stat-reporting!
+ return;
+ }
+ }
+
+ //call member functions outside of mutex scope:
+ cb.reportStatus(getCurrentStatus()); //throw X
+ reportStats(cb);
+ }
+ }
+
+ void notifyWorkBegin(size_t threadIdx) //noexcept
+ {
+ std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
+ assert(!threadStatus_[threadIdx].active);
+ threadStatus_[threadIdx].active = true;
+ }
+
+ void notifyWorkEnd(size_t threadIdx) //noexcept
+ {
+ std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
+ assert(threadStatus_[threadIdx].active);
+ threadStatus_[threadIdx].active = false;
+ threadStatus_[threadIdx].statusMsg.clear();
+ }
+
+ void notifyAllDone() //noexcept
+ {
+ std::lock_guard<std::mutex> dummy(lockRequest_);
+ assert(!finishNowRequest_);
+ finishNowRequest_ = true;
+ conditionNewRequest.notify_all(); //perf: should unlock mutex before notify!? (insignificant)
+ }
+
+private:
+ AsyncCallback (const AsyncCallback&) = delete;
+ AsyncCallback& operator=(const AsyncCallback&) = delete;
+
+ struct ThreadStatus
+ {
+ bool active = false;
+ std::wstring statusMsg;
+ };
+ struct ErrorInfo
+ {
+ std::wstring msg;
+ size_t retryNumber = 0;
+ };
+
+ //---- main <-> worker communication channel ----
+ std::mutex lockRequest_;
+ std::condition_variable conditionReadyForNewRequest_;
+ std::condition_variable conditionNewRequest;
+ std::condition_variable conditionHaveResponse_;
+ Opt<ErrorInfo> errorRequest_;
+ Opt<ProcessCallback::Response> errorResponse_;
+ Opt<std::wstring> logInfoRequest_;
+ bool finishNowRequest_ = false;
+
+ //---- status updates ----
+ std::mutex lockCurrentStatus_; //use a different lock for current file: continue traversing while some thread may process an error
+ std::vector<ThreadStatus> threadStatus_;
+
+ const size_t totalThreadCount_;
+
+ //---- status updates II (lock-free) ----
+ std::atomic<int> itemsDeltaProcessed_{ 0 }; //
+ std::atomic<int64_t> bytesDeltaProcessed_{ 0 }; //std:atomic is uninitialized by default!
+ std::atomic<int> itemsDeltaTotal_ { 0 }; //
+ std::atomic<int64_t> bytesDeltaTotal_ { 0 }; //
+};
+
+
+template <typename Function> inline //return ignored error message if available
+Opt<std::wstring> tryReportingError(Function cmd, size_t threadIdx, AsyncCallback& acb) //throw ThreadInterruption
+{
+ for (size_t retryNumber = 0;; ++retryNumber)
try
{
- tryCleanup(false /*allowCallbackException*/); //throw FileError, (throw X)
+ cmd(); //throw FileError
+ return NoValue();
+ }
+ catch (FileError& error)
+ {
+ switch (acb.reportError(error.toString(), retryNumber, threadIdx)) //throw ThreadInterruption
+ {
+ case ProcessCallback::IGNORE_ERROR:
+ return error.toString();
+ case ProcessCallback::RETRY:
+ break; //continue with loop
+ }
+ }
+}
+
+
+//manage statistics reporting for a single item of work
+class AsyncItemStatReporter
+{
+public:
+ AsyncItemStatReporter(int itemsExpected, int64_t bytesExpected, size_t threadIdx, AsyncCallback& acb) :
+ itemsExpected_(itemsExpected),
+ bytesExpected_(bytesExpected),
+ threadIdx_(threadIdx),
+ acb_(acb) {}
+
+ ~AsyncItemStatReporter()
+ {
+ const bool scopeFail = getUncaughtExceptionCount() > exeptionCount_;
+ if (scopeFail)
+ acb_.updateDataTotal(itemsReported_, bytesReported_); //=> unexpected increase of total workload
+ else
+ //update statistics to consider the real amount of data, e.g. more than the "file size" for ADS streams,
+ //less for sparse and compressed files, or file changed in the meantime!
+ acb_.updateDataTotal(itemsReported_ - itemsExpected_, bytesReported_ - bytesExpected_); //noexcept!
+ }
+
+ void reportStatus(const std::wstring& text) { acb_.reportStatus(text, threadIdx_); } //throw ThreadInterruption
+
+ void reportDelta(int itemsDelta, int64_t bytesDelta) //throw ThreadInterruption
+ {
+ acb_.updateDataProcessed(itemsDelta, bytesDelta); //nothrow!
+ itemsReported_ += itemsDelta;
+ bytesReported_ += bytesDelta;
+
+ //special rule: avoid temporary statistics mess up, even though they are corrected anyway below:
+ if (itemsReported_ > itemsExpected_)
+ {
+ acb_.updateDataTotal(itemsReported_ - itemsExpected_, 0);
+ itemsReported_ = itemsExpected_;
+ }
+ if (bytesReported_ > bytesExpected_)
+ {
+ acb_.updateDataTotal(0, bytesReported_ - bytesExpected_); //=> everything above "bytesExpected" adds to both "processed" and "total" data
+ bytesReported_ = bytesExpected_;
}
- catch (FileError&) {}
- catch (...) { assert(false); } //what is this?
- /*
- may block heavily, but still do not allow user callback:
- -> avoid throwing user cancel exception again, leading to incomplete clean-up!
- */
+
+ interruptionPoint(); //throw ThreadInterruption
}
+private:
+ int itemsReported_ = 0;
+ int64_t bytesReported_ = 0;
+ const int itemsExpected_;
+ const int64_t bytesExpected_;
+ const size_t threadIdx_;
+ AsyncCallback& acb_;
+ const int exeptionCount_ = getUncaughtExceptionCount();
+};
+}
+
+//#################################################################################################################
+//#################################################################################################################
+
+class DeletionHandling //abstract deletion variants: permanently, recycle bin, user-defined directory
+{
+public:
+ DeletionHandling(const AbstractPath& baseFolderPath,
+ DeletionPolicy handleDel, //nothrow!
+ const Zstring& versioningFolderPhrase,
+ VersioningStyle versioningStyle,
+ const TimeComp& timeStamp);
+
//clean-up temporary directory (recycle bin optimization)
- void tryCleanup(bool allowCallbackException); //throw FileError; throw X -> call this in non-exceptional coding, i.e. somewhere after sync!
+ void tryCleanup(ProcessCallback& cb /*throw X*/, bool allowCallbackException); //throw FileError -> call this in non-exceptional code path, i.e. somewhere after sync!
- template <class Function> void removeFileWithCallback (const FileDescriptor& fileDescr, const Zstring& relativePath, Function onNotifyItemDeletion, const IOCallback& notifyUnbufferedIO); //
- template <class Function> void removeDirWithCallback (const AbstractPath& dirPath, const Zstring& relativePath, Function onNotifyItemDeletion, const IOCallback& notifyUnbufferedIO); //throw FileError
- template <class Function> void removeLinkWithCallback (const AbstractPath& linkPath, const Zstring& relativePath, Function onNotifyItemDeletion); //
+ void removeDirWithCallback (const AbstractPath& dirPath, const Zstring& relativePath, AsyncItemStatReporter& statReporter, std::mutex& singleThread); //
+ void removeFileWithCallback(const FileDescriptor& fileDescr, const Zstring& relativePath, AsyncItemStatReporter& statReporter, std::mutex& singleThread); //throw FileError, ThreadInterruption
+ void removeLinkWithCallback(const AbstractPath& linkPath, const Zstring& relativePath, AsyncItemStatReporter& statReporter, std::mutex& singleThread); //
const std::wstring& getTxtRemovingFile () const { return txtRemovingFile_; } //
const std::wstring& getTxtRemovingFolder () const { return txtRemovingFolder_; } //buffered status texts
@@ -365,7 +797,7 @@ private:
AFS::RecycleSession& getOrCreateRecyclerSession() //throw FileError => dont create in constructor!!!
{
assert(deletionPolicy_ == DeletionPolicy::RECYCLER);
- if (!recyclerSession_.get())
+ if (!recyclerSession_)
recyclerSession_ = AFS::createRecyclerSession(baseFolderPath_); //throw FileError
return *recyclerSession_;
}
@@ -373,13 +805,11 @@ private:
FileVersioner& getOrCreateVersioner() //throw FileError => dont create in constructor!!!
{
assert(deletionPolicy_ == DeletionPolicy::VERSIONING);
- if (!versioner_.get())
+ if (!versioner_)
versioner_ = std::make_unique<FileVersioner>(versioningFolderPath_, versioningStyle_, timeStamp_); //throw FileError
return *versioner_;
}
- ProcessCallback& procCallback_;
-
const DeletionPolicy deletionPolicy_; //keep it invariant! e.g. consider getOrCreateVersioner() one-time construction!
const AbstractPath baseFolderPath_;
@@ -392,71 +822,84 @@ private:
std::unique_ptr<FileVersioner> versioner_; //throw FileError in constructor => create on demand!
//buffer status texts:
- std::wstring txtRemovingFile_;
- std::wstring txtRemovingSymlink_;
- std::wstring txtRemovingFolder_;
-
- const std::wstring txtMovingFile_;
- const std::wstring txtMovingFolder_;
+ const std::wstring txtRemovingFile_;
+ const std::wstring txtRemovingSymlink_;
+ const std::wstring txtRemovingFolder_;
+ const std::wstring txtMovingFileXtoY_ = _("Moving file %x to %y");
+ const std::wstring txtMovingFolderXtoY_ = _("Moving folder %x to %y");
};
-DeletionHandling::DeletionHandling(const AbstractPath& baseFolderPath,
- DeletionPolicy handleDel, //nothrow!
+DeletionHandling::DeletionHandling(const AbstractPath& baseFolderPath, //nothrow!
+ DeletionPolicy handleDel,
const Zstring& versioningFolderPhrase,
VersioningStyle versioningStyle,
- const TimeComp& timeStamp,
- ProcessCallback& procCallback) :
- procCallback_(procCallback),
+ const TimeComp& timeStamp) :
deletionPolicy_(handleDel),
baseFolderPath_(baseFolderPath),
versioningFolderPath_(createAbstractPath(versioningFolderPhrase)),
versioningStyle_(versioningStyle),
timeStamp_(timeStamp),
- txtMovingFile_ (_("Moving file %x to %y")),
- txtMovingFolder_(_("Moving folder %x to %y"))
+ txtRemovingFile_([&]
{
- switch (deletionPolicy_)
+ switch (handleDel)
{
case DeletionPolicy::PERMANENT:
- txtRemovingFile_ = _("Deleting file %x" );
- txtRemovingFolder_ = _("Deleting folder %x" );
- txtRemovingSymlink_ = _("Deleting symbolic link %x");
- break;
-
+ return _("Deleting file %x");
case DeletionPolicy::RECYCLER:
- txtRemovingFile_ = _("Moving file %x to the recycle bin" );
- txtRemovingFolder_ = _("Moving folder %x to the recycle bin" );
- txtRemovingSymlink_ = _("Moving symbolic link %x to the recycle bin");
- break;
-
+ return _("Moving file %x to the recycle bin");
case DeletionPolicy::VERSIONING:
- txtRemovingFile_ = replaceCpy(_("Moving file %x to %y" ), L"%y", fmtPath(AFS::getDisplayPath(versioningFolderPath_)));
- txtRemovingFolder_ = replaceCpy(_("Moving folder %x to %y" ), L"%y", fmtPath(AFS::getDisplayPath(versioningFolderPath_)));
- txtRemovingSymlink_ = replaceCpy(_("Moving symbolic link %x to %y"), L"%y", fmtPath(AFS::getDisplayPath(versioningFolderPath_)));
- break;
+ return replaceCpy(_("Moving file %x to %y"), L"%y", fmtPath(AFS::getDisplayPath(versioningFolderPath_)));
}
-}
+ return std::wstring();
+}()),
+txtRemovingSymlink_([&]
+{
+ switch (handleDel)
+ {
+ case DeletionPolicy::PERMANENT:
+ return _("Deleting symbolic link %x");
+ case DeletionPolicy::RECYCLER:
+ return _("Moving symbolic link %x to the recycle bin");
+ case DeletionPolicy::VERSIONING:
+ return replaceCpy(_("Moving symbolic link %x to %y"), L"%y", fmtPath(AFS::getDisplayPath(versioningFolderPath_)));
+ }
+ return std::wstring();
+}()),
+txtRemovingFolder_([&]
+{
+ switch (handleDel)
+ {
+ case DeletionPolicy::PERMANENT:
+ return _("Deleting folder %x");
+ case DeletionPolicy::RECYCLER:
+ return _("Moving folder %x to the recycle bin");
+ case DeletionPolicy::VERSIONING:
+ return replaceCpy(_("Moving folder %x to %y"), L"%y", fmtPath(AFS::getDisplayPath(versioningFolderPath_)));
+ }
+ return std::wstring();
+}()) {}
-void DeletionHandling::tryCleanup(bool allowCallbackException) //throw FileError; throw X
+void DeletionHandling::tryCleanup(ProcessCallback& cb /*throw X*/, bool allowCallbackException) //throw FileError
{
+ assert(std::this_thread::get_id() == mainThreadId);
switch (deletionPolicy_)
{
case DeletionPolicy::PERMANENT:
break;
case DeletionPolicy::RECYCLER:
- if (recyclerSession_.get())
+ if (recyclerSession_)
{
auto notifyDeletionStatus = [&](const std::wstring& displayPath)
{
try
{
if (!displayPath.empty())
- procCallback_.reportStatus(replaceCpy(txtRemovingFile_, L"%x", fmtPath(displayPath))); //throw X
+ cb.reportStatus(replaceCpy(txtRemovingFile_, L"%x", fmtPath(displayPath))); //throw X
else
- procCallback_.requestUiRefresh(); //throw X
+ cb.requestUiRefresh(); //throw X
}
catch (...)
{
@@ -471,12 +914,12 @@ void DeletionHandling::tryCleanup(bool allowCallbackException) //throw FileError
break;
case DeletionPolicy::VERSIONING:
- //if (versioner.get())
+ //if (versioner_)
//{
// if (allowUserCallback)
// {
- // procCallback_.reportStatus(_("Removing old versions...")); //throw ?
- // versioner->limitVersions([&] { procCallback_.requestUiRefresh(); /*throw ? */ }); //throw FileError
+ // cb_.reportStatus(_("Removing old versions...")); //throw X
+ // versioner->limitVersions([&] { cb_.requestUiRefresh(); /*throw X */ }); //throw FileError
// }
// else
// versioner->limitVersions([] {}); //throw FileError
@@ -486,97 +929,104 @@ void DeletionHandling::tryCleanup(bool allowCallbackException) //throw FileError
}
-template <class Function>
-void DeletionHandling::removeDirWithCallback(const AbstractPath& folderPath,
+void DeletionHandling::removeDirWithCallback(const AbstractPath& folderPath,//throw FileError, ThreadInterruption
const Zstring& relativePath,
- Function onNotifyItemDeletion,
- const IOCallback& notifyUnbufferedIO) //throw FileError
+ AsyncItemStatReporter& statReporter, std::mutex& singleThread)
{
switch (deletionPolicy_)
{
case DeletionPolicy::PERMANENT:
{
- auto notifyDeletion = [&](const std::wstring& statusText, const std::wstring& displayPath)
+ //callbacks run *outside* singleThread_ lock! => fine
+ auto notifyDeletion = [&statReporter](const std::wstring& statusText, const std::wstring& displayPath)
{
- onNotifyItemDeletion(); //it would be more correct to report *after* work was done!
- procCallback_.reportStatus(replaceCpy(statusText, L"%x", fmtPath(displayPath)));
+ statReporter.reportStatus(replaceCpy(statusText, L"%x", fmtPath(displayPath))); //throw ThreadInterruption
+ statReporter.reportDelta(1, 0); //throw ThreadInterruption; it would be more correct to report *after* work was done!
};
+ static_assert(std::is_const<decltype(txtRemovingFile_)>::value, "callbacks better be thread-safe!");
auto onBeforeFileDeletion = [&](const std::wstring& displayPath) { notifyDeletion(txtRemovingFile_, displayPath); };
auto onBeforeDirDeletion = [&](const std::wstring& displayPath) { notifyDeletion(txtRemovingFolder_, displayPath); };
- AFS::removeFolderIfExistsRecursion(folderPath, onBeforeFileDeletion, onBeforeDirDeletion); //throw FileError
+ parallel::removeFolderIfExistsRecursion(folderPath, onBeforeFileDeletion, onBeforeDirDeletion, singleThread); //throw FileError
}
break;
case DeletionPolicy::RECYCLER:
- if (getOrCreateRecyclerSession().recycleItem(folderPath, relativePath)) //throw FileError
- onNotifyItemDeletion(); //moving to recycler is ONE logical operation, irrespective of the number of child elements!
+ parallel::recycleItem(getOrCreateRecyclerSession(), folderPath, relativePath, singleThread); //throw FileError
+ statReporter.reportDelta(1, 0); //throw ThreadInterruption; moving to recycler is ONE logical operation, irrespective of the number of child elements!
break;
case DeletionPolicy::VERSIONING:
{
- auto notifyMove = [&](const std::wstring& statusText, const std::wstring& displayPathFrom, const std::wstring& displayPathTo)
+ //callbacks run *outside* singleThread_ lock! => fine
+ auto notifyMove = [&statReporter](const std::wstring& statusText, const std::wstring& displayPathFrom, const std::wstring& displayPathTo)
{
- onNotifyItemDeletion(); //it would be more correct to report *after* work was done!
- procCallback_.reportStatus(replaceCpy(replaceCpy(statusText, L"%x", L"\n" + fmtPath(displayPathFrom)), L"%y", L"\n" + fmtPath(displayPathTo)));
+ statReporter.reportStatus(replaceCpy(replaceCpy(statusText, L"%x", L"\n" + fmtPath(displayPathFrom)), L"%y", L"\n" + fmtPath(displayPathTo))); //throw ThreadInterruption
+ statReporter.reportDelta(1, 0); //throw ThreadInterruption; it would be more correct to report *after* work was done!
};
- auto onBeforeFileMove = [&](const std::wstring& displayPathFrom, const std::wstring& displayPathTo) { notifyMove(txtMovingFile_, displayPathFrom, displayPathTo); };
- auto onBeforeFolderMove = [&](const std::wstring& displayPathFrom, const std::wstring& displayPathTo) { notifyMove(txtMovingFolder_, displayPathFrom, displayPathTo); };
+ static_assert(std::is_const<decltype(txtMovingFileXtoY_)>::value, "callbacks better be thread-safe!");
+ auto onBeforeFileMove = [&](const std::wstring& displayPathFrom, const std::wstring& displayPathTo) { notifyMove(txtMovingFileXtoY_, displayPathFrom, displayPathTo); };
+ auto onBeforeFolderMove = [&](const std::wstring& displayPathFrom, const std::wstring& displayPathTo) { notifyMove(txtMovingFolderXtoY_, displayPathFrom, displayPathTo); };
+ auto notifyUnbufferedIO = [&](int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; //throw ThreadInterruption
- getOrCreateVersioner().revisionFolder(folderPath, relativePath, onBeforeFileMove, onBeforeFolderMove, notifyUnbufferedIO); //throw FileError
+ parallel::revisionFolder(getOrCreateVersioner(), folderPath, relativePath, onBeforeFileMove, onBeforeFolderMove, notifyUnbufferedIO, singleThread); //throw FileError, ThreadInterruption
}
break;
}
}
-template <class Function>
-void DeletionHandling::removeFileWithCallback(const FileDescriptor& fileDescr,
+void DeletionHandling::removeFileWithCallback(const FileDescriptor& fileDescr, //throw FileError, ThreadInterruption
const Zstring& relativePath,
- Function onNotifyItemDeletion,
- const IOCallback& notifyUnbufferedIO) //throw FileError
+ AsyncItemStatReporter& statReporter, std::mutex& singleThread)
{
- bool deleted = false;
if (endsWith(relativePath, AFS::TEMP_FILE_ENDING)) //special rule for .ffs_tmp files: always delete permanently!
- deleted = AFS::removeFileIfExists(fileDescr.path); //throw FileError
+ parallel::removeFileIfExists(fileDescr.path, singleThread); //throw FileError
else
switch (deletionPolicy_)
{
case DeletionPolicy::PERMANENT:
- deleted = AFS::removeFileIfExists(fileDescr.path); //throw FileError
+ parallel::removeFileIfExists(fileDescr.path, singleThread); //throw FileError
break;
case DeletionPolicy::RECYCLER:
- deleted = getOrCreateRecyclerSession().recycleItem(fileDescr.path, relativePath); //throw FileError
+ parallel::recycleItem(getOrCreateRecyclerSession(), fileDescr.path, relativePath, singleThread); //throw FileError
break;
case DeletionPolicy::VERSIONING:
- deleted = getOrCreateVersioner().revisionFile(fileDescr, relativePath, notifyUnbufferedIO); //throw FileError
- break;
+ {
+ //callback runs *outside* singleThread_ lock! => fine
+ auto notifyUnbufferedIO = [&](int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; //throw ThreadInterruption
+
+ parallel::revisionFile(getOrCreateVersioner(), fileDescr, relativePath, notifyUnbufferedIO, singleThread); //throw FileError
+ }
+ break;
}
- if (deleted)
- onNotifyItemDeletion();
+
+ //even if the source item does not exist anymore, significant I/O work was done => report
+ //-> also consider unconditional statReporter.reportDelta(-1, 0) when overwriting a file
+ statReporter.reportDelta(1, 0); //throw ThreadInterruption
}
-template <class Function> inline
-void DeletionHandling::removeLinkWithCallback(const AbstractPath& linkPath, const Zstring& relativePath, Function onNotifyItemDeletion) //throw FileError
+void DeletionHandling::removeLinkWithCallback(const AbstractPath& linkPath, //throw FileError, throw ThreadInterruption
+ const Zstring& relativePath,
+ AsyncItemStatReporter& statReporter, std::mutex& singleThread)
{
- bool deleted = false;
-
switch (deletionPolicy_)
{
case DeletionPolicy::PERMANENT:
- deleted = AFS::removeSymlinkIfExists(linkPath); //throw FileError
+ parallel::removeSymlinkIfExists(linkPath, singleThread); //throw FileError
break;
case DeletionPolicy::RECYCLER:
- deleted = getOrCreateRecyclerSession().recycleItem(linkPath, relativePath); //throw FileError
+ parallel::recycleItem(getOrCreateRecyclerSession(), linkPath, relativePath, singleThread); //throw FileError
break;
case DeletionPolicy::VERSIONING:
- deleted = getOrCreateVersioner().revisionSymlink(linkPath, relativePath); //throw FileError
+ parallel::revisionSymlink(getOrCreateVersioner(), linkPath, relativePath, singleThread); //throw FileError
break;
}
- if (deleted)
- onNotifyItemDeletion();
+
+ //report unconditionally, see removeFileWithCallback()
+ statReporter.reportDelta(1, 0); //throw ThreadInterruption
}
//------------------------------------------------------------------------------------------------------------
@@ -664,82 +1114,88 @@ private:
};
//----------------------------------------------------------------------------------------
+class Workload;
-class SynchronizeFolderPair
+class FolderPairSyncer
{
public:
- SynchronizeFolderPair(ProcessCallback& procCallback,
- bool verifyCopiedFiles,
- bool copyFilePermissions,
- bool failSafeFileCopy,
- std::vector<FileError>& errorsModTime,
- DeletionHandling& delHandlingLeft,
- DeletionHandling& delHandlingRight) :
- procCallback_(procCallback),
- errorsModTime_(errorsModTime),
- delHandlingLeft_(delHandlingLeft),
- delHandlingRight_(delHandlingRight),
- verifyCopiedFiles_(verifyCopiedFiles),
- copyFilePermissions_(copyFilePermissions),
- failSafeFileCopy_(failSafeFileCopy) {}
-
- void startSync(BaseFolderPair& baseFolder)
- {
- runZeroPass(baseFolder); //first process file moves
- runPass<PASS_ONE>(baseFolder); //delete files (or overwrite big ones with smaller ones)
- runPass<PASS_TWO>(baseFolder); //copy rest
+ struct SyncCtx
+ {
+ bool verifyCopiedFiles;
+ bool copyFilePermissions;
+ bool failSafeFileCopy;
+ std::vector<FileError>& errorsModTime;
+ DeletionHandling& delHandlingLeft;
+ DeletionHandling& delHandlingRight;
+ size_t threadCount;
+ };
+
+ static void runSync(SyncCtx& syncCtx, BaseFolderPair& baseFolder, ProcessCallback& cb)
+ {
+ runPass(PASS_ZERO, syncCtx, baseFolder, cb); //prepare file moves
+ runPass(PASS_ONE, syncCtx, baseFolder, cb); //delete files (or overwrite big ones with smaller ones)
+ runPass(PASS_TWO, syncCtx, baseFolder, cb); //copy rest
}
private:
+ friend class Workload;
enum PassNo
{
- PASS_ONE, //delete files
- PASS_TWO, //create, modify
+ PASS_ZERO, //prepare file moves
+ PASS_ONE, //delete files
+ PASS_TWO, //create, modify
PASS_NEVER //skip item
};
+ FolderPairSyncer(SyncCtx& syncCtx, Workload& workload, std::mutex& singleThread, size_t threadIdx, AsyncCallback& acb) :
+ errorsModTime_ (syncCtx.errorsModTime),
+ delHandlingLeft_ (syncCtx.delHandlingLeft),
+ delHandlingRight_ (syncCtx.delHandlingRight),
+ verifyCopiedFiles_ (syncCtx.verifyCopiedFiles),
+ copyFilePermissions_(syncCtx.copyFilePermissions),
+ failSafeFileCopy_ (syncCtx.failSafeFileCopy),
+ workload_(workload),
+ singleThread_(singleThread),
+ threadIdx_(threadIdx),
+ acb_(acb) {}
+
static PassNo getPass(const FilePair& file);
static PassNo getPass(const SymlinkPair& link);
static PassNo getPass(const FolderPair& folder);
+ static void runPass(PassNo pass, SyncCtx& syncCtx, BaseFolderPair& baseFolder, ProcessCallback& cb); //throw X
+
+ static void appendFolderLevelWorkItems(PassNo pass, ContainerObject& hierObj, //in
+ std::vector<std::function<void(FolderPairSyncer& fps)>>& workItems, //out
+ std::vector<ContainerObject*>& foldersToProcess); //
+
template <SelectedSide side>
- void prepare2StepMove(FilePair& sourceObj, FilePair& targetObj); //throw FileError
- bool createParentFolder(FileSystemObject& fsObj); //throw FileError
+ void setup2StepMove(FilePair& sourceObj, FilePair& targetObj); //throw FileError, ThreadInterruption
+ bool createParentFolder(FileSystemObject& fsObj); //throw FileError, ThreadInterruption
template <SelectedSide side>
- void manageFileMove(FilePair& sourceObj, FilePair& targetObj); //throw FileError
+ void resolveMoveConflicts(FilePair& sourceObj, FilePair& targetObj); //throw FileError, ThreadInterruption
+ void prepareFileMove(FilePair& file); //throw ThreadInterruption
- void runZeroPass(ContainerObject& hierObj);
- template <PassNo pass>
- void runPass(ContainerObject& hierObj); //throw X
+ void synchronizeFile(FilePair& file); //
+ template <SelectedSide side> void synchronizeFileInt(FilePair& file, SyncOperation syncOp); //throw FileError, ThreadInterruption
- void synchronizeFile(FilePair& file);
- template <SelectedSide side> void synchronizeFileInt(FilePair& file, SyncOperation syncOp);
+ void synchronizeLink(SymlinkPair& link); //
+ template <SelectedSide sideTrg> void synchronizeLinkInt(SymlinkPair& link, SyncOperation syncOp); //throw FileError, ThreadInterruption
- void synchronizeLink(SymlinkPair& link);
- template <SelectedSide sideTrg> void synchronizeLinkInt(SymlinkPair& link, SyncOperation syncOp);
+ void synchronizeFolder(FolderPair& folder); //
+ template <SelectedSide sideTrg> void synchronizeFolderInt(FolderPair& folder, SyncOperation syncOp); //throw FileError, ThreadInterruption
- void synchronizeFolder(FolderPair& folder);
- template <SelectedSide sideTrg> void synchronizeFolderInt(FolderPair& folder, SyncOperation syncOp);
-
- void reportStatus(const std::wstring& rawText, const std::wstring& displayPath) const { procCallback_.reportStatus(replaceCpy(rawText, L"%x", fmtPath(displayPath))); }
- void reportInfo (const std::wstring& rawText, const std::wstring& displayPath) const { procCallback_.reportInfo (replaceCpy(rawText, L"%x", fmtPath(displayPath))); }
- void reportInfo (const std::wstring& rawText,
- const std::wstring& displayPath1,
- const std::wstring& displayPath2) const
+ void reportInfo(const std::wstring& rawText, const std::wstring& displayPath) { acb_.reportInfo(replaceCpy(rawText, L"%x", fmtPath(displayPath)), threadIdx_); }
+ void reportInfo(const std::wstring& rawText, const std::wstring& displayPath1, const std::wstring& displayPath2)
{
- procCallback_.reportInfo(replaceCpy(replaceCpy(rawText, L"%x", L"\n" + fmtPath(displayPath1)), L"%y", L"\n" + fmtPath(displayPath2)));
+ acb_.reportInfo(replaceCpy(replaceCpy(rawText, L"%x", L"\n" + fmtPath(displayPath1)), L"%y", L"\n" + fmtPath(displayPath2)), threadIdx_);
}
//target existing after onDeleteTargetFile(): undefined behavior! (fail/overwrite/auto-rename)
AFS::FileCopyResult copyFileWithCallback(const FileDescriptor& sourceDescr, //throw FileError
const AbstractPath& targetPath,
- const std::function<void()>& onDeleteTargetFile,
- const IOCallback& notifyUnbufferedIO) const;
-
- template <SelectedSide side>
- DeletionHandling& getDelHandling();
-
- ProcessCallback& procCallback_;
+ const std::function<void()>& onDeleteTargetFile, //optional!
+ AsyncItemStatReporter& statReporter);
std::vector<FileError>& errorsModTime_;
DeletionHandling& delHandlingLeft_;
@@ -749,24 +1205,218 @@ private:
const bool copyFilePermissions_;
const bool failSafeFileCopy_;
- //preload status texts
- 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" )};
+ Workload& workload_;
+ std::mutex& singleThread_;
+ const size_t threadIdx_;
+ AsyncCallback& acb_;
+
+ //preload status texts (premature?)
+ 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 txtUpdatingFile_ {_("Updating file %x" )};
+ const std::wstring txtUpdatingLink_ {_("Updating symbolic link %x")};
+ const std::wstring txtVerifyingFile_ {_("Verifying file %x" )};
+ const std::wstring txtUpdatingAttributes_{_("Updating attributes of %x")};
+ const std::wstring txtMovingFileXtoY_ {_("Moving file %x to %y" )};
+ const std::wstring txtSourceItemNotFound_{_("Source item %x not found" )};
};
//---------------------------------------------------------------------------------------------------------------
+/* ___________________________
+ | |
+ | Multithreaded File Copy |
+ |_________________________|
+
+ ---------------- =================
+ |Async Callback| <-- |Worker Thread 1|
+ ---------------- ====================
+ /|\ |Worker Thread 2|
+ | =================
+ ============= | ... |
+ GUI <-- |Main Thread| \|/ \|/
+Callback ============= -------------------------------
+ |Workload | folders to process|
+ -------------------------------
+
+Notes: - All threads share a single mutex, unlocked only during file I/O => do NOT require file_hierarchy.cpp classes to be thread-safe (i.e. internally synchronized)!
+ - Workload holds (folder-level-) items in buckets associated with each worker thread (FTP scenario: avoid CWDs)
+ - If a worker is idle, its Workload bucket is empty and no more folders to anaylze: steal from other buckets (=> take half of largest bucket)
+ - Maximize opportunity for parallelization ASAP: Workload buckets serve folder-items *before* files/symlinks => reduce risk of work-stealing
+ - Memory consumption: "Folders to process" may grow indefinitely; however: test case "C:\", 100.000 folders => worst case only ~ 800kB on x64
+*/
+
+class Workload
+{
+public:
+ Workload(FolderPairSyncer::PassNo pass, BaseFolderPair& baseFolder, size_t threadCount, AsyncCallback& acb) :
+ pass_(pass), acb_(acb), workload_(threadCount), foldersToProcess_{ &baseFolder } { assert(threadCount > 0); }
+
+ //blocking call: context of worker thread
+ std::function<void(FolderPairSyncer& fps)> getNext(size_t threadIdx) //throw ThreadInterruption
+ {
+ std::unique_lock<std::mutex> dummy(lockWork_);
+ for (;;)
+ {
+ for (;;)
+ {
+ if (!workload_[threadIdx].empty())
+ {
+ auto workItem = workload_[threadIdx]. back(); //yes, no strong exception guarantee (std::bad_alloc)
+ /**/ workload_[threadIdx].pop_back(); //
+ return workItem;
+ }
+ if (!foldersToProcess_.empty())
+ {
+ ContainerObject& hierObj = *foldersToProcess_. back();
+ /**/ foldersToProcess_.pop_back();
+
+ //thread-safe thanks to std::mutex singleThread:
+ FolderPairSyncer::appendFolderLevelWorkItems(pass_, hierObj, //in
+ workload_[threadIdx], //out, appending
+ foldersToProcess_); //
+ }
+ else
+ break;
+ }
+
+ //steal half of largest workload from other thread
+ WorkItems& items = *std::max_element(workload_.begin(), workload_.end(), [](const WorkItems& lhs, const WorkItems& rhs) { return lhs.size() < rhs.size(); });
+ if (!items.empty()) //=> != workload_[threadIdx]
+ {
+ size_t pos = 0;
+ erase_if(items, [&](const WorkItem& wi)
+ {
+ if (pos++ % 2 == 0)
+ {
+ workload_[threadIdx].push_back(wi);
+ return true;
+ }
+ return false;
+ });
+ auto workItem = workload_[threadIdx]. back(); //yes, no strong exception guarantee (std::bad_alloc)
+ /**/ workload_[threadIdx].pop_back(); //
+ return workItem;
+ }
+
+ if (++idleThreads_ == workload_.size())
+ acb_.notifyAllDone(); //noexcept
+ ZEN_ON_SCOPE_EXIT(--idleThreads_);
+
+ acb_.notifyWorkEnd(threadIdx);
+ ZEN_ON_SCOPE_EXIT(acb_.notifyWorkBegin(threadIdx));
+
+ auto haveNewWork = [&] { return !foldersToProcess_.empty() || std::any_of(workload_.begin(), workload_.end(), [](const WorkItems& wi) { return !wi.empty(); }); };
+
+ interruptibleWait(conditionNewWork_, dummy, [&] { return haveNewWork(); }); //throw ThreadInterruption
+ //it's sufficient to notify condition in addFolderToProcess() only (as long as we use std::condition_variable::notify_all())
+ }
+ }
+
+ void addFolderToProcess(ContainerObject& folder)
+ {
+ {
+ std::lock_guard<std::mutex> dummy(lockWork_);
+ foldersToProcess_.push_back(&folder);
+ }
+ conditionNewWork_.notify_all();
+ }
+
+private:
+ Workload (const Workload&) = delete;
+ Workload& operator=(const Workload&) = delete;
+
+ using WorkItem = std::function<void(FolderPairSyncer& fps) /*throw ThreadInterruption*/>;
+ using WorkItems = std::vector<WorkItem>;
+
+ const FolderPairSyncer::PassNo pass_;
+ AsyncCallback& acb_;
+
+ std::mutex lockWork_;
+ std::condition_variable conditionNewWork_;
+
+ size_t idleThreads_ = 0;
+
+ std::vector<WorkItems> workload_; //thread-specific buckets
+ std::vector<ContainerObject*> foldersToProcess_;
+};
+
+
+void FolderPairSyncer::runPass(PassNo pass, SyncCtx& syncCtx, BaseFolderPair& baseFolder, ProcessCallback& cb) //throw X
+{
+ const size_t threadCount = std::max<size_t>(syncCtx.threadCount, 1);
+
+ std::mutex singleThread; //only a single worker thread may run at a time, except for parallel file I/O
+
+ AsyncCallback acb(threadCount); //
+ Workload workload(pass, baseFolder, threadCount, acb); //manage life time: enclose InterruptibleThread's!!!
+
+ FixedList<InterruptibleThread> worker;
+
+ ZEN_ON_SCOPE_EXIT( for (InterruptibleThread& wt : worker) wt.join(); );
+ ZEN_ON_SCOPE_EXIT( for (InterruptibleThread& wt : worker) wt.interrupt(); ); //interrupt all first, then join
+
+ for (size_t threadIdx = 0; threadIdx < threadCount; ++threadIdx)
+ worker.emplace_back([fps = FolderPairSyncer(syncCtx, workload, singleThread, threadIdx, acb), threadIdx, &singleThread, &acb, &workload]() mutable
+ {
+ setCurrentThreadName(("Sync Worker[" + numberTo<std::string>(threadIdx) + "]").c_str());
+
+ acb.notifyWorkBegin(threadIdx);
+ ZEN_ON_SCOPE_EXIT(acb.notifyWorkEnd(threadIdx));
+
+ while (/*blocking call:*/ std::function<void(FolderPairSyncer& fps)> workItem = workload.getNext(threadIdx)) //throw ThreadInterruption
+ {
+ std::lock_guard<std::mutex> dummy(singleThread); //protect ALL accesses to "fps" and workItem execution!
+ workItem(fps); //throw ThreadInterruption
+ }
+ });
+
+ acb.waitUntilDone(UI_UPDATE_INTERVAL / 2 /*every ~50 ms*/, cb); //throw X
+}
+
+
+void FolderPairSyncer::appendFolderLevelWorkItems(PassNo pass, ContainerObject& hierObj, //in
+ std::vector<std::function<void(FolderPairSyncer& fps)>>& workItems, //out
+ std::vector<ContainerObject* >& foldersToProcess) //
+{
+ const size_t itemCountOld = workItems.size();
+ const size_t folderCountOld = foldersToProcess.size();
+
+ //synchronize folders:
+ for (FolderPair& folder : hierObj.refSubFolders())
+ if (pass == getPass(folder))
+ workItems.push_back([&folder](FolderPairSyncer& fps)
+ {
+ tryReportingError([&] { fps.synchronizeFolder(folder); }, fps.threadIdx_, fps.acb_); //throw ThreadInterruption
+ fps.workload_.addFolderToProcess(folder);
+ warn_static("unnatural processing order!?")
+ });
+ else
+ foldersToProcess.push_back(&folder);
+
+ //synchronize files:
+ for (FilePair& file : hierObj.refSubFiles())
+ if (pass == PASS_ZERO)
+ workItems.push_back([&file](FolderPairSyncer& fps) { fps.prepareFileMove(file); /*throw ThreadInterruption*/ });
+ else if (pass == getPass(file))
+ workItems.push_back([&file](FolderPairSyncer& fps)
+ {
+ tryReportingError([&] { fps.synchronizeFile(file); }, fps.threadIdx_, fps.acb_); //throw ThreadInterruption
+ });
+
+ //synchronize symbolic links:
+ for (SymlinkPair& symlink : hierObj.refSubLinks())
+ if (pass == getPass(symlink))
+ workItems.push_back([&symlink](FolderPairSyncer& fps)
+ {
+ tryReportingError([&] { fps.synchronizeLink(symlink); }, fps.threadIdx_, fps.acb_); //throw ThreadInterruption
+ });
-template <> inline
-DeletionHandling& SynchronizeFolderPair::getDelHandling<LEFT_SIDE>() { return delHandlingLeft_; }
+ //ensure natural processing order despite LIFO:
+ std::reverse(workItems .begin() + itemCountOld, workItems .end());
+ std::reverse(foldersToProcess.begin() + folderCountOld, foldersToProcess.end());
+}
-template <> inline
-DeletionHandling& SynchronizeFolderPair::getDelHandling<RIGHT_SIDE>() { return delHandlingRight_; }
/*
__________________________
@@ -806,8 +1456,8 @@ bool haveNameClash(const Zstring& shortname, List& m)
template <SelectedSide side>
-void SynchronizeFolderPair::prepare2StepMove(FilePair& sourceObj,
- FilePair& targetObj) //throw FileError
+void FolderPairSyncer::setup2StepMove(FilePair& sourceObj, //throw FileError, ThreadInterruption
+ FilePair& targetObj)
{
//generate (hopefully) unique file name to avoid clashing with some remnant ffs_tmp file
const Zstring shortGuid = printNumber<Zstring>(Zstr("%04x"), static_cast<unsigned int>(getCrc16(generateGUID())));
@@ -822,11 +1472,11 @@ void SynchronizeFolderPair::prepare2StepMove(FilePair& sourceObj,
const AbstractPath sourcePathTmp = AFS::appendRelPath(sourceObj.base().getAbstractPath<side>(), sourceRelPathTmp);
- reportInfo(txtMovingFile,
+ reportInfo(txtMovingFileXtoY_, //ThreadInterruption
AFS::getDisplayPath(sourceObj.getAbstractPath<side>()),
AFS::getDisplayPath(sourcePathTmp));
- AFS::renameItem(sourceObj.getAbstractPath<side>(), sourcePathTmp); //throw FileError, (ErrorDifferentVolume)
+ parallel::renameItem(sourceObj.getAbstractPath<side>(), sourcePathTmp, singleThread_); //throw FileError, (ErrorDifferentVolume)
//TODO: prepare2StepMove: consider ErrorDifferentVolume! e.g. symlink aliasing!
@@ -844,11 +1494,12 @@ void SynchronizeFolderPair::prepare2StepMove(FilePair& sourceObj,
tempFile .setMoveRef(targetObj.getId());
//NO statistics update!
- procCallback_.requestUiRefresh(); //throw ?
+ interruptionPoint(); //throw ThreadInterruption
}
-bool SynchronizeFolderPair::createParentFolder(FileSystemObject& fsObj) //throw FileError, "false" on name clash
+//return "false" on name clash
+bool FolderPairSyncer::createParentFolder(FileSystemObject& fsObj) //throw FileError, ThreadInterruption
{
if (auto parentFolder = dynamic_cast<FolderPair*>(&fsObj.parent()))
{
@@ -866,20 +1517,20 @@ bool SynchronizeFolderPair::createParentFolder(FileSystemObject& fsObj) //throw
assert(parentFolder->getSyncOperation() != SO_DELETE_LEFT &&
parentFolder->getSyncOperation() != SO_DELETE_RIGHT);
- synchronizeFolder(*parentFolder); //throw FileError
+ synchronizeFolder(*parentFolder); //throw FileError, ThreadInterruption
}
return true;
}
template <SelectedSide side>
-void SynchronizeFolderPair::manageFileMove(FilePair& sourceFile,
- FilePair& targetFile) //throw FileError
+void FolderPairSyncer::resolveMoveConflicts(FilePair& sourceFile, //throw FileError, ThreadInterruption
+ FilePair& targetFile)
{
assert((sourceFile.getSyncOperation() == SO_MOVE_LEFT_FROM && targetFile.getSyncOperation() == SO_MOVE_LEFT_TO && side == LEFT_SIDE) ||
(sourceFile.getSyncOperation() == SO_MOVE_RIGHT_FROM && targetFile.getSyncOperation() == SO_MOVE_RIGHT_TO && side == RIGHT_SIDE));
- const bool sourceWillBeDeleted = [&]() -> bool
+ const bool sourceWillBeDeleted = [&]
{
if (auto parentFolder = dynamic_cast<const FolderPair*>(&sourceFile.parent()))
{
@@ -917,83 +1568,76 @@ void SynchronizeFolderPair::manageFileMove(FilePair& sourceFile,
{
//prepare for move now: - revert to 2-step move on name clashes
if (haveNameClash(targetFile) ||
- !createParentFolder(targetFile)) //throw FileError
- return prepare2StepMove<side>(sourceFile, targetFile); //throw FileError
+ !createParentFolder(targetFile)) //throw FileError, ThreadInterruption
+ return setup2StepMove<side>(sourceFile, targetFile); //throw FileError, ThreadInterruption
//finally start move! this should work now:
- synchronizeFile(targetFile); //throw FileError
- //SynchronizeFolderPair::synchronizeFileInt() is *not* expecting SO_MOVE_LEFT_FROM/SO_MOVE_RIGHT_FROM => start move from targetFile, not sourceFile!
+ synchronizeFile(targetFile); //throw FileError, ThreadInterruption
+ //FolderPairSyncer::synchronizeFileInt() is *not* expecting SO_MOVE_LEFT_FROM/SO_MOVE_RIGHT_FROM => start move from targetFile, not sourceFile!
}
//else: sourceFile will not be deleted, and is not standing in the way => delay to second pass
//note: this case may include new "move sources" from two-step sub-routine!!!
}
-//search for file move-operations
-void SynchronizeFolderPair::runZeroPass(ContainerObject& hierObj)
+void FolderPairSyncer::prepareFileMove(FilePair& file) //throw ThreadInterruption
{
- for (FilePair& file : hierObj.refSubFiles())
+ const SyncOperation syncOp = file.getSyncOperation();
+ switch (syncOp) //evaluate comparison result and sync direction
{
- const SyncOperation syncOp = file.getSyncOperation();
- switch (syncOp) //evaluate comparison result and sync direction
- {
- case SO_MOVE_LEFT_FROM:
- case SO_MOVE_RIGHT_FROM:
- if (FilePair* targetObj = dynamic_cast<FilePair*>(FileSystemObject::retrieve(file.getMoveRef())))
+ case SO_MOVE_LEFT_FROM:
+ case SO_MOVE_RIGHT_FROM:
+ if (FilePair* targetObj = dynamic_cast<FilePair*>(FileSystemObject::retrieve(file.getMoveRef())))
+ {
+ FilePair* sourceObj = &file;
+ assert(dynamic_cast<FilePair*>(FileSystemObject::retrieve(targetObj->getMoveRef())) == sourceObj);
+
+ Opt<std::wstring> errMsg = tryReportingError([&] //throw ThreadInterruption
{
- FilePair* sourceObj = &file;
- assert(dynamic_cast<FilePair*>(FileSystemObject::retrieve(targetObj->getMoveRef())) == sourceObj);
+ if (syncOp == SO_MOVE_LEFT_FROM)
+ resolveMoveConflicts<LEFT_SIDE>(*sourceObj, *targetObj); //throw FileError, ThreadInterruption
+ else
+ resolveMoveConflicts<RIGHT_SIDE>(*sourceObj, *targetObj); //
+ }, threadIdx_, acb_); //throw ThreadInterruption
- zen::Opt<std::wstring> errMsg = tryReportingError([&]
- {
- if (syncOp == SO_MOVE_LEFT_FROM)
- this->manageFileMove<LEFT_SIDE>(*sourceObj, *targetObj); //throw FileError
- else
- this->manageFileMove<RIGHT_SIDE>(*sourceObj, *targetObj); //
- }, procCallback_); //throw X?
+ if (errMsg)
+ {
+ //move operation has failed! We cannot allow to continue and have move source's parent directory deleted, messing up statistics!
+ // => revert to ordinary "copy + delete"
- if (errMsg)
+ auto getStats = [&]() -> std::pair<int, int64_t>
{
- //move operation has failed! We cannot allow to continue and have move source's parent directory deleted, messing up statistics!
- // => revert to ordinary "copy + delete"
-
- auto getStats = [&]() -> std::pair<int, int64_t>
- {
- SyncStatistics statSrc(*sourceObj);
- SyncStatistics statTrg(*targetObj);
- return { getCUD(statSrc) + getCUD(statTrg), statSrc.getBytesToProcess() + statTrg.getBytesToProcess() };
- };
-
- const auto statBefore = getStats();
- sourceObj->setMoveRef(nullptr);
- targetObj->setMoveRef(nullptr);
- const auto statAfter = getStats();
- //fix statistics total to match "copy + delete"
- procCallback_.updateTotalData(statAfter.first - statBefore.first, statAfter.second - statBefore.second);
- }
+ SyncStatistics statSrc(*sourceObj);
+ SyncStatistics statTrg(*targetObj);
+ return { getCUD(statSrc) + getCUD(statTrg), statSrc.getBytesToProcess() + statTrg.getBytesToProcess() };
+ };
+
+ const auto statBefore = getStats();
+ sourceObj->setMoveRef(nullptr);
+ targetObj->setMoveRef(nullptr);
+ const auto statAfter = getStats();
+ //fix statistics total to match "copy + delete"
+ acb_.updateDataTotal(statAfter.first - statBefore.first, statAfter.second - statBefore.second); //noexcept
}
- else assert(false);
- break;
+ }
+ else assert(false);
+ break;
- case SO_MOVE_LEFT_TO: //it's enough to try each move-pair *once*
- case SO_MOVE_RIGHT_TO: //
- case SO_DELETE_LEFT:
- case SO_DELETE_RIGHT:
- case SO_OVERWRITE_LEFT:
- case SO_OVERWRITE_RIGHT:
- case SO_CREATE_NEW_LEFT:
- case SO_CREATE_NEW_RIGHT:
- case SO_DO_NOTHING:
- case SO_EQUAL:
- case SO_UNRESOLVED_CONFLICT:
- case SO_COPY_METADATA_TO_LEFT:
- case SO_COPY_METADATA_TO_RIGHT:
- break;
- }
+ case SO_MOVE_LEFT_TO: //it's enough to try each move-pair *once*
+ case SO_MOVE_RIGHT_TO: //
+ case SO_DELETE_LEFT:
+ case SO_DELETE_RIGHT:
+ case SO_OVERWRITE_LEFT:
+ case SO_OVERWRITE_RIGHT:
+ case SO_CREATE_NEW_LEFT:
+ case SO_CREATE_NEW_RIGHT:
+ case SO_DO_NOTHING:
+ case SO_EQUAL:
+ case SO_UNRESOLVED_CONFLICT:
+ case SO_COPY_METADATA_TO_LEFT:
+ case SO_COPY_METADATA_TO_RIGHT:
+ break;
}
-
- for (FolderPair& folder : hierObj.refSubFolders())
- runZeroPass(folder); //recurse
}
//---------------------------------------------------------------------------------------------------------------
@@ -1003,7 +1647,7 @@ void SynchronizeFolderPair::runZeroPass(ContainerObject& hierObj)
// - support change in type: overwrite file by directory, symlink by file, ect.
inline
-SynchronizeFolderPair::PassNo SynchronizeFolderPair::getPass(const FilePair& file)
+FolderPairSyncer::PassNo FolderPairSyncer::getPass(const FilePair& file)
{
switch (file.getSyncOperation()) //evaluate comparison result and sync direction
{
@@ -1036,12 +1680,12 @@ SynchronizeFolderPair::PassNo SynchronizeFolderPair::getPass(const FilePair& fil
return PASS_NEVER;
}
assert(false);
- return PASS_TWO; //dummy
+ return PASS_NEVER; //dummy
}
inline
-SynchronizeFolderPair::PassNo SynchronizeFolderPair::getPass(const SymlinkPair& link)
+FolderPairSyncer::PassNo FolderPairSyncer::getPass(const SymlinkPair& link)
{
switch (link.getSyncOperation()) //evaluate comparison result and sync direction
{
@@ -1068,12 +1712,12 @@ SynchronizeFolderPair::PassNo SynchronizeFolderPair::getPass(const SymlinkPair&
return PASS_NEVER;
}
assert(false);
- return PASS_TWO; //dummy
+ return PASS_NEVER; //dummy
}
inline
-SynchronizeFolderPair::PassNo SynchronizeFolderPair::getPass(const FolderPair& folder)
+FolderPairSyncer::PassNo FolderPairSyncer::getPass(const FolderPair& folder)
{
switch (folder.getSyncOperation()) //evaluate comparison result and sync direction
{
@@ -1100,37 +1744,13 @@ SynchronizeFolderPair::PassNo SynchronizeFolderPair::getPass(const FolderPair& f
return PASS_NEVER;
}
assert(false);
- return PASS_TWO; //dummy
-}
-
-
-template <SynchronizeFolderPair::PassNo pass>
-void SynchronizeFolderPair::runPass(ContainerObject& hierObj) //throw X
-{
- //synchronize files:
- for (FilePair& file : hierObj.refSubFiles())
- if (pass == this->getPass(file)) //"this->" required by two-pass lookup as enforced by GCC 4.7
- tryReportingError([&] { synchronizeFile(file); }, procCallback_); //throw X
-
- //synchronize symbolic links:
- for (SymlinkPair& symlink : hierObj.refSubLinks())
- if (pass == this->getPass(symlink))
- tryReportingError([&] { synchronizeLink(symlink); }, procCallback_); //throw X
-
- //synchronize folders:
- for (FolderPair& folder : hierObj.refSubFolders())
- {
- if (pass == this->getPass(folder))
- tryReportingError([&] { synchronizeFolder(folder); }, procCallback_); //throw X
-
- this->runPass<pass>(folder); //recurse
- }
+ return PASS_NEVER; //dummy
}
//---------------------------------------------------------------------------------------------------------------
inline
-void SynchronizeFolderPair::synchronizeFile(FilePair& file)
+void FolderPairSyncer::synchronizeFile(FilePair& file) //throw FileError, ThreadInterruption
{
const SyncOperation syncOp = file.getSyncOperation();
@@ -1145,9 +1765,10 @@ void SynchronizeFolderPair::synchronizeFile(FilePair& file)
template <SelectedSide sideTrg>
-void SynchronizeFolderPair::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
+void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp) //throw FileError, ThreadInterruption
{
static const SelectedSide sideSrc = OtherSide<sideTrg>::result;
+ DeletionHandling& delHandlingTrg = SelectParam<sideTrg>::ref(delHandlingLeft_, delHandlingRight_);
switch (syncOp)
{
@@ -1160,17 +1781,15 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& file, SyncOperation syn
//can't use "getAbstractPath<sideTrg>()" as file name is not available!
const AbstractPath targetPath = file.getAbstractPath<sideTrg>();
- reportInfo(txtCreatingFile, AFS::getDisplayPath(targetPath));
+ reportInfo(txtCreatingFile_, AFS::getDisplayPath(targetPath));
- StatisticsReporter statReporter(1, file.getFileSize<sideSrc>(), procCallback_);
+ AsyncItemStatReporter statReporter(1, file.getFileSize<sideSrc>(), threadIdx_, acb_);
try
{
- auto notifyUnbufferedIO = [&](int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); };
-
const AFS::FileCopyResult result = copyFileWithCallback({ file.getAbstractPath<sideSrc>(), file.getAttributes<sideSrc>() },
targetPath,
nullptr, //onDeleteTargetFile: nothing to delete; if existing: undefined behavior! (fail/overwrite/auto-rename)
- notifyUnbufferedIO); //throw FileError
+ statReporter); //throw FileError
if (result.errorModTime)
errorsModTime_.push_back(*result.errorModTime); //show all warnings later as a single message
@@ -1187,29 +1806,32 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& file, SyncOperation syn
catch (FileError&)
{
bool sourceWasDeleted = false;
- try { sourceWasDeleted = !AFS::getItemTypeIfExists(file.getAbstractPath<sideSrc>()); /*throw FileError*/ }
+ try { sourceWasDeleted = !parallel::getItemTypeIfExists(file.getAbstractPath<sideSrc>(), singleThread_); /*throw FileError*/ }
catch (FileError&) {} //previous exception is more relevant
+ //do not check on type (symlink, file, folder) -> if there is a type change, FFS should not be quiet about it!
if (sourceWasDeleted)
+ {
+ //even if the source item does not exist anymore, significant I/O work was done => report
+ statReporter.reportDelta(1, 0);
+ reportInfo(txtSourceItemNotFound_, AFS::getDisplayPath(file.getAbstractPath<sideSrc>()));
+
file.removeObject<sideSrc>(); //source deleted meanwhile...nothing was done (logical point of view!)
+ }
else
- throw; //do not check on type (symlink, file, folder) -> if there is a type change, FFS should not be quiet about it!
+ throw;
}
}
break;
case SO_DELETE_LEFT:
case SO_DELETE_RIGHT:
- reportInfo(getDelHandling<sideTrg>().getTxtRemovingFile(), AFS::getDisplayPath(file.getAbstractPath<sideTrg>()));
+ reportInfo(delHandlingTrg.getTxtRemovingFile(), AFS::getDisplayPath(file.getAbstractPath<sideTrg>()));
{
- StatisticsReporter statReporter(1, 0, procCallback_);
-
- auto onNotifyItemDeletion = [&] { statReporter.reportDelta(1, 0); };
- auto notifyUnbufferedIO = [&](int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); };
-
- getDelHandling<sideTrg>().removeFileWithCallback({ file.getAbstractPath<sideTrg>(), file.getAttributes<sideTrg>() },
- file.getPairRelativePath(), onNotifyItemDeletion, notifyUnbufferedIO); //throw FileError
+ AsyncItemStatReporter statReporter(1, 0, threadIdx_, acb_);
+ delHandlingTrg.removeFileWithCallback({ file.getAbstractPath<sideTrg>(), file.getAttributes<sideTrg>() },
+ file.getPairRelativePath(), statReporter, singleThread_); //throw FileError, X
file.removeObject<sideTrg>(); //update FilePair
}
break;
@@ -1226,13 +1848,13 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& file, SyncOperation syn
const AbstractPath pathFrom = moveFrom->getAbstractPath<sideTrg>();
const AbstractPath pathTo = moveTo ->getAbstractPath<sideTrg>();
- reportInfo(txtMovingFile, AFS::getDisplayPath(pathFrom), AFS::getDisplayPath(pathTo));
+ reportInfo(txtMovingFileXtoY_, AFS::getDisplayPath(pathFrom), AFS::getDisplayPath(pathTo));
- StatisticsReporter statReporter(1, 0, procCallback_);
+ AsyncItemStatReporter statReporter(1, 0, threadIdx_, acb_);
//TODO: synchronizeFileInt: consider ErrorDifferentVolume! e.g. symlink aliasing!
- AFS::renameItem(pathFrom, pathTo); //throw FileError, (ErrorDifferentVolume)
+ parallel::renameItem(pathFrom, pathTo, singleThread_); //throw FileError, (ErrorDifferentVolume)
statReporter.reportDelta(1, 0);
@@ -1259,40 +1881,38 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& file, SyncOperation syn
AbstractPath targetPathResolvedOld = file.getAbstractPath<sideTrg>(); //support change in case when syncing to case-sensitive SFTP on Windows!
AbstractPath targetPathResolvedNew = targetPathLogical;
if (file.isFollowedSymlink<sideTrg>()) //follow link when updating file rather than delete it and replace with regular file!!!
- targetPathResolvedOld = targetPathResolvedNew = AFS::getSymlinkResolvedPath(file.getAbstractPath<sideTrg>()); //throw FileError
+ targetPathResolvedOld = targetPathResolvedNew = parallel::getSymlinkResolvedPath(file.getAbstractPath<sideTrg>(), singleThread_); //throw FileError
- reportInfo(txtOverwritingFile, AFS::getDisplayPath(targetPathResolvedOld));
+ reportInfo(txtUpdatingFile_, AFS::getDisplayPath(targetPathResolvedOld));
- StatisticsReporter statReporter(1, file.getFileSize<sideSrc>(), procCallback_);
+ AsyncItemStatReporter statReporter(1, file.getFileSize<sideSrc>(), threadIdx_, acb_);
if (file.isFollowedSymlink<sideTrg>()) //since we follow the link, we need to sync case sensitivity of the link manually!
if (file.getItemName<sideTrg>() != file.getItemName<sideSrc>()) //have difference in case?
- AFS::renameItem(file.getAbstractPath<sideTrg>(), targetPathLogical); //throw FileError, (ErrorDifferentVolume)
-
- auto notifyUnbufferedIO = [&](int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); };
+ parallel::renameItem(file.getAbstractPath<sideTrg>(), targetPathLogical, singleThread_); //throw FileError, (ErrorDifferentVolume)
auto onDeleteTargetFile = [&] //delete target at appropriate time
{
- //reportStatus(this->getDelHandling<sideTrg>().getTxtRemovingFile(), AFS::getDisplayPath(targetPathResolvedOld)); -> superfluous/confuses user
+ //reportStatus(this->delHandlingTrg.getTxtRemovingFile(), AFS::getDisplayPath(targetPathResolvedOld)); -> superfluous/confuses user
FileAttributes followedTargetAttr = file.getAttributes<sideTrg>();
followedTargetAttr.isFollowedSymlink = false;
- this->getDelHandling<sideTrg>().removeFileWithCallback({ targetPathResolvedOld, followedTargetAttr},
- file.getPairRelativePath(), [] {}, notifyUnbufferedIO); //throw FileError;
- //no (logical) item count update desired - but total byte count may change, e.g. move(copy) deleted file to versioning dir
+ delHandlingTrg.removeFileWithCallback({ targetPathResolvedOld, followedTargetAttr }, file.getPairRelativePath(), statReporter, singleThread_); //throw FileError, X
+ //no (logical) item count update desired - but total byte count may change, e.g. move(copy) old file to versioning dir
+ statReporter.reportDelta(-1, 0); //undo item stats reporting within DeletionHandling::removeFileWithCallback()
//file.removeObject<sideTrg>(); -> doesn't make sense for isFollowedSymlink(); "file, sideTrg" evaluated below!
//if fail-safe file copy is active, then the next operation will be a simple "rename"
- //=> don't risk reportStatus() throwing AbortProcess() leaving the target deleted rather than updated!
+ //=> don't risk reportStatus() throwing ThreadInterruption() leaving the target deleted rather than updated!
//=> if failSafeFileCopy_ : don't run callbacks that could throw
};
const AFS::FileCopyResult result = copyFileWithCallback({ file.getAbstractPath<sideSrc>(), file.getAttributes<sideSrc>() },
targetPathResolvedNew,
onDeleteTargetFile,
- notifyUnbufferedIO); //throw FileError
+ statReporter); //throw FileError
if (result.errorModTime)
errorsModTime_.push_back(*result.errorModTime); //show all warnings later as a single message
@@ -1312,21 +1932,21 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& file, SyncOperation syn
case SO_COPY_METADATA_TO_LEFT:
case SO_COPY_METADATA_TO_RIGHT:
//harmonize with file_hierarchy.cpp::getSyncOpDescription!!
- reportInfo(txtWritingAttributes, AFS::getDisplayPath(file.getAbstractPath<sideTrg>()));
+ reportInfo(txtUpdatingAttributes_, AFS::getDisplayPath(file.getAbstractPath<sideTrg>()));
{
- StatisticsReporter statReporter(1, 0, procCallback_);
+ AsyncItemStatReporter statReporter(1, 0, threadIdx_, acb_);
assert(file.getItemName<sideTrg>() != file.getItemName<sideSrc>());
if (file.getItemName<sideTrg>() != file.getItemName<sideSrc>()) //have difference in case?
- AFS::renameItem(file.getAbstractPath<sideTrg>(), //throw FileError, (ErrorDifferentVolume)
- AFS::appendRelPath(file.parent().getAbstractPath<sideTrg>(), file.getItemName<sideSrc>()));
+ parallel::renameItem(file.getAbstractPath<sideTrg>(), //throw FileError, (ErrorDifferentVolume)
+ AFS::appendRelPath(file.parent().getAbstractPath<sideTrg>(), file.getItemName<sideSrc>()), singleThread_);
#if 0 //changing file time without copying content is not justified after CompareVariant::SIZE finds "equal" files! similar issue with CompareVariant::TIME_SIZE and FileTimeTolerance == -1
//Bonus: some devices don't support setting (precise) file times anyway, e.g. FAT or MTP!
if (file.getLastWriteTime<sideTrg>() != file.getLastWriteTime<sideSrc>())
//- no need to call sameFileTime() or respect 2 second FAT/FAT32 precision in this comparison
//- do NOT read *current* source file time, but use buffered value which corresponds to time of comparison!
- AFS::setModTime(file.getAbstractPath<sideTrg>(), file.getLastWriteTime<sideSrc>()); //throw FileError
+ parallel::setModTime(file.getAbstractPath<sideTrg>(), file.getLastWriteTime<sideSrc>()); //throw FileError
#endif
statReporter.reportDelta(1, 0);
@@ -1347,16 +1967,16 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& file, SyncOperation syn
case SO_DO_NOTHING:
case SO_EQUAL:
case SO_UNRESOLVED_CONFLICT:
- assert(false); //should have been filtered out by SynchronizeFolderPair::getPass()
+ assert(false); //should have been filtered out by FolderPairSyncer::getPass()
return; //no update on processed data!
}
- procCallback_.requestUiRefresh(); //throw ?
+ interruptionPoint(); //throw ThreadInterruption
}
inline
-void SynchronizeFolderPair::synchronizeLink(SymlinkPair& link)
+void FolderPairSyncer::synchronizeLink(SymlinkPair& link) //throw FileError, ThreadInterruption
{
const SyncOperation syncOp = link.getSyncOperation();
@@ -1371,9 +1991,11 @@ void SynchronizeFolderPair::synchronizeLink(SymlinkPair& link)
template <SelectedSide sideTrg>
-void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& symlink, SyncOperation syncOp)
+void FolderPairSyncer::synchronizeLinkInt(SymlinkPair& symlink, SyncOperation syncOp) //throw FileError, ThreadInterruption
{
+ warn_static("test constexpr compiler conformance")
static const SelectedSide sideSrc = OtherSide<sideTrg>::result;
+ DeletionHandling& delHandlingTrg = SelectParam<sideTrg>::ref(delHandlingLeft_, delHandlingRight_);
switch (syncOp)
{
@@ -1385,12 +2007,12 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& symlink, SyncOperati
return; //if parent directory creation failed, there's no reason to show more errors!
const AbstractPath targetPath = symlink.getAbstractPath<sideTrg>();
- reportInfo(txtCreatingLink, AFS::getDisplayPath(targetPath));
+ reportInfo(txtCreatingLink_, AFS::getDisplayPath(targetPath));
- StatisticsReporter statReporter(1, 0, procCallback_);
+ AsyncItemStatReporter statReporter(1, 0, threadIdx_, acb_);
try
{
- AFS::copySymlink(symlink.getAbstractPath<sideSrc>(), targetPath, copyFilePermissions_); //throw FileError
+ parallel::copySymlink(symlink.getAbstractPath<sideSrc>(), targetPath, copyFilePermissions_, singleThread_); //throw FileError
statReporter.reportDelta(1, 0);
@@ -1403,26 +2025,31 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& symlink, SyncOperati
catch (FileError&)
{
bool sourceWasDeleted = false;
- try { sourceWasDeleted = !AFS::getItemTypeIfExists(symlink.getAbstractPath<sideSrc>()); /*throw FileError*/ }
+ try { sourceWasDeleted = !parallel::getItemTypeIfExists(symlink.getAbstractPath<sideSrc>(), singleThread_); /*throw FileError*/ }
catch (FileError&) {} //previous exception is more relevant
+ //do not check on type (symlink, file, folder) -> if there is a type change, FFS should not be quiet about it!
if (sourceWasDeleted)
+ {
+ //even if the source item does not exist anymore, significant I/O work was done => report
+ statReporter.reportDelta(1, 0);
+ reportInfo(txtSourceItemNotFound_, AFS::getDisplayPath(symlink.getAbstractPath<sideSrc>()));
+
symlink.removeObject<sideSrc>(); //source deleted meanwhile...nothing was done (logical point of view!)
+ }
else
- throw; //do not check on type (symlink, file, folder) -> if there is a type change, FFS should not be quiet about it!
+ throw;
}
}
break;
case SO_DELETE_LEFT:
case SO_DELETE_RIGHT:
- reportInfo(getDelHandling<sideTrg>().getTxtRemovingSymLink(), AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>()));
+ reportInfo(delHandlingTrg.getTxtRemovingSymLink(), AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>()));
{
- StatisticsReporter statReporter(1, 0, procCallback_);
+ AsyncItemStatReporter statReporter(1, 0, threadIdx_, acb_);
- auto onNotifyItemDeletion = [&] { statReporter.reportDelta(1, 0); };
-
- getDelHandling<sideTrg>().removeLinkWithCallback(symlink.getAbstractPath<sideTrg>(), symlink.getPairRelativePath(), onNotifyItemDeletion); //throw FileError
+ delHandlingTrg.removeLinkWithCallback(symlink.getAbstractPath<sideTrg>(), symlink.getPairRelativePath(), statReporter, singleThread_); //throw FileError, X
symlink.removeObject<sideTrg>(); //update SymlinkPair
}
@@ -1430,21 +2057,22 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& symlink, SyncOperati
case SO_OVERWRITE_LEFT:
case SO_OVERWRITE_RIGHT:
- reportInfo(txtOverwritingLink, AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>()));
+ reportInfo(txtUpdatingLink_, AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>()));
{
- StatisticsReporter statReporter(1, 0, procCallback_);
+ AsyncItemStatReporter statReporter(1, 0, threadIdx_, acb_);
- //reportStatus(getDelHandling<sideTrg>().getTxtRemovingSymLink(), AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>()));
- getDelHandling<sideTrg>().removeLinkWithCallback(symlink.getAbstractPath<sideTrg>(), symlink.getPairRelativePath(), [] {}); //throw FileError
+ //reportStatus(delHandlingTrg.getTxtRemovingSymLink(), AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>()));
+ delHandlingTrg.removeLinkWithCallback(symlink.getAbstractPath<sideTrg>(), symlink.getPairRelativePath(), statReporter, singleThread_); //throw FileError, X
+ statReporter.reportDelta(-1, 0); //undo item stats reporting within DeletionHandling::removeLinkWithCallback()
//symlink.removeObject<sideTrg>(); -> "symlink, sideTrg" evaluated below!
- //=> don't risk reportStatus() throwing AbortProcess() leaving the target deleted rather than updated:
- //reportStatus(txtOverwritingLink, AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>())); //restore status text
+ //=> don't risk reportStatus() throwing ThreadInterruption() leaving the target deleted rather than updated:
+ //reportStatus(txtUpdatingLink_, AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>())); //restore status text
- AFS::copySymlink(symlink.getAbstractPath<sideSrc>(),
- AFS::appendRelPath(symlink.parent().getAbstractPath<sideTrg>(), symlink.getItemName<sideSrc>()), //respect differences in case of source object
- copyFilePermissions_); //throw FileError
+ parallel::copySymlink(symlink.getAbstractPath<sideSrc>(),
+ AFS::appendRelPath(symlink.parent().getAbstractPath<sideTrg>(), symlink.getItemName<sideSrc>()), //respect differences in case of source object
+ copyFilePermissions_, singleThread_); //throw FileError
statReporter.reportDelta(1, 0); //we model "delete + copy" as ONE logical operation
@@ -1457,18 +2085,18 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& symlink, SyncOperati
case SO_COPY_METADATA_TO_LEFT:
case SO_COPY_METADATA_TO_RIGHT:
- reportInfo(txtWritingAttributes, AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>()));
+ reportInfo(txtUpdatingAttributes_, AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>()));
{
- StatisticsReporter statReporter(1, 0, procCallback_);
+ AsyncItemStatReporter statReporter(1, 0, threadIdx_, acb_);
if (symlink.getItemName<sideTrg>() != symlink.getItemName<sideSrc>()) //have difference in case?
- AFS::renameItem(symlink.getAbstractPath<sideTrg>(), //throw FileError, (ErrorDifferentVolume)
- AFS::appendRelPath(symlink.parent().getAbstractPath<sideTrg>(), symlink.getItemName<sideSrc>()));
+ parallel::renameItem(symlink.getAbstractPath<sideTrg>(), //throw FileError, (ErrorDifferentVolume)
+ AFS::appendRelPath(symlink.parent().getAbstractPath<sideTrg>(), symlink.getItemName<sideSrc>()), singleThread_);
//if (symlink.getLastWriteTime<sideTrg>() != symlink.getLastWriteTime<sideSrc>())
// //- no need to call sameFileTime() or respect 2 second FAT/FAT32 precision in this comparison
// //- do NOT read *current* source file time, but use buffered value which corresponds to time of comparison!
- // AFS::setModTimeSymlink(symlink.getAbstractPath<sideTrg>(), symlink.getLastWriteTime<sideSrc>()); //throw FileError
+ // parallel::setModTimeSymlink(symlink.getAbstractPath<sideTrg>(), symlink.getLastWriteTime<sideSrc>()); //throw FileError
statReporter.reportDelta(1, 0);
@@ -1486,16 +2114,16 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& symlink, SyncOperati
case SO_DO_NOTHING:
case SO_EQUAL:
case SO_UNRESOLVED_CONFLICT:
- assert(false); //should have been filtered out by SynchronizeFolderPair::getPass()
+ assert(false); //should have been filtered out by FolderPairSyncer::getPass()
return; //no update on processed data!
}
- procCallback_.requestUiRefresh(); //throw ?
+ interruptionPoint(); //throw ThreadInterruption
}
inline
-void SynchronizeFolderPair::synchronizeFolder(FolderPair& folder)
+void FolderPairSyncer::synchronizeFolder(FolderPair& folder) //throw FileError, ThreadInterruption
{
const SyncOperation syncOp = folder.getSyncOperation();
@@ -1510,9 +2138,10 @@ void SynchronizeFolderPair::synchronizeFolder(FolderPair& folder)
template <SelectedSide sideTrg>
-void SynchronizeFolderPair::synchronizeFolderInt(FolderPair& folder, SyncOperation syncOp)
+void FolderPairSyncer::synchronizeFolderInt(FolderPair& folder, SyncOperation syncOp) //throw FileError, ThreadInterruption
{
static const SelectedSide sideSrc = OtherSide<sideTrg>::result;
+ DeletionHandling& delHandlingTrg = SelectParam<sideTrg>::ref(delHandlingLeft_, delHandlingRight_);
switch (syncOp)
{
@@ -1524,21 +2153,22 @@ void SynchronizeFolderPair::synchronizeFolderInt(FolderPair& folder, SyncOperati
return; //if parent directory creation failed, there's no reason to show more errors!
const AbstractPath targetPath = folder.getAbstractPath<sideTrg>();
- reportInfo(txtCreatingFolder, AFS::getDisplayPath(targetPath));
+ reportInfo(txtCreatingFolder_, AFS::getDisplayPath(targetPath));
//shallow-"copying" a folder might not fail if source is missing, so we need to check this first:
- if (AFS::getItemTypeIfExists(folder.getAbstractPath<sideSrc>())) //throw FileError
+ if (parallel::getItemTypeIfExists(folder.getAbstractPath<sideSrc>(), singleThread_)) //throw FileError
{
- StatisticsReporter statReporter(1, 0, procCallback_);
+ AsyncItemStatReporter statReporter(1, 0, threadIdx_, acb_);
try
{
//target existing: undefined behavior! (fail/overwrite)
- AFS::copyNewFolder(folder.getAbstractPath<sideSrc>(), targetPath, copyFilePermissions_); //throw FileError
+ parallel::copyNewFolder(folder.getAbstractPath<sideSrc>(), targetPath, copyFilePermissions_, singleThread_); //throw FileError
}
catch (FileError&)
{
bool folderAlreadyExists = false;
- try { folderAlreadyExists = AFS::getItemType(targetPath) == AFS::ItemType::FOLDER; } /*throw FileError*/ catch (FileError&) {} //previous exception is more relevant
+ try { folderAlreadyExists = parallel::getItemType(targetPath, singleThread_) == AFS::ItemType::FOLDER; } /*throw FileError*/ catch (FileError&) {}
+ //previous exception is more relevant
if (!folderAlreadyExists)
throw;
@@ -1551,10 +2181,14 @@ void SynchronizeFolderPair::synchronizeFolderInt(FolderPair& folder, SyncOperati
false, //isSymlinkTrg
folder.isFollowedSymlink<sideSrc>());
}
- else //source deleted meanwhile...nothing was done (logical point of view!)
+ else //source deleted meanwhile...
{
const SyncStatistics subStats(folder);
- StatisticsReporter statReporter(1 + getCUD(subStats), subStats.getBytesToProcess(), procCallback_);
+ AsyncItemStatReporter statReporter(1 + getCUD(subStats), subStats.getBytesToProcess(), threadIdx_, acb_);
+
+ //even if the source item does not exist anymore, significant I/O work was done => report
+ statReporter.reportDelta(1, 0);
+ reportInfo(txtSourceItemNotFound_, AFS::getDisplayPath(folder.getAbstractPath<sideSrc>()));
//remove only *after* evaluating folder!!
folder.refSubFiles ().clear(); //
@@ -1567,15 +2201,14 @@ void SynchronizeFolderPair::synchronizeFolderInt(FolderPair& folder, SyncOperati
case SO_DELETE_LEFT:
case SO_DELETE_RIGHT:
- reportInfo(getDelHandling<sideTrg>().getTxtRemovingFolder(), AFS::getDisplayPath(folder.getAbstractPath<sideTrg>()));
+ reportInfo(delHandlingTrg.getTxtRemovingFolder(), AFS::getDisplayPath(folder.getAbstractPath<sideTrg>()));
{
const SyncStatistics subStats(folder); //counts sub-objects only!
- StatisticsReporter statReporter(1 + getCUD(subStats), subStats.getBytesToProcess(), procCallback_);
+ AsyncItemStatReporter statReporter(1 + getCUD(subStats), subStats.getBytesToProcess(), threadIdx_, acb_);
- auto onNotifyItemDeletion = [&] { statReporter.reportDelta(1, 0); };
- auto notifyUnbufferedIO = [&](int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); };
+ delHandlingTrg.removeDirWithCallback(folder.getAbstractPath<sideTrg>(), folder.getPairRelativePath(), statReporter, singleThread_); //throw FileError, X
- getDelHandling<sideTrg>().removeDirWithCallback(folder.getAbstractPath<sideTrg>(), folder.getPairRelativePath(), onNotifyItemDeletion, notifyUnbufferedIO); //throw FileError
+ warn_static("perf => not parallel!")
folder.refSubFiles ().clear(); //
folder.refSubLinks ().clear(); //update FolderPair
@@ -1588,14 +2221,14 @@ void SynchronizeFolderPair::synchronizeFolderInt(FolderPair& folder, SyncOperati
case SO_OVERWRITE_RIGHT: //
case SO_COPY_METADATA_TO_LEFT:
case SO_COPY_METADATA_TO_RIGHT:
- reportInfo(txtWritingAttributes, AFS::getDisplayPath(folder.getAbstractPath<sideTrg>()));
+ reportInfo(txtUpdatingAttributes_, AFS::getDisplayPath(folder.getAbstractPath<sideTrg>()));
{
- StatisticsReporter statReporter(1, 0, procCallback_);
+ AsyncItemStatReporter statReporter(1, 0, threadIdx_, acb_);
assert(folder.getItemName<sideTrg>() != folder.getItemName<sideSrc>());
if (folder.getItemName<sideTrg>() != folder.getItemName<sideSrc>()) //have difference in case?
- AFS::renameItem(folder.getAbstractPath<sideTrg>(), //throw FileError, (ErrorDifferentVolume)
- AFS::appendRelPath(folder.parent().getAbstractPath<sideTrg>(), folder.getItemName<sideSrc>()));
+ parallel::renameItem(folder.getAbstractPath<sideTrg>(), //throw FileError, (ErrorDifferentVolume)
+ AFS::appendRelPath(folder.parent().getAbstractPath<sideTrg>(), folder.getItemName<sideSrc>()), singleThread_);
//copyFileTimes -> useless: modification time changes with each child-object creation/deletion
statReporter.reportDelta(1, 0);
@@ -1614,70 +2247,54 @@ void SynchronizeFolderPair::synchronizeFolderInt(FolderPair& folder, SyncOperati
case SO_DO_NOTHING:
case SO_EQUAL:
case SO_UNRESOLVED_CONFLICT:
- assert(false); //should have been filtered out by SynchronizeFolderPair::getPass()
+ assert(false); //should have been filtered out by FolderPairSyncer::getPass()
return; //no update on processed data!
}
- procCallback_.requestUiRefresh(); //throw ?
+ interruptionPoint(); //throw ThreadInterruption
}
//###########################################################################################
-//--------------------- data verification -------------------------
-void verifyFiles(const AbstractPath& sourcePath, const AbstractPath& targetPath, const IOCallback& notifyUnbufferedIO) //throw FileError
-{
- try
- {
- //do like "copy /v": 1. flush target file buffers, 2. read again as usual (using OS buffers)
- // => it seems OS buffered are not invalidated by this: snake oil???
- if (Opt<Zstring> nativeTargetPath = AFS::getNativeItemPath(targetPath))
- {
- const int fileHandle = ::open(nativeTargetPath->c_str(), O_WRONLY | O_APPEND);
- if (fileHandle == -1)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open file %x."), L"%x", fmtPath(*nativeTargetPath)), L"open");
- ZEN_ON_SCOPE_EXIT(::close(fileHandle));
-
- if (::fsync(fileHandle) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(*nativeTargetPath)), L"fsync");
- } //close file handles!
-
- if (!filesHaveSameContent(sourcePath, targetPath, notifyUnbufferedIO)) //throw FileError
- throw FileError(replaceCpy(replaceCpy(_("%x and %y have different content."),
- L"%x", L"\n" + fmtPath(AFS::getDisplayPath(sourcePath))),
- L"%y", L"\n" + fmtPath(AFS::getDisplayPath(targetPath))));
- }
- catch (const FileError& e) //add some context to error message
- {
- throw FileError(_("Data verification error:"), e.toString());
- }
-}
-
-
-AFS::FileCopyResult SynchronizeFolderPair::copyFileWithCallback(const FileDescriptor& sourceDescr, //throw FileError
- const AbstractPath& targetPath,
- const std::function<void()>& onDeleteTargetFile,
- const IOCallback& notifyUnbufferedIO) const //returns current attributes of source file
+//returns current attributes of source file
+AFS::FileCopyResult FolderPairSyncer::copyFileWithCallback(const FileDescriptor& sourceDescr, //throw FileError
+ const AbstractPath& targetPath,
+ const std::function<void()>& onDeleteTargetFile, //optional!
+ AsyncItemStatReporter& statReporter)
{
const AbstractPath& sourcePath = sourceDescr.path;
const AFS::StreamAttributes sourceAttr{ sourceDescr.attr.modTime, sourceDescr.attr.fileSize, sourceDescr.attr.fileId };
- auto copyOperation = [this, &sourceAttr, &targetPath, &onDeleteTargetFile, &notifyUnbufferedIO](const AbstractPath& sourcePathTmp)
+ auto copyOperation = [this, &sourceAttr, &targetPath, &onDeleteTargetFile, &statReporter](const AbstractPath& sourcePathTmp)
{
//target existing after onDeleteTargetFile(): undefined behavior! (fail/overwrite/auto-rename)
- const AFS::FileCopyResult result = AFS::copyFileTransactional(sourcePathTmp, sourceAttr, //throw FileError, ErrorFileLocked
- targetPath,
- copyFilePermissions_,
- failSafeFileCopy_,
- onDeleteTargetFile,
- notifyUnbufferedIO);
+ const AFS::FileCopyResult result = parallel::copyFileTransactional(sourcePathTmp, sourceAttr, //throw FileError, ErrorFileLocked
+ targetPath,
+ copyFilePermissions_,
+ failSafeFileCopy_,
+ [&]
+ {
+ if (onDeleteTargetFile) //running *outside* singleThread_ lock! => onDeleteTargetFile-callback expects lock being held:
+ {
+ std::lock_guard<std::mutex> dummy(singleThread_);
+ onDeleteTargetFile();
+ }
+ },
+ [&](int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }, //callback runs *outside* singleThread_ lock! => fine
+ singleThread_);
+
//#################### Verification #############################
if (verifyCopiedFiles_)
{
- ZEN_ON_SCOPE_FAIL(try { AFS::removeFilePlain(targetPath); }
+ ZEN_ON_SCOPE_FAIL(try { parallel::removeFilePlain(targetPath, singleThread_); }
catch (FileError&) {}); //delete target if verification fails
- procCallback_.reportInfo(replaceCpy(txtVerifying, L"%x", fmtPath(AFS::getDisplayPath(targetPath))));
- verifyFiles(sourcePathTmp, targetPath, [&](int64_t bytesDelta) { procCallback_.requestUiRefresh(); }); //throw FileError
+ reportInfo(txtVerifyingFile_, AFS::getDisplayPath(targetPath));
+
+ //callback runs *outside* singleThread_ lock! => fine
+ auto verifyCallback = [&](int64_t bytesDelta) { interruptionPoint(); /*throw ThreadInterruption*/ };
+
+ parallel::verifyFiles(sourcePathTmp, targetPath, verifyCallback, singleThread_); //throw FileError
}
//#################### /Verification #############################
@@ -1697,7 +2314,8 @@ bool baseFolderDrop(BaseFolderPair& baseFolder, int folderAccessTimeout, Process
if (baseFolder.isAvailable<side>())
if (Opt<std::wstring> errMsg = tryReportingError([&]
{
- const FolderStatus status = getFolderStatusNonBlocking({ folderPath }, folderAccessTimeout, false /*allowUserInteraction*/, callback);
+ const FolderStatus status = getFolderStatusNonBlocking({ folderPath }, {} /*deviceParallelOps*/,
+ folderAccessTimeout, false /*allowUserInteraction*/, callback);
static_assert(IsSameType<decltype(status.failedChecks.begin()->second), FileError>::value, "");
if (!status.failedChecks.empty())
@@ -1706,7 +2324,7 @@ bool baseFolderDrop(BaseFolderPair& baseFolder, int folderAccessTimeout, Process
if (status.existing.find(folderPath) == status.existing.end())
throw FileError(replaceCpy(_("Cannot find folder %x."), L"%x", fmtPath(AFS::getDisplayPath(folderPath))));
//should really be logged as a "fatal error" if ignored by the user...
- }, callback)) //throw X?
+ }, callback)) //throw X
return true;
return false;
@@ -1714,7 +2332,7 @@ bool baseFolderDrop(BaseFolderPair& baseFolder, int folderAccessTimeout, Process
template <SelectedSide side> //create base directories first (if not yet existing) -> no symlink or attribute copying!
-bool createBaseFolder(BaseFolderPair& baseFolder, int folderAccessTimeout, ProcessCallback& callback) //nothrow; return false if fatal error occurred
+bool createBaseFolder(BaseFolderPair& baseFolder, int folderAccessTimeout, ProcessCallback& callback) //return false if fatal error occurred
{
const AbstractPath baseFolderPath = baseFolder.getAbstractPath<side>();
@@ -1726,7 +2344,8 @@ bool createBaseFolder(BaseFolderPair& baseFolder, int folderAccessTimeout, Proce
bool temporaryNetworkDrop = false;
zen::Opt<std::wstring> errMsg = tryReportingError([&]
{
- const FolderStatus status = getFolderStatusNonBlocking({ baseFolderPath }, folderAccessTimeout, false /*allowUserInteraction*/, callback);
+ const FolderStatus status = getFolderStatusNonBlocking({ baseFolderPath }, {} /*deviceParallelOps*/,
+ folderAccessTimeout, false /*allowUserInteraction*/, callback);
static_assert(IsSameType<decltype(status.failedChecks.begin()->second), FileError>::value, "");
if (!status.failedChecks.empty())
@@ -1749,7 +2368,7 @@ bool createBaseFolder(BaseFolderPair& baseFolder, int folderAccessTimeout, Proce
// 2. deletion handling: versioning -> "
// 3. log file creates containing folder -> no, log only created in batch mode, and only *before* comparison
}
- }, callback); //throw X?
+ }, callback); //throw X
return !errMsg && !temporaryNetworkDrop;
}
return true;
@@ -1774,6 +2393,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
int folderAccessTimeout,
const std::vector<FolderPairSyncCfg>& syncConfig,
FolderComparison& folderCmp,
+ const std::map<AbstractPath, size_t>& deviceParallelOps,
WarningDialogs& warnings,
ProcessCallback& callback)
{
@@ -1841,9 +2461,9 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
std::vector<std::pair<AbstractPath, std::pair<int64_t, int64_t>>> diskSpaceMissing; //base folder / space required / space available
//status of base directories which are set to DeletionPolicy::RECYCLER (and contain actual items to be deleted)
- std::map<AbstractPath, bool, AFS::LessAbstractPath> recyclerSupported; //expensive to determine on Win XP => buffer + check recycle bin existence only once per base folder!
+ std::map<AbstractPath, bool> recyclerSupported; //expensive to determine on Win XP => buffer + check recycle bin existence only once per base folder!
- std::set<AbstractPath, AFS::LessAbstractPath> verCheckVersioningPaths;
+ std::set<AbstractPath> verCheckVersioningPaths;
std::vector<std::pair<AbstractPath, const HardFilter*>> verCheckBaseFolderPaths; //hard filter creates new logical hierarchies for otherwise equal AbstractPath...
//start checking folder pairs
@@ -1882,8 +2502,8 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
folderPairStat.deleteCount<RIGHT_SIDE>() > 0;
//check for empty target folder paths: this only makes sense if empty field is source (and no DB files need to be created)
- if ((AFS::isNullPath(baseFolder.getAbstractPath< LEFT_SIDE>()) && (writeLeft || folderPairCfg.saveSyncDB_)) ||
- (AFS::isNullPath(baseFolder.getAbstractPath<RIGHT_SIDE>()) && (writeRight || folderPairCfg.saveSyncDB_)))
+ if ((AFS::isNullPath(baseFolder.getAbstractPath< LEFT_SIDE>()) && (writeLeft || folderPairCfg.saveSyncDB)) ||
+ (AFS::isNullPath(baseFolder.getAbstractPath<RIGHT_SIDE>()) && (writeRight || folderPairCfg.saveSyncDB)))
{
callback.reportFatalError(_("Target folder input field must not be empty."));
jobType[folderIndex] = FolderPairJobType::SKIP;
@@ -1986,7 +2606,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
tryReportingError([&]
{
recSupported = AFS::supportsRecycleBin(baseFolderPath, [&]{ callback.requestUiRefresh(); /*may throw*/ }); //throw FileError
- }, callback); //throw X?
+ }, callback); //throw X
recyclerSupported.emplace(baseFolderPath, recSupported);
}
@@ -2052,7 +2672,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
//check if folders are used by multiple pairs in read/write access
{
- std::set<AbstractPath, AFS::LessAbstractPath> dependentFolders;
+ std::set<AbstractPath> dependentFolders;
//race condition := multiple accesses of which at least one is a write
for (auto it = readWriteCheckBaseFolders.begin(); it != readWriteCheckBaseFolders.end(); ++it)
@@ -2083,7 +2703,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
std::wstring msg;
for (const AbstractPath& versioningFolderPath : verCheckVersioningPaths)
{
- std::map<AbstractPath, std::wstring, AFS::LessAbstractPath> uniqueMsgs; //=> at most one msg per base folder (*and* per versioningFolderPath)
+ std::map<AbstractPath, std::wstring> uniqueMsgs; //=> at most one msg per base folder (*and* per versioningFolderPath)
for (const auto& item : verCheckBaseFolderPaths) //may contain duplicate paths, but with *different* hard filter!
if (Opt<PathDependency> pd = getPathDependency(versioningFolderPath, NullFilter(), item.first, *item.second))
@@ -2125,7 +2745,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
continue;
//------------------------------------------------------------------------------------------
- callback.reportInfo(_("Synchronizing folder pair:") + L" " + getVariantNameForLog(folderPairCfg.syncVariant_) + L"\n" +
+ callback.reportInfo(_("Synchronizing folder pair:") + L" " + getVariantNameForLog(folderPairCfg.syncVariant) + L"\n" +
L" " + AFS::getDisplayPath(baseFolder.getAbstractPath< LEFT_SIDE>()) + L"\n" +
L" " + AFS::getDisplayPath(baseFolder.getAbstractPath<RIGHT_SIDE>()));
//------------------------------------------------------------------------------------------
@@ -2136,7 +2756,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
continue;
//create base folders if not yet existing
- if (folderPairStat.createCount() > 0 || folderPairCfg.saveSyncDB_) //else: temporary network drop leading to deletions already caught by "sourceFolderMissing" check!
+ if (folderPairStat.createCount() > 0 || folderPairCfg.saveSyncDB) //else: temporary network drop leading to deletions already caught by "sourceFolderMissing" check!
if (!createBaseFolder< LEFT_SIDE>(baseFolder, folderAccessTimeout, callback) || //+ detect temporary network drop!!
!createBaseFolder<RIGHT_SIDE>(baseFolder, folderAccessTimeout, callback)) //
continue;
@@ -2149,7 +2769,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
{
try
{
- if (folderPairCfg.saveSyncDB_)
+ if (folderPairCfg.saveSyncDB)
saveLastSynchronousState(baseFolder, //throw FileError
[&](const std::wstring& statusMsg) { try { callback.reportStatus(statusMsg); /*throw X*/} catch (...) {}});
}
@@ -2169,7 +2789,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
!AFS::isNullPath(baseFolder.getAbstractPath<RIGHT_SIDE>()) && //
AFS::supportPermissionCopy(baseFolder.getAbstractPath<LEFT_SIDE>(),
baseFolder.getAbstractPath<RIGHT_SIDE>()); //throw FileError
- }, callback); //throw X?
+ }, callback); //throw X
auto getEffectiveDeletionPolicy = [&](const AbstractPath& baseFolderPath) -> DeletionPolicy
@@ -2187,30 +2807,62 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
DeletionHandling delHandlerL(baseFolder.getAbstractPath<LEFT_SIDE>(),
getEffectiveDeletionPolicy(baseFolder.getAbstractPath<LEFT_SIDE>()),
folderPairCfg.versioningFolderPhrase,
- folderPairCfg.versioningStyle_,
- timeStamp,
- callback);
+ folderPairCfg.versioningStyle,
+ timeStamp);
DeletionHandling delHandlerR(baseFolder.getAbstractPath<RIGHT_SIDE>(),
getEffectiveDeletionPolicy(baseFolder.getAbstractPath<RIGHT_SIDE>()),
folderPairCfg.versioningFolderPhrase,
- folderPairCfg.versioningStyle_,
- timeStamp,
- callback);
+ folderPairCfg.versioningStyle,
+ timeStamp);
+
+ //always (try to) clean up, even if synchronization is aborted!
+ ZEN_ON_SCOPE_EXIT(
+ //may block heavily, but still do not allow user callback:
+ //-> avoid throwing user cancel exception again, leading to incomplete clean-up!
+ try
+ {
+ delHandlerL.tryCleanup(callback, false /*allowCallbackException*/); //throw FileError, (throw X)
+ }
+ catch (FileError&) {}
+ catch (...) { assert(false); } //what is this?
+ try
+ {
+ delHandlerR.tryCleanup(callback, false /*allowCallbackException*/); //throw FileError, (throw X)
+ }
+ catch (FileError&) {}
+ catch (...) { assert(false); } //what is this?
+ );
+
+ auto getParallelOps = [&](const AbstractPath& ap)
+ {
+ auto itParOps = deviceParallelOps.find(AFS::getPathComponents(ap).rootPath);
+ return std::max<size_t>(itParOps != deviceParallelOps.end() ? itParOps->second : 1, 1); //sanitize early for correct status display
+ };
+ const size_t parallelOps = std::max(getParallelOps(baseFolder.getAbstractPath<LEFT_SIDE >()),
+ getParallelOps(baseFolder.getAbstractPath<RIGHT_SIDE>()));
+ //harmonize with sync_cfg.cpp: parallelOps used for versioning shown == number used for folder pair!
+ warn_static("TODO: warn if parallelOps is > than what versioningFolderPhrase can handle (S)FTP!")
+ //const AbstractPath versioningFolderPath = createAbstractPath(folderPairCfg.versioningFolderPhrase)
+ //getParallelOps(versioningFolderPath)
- SynchronizeFolderPair syncFP(callback, verifyCopiedFiles, copyPermissionsFp, failSafeFileCopy,
- errorsModTime,
- delHandlerL, delHandlerR);
- syncFP.startSync(baseFolder);
+ FolderPairSyncer::SyncCtx syncCtx =
+ {
+ verifyCopiedFiles, copyPermissionsFp, failSafeFileCopy,
+ errorsModTime,
+ delHandlerL, delHandlerR,
+ parallelOps
+ };
+ FolderPairSyncer::runSync(syncCtx, baseFolder, callback);
//(try to gracefully) cleanup temporary Recycle bin folders and versioning -> will be done in ~DeletionHandling anyway...
- tryReportingError([&] { delHandlerL.tryCleanup(true /*allowCallbackException*/); /*throw FileError*/}, callback); //throw X?
- tryReportingError([&] { delHandlerR.tryCleanup(true ); /*throw FileError*/}, callback); //throw X?
+ tryReportingError([&] { delHandlerL.tryCleanup(callback, true /*allowCallbackException*/); /*throw FileError*/}, callback); //throw X
+ tryReportingError([&] { delHandlerR.tryCleanup(callback, true ); /*throw FileError*/}, callback); //throw X
}
//(try to gracefully) write database file
- if (folderPairCfg.saveSyncDB_)
+ if (folderPairCfg.saveSyncDB)
{
callback.reportStatus(_("Generating database..."));
callback.forceUiRefresh(); //throw X
diff --git a/FreeFileSync/Source/synchronization.h b/FreeFileSync/Source/synchronization.h
index 076f0350..6e09709c 100755
--- a/FreeFileSync/Source/synchronization.h
+++ b/FreeFileSync/Source/synchronization.h
@@ -73,22 +73,11 @@ private:
struct FolderPairSyncCfg
{
- FolderPairSyncCfg(bool saveSyncDB,
- const DeletionPolicy handleDel,
- VersioningStyle versioningStyle,
- const Zstring& versioningPhrase,
- DirectionConfig::Variant syncVariant) :
- saveSyncDB_(saveSyncDB),
- handleDeletion(handleDel),
- versioningStyle_(versioningStyle),
- versioningFolderPhrase(versioningPhrase),
- syncVariant_(syncVariant) {}
-
- bool saveSyncDB_; //save database if in automatic mode or dection of moved files is active
+ bool saveSyncDB; //save database if in automatic mode or dection of moved files is active
DeletionPolicy handleDeletion;
- VersioningStyle versioningStyle_;
+ VersioningStyle versioningStyle;
Zstring versioningFolderPhrase; //unresolved directory names as entered by user!
- DirectionConfig::Variant syncVariant_;
+ DirectionConfig::Variant syncVariant;
};
std::vector<FolderPairSyncCfg> extractSyncCfg(const MainConfiguration& mainCfg);
@@ -103,6 +92,7 @@ void synchronize(const std::chrono::system_clock::time_point& syncStartTime,
int folderAccessTimeout,
const std::vector<FolderPairSyncCfg>& syncConfig, //CONTRACT: syncConfig and folderCmp correspond row-wise!
FolderComparison& folderCmp, //
+ const std::map<AbstractPath, size_t>& deviceParallelOps,
WarningDialogs& warnings,
ProcessCallback& callback);
}
diff --git a/FreeFileSync/Source/ui/batch_config.cpp b/FreeFileSync/Source/ui/batch_config.cpp
index fe647828..54643a5c 100755
--- a/FreeFileSync/Source/ui/batch_config.cpp
+++ b/FreeFileSync/Source/ui/batch_config.cpp
@@ -82,7 +82,10 @@ BatchDialog::BatchDialog(wxWindow* parent, BatchDialogConfig& dlgCfg) :
m_bitmapBatchJob->SetBitmap(getResourceImage(L"file_batch"));
- logfileDir_ = std::make_unique<FolderSelector>(*m_panelLogfile, *m_buttonSelectLogFolder, *m_bpButtonSelectAltLogFolder, *m_logFolderPath, nullptr /*staticText*/, nullptr /*wxWindow*/);
+ logfileDir_ = std::make_unique<FolderSelector>(*m_panelLogfile, *m_buttonSelectLogFolder, *m_bpButtonSelectAltLogFolder, *m_logFolderPath, nullptr /*staticText*/, nullptr /*wxWindow*/,
+ nullptr /*droppedPathsFilter*/,
+ [](const Zstring& folderPathPhrase) { return 1; } /*getDeviceParallelOps*/,
+ nullptr /*setDeviceParallelOps*/);
logfileDir_->setBackgroundText(utfTo<std::wstring>(getDefaultLogFolderPath()));
diff --git a/FreeFileSync/Source/ui/batch_status_handler.cpp b/FreeFileSync/Source/ui/batch_status_handler.cpp
index 6d49ac08..c5541cad 100755
--- a/FreeFileSync/Source/ui/batch_status_handler.cpp
+++ b/FreeFileSync/Source/ui/batch_status_handler.cpp
@@ -68,77 +68,6 @@ std::unique_ptr<AFS::OutputStream> prepareNewLogfile(const AbstractPath& logFold
return AFS::getOutputStream(logFilePath, nullptr, /*streamSize*/ notifyUnbufferedIO); //throw FileError
}
-
-
-struct LogTraverserCallback: public AFS::TraverserCallback
-{
- LogTraverserCallback(const Zstring& prefix, const std::function<void()>& onUpdateStatus) :
- prefix_(prefix),
- onUpdateStatus_(onUpdateStatus) {}
-
- void onFile(const FileInfo& fi) override
- {
- if (startsWith(fi.itemName, prefix_, CmpFilePath() /*even on Linux!*/) && endsWith(fi.itemName, Zstr(".log"), CmpFilePath()))
- logFileNames_.push_back(fi.itemName);
-
- if (onUpdateStatus_) onUpdateStatus_();
- }
- std::unique_ptr<TraverserCallback> onFolder (const FolderInfo& fi) override { return nullptr; }
- HandleLink onSymlink(const SymlinkInfo& si) override { return TraverserCallback::LINK_SKIP; }
-
- HandleError reportDirError (const std::wstring& msg, size_t retryNumber ) override { setError(msg); return ON_ERROR_CONTINUE; }
- HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zstring& itemName) override { setError(msg); return ON_ERROR_CONTINUE; }
-
- const std::vector<Zstring>& refFileNames() const { return logFileNames_; }
- const Opt<FileError>& getLastError() const { return lastError_; }
-
-private:
- void setError(const std::wstring& msg) //implement late failure
- {
- if (!lastError_)
- lastError_ = FileError(msg);
- }
-
- const Zstring prefix_;
- const std::function<void()> onUpdateStatus_;
- std::vector<Zstring> logFileNames_; //out
- Opt<FileError> lastError_;
-};
-
-
-void limitLogfileCount(const AbstractPath& logFolderPath, const std::wstring& jobname, size_t maxCount, //throw FileError
- const std::function<void(const std::wstring& msg)>& notifyStatus)
-{
- const Zstring prefix = utfTo<Zstring>(jobname);
- const std::wstring cleaningMsg = _("Cleaning up old log files...");;
-
- LogTraverserCallback lt(prefix, [&] { if (notifyStatus) notifyStatus(cleaningMsg); }); //traverse source directory one level deep
- AFS::traverseFolder(logFolderPath, lt);
-
- std::vector<Zstring> logFileNames = lt.refFileNames();
- Opt<FileError> lastError = lt.getLastError();
-
- if (logFileNames.size() > maxCount)
- {
- //delete oldest logfiles: take advantage of logfile naming convention to find them
- std::nth_element(logFileNames.begin(), logFileNames.end() - maxCount, logFileNames.end(), LessFilePath());
-
- std::for_each(logFileNames.begin(), logFileNames.end() - maxCount, [&](const Zstring& logFileName)
- {
- const AbstractPath filePath = AFS::appendRelPath(logFolderPath, logFileName);
- if (notifyStatus) notifyStatus(cleaningMsg + L" " + fmtPath(AFS::getDisplayPath(filePath)));
-
- try
- {
- AFS::removeFilePlain(filePath); //throw FileError
- }
- catch (const FileError& e) { if (!lastError) lastError = e; };
- });
- }
-
- if (lastError) //late failure!
- throw* lastError;
-}
}
//##############################################################################################################################
@@ -268,7 +197,7 @@ BatchStatusHandler::~BatchStatusHandler()
errorLog_.logMsg(replaceCpy(_("Executing command %x"), L"%x", fmtPath(commandLine)), MSG_TYPE_INFO);
//----------------- write results into user-specified logfile ------------------------
- const SummaryInfo summary =
+ const LogSummary summary =
{
jobName_,
finalStatusMsg,
@@ -413,9 +342,9 @@ void BatchStatusHandler::initNewPhase(int itemsTotal, int64_t bytesTotal, Proces
}
-void BatchStatusHandler::updateProcessedData(int itemsDelta, int64_t bytesDelta)
+void BatchStatusHandler::updateDataProcessed(int itemsDelta, int64_t bytesDelta)
{
- StatusHandler::updateProcessedData(itemsDelta, bytesDelta);
+ StatusHandler::updateDataProcessed(itemsDelta, bytesDelta);
if (progressDlg_)
progressDlg_->notifyProgressChange(); //noexcept
@@ -423,10 +352,9 @@ void BatchStatusHandler::updateProcessedData(int itemsDelta, int64_t bytesDelta)
}
-void BatchStatusHandler::reportInfo(const std::wstring& text)
+void BatchStatusHandler::logInfo(const std::wstring& msg)
{
- errorLog_.logMsg(text, MSG_TYPE_INFO); //log first!
- StatusHandler::reportInfo(text); //throw X
+ errorLog_.logMsg(msg, MSG_TYPE_INFO);
}
diff --git a/FreeFileSync/Source/ui/batch_status_handler.h b/FreeFileSync/Source/ui/batch_status_handler.h
index 55ceccb6..f3f3cc61 100755
--- a/FreeFileSync/Source/ui/batch_status_handler.h
+++ b/FreeFileSync/Source/ui/batch_status_handler.h
@@ -43,8 +43,8 @@ public:
~BatchStatusHandler();
void initNewPhase (int itemsTotal, int64_t bytesTotal, Phase phaseID) override;
- void updateProcessedData(int itemsDelta, int64_t bytesDelta) override;
- void reportInfo (const std::wstring& text) override;
+ void updateDataProcessed(int itemsDelta, int64_t bytesDelta) override;
+ void logInfo (const std::wstring& msg) override;
void forceUiRefreshNoThrow() override;
void reportWarning (const std::wstring& warningMessage, bool& warningActive) override;
diff --git a/FreeFileSync/Source/ui/folder_selector.cpp b/FreeFileSync/Source/ui/folder_selector.cpp
index ac501107..66e45aae 100755
--- a/FreeFileSync/Source/ui/folder_selector.cpp
+++ b/FreeFileSync/Source/ui/folder_selector.cpp
@@ -19,6 +19,7 @@
// #include <gtk/gtk.h>
+
using namespace zen;
using namespace fff;
@@ -62,7 +63,13 @@ FolderSelector::FolderSelector(wxWindow& dropWindow,
wxButton& selectAltFolderButton,
FolderHistoryBox& folderComboBox,
wxStaticText* staticText,
- wxWindow* dropWindow2) :
+ wxWindow* dropWindow2,
+ const std::function<bool (const std::vector<Zstring>& shellItemPaths)>& droppedPathsFilter,
+ const std::function<size_t(const Zstring& folderPathPhrase)>& getDeviceParallelOps,
+ const std::function<void (const Zstring& folderPathPhrase, size_t parallelOps)>& setDeviceParallelOps) :
+ droppedPathsFilter_ (droppedPathsFilter),
+ getDeviceParallelOps_(getDeviceParallelOps),
+ setDeviceParallelOps_(setDeviceParallelOps),
dropWindow_(dropWindow),
dropWindow2_(dropWindow2),
selectFolderButton_(selectFolderButton),
@@ -70,6 +77,8 @@ FolderSelector::FolderSelector(wxWindow& dropWindow,
folderComboBox_(folderComboBox),
staticText_(staticText)
{
+ assert(getDeviceParallelOps_);
+
auto setupDragDrop = [&](wxWindow& dropWin)
{
setupFileDrop(dropWin);
@@ -129,7 +138,7 @@ void FolderSelector::onItemPathDropped(FileDropEvent& event)
if (itemPaths.empty())
return;
- if (shouldSetDroppedPaths(itemPaths))
+ if (droppedPathsFilter_ && droppedPathsFilter_(itemPaths))
{
auto fmtShellPath = [](const Zstring& shellItemPath)
{
diff --git a/FreeFileSync/Source/ui/folder_selector.h b/FreeFileSync/Source/ui/folder_selector.h
index a2154ac6..732ecd72 100755
--- a/FreeFileSync/Source/ui/folder_selector.h
+++ b/FreeFileSync/Source/ui/folder_selector.h
@@ -12,6 +12,7 @@
#include <wx/button.h>
#include <wx+/file_drop.h>
#include "folder_history_box.h"
+#include "../fs/abstract.h"
namespace fff
@@ -37,7 +38,10 @@ public:
wxButton& selectAltFolderButton,
FolderHistoryBox& folderComboBox,
wxStaticText* staticText, //optional
- wxWindow* dropWindow2); //
+ wxWindow* dropWindow2, //
+ const std::function<bool (const std::vector<Zstring>& shellItemPaths)>& droppedPathsFilter, //optional
+ const std::function<size_t(const Zstring& folderPathPhrase)>& getDeviceParallelOps, //mandatory
+ const std::function<void (const Zstring& folderPathPhrase, size_t parallelOps)>& setDeviceParallelOps); //optional
~FolderSelector();
@@ -49,20 +53,22 @@ public:
void setBackgroundText(const std::wstring& text) { folderComboBox_.SetHint(text); }
private:
- virtual bool shouldSetDroppedPaths(const std::vector<Zstring>& shellItemPaths) { return true; } //return true if drop should be processed
-
void onMouseWheel (wxMouseEvent& event);
void onItemPathDropped(zen::FileDropEvent& event);
void onEditFolderPath (wxCommandEvent& event);
void onSelectFolder (wxCommandEvent& event);
void onSelectAltFolder(wxCommandEvent& event);
+ const std::function<bool (const std::vector<Zstring>& shellItemPaths)> droppedPathsFilter_;
+ const std::function<size_t(const Zstring& folderPathPhrase)> getDeviceParallelOps_;
+ const std::function<void (const Zstring& folderPathPhrase, size_t parallelOps)> setDeviceParallelOps_;
+
wxWindow& dropWindow_;
wxWindow* dropWindow2_ = nullptr;
wxButton& selectFolderButton_;
wxButton& selectAltFolderButton_;
FolderHistoryBox& folderComboBox_;
- wxStaticText* staticText_ = nullptr; //optional
+ wxStaticText* staticText_ = nullptr; //optional
FolderSelector* siblingSelector_ = nullptr;
};
}
diff --git a/FreeFileSync/Source/ui/gui_generated.cpp b/FreeFileSync/Source/ui/gui_generated.cpp
index 47843381..0ee3fbd0 100755
--- a/FreeFileSync/Source/ui/gui_generated.cpp
+++ b/FreeFileSync/Source/ui/gui_generated.cpp
@@ -1200,6 +1200,9 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
m_panelComparisonSettings = new wxPanel( m_panelCompSettingsTab, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panelComparisonSettings->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+ wxBoxSizer* bSizer2561;
+ bSizer2561 = new wxBoxSizer( wxHORIZONTAL );
+
wxBoxSizer* bSizer159;
bSizer159 = new wxBoxSizer( wxVERTICAL );
@@ -1237,9 +1240,6 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer178->Add( bSizer182, 0, wxALL, 5 );
- m_staticline42 = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
- bSizer178->Add( m_staticline42, 0, wxEXPAND, 5 );
-
wxBoxSizer* bSizer2371;
bSizer2371 = new wxBoxSizer( wxHORIZONTAL );
@@ -1281,13 +1281,16 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer176->Add( m_radioBtnSymlinksDirect, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
- bSizer1721->Add( bSizer176, 0, wxEXPAND|wxLEFT, 18 );
+ bSizer1721->Add( bSizer176, 0, wxLEFT|wxEXPAND, 18 );
+
+
+ bSizer1721->Add( 0, 0, 1, wxEXPAND, 5 );
m_hyperlink24 = new wxHyperlinkCtrl( m_panelComparisonSettings, wxID_ANY, _("More information"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
bSizer1721->Add( m_hyperlink24, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
- bSizer1734->Add( bSizer1721, 0, wxALL, 5 );
+ bSizer1734->Add( bSizer1721, 0, wxALL|wxEXPAND, 5 );
m_staticline44 = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
bSizer1734->Add( m_staticline44, 0, wxEXPAND, 5 );
@@ -1322,11 +1325,14 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer1733->Add( bSizer197, 0, 0, 5 );
+
+ bSizer1733->Add( 0, 0, 1, wxEXPAND, 5 );
+
m_hyperlink241 = new wxHyperlinkCtrl( m_panelComparisonSettings, wxID_ANY, _("Handle daylight saving time"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
bSizer1733->Add( m_hyperlink241, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
- bSizer1734->Add( bSizer1733, 0, wxALL, 5 );
+ bSizer1734->Add( bSizer1733, 0, wxALL|wxEXPAND, 5 );
m_staticline441 = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
bSizer1734->Add( m_staticline441, 0, wxEXPAND, 5 );
@@ -1338,10 +1344,77 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer159->Add( m_staticline331, 0, wxEXPAND, 5 );
- m_panelComparisonSettings->SetSizer( bSizer159 );
+ bSizer2561->Add( bSizer159, 1, wxEXPAND, 5 );
+
+ m_staticlinePerformance = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
+ bSizer2561->Add( m_staticlinePerformance, 0, wxEXPAND, 5 );
+
+ bSizerPerformance = new wxBoxSizer( wxVERTICAL );
+
+ m_staticTextPerfDeRequired = new wxStaticText( m_panelComparisonSettings, wxID_ANY, _("Requires FreeFileSync Donation Edition"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextPerfDeRequired->Wrap( -1 );
+ bSizerPerformance->Add( m_staticTextPerfDeRequired, 0, wxALL, 5 );
+
+ m_staticlinePerfDeRequired = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizerPerformance->Add( m_staticlinePerfDeRequired, 0, wxEXPAND, 5 );
+
+ m_panelPerfHeader = new wxPanel( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ m_panelPerfHeader->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
+
+ wxBoxSizer* bSizer2191;
+ bSizer2191 = new wxBoxSizer( wxHORIZONTAL );
+
+ m_bitmapPerf = new wxStaticBitmap( m_panelPerfHeader, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
+ bSizer2191->Add( m_bitmapPerf, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
+
+ m_staticText13611 = new wxStaticText( m_panelPerfHeader, wxID_ANY, _("Performance improvements:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText13611->Wrap( -1 );
+ bSizer2191->Add( m_staticText13611, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 10 );
+
+
+ m_panelPerfHeader->SetSizer( bSizer2191 );
+ m_panelPerfHeader->Layout();
+ bSizer2191->Fit( m_panelPerfHeader );
+ bSizerPerformance->Add( m_panelPerfHeader, 0, wxEXPAND, 5 );
+
+ wxStaticLine* m_staticline75;
+ m_staticline75 = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizerPerformance->Add( m_staticline75, 0, wxEXPAND, 5 );
+
+ bSizer260 = new wxBoxSizer( wxVERTICAL );
+
+ m_staticTextPerfParallelOps = new wxStaticText( m_panelComparisonSettings, wxID_ANY, _("Parallel file operations:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextPerfParallelOps->Wrap( -1 );
+ bSizer260->Add( m_staticTextPerfParallelOps, 0, wxTOP|wxRIGHT|wxLEFT, 5 );
+
+ m_scrolledWindowPerf = new wxScrolledWindow( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL );
+ m_scrolledWindowPerf->SetScrollRate( 5, 5 );
+ m_scrolledWindowPerf->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+
+ fgSizerPerf = new wxFlexGridSizer( 0, 2, 5, 5 );
+ fgSizerPerf->SetFlexibleDirection( wxBOTH );
+ fgSizerPerf->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
+
+
+ m_scrolledWindowPerf->SetSizer( fgSizerPerf );
+ m_scrolledWindowPerf->Layout();
+ fgSizerPerf->Fit( m_scrolledWindowPerf );
+ bSizer260->Add( m_scrolledWindowPerf, 1, wxALL|wxEXPAND, 5 );
+
+ m_hyperlink1711 = new wxHyperlinkCtrl( m_panelComparisonSettings, wxID_ANY, _("How to get best performance?"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ bSizer260->Add( m_hyperlink1711, 0, wxALL, 5 );
+
+
+ bSizerPerformance->Add( bSizer260, 1, wxALL|wxEXPAND, 5 );
+
+
+ bSizer2561->Add( bSizerPerformance, 0, wxEXPAND, 5 );
+
+
+ m_panelComparisonSettings->SetSizer( bSizer2561 );
m_panelComparisonSettings->Layout();
- bSizer159->Fit( m_panelComparisonSettings );
- bSizer275->Add( m_panelComparisonSettings, 0, wxEXPAND, 5 );
+ bSizer2561->Fit( m_panelComparisonSettings );
+ bSizer275->Add( m_panelComparisonSettings, 1, wxEXPAND, 5 );
m_panelCompSettingsTab->SetSizer( bSizer275 );
@@ -1404,9 +1477,6 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer166->Add( bSizer1661, 3, wxEXPAND|wxLEFT, 5 );
- m_staticline22 = new wxStaticLine( m_panelFilterSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
- bSizer166->Add( m_staticline22, 0, wxEXPAND, 5 );
-
bSizer166->Add( 0, 10, 0, 0, 5 );
@@ -1444,6 +1514,12 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer166->Add( bSizer1651, 5, wxEXPAND|wxLEFT, 5 );
+ m_staticTextFilterDescr = new wxStaticText( m_panelFilterSettings, wxID_ANY, _("Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair."), wxDefaultPosition, wxSize( -1, -1 ), 0 );
+ m_staticTextFilterDescr->Wrap( -1 );
+ m_staticTextFilterDescr->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
+
+ bSizer166->Add( m_staticTextFilterDescr, 0, wxALL, 10 );
+
bSizer1591->Add( bSizer166, 1, wxEXPAND, 5 );
@@ -1453,36 +1529,6 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
wxBoxSizer* bSizer160;
bSizer160 = new wxBoxSizer( wxVERTICAL );
- wxBoxSizer* bSizer167;
- bSizer167 = new wxBoxSizer( wxHORIZONTAL );
-
- m_bitmapFilterDate = new wxStaticBitmap( m_panelFilterSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 );
- bSizer167->Add( m_bitmapFilterDate, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 );
-
- wxBoxSizer* bSizer165;
- bSizer165 = new wxBoxSizer( wxVERTICAL );
-
- m_staticText79 = new wxStaticText( m_panelFilterSettings, wxID_ANY, _("Time span:"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticText79->Wrap( -1 );
- bSizer165->Add( m_staticText79, 0, wxBOTTOM, 5 );
-
- m_spinCtrlTimespan = new wxSpinCtrl( m_panelFilterSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 2000000000, 0 );
- bSizer165->Add( m_spinCtrlTimespan, 0, wxEXPAND, 5 );
-
- wxArrayString m_choiceUnitTimespanChoices;
- m_choiceUnitTimespan = new wxChoice( m_panelFilterSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceUnitTimespanChoices, 0 );
- m_choiceUnitTimespan->SetSelection( 0 );
- bSizer165->Add( m_choiceUnitTimespan, 0, wxEXPAND, 5 );
-
-
- bSizer167->Add( bSizer165, 1, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 );
-
-
- bSizer160->Add( bSizer167, 1, wxEXPAND|wxALL, 5 );
-
- m_staticline23 = new wxStaticLine( m_panelFilterSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
- bSizer160->Add( m_staticline23, 0, wxEXPAND, 5 );
-
wxBoxSizer* bSizer168;
bSizer168 = new wxBoxSizer( wxHORIZONTAL );
@@ -1536,44 +1582,58 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer168->Add( bSizer158, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 );
- bSizer160->Add( bSizer168, 0, wxEXPAND|wxALL, 5 );
+ bSizer160->Add( bSizer168, 2, wxEXPAND|wxALL, 5 );
+ m_staticline23 = new wxStaticLine( m_panelFilterSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizer160->Add( m_staticline23, 0, wxEXPAND, 5 );
- bSizer1591->Add( bSizer160, 0, wxEXPAND, 5 );
+ wxBoxSizer* bSizer167;
+ bSizer167 = new wxBoxSizer( wxHORIZONTAL );
+ m_bitmapFilterDate = new wxStaticBitmap( m_panelFilterSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 );
+ bSizer167->Add( m_bitmapFilterDate, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 );
- m_panelFilterSettings->SetSizer( bSizer1591 );
- m_panelFilterSettings->Layout();
- bSizer1591->Fit( m_panelFilterSettings );
- bSizer278->Add( m_panelFilterSettings, 1, wxEXPAND, 5 );
+ wxBoxSizer* bSizer165;
+ bSizer165 = new wxBoxSizer( wxVERTICAL );
- m_staticline62 = new wxStaticLine( m_panelFilterSettingsTab, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
- bSizer278->Add( m_staticline62, 0, wxEXPAND, 5 );
+ m_staticText79 = new wxStaticText( m_panelFilterSettings, wxID_ANY, _("Time span:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText79->Wrap( -1 );
+ bSizer165->Add( m_staticText79, 0, wxBOTTOM, 5 );
- wxBoxSizer* bSizer280;
- bSizer280 = new wxBoxSizer( wxHORIZONTAL );
+ m_spinCtrlTimespan = new wxSpinCtrl( m_panelFilterSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 2000000000, 0 );
+ bSizer165->Add( m_spinCtrlTimespan, 0, wxEXPAND, 5 );
- m_staticTextFilterDescr = new wxStaticText( m_panelFilterSettingsTab, wxID_ANY, _("Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair."), wxDefaultPosition, wxSize( -1, -1 ), 0 );
- m_staticTextFilterDescr->Wrap( -1 );
- bSizer280->Add( m_staticTextFilterDescr, 0, wxALIGN_CENTER_VERTICAL|wxALL, 10 );
+ wxArrayString m_choiceUnitTimespanChoices;
+ m_choiceUnitTimespan = new wxChoice( m_panelFilterSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceUnitTimespanChoices, 0 );
+ m_choiceUnitTimespan->SetSelection( 0 );
+ bSizer165->Add( m_choiceUnitTimespan, 0, wxEXPAND, 5 );
- bSizer280->Add( 0, 0, 1, wxEXPAND, 5 );
+ bSizer167->Add( bSizer165, 1, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 );
- m_staticline46 = new wxStaticLine( m_panelFilterSettingsTab, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
- bSizer280->Add( m_staticline46, 0, wxEXPAND, 5 );
- m_buttonClear = new wxButton( m_panelFilterSettingsTab, wxID_ANY, _("C&lear"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
- bSizer280->Add( m_buttonClear, 0, wxALL|wxALIGN_CENTER_VERTICAL, 10 );
+ bSizer160->Add( bSizer167, 1, wxEXPAND|wxALL, 5 );
+
+ m_staticline231 = new wxStaticLine( m_panelFilterSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizer160->Add( m_staticline231, 0, wxEXPAND, 5 );
+
+ m_buttonClear = new wxButton( m_panelFilterSettings, wxID_ANY, _("C&lear"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
+ bSizer160->Add( m_buttonClear, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 10 );
+
+
+ bSizer1591->Add( bSizer160, 0, wxEXPAND, 5 );
- bSizer278->Add( bSizer280, 0, wxEXPAND, 5 );
+ m_panelFilterSettings->SetSizer( bSizer1591 );
+ m_panelFilterSettings->Layout();
+ bSizer1591->Fit( m_panelFilterSettings );
+ bSizer278->Add( m_panelFilterSettings, 1, wxEXPAND, 5 );
m_panelFilterSettingsTab->SetSizer( bSizer278 );
m_panelFilterSettingsTab->Layout();
bSizer278->Fit( m_panelFilterSettingsTab );
- m_notebook->AddPage( m_panelFilterSettingsTab, _("dummy"), false );
+ m_notebook->AddPage( m_panelFilterSettingsTab, _("dummy"), true );
m_panelSyncSettingsTab = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panelSyncSettingsTab->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
@@ -1640,9 +1700,6 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer237->Add( bSizer235, 0, wxALL, 5 );
- m_staticline53 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
- bSizer237->Add( m_staticline53, 0, wxEXPAND, 5 );
-
wxBoxSizer* bSizer238;
bSizer238 = new wxBoxSizer( wxVERTICAL );
@@ -1755,16 +1812,25 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
wxBoxSizer* bSizer201;
bSizer201 = new wxBoxSizer( wxHORIZONTAL );
+ m_staticline72 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
+ bSizer201->Add( m_staticline72, 0, wxEXPAND, 5 );
+
+ wxBoxSizer* bSizer249;
+ bSizer249 = new wxBoxSizer( wxHORIZONTAL );
+
m_checkBoxDetectMove = new wxCheckBox( m_panelSyncSettings, wxID_ANY, _("Detect moved files"), wxDefaultPosition, wxDefaultSize, 0 );
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 );
+ bSizer249->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 );
+ bSizer249->Add( m_hyperlink242, 0, wxTOP|wxBOTTOM|wxRIGHT, 5 );
+
+
+ bSizer201->Add( bSizer249, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
- bSizer238->Add( bSizer201, 0, wxALL, 5 );
+ bSizer238->Add( bSizer201, 0, 0, 5 );
bSizer237->Add( bSizer238, 1, wxEXPAND, 5 );
@@ -1802,9 +1868,6 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer2361->Add( bSizer202, 0, wxALL, 5 );
- m_staticline531 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
- bSizer2361->Add( m_staticline531, 0, wxEXPAND, 5 );
-
bSizerVersioningHolder = new wxBoxSizer( wxVERTICAL );
@@ -1942,7 +2005,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
m_staticTextAutoRetryDelay->Wrap( -1 );
fgSizerAutoRetry->Add( m_staticTextAutoRetryDelay, 0, wxALIGN_CENTER_VERTICAL, 5 );
- m_spinCtrlAutoRetryCount = new wxSpinCtrl( m_panelSyncSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1, -1 ), wxSP_ARROW_KEYS, 1, 2000000000, 0 );
+ m_spinCtrlAutoRetryCount = new wxSpinCtrl( m_panelSyncSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1, -1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 );
fgSizerAutoRetry->Add( m_spinCtrlAutoRetryCount, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_spinCtrlAutoRetryDelay = new wxSpinCtrl( m_panelSyncSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1, -1 ), wxSP_ARROW_KEYS, 0, 2000000000, 0 );
@@ -1991,7 +2054,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
m_panelSyncSettingsTab->SetSizer( bSizer276 );
m_panelSyncSettingsTab->Layout();
bSizer276->Fit( m_panelSyncSettingsTab );
- m_notebook->AddPage( m_panelSyncSettingsTab, _("dummy"), true );
+ m_notebook->AddPage( m_panelSyncSettingsTab, _("dummy"), false );
bSizer190->Add( m_notebook, 1, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 );
@@ -2034,12 +2097,13 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
m_hyperlink24->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( ConfigDlgGenerated::OnHelpComparisonSettings ), NULL, this );
m_textCtrlTimeShift->Connect( wxEVT_KEY_DOWN, wxKeyEventHandler( ConfigDlgGenerated::onlTimeShiftKeyDown ), NULL, this );
m_hyperlink241->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( ConfigDlgGenerated::OnHelpTimeShift ), NULL, this );
+ m_hyperlink1711->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( ConfigDlgGenerated::OnHelpPerformance ), NULL, this );
m_textCtrlInclude->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeFilterOption ), NULL, this );
m_hyperlink171->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( ConfigDlgGenerated::OnHelpShowExamples ), NULL, this );
m_textCtrlExclude->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeFilterOption ), NULL, this );
- m_choiceUnitTimespan->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeFilterOption ), NULL, this );
m_choiceUnitMinSize->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeFilterOption ), NULL, this );
m_choiceUnitMaxSize->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeFilterOption ), NULL, this );
+ m_choiceUnitTimespan->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeFilterOption ), NULL, this );
m_buttonClear->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnFilterReset ), NULL, this );
m_checkBoxUseLocalSyncOptions->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnToggleLocalSyncSettings ), NULL, this );
m_toggleBtnTwoWay->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( ConfigDlgGenerated::OnSyncTwoWayDouble ), NULL, this );
@@ -2297,20 +2361,23 @@ CloudSetupDlgGenerated::CloudSetupDlgGenerated( wxWindow* parent, wxWindowID id,
bSizer185->Fit( m_panel41 );
bSizer134->Add( m_panel41, 0, wxEXPAND, 5 );
- bSizerSftpTweaks = new wxBoxSizer( wxVERTICAL );
+ bSizer255 = new wxBoxSizer( wxVERTICAL );
m_staticline571 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
- bSizerSftpTweaks->Add( m_staticline571, 0, wxEXPAND, 5 );
+ bSizer255->Add( m_staticline571, 0, wxEXPAND, 5 );
wxBoxSizer* bSizer219;
bSizer219 = new wxBoxSizer( wxHORIZONTAL );
- m_bitmapSpeedSftp = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
- bSizer219->Add( m_bitmapSpeedSftp, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 );
+
+ bSizer219->Add( 5, 0, 0, 0, 5 );
+
+ m_bitmapPerf = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
+ bSizer219->Add( m_bitmapPerf, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_staticText1361 = new wxStaticText( this, wxID_ANY, _("Performance improvements:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText1361->Wrap( -1 );
- bSizer219->Add( m_staticText1361, 0, wxALL|wxALIGN_CENTER_VERTICAL, 10 );
+ bSizer219->Add( m_staticText1361, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 10 );
bSizer219->Add( 0, 0, 1, wxEXPAND, 5 );
@@ -2319,10 +2386,10 @@ CloudSetupDlgGenerated::CloudSetupDlgGenerated( wxWindow* parent, wxWindowID id,
bSizer219->Add( m_hyperlink171, 0, wxALL|wxALIGN_CENTER_VERTICAL, 10 );
- bSizerSftpTweaks->Add( bSizer219, 0, wxEXPAND, 5 );
+ bSizer255->Add( bSizer219, 0, wxEXPAND, 5 );
m_staticline57 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
- bSizerSftpTweaks->Add( m_staticline57, 0, wxEXPAND, 5 );
+ bSizer255->Add( m_staticline57, 0, wxEXPAND, 5 );
m_panel411 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panel411->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
@@ -2336,28 +2403,37 @@ CloudSetupDlgGenerated::CloudSetupDlgGenerated( wxWindow* parent, wxWindowID id,
fgSizer1611->SetFlexibleDirection( wxBOTH );
fgSizer1611->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
- m_staticText12341 = new wxStaticText( m_panel411, wxID_ANY, _("Connections for directory reading:"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticText12341->Wrap( -1 );
- fgSizer1611->Add( m_staticText12341, 0, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5 );
+ bSizerConnectionsLabel = new wxBoxSizer( wxVERTICAL );
+
+ m_staticTextConnectionsLabel = new wxStaticText( m_panel411, wxID_ANY, _("Parallel file operations:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextConnectionsLabel->Wrap( -1 );
+ bSizerConnectionsLabel->Add( m_staticTextConnectionsLabel, 0, 0, 5 );
+
+ m_staticTextConnectionsLabelSub = new wxStaticText( m_panel411, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextConnectionsLabelSub->Wrap( -1 );
+ bSizerConnectionsLabel->Add( m_staticTextConnectionsLabelSub, 0, wxALIGN_RIGHT, 5 );
+
- m_spinCtrlConnectionCountSftp = new wxSpinCtrl( m_panel411, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1, -1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 );
- fgSizer1611->Add( m_spinCtrlConnectionCountSftp, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
+ fgSizer1611->Add( bSizerConnectionsLabel, 0, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 5 );
- m_staticTextSftpConnectionCountHint = new wxStaticText( m_panel411, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticTextSftpConnectionCountHint->Wrap( -1 );
- m_staticTextSftpConnectionCountHint->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
+ m_spinCtrlConnectionCount = new wxSpinCtrl( m_panel411, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1, -1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 );
+ fgSizer1611->Add( m_spinCtrlConnectionCount, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
- fgSizer1611->Add( m_staticTextSftpConnectionCountHint, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
+ m_staticTextConnectionCountDescr = new wxStaticText( m_panel411, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextConnectionCountDescr->Wrap( -1 );
+ m_staticTextConnectionCountDescr->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
- m_staticText1231111 = new wxStaticText( m_panel411, wxID_ANY, _("SFTP channels per connection:"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticText1231111->Wrap( -1 );
- fgSizer1611->Add( m_staticText1231111, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxTOP|wxBOTTOM|wxLEFT, 5 );
+ fgSizer1611->Add( m_staticTextConnectionCountDescr, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
+
+ m_staticTextChannelCountSftp = new wxStaticText( m_panel411, wxID_ANY, _("SFTP channels per connection:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextChannelCountSftp->Wrap( -1 );
+ fgSizer1611->Add( m_staticTextChannelCountSftp, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxTOP|wxBOTTOM|wxLEFT, 5 );
m_spinCtrlChannelCountSftp = new wxSpinCtrl( m_panel411, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1, -1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 );
fgSizer1611->Add( m_spinCtrlChannelCountSftp, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
- m_button42 = new wxButton( m_panel411, wxID_ANY, _("Detect server limit"), wxDefaultPosition, wxDefaultSize, 0 );
- fgSizer1611->Add( m_button42, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
+ m_buttonChannelCountSftp = new wxButton( m_panel411, wxID_ANY, _("Detect server limit"), wxDefaultPosition, wxDefaultSize, 0 );
+ fgSizer1611->Add( m_buttonChannelCountSftp, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
bSizer1851->Add( fgSizer1611, 0, wxALL, 5 );
@@ -2366,74 +2442,10 @@ CloudSetupDlgGenerated::CloudSetupDlgGenerated( wxWindow* parent, wxWindowID id,
m_panel411->SetSizer( bSizer1851 );
m_panel411->Layout();
bSizer1851->Fit( m_panel411 );
- bSizerSftpTweaks->Add( m_panel411, 1, wxEXPAND, 5 );
-
-
- bSizer134->Add( bSizerSftpTweaks, 1, wxEXPAND, 5 );
-
- bSizerFtpTweaks = new wxBoxSizer( wxVERTICAL );
-
- m_staticline5711 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
- bSizerFtpTweaks->Add( m_staticline5711, 0, wxEXPAND, 5 );
-
- wxBoxSizer* bSizer2191;
- bSizer2191 = new wxBoxSizer( wxHORIZONTAL );
-
- m_bitmapSpeedFtp = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
- bSizer2191->Add( m_bitmapSpeedFtp, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 );
-
- m_staticText13611 = new wxStaticText( this, wxID_ANY, _("Performance improvements:"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticText13611->Wrap( -1 );
- bSizer2191->Add( m_staticText13611, 0, wxALL|wxALIGN_CENTER_VERTICAL, 10 );
-
-
- bSizer2191->Add( 0, 0, 1, wxEXPAND, 5 );
-
- m_hyperlink1711 = new wxHyperlinkCtrl( this, wxID_ANY, _("How to get best performance?"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
- bSizer2191->Add( m_hyperlink1711, 0, wxALL|wxALIGN_CENTER_VERTICAL, 10 );
-
+ bSizer255->Add( m_panel411, 1, wxEXPAND, 5 );
- bSizerFtpTweaks->Add( bSizer2191, 0, wxEXPAND, 5 );
- m_staticline573 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
- bSizerFtpTweaks->Add( m_staticline573, 0, wxEXPAND, 5 );
-
- m_panel4111 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
- m_panel4111->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
-
- wxBoxSizer* bSizer18511;
- bSizer18511 = new wxBoxSizer( wxVERTICAL );
-
- wxFlexGridSizer* fgSizer16111;
- fgSizer16111 = new wxFlexGridSizer( 0, 3, 0, 0 );
- fgSizer16111->AddGrowableCol( 1 );
- fgSizer16111->SetFlexibleDirection( wxBOTH );
- fgSizer16111->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
-
- m_staticText123411 = new wxStaticText( m_panel4111, wxID_ANY, _("Connections for directory reading:"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticText123411->Wrap( -1 );
- fgSizer16111->Add( m_staticText123411, 0, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5 );
-
- m_spinCtrlConnectionCountFtp = new wxSpinCtrl( m_panel4111, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1, -1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 );
- fgSizer16111->Add( m_spinCtrlConnectionCountFtp, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
-
- m_staticTextFtpConnectionCountHint = new wxStaticText( m_panel4111, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticTextFtpConnectionCountHint->Wrap( -1 );
- m_staticTextFtpConnectionCountHint->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
-
- fgSizer16111->Add( m_staticTextFtpConnectionCountHint, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
-
-
- bSizer18511->Add( fgSizer16111, 0, wxALL, 5 );
-
-
- m_panel4111->SetSizer( bSizer18511 );
- m_panel4111->Layout();
- bSizer18511->Fit( m_panel4111 );
- bSizerFtpTweaks->Add( m_panel4111, 1, wxEXPAND, 5 );
-
-
- bSizer134->Add( bSizerFtpTweaks, 1, wxEXPAND, 5 );
+ bSizer134->Add( bSizer255, 1, wxEXPAND, 5 );
m_staticline12 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
bSizer134->Add( m_staticline12, 0, wxEXPAND, 5 );
@@ -2469,9 +2481,8 @@ CloudSetupDlgGenerated::CloudSetupDlgGenerated( wxWindow* parent, wxWindowID id,
m_buttonSelectKeyfile->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( CloudSetupDlgGenerated::OnSelectKeyfile ), NULL, this );
m_checkBoxShowPassword->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( CloudSetupDlgGenerated::OnToggleShowPassword ), NULL, this );
m_buttonSelectFolder->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( CloudSetupDlgGenerated::OnBrowseCloudFolder ), NULL, this );
- m_hyperlink171->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( CloudSetupDlgGenerated::OnHelpSftpPerformance ), NULL, this );
- m_button42->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( CloudSetupDlgGenerated::OnDetectServerChannelLimit ), NULL, this );
- m_hyperlink1711->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( CloudSetupDlgGenerated::OnHelpSftpPerformance ), NULL, this );
+ m_hyperlink171->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( CloudSetupDlgGenerated::OnHelpFtpPerformance ), NULL, this );
+ m_buttonChannelCountSftp->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( CloudSetupDlgGenerated::OnDetectServerChannelLimit ), NULL, this );
m_buttonOkay->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( CloudSetupDlgGenerated::OnOkay ), NULL, this );
m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( CloudSetupDlgGenerated::OnCancel ), NULL, this );
}
@@ -4140,99 +4151,78 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
m_staticline341 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
bSizer162->Add( m_staticline341, 0, wxEXPAND, 5 );
- wxBoxSizer* bSizer174;
- bSizer174 = new wxBoxSizer( wxHORIZONTAL );
-
- wxBoxSizer* bSizer181;
- bSizer181 = new wxBoxSizer( wxVERTICAL );
-
- wxBoxSizer* bSizer187;
- bSizer187 = new wxBoxSizer( wxVERTICAL );
-
- m_staticText96 = new wxStaticText( m_panel41, wxID_ANY, _("Source code written in C++ using:"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticText96->Wrap( -1 );
- bSizer187->Add( m_staticText96, 0, wxALL, 5 );
-
- wxBoxSizer* bSizer171;
- bSizer171 = new wxBoxSizer( wxHORIZONTAL );
+ wxBoxSizer* bSizer186;
+ bSizer186 = new wxBoxSizer( wxVERTICAL );
- m_hyperlink11 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("MS Visual Studio"), wxT("https://www.visualstudio.com"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
- m_hyperlink11->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- m_hyperlink11->SetToolTip( _("https://www.visualstudio.com") );
+ m_staticText94 = new wxStaticText( m_panel41, wxID_ANY, _("Feedback and suggestions are welcome"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText94->Wrap( -1 );
+ bSizer186->Add( m_staticText94, 0, wxALL, 5 );
- bSizer171->Add( m_hyperlink11, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+ wxBoxSizer* bSizer166;
+ bSizer166 = new wxBoxSizer( wxHORIZONTAL );
- m_hyperlink7 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("wxWidgets"), wxT("http://www.wxwidgets.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
- m_hyperlink7->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- m_hyperlink7->SetToolTip( _("http://www.wxwidgets.org") );
- bSizer171->Add( m_hyperlink7, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+ bSizer166->Add( 0, 0, 1, wxEXPAND, 5 );
- m_hyperlink14 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("wxFormBuilder"), wxT("https://github.com/wxFormBuilder/wxFormBuilder"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
- m_hyperlink14->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- m_hyperlink14->SetToolTip( _("https://github.com/wxFormBuilder/wxFormBuilder") );
+ m_bitmapHomepage = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 );
+ m_bitmapHomepage->SetToolTip( _("Home page") );
- bSizer171->Add( m_hyperlink14, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+ bSizer166->Add( m_bitmapHomepage, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
- m_hyperlink16 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Artistic Style"), wxT("http://astyle.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
- m_hyperlink16->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- m_hyperlink16->SetToolTip( _("http://astyle.sourceforge.net") );
+ m_hyperlink1 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("FreeFileSync.org"), wxT("https://www.freefilesync.org/"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink1->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, true, wxEmptyString ) );
+ m_hyperlink1->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+ m_hyperlink1->SetToolTip( _("https://www.freefilesync.org") );
- bSizer171->Add( m_hyperlink16, 0, wxALIGN_CENTER_VERTICAL, 5 );
+ bSizer166->Add( m_hyperlink1, 0, wxALIGN_CENTER_VERTICAL, 5 );
- bSizer187->Add( bSizer171, 0, wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
+ bSizer166->Add( 0, 0, 1, wxEXPAND, 5 );
- wxBoxSizer* bSizer172;
- bSizer172 = new wxBoxSizer( wxHORIZONTAL );
+ m_bitmapForum = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 );
+ m_bitmapForum->SetToolTip( _("FreeFileSync Forum") );
- m_hyperlink15 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("zen::Xml"), wxT("http://zenxml.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
- m_hyperlink15->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- m_hyperlink15->SetToolTip( _("http://zenxml.sourceforge.net") );
+ bSizer166->Add( m_bitmapForum, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
- bSizer172->Add( m_hyperlink15, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+ m_hyperlink21 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("FreeFileSync Forum"), wxT("https://www.freefilesync.org/forum/"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink21->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, true, wxEmptyString ) );
+ m_hyperlink21->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+ m_hyperlink21->SetToolTip( _("https://www.freefilesync.org/forum/") );
- m_hyperlink12 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Google Test"), wxT("https://github.com/google/googletest"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
- m_hyperlink12->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- m_hyperlink12->SetToolTip( _("https://github.com/google/googletest") );
+ bSizer166->Add( m_hyperlink21, 0, wxALIGN_CENTER_VERTICAL, 5 );
- bSizer172->Add( m_hyperlink12, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
- m_hyperlink13 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Boost"), wxT("http://www.boost.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
- m_hyperlink13->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- m_hyperlink13->SetToolTip( _("http://www.boost.org") );
+ bSizer166->Add( 0, 0, 1, wxEXPAND, 5 );
- bSizer172->Add( m_hyperlink13, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+ m_bitmapEmail = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 );
+ m_bitmapEmail->SetToolTip( _("Email") );
- m_hyperlink10 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("libssh2"), wxT("https://www.libssh2.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
- m_hyperlink10->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- m_hyperlink10->SetToolTip( _("https://www.libssh2.org") );
+ bSizer166->Add( m_bitmapEmail, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
- bSizer172->Add( m_hyperlink10, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+ m_hyperlink2 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("zenju@freefilesync.org"), wxT("mailto:zenju@freefilesync.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink2->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, true, wxEmptyString ) );
+ m_hyperlink2->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+ m_hyperlink2->SetToolTip( _("mailto:zenju@freefilesync.org") );
- m_hyperlink101 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("libcurl"), wxT("https://curl.haxx.se/libcurl"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
- m_hyperlink101->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- m_hyperlink101->SetToolTip( _("https://curl.haxx.se/libcurl") );
+ bSizer166->Add( m_hyperlink2, 0, wxALIGN_CENTER_VERTICAL, 5 );
- bSizer172->Add( m_hyperlink101, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 );
- m_hyperlink18 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("NSIS"), wxT("http://nsis.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
- m_hyperlink18->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- m_hyperlink18->SetToolTip( _("http://nsis.sourceforge.net") );
+ bSizer166->Add( 0, 0, 1, wxEXPAND, 5 );
- bSizer172->Add( m_hyperlink18, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
- m_hyperlink9 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Inno Setup"), wxT("http://www.jrsoftware.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
- m_hyperlink9->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- m_hyperlink9->SetToolTip( _("http://www.jrsoftware.org") );
+ bSizer186->Add( bSizer166, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 );
- bSizer172->Add( m_hyperlink9, 0, wxALIGN_CENTER_VERTICAL, 5 );
+ bSizer162->Add( bSizer186, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 );
- bSizer187->Add( bSizer172, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 );
+ m_staticline3412 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizer162->Add( m_staticline3412, 0, wxEXPAND, 5 );
+ wxBoxSizer* bSizer174;
+ bSizer174 = new wxBoxSizer( wxHORIZONTAL );
- bSizer181->Add( bSizer187, 0, wxALL|wxEXPAND, 5 );
+ wxBoxSizer* bSizer181;
+ bSizer181 = new wxBoxSizer( wxVERTICAL );
m_panelDonate = new wxPanel( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panelDonate->SetBackgroundColour( wxColour( 153, 170, 187 ) );
@@ -4284,7 +4274,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
m_panelDonate->SetSizer( bSizer183 );
m_panelDonate->Layout();
bSizer183->Fit( m_panelDonate );
- bSizer181->Add( m_panelDonate, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 );
+ bSizer181->Add( m_panelDonate, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 10 );
m_panelThankYou = new wxPanel( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panelThankYou->SetBackgroundColour( wxColour( 153, 170, 187 ) );
@@ -4302,28 +4292,17 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
bSizer1841 = new wxBoxSizer( wxHORIZONTAL );
m_bitmapThanks = new wxStaticBitmap( m_panel391, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
- bSizer1841->Add( m_bitmapThanks, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 );
-
- wxBoxSizer* bSizer1781;
- bSizer1781 = new wxBoxSizer( wxVERTICAL );
+ bSizer1841->Add( m_bitmapThanks, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
m_staticTextThanks = new wxStaticText( m_panel391, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE );
m_staticTextThanks->Wrap( -1 );
m_staticTextThanks->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) );
m_staticTextThanks->SetForegroundColour( wxColour( 0, 0, 0 ) );
- bSizer1781->Add( m_staticTextThanks, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 );
-
- m_buttonShowDonationDetails = new wxButton( m_panel391, wxID_ANY, _("Donation details"), wxDefaultPosition, wxDefaultSize, 0 );
- m_buttonShowDonationDetails->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) );
+ bSizer1841->Add( m_staticTextThanks, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
- bSizer1781->Add( m_buttonShowDonationDetails, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 );
-
- bSizer1841->Add( bSizer1781, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
-
-
- bSizer243->Add( bSizer1841, 1, wxALIGN_CENTER_HORIZONTAL, 5 );
+ bSizer243->Add( bSizer1841, 1, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT, 5 );
m_staticTextNoAutoUpdate = new wxStaticText( m_panel391, wxID_ANY, _("The auto updater was disabled by the administrator."), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextNoAutoUpdate->Wrap( -1 );
@@ -4331,6 +4310,11 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
bSizer243->Add( m_staticTextNoAutoUpdate, 0, wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
+ m_buttonShowDonationDetails = new wxButton( m_panel391, wxID_ANY, _("Donation details"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_buttonShowDonationDetails->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) );
+
+ bSizer243->Add( m_buttonShowDonationDetails, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 );
+
m_panel391->SetSizer( bSizer243 );
m_panel391->Layout();
@@ -4341,56 +4325,95 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
m_panelThankYou->SetSizer( bSizer1831 );
m_panelThankYou->Layout();
bSizer1831->Fit( m_panelThankYou );
- bSizer181->Add( m_panelThankYou, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 );
+ bSizer181->Add( m_panelThankYou, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 10 );
- wxBoxSizer* bSizer186;
- bSizer186 = new wxBoxSizer( wxVERTICAL );
+ wxBoxSizer* bSizer187;
+ bSizer187 = new wxBoxSizer( wxVERTICAL );
- m_staticText94 = new wxStaticText( m_panel41, wxID_ANY, _("Feedback and suggestions are welcome"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticText94->Wrap( -1 );
- bSizer186->Add( m_staticText94, 0, wxALL, 5 );
+ m_staticText96 = new wxStaticText( m_panel41, wxID_ANY, _("Source code written in C++ using:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText96->Wrap( -1 );
+ bSizer187->Add( m_staticText96, 0, wxALL, 5 );
- wxBoxSizer* bSizer166;
- bSizer166 = new wxBoxSizer( wxHORIZONTAL );
+ wxBoxSizer* bSizer171;
+ bSizer171 = new wxBoxSizer( wxHORIZONTAL );
+ m_hyperlink11 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("MS Visual Studio"), wxT("https://www.visualstudio.com"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink11->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+ m_hyperlink11->SetToolTip( _("https://www.visualstudio.com") );
- bSizer166->Add( 0, 0, 1, wxEXPAND, 5 );
+ bSizer171->Add( m_hyperlink11, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
- m_bitmapHomepage = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 );
- m_bitmapHomepage->SetToolTip( _("Home page") );
+ m_hyperlink7 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("wxWidgets"), wxT("http://www.wxwidgets.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink7->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+ m_hyperlink7->SetToolTip( _("http://www.wxwidgets.org") );
- bSizer166->Add( m_bitmapHomepage, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
+ bSizer171->Add( m_hyperlink7, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
- m_hyperlink1 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("FreeFileSync.org"), wxT("https://www.freefilesync.org/"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
- m_hyperlink1->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, true, wxEmptyString ) );
- m_hyperlink1->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- m_hyperlink1->SetToolTip( _("https://www.freefilesync.org") );
+ m_hyperlink14 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("wxFormBuilder"), wxT("https://github.com/wxFormBuilder/wxFormBuilder"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink14->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+ m_hyperlink14->SetToolTip( _("https://github.com/wxFormBuilder/wxFormBuilder") );
- bSizer166->Add( m_hyperlink1, 0, wxALIGN_CENTER_VERTICAL, 5 );
+ bSizer171->Add( m_hyperlink14, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+ m_hyperlink16 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Artistic Style"), wxT("http://astyle.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink16->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+ m_hyperlink16->SetToolTip( _("http://astyle.sourceforge.net") );
- bSizer166->Add( 0, 0, 1, wxEXPAND, 5 );
+ bSizer171->Add( m_hyperlink16, 0, wxALIGN_CENTER_VERTICAL, 5 );
- m_bitmapEmail = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 );
- m_bitmapEmail->SetToolTip( _("Email") );
- bSizer166->Add( m_bitmapEmail, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
+ bSizer187->Add( bSizer171, 0, wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
- m_hyperlink2 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("zenju@freefilesync.org"), wxT("mailto:zenju@freefilesync.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
- m_hyperlink2->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, true, wxEmptyString ) );
- m_hyperlink2->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- m_hyperlink2->SetToolTip( _("mailto:zenju@freefilesync.org") );
+ wxBoxSizer* bSizer172;
+ bSizer172 = new wxBoxSizer( wxHORIZONTAL );
- bSizer166->Add( m_hyperlink2, 0, wxALIGN_CENTER_VERTICAL, 5 );
+ m_hyperlink15 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("zen::Xml"), wxT("http://zenxml.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink15->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+ m_hyperlink15->SetToolTip( _("http://zenxml.sourceforge.net") );
+ bSizer172->Add( m_hyperlink15, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
- bSizer166->Add( 0, 0, 1, wxEXPAND, 5 );
+ m_hyperlink12 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Google Test"), wxT("https://github.com/google/googletest"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink12->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+ m_hyperlink12->SetToolTip( _("https://github.com/google/googletest") );
+
+ bSizer172->Add( m_hyperlink12, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+
+ m_hyperlink13 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Boost"), wxT("http://www.boost.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink13->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+ m_hyperlink13->SetToolTip( _("http://www.boost.org") );
+
+ bSizer172->Add( m_hyperlink13, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+
+ m_hyperlink10 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("libssh2"), wxT("https://www.libssh2.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink10->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+ m_hyperlink10->SetToolTip( _("https://www.libssh2.org") );
+
+ bSizer172->Add( m_hyperlink10, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+ m_hyperlink101 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("libcurl"), wxT("https://curl.haxx.se/libcurl"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink101->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+ m_hyperlink101->SetToolTip( _("https://curl.haxx.se/libcurl") );
- bSizer186->Add( bSizer166, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
+ bSizer172->Add( m_hyperlink101, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 );
+ m_hyperlink18 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("NSIS"), wxT("http://nsis.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink18->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+ m_hyperlink18->SetToolTip( _("http://nsis.sourceforge.net") );
- bSizer181->Add( bSizer186, 0, wxALL|wxEXPAND, 5 );
+ bSizer172->Add( m_hyperlink18, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+
+ m_hyperlink9 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Inno Setup"), wxT("http://www.jrsoftware.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink9->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+ m_hyperlink9->SetToolTip( _("http://www.jrsoftware.org") );
+
+ bSizer172->Add( m_hyperlink9, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+
+ bSizer187->Add( bSizer172, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 );
+
+
+ bSizer181->Add( bSizer187, 0, wxALL|wxEXPAND, 5 );
m_staticline34 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
bSizer181->Add( m_staticline34, 0, wxEXPAND, 5 );
@@ -4452,7 +4475,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
bSizer177->Add( m_scrolledWindowTranslators, 1, wxLEFT|wxEXPAND, 5 );
- bSizer174->Add( bSizer177, 0, wxTOP|wxLEFT|wxEXPAND, 5 );
+ bSizer174->Add( bSizer177, 0, wxEXPAND|wxLEFT, 5 );
bSizer162->Add( bSizer174, 0, 0, 5 );
diff --git a/FreeFileSync/Source/ui/gui_generated.h b/FreeFileSync/Source/ui/gui_generated.h
index 3b7034db..4c91d1fe 100755
--- a/FreeFileSync/Source/ui/gui_generated.h
+++ b/FreeFileSync/Source/ui/gui_generated.h
@@ -311,7 +311,6 @@ protected:
wxToggleButton* m_toggleBtnByTimeSize;
wxToggleButton* m_toggleBtnByContent;
wxToggleButton* m_toggleBtnBySize;
- wxStaticLine* m_staticline42;
wxStaticBitmap* m_bitmapCompVariant;
wxStaticText* m_staticTextCompVarDescription;
wxStaticLine* m_staticline33;
@@ -327,6 +326,18 @@ protected:
wxHyperlinkCtrl* m_hyperlink241;
wxStaticLine* m_staticline441;
wxStaticLine* m_staticline331;
+ wxStaticLine* m_staticlinePerformance;
+ wxBoxSizer* bSizerPerformance;
+ wxStaticText* m_staticTextPerfDeRequired;
+ wxStaticLine* m_staticlinePerfDeRequired;
+ wxPanel* m_panelPerfHeader;
+ wxStaticBitmap* m_bitmapPerf;
+ wxStaticText* m_staticText13611;
+ wxBoxSizer* bSizer260;
+ wxStaticText* m_staticTextPerfParallelOps;
+ wxScrolledWindow* m_scrolledWindowPerf;
+ wxFlexGridSizer* fgSizerPerf;
+ wxHyperlinkCtrl* m_hyperlink1711;
wxPanel* m_panelFilterSettingsTab;
wxBoxSizer* bSizerHeaderFilterSettings;
wxStaticText* m_staticTextMainFilterSettings;
@@ -336,17 +347,11 @@ protected:
wxStaticBitmap* m_bitmapInclude;
wxStaticText* m_staticText78;
wxTextCtrl* m_textCtrlInclude;
- wxStaticLine* m_staticline22;
wxStaticBitmap* m_bitmapExclude;
wxStaticText* m_staticText77;
wxHyperlinkCtrl* m_hyperlink171;
wxTextCtrl* m_textCtrlExclude;
wxStaticLine* m_staticline24;
- wxStaticBitmap* m_bitmapFilterDate;
- wxStaticText* m_staticText79;
- wxSpinCtrl* m_spinCtrlTimespan;
- wxChoice* m_choiceUnitTimespan;
- wxStaticLine* m_staticline23;
wxStaticBitmap* m_bitmapFilterSize;
wxStaticText* m_staticText80;
wxStaticText* m_staticText101;
@@ -355,8 +360,12 @@ protected:
wxStaticText* m_staticText102;
wxSpinCtrl* m_spinCtrlMaxSize;
wxChoice* m_choiceUnitMaxSize;
- wxStaticLine* m_staticline62;
- wxStaticLine* m_staticline46;
+ wxStaticLine* m_staticline23;
+ wxStaticBitmap* m_bitmapFilterDate;
+ wxStaticText* m_staticText79;
+ wxSpinCtrl* m_spinCtrlTimespan;
+ wxChoice* m_choiceUnitTimespan;
+ wxStaticLine* m_staticline231;
wxButton* m_buttonClear;
wxPanel* m_panelSyncSettingsTab;
wxBoxSizer* bSizerHeaderSyncSettings;
@@ -369,7 +378,6 @@ protected:
wxToggleButton* m_toggleBtnMirror;
wxToggleButton* m_toggleBtnUpdate;
wxToggleButton* m_toggleBtnCustom;
- wxStaticLine* m_staticline53;
wxBoxSizer* bSizerSyncDirHolder;
wxBoxSizer* bSizerSyncDirections;
wxStaticText* m_staticTextCategory;
@@ -392,6 +400,7 @@ protected:
wxStaticText* m_staticText145;
wxStaticText* m_staticTextSyncVarDescription;
wxStaticLine* m_staticline431;
+ wxStaticLine* m_staticline72;
wxCheckBox* m_checkBoxDetectMove;
wxHyperlinkCtrl* m_hyperlink242;
wxStaticLine* m_staticline54;
@@ -400,7 +409,6 @@ protected:
wxToggleButton* m_toggleBtnRecycler;
wxToggleButton* m_toggleBtnPermanent;
wxToggleButton* m_toggleBtnVersioning;
- wxStaticLine* m_staticline531;
wxBoxSizer* bSizerVersioningHolder;
wxStaticBitmap* m_bitmapDeletionType;
wxStaticText* m_staticTextDeletionTypeDescription;
@@ -445,6 +453,7 @@ protected:
virtual void OnHelpComparisonSettings( wxHyperlinkEvent& event ) { event.Skip(); }
virtual void onlTimeShiftKeyDown( wxKeyEvent& event ) { event.Skip(); }
virtual void OnHelpTimeShift( wxHyperlinkEvent& event ) { event.Skip(); }
+ virtual void OnHelpPerformance( wxHyperlinkEvent& event ) { event.Skip(); }
virtual void OnChangeFilterOption( wxCommandEvent& event ) { event.Skip(); }
virtual void OnHelpShowExamples( wxHyperlinkEvent& event ) { event.Skip(); }
virtual void OnFilterReset( wxCommandEvent& event ) { event.Skip(); }
@@ -535,29 +544,21 @@ protected:
wxStaticText* m_staticText1232;
wxTextCtrl* m_textCtrlServerPath;
wxButton* m_buttonSelectFolder;
- wxBoxSizer* bSizerSftpTweaks;
+ wxBoxSizer* bSizer255;
wxStaticLine* m_staticline571;
- wxStaticBitmap* m_bitmapSpeedSftp;
+ wxStaticBitmap* m_bitmapPerf;
wxStaticText* m_staticText1361;
wxHyperlinkCtrl* m_hyperlink171;
wxStaticLine* m_staticline57;
wxPanel* m_panel411;
- wxStaticText* m_staticText12341;
- wxSpinCtrl* m_spinCtrlConnectionCountSftp;
- wxStaticText* m_staticTextSftpConnectionCountHint;
- wxStaticText* m_staticText1231111;
+ wxBoxSizer* bSizerConnectionsLabel;
+ wxStaticText* m_staticTextConnectionsLabel;
+ wxStaticText* m_staticTextConnectionsLabelSub;
+ wxSpinCtrl* m_spinCtrlConnectionCount;
+ wxStaticText* m_staticTextConnectionCountDescr;
+ wxStaticText* m_staticTextChannelCountSftp;
wxSpinCtrl* m_spinCtrlChannelCountSftp;
- wxButton* m_button42;
- wxBoxSizer* bSizerFtpTweaks;
- wxStaticLine* m_staticline5711;
- wxStaticBitmap* m_bitmapSpeedFtp;
- wxStaticText* m_staticText13611;
- wxHyperlinkCtrl* m_hyperlink1711;
- wxStaticLine* m_staticline573;
- wxPanel* m_panel4111;
- wxStaticText* m_staticText123411;
- wxSpinCtrl* m_spinCtrlConnectionCountFtp;
- wxStaticText* m_staticTextFtpConnectionCountHint;
+ wxButton* m_buttonChannelCountSftp;
wxStaticLine* m_staticline12;
wxBoxSizer* bSizerStdButtons;
wxButton* m_buttonOkay;
@@ -573,7 +574,7 @@ protected:
virtual void OnSelectKeyfile( wxCommandEvent& event ) { event.Skip(); }
virtual void OnToggleShowPassword( wxCommandEvent& event ) { event.Skip(); }
virtual void OnBrowseCloudFolder( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnHelpSftpPerformance( wxHyperlinkEvent& event ) { event.Skip(); }
+ virtual void OnHelpFtpPerformance( wxHyperlinkEvent& event ) { event.Skip(); }
virtual void OnDetectServerChannelLimit( wxCommandEvent& event ) { event.Skip(); }
virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); }
virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); }
@@ -1037,6 +1038,25 @@ protected:
wxPanel* m_panel41;
wxStaticBitmap* m_bitmapLogo;
wxStaticLine* m_staticline341;
+ wxStaticText* m_staticText94;
+ wxStaticBitmap* m_bitmapHomepage;
+ wxHyperlinkCtrl* m_hyperlink1;
+ wxStaticBitmap* m_bitmapForum;
+ wxHyperlinkCtrl* m_hyperlink21;
+ wxStaticBitmap* m_bitmapEmail;
+ wxHyperlinkCtrl* m_hyperlink2;
+ wxStaticLine* m_staticline3412;
+ wxPanel* m_panelDonate;
+ wxPanel* m_panel39;
+ wxStaticBitmap* m_bitmapDonate;
+ wxStaticText* m_staticTextDonate;
+ wxButton* m_buttonDonate;
+ wxPanel* m_panelThankYou;
+ wxPanel* m_panel391;
+ wxStaticBitmap* m_bitmapThanks;
+ wxStaticText* m_staticTextThanks;
+ wxStaticText* m_staticTextNoAutoUpdate;
+ wxButton* m_buttonShowDonationDetails;
wxStaticText* m_staticText96;
wxHyperlinkCtrl* m_hyperlink11;
wxHyperlinkCtrl* m_hyperlink7;
@@ -1049,22 +1069,6 @@ protected:
wxHyperlinkCtrl* m_hyperlink101;
wxHyperlinkCtrl* m_hyperlink18;
wxHyperlinkCtrl* m_hyperlink9;
- wxPanel* m_panelDonate;
- wxPanel* m_panel39;
- wxStaticBitmap* m_bitmapDonate;
- wxStaticText* m_staticTextDonate;
- wxButton* m_buttonDonate;
- wxPanel* m_panelThankYou;
- wxPanel* m_panel391;
- wxStaticBitmap* m_bitmapThanks;
- wxStaticText* m_staticTextThanks;
- wxButton* m_buttonShowDonationDetails;
- wxStaticText* m_staticTextNoAutoUpdate;
- wxStaticText* m_staticText94;
- wxStaticBitmap* m_bitmapHomepage;
- wxHyperlinkCtrl* m_hyperlink1;
- wxStaticBitmap* m_bitmapEmail;
- wxHyperlinkCtrl* m_hyperlink2;
wxStaticLine* m_staticline34;
wxStaticText* m_staticText93;
wxStaticBitmap* m_bitmapGpl;
diff --git a/FreeFileSync/Source/ui/gui_status_handler.cpp b/FreeFileSync/Source/ui/gui_status_handler.cpp
index 4a1bebbd..198b5726 100755
--- a/FreeFileSync/Source/ui/gui_status_handler.cpp
+++ b/FreeFileSync/Source/ui/gui_status_handler.cpp
@@ -126,10 +126,9 @@ void StatusHandlerTemporaryPanel::initNewPhase(int itemsTotal, int64_t bytesTota
}
-void StatusHandlerTemporaryPanel::reportInfo(const std::wstring& text)
+void StatusHandlerTemporaryPanel::logInfo(const std::wstring& msg)
{
- errorLog_.logMsg(text, MSG_TYPE_INFO); //log first!
- StatusHandler::reportInfo(text); //throw X
+ errorLog_.logMsg(msg, MSG_TYPE_INFO);
}
@@ -318,7 +317,7 @@ StatusHandlerFloatingDialog::~StatusHandlerFloatingDialog()
errorLog_.logMsg(replaceCpy(_("Executing command %x"), L"%x", fmtPath(commandLine)), MSG_TYPE_INFO);
//----------------- write results into LastSyncs.log------------------------
- const SummaryInfo summary =
+ const LogSummary summary =
{
jobName_, finalStatusMsg,
getItemsCurrent(PHASE_SYNCHRONIZING), getBytesCurrent(PHASE_SYNCHRONIZING),
@@ -436,19 +435,18 @@ void StatusHandlerFloatingDialog::initNewPhase(int itemsTotal, int64_t bytesTota
}
-void StatusHandlerFloatingDialog::updateProcessedData(int itemsDelta, int64_t bytesDelta)
+void StatusHandlerFloatingDialog::updateDataProcessed(int itemsDelta, int64_t bytesDelta)
{
- StatusHandler::updateProcessedData(itemsDelta, bytesDelta);
+ StatusHandler::updateDataProcessed(itemsDelta, bytesDelta);
if (progressDlg_)
progressDlg_->notifyProgressChange(); //noexcept
//note: this method should NOT throw in order to properly allow undoing setting of statistics!
}
-void StatusHandlerFloatingDialog::reportInfo(const std::wstring& text)
+void StatusHandlerFloatingDialog::logInfo(const std::wstring& msg)
{
- errorLog_.logMsg(text, MSG_TYPE_INFO); //log first!
- StatusHandler::reportInfo(text); //throw X
+ errorLog_.logMsg(msg, MSG_TYPE_INFO);
}
diff --git a/FreeFileSync/Source/ui/gui_status_handler.h b/FreeFileSync/Source/ui/gui_status_handler.h
index c9d9eef3..ae8125f6 100755
--- a/FreeFileSync/Source/ui/gui_status_handler.h
+++ b/FreeFileSync/Source/ui/gui_status_handler.h
@@ -27,7 +27,7 @@ public:
void initNewPhase(int itemsTotal, int64_t bytesTotal, Phase phaseID) override;
- void reportInfo (const std::wstring& text) override;
+ void logInfo (const std::wstring& msg) 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;
@@ -64,9 +64,9 @@ public:
~StatusHandlerFloatingDialog();
void initNewPhase (int itemsTotal, int64_t bytesTotal, Phase phaseID) override;
- void updateProcessedData(int itemsDelta, int64_t bytesDelta ) override;
+ void updateDataProcessed(int itemsDelta, int64_t bytesDelta ) override;
- void reportInfo (const std::wstring& text ) override;
+ void logInfo (const std::wstring& msg ) 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;
diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp
index 4a0d4ed1..5e40c4c4 100755
--- a/FreeFileSync/Source/ui/main_dlg.cpp
+++ b/FreeFileSync/Source/ui/main_dlg.cpp
@@ -47,7 +47,6 @@
#include "../lib/localization.h"
#include "../version/version.h"
-
using namespace zen;
using namespace fff;
@@ -86,38 +85,6 @@ bool acceptDialogFileDrop(const std::vector<Zstring>& shellItemPaths)
}
}
-
-class fff::FolderSelectorImpl : public FolderSelector
-{
-public:
- FolderSelectorImpl(MainDialog& mainDlg,
- wxPanel& dropWindow1,
- wxButton& selectFolderButton,
- wxButton& selectSftpButton,
- FolderHistoryBox& dirpath,
- wxStaticText* staticText = nullptr,
- wxWindow* dropWindow2 = nullptr) :
- FolderSelector(dropWindow1, selectFolderButton, selectSftpButton, dirpath, staticText, dropWindow2),
- mainDlg_(mainDlg) {}
-
- bool shouldSetDroppedPaths(const std::vector<Zstring>& shellItemPaths) override
- {
- if (acceptDialogFileDrop(shellItemPaths))
- {
- assert(!shellItemPaths.empty());
- mainDlg_.loadConfiguration(shellItemPaths);
- return false;
- }
- return true; //=> return true: change directory selection via drag and drop
- }
-
-private:
- FolderSelectorImpl (const FolderSelectorImpl&) = delete;
- FolderSelectorImpl& operator=(const FolderSelectorImpl&) = delete;
-
- MainDialog& mainDlg_;
-};
-
//------------------------------------------------------------------
/* class hierarchy:
@@ -128,8 +95,8 @@ private:
template<>
FolderPairCallback FolderPairPanelGenerated
/|\ /|\
- _________|________ ________|
- | | |
+ _________|_________ ________|
+ | | |
FolderPairFirst FolderPairPanel
*/
@@ -137,33 +104,25 @@ template <class GuiPanel>
class fff::FolderPairCallback : public FolderPairPanelBasic<GuiPanel> //implements callback functionality to MainDialog as imposed by FolderPairPanelBasic
{
public:
- FolderPairCallback(GuiPanel& basicPanel, MainDialog& mainDialog) :
+ FolderPairCallback(GuiPanel& basicPanel, MainDialog& mainDialog,
+
+ wxPanel& dropWindow1L,
+ wxButton& selectFolderButtonL,
+ wxButton& selectSftpButtonL,
+ FolderHistoryBox& dirpathL,
+ wxStaticText* staticTextL,
+ wxWindow* dropWindow2L,
+
+ wxPanel& dropWindow1R,
+ wxButton& selectFolderButtonR,
+ wxButton& selectSftpButtonR,
+ FolderHistoryBox& dirpathR,
+ wxStaticText* staticTextR,
+ wxWindow* dropWindow2R) :
FolderPairPanelBasic<GuiPanel>(basicPanel), //pass FolderPairPanelGenerated part...
- mainDlg_(mainDialog) {}
-
-private:
- MainConfiguration getMainConfig() const override { return mainDlg_.getConfig().mainCfg; }
- wxWindow* getParentWindow() override { return &mainDlg_; }
- std::unique_ptr<FilterConfig>& getFilterCfgOnClipboardRef() override { return mainDlg_.filterCfgOnClipboard_; }
-
- void onLocalCompCfgChange () override { mainDlg_.applyCompareConfig(false /*setDefaultViewType*/); }
- void onLocalSyncCfgChange () override { mainDlg_.applySyncConfig(); }
- void onLocalFilterCfgChange() override { mainDlg_.applyFilterConfig(); } //re-apply filter
-
- MainDialog& mainDlg_;
-};
-
-
-class fff::FolderPairPanel :
- public FolderPairPanelGenerated, //FolderPairPanel "owns" FolderPairPanelGenerated!
- public FolderPairCallback<FolderPairPanelGenerated>
-{
-public:
- FolderPairPanel(wxWindow* parent, MainDialog& mainDialog) :
- FolderPairPanelGenerated(parent),
- FolderPairCallback<FolderPairPanelGenerated>(static_cast<FolderPairPanelGenerated&>(*this), mainDialog), //pass FolderPairPanelGenerated part...
- folderSelectorLeft_ (mainDialog, *m_panelLeft, *m_buttonSelectFolderLeft, *m_bpButtonSelectAltFolderLeft, *m_folderPathLeft),
- folderSelectorRight_(mainDialog, *m_panelRight, *m_buttonSelectFolderRight, *m_bpButtonSelectAltFolderRight, *m_folderPathRight)
+ mainDlg_(mainDialog),
+ folderSelectorLeft_ (dropWindow1L, selectFolderButtonL, selectSftpButtonL, dirpathL, staticTextL, dropWindow2L, droppedPathsFilter_, getDeviceParallelOps_, setDeviceParallelOps_),
+ folderSelectorRight_(dropWindow1R, selectFolderButtonR, selectSftpButtonR, dirpathR, staticTextR, dropWindow2R, droppedPathsFilter_, getDeviceParallelOps_, setDeviceParallelOps_)
{
folderSelectorLeft_ .setSiblingSelector(&folderSelectorRight_);
folderSelectorRight_.setSiblingSelector(&folderSelectorLeft_);
@@ -173,75 +132,113 @@ public:
folderSelectorLeft_ .Connect(EVENT_ON_FOLDER_MANUAL_EDIT, wxCommandEventHandler(MainDialog::onDirManualCorrection), nullptr, &mainDialog);
folderSelectorRight_.Connect(EVENT_ON_FOLDER_MANUAL_EDIT, wxCommandEventHandler(MainDialog::onDirManualCorrection), nullptr, &mainDialog);
-
- m_bpButtonFolderPairOptions->SetBitmapLabel(getResourceImage(L"button_arrow_down"));
}
void setValues(const LocalPairConfig& lpc)
{
- setConfig(lpc.localCmpCfg, lpc.localSyncCfg, lpc.localFilter);
+ this->setConfig(lpc.localCmpCfg, lpc.localSyncCfg, lpc.localFilter);
folderSelectorLeft_ .setPath(lpc.folderPathPhraseLeft);
folderSelectorRight_.setPath(lpc.folderPathPhraseRight);
}
- LocalPairConfig getValues() const { return LocalPairConfig(folderSelectorLeft_.getPath(), folderSelectorRight_.getPath(), getCompConfig(), getSyncConfig(), getFilterConfig()); }
+ LocalPairConfig getValues() const
+ {
+ return LocalPairConfig(folderSelectorLeft_.getPath(), folderSelectorRight_.getPath(), this->getCompConfig(), this->getSyncConfig(), this->getFilterConfig());
+ }
private:
- //support for drag and drop
- FolderSelectorImpl folderSelectorLeft_;
- FolderSelectorImpl folderSelectorRight_;
-};
+ MainConfiguration getMainConfig() const override { return mainDlg_.getConfig().mainCfg; }
+ wxWindow* getParentWindow() override { return &mainDlg_; }
+ std::unique_ptr<FilterConfig>& getFilterCfgOnClipboardRef() override { return mainDlg_.filterCfgOnClipboard_; }
+ void onLocalCompCfgChange () override { mainDlg_.applyCompareConfig(false /*setDefaultViewType*/); }
+ void onLocalSyncCfgChange () override { mainDlg_.applySyncConfig (); }
+ void onLocalFilterCfgChange() override { mainDlg_.applyFilterConfig(); } //re-apply filter
-class fff::FolderPairFirst : public FolderPairCallback<MainDialogGenerated>
-{
-public:
- FolderPairFirst(MainDialog& mainDialog) :
- FolderPairCallback<MainDialogGenerated>(mainDialog, mainDialog),
-
- //prepare drag & drop
- folderSelectorLeft_(mainDialog,
- *mainDialog.m_panelTopLeft,
- *mainDialog.m_buttonSelectFolderLeft,
- *mainDialog.m_bpButtonSelectAltFolderLeft,
- *mainDialog.m_folderPathLeft,
- mainDialog.m_staticTextResolvedPathL,
- &mainDialog.m_gridMainL->getMainWin()),
- folderSelectorRight_(mainDialog,
- *mainDialog.m_panelTopRight,
- *mainDialog.m_buttonSelectFolderRight,
- *mainDialog.m_bpButtonSelectAltFolderRight,
- *mainDialog.m_folderPathRight,
- mainDialog.m_staticTextResolvedPathR,
- &mainDialog.m_gridMainR->getMainWin())
+ const std::function<bool(const std::vector<Zstring>& shellItemPaths)> droppedPathsFilter_ = [&](const std::vector<Zstring>& shellItemPaths)
{
- folderSelectorLeft_ .setSiblingSelector(&folderSelectorRight_);
- folderSelectorRight_.setSiblingSelector(&folderSelectorLeft_);
-
- folderSelectorLeft_ .Connect(EVENT_ON_FOLDER_SELECTED, wxCommandEventHandler(MainDialog::onDirSelected), nullptr, &mainDialog);
- folderSelectorRight_.Connect(EVENT_ON_FOLDER_SELECTED, wxCommandEventHandler(MainDialog::onDirSelected), nullptr, &mainDialog);
+ if (acceptDialogFileDrop(shellItemPaths))
+ {
+ assert(!shellItemPaths.empty());
+ mainDlg_.loadConfiguration(shellItemPaths);
+ return false; //don't set dropped paths
+ }
+ return true; //do set dropped paths
+ };
- folderSelectorLeft_ .Connect(EVENT_ON_FOLDER_MANUAL_EDIT, wxCommandEventHandler(MainDialog::onDirManualCorrection), nullptr, &mainDialog);
- folderSelectorRight_.Connect(EVENT_ON_FOLDER_MANUAL_EDIT, wxCommandEventHandler(MainDialog::onDirManualCorrection), nullptr, &mainDialog);
+ const std::function<size_t(const Zstring& folderPathPhrase)> getDeviceParallelOps_ = [&](const Zstring& folderPathPhrase)
+ {
+ //follow deviceParallelOps editing-behavior from sync_cfg.cpp:
+ const auto& deviceParallelOps = mainDlg_.currentCfg_.mainCfg.deviceParallelOps;
+ const AbstractPath rootPath = AFS::getPathComponents(createAbstractPath(folderPathPhrase)).rootPath;
- mainDialog.m_panelTopLeft ->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::onTopFolderPairKeyEvent), nullptr, &mainDialog);
- mainDialog.m_panelTopCenter->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::onTopFolderPairKeyEvent), nullptr, &mainDialog);
- mainDialog.m_panelTopRight ->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::onTopFolderPairKeyEvent), nullptr, &mainDialog);
- }
+ auto itParOps = deviceParallelOps.find(rootPath);
+ return std::max<size_t>(itParOps != deviceParallelOps.end() ? static_cast<int>(itParOps->second) : 1, 1);
+ };
- void setValues(const LocalPairConfig& lpc)
+ const std::function<void(const Zstring& folderPathPhrase, size_t parallelOps)> setDeviceParallelOps_ = [&](const Zstring& folderPathPhrase, size_t parallelOps)
{
- setConfig(lpc.localCmpCfg, lpc.localSyncCfg, lpc.localFilter);
- folderSelectorLeft_ .setPath(lpc.folderPathPhraseLeft);
- folderSelectorRight_.setPath(lpc.folderPathPhraseRight);
- }
+ auto& deviceParallelOps = mainDlg_.currentCfg_.mainCfg.deviceParallelOps;
+ const AbstractPath rootPath = AFS::getPathComponents(createAbstractPath(folderPathPhrase)).rootPath;
+ if (!AFS::isNullPath(rootPath))
+ {
+ if (parallelOps > 1)
+ deviceParallelOps[rootPath] = parallelOps;
+ else
+ deviceParallelOps.erase(rootPath);
- LocalPairConfig getValues() const { return LocalPairConfig(folderSelectorLeft_.getPath(), folderSelectorRight_.getPath(), getCompConfig(), getSyncConfig(), getFilterConfig()); }
+ mainDlg_.updateUnsavedCfgStatus();
+ }
+ };
-private:
- //support for drag and drop
- FolderSelectorImpl folderSelectorLeft_;
- FolderSelectorImpl folderSelectorRight_;
+ MainDialog& mainDlg_;
+ FolderSelector folderSelectorLeft_;
+ FolderSelector folderSelectorRight_;
+};
+
+
+class fff::FolderPairPanel :
+ public FolderPairPanelGenerated, //FolderPairPanel "owns" FolderPairPanelGenerated!
+ public FolderPairCallback<FolderPairPanelGenerated>
+{
+public:
+ FolderPairPanel(wxWindow* parent, MainDialog& mainDialog) :
+ FolderPairPanelGenerated(parent),
+ FolderPairCallback<FolderPairPanelGenerated>(static_cast<FolderPairPanelGenerated&>(*this), mainDialog,
+
+ *m_panelLeft,
+ *m_buttonSelectFolderLeft,
+ *m_bpButtonSelectAltFolderLeft,
+ *m_folderPathLeft,
+ nullptr /*staticText*/, nullptr /*dropWindow2*/,
+
+ *m_panelRight,
+ *m_buttonSelectFolderRight,
+ *m_bpButtonSelectAltFolderRight,
+ *m_folderPathRight,
+ nullptr /*staticText*/, nullptr /*dropWindow2*/) {}
+};
+
+
+class fff::FolderPairFirst : public FolderPairCallback<MainDialogGenerated>
+{
+public:
+ FolderPairFirst(MainDialog& mainDialog) :
+ FolderPairCallback<MainDialogGenerated>(mainDialog, mainDialog,
+
+ *mainDialog.m_panelTopLeft,
+ *mainDialog.m_buttonSelectFolderLeft,
+ *mainDialog.m_bpButtonSelectAltFolderLeft,
+ *mainDialog.m_folderPathLeft,
+ mainDialog.m_staticTextResolvedPathL,
+ &mainDialog.m_gridMainL->getMainWin(),
+
+ *mainDialog.m_panelTopRight,
+ *mainDialog.m_buttonSelectFolderRight,
+ *mainDialog.m_bpButtonSelectAltFolderRight,
+ *mainDialog.m_folderPathRight,
+ mainDialog.m_staticTextResolvedPathR,
+ &mainDialog.m_gridMainR->getMainWin()) {}
};
@@ -675,6 +672,10 @@ MainDialog::MainDialog(const Zstring& globalConfigFilePath,
//calculate witdh of folder pair manually (if scrollbars are visible)
m_panelTopLeft->Connect(wxEVT_SIZE, wxEventHandler(MainDialog::OnResizeLeftFolderWidth), nullptr, this);
+ m_panelTopLeft ->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::onTopFolderPairKeyEvent), nullptr, this);
+ m_panelTopCenter->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::onTopFolderPairKeyEvent), nullptr, this);
+ m_panelTopRight ->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::onTopFolderPairKeyEvent), nullptr, this);
+
//dynamically change sizer direction depending on size
m_panelTopButtons->Connect(wxEVT_SIZE, wxEventHandler(MainDialog::OnResizeTopButtonPanel), nullptr, this);
m_panelConfig ->Connect(wxEVT_SIZE, wxEventHandler(MainDialog::OnResizeConfigPanel), nullptr, this);
@@ -948,7 +949,7 @@ void MainDialog::setGlobalCfgOnInit(const XmlGlobalSettings& globalSettings)
filegrid::setItemPathForm(*m_gridMainL, globalSettings.gui.mainDlg.itemPathFormatLeftGrid);
filegrid::setItemPathForm(*m_gridMainR, globalSettings.gui.mainDlg.itemPathFormatRightGrid);
- //------------------------------------------------------------------------------------------------
+ //--------------------------------------------------------------------------------
m_checkBoxMatchCase->SetValue(globalCfg_.gui.mainDlg.textSearchRespectCase);
//wxAuiManager erroneously loads panel captions, we don't want that
@@ -962,7 +963,7 @@ void MainDialog::setGlobalCfgOnInit(const XmlGlobalSettings& globalSettings)
//restore original captions
for (const auto& item : captionNameMap)
auiMgr_.GetPane(item.second).Caption(item.first);
- //------------------------------------------------------------------------------------------------
+ //--------------------------------------------------------------------------------
//if MainDialog::onQueryEndSession() is called while comparison is active, this panel is saved and restored as "visible"
auiMgr_.GetPane(compareStatus_->getAsWindow()).Hide();
@@ -978,7 +979,8 @@ void MainDialog::setGlobalCfgOnInit(const XmlGlobalSettings& globalSettings)
XmlGlobalSettings MainDialog::getGlobalCfgBeforeExit()
{
Freeze(); //no need to Thaw() again!!
-
+ recalcMaxFolderPairsVisible();
+ //--------------------------------------------------------------------------------
XmlGlobalSettings globalSettings = globalCfg_;
globalSettings.programLanguage = getLanguage();
@@ -2611,11 +2613,11 @@ void MainDialog::OnCompSettingsContext(wxEvent& event)
auto setVariant = [&](CompareVariant var)
{
- currentCfg_.mainCfg.cmpConfig.compareVar = var;
+ currentCfg_.mainCfg.cmpCfg.compareVar = var;
applyCompareConfig(true /*setDefaultViewType*/);
};
- const CompareVariant activeCmpVar = getConfig().mainCfg.cmpConfig.compareVar;
+ const CompareVariant activeCmpVar = getConfig().mainCfg.cmpCfg.compareVar;
auto addVariantItem = [&](CompareVariant cmpVar, const wchar_t* iconName)
{
@@ -3339,10 +3341,11 @@ void MainDialog::updateGuiDelayedIf(bool condition)
void MainDialog::showConfigDialog(SyncConfigPanel panelToShow, int localPairIndexToShow)
{
GlobalPairConfig globalPairCfg;
- globalPairCfg.cmpConfig = currentCfg_.mainCfg.cmpConfig;
- globalPairCfg.syncCfg = currentCfg_.mainCfg.syncCfg;
- globalPairCfg.filter = currentCfg_.mainCfg.globalFilter;
+ globalPairCfg.cmpCfg = currentCfg_.mainCfg.cmpCfg;
+ globalPairCfg.syncCfg = currentCfg_.mainCfg.syncCfg;
+ globalPairCfg.filter = currentCfg_.mainCfg.globalFilter;
+ globalPairCfg.miscCfg.deviceParallelOps = currentCfg_.mainCfg.deviceParallelOps;
globalPairCfg.miscCfg.ignoreErrors = currentCfg_.mainCfg.ignoreErrors;
globalPairCfg.miscCfg.automaticRetryCount = currentCfg_.mainCfg.automaticRetryCount;
globalPairCfg.miscCfg.automaticRetryDelay = currentCfg_.mainCfg.automaticRetryDelay;
@@ -3352,28 +3355,27 @@ void MainDialog::showConfigDialog(SyncConfigPanel panelToShow, int localPairInde
//don't recalculate value but consider current screen status!!!
//e.g. it's possible that the first folder pair local config is shown with all config initial if user just removed local config via mouse context menu!
- const bool showLocalCfg = m_bpButtonLocalCompCfg->IsShown();
+ const bool showMultipleCfgs = m_bpButtonLocalCompCfg->IsShown();
//harmonize with MainDialog::updateGuiForFolderPair()!
+ assert(showMultipleCfgs || localPairIndexToShow == -1);
assert(m_bpButtonLocalCompCfg->IsShown() == m_bpButtonLocalSyncCfg->IsShown() &&
- m_bpButtonLocalCompCfg->IsShown() == m_bpButtonLocalFilter->IsShown());
+ m_bpButtonLocalCompCfg->IsShown() == m_bpButtonLocalFilter ->IsShown());
- std::vector<LocalPairConfig> localCfgs;
- if (showLocalCfg)
- {
- localCfgs.push_back(firstFolderPair_->getValues());
+ std::vector<LocalPairConfig> localCfgs; //showSyncConfigDlg() needs *all* folder pairs for deviceParallelOps update
+ localCfgs.push_back(firstFolderPair_->getValues());
- for (const FolderPairPanel* panel : additionalFolderPairs_)
- localCfgs.push_back(panel->getValues());
- }
+ for (const FolderPairPanel* panel : additionalFolderPairs_)
+ localCfgs.push_back(panel->getValues());
- //------------------------------------------------
+ //------------------------------------------------------------------------------------
const GlobalPairConfig globalPairCfgOld = globalPairCfg;
const std::vector<LocalPairConfig> localPairCfgOld = localCfgs;
if (showSyncConfigDlg(this,
panelToShow,
- localPairIndexToShow,
+ showMultipleCfgs ? localPairIndexToShow : -1,
+ showMultipleCfgs,
globalPairCfg,
localCfgs,
globalCfg_.gui.commandHistItemsMax) != ReturnSyncConfig::BUTTON_OKAY)
@@ -3381,10 +3383,11 @@ void MainDialog::showConfigDialog(SyncConfigPanel panelToShow, int localPairInde
assert(localCfgs.size() == localPairCfgOld.size());
- currentCfg_.mainCfg.cmpConfig = globalPairCfg.cmpConfig;
+ currentCfg_.mainCfg.cmpCfg = globalPairCfg.cmpCfg;
currentCfg_.mainCfg.syncCfg = globalPairCfg.syncCfg;
currentCfg_.mainCfg.globalFilter = globalPairCfg.filter;
+ currentCfg_.mainCfg.deviceParallelOps = globalPairCfg.miscCfg.deviceParallelOps;
currentCfg_.mainCfg.ignoreErrors = globalPairCfg.miscCfg.ignoreErrors;
currentCfg_.mainCfg.automaticRetryCount = globalPairCfg.miscCfg.automaticRetryCount;
currentCfg_.mainCfg.automaticRetryDelay = globalPairCfg.miscCfg.automaticRetryDelay;
@@ -3392,17 +3395,14 @@ void MainDialog::showConfigDialog(SyncConfigPanel panelToShow, int localPairInde
currentCfg_.mainCfg.postSyncCondition = globalPairCfg.miscCfg.postSyncCondition;
globalCfg_.gui.commandHistory = globalPairCfg.miscCfg.commandHistory;
- if (showLocalCfg)
- {
- firstFolderPair_->setValues(localCfgs[0]);
+ firstFolderPair_->setValues(localCfgs[0]);
- for (size_t i = 1; i < localCfgs.size(); ++i)
- additionalFolderPairs_[i - 1]->setValues(localCfgs[i]);
- }
+ for (size_t i = 1; i < localCfgs.size(); ++i)
+ additionalFolderPairs_[i - 1]->setValues(localCfgs[i]);
- //------------------------------------------------
+ //------------------------------------------------------------------------------------
- const bool cmpConfigChanged = globalPairCfg.cmpConfig != globalPairCfgOld.cmpConfig || [&]
+ const bool cmpConfigChanged = globalPairCfg.cmpCfg != globalPairCfgOld.cmpCfg || [&]
{
for (size_t i = 0; i < localCfgs.size(); ++i)
if (localCfgs[i].localCmpCfg != localPairCfgOld[i].localCmpCfg)
@@ -3426,18 +3426,18 @@ void MainDialog::showConfigDialog(SyncConfigPanel panelToShow, int localPairInde
return false;
}();
- const bool miscConfigChanged = globalPairCfg.miscCfg.ignoreErrors != globalPairCfgOld.miscCfg.ignoreErrors ||
+ const bool miscConfigChanged = globalPairCfg.miscCfg.deviceParallelOps != globalPairCfgOld.miscCfg.deviceParallelOps ||
+ globalPairCfg.miscCfg.ignoreErrors != globalPairCfgOld.miscCfg.ignoreErrors ||
globalPairCfg.miscCfg.automaticRetryCount != globalPairCfgOld.miscCfg.automaticRetryCount ||
globalPairCfg.miscCfg.automaticRetryDelay != globalPairCfgOld.miscCfg.automaticRetryDelay ||
globalPairCfg.miscCfg.postSyncCommand != globalPairCfgOld.miscCfg.postSyncCommand ||
globalPairCfg.miscCfg.postSyncCondition != globalPairCfgOld.miscCfg.postSyncCondition;
- //globalPairCfg.miscCfg.commandHistory != globalPairCfgOld.miscCfg.commandHistory;
-
- //------------------------------------------------
+ /**/ //globalPairCfg.miscCfg.commandHistory != globalPairCfgOld.miscCfg.commandHistory;
+ //------------------------------------------------------------------------------------
if (cmpConfigChanged)
{
- const bool setDefaultViewType = globalPairCfg.cmpConfig.compareVar != globalPairCfgOld.cmpConfig.compareVar;
+ const bool setDefaultViewType = globalPairCfg.cmpCfg.compareVar != globalPairCfgOld.cmpCfg.compareVar;
applyCompareConfig(setDefaultViewType);
}
@@ -3659,12 +3659,16 @@ void MainDialog::OnCompare(wxCommandEvent& event)
auto app = wxTheApp; //fix lambda/wxWigets/VC fuck up
ZEN_ON_SCOPE_EXIT(app->Yield(); enableAllElements()); //ui update before enabling buttons again: prevent strange behaviour of delayed button clicks
+ const auto& guiCfg = getConfig();
+
+ const std::map<AbstractPath, size_t>& deviceParallelOps = guiCfg.mainCfg.deviceParallelOps;
+
try
{
//handle status display and error messages
StatusHandlerTemporaryPanel statusHandler(*this);
- const std::vector<FolderPairCfg> cmpConfig = extractCompareCfg(getConfig().mainCfg);
+ const std::vector<FolderPairCfg> fpCfgList = extractCompareCfg(guiCfg.mainCfg);
//GUI mode: place directory locks on directories isolated(!) during both comparison and synchronization
std::unique_ptr<LockHolder> dirLocks;
@@ -3677,7 +3681,8 @@ void MainDialog::OnCompare(wxCommandEvent& event)
globalCfg_.folderAccessTimeout,
globalCfg_.createLockFile,
dirLocks,
- cmpConfig,
+ fpCfgList,
+ deviceParallelOps,
statusHandler); //throw AbortProcess
}
catch (AbortProcess&)
@@ -3804,7 +3809,7 @@ void MainDialog::applyCompareConfig(bool setDefaultViewType)
//convenience: change sync view
if (setDefaultViewType)
- switch (currentCfg_.mainCfg.cmpConfig.compareVar)
+ switch (currentCfg_.mainCfg.cmpCfg.compareVar)
{
case CompareVariant::TIME_SIZE:
case CompareVariant::SIZE:
@@ -3830,13 +3835,15 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
return;
}
+ const auto& guiCfg = getConfig();
+
//show sync preview/confirmation dialog
if (globalCfg_.confirmDlgs.confirmSyncStart)
{
bool dontShowAgain = false;
if (showSyncConfirmationDlg(this,
- getSyncVariantName(getConfig().mainCfg),
+ getSyncVariantName(guiCfg.mainCfg),
SyncStatistics(folderCmp_),
dontShowAgain) != ReturnSmallDlg::BUTTON_OKAY)
return;
@@ -3844,6 +3851,8 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
globalCfg_.confirmDlgs.confirmSyncStart = !dontShowAgain;
}
+ const std::map<AbstractPath, size_t>& deviceParallelOps = guiCfg.mainCfg.deviceParallelOps;
+
bool exitAfterSync = false;
try
{
@@ -3852,8 +3861,6 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
//PERF_START;
const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalFilePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring();
- const auto& guiCfg = getConfig();
-
disableAllElements(false); //StatusHandlerFloatingDialog will internally process Window messages, so avoid unexpected callbacks!
ZEN_ON_SCOPE_EXIT(enableAllElements());
@@ -3909,6 +3916,7 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
globalCfg_.folderAccessTimeout,
syncProcessCfg,
folderCmp_,
+ deviceParallelOps,
globalCfg_.warnDlgs,
statusHandler);
@@ -4497,6 +4505,8 @@ void MainDialog::onAddFolderPairKeyEvent(wxKeyEvent& event)
void MainDialog::updateGuiForFolderPair()
{
+ recalcMaxFolderPairsVisible();
+
//adapt delete top folder pair button
m_bpButtonRemovePair->Show(!additionalFolderPairs_.empty());
m_panelTopLeft->Layout();
@@ -4516,24 +4526,19 @@ void MainDialog::updateGuiForFolderPair()
//update sub-panel sizes for calculations below!!!
m_panelTopCenter->GetSizer()->SetSizeHints(m_panelTopCenter); //~=Fit() + SetMinSize()
- int addPairMinimalHeight = 0;
- int addPairOptimalHeight = 0;
- if (!additionalFolderPairs_.empty())
- {
- const int pairHeight = additionalFolderPairs_[0]->GetSize().GetHeight();
- addPairMinimalHeight = std::min<double>(1.5, additionalFolderPairs_.size()) * pairHeight; //have 1.5 * height indicate that more folders are there
- addPairOptimalHeight = std::min<double>(globalCfg_.gui.mainDlg.maxFolderPairsVisible - 1 + 0.5, //subtract first/main folder pair and add 0.5 to indicate additional folders
- additionalFolderPairs_.size()) * pairHeight;
+ const int firstPairHeight = std::max(m_panelDirectoryPairs->ClientToWindowSize(m_panelTopLeft ->GetSize()).y, //include m_panelDirectoryPairs window borders!
+ m_panelDirectoryPairs->ClientToWindowSize(m_panelTopCenter->GetSize()).y); //
+ const int addPairHeight = !additionalFolderPairs_.empty() ? additionalFolderPairs_[0]->GetSize().y : 0;
- addPairOptimalHeight = std::max(addPairOptimalHeight, addPairMinimalHeight); //implicitly handle corrupted values for "maxFolderPairsVisible"
- }
+ const double addPairCountMax = std::max(globalCfg_.gui.mainDlg.maxFolderPairsVisible - 1 + 0.5, 1.5);
- const int firstPairHeight = std::max(m_panelDirectoryPairs->ClientToWindowSize(m_panelTopLeft ->GetSize()).GetHeight(), //include m_panelDirectoryPairs window borders!
- m_panelDirectoryPairs->ClientToWindowSize(m_panelTopCenter->GetSize()).GetHeight()); //
+ const double addPairCountMin = std::min<double>(1.5, additionalFolderPairs_.size()); //add 0.5 to indicate additional folders
+ const double addPairCountOpt = std::min<double>(addPairCountMax, additionalFolderPairs_.size()); //
+ addPairCountLast_ = addPairCountOpt;
//########################################################################################################################
//wxAUI hack: set minimum height to desired value, then call wxAuiPaneInfo::Fixed() to apply it
- auiMgr_.GetPane(m_panelDirectoryPairs).MinSize(-1, firstPairHeight + addPairOptimalHeight);
+ auiMgr_.GetPane(m_panelDirectoryPairs).MinSize(-1, firstPairHeight + addPairCountOpt * addPairHeight);
auiMgr_.GetPane(m_panelDirectoryPairs).Fixed();
auiMgr_.Update();
@@ -4543,7 +4548,7 @@ void MainDialog::updateGuiForFolderPair()
//########################################################################################################################
//make sure user cannot fully shrink additional folder pairs
- auiMgr_.GetPane(m_panelDirectoryPairs).MinSize(-1, firstPairHeight + addPairMinimalHeight);
+ auiMgr_.GetPane(m_panelDirectoryPairs).MinSize(-1, firstPairHeight + addPairCountMin * addPairHeight);
auiMgr_.Update();
//it seems there is no GetSizer()->SetSizeHints(this)/Fit() required due to wxAui "magic"
@@ -4551,6 +4556,26 @@ void MainDialog::updateGuiForFolderPair()
}
+void MainDialog::recalcMaxFolderPairsVisible()
+{
+ const int firstPairHeight = std::max(m_panelDirectoryPairs->ClientToWindowSize(m_panelTopLeft ->GetSize()).y, //include m_panelDirectoryPairs window borders!
+ m_panelDirectoryPairs->ClientToWindowSize(m_panelTopCenter->GetSize()).y); //
+ const int addPairHeight = !additionalFolderPairs_.empty() ? additionalFolderPairs_[0]->GetSize().y :
+ m_bpButtonAddPair->GetSize().y; //an educated guess
+ assert(addPairHeight > 0);
+
+ if (addPairCountLast_ && addPairHeight > 0)
+ {
+ const double addPairCountCurrent = (m_panelDirectoryPairs->GetSize().y - firstPairHeight) / (1.0 * addPairHeight); //include m_panelDirectoryPairs window borders!
+
+ if (numeric::dist(addPairCountCurrent, *addPairCountLast_) > 0.4) //=> presumely changed by user!
+ {
+ globalCfg_.gui.mainDlg.maxFolderPairsVisible = numeric::round(addPairCountCurrent) + 1;
+ }
+ }
+}
+
+
void MainDialog::insertAddFolderPair(const std::vector<LocalPairConfig>& newPairs, size_t pos)
{
assert(pos <= additionalFolderPairs_.size() && additionalFolderPairs_.size() == bSizerAddFolderPairs->GetItemCount());
@@ -4564,6 +4589,8 @@ void MainDialog::insertAddFolderPair(const std::vector<LocalPairConfig>& newPair
newPair->m_folderPathLeft ->init(folderHistoryLeft_);
newPair->m_folderPathRight->init(folderHistoryRight_);
+ newPair->m_bpButtonFolderPairOptions->SetBitmapLabel(getResourceImage(L"button_arrow_down"));
+
//set width of left folder panel
const int width = m_panelTopLeft->GetSize().GetWidth();
newPair->m_panelLeft->SetMinSize(wxSize(width, -1));
@@ -4650,7 +4677,6 @@ void MainDialog::setAddFolderPairs(const std::vector<LocalPairConfig>& newPairs)
additionalFolderPairs_.clear();
bSizerAddFolderPairs->Clear(true);
- //updateGuiForFolderPair(); -> already called in insertAddFolderPair()
insertAddFolderPair(newPairs, 0);
}
diff --git a/FreeFileSync/Source/ui/main_dlg.h b/FreeFileSync/Source/ui/main_dlg.h
index 40496410..e4f4be05 100755
--- a/FreeFileSync/Source/ui/main_dlg.h
+++ b/FreeFileSync/Source/ui/main_dlg.h
@@ -26,7 +26,6 @@ namespace fff
class FolderPairFirst;
class FolderPairPanel;
class CompareProgressDialog;
-class FolderSelectorImpl;
template <class GuiPanel>
class FolderPairCallback;
class PanelMoveWindow;
@@ -64,7 +63,6 @@ private:
friend class StatusHandlerFloatingDialog;
friend class FolderPairFirst;
friend class FolderPairPanel;
- friend class FolderSelectorImpl;
template <class GuiPanel>
friend class FolderPairCallback;
friend class PanelMoveWindow;
@@ -98,6 +96,7 @@ private:
void setAddFolderPairs(const std::vector<LocalPairConfig>& newPairs);
void updateGuiForFolderPair(); //helper method: add usability by showing/hiding buttons related to folder pairs
+ void recalcMaxFolderPairsVisible();
//main method for putting gridDataView on UI: updates data respecting current view settings
void updateGui(); //kitchen-sink update
@@ -307,6 +306,8 @@ private:
//folder pairs:
std::unique_ptr<FolderPairFirst> firstFolderPair_; //always bound!!!
std::vector<FolderPairPanel*> additionalFolderPairs_; //additional pairs to the first pair
+
+ zen::Opt<double> addPairCountLast_;
//-------------------------------------
//***********************************************
diff --git a/FreeFileSync/Source/ui/small_dlgs.cpp b/FreeFileSync/Source/ui/small_dlgs.cpp
index 1fd6229b..29365b27 100755
--- a/FreeFileSync/Source/ui/small_dlgs.cpp
+++ b/FreeFileSync/Source/ui/small_dlgs.cpp
@@ -33,6 +33,7 @@
+
using namespace zen;
using namespace fff;
@@ -57,6 +58,7 @@ AboutDlg::AboutDlg(wxWindow* parent) : AboutDlgGenerated(parent)
assert(m_buttonClose->GetId() == wxID_OK); //we cannot use wxID_CLOSE else Esc key won't work: yet another wxWidgets bug??
m_bitmapHomepage->SetBitmap(getResourceImage(L"website"));
+ m_bitmapForum ->SetBitmap(getResourceImage(L"forum"));
m_bitmapEmail ->SetBitmap(getResourceImage(L"email"));
m_bitmapGpl ->SetBitmap(getResourceImage(L"gpl"));
@@ -215,7 +217,10 @@ CopyToDialog::CopyToDialog(wxWindow* parent,
m_bitmapCopyTo->SetBitmap(getResourceImage(L"copy_to"));
- targetFolder = std::make_unique<FolderSelector>(*this, *m_buttonSelectTargetFolder, *m_bpButtonSelectAltTargetFolder, *m_targetFolderPath, nullptr /*staticText*/, nullptr /*wxWindow*/);
+ targetFolder = std::make_unique<FolderSelector>(*this, *m_buttonSelectTargetFolder, *m_bpButtonSelectAltTargetFolder, *m_targetFolderPath, nullptr /*staticText*/, nullptr /*wxWindow*/,
+ nullptr /*droppedPathsFilter*/,
+ [](const Zstring& folderPathPhrase) { return 1; } /*getDeviceParallelOps*/,
+ nullptr /*setDeviceParallelOps*/);
m_targetFolderPath->init(folderHistory_);
diff --git a/FreeFileSync/Source/ui/sync_cfg.cpp b/FreeFileSync/Source/ui/sync_cfg.cpp
index 38aa8940..101e2b8b 100755
--- a/FreeFileSync/Source/ui/sync_cfg.cpp
+++ b/FreeFileSync/Source/ui/sync_cfg.cpp
@@ -25,13 +25,14 @@
#include "../fs/concrete.h"
+
using namespace zen;
using namespace fff;
namespace
{
-const int CFG_DESCRIPTION_WIDTH_DIP = 250;
+const int CFG_DESCRIPTION_WIDTH_DIP = 230;
class ConfigDialog : public ConfigDlgGenerated
@@ -39,7 +40,7 @@ class ConfigDialog : public ConfigDlgGenerated
public:
ConfigDialog(wxWindow* parent,
SyncConfigPanel panelToShow,
- int localPairIndexToShow,
+ int localPairIndexToShow, bool showMultipleCfgs,
GlobalPairConfig& globalPairCfg,
std::vector<LocalPairConfig>& localPairConfig,
size_t commandHistItemsMax);
@@ -66,6 +67,8 @@ private:
//------------- comparison panel ----------------------
void OnHelpComparisonSettings(wxHyperlinkEvent& event) override { displayHelpEntry(L"comparison-settings", this); }
void OnHelpTimeShift (wxHyperlinkEvent& event) override { displayHelpEntry(L"daylight-saving-time", this); }
+ void OnHelpPerformance (wxHyperlinkEvent& event) override { displayHelpEntry(L"performance", this); }
+ warn_static("write performance help entry")
void OnToggleLocalCompSettings(wxCommandEvent& event) override { updateCompGui(); updateSyncGui(); /*affects sync settings, too!*/ }
void OnCompByTimeSize (wxCommandEvent& event) override { localCmpVar_ = CompareVariant::TIME_SIZE; updateCompGui(); updateSyncGui(); } //
@@ -140,11 +143,16 @@ private:
void OnToggleIgnoreErrors(wxCommandEvent& event) override { updateMiscGui(); }
void OnToggleAutoRetry (wxCommandEvent& event) override { updateMiscGui(); }
+ size_t getParallelOpsForVersioning() const;
+
MiscSyncConfig getMiscSyncOptions() const;
void setMiscSyncOptions(const MiscSyncConfig& miscCfg);
void updateMiscGui();
+ std::set<AbstractPath> devicePathsForEdit_; //helper data for deviceParallelOps
+ std::map<AbstractPath, size_t> deviceParallelOps_; //
+
//parameters with ownership NOT within GUI controls!
DirectionConfig directionCfg_;
DeletionPolicy handleDeletion_ = DeletionPolicy::RECYCLER; //use Recycler, delete permanently or move to user-defined location
@@ -168,6 +176,9 @@ private:
int selectedPairIndexToShow_ = EMPTY_PAIR_INDEX_SELECTED;
static const int EMPTY_PAIR_INDEX_SELECTED = -2;
+ const bool showMultipleCfgs_;
+ const bool perfPanelActive_;
+
const size_t commandHistItemsMax_;
};
@@ -209,17 +220,21 @@ std::wstring getSyncVariantDescription(DirectionConfig::Variant var)
ConfigDialog::ConfigDialog(wxWindow* parent,
SyncConfigPanel panelToShow,
- int localPairIndexToShow,
+ int localPairIndexToShow, bool showMultipleCfgs,
GlobalPairConfig& globalPairCfg,
std::vector<LocalPairConfig>& localPairConfig,
size_t commandHistItemsMax) :
ConfigDlgGenerated(parent),
- versioningFolder_(*m_panelVersioning, *m_buttonSelectVersioningFolder, *m_bpButtonSelectAltFolder, *m_versioningFolderPath, nullptr /*staticText*/, nullptr /*dropWindow2*/),
- globalPairCfgOut_(globalPairCfg),
- localPairCfgOut_(localPairConfig),
- globalPairCfg_(globalPairCfg),
- localPairCfg_(localPairConfig),
- commandHistItemsMax_(commandHistItemsMax)
+ versioningFolder_(*m_panelVersioning, *m_buttonSelectVersioningFolder, *m_bpButtonSelectAltFolder, *m_versioningFolderPath, nullptr /*staticText*/, nullptr /*dropWindow2*/,
+ nullptr /*droppedPathsFilter*/,
+ [this](const Zstring& /*folderPathPhrase*/) { return getParallelOpsForVersioning(); }, nullptr /*setDeviceParallelOps*/),
+ globalPairCfgOut_(globalPairCfg),
+ localPairCfgOut_(localPairConfig),
+ globalPairCfg_(globalPairCfg),
+ localPairCfg_(localPairConfig),
+ showMultipleCfgs_(showMultipleCfgs),
+ perfPanelActive_(true),
+commandHistItemsMax_(commandHistItemsMax)
{
setStandardButtonLayout(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonOkay).setCancel(m_buttonCancel));
@@ -261,6 +276,11 @@ ConfigDialog::ConfigDialog(wxWindow* parent,
m_staticTextCompVarDescription->SetMinSize(wxSize(fastFromDIP(CFG_DESCRIPTION_WIDTH_DIP), -1));
+ m_scrolledWindowPerf->SetMinSize(wxSize(fastFromDIP(200), -1));
+ m_bitmapPerf->SetBitmap(perfPanelActive_ ? getResourceImage(L"speed") : greyScale(getResourceImage(L"speed")));
+ m_panelPerfHeader ->Enable(perfPanelActive_);
+ m_staticTextPerfParallelOps->Enable(perfPanelActive_);
+
//------------- filter panel --------------------------
m_textCtrlInclude->SetMinSize(wxSize(fastFromDIP(280), -1));
@@ -269,7 +289,7 @@ ConfigDialog::ConfigDialog(wxWindow* parent,
m_textCtrlInclude->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(ConfigDialog::onFilterKeyEvent), nullptr, this);
m_textCtrlExclude->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(ConfigDialog::onFilterKeyEvent), nullptr, this);
- m_staticTextFilterDescr->Wrap(fastFromDIP(590));
+ m_staticTextFilterDescr->Wrap(fastFromDIP(450));
enumTimeDescr_.
add(UnitTime::NONE, L"(" + _("None") + L")"). //meta options should be enclosed in parentheses
@@ -347,7 +367,7 @@ ConfigDialog::ConfigDialog(wxWindow* parent,
m_listBoxFolderPair->Append(L" " + fpName);
}
- if (localPairConfig.empty())
+ if (!showMultipleCfgs)
{
m_listBoxFolderPair->Hide();
m_staticTextFolderPairLabel->Hide();
@@ -517,6 +537,7 @@ Opt<CompConfig> ConfigDialog::getCompConfig() const
compCfg.compareVar = localCmpVar_;
compCfg.handleSymlinks = !m_checkBoxSymlinksInclude->GetValue() ? SymLinkHandling::EXCLUDE : m_radioBtnSymlinksDirect->GetValue() ? SymLinkHandling::DIRECT : SymLinkHandling::FOLLOW;
compCfg.ignoreTimeShiftMinutes = fromTimeShiftPhrase(copyStringTo<std::wstring>(m_textCtrlTimeShift->GetValue()));
+
return compCfg;
}
@@ -526,7 +547,7 @@ void ConfigDialog::setCompConfig(const CompConfig* compCfg)
m_checkBoxUseLocalCmpOptions->SetValue(compCfg);
//when local settings are inactive, display (current) global settings instead:
- const CompConfig tmpCfg = compCfg ? *compCfg : globalPairCfg_.cmpConfig;
+ const CompConfig tmpCfg = compCfg ? *compCfg : globalPairCfg_.cmpCfg;
localCmpVar_ = tmpCfg.compareVar;
@@ -943,7 +964,7 @@ void ConfigDialog::updateSyncGui()
setBitmap(*m_bitmapDatabase, getResourceImage(L"database"));
else
{
- const CompareVariant activeCmpVar = m_checkBoxUseLocalCmpOptions->GetValue() ? localCmpVar_ : globalPairCfg_.cmpConfig.compareVar;
+ const CompareVariant activeCmpVar = m_checkBoxUseLocalCmpOptions->GetValue() ? localCmpVar_ : globalPairCfg_.cmpCfg.compareVar;
m_bitmapLeftNewer ->Show(activeCmpVar == CompareVariant::TIME_SIZE);
m_bpButtonLeftNewer ->Show(activeCmpVar == CompareVariant::TIME_SIZE);
@@ -1046,13 +1067,63 @@ void ConfigDialog::updateSyncGui()
}
+size_t ConfigDialog::getParallelOpsForVersioning() const
+{
+ assert(selectedPairIndexToShow_ == -1 || makeUnsigned(selectedPairIndexToShow_) < localPairCfg_.size());
+
+ const auto& deviceParallelOps = selectedPairIndexToShow_ < 0 ? getMiscSyncOptions().deviceParallelOps : globalPairCfg_.miscCfg.deviceParallelOps; //ternary-WTF!
+
+ auto getParallelOps = [&](const Zstring& folderPathPhrase)
+ {
+ const AbstractPath rootPath = AFS::getPathComponents(createAbstractPath(folderPathPhrase)).rootPath;
+ auto itParOps = deviceParallelOps.find(rootPath);
+ return std::max<size_t>(itParOps != deviceParallelOps.end() ? itParOps->second : 1, 1);
+ };
+ //follow deviceParallelOps editing-behavior from synchronization.cpp:
+ //=> parallelOps used for versioning == number used for folder pair!
+ auto getParallelOpsFp = [&](const LocalPairConfig& fpCfg)
+ {
+ return std::max(getParallelOps(fpCfg.folderPathPhraseLeft), getParallelOps(fpCfg.folderPathPhraseRight));
+ };
+
+ if (selectedPairIndexToShow_ < 0)
+ {
+ size_t parallelOps = 1;
+ for (const LocalPairConfig& fpCfg : localPairCfg_)
+ if (!fpCfg.localSyncCfg) //only consider folder pairs affected by main config's versioning folder
+ parallelOps = std::max(parallelOps, getParallelOpsFp(fpCfg));
+ return parallelOps;
+ }
+ else
+ return getParallelOpsFp(localPairCfg_[selectedPairIndexToShow_]);
+}
+
+
MiscSyncConfig ConfigDialog::getMiscSyncOptions() const
{
assert(selectedPairIndexToShow_ == -1);
-
MiscSyncConfig miscCfg;
- miscCfg.ignoreErrors = m_checkBoxIgnoreErrors->GetValue();
- miscCfg.automaticRetryCount = m_checkBoxAutoRetry->GetValue() ? m_spinCtrlAutoRetryCount->GetValue() : 0;
+
+ // Avoid "fake" changed configs! =>
+ // - don't touch items corresponding to paths not currently used
+ // - don't store parallel ops == 1
+ miscCfg.deviceParallelOps = deviceParallelOps_;
+ assert(fgSizerPerf->GetItemCount() == 2 * devicePathsForEdit_.size());
+ int i = 0;
+ for (const AbstractPath& devPath : devicePathsForEdit_)
+ {
+ wxSpinCtrl* spinCtrlParallelOps = dynamic_cast<wxSpinCtrl*>(fgSizerPerf->GetItem(i * 2)->GetWindow());
+ const size_t parallelOps = spinCtrlParallelOps->GetValue();
+
+ if (parallelOps > 1)
+ miscCfg.deviceParallelOps[devPath] = parallelOps;
+ else
+ miscCfg.deviceParallelOps.erase(devPath);
+ ++i;
+ }
+ //----------------------------------------------------------------------------
+ miscCfg.ignoreErrors = m_checkBoxIgnoreErrors ->GetValue();
+ miscCfg.automaticRetryCount = m_checkBoxAutoRetry ->GetValue() ? m_spinCtrlAutoRetryCount->GetValue() : 0;
miscCfg.automaticRetryDelay = m_spinCtrlAutoRetryDelay->GetValue();
miscCfg.postSyncCommand = m_comboBoxPostSyncCommand->getValue();
@@ -1064,7 +1135,60 @@ MiscSyncConfig ConfigDialog::getMiscSyncOptions() const
void ConfigDialog::setMiscSyncOptions(const MiscSyncConfig& miscCfg)
{
- m_checkBoxIgnoreErrors->SetValue(miscCfg.ignoreErrors);
+ assert(selectedPairIndexToShow_ == -1);
+
+ // Avoid "fake" changed configs! =>
+ //- when editting, consider only the deviceParallelOps items corresponding to the currently-used folder paths
+ //- show parallel ops == 1 only temporarily during edit
+ deviceParallelOps_ = miscCfg.deviceParallelOps;
+
+ devicePathsForEdit_.clear();
+ auto addDevicePath = [&](const Zstring& folderPathPhrase)
+ {
+ const AbstractPath rootPath = AFS::getPathComponents(createAbstractPath(folderPathPhrase)).rootPath;
+ if (!AFS::isNullPath(rootPath))
+ devicePathsForEdit_.insert(rootPath);
+ };
+ for (const LocalPairConfig& fpCfg : localPairCfg_)
+ {
+ addDevicePath(fpCfg.folderPathPhraseLeft);
+ addDevicePath(fpCfg.folderPathPhraseRight);
+ }
+
+ assert(fgSizerPerf->GetItemCount() % 2 == 0);
+ const int rowsToCreate = static_cast<int>(devicePathsForEdit_.size()) - static_cast<int>(fgSizerPerf->GetItemCount() / 2);
+ if (rowsToCreate >= 0)
+ for (int i = 0; i < rowsToCreate; ++i)
+ {
+ wxSpinCtrl* spinCtrlParallelOps = new wxSpinCtrl(m_scrolledWindowPerf, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 1, 2000000000, 1);
+ spinCtrlParallelOps->SetMinSize(wxSize(fastFromDIP(60), -1)); //Hack: set size (why does wxWindow::Size() not work?)
+ spinCtrlParallelOps->Enable(perfPanelActive_);
+ fgSizerPerf->Add(spinCtrlParallelOps, 0, wxALIGN_CENTER_VERTICAL);
+
+ wxStaticText* staticTextDevice = new wxStaticText(m_scrolledWindowPerf, wxID_ANY, wxEmptyString);
+ staticTextDevice->Enable(perfPanelActive_);
+ fgSizerPerf->Add(staticTextDevice, 0, wxALIGN_CENTER_VERTICAL);
+ }
+ else
+ for (int i = 0; i < -rowsToCreate * 2; ++i)
+ fgSizerPerf->GetItem(size_t(0))->GetWindow()->Destroy();
+ assert(fgSizerPerf->GetItemCount() == 2 * devicePathsForEdit_.size());
+
+ int i = 0;
+ for (const AbstractPath& devPath : devicePathsForEdit_)
+ {
+ wxSpinCtrl* spinCtrlParallelOps = dynamic_cast<wxSpinCtrl*> (fgSizerPerf->GetItem(i * 2 )->GetWindow());
+ wxStaticText* staticTextDevice = dynamic_cast<wxStaticText*>(fgSizerPerf->GetItem(i * 2 + 1)->GetWindow());
+
+ auto itParOps = deviceParallelOps_.find(devPath);
+ spinCtrlParallelOps->SetValue(std::max<int>(itParOps != deviceParallelOps_.end() ? static_cast<int>(itParOps->second) : 1, 1));
+ staticTextDevice->SetLabel(AFS::getDisplayPath(devPath));
+ ++i;
+ }
+ m_panelComparisonSettings->Layout(); //*after* setting text labels
+
+ //----------------------------------------------------------------------------
+ m_checkBoxIgnoreErrors ->SetValue(miscCfg.ignoreErrors);
m_checkBoxAutoRetry ->SetValue(miscCfg.automaticRetryCount > 0);
m_spinCtrlAutoRetryCount->SetValue(std::max<size_t>(miscCfg.automaticRetryCount, 0));
m_spinCtrlAutoRetryDelay->SetValue(miscCfg.automaticRetryDelay);
@@ -1081,6 +1205,7 @@ void ConfigDialog::updateMiscGui()
{
const MiscSyncConfig miscCfg = getMiscSyncOptions();
+ //----------------------------------------------------------------------------
m_bitmapIgnoreErrors->SetBitmap(miscCfg.ignoreErrors ? getResourceImage(L"error_ignore_active") : greyScale(getResourceImage(L"error_ignore_inactive")));
m_bitmapRetryErrors ->SetBitmap(miscCfg.automaticRetryCount > 0 ? getResourceImage(L"error_retry") : greyScale(getResourceImage(L"error_retry")));
@@ -1102,26 +1227,32 @@ void ConfigDialog::selectFolderPairConfig(int newPairIndexToShow)
//show/hide controls that are only relevant for main/local config
const bool mainConfigSelected = newPairIndexToShow < 0;
//comparison panel:
- m_staticTextMainCompSettings->Show( mainConfigSelected && !localPairCfg_.empty());
- m_checkBoxUseLocalCmpOptions->Show(!mainConfigSelected && !localPairCfg_.empty());
- m_staticlineCompHeader->Show(!localPairCfg_.empty());
- m_panelCompSettingsTab->Layout(); //fix comp panel glitch on Win 7 125% font size
+ m_staticTextMainCompSettings->Show( mainConfigSelected && showMultipleCfgs_);
+ m_checkBoxUseLocalCmpOptions->Show(!mainConfigSelected && showMultipleCfgs_);
+ m_staticlineCompHeader->Show(showMultipleCfgs_);
//filter panel
- m_staticTextMainFilterSettings ->Show( mainConfigSelected && !localPairCfg_.empty());
- m_staticTextLocalFilterSettings->Show(!mainConfigSelected && !localPairCfg_.empty());
- m_staticlineFilterHeader->Show(!localPairCfg_.empty());
- m_panelFilterSettingsTab->Layout();
+ m_staticTextMainFilterSettings ->Show( mainConfigSelected && showMultipleCfgs_);
+ m_staticTextLocalFilterSettings->Show(!mainConfigSelected && showMultipleCfgs_);
+ m_staticlineFilterHeader->Show(showMultipleCfgs_);
//sync panel:
- m_staticTextMainSyncSettings ->Show( mainConfigSelected && !localPairCfg_.empty());
- m_checkBoxUseLocalSyncOptions->Show(!mainConfigSelected && !localPairCfg_.empty());
- m_staticlineSyncHeader->Show(!localPairCfg_.empty());
- m_panelSyncSettingsTab->Layout();
+ m_staticTextMainSyncSettings ->Show( mainConfigSelected && showMultipleCfgs_);
+ m_checkBoxUseLocalSyncOptions->Show(!mainConfigSelected && showMultipleCfgs_);
+ m_staticlineSyncHeader->Show(showMultipleCfgs_);
//misc
- bSizerMiscConfig->Show(mainConfigSelected);
+ bSizerPerformance ->Show(mainConfigSelected); //caveat: recursively shows hidden child items!
+ m_staticlinePerformance->Show(mainConfigSelected);
+ bSizerMiscConfig ->Show(mainConfigSelected);
+
+ if (mainConfigSelected) m_staticTextPerfDeRequired->Show(!perfPanelActive_); //keep after bSizerPerformance->Show()
+ if (mainConfigSelected) m_staticlinePerfDeRequired->Show(!perfPanelActive_); //
+
+ m_panelCompSettingsTab ->Layout(); //fix comp panel glitch on Win 7 125% font size + perf panel
+ m_panelFilterSettingsTab->Layout();
+ m_panelSyncSettingsTab ->Layout();
if (mainConfigSelected)
{
- setCompConfig (&globalPairCfg_.cmpConfig);
+ setCompConfig (&globalPairCfg_.cmpCfg);
setSyncConfig (&globalPairCfg_.syncCfg);
setFilterConfig (globalPairCfg_.filter);
setMiscSyncOptions(globalPairCfg_.miscCfg);
@@ -1166,10 +1297,10 @@ bool ConfigDialog::unselectFolderPairConfig()
if (selectedPairIndexToShow_ < 0)
{
- globalPairCfg_.cmpConfig = *compCfg;
- globalPairCfg_.syncCfg = *syncCfg;
- globalPairCfg_.filter = filterCfg;
- globalPairCfg_.miscCfg = getMiscSyncOptions();
+ globalPairCfg_.cmpCfg = *compCfg;
+ globalPairCfg_.syncCfg = *syncCfg;
+ globalPairCfg_.filter = filterCfg;
+ globalPairCfg_.miscCfg = getMiscSyncOptions();
}
else
{
@@ -1189,8 +1320,8 @@ void ConfigDialog::OnOkay(wxCommandEvent& event)
if (!unselectFolderPairConfig())
return;
- globalPairCfgOut_ = globalPairCfg_;
- localPairCfgOut_ = localPairCfg_;
+ globalPairCfgOut_ = globalPairCfg_;
+ localPairCfgOut_ = localPairCfg_;
EndModal(ReturnSyncConfig::BUTTON_OKAY);
}
@@ -1200,16 +1331,17 @@ void ConfigDialog::OnOkay(wxCommandEvent& event)
ReturnSyncConfig::ButtonPressed fff::showSyncConfigDlg(wxWindow* parent,
SyncConfigPanel panelToShow,
- int localPairIndexToShow,
+ int localPairIndexToShow, bool showMultipleCfgs,
GlobalPairConfig& globalPairCfg,
std::vector<LocalPairConfig>& localPairConfig,
size_t commandHistItemsMax)
{
+
ConfigDialog syncDlg(parent,
panelToShow,
- localPairIndexToShow,
+ localPairIndexToShow, showMultipleCfgs,
globalPairCfg,
localPairConfig,
commandHistItemsMax);
diff --git a/FreeFileSync/Source/ui/sync_cfg.h b/FreeFileSync/Source/ui/sync_cfg.h
index a18207de..fd41a529 100755
--- a/FreeFileSync/Source/ui/sync_cfg.h
+++ b/FreeFileSync/Source/ui/sync_cfg.h
@@ -31,6 +31,7 @@ enum class SyncConfigPanel
struct MiscSyncConfig
{
+ std::map<AbstractPath, size_t> deviceParallelOps;
bool ignoreErrors = false;
size_t automaticRetryCount = 0;
size_t automaticRetryDelay = 0;
@@ -41,7 +42,7 @@ struct MiscSyncConfig
struct GlobalPairConfig
{
- CompConfig cmpConfig;
+ CompConfig cmpCfg;
SyncConfig syncCfg;
FilterConfig filter;
MiscSyncConfig miscCfg;
@@ -51,6 +52,7 @@ struct GlobalPairConfig
ReturnSyncConfig::ButtonPressed showSyncConfigDlg(wxWindow* parent,
SyncConfigPanel panelToShow,
int localPairIndexToShow, //< 0 to show global config
+ bool showMultipleCfgs,
GlobalPairConfig& globalPairCfg,
std::vector<LocalPairConfig>& localPairConfig,
diff --git a/FreeFileSync/Source/ui/tree_grid.cpp b/FreeFileSync/Source/ui/tree_grid.cpp
index d17985f7..de7561d0 100755
--- a/FreeFileSync/Source/ui/tree_grid.cpp
+++ b/FreeFileSync/Source/ui/tree_grid.cpp
@@ -179,9 +179,9 @@ struct TreeView::LessShortName
switch (lhs.type)
{
case TreeView::TYPE_ROOT:
- return makeSortDirection(LessNaturalSort() /*even on Linux*/,
- Int2Type<ascending>())(utfTo<Zstring>(static_cast<const RootNodeImpl*>(lhs.node)->displayName),
- utfTo<Zstring>(static_cast<const RootNodeImpl*>(rhs.node)->displayName));
+ return makeSortDirection(LessNaturalSort() /*even on Linux*/,
+ Int2Type<ascending>())(utfTo<Zstring>(static_cast<const RootNodeImpl*>(lhs.node)->displayName),
+ utfTo<Zstring>(static_cast<const RootNodeImpl*>(rhs.node)->displayName));
case TreeView::TYPE_DIRECTORY:
{
diff --git a/FreeFileSync/Source/ui/version_check.cpp b/FreeFileSync/Source/ui/version_check.cpp
index 9b241c7d..822ce230 100755
--- a/FreeFileSync/Source/ui/version_check.cpp
+++ b/FreeFileSync/Source/ui/version_check.cpp
@@ -21,6 +21,7 @@
#include "version_check_impl.h"
+
using namespace zen;
using namespace fff;
@@ -126,7 +127,7 @@ void showUpdateAvailableDialog(wxWindow* parent, const std::string& onlineVersio
_("&Download")))
{
case ConfirmationButton::ACCEPT:
- wxLaunchDefaultBrowser(L"https://www.freefilesync.org/get_latest.php");
+ wxLaunchDefaultBrowser(L"https://www.freefilesync.org/get_latest.php");
break;
case ConfirmationButton::CANCEL:
break;
@@ -263,6 +264,7 @@ std::shared_ptr<UpdateCheckResult> fff::automaticUpdateCheckRunAsync(const Updat
//run on main thread:
void fff::automaticUpdateCheckEval(wxWindow* parent, time_t& lastUpdateCheck, std::string& lastOnlineVersion, const UpdateCheckResult* resultAsync)
{
+
UpdateCheckResult result;
try
{
diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h
index 677f9556..797c1901 100755
--- a/FreeFileSync/Source/version/version.h
+++ b/FreeFileSync/Source/version/version.h
@@ -3,7 +3,7 @@
namespace fff
{
-const char ffsVersion[] = "9.9"; //internal linkage!
+const char ffsVersion[] = "10.0"; //internal linkage!
const char FFS_VERSION_SEPARATOR = '.';
}
diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp
index e87b245c..e361515a 100755
--- a/wx+/image_resources.cpp
+++ b/wx+/image_resources.cpp
@@ -86,7 +86,7 @@ ImageHolder dpiScale(int width, int height, int dpiWidth, int dpiHeight, const u
struct WorkItem
{
- Zbase<wchar_t> name;
+ std::wstring name; //don't trust wxString to be thread-safe like an int
int width = 0;
int height = 0;
int dpiWidth = 0;
@@ -127,19 +127,20 @@ public:
interruptibleWait(conditionNewWork_, dummy, [this] { return !workLoad_.empty() || !expectMoreWork_; }); //throw ThreadInterruption
- if (workLoad_.empty())
- return NoValue();
-
- WorkItem wi = workLoad_.back(); //
- workLoad_.pop_back(); //yes, no strong exception guarantee (std::bad_alloc)
- return wi; //
+ if (!workLoad_.empty())
+ {
+ WorkItem wi = std::move(workLoad_. back()); //
+ /**/ workLoad_.pop_back(); //yes, no strong exception guarantee (std::bad_alloc)
+ return std::move(wi); //
+ }
+ return NoValue();
}
private:
- bool expectMoreWork_ = true;
- std::vector<WorkItem> workLoad_;
std::mutex lockWork_;
+ std::vector<WorkItem> workLoad_;
std::condition_variable conditionNewWork_; //signal event: data for processing available
+ bool expectMoreWork_ = true;
};
@@ -160,7 +161,7 @@ public:
ImageHolder ih = dpiScale(wi->width, wi->height,
wi->dpiWidth, wi->dpiHeight,
wi->rgb, wi->alpha, hqScale);
- result.access([&](std::vector<std::pair<Zbase<wchar_t>, ImageHolder>>& r) { r.emplace_back(wi->name, std::move(ih)); });
+ result.access([&](std::vector<std::pair<std::wstring, ImageHolder>>& r) { r.emplace_back(wi->name, std::move(ih)); });
}
});
}
@@ -178,7 +179,7 @@ public:
void add(const wxString& name, const wxImage& img)
{
imgKeeper_.push_back(img); //retain (ref-counted) wxImage so that the rgb/alpha pointers remain valid after passed to threads
- workload_.add({ copyStringTo<Zbase<wchar_t>>(name),
+ workload_.add({ copyStringTo<std::wstring>(name),
img.GetWidth(), img.GetHeight(),
fastFromDIP(img.GetWidth()), fastFromDIP(img.GetHeight()), //don't call fastFromDIP() from worker thread (wxWidgets function!)
img.GetData(), img.GetAlpha() });
@@ -193,7 +194,7 @@ public:
std::map<wxString, wxBitmap> output;
- result_.access([&](std::vector<std::pair<Zbase<wchar_t>, ImageHolder>>& r)
+ result_.access([&](std::vector<std::pair<std::wstring, ImageHolder>>& r)
{
for (auto& item : r)
{
@@ -202,7 +203,7 @@ public:
wxImage img(ih.getWidth(), ih.getHeight(), ih.releaseRgb(), false /*static_data*/); //pass ownership
img.SetAlpha(ih.releaseAlpha(), false /*static_data*/);
- output[utfTo<wxString>(item.first)] = wxBitmap(img);
+ output[item.first] = wxBitmap(img);
}
});
return output;
@@ -211,7 +212,7 @@ public:
private:
std::vector<InterruptibleThread> worker_;
WorkLoad workload_;
- Protected<std::vector<std::pair<Zbase<wchar_t>, ImageHolder>>> result_;
+ Protected<std::vector<std::pair<std::wstring, ImageHolder>>> result_;
std::vector<wxImage> imgKeeper_;
};
diff --git a/xBRZ/src/xbrz.cpp b/xBRZ/src/xbrz.cpp
new file mode 100755
index 00000000..4d3ccd25
--- /dev/null
+++ b/xBRZ/src/xbrz.cpp
@@ -0,0 +1,1262 @@
+// ****************************************************************************
+// * This file is part of the xBRZ project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// * *
+// * Additionally and as a special exception, the author gives permission *
+// * to link the code of this program with the following libraries *
+// * (or with modified versions that use the same licenses), and distribute *
+// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
+// * You must obey the GNU General Public License in all respects for all of *
+// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
+// * If you modify this file, you may extend this exception to your version *
+// * of the file, but you are not obligated to do so. If you do not wish to *
+// * do so, delete this exception statement from your version. *
+// ****************************************************************************
+
+#include "xbrz.h"
+#include <cassert>
+#include <vector>
+#include <algorithm>
+#include <cmath> //std::sqrt
+#include "xbrz_tools.h"
+
+using namespace xbrz;
+
+
+namespace
+{
+template <unsigned int M, unsigned int N> inline
+uint32_t gradientRGB(uint32_t pixFront, uint32_t pixBack) //blend front color with opacity M / N over opaque background: http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
+{
+ static_assert(0 < M && M < N && N <= 1000, "");
+
+ auto calcColor = [](unsigned char colFront, unsigned char colBack) -> unsigned char { return (colFront * M + colBack * (N - M)) / N; };
+
+ return makePixel(calcColor(getRed (pixFront), getRed (pixBack)),
+ calcColor(getGreen(pixFront), getGreen(pixBack)),
+ calcColor(getBlue (pixFront), getBlue (pixBack)));
+}
+
+
+template <unsigned int M, unsigned int N> inline
+uint32_t gradientARGB(uint32_t pixFront, uint32_t pixBack) //find intermediate color between two colors with alpha channels (=> NO alpha blending!!!)
+{
+ static_assert(0 < M && M < N && N <= 1000, "");
+
+ const unsigned int weightFront = getAlpha(pixFront) * M;
+ const unsigned int weightBack = getAlpha(pixBack) * (N - M);
+ const unsigned int weightSum = weightFront + weightBack;
+ if (weightSum == 0)
+ return 0;
+
+ auto calcColor = [=](unsigned char colFront, unsigned char colBack)
+ {
+ return static_cast<unsigned char>((colFront * weightFront + colBack * weightBack) / weightSum);
+ };
+
+ return makePixel(static_cast<unsigned char>(weightSum / N),
+ calcColor(getRed (pixFront), getRed (pixBack)),
+ calcColor(getGreen(pixFront), getGreen(pixBack)),
+ calcColor(getBlue (pixFront), getBlue (pixBack)));
+}
+
+
+//inline
+//double fastSqrt(double n)
+//{
+// __asm //speeds up xBRZ by about 9% compared to std::sqrt which internally uses the same assembler instructions but adds some "fluff"
+// {
+// fld n
+// fsqrt
+// }
+//}
+//
+
+
+#if defined __GNUC__
+ #define FORCE_INLINE __attribute__((always_inline)) inline
+#else
+ #define FORCE_INLINE inline
+#endif
+
+
+enum RotationDegree //clock-wise
+{
+ ROT_0,
+ ROT_90,
+ ROT_180,
+ ROT_270
+};
+
+//calculate input matrix coordinates after rotation at compile time
+template <RotationDegree rotDeg, size_t I, size_t J, size_t N>
+struct MatrixRotation;
+
+template <size_t I, size_t J, size_t N>
+struct MatrixRotation<ROT_0, I, J, N>
+{
+ static const size_t I_old = I;
+ static const size_t J_old = J;
+};
+
+template <RotationDegree rotDeg, size_t I, size_t J, size_t N> //(i, j) = (row, col) indices, N = size of (square) matrix
+struct MatrixRotation
+{
+ static const size_t I_old = N - 1 - MatrixRotation<static_cast<RotationDegree>(rotDeg - 1), I, J, N>::J_old; //old coordinates before rotation!
+ static const size_t J_old = MatrixRotation<static_cast<RotationDegree>(rotDeg - 1), I, J, N>::I_old; //
+};
+
+
+template <size_t N, RotationDegree rotDeg>
+class OutputMatrix
+{
+public:
+ OutputMatrix(uint32_t* out, int outWidth) : //access matrix area, top-left at position "out" for image with given width
+ out_(out),
+ outWidth_(outWidth) {}
+
+ template <size_t I, size_t J>
+ uint32_t& ref() const
+ {
+ static const size_t I_old = MatrixRotation<rotDeg, I, J, N>::I_old;
+ static const size_t J_old = MatrixRotation<rotDeg, I, J, N>::J_old;
+ return *(out_ + J_old + I_old * outWidth_);
+ }
+
+private:
+ uint32_t* out_;
+ const int outWidth_;
+};
+
+
+template <class T> inline
+T square(T value) { return value * value; }
+
+
+#if 0
+inline
+double distRGB(uint32_t pix1, uint32_t pix2)
+{
+ const double r_diff = static_cast<int>(getRed (pix1)) - getRed (pix2);
+ const double g_diff = static_cast<int>(getGreen(pix1)) - getGreen(pix2);
+ const double b_diff = static_cast<int>(getBlue (pix1)) - getBlue (pix2);
+
+ //euklidean RGB distance
+ return std::sqrt(square(r_diff) + square(g_diff) + square(b_diff));
+}
+#endif
+
+
+inline
+double distYCbCr(uint32_t pix1, uint32_t pix2, double lumaWeight)
+{
+ //http://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion
+ //YCbCr conversion is a matrix multiplication => take advantage of linearity by subtracting first!
+ const int r_diff = static_cast<int>(getRed (pix1)) - getRed (pix2); //we may delay division by 255 to after matrix multiplication
+ const int g_diff = static_cast<int>(getGreen(pix1)) - getGreen(pix2); //
+ const int b_diff = static_cast<int>(getBlue (pix1)) - getBlue (pix2); //substraction for int is noticeable faster than for double!
+
+ //const double k_b = 0.0722; //ITU-R BT.709 conversion
+ //const double k_r = 0.2126; //
+ const double k_b = 0.0593; //ITU-R BT.2020 conversion
+ const double k_r = 0.2627; //
+ const double k_g = 1 - k_b - k_r;
+
+ const double scale_b = 0.5 / (1 - k_b);
+ const double scale_r = 0.5 / (1 - k_r);
+
+ const double y = k_r * r_diff + k_g * g_diff + k_b * b_diff; //[!], analog YCbCr!
+ const double c_b = scale_b * (b_diff - y);
+ const double c_r = scale_r * (r_diff - y);
+
+ //we skip division by 255 to have similar range like other distance functions
+ return std::sqrt(square(lumaWeight * y) + square(c_b) + square(c_r));
+}
+
+
+inline
+double distYCbCrBuffered(uint32_t pix1, uint32_t pix2)
+{
+ //30% perf boost compared to plain distYCbCr()!
+ //consumes 64 MB memory; using double is only 2% faster, but takes 128 MB
+ static const std::vector<float> diffToDist = []
+ {
+ std::vector<float> tmp;
+
+ for (uint32_t i = 0; i < 256 * 256 * 256; ++i) //startup time: 114 ms on Intel Core i5 (four cores)
+ {
+ const int r_diff = static_cast<signed char>(getByte<2>(i)) * 2;
+ const int g_diff = static_cast<signed char>(getByte<1>(i)) * 2;
+ const int b_diff = static_cast<signed char>(getByte<0>(i)) * 2;
+
+ const double k_b = 0.0593; //ITU-R BT.2020 conversion
+ const double k_r = 0.2627; //
+ const double k_g = 1 - k_b - k_r;
+
+ const double scale_b = 0.5 / (1 - k_b);
+ const double scale_r = 0.5 / (1 - k_r);
+
+ const double y = k_r * r_diff + k_g * g_diff + k_b * b_diff; //[!], analog YCbCr!
+ const double c_b = scale_b * (b_diff - y);
+ const double c_r = scale_r * (r_diff - y);
+
+ tmp.push_back(static_cast<float>(std::sqrt(square(y) + square(c_b) + square(c_r))));
+ }
+ return tmp;
+ }();
+
+ //if (pix1 == pix2) -> 8% perf degradation!
+ // return 0;
+ //if (pix1 < pix2)
+ // std::swap(pix1, pix2); -> 30% perf degradation!!!
+
+ const int r_diff = static_cast<int>(getRed (pix1)) - getRed (pix2);
+ const int g_diff = static_cast<int>(getGreen(pix1)) - getGreen(pix2);
+ const int b_diff = static_cast<int>(getBlue (pix1)) - getBlue (pix2);
+
+ const size_t index = (static_cast<unsigned char>(r_diff / 2) << 16) | //slightly reduce precision (division by 2) to squeeze value into single byte
+ (static_cast<unsigned char>(g_diff / 2) << 8) |
+ (static_cast<unsigned char>(b_diff / 2));
+
+#if 0 //attention: the following calculation creates an asymmetric color distance!!! (e.g. r_diff=46 will be unpacked as 45, but r_diff=-46 unpacks to -47
+ const size_t index = (((r_diff + 0xFF) / 2) << 16) | //slightly reduce precision (division by 2) to squeeze value into single byte
+ (((g_diff + 0xFF) / 2) << 8) |
+ (( b_diff + 0xFF) / 2);
+#endif
+ return diffToDist[index];
+}
+
+
+enum BlendType
+{
+ BLEND_NONE = 0,
+ BLEND_NORMAL, //a normal indication to blend
+ BLEND_DOMINANT, //a strong indication to blend
+ //attention: BlendType must fit into the value range of 2 bit!!!
+};
+
+struct BlendResult
+{
+ BlendType
+ /**/blend_f, blend_g,
+ /**/blend_j, blend_k;
+};
+
+
+struct Kernel_4x4 //kernel for preprocessing step
+{
+ uint32_t
+ /**/a, b, c, d,
+ /**/e, f, g, h,
+ /**/i, j, k, l,
+ /**/m, n, o, p;
+};
+
+/*
+input kernel area naming convention:
+-----------------
+| A | B | C | D |
+----|---|---|---|
+| E | F | G | H | //evaluate the four corners between F, G, J, K
+----|---|---|---| //input pixel is at position F
+| I | J | K | L |
+----|---|---|---|
+| M | N | O | P |
+-----------------
+*/
+template <class ColorDistance>
+FORCE_INLINE //detect blend direction
+BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz::ScalerCfg& cfg) //result: F, G, J, K corners of "GradientType"
+{
+
+ BlendResult result = {};
+
+ if ((ker.f == ker.g &&
+ ker.j == ker.k) ||
+ (ker.f == ker.j &&
+ ker.g == ker.k))
+ return result;
+
+ auto dist = [&](uint32_t pix1, uint32_t pix2) { return ColorDistance::dist(pix1, pix2, cfg.luminanceWeight); };
+
+ double jg = dist(ker.i, ker.f) + dist(ker.f, ker.c) + dist(ker.n, ker.k) + dist(ker.k, ker.h) + cfg.centerDirectionBias * dist(ker.j, ker.g);
+ double fk = dist(ker.e, ker.j) + dist(ker.j, ker.o) + dist(ker.b, ker.g) + dist(ker.g, ker.l) + cfg.centerDirectionBias * dist(ker.f, ker.k);
+
+ if (jg < fk) //test sample: 70% of values max(jg, fk) / min(jg, fk) are between 1.1 and 3.7 with median being 1.8
+ {
+ const bool dominantGradient = cfg.dominantDirectionThreshold * jg < fk;
+ if (ker.f != ker.g && ker.f != ker.j)
+ result.blend_f = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL;
+
+ if (ker.k != ker.j && ker.k != ker.g)
+ result.blend_k = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL;
+ }
+ else if (fk < jg)
+ {
+ const bool dominantGradient = cfg.dominantDirectionThreshold * fk < jg;
+ if (ker.j != ker.f && ker.j != ker.k)
+ result.blend_j = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL;
+
+ if (ker.g != ker.f && ker.g != ker.k)
+ result.blend_g = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL;
+ }
+ return result;
+}
+
+struct Kernel_3x3
+{
+ uint32_t
+ /**/a, b, c,
+ /**/d, e, f,
+ /**/g, h, i;
+};
+
+#define DEF_GETTER(x) template <RotationDegree rotDeg> uint32_t inline get_##x(const Kernel_3x3& ker) { return ker.x; }
+//we cannot and NEED NOT write "ker.##x" since ## concatenates preprocessor tokens but "." is not a token
+DEF_GETTER(a) DEF_GETTER(b) DEF_GETTER(c)
+DEF_GETTER(d) DEF_GETTER(e) DEF_GETTER(f)
+DEF_GETTER(g) DEF_GETTER(h) DEF_GETTER(i)
+#undef DEF_GETTER
+
+#define DEF_GETTER(x, y) template <> inline uint32_t get_##x<ROT_90>(const Kernel_3x3& ker) { return ker.y; }
+DEF_GETTER(a, g) DEF_GETTER(b, d) DEF_GETTER(c, a)
+DEF_GETTER(d, h) DEF_GETTER(e, e) DEF_GETTER(f, b)
+DEF_GETTER(g, i) DEF_GETTER(h, f) DEF_GETTER(i, c)
+#undef DEF_GETTER
+
+#define DEF_GETTER(x, y) template <> inline uint32_t get_##x<ROT_180>(const Kernel_3x3& ker) { return ker.y; }
+DEF_GETTER(a, i) DEF_GETTER(b, h) DEF_GETTER(c, g)
+DEF_GETTER(d, f) DEF_GETTER(e, e) DEF_GETTER(f, d)
+DEF_GETTER(g, c) DEF_GETTER(h, b) DEF_GETTER(i, a)
+#undef DEF_GETTER
+
+#define DEF_GETTER(x, y) template <> inline uint32_t get_##x<ROT_270>(const Kernel_3x3& ker) { return ker.y; }
+DEF_GETTER(a, c) DEF_GETTER(b, f) DEF_GETTER(c, i)
+DEF_GETTER(d, b) DEF_GETTER(e, e) DEF_GETTER(f, h)
+DEF_GETTER(g, a) DEF_GETTER(h, d) DEF_GETTER(i, g)
+#undef DEF_GETTER
+
+
+//compress four blend types into a single byte
+//inline BlendType getTopL (unsigned char b) { return static_cast<BlendType>(0x3 & b); }
+inline BlendType getTopR (unsigned char b) { return static_cast<BlendType>(0x3 & (b >> 2)); }
+inline BlendType getBottomR(unsigned char b) { return static_cast<BlendType>(0x3 & (b >> 4)); }
+inline BlendType getBottomL(unsigned char b) { return static_cast<BlendType>(0x3 & (b >> 6)); }
+
+inline void setTopL (unsigned char& b, BlendType bt) { b |= bt; } //buffer is assumed to be initialized before preprocessing!
+inline void setTopR (unsigned char& b, BlendType bt) { b |= (bt << 2); }
+inline void setBottomR(unsigned char& b, BlendType bt) { b |= (bt << 4); }
+inline void setBottomL(unsigned char& b, BlendType bt) { b |= (bt << 6); }
+
+inline bool blendingNeeded(unsigned char b) { return b != 0; }
+
+template <RotationDegree rotDeg> inline
+unsigned char rotateBlendInfo(unsigned char b) { return b; }
+template <> inline unsigned char rotateBlendInfo<ROT_90 >(unsigned char b) { return ((b << 2) | (b >> 6)) & 0xff; }
+template <> inline unsigned char rotateBlendInfo<ROT_180>(unsigned char b) { return ((b << 4) | (b >> 4)) & 0xff; }
+template <> inline unsigned char rotateBlendInfo<ROT_270>(unsigned char b) { return ((b << 6) | (b >> 2)) & 0xff; }
+
+
+
+
+/*
+input kernel area naming convention:
+-------------
+| A | B | C |
+----|---|---|
+| D | E | F | //input pixel is at position E
+----|---|---|
+| G | H | I |
+-------------
+*/
+template <class Scaler, class ColorDistance, RotationDegree rotDeg>
+FORCE_INLINE //perf: quite worth it!
+void blendPixel(const Kernel_3x3& ker,
+ uint32_t* target, int trgWidth,
+ unsigned char blendInfo, //result of preprocessing all four corners of pixel "e"
+ const xbrz::ScalerCfg& cfg)
+{
+#define a get_a<rotDeg>(ker)
+#define b get_b<rotDeg>(ker)
+#define c get_c<rotDeg>(ker)
+#define d get_d<rotDeg>(ker)
+#define e get_e<rotDeg>(ker)
+#define f get_f<rotDeg>(ker)
+#define g get_g<rotDeg>(ker)
+#define h get_h<rotDeg>(ker)
+#define i get_i<rotDeg>(ker)
+
+
+ (void)a; //silence Clang's -Wunused-function
+
+ const unsigned char blend = rotateBlendInfo<rotDeg>(blendInfo);
+
+ if (getBottomR(blend) >= BLEND_NORMAL)
+ {
+ auto eq = [&](uint32_t pix1, uint32_t pix2) { return ColorDistance::dist(pix1, pix2, cfg.luminanceWeight) < cfg.equalColorTolerance; };
+ auto dist = [&](uint32_t pix1, uint32_t pix2) { return ColorDistance::dist(pix1, pix2, cfg.luminanceWeight); };
+
+ const bool doLineBlend = [&]() -> bool
+ {
+ if (getBottomR(blend) >= BLEND_DOMINANT)
+ return true;
+
+ //make sure there is no second blending in an adjacent rotation for this pixel: handles insular pixels, mario eyes
+ if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90° corners
+ return false;
+ if (getBottomL(blend) != BLEND_NONE && !eq(e, c))
+ return false;
+
+ //no full blending for L-shapes; blend corner only (handles "mario mushroom eyes")
+ if (!eq(e, i) && eq(g, h) && eq(h, i) && eq(i, f) && eq(f, c))
+ return false;
+
+ return true;
+ }();
+
+ const uint32_t px = dist(e, f) <= dist(e, h) ? f : h; //choose most similar color
+
+ OutputMatrix<Scaler::scale, rotDeg> out(target, trgWidth);
+
+ if (doLineBlend)
+ {
+ const double fg = dist(f, g); //test sample: 70% of values max(fg, hc) / min(fg, hc) are between 1.1 and 3.7 with median being 1.9
+ const double hc = dist(h, c); //
+
+ const bool haveShallowLine = cfg.steepDirectionThreshold * fg <= hc && e != g && d != g;
+ const bool haveSteepLine = cfg.steepDirectionThreshold * hc <= fg && e != c && b != c;
+
+ if (haveShallowLine)
+ {
+ if (haveSteepLine)
+ Scaler::blendLineSteepAndShallow(px, out);
+ else
+ Scaler::blendLineShallow(px, out);
+ }
+ else
+ {
+ if (haveSteepLine)
+ Scaler::blendLineSteep(px, out);
+ else
+ Scaler::blendLineDiagonal(px, out);
+ }
+ }
+ else
+ Scaler::blendCorner(px, out);
+ }
+
+#undef a
+#undef b
+#undef c
+#undef d
+#undef e
+#undef f
+#undef g
+#undef h
+#undef i
+}
+
+
+template <class Scaler, class ColorDistance> //scaler policy: see "Scaler2x" reference implementation
+void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const xbrz::ScalerCfg& cfg, int yFirst, int yLast)
+{
+ yFirst = std::max(yFirst, 0);
+ yLast = std::min(yLast, srcHeight);
+ if (yFirst >= yLast || srcWidth <= 0)
+ return;
+
+ const int trgWidth = srcWidth * Scaler::scale;
+
+ //"use" space at the end of the image as temporary buffer for "on the fly preprocessing": we even could use larger area of
+ //"sizeof(uint32_t) * srcWidth * (yLast - yFirst)" bytes without risk of accidental overwriting before accessing
+ const int bufferSize = srcWidth;
+ unsigned char* preProcBuffer = reinterpret_cast<unsigned char*>(trg + yLast * Scaler::scale * trgWidth) - bufferSize;
+ std::fill(preProcBuffer, preProcBuffer + bufferSize, '\0');
+ static_assert(BLEND_NONE == 0, "");
+
+ //initialize preprocessing buffer for first row of current stripe: detect upper left and right corner blending
+ //this cannot be optimized for adjacent processing stripes; we must not allow for a memory race condition!
+ if (yFirst > 0)
+ {
+ const int y = yFirst - 1;
+
+ const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0);
+ const uint32_t* s_0 = src + srcWidth * y; //center line
+ const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1);
+ const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1);
+
+ for (int x = 0; x < srcWidth; ++x)
+ {
+ const int x_m1 = std::max(x - 1, 0);
+ const int x_p1 = std::min(x + 1, srcWidth - 1);
+ const int x_p2 = std::min(x + 2, srcWidth - 1);
+
+ Kernel_4x4 ker = {}; //perf: initialization is negligible
+ ker.a = s_m1[x_m1]; //read sequentially from memory as far as possible
+ ker.b = s_m1[x];
+ ker.c = s_m1[x_p1];
+ ker.d = s_m1[x_p2];
+
+ ker.e = s_0[x_m1];
+ ker.f = s_0[x];
+ ker.g = s_0[x_p1];
+ ker.h = s_0[x_p2];
+
+ ker.i = s_p1[x_m1];
+ ker.j = s_p1[x];
+ ker.k = s_p1[x_p1];
+ ker.l = s_p1[x_p2];
+
+ ker.m = s_p2[x_m1];
+ ker.n = s_p2[x];
+ ker.o = s_p2[x_p1];
+ ker.p = s_p2[x_p2];
+
+ const BlendResult res = preProcessCorners<ColorDistance>(ker, cfg);
+ /*
+ preprocessing blend result:
+ ---------
+ | F | G | //evalute corner between F, G, J, K
+ ----|---| //input pixel is at position F
+ | J | K |
+ ---------
+ */
+ setTopR(preProcBuffer[x], res.blend_j);
+
+ if (x + 1 < bufferSize)
+ setTopL(preProcBuffer[x + 1], res.blend_k);
+ }
+ }
+ //------------------------------------------------------------------------------------
+
+ for (int y = yFirst; y < yLast; ++y)
+ {
+ uint32_t* out = trg + Scaler::scale * y * trgWidth; //consider MT "striped" access
+
+ const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0);
+ const uint32_t* s_0 = src + srcWidth * y; //center line
+ const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1);
+ const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1);
+
+ unsigned char blend_xy1 = 0; //corner blending for current (x, y + 1) position
+
+ for (int x = 0; x < srcWidth; ++x, out += Scaler::scale)
+ {
+ //all those bounds checks have only insignificant impact on performance!
+ const int x_m1 = std::max(x - 1, 0); //perf: prefer array indexing to additional pointers!
+ const int x_p1 = std::min(x + 1, srcWidth - 1);
+ const int x_p2 = std::min(x + 2, srcWidth - 1);
+
+ Kernel_4x4 ker4 = {}; //perf: initialization is negligible
+
+ ker4.a = s_m1[x_m1]; //read sequentially from memory as far as possible
+ ker4.b = s_m1[x];
+ ker4.c = s_m1[x_p1];
+ ker4.d = s_m1[x_p2];
+
+ ker4.e = s_0[x_m1];
+ ker4.f = s_0[x];
+ ker4.g = s_0[x_p1];
+ ker4.h = s_0[x_p2];
+
+ ker4.i = s_p1[x_m1];
+ ker4.j = s_p1[x];
+ ker4.k = s_p1[x_p1];
+ ker4.l = s_p1[x_p2];
+
+ ker4.m = s_p2[x_m1];
+ ker4.n = s_p2[x];
+ ker4.o = s_p2[x_p1];
+ ker4.p = s_p2[x_p2];
+
+ //evaluate the four corners on bottom-right of current pixel
+ unsigned char blend_xy = 0; //for current (x, y) position
+ {
+ const BlendResult res = preProcessCorners<ColorDistance>(ker4, cfg);
+ /*
+ preprocessing blend result:
+ ---------
+ | F | G | //evalute corner between F, G, J, K
+ ----|---| //current input pixel is at position F
+ | J | K |
+ ---------
+ */
+ blend_xy = preProcBuffer[x];
+ setBottomR(blend_xy, res.blend_f); //all four corners of (x, y) have been determined at this point due to processing sequence!
+
+ setTopR(blend_xy1, res.blend_j); //set 2nd known corner for (x, y + 1)
+ preProcBuffer[x] = blend_xy1; //store on current buffer position for use on next row
+
+ blend_xy1 = 0;
+ setTopL(blend_xy1, res.blend_k); //set 1st known corner for (x + 1, y + 1) and buffer for use on next column
+
+ if (x + 1 < bufferSize) //set 3rd known corner for (x + 1, y)
+ setBottomL(preProcBuffer[x + 1], res.blend_g);
+ }
+
+ //fill block of size scale * scale with the given color
+ fillBlock(out, trgWidth * sizeof(uint32_t), ker4.f, Scaler::scale, Scaler::scale);
+ //place *after* preprocessing step, to not overwrite the results while processing the the last pixel!
+
+ //blend four corners of current pixel
+ if (blendingNeeded(blend_xy)) //good 5% perf-improvement
+ {
+ Kernel_3x3 ker3 = {}; //perf: initialization is negligible
+
+ ker3.a = ker4.a;
+ ker3.b = ker4.b;
+ ker3.c = ker4.c;
+
+ ker3.d = ker4.e;
+ ker3.e = ker4.f;
+ ker3.f = ker4.g;
+
+ ker3.g = ker4.i;
+ ker3.h = ker4.j;
+ ker3.i = ker4.k;
+
+ blendPixel<Scaler, ColorDistance, ROT_0 >(ker3, out, trgWidth, blend_xy, cfg);
+ blendPixel<Scaler, ColorDistance, ROT_90 >(ker3, out, trgWidth, blend_xy, cfg);
+ blendPixel<Scaler, ColorDistance, ROT_180>(ker3, out, trgWidth, blend_xy, cfg);
+ blendPixel<Scaler, ColorDistance, ROT_270>(ker3, out, trgWidth, blend_xy, cfg);
+ }
+ }
+ }
+}
+
+//------------------------------------------------------------------------------------
+
+template <class ColorGradient>
+struct Scaler2x : public ColorGradient
+{
+ static const int scale = 2;
+
+ template <unsigned int M, unsigned int N> //bring template function into scope for GCC
+ static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad<M, N>(pixBack, pixFront); }
+
+
+ template <class OutputMatrix>
+ static void blendLineShallow(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
+ alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
+ }
+
+ template <class OutputMatrix>
+ static void blendLineSteep(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
+ alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
+ }
+
+ template <class OutputMatrix>
+ static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<1, 4>(out.template ref<1, 0>(), col);
+ alphaGrad<1, 4>(out.template ref<0, 1>(), col);
+ alphaGrad<5, 6>(out.template ref<1, 1>(), col); //[!] fixes 7/8 used in xBR
+ }
+
+ template <class OutputMatrix>
+ static void blendLineDiagonal(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<1, 2>(out.template ref<1, 1>(), col);
+ }
+
+ template <class OutputMatrix>
+ static void blendCorner(uint32_t col, OutputMatrix& out)
+ {
+ //model a round corner
+ alphaGrad<21, 100>(out.template ref<1, 1>(), col); //exact: 1 - pi/4 = 0.2146018366
+ }
+};
+
+
+template <class ColorGradient>
+struct Scaler3x : public ColorGradient
+{
+ static const int scale = 3;
+
+ template <unsigned int M, unsigned int N> //bring template function into scope for GCC
+ static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad<M, N>(pixBack, pixFront); }
+
+
+ template <class OutputMatrix>
+ static void blendLineShallow(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
+ alphaGrad<1, 4>(out.template ref<scale - 2, 2>(), col);
+
+ alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
+ out.template ref<scale - 1, 2>() = col;
+ }
+
+ template <class OutputMatrix>
+ static void blendLineSteep(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
+ alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col);
+
+ alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
+ out.template ref<2, scale - 1>() = col;
+ }
+
+ template <class OutputMatrix>
+ static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<1, 4>(out.template ref<2, 0>(), col);
+ alphaGrad<1, 4>(out.template ref<0, 2>(), col);
+ alphaGrad<3, 4>(out.template ref<2, 1>(), col);
+ alphaGrad<3, 4>(out.template ref<1, 2>(), col);
+ out.template ref<2, 2>() = col;
+ }
+
+ template <class OutputMatrix>
+ static void blendLineDiagonal(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<1, 8>(out.template ref<1, 2>(), col); //conflict with other rotations for this odd scale
+ alphaGrad<1, 8>(out.template ref<2, 1>(), col);
+ alphaGrad<7, 8>(out.template ref<2, 2>(), col); //
+ }
+
+ template <class OutputMatrix>
+ static void blendCorner(uint32_t col, OutputMatrix& out)
+ {
+ //model a round corner
+ alphaGrad<45, 100>(out.template ref<2, 2>(), col); //exact: 0.4545939598
+ //alphaGrad<7, 256>(out.template ref<2, 1>(), col); //0.02826017254 -> negligible + avoid conflicts with other rotations for this odd scale
+ //alphaGrad<7, 256>(out.template ref<1, 2>(), col); //0.02826017254
+ }
+};
+
+
+template <class ColorGradient>
+struct Scaler4x : public ColorGradient
+{
+ static const int scale = 4;
+
+ template <unsigned int M, unsigned int N> //bring template function into scope for GCC
+ static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad<M, N>(pixBack, pixFront); }
+
+
+ template <class OutputMatrix>
+ static void blendLineShallow(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
+ alphaGrad<1, 4>(out.template ref<scale - 2, 2>(), col);
+
+ alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
+ alphaGrad<3, 4>(out.template ref<scale - 2, 3>(), col);
+
+ out.template ref<scale - 1, 2>() = col;
+ out.template ref<scale - 1, 3>() = col;
+ }
+
+ template <class OutputMatrix>
+ static void blendLineSteep(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
+ alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col);
+
+ alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
+ alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col);
+
+ out.template ref<2, scale - 1>() = col;
+ out.template ref<3, scale - 1>() = col;
+ }
+
+ template <class OutputMatrix>
+ static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<3, 4>(out.template ref<3, 1>(), col);
+ alphaGrad<3, 4>(out.template ref<1, 3>(), col);
+ alphaGrad<1, 4>(out.template ref<3, 0>(), col);
+ alphaGrad<1, 4>(out.template ref<0, 3>(), col);
+
+ alphaGrad<1, 3>(out.template ref<2, 2>(), col); //[!] fixes 1/4 used in xBR
+
+ out.template ref<3, 3>() = col;
+ out.template ref<3, 2>() = col;
+ out.template ref<2, 3>() = col;
+ }
+
+ template <class OutputMatrix>
+ static void blendLineDiagonal(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<1, 2>(out.template ref<scale - 1, scale / 2 >(), col);
+ alphaGrad<1, 2>(out.template ref<scale - 2, scale / 2 + 1>(), col);
+ out.template ref<scale - 1, scale - 1>() = col;
+ }
+
+ template <class OutputMatrix>
+ static void blendCorner(uint32_t col, OutputMatrix& out)
+ {
+ //model a round corner
+ alphaGrad<68, 100>(out.template ref<3, 3>(), col); //exact: 0.6848532563
+ alphaGrad< 9, 100>(out.template ref<3, 2>(), col); //0.08677704501
+ alphaGrad< 9, 100>(out.template ref<2, 3>(), col); //0.08677704501
+ }
+};
+
+
+template <class ColorGradient>
+struct Scaler5x : public ColorGradient
+{
+ static const int scale = 5;
+
+ template <unsigned int M, unsigned int N> //bring template function into scope for GCC
+ static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad<M, N>(pixBack, pixFront); }
+
+
+ template <class OutputMatrix>
+ static void blendLineShallow(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
+ alphaGrad<1, 4>(out.template ref<scale - 2, 2>(), col);
+ alphaGrad<1, 4>(out.template ref<scale - 3, 4>(), col);
+
+ alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
+ alphaGrad<3, 4>(out.template ref<scale - 2, 3>(), col);
+
+ out.template ref<scale - 1, 2>() = col;
+ out.template ref<scale - 1, 3>() = col;
+ out.template ref<scale - 1, 4>() = col;
+ out.template ref<scale - 2, 4>() = col;
+ }
+
+ template <class OutputMatrix>
+ static void blendLineSteep(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
+ alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col);
+ alphaGrad<1, 4>(out.template ref<4, scale - 3>(), col);
+
+ alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
+ alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col);
+
+ out.template ref<2, scale - 1>() = col;
+ out.template ref<3, scale - 1>() = col;
+ out.template ref<4, scale - 1>() = col;
+ out.template ref<4, scale - 2>() = col;
+ }
+
+ template <class OutputMatrix>
+ static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
+ alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col);
+ alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
+
+ alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
+ alphaGrad<1, 4>(out.template ref<scale - 2, 2>(), col);
+ alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
+
+ alphaGrad<2, 3>(out.template ref<3, 3>(), col);
+
+ out.template ref<2, scale - 1>() = col;
+ out.template ref<3, scale - 1>() = col;
+ out.template ref<4, scale - 1>() = col;
+
+ out.template ref<scale - 1, 2>() = col;
+ out.template ref<scale - 1, 3>() = col;
+ }
+
+ template <class OutputMatrix>
+ static void blendLineDiagonal(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<1, 8>(out.template ref<scale - 1, scale / 2 >(), col); //conflict with other rotations for this odd scale
+ alphaGrad<1, 8>(out.template ref<scale - 2, scale / 2 + 1>(), col);
+ alphaGrad<1, 8>(out.template ref<scale - 3, scale / 2 + 2>(), col); //
+
+ alphaGrad<7, 8>(out.template ref<4, 3>(), col);
+ alphaGrad<7, 8>(out.template ref<3, 4>(), col);
+
+ out.template ref<4, 4>() = col;
+ }
+
+ template <class OutputMatrix>
+ static void blendCorner(uint32_t col, OutputMatrix& out)
+ {
+ //model a round corner
+ alphaGrad<86, 100>(out.template ref<4, 4>(), col); //exact: 0.8631434088
+ alphaGrad<23, 100>(out.template ref<4, 3>(), col); //0.2306749731
+ alphaGrad<23, 100>(out.template ref<3, 4>(), col); //0.2306749731
+ //alphaGrad<1, 64>(out.template ref<4, 2>(), col); //0.01676812367 -> negligible + avoid conflicts with other rotations for this odd scale
+ //alphaGrad<1, 64>(out.template ref<2, 4>(), col); //0.01676812367
+ }
+};
+
+
+template <class ColorGradient>
+struct Scaler6x : public ColorGradient
+{
+ static const int scale = 6;
+
+ template <unsigned int M, unsigned int N> //bring template function into scope for GCC
+ static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad<M, N>(pixBack, pixFront); }
+
+
+ template <class OutputMatrix>
+ static void blendLineShallow(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
+ alphaGrad<1, 4>(out.template ref<scale - 2, 2>(), col);
+ alphaGrad<1, 4>(out.template ref<scale - 3, 4>(), col);
+
+ alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
+ alphaGrad<3, 4>(out.template ref<scale - 2, 3>(), col);
+ alphaGrad<3, 4>(out.template ref<scale - 3, 5>(), col);
+
+ out.template ref<scale - 1, 2>() = col;
+ out.template ref<scale - 1, 3>() = col;
+ out.template ref<scale - 1, 4>() = col;
+ out.template ref<scale - 1, 5>() = col;
+
+ out.template ref<scale - 2, 4>() = col;
+ out.template ref<scale - 2, 5>() = col;
+ }
+
+ template <class OutputMatrix>
+ static void blendLineSteep(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
+ alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col);
+ alphaGrad<1, 4>(out.template ref<4, scale - 3>(), col);
+
+ alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
+ alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col);
+ alphaGrad<3, 4>(out.template ref<5, scale - 3>(), col);
+
+ out.template ref<2, scale - 1>() = col;
+ out.template ref<3, scale - 1>() = col;
+ out.template ref<4, scale - 1>() = col;
+ out.template ref<5, scale - 1>() = col;
+
+ out.template ref<4, scale - 2>() = col;
+ out.template ref<5, scale - 2>() = col;
+ }
+
+ template <class OutputMatrix>
+ static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col);
+ alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col);
+ alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col);
+ alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col);
+
+ alphaGrad<1, 4>(out.template ref<scale - 1, 0>(), col);
+ alphaGrad<1, 4>(out.template ref<scale - 2, 2>(), col);
+ alphaGrad<3, 4>(out.template ref<scale - 1, 1>(), col);
+ alphaGrad<3, 4>(out.template ref<scale - 2, 3>(), col);
+
+ out.template ref<2, scale - 1>() = col;
+ out.template ref<3, scale - 1>() = col;
+ out.template ref<4, scale - 1>() = col;
+ out.template ref<5, scale - 1>() = col;
+
+ out.template ref<4, scale - 2>() = col;
+ out.template ref<5, scale - 2>() = col;
+
+ out.template ref<scale - 1, 2>() = col;
+ out.template ref<scale - 1, 3>() = col;
+ }
+
+ template <class OutputMatrix>
+ static void blendLineDiagonal(uint32_t col, OutputMatrix& out)
+ {
+ alphaGrad<1, 2>(out.template ref<scale - 1, scale / 2 >(), col);
+ alphaGrad<1, 2>(out.template ref<scale - 2, scale / 2 + 1>(), col);
+ alphaGrad<1, 2>(out.template ref<scale - 3, scale / 2 + 2>(), col);
+
+ out.template ref<scale - 2, scale - 1>() = col;
+ out.template ref<scale - 1, scale - 1>() = col;
+ out.template ref<scale - 1, scale - 2>() = col;
+ }
+
+ template <class OutputMatrix>
+ static void blendCorner(uint32_t col, OutputMatrix& out)
+ {
+ //model a round corner
+ alphaGrad<97, 100>(out.template ref<5, 5>(), col); //exact: 0.9711013910
+ alphaGrad<42, 100>(out.template ref<4, 5>(), col); //0.4236372243
+ alphaGrad<42, 100>(out.template ref<5, 4>(), col); //0.4236372243
+ alphaGrad< 6, 100>(out.template ref<5, 3>(), col); //0.05652034508
+ alphaGrad< 6, 100>(out.template ref<3, 5>(), col); //0.05652034508
+ }
+};
+
+//------------------------------------------------------------------------------------
+
+struct ColorDistanceRGB
+{
+ static double dist(uint32_t pix1, uint32_t pix2, double luminanceWeight)
+ {
+ return distYCbCrBuffered(pix1, pix2);
+
+ //if (pix1 == pix2) //about 4% perf boost
+ // return 0;
+ //return distYCbCr(pix1, pix2, luminanceWeight);
+ }
+};
+
+struct ColorDistanceARGB
+{
+ static double dist(uint32_t pix1, uint32_t pix2, double luminanceWeight)
+ {
+ const double a1 = getAlpha(pix1) / 255.0 ;
+ const double a2 = getAlpha(pix2) / 255.0 ;
+ /*
+ Requirements for a color distance handling alpha channel: with a1, a2 in [0, 1]
+
+ 1. if a1 = a2, distance should be: a1 * distYCbCr()
+ 2. if a1 = 0, distance should be: a2 * distYCbCr(black, white) = a2 * 255
+ 3. if a1 = 1, ??? maybe: 255 * (1 - a2) + a2 * distYCbCr()
+ */
+
+ //return std::min(a1, a2) * distYCbCrBuffered(pix1, pix2) + 255 * abs(a1 - a2);
+ //=> following code is 15% faster:
+ const double d = distYCbCrBuffered(pix1, pix2);
+ if (a1 < a2)
+ return a1 * d + 255 * (a2 - a1);
+ else
+ return a2 * d + 255 * (a1 - a2);
+
+ //alternative? return std::sqrt(a1 * a2 * square(distYCbCrBuffered(pix1, pix2)) + square(255 * (a1 - a2)));
+ }
+};
+
+
+struct ColorDistanceUnbufferedARGB
+{
+ static double dist(uint32_t pix1, uint32_t pix2, double luminanceWeight)
+ {
+ const double a1 = getAlpha(pix1) / 255.0 ;
+ const double a2 = getAlpha(pix2) / 255.0 ;
+
+ const double d = distYCbCr(pix1, pix2, luminanceWeight);
+ if (a1 < a2)
+ return a1 * d + 255 * (a2 - a1);
+ else
+ return a2 * d + 255 * (a1 - a2);
+ }
+};
+
+
+struct ColorGradientRGB
+{
+ template <unsigned int M, unsigned int N>
+ static void alphaGrad(uint32_t& pixBack, uint32_t pixFront)
+ {
+ pixBack = gradientRGB<M, N>(pixFront, pixBack);
+ }
+};
+
+struct ColorGradientARGB
+{
+ template <unsigned int M, unsigned int N>
+ static void alphaGrad(uint32_t& pixBack, uint32_t pixFront)
+ {
+ pixBack = gradientARGB<M, N>(pixFront, pixBack);
+ }
+};
+}
+
+
+void xbrz::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, ColorFormat colFmt, const xbrz::ScalerCfg& cfg, int yFirst, int yLast)
+{
+if (factor == 1)
+ {
+ std::copy(src + yFirst * srcWidth, src + yLast * srcWidth, trg);
+ return;
+ }
+
+ static_assert(SCALE_FACTOR_MAX == 6, "");
+ switch (colFmt)
+ {
+ case ColorFormat::RGB:
+ switch (factor)
+ {
+ case 2:
+ return scaleImage<Scaler2x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
+ case 3:
+ return scaleImage<Scaler3x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
+ case 4:
+ return scaleImage<Scaler4x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
+ case 5:
+ return scaleImage<Scaler5x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
+ case 6:
+ return scaleImage<Scaler6x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
+ }
+ break;
+
+ case ColorFormat::ARGB:
+ switch (factor)
+ {
+ case 2:
+ return scaleImage<Scaler2x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
+ case 3:
+ return scaleImage<Scaler3x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
+ case 4:
+ return scaleImage<Scaler4x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
+ case 5:
+ return scaleImage<Scaler5x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
+ case 6:
+ return scaleImage<Scaler6x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
+ }
+ break;
+
+ case ColorFormat::ARGB_UNBUFFERED:
+ switch (factor)
+ {
+ case 2:
+ return scaleImage<Scaler2x<ColorGradientARGB>, ColorDistanceUnbufferedARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
+ case 3:
+ return scaleImage<Scaler3x<ColorGradientARGB>, ColorDistanceUnbufferedARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
+ case 4:
+ return scaleImage<Scaler4x<ColorGradientARGB>, ColorDistanceUnbufferedARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
+ case 5:
+ return scaleImage<Scaler5x<ColorGradientARGB>, ColorDistanceUnbufferedARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
+ case 6:
+ return scaleImage<Scaler6x<ColorGradientARGB>, ColorDistanceUnbufferedARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
+ }
+ break;
+ }
+ assert(false);
+}
+
+
+bool xbrz::equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, double luminanceWeight, double equalColorTolerance)
+{
+ switch (colFmt)
+ {
+ case ColorFormat::RGB:
+ return ColorDistanceRGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
+ case ColorFormat::ARGB:
+ return ColorDistanceARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
+ case ColorFormat::ARGB_UNBUFFERED:
+ return ColorDistanceUnbufferedARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
+ }
+ assert(false);
+ return false;
+}
+
+
+void xbrz::bilinearScale(const uint32_t* src, int srcWidth, int srcHeight,
+ /**/ uint32_t* trg, int trgWidth, int trgHeight)
+{
+ bilinearScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
+ trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
+ 0, trgHeight, [](uint32_t pix) { return pix; });
+}
+
+
+void xbrz::nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight,
+ /**/ uint32_t* trg, int trgWidth, int trgHeight)
+{
+ nearestNeighborScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
+ trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
+ 0, trgHeight, [](uint32_t pix) { return pix; });
+}
+
+
+#if 0
+//#include <ppl.h>
+void bilinearScaleCpu(const uint32_t* src, int srcWidth, int srcHeight,
+ /**/ uint32_t* trg, int trgWidth, int trgHeight)
+{
+ const int TASK_GRANULARITY = 16;
+
+ concurrency::task_group tg;
+
+ for (int i = 0; i < trgHeight; i += TASK_GRANULARITY)
+ tg.run([=]
+ {
+ const int iLast = std::min(i + TASK_GRANULARITY, trgHeight);
+ xbrz::bilinearScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
+ trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
+ i, iLast, [](uint32_t pix) { return pix; });
+ });
+ tg.wait();
+}
+
+
+//Perf: AMP vs CPU: merely ~10% shorter runtime (scaling 1280x800 -> 1920x1080)
+//#include <amp.h>
+void bilinearScaleAmp(const uint32_t* src, int srcWidth, int srcHeight, //throw concurrency::runtime_exception
+ /**/ uint32_t* trg, int trgWidth, int trgHeight)
+{
+ //C++ AMP reference: https://msdn.microsoft.com/en-us/library/hh289390.aspx
+ //introduction to C++ AMP: https://msdn.microsoft.com/en-us/magazine/hh882446.aspx
+ using namespace concurrency;
+ //TODO: pitch
+
+ if (srcHeight <= 0 || srcWidth <= 0) return;
+
+ const float scaleX = static_cast<float>(trgWidth ) / srcWidth;
+ const float scaleY = static_cast<float>(trgHeight) / srcHeight;
+
+ array_view<const uint32_t, 2> srcView(srcHeight, srcWidth, src);
+ array_view< uint32_t, 2> trgView(trgHeight, trgWidth, trg);
+ trgView.discard_data();
+
+ parallel_for_each(trgView.extent, [=](index<2> idx) restrict(amp) //throw ?
+ {
+ const int y = idx[0];
+ const int x = idx[1];
+ //Perf notes:
+ // -> float-based calculation is (almost) 2x as fas as double!
+ // -> no noticeable improvement via tiling: https://msdn.microsoft.com/en-us/magazine/hh882447.aspx
+ // -> no noticeable improvement with restrict(amp,cpu)
+ // -> iterating over y-axis only is significantly slower!
+ // -> pre-calculating x,y-dependent variables in a buffer + array_view<> is ~ 20 % slower!
+ const int y1 = srcHeight * y / trgHeight;
+ int y2 = y1 + 1;
+ if (y2 == srcHeight) --y2;
+
+ const float yy1 = y / scaleY - y1;
+ const float y2y = 1 - yy1;
+ //-------------------------------------
+ const int x1 = srcWidth * x / trgWidth;
+ int x2 = x1 + 1;
+ if (x2 == srcWidth) --x2;
+
+ const float xx1 = x / scaleX - x1;
+ const float x2x = 1 - xx1;
+ //-------------------------------------
+ const float x2xy2y = x2x * y2y;
+ const float xx1y2y = xx1 * y2y;
+ const float x2xyy1 = x2x * yy1;
+ const float xx1yy1 = xx1 * yy1;
+
+ auto interpolate = [=](int offset)
+ {
+ /*
+ https://en.wikipedia.org/wiki/Bilinear_interpolation
+ (c11(x2 - x) + c21(x - x1)) * (y2 - y ) +
+ (c12(x2 - x) + c22(x - x1)) * (y - y1)
+ */
+ const auto c11 = (srcView(y1, x1) >> (8 * offset)) & 0xff;
+ const auto c21 = (srcView(y1, x2) >> (8 * offset)) & 0xff;
+ const auto c12 = (srcView(y2, x1) >> (8 * offset)) & 0xff;
+ const auto c22 = (srcView(y2, x2) >> (8 * offset)) & 0xff;
+
+ return c11 * x2xy2y + c21 * xx1y2y +
+ c12 * x2xyy1 + c22 * xx1yy1;
+ };
+
+ const float bi = interpolate(0);
+ const float gi = interpolate(1);
+ const float ri = interpolate(2);
+ const float ai = interpolate(3);
+
+ const auto b = static_cast<uint32_t>(bi + 0.5f);
+ const auto g = static_cast<uint32_t>(gi + 0.5f);
+ const auto r = static_cast<uint32_t>(ri + 0.5f);
+ const auto a = static_cast<uint32_t>(ai + 0.5f);
+
+ trgView(y, x) = (a << 24) | (r << 16) | (g << 8) | b;
+ });
+ trgView.synchronize(); //throw ?
+}
+#endif
diff --git a/xBRZ/src/xbrz.h b/xBRZ/src/xbrz.h
new file mode 100755
index 00000000..f7f7169a
--- /dev/null
+++ b/xBRZ/src/xbrz.h
@@ -0,0 +1,80 @@
+// ****************************************************************************
+// * This file is part of the xBRZ project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// * *
+// * Additionally and as a special exception, the author gives permission *
+// * to link the code of this program with the following libraries *
+// * (or with modified versions that use the same licenses), and distribute *
+// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
+// * You must obey the GNU General Public License in all respects for all of *
+// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
+// * If you modify this file, you may extend this exception to your version *
+// * of the file, but you are not obligated to do so. If you do not wish to *
+// * do so, delete this exception statement from your version. *
+// ****************************************************************************
+
+#ifndef XBRZ_HEADER_3847894708239054
+#define XBRZ_HEADER_3847894708239054
+
+#include <cstddef> //size_t
+#include <cstdint> //uint32_t
+#include <limits>
+#include "xbrz_config.h"
+
+
+namespace xbrz
+{
+/*
+-------------------------------------------------------------------------
+| xBRZ: "Scale by rules" - high quality image upscaling filter by Zenju |
+-------------------------------------------------------------------------
+using a modified approach of xBR:
+http://board.byuu.org/viewtopic.php?f=10&t=2248
+- new rule set preserving small image features
+- highly optimized for performance
+- support alpha channel
+- support multithreading
+- support 64-bit architectures
+- support processing image slices
+- support scaling up to 6xBRZ
+*/
+
+enum class ColorFormat //from high bits -> low bits, 8 bit per channel
+{
+ RGB, //8 bit for each red, green, blue, upper 8 bits unused
+ ARGB, //including alpha channel, BGRA byte order on little-endian machines
+ ARGB_UNBUFFERED, //like ARGB, but without the one-time buffer creation overhead (ca. 100 - 300 ms) at the expense of a slightly slower scaling time
+};
+
+const int SCALE_FACTOR_MAX = 6;
+
+/*
+-> map source (srcWidth * srcHeight) to target (scale * width x scale * height) image, optionally processing a half-open slice of rows [yFirst, yLast) only
+-> support for source/target pitch in bytes!
+-> if your emulator changes only a few image slices during each cycle (e.g. DOSBox) then there's no need to run xBRZ on the complete image:
+ Just make sure you enlarge the source image slice by 2 rows on top and 2 on bottom (this is the additional range the xBRZ algorithm is using during analysis)
+ CAVEAT: If there are multiple changed slices, make sure they do not overlap after adding these additional rows in order to avoid a memory race condition
+ in the target image data if you are using multiple threads for processing each enlarged slice!
+
+THREAD-SAFETY: - parts of the same image may be scaled by multiple threads as long as the [yFirst, yLast) ranges do not overlap!
+ - there is a minor inefficiency for the first row of a slice, so avoid processing single rows only; suggestion: process at least 8-16 rows
+*/
+void scale(size_t factor, //valid range: 2 - SCALE_FACTOR_MAX
+ const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight,
+ ColorFormat colFmt,
+ const ScalerCfg& cfg = ScalerCfg(),
+ int yFirst = 0, int yLast = std::numeric_limits<int>::max()); //slice of source image
+
+void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight,
+ /**/ uint32_t* trg, int trgWidth, int trgHeight);
+
+void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight,
+ /**/ uint32_t* trg, int trgWidth, int trgHeight);
+
+
+//parameter tuning
+bool equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, double luminanceWeight, double equalColorTolerance);
+}
+
+#endif
diff --git a/xBRZ/src/xbrz_config.h b/xBRZ/src/xbrz_config.h
new file mode 100755
index 00000000..fc036d17
--- /dev/null
+++ b/xBRZ/src/xbrz_config.h
@@ -0,0 +1,35 @@
+// ****************************************************************************
+// * This file is part of the xBRZ project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// * *
+// * Additionally and as a special exception, the author gives permission *
+// * to link the code of this program with the following libraries *
+// * (or with modified versions that use the same licenses), and distribute *
+// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
+// * You must obey the GNU General Public License in all respects for all of *
+// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
+// * If you modify this file, you may extend this exception to your version *
+// * of the file, but you are not obligated to do so. If you do not wish to *
+// * do so, delete this exception statement from your version. *
+// ****************************************************************************
+
+#ifndef XBRZ_CONFIG_HEADER_284578425345
+#define XBRZ_CONFIG_HEADER_284578425345
+
+//do NOT include any headers here! used by xBRZ_dll!!!
+
+namespace xbrz
+{
+struct ScalerCfg
+{
+ double luminanceWeight = 1;
+ double equalColorTolerance = 30;
+ double centerDirectionBias = 4;
+ double dominantDirectionThreshold = 3.6;
+ double steepDirectionThreshold = 2.2;
+ double newTestAttribute = 0; //unused; test new parameters
+};
+}
+
+#endif
diff --git a/xBRZ/src/xbrz_tools.h b/xBRZ/src/xbrz_tools.h
new file mode 100755
index 00000000..aaa2b769
--- /dev/null
+++ b/xBRZ/src/xbrz_tools.h
@@ -0,0 +1,268 @@
+// ****************************************************************************
+// * This file is part of the xBRZ project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// * *
+// * Additionally and as a special exception, the author gives permission *
+// * to link the code of this program with the following libraries *
+// * (or with modified versions that use the same licenses), and distribute *
+// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
+// * You must obey the GNU General Public License in all respects for all of *
+// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
+// * If you modify this file, you may extend this exception to your version *
+// * of the file, but you are not obligated to do so. If you do not wish to *
+// * do so, delete this exception statement from your version. *
+// ****************************************************************************
+
+#ifndef XBRZ_TOOLS_H_825480175091875
+#define XBRZ_TOOLS_H_825480175091875
+
+#include <cassert>
+#include <algorithm>
+#include <type_traits>
+
+
+namespace xbrz
+{
+template <uint32_t N> inline
+unsigned char getByte(uint32_t val) { return static_cast<unsigned char>((val >> (8 * N)) & 0xff); }
+
+inline unsigned char getAlpha(uint32_t pix) { return getByte<3>(pix); }
+inline unsigned char getRed (uint32_t pix) { return getByte<2>(pix); }
+inline unsigned char getGreen(uint32_t pix) { return getByte<1>(pix); }
+inline unsigned char getBlue (uint32_t pix) { return getByte<0>(pix); }
+
+inline uint32_t makePixel(unsigned char a, unsigned char r, unsigned char g, unsigned char b) { return (a << 24) | (r << 16) | (g << 8) | b; }
+inline uint32_t makePixel( unsigned char r, unsigned char g, unsigned char b) { return (r << 16) | (g << 8) | b; }
+
+inline uint32_t rgb555to888(uint16_t pix) { return ((pix & 0x7C00) << 9) | ((pix & 0x03E0) << 6) | ((pix & 0x001F) << 3); }
+inline uint32_t rgb565to888(uint16_t pix) { return ((pix & 0xF800) << 8) | ((pix & 0x07E0) << 5) | ((pix & 0x001F) << 3); }
+
+inline uint16_t rgb888to555(uint32_t pix) { return static_cast<uint16_t>(((pix & 0xF80000) >> 9) | ((pix & 0x00F800) >> 6) | ((pix & 0x0000F8) >> 3)); }
+inline uint16_t rgb888to565(uint32_t pix) { return static_cast<uint16_t>(((pix & 0xF80000) >> 8) | ((pix & 0x00FC00) >> 5) | ((pix & 0x0000F8) >> 3)); }
+
+
+template <class Pix> inline
+Pix* byteAdvance(Pix* ptr, int bytes)
+{
+ using PixNonConst = typename std::remove_cv<Pix>::type;
+ using PixByte = typename std::conditional<std::is_same<Pix, PixNonConst>::value, char, const char>::type;
+
+ static_assert(std::is_integral<PixNonConst>::value, "Pix* is expected to be cast-able to char*");
+
+ return reinterpret_cast<Pix*>(reinterpret_cast<PixByte*>(ptr) + bytes);
+}
+
+
+//fill block with the given color
+template <class Pix> inline
+void fillBlock(Pix* trg, int pitch, Pix col, int blockWidth, int blockHeight)
+{
+ //for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch))
+ // std::fill(trg, trg + blockWidth, col);
+
+ for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch))
+ for (int x = 0; x < blockWidth; ++x)
+ trg[x] = col;
+}
+
+
+//nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!)
+template <class PixSrc, class PixTrg, class PixConverter>
+void nearestNeighborScale(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch,
+ /**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch,
+ int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/)
+{
+ static_assert(std::is_integral<PixSrc>::value, "PixSrc* is expected to be cast-able to char*");
+ static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
+
+ static_assert(std::is_same<decltype(pixCvrt(PixSrc())), PixTrg>::value, "PixConverter returning wrong pixel format");
+
+ if (srcPitch < srcWidth * static_cast<int>(sizeof(PixSrc)) ||
+ trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg)))
+ {
+ assert(false);
+ return;
+ }
+
+ yFirst = std::max(yFirst, 0);
+ yLast = std::min(yLast, trgHeight);
+ if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return;
+
+ for (int y = yFirst; y < yLast; ++y)
+ {
+ const int ySrc = srcHeight * y / trgHeight;
+ const PixSrc* const srcLine = byteAdvance(src, ySrc * srcPitch);
+ PixTrg* const trgLine = byteAdvance(trg, y * trgPitch);
+
+ for (int x = 0; x < trgWidth; ++x)
+ {
+ const int xSrc = srcWidth * x / trgWidth;
+ trgLine[x] = pixCvrt(srcLine[xSrc]);
+ }
+ }
+}
+
+
+//nearest-neighbor (going over source image - fast for upscaling, since source is read only once
+template <class PixSrc, class PixTrg, class PixConverter>
+void nearestNeighborScaleOverSource(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch,
+ /**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch,
+ int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/)
+{
+ static_assert(std::is_integral<PixSrc>::value, "PixSrc* is expected to be cast-able to char*");
+ static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
+
+ static_assert(std::is_same<decltype(pixCvrt(PixSrc())), PixTrg>::value, "PixConverter returning wrong pixel format");
+
+ if (srcPitch < srcWidth * static_cast<int>(sizeof(PixSrc)) ||
+ trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg)))
+ {
+ assert(false);
+ return;
+ }
+
+ yFirst = std::max(yFirst, 0);
+ yLast = std::min(yLast, srcHeight);
+ if (yFirst >= yLast || trgWidth <= 0 || trgHeight <= 0) return;
+
+ for (int y = yFirst; y < yLast; ++y)
+ {
+ //mathematically: ySrc = floor(srcHeight * yTrg / trgHeight)
+ // => search for integers in: [ySrc, ySrc + 1) * trgHeight / srcHeight
+
+ //keep within for loop to support MT input slices!
+ const int yTrgFirst = ( y * trgHeight + srcHeight - 1) / srcHeight; //=ceil(y * trgHeight / srcHeight)
+ const int yTrgLast = ((y + 1) * trgHeight + srcHeight - 1) / srcHeight; //=ceil(((y + 1) * trgHeight) / srcHeight)
+ const int blockHeight = yTrgLast - yTrgFirst;
+
+ if (blockHeight > 0)
+ {
+ const PixSrc* srcLine = byteAdvance(src, y * srcPitch);
+ /**/ PixTrg* trgLine = byteAdvance(trg, yTrgFirst * trgPitch);
+ int xTrgFirst = 0;
+
+ for (int x = 0; x < srcWidth; ++x)
+ {
+ const int xTrgLast = ((x + 1) * trgWidth + srcWidth - 1) / srcWidth;
+ const int blockWidth = xTrgLast - xTrgFirst;
+ if (blockWidth > 0)
+ {
+ xTrgFirst = xTrgLast;
+
+ const auto trgPix = pixCvrt(srcLine[x]);
+ fillBlock(trgLine, trgPitch, trgPix, blockWidth, blockHeight);
+ trgLine += blockWidth;
+ }
+ }
+ }
+ }
+}
+
+
+template <class PixTrg, class PixConverter>
+void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch,
+ /**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch,
+ int yFirst, int yLast, PixConverter pixCvrt /*convert uint32_t to PixTrg*/)
+{
+ static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
+ static_assert(std::is_same<decltype(pixCvrt(uint32_t())), PixTrg>::value, "PixConverter returning wrong pixel format");
+
+ if (srcPitch < srcWidth * static_cast<int>(sizeof(uint32_t)) ||
+ trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg)))
+ {
+ assert(false);
+ return;
+ }
+
+ yFirst = std::max(yFirst, 0);
+ yLast = std::min(yLast, trgHeight);
+ if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return;
+
+ const double scaleX = static_cast<double>(trgWidth ) / srcWidth;
+ const double scaleY = static_cast<double>(trgHeight) / srcHeight;
+
+ //perf notes:
+ // -> double-based calculation is (slightly) faster than float
+ // -> precalculation gives significant boost; std::vector<> memory allocation is negligible!
+ struct CoeffsX
+ {
+ int x1 = 0;
+ int x2 = 0;
+ double xx1 = 0;
+ double x2x = 0;
+ };
+ std::vector<CoeffsX> buf(trgWidth);
+ for (int x = 0; x < trgWidth; ++x)
+ {
+ const int x1 = srcWidth * x / trgWidth;
+ int x2 = x1 + 1;
+ if (x2 == srcWidth) --x2;
+
+ const double xx1 = x / scaleX - x1;
+ const double x2x = 1 - xx1;
+
+ buf[x] = { x1, x2, xx1, x2x };
+ }
+
+ for (int y = yFirst; y < yLast; ++y)
+ {
+ const int y1 = srcHeight * y / trgHeight;
+ int y2 = y1 + 1;
+ if (y2 == srcHeight) --y2;
+
+ const double yy1 = y / scaleY - y1;
+ const double y2y = 1 - yy1;
+
+ const uint32_t* const srcLine = byteAdvance(src, y1 * srcPitch);
+ const uint32_t* const srcLineNext = byteAdvance(src, y2 * srcPitch);
+ PixTrg* const trgLine = byteAdvance(trg, y * trgPitch);
+
+ for (int x = 0; x < trgWidth; ++x)
+ {
+ //perf: do NOT "simplify" the variable layout without measurement!
+ const int x1 = buf[x].x1;
+ const int x2 = buf[x].x2;
+ const double xx1 = buf[x].xx1;
+ const double x2x = buf[x].x2x;
+
+ const double x2xy2y = x2x * y2y;
+ const double xx1y2y = xx1 * y2y;
+ const double x2xyy1 = x2x * yy1;
+ const double xx1yy1 = xx1 * yy1;
+
+ auto interpolate = [=](int offset)
+ {
+ /*
+ https://en.wikipedia.org/wiki/Bilinear_interpolation
+ (c11(x2 - x) + c21(x - x1)) * (y2 - y ) +
+ (c12(x2 - x) + c22(x - x1)) * (y - y1)
+ */
+ const auto c11 = (srcLine [x1] >> (8 * offset)) & 0xff;
+ const auto c21 = (srcLine [x2] >> (8 * offset)) & 0xff;
+ const auto c12 = (srcLineNext[x1] >> (8 * offset)) & 0xff;
+ const auto c22 = (srcLineNext[x2] >> (8 * offset)) & 0xff;
+
+ return c11 * x2xy2y + c21 * xx1y2y +
+ c12 * x2xyy1 + c22 * xx1yy1;
+ };
+
+ const double bi = interpolate(0);
+ const double gi = interpolate(1);
+ const double ri = interpolate(2);
+ const double ai = interpolate(3);
+
+ const auto b = static_cast<uint32_t>(bi + 0.5);
+ const auto g = static_cast<uint32_t>(gi + 0.5);
+ const auto r = static_cast<uint32_t>(ri + 0.5);
+ const auto a = static_cast<uint32_t>(ai + 0.5);
+
+ const uint32_t trgPix = (a << 24) | (r << 16) | (g << 8) | b;
+
+ trgLine[x] = pixCvrt(trgPix);
+ }
+ }
+}
+}
+
+#endif //XBRZ_TOOLS_H_825480175091875
diff --git a/zen/deprecate.h b/zen/deprecate.h
index 2a6bcb0d..1f4e6ab4 100755
--- a/zen/deprecate.h
+++ b/zen/deprecate.h
@@ -8,7 +8,11 @@
#define DEPRECATE_H_234897087787348
//compiler macros: http://predef.sourceforge.net/precomp.html
+#ifdef __GNUC__
#define ZEN_DEPRECATE __attribute__ ((deprecated))
+#else
+ #error add your platform here!
+#endif
#endif //DEPRECATE_H_234897087787348
diff --git a/zen/file_access.cpp b/zen/file_access.cpp
index 1711e934..18c0ed26 100755
--- a/zen/file_access.cpp
+++ b/zen/file_access.cpp
@@ -32,13 +32,45 @@ using namespace zen;
Opt<PathComponents> zen::parsePathComponents(const Zstring& itemPath)
{
+ auto doParse = [&](int sepCountVolumeRoot, bool rootWithSep) -> Opt<PathComponents>
+ {
+ const Zstring itemPathFmt = appendSeparator(itemPath); //simplify analysis of root without seperator, e.g. \\server-name\share
+ int sepCount = 0;
+ for (auto it = itemPathFmt.begin(); it != itemPathFmt.end(); ++it)
+ if (*it == FILE_NAME_SEPARATOR)
+ if (++sepCount == sepCountVolumeRoot)
+ {
+ Zstring rootPath(itemPathFmt.begin(), rootWithSep ? it + 1 : it);
+
+ Zstring relPath(it + 1, itemPathFmt.end());
+ trim(relPath, true, true, [](Zchar c) { return c == FILE_NAME_SEPARATOR; });
+
+ return PathComponents({ rootPath, relPath });
+ }
+ return NoValue();
+ };
+
if (startsWith(itemPath, "/"))
{
- Zstring relPath(itemPath.c_str() + 1);
- if (endsWith(relPath, FILE_NAME_SEPARATOR))
- relPath.pop_back();
- return PathComponents({ "/", relPath });
+ if (startsWith(itemPath, "/media/"))
+ {
+ //Ubuntu: e.g. /media/zenju/DEVICE_NAME
+ if (const char* username = ::getenv("USER"))
+ if (startsWith(itemPath, std::string("/media/") + username + "/"))
+ return doParse(4 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
+
+ //Ubuntu: e.g. /media/cdrom0
+ return doParse(3 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
+ }
+
+ if (startsWith(itemPath, "/run/media/")) //Suse: e.g. /run/media/zenju/DEVICE_NAME
+ if (const char* username = ::getenv("USER"))
+ if (startsWith(itemPath, std::string("/run/media/") + username + "/"))
+ return doParse(5 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
+
+ return doParse(1 /*sepCountVolumeRoot*/, true /*rootWithSep*/);
}
+
//we do NOT support relative paths!
return NoValue();
}
@@ -115,9 +147,9 @@ PathStatus zen::getPathStatus(const Zstring& itemPath) //throw FileError
Opt<ItemType> zen::getItemTypeIfExists(const Zstring& itemPath) //throw FileError
{
- const PathStatus pd = getPathStatus(itemPath); //throw FileError
- if (pd.relPath.empty())
- return pd.existingType;
+ const PathStatus ps = getPathStatus(itemPath); //throw FileError
+ if (ps.relPath.empty())
+ return ps.existingType;
return NoValue();
}
@@ -541,23 +573,18 @@ void zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw File
}
catch (FileError&)
{
- Opt<PathStatus> pd;
- try { pd = getPathStatus(dirPath); /*throw FileError*/ }
- catch (FileError&) {} //previous exception is more relevant
+ const PathStatus ps = getPathStatus(dirPath); //throw FileError
+ if (ps.existingType == ItemType::FILE)
+ throw;
- if (pd &&
- pd->existingType != ItemType::FILE &&
- pd->relPath.size() != 1) //don't repeat the very same createDirectory() call from above!
- {
- Zstring intermediatePath = pd->existingPath;
- for (const Zstring& itemName : pd->relPath)
+ //ps.relPath.size() == 1 => same createDirectory() call from above? Maybe parent folder was created by parallel thread shortly after failure!
+ Zstring intermediatePath = ps.existingPath;
+ for (const Zstring& itemName : ps.relPath)
+ try
{
- intermediatePath = appendSeparator(intermediatePath) + itemName;
- createDirectory(intermediatePath); //throw FileError, (ErrorTargetExisting)
+ createDirectory(intermediatePath = appendSeparator(intermediatePath) + itemName); //throw FileError, ErrorTargetExisting
}
- return;
- }
- throw;
+ catch (ErrorTargetExisting&) {} //possible, if createDirectoryIfMissingRecursion() is run in parallel
}
}
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp
index bc53f206..e342c8ec 100755
--- a/zen/file_traverser.cpp
+++ b/zen/file_traverser.cpp
@@ -24,14 +24,6 @@ void zen::traverseFolder(const Zstring& dirPath,
{
try
{
- /* quote: "Since POSIX.1 does not specify the size of the d_name field, and other nonstandard fields may precede
- that field within the dirent structure, portable applications that use readdir_r() should allocate
- the buffer whose address is passed in entry as follows:
- len = offsetof(struct dirent, d_name) + pathconf(dirPath, _PC_NAME_MAX) + 1
- entryp = malloc(len); */
- const size_t nameMax = std::max<long>(::pathconf(dirPath.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return long(-1)
- std::vector<char> buffer(offsetof(struct ::dirent, d_name) + nameMax + 1);
-
DIR* folder = ::opendir(dirPath.c_str()); //directory must NOT end with path separator, except "/"
if (!folder)
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), L"opendir");
@@ -39,13 +31,16 @@ void zen::traverseFolder(const Zstring& dirPath,
for (;;)
{
- struct ::dirent* dirEntry = nullptr;
- if (::readdir_r(folder, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r");
- //don't retry but restart dir traversal on error! https://blogs.msdn.microsoft.com/oldnewthing/20140612-00/?p=753/
+ errno = 0;
+ const struct ::dirent* dirEntry = ::readdir(folder); //don't use readdir_r(), see comment in native.cpp
+ if (!dirEntry)
+ {
+ if (errno == 0) //errno left unchanged => no more items
+ return;
- if (!dirEntry) //no more items
- return;
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir");
+ //don't retry but restart dir traversal on error! https://blogs.msdn.microsoft.com/oldnewthing/20140612-00/?p=753/
+ }
//don't return "." and ".."
const char* itemNameRaw = dirEntry->d_name;
@@ -56,7 +51,7 @@ void zen::traverseFolder(const Zstring& dirPath,
const Zstring& itemName = itemNameRaw;
if (itemName.empty()) //checks result of normalizeUtfForPosix, too!
- throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item with empty name.");
+ throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir: Data corruption; item with empty name.");
const Zstring& itemPath = appendSeparator(dirPath) + itemName;
diff --git a/zen/guid.h b/zen/guid.h
index b2ada48c..6cffc708 100755
--- a/zen/guid.h
+++ b/zen/guid.h
@@ -9,10 +9,14 @@
#include <string>
+#ifdef __GNUC__ //boost should clean this mess up
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
#include <boost/uuid/uuid_generators.hpp>
+#ifdef __GNUC__
#pragma GCC diagnostic pop
+#endif
namespace zen
diff --git a/zen/scope_guard.h b/zen/scope_guard.h
index 328e2caa..6a732c9f 100755
--- a/zen/scope_guard.h
+++ b/zen/scope_guard.h
@@ -13,7 +13,7 @@
//std::uncaught_exceptions() currently unsupported on GCC and Clang => clean up ASAP
- static_assert(__GNUC__ < 7 || (__GNUC__ == 7 && (__GNUC_MINOR__ < 3 || (__GNUC_MINOR__ == 3 && __GNUC_PATCHLEVEL__ <= 0))), "check std::uncaught_exceptions support");
+ static_assert(__GNUC__ < 7 || (__GNUC__ == 7 && (__GNUC_MINOR__ < 3 || (__GNUC_MINOR__ == 3 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support");
namespace __cxxabiv1
{
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index ba4a6c89..f09639e1 100755
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -58,9 +58,8 @@ template <class InputIterator1, class InputIterator2>
bool equal(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2);
-template <class ByteIterator> size_t hashBytes (ByteIterator first, ByteIterator last);
-template <class ByteIterator> size_t hashBytesAppend(size_t hashVal, ByteIterator first, ByteIterator last);
-
+template <class Num, class ByteIterator> Num hashBytes (ByteIterator first, ByteIterator last);
+template <class Num, class ByteIterator> Num hashBytesAppend(Num hashVal, ByteIterator first, ByteIterator last);
//support for custom string classes in std::unordered_set/map
struct StringHash
@@ -69,7 +68,7 @@ struct StringHash
size_t operator()(const String& str) const
{
const auto* strFirst = strBegin(str);
- return hashBytes(reinterpret_cast<const char*>(strFirst),
+ return hashBytes<size_t>(reinterpret_cast<const char*>(strFirst),
reinterpret_cast<const char*>(strFirst + strLength(str)));
}
};
@@ -190,34 +189,29 @@ bool equal(InputIterator1 first1, InputIterator1 last1,
}
+
-
-template <class ByteIterator> inline
-size_t hashBytes(ByteIterator first, ByteIterator last)
+//FNV-1a: http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
+template <class Num, class ByteIterator> inline
+Num hashBytes(ByteIterator first, ByteIterator last)
{
- //FNV-1a: http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
-#ifdef ZEN_BUILD_32BIT
- const size_t basis = 2166136261U;
-#elif defined ZEN_BUILD_64BIT
- const size_t basis = 14695981039346656037ULL;
-#endif
- return hashBytesAppend(basis, first, last);
+ static_assert(std::is_integral<Num>::value, "");
+ static_assert(sizeof(Num) == 4 || sizeof(Num) == 8, ""); //macOS: size_t is "unsigned long"
+ const Num base = sizeof(Num) == 4 ? 2166136261U : 14695981039346656037ULL;
+
+ return hashBytesAppend(base, first, last);
}
-template <class ByteIterator> inline
-size_t hashBytesAppend(size_t hashVal, ByteIterator first, ByteIterator last)
+template <class Num, class ByteIterator> inline
+Num hashBytesAppend(Num hashVal, ByteIterator first, ByteIterator last)
{
-#ifdef ZEN_BUILD_32BIT
- const size_t prime = 16777619U;
-#elif defined ZEN_BUILD_64BIT
- const size_t prime = 1099511628211ULL;
-#endif
- static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1, "");
-
- for (; first != last; ++first)
+ static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1, "");
+ const Num prime = sizeof(Num) == 4 ? 16777619U : 1099511628211ULL;
+
+ for (; first != last; ++first)
{
- hashVal ^= static_cast<size_t>(*first);
+ hashVal ^= static_cast<Num>(*first);
hashVal *= prime;
}
return hashVal;
diff --git a/zen/thread.h b/zen/thread.h
index 3721b3c7..ed61e06b 100755
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -71,8 +71,8 @@ std::async replacement without crappy semantics:
2. does not follow C++11 [futures.async], Paragraph 5, where std::future waits for thread in destructor
Example:
- Zstring dirpath = ...
- auto ft = zen::runAsync([=]{ return zen::dirExists(dirpath); });
+ Zstring dirPath = ...
+ auto ft = zen::runAsync([=]{ return zen::dirExists(dirPath); });
if (ft.wait_for(std::chrono::milliseconds(200)) == std::future_status::ready && ft.get())
//dir exising
*/
@@ -120,10 +120,10 @@ public:
Protected(const T& value) : value_(value) {}
template <class Function>
- void access(Function fun)
+ auto access(Function fun) //-> decltype(fun(std::declval<T&>()))
{
std::lock_guard<std::mutex> dummy(lockValue_);
- fun(value_);
+ return fun(value_);
}
private:
@@ -134,6 +134,60 @@ private:
T value_{};
};
+//------------------------------------------------------------------------------------------
+
+template <class Function>
+class ThreadGroup
+{
+public:
+ ThreadGroup(size_t threadCount, const std::string& groupName)
+ {
+ for (size_t i = 0; i < threadCount; ++i)
+ worker_.emplace_back([this, groupName, i, threadCount]
+ {
+ setCurrentThreadName((groupName + "[" + numberTo<std::string>(i + 1) + "/" + numberTo<std::string>(threadCount) + "]").c_str());
+ for (;;)
+ getNextWorkItem()(); //throw ThreadInterruption
+ });
+ }
+ ~ThreadGroup()
+ {
+ for (InterruptibleThread& w : worker_) w.interrupt(); //interrupt all first, then join
+ for (InterruptibleThread& w : worker_) w.join();
+ }
+
+ //context of controlling thread, non-blocking:
+ void run(Function&& wi)
+ {
+ assert(!worker_.empty());
+ {
+ std::lock_guard<std::mutex> dummy(lockWork_);
+ workItems_.push_back(std::move(wi));
+ }
+ conditionNewWork_.notify_all();
+ }
+
+private:
+ ThreadGroup (const ThreadGroup&) = delete;
+ ThreadGroup& operator=(const ThreadGroup&) = delete;
+
+ //context of worker threads, blocking:
+ Function getNextWorkItem() //throw ThreadInterruption
+ {
+ std::unique_lock<std::mutex> dummy(lockWork_);
+
+ interruptibleWait(conditionNewWork_, dummy, [this] { return !workItems_.empty(); }); //throw ThreadInterruption
+ warn_static("implement FIFO!?")
+
+ Function wi = std::move(workItems_. back()); //
+ /**/ workItems_.pop_back(); //noexcept thanks to move
+ return wi; //
+ }
+ std::vector<InterruptibleThread> worker_;
+ std::mutex lockWork_;
+ std::vector<Function> workItems_;
+ std::condition_variable conditionNewWork_;
+};
@@ -222,10 +276,9 @@ public:
private:
bool jobDone(size_t jobsTotal) const { return result_ || (jobsFinished_ >= jobsTotal); } //call while locked!
-
std::mutex lockResult_;
size_t jobsFinished_ = 0; //
- Opt<T> result_; //our condition is: "have result" or "jobsFinished_ == jobsTotal"
+ Opt<T> result_; //our condition is: "have result" or "jobsFinished_ == jobsTotal"
std::condition_variable conditionJobDone_;
};
@@ -256,7 +309,11 @@ Opt<T> GetFirstResult<T>::get() const { return asyncResult_->getResult(jobsTotal
//------------------------------------------------------------------------------------------
//thread_local with non-POD seems to cause memory leaks on VS 14 => pointer only is fine:
+#if defined __GNUC__ || defined __clang__
#define ZEN_THREAD_LOCAL_SPECIFIER __thread
+#else
+ #error "Game over!"
+#endif
class ThreadInterruption {};
@@ -296,7 +353,8 @@ public:
setConditionVar(&cv);
ZEN_ON_SCOPE_EXIT(setConditionVar(nullptr));
- //"interrupted_" is not protected by cv's mutex => signal may get lost!!! => add artifical time out to mitigate! CPU: 0.25% vs 0% for longer time out!
+ //"interrupted_" is not protected by cv's mutex => signal may get lost!!! e.g. after condition was checked but before the wait begins
+ //=> add artifical time out to mitigate! CPU: 0.25% vs 0% for longer time out!
while (!cv.wait_for(lock, std::chrono::milliseconds(1), [&] { return this->interrupted_ || pred(); }))
;
diff --git a/zen/type_traits.h b/zen/type_traits.h
index f0f96b43..83a74d1e 100755
--- a/zen/type_traits.h
+++ b/zen/type_traits.h
@@ -37,6 +37,12 @@ struct ResultType
using Type = T;
};
+template<class T, class...>
+struct GetFirstOf
+{
+ using Type = T;
+};
+
//Herb Sutter's signedness conversion helpers: http://herbsutter.com/2013/06/13/gotw-93-solution-auto-variables-part-2/
template<class T> inline auto makeSigned (T t) { return static_cast<std::make_signed_t <T>>(t); }
template<class T> inline auto makeUnsigned(T t) { return static_cast<std::make_unsigned_t<T>>(t); }
diff --git a/zen/warn_static.h b/zen/warn_static.h
index 5b9a4fee..e4931c08 100755
--- a/zen/warn_static.h
+++ b/zen/warn_static.h
@@ -14,11 +14,13 @@ Usage:
warn_static("my message")
*/
+#if defined __GNUC__
#define STATIC_WARNING_CONCAT_SUB(X, Y) X ## Y
#define STATIC_WARNING_CONCAT(X, Y) STATIC_WARNING_CONCAT_SUB(X, Y)
#define warn_static(TXT) \
typedef int STATIC_WARNING_87903124 __attribute__ ((deprecated)); \
enum { STATIC_WARNING_CONCAT(warn_static_dummy_value, __LINE__) = sizeof(STATIC_WARNING_87903124) };
+#endif
#endif //WARN_STATIC_H_08724567834560832745
diff --git a/zen/xml_io.cpp b/zen/xml_io.cpp
index a618f27c..dd116ef0 100755
--- a/zen/xml_io.cpp
+++ b/zen/xml_io.cpp
@@ -57,7 +57,7 @@ void zen::saveXmlDocument(const XmlDoc& doc, const Zstring& filePath) //throw Fi
{
const std::string stream = serialize(doc); //noexcept
- //only update xml file if there are real changes
+ //only update XML file if there are real changes
try
{
if (getFileSize(filePath) == stream.size()) //throw FileError
diff --git a/zen/xml_io.h b/zen/xml_io.h
index a53a7edb..81b45aa1 100755
--- a/zen/xml_io.h
+++ b/zen/xml_io.h
@@ -11,7 +11,7 @@
#include "file_error.h"
-//combine zen::Xml and zen file i/o
+//combine zen::Xml and zen file I/O
//-> loadXmlDocument vs loadStream:
//1. better error reporting
//2. quick exit if (potentially large) input file is not an XML
diff --git a/zen/zstring.cpp b/zen/zstring.cpp
index ce94fe56..afa62c93 100755
--- a/zen/zstring.cpp
+++ b/zen/zstring.cpp
@@ -54,7 +54,9 @@ int compareNoCaseUtf8(const char* lhs, size_t lhsLen, const char* rhs, size_t rh
if (!cpL || !cpR)
return static_cast<int>(!cpR) - static_cast<int>(!cpL);
- static_assert(sizeof(wchar_t) == sizeof(impl::CodePoint), "");
+//support unit-testing on Windows: CodePoint is truncated to wchar_t
+static_assert(sizeof(wchar_t) == sizeof(impl::CodePoint), "");
+
const wchar_t charL = ::towlower(static_cast<wchar_t>(*cpL)); //ordering: towlower() converts to higher code points than towupper()
const wchar_t charR = ::towlower(static_cast<wchar_t>(*cpR)); //uses LC_CTYPE category of current locale
if (charL != charR)
@@ -65,7 +67,7 @@ int compareNoCaseUtf8(const char* lhs, size_t lhsLen, const char* rhs, size_t rh
}
-int cmpStringNaturalLinux(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen)
+int cmpStringNaturalLinuxTest(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen)
{
const char* const lhsEnd = lhs + lhsLen;
const char* const rhsEnd = rhs + rhsLen;
diff --git a/zen/zstring.h b/zen/zstring.h
index 258603dc..5a6ecbdd 100755
--- a/zen/zstring.h
+++ b/zen/zstring.h
@@ -163,7 +163,9 @@ S ciReplaceCpy(const S& str, const T& oldTerm, const U& newTerm)
}
}
- int cmpStringNaturalLinux(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen);
+//expose for unit tests
+int cmpStringNaturalLinuxTest(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen);
+inline int cmpStringNaturalLinux(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen) { return cmpStringNaturalLinuxTest(lhs, lhsLen, rhs, rhsLen); }
//---------------------------------------------------------------------------
//ZEN macro consistency checks:
bgstack15