diff options
author | B Stack <bgstack15@gmail.com> | 2018-09-10 02:46:25 +0000 |
---|---|---|
committer | B Stack <bgstack15@gmail.com> | 2018-09-10 02:46:25 +0000 |
commit | 728d32e6da9ce66968f8eef47a59505d613e2c1b (patch) | |
tree | 0f0441755ff0e6d65e12222d4502c648bffd6a7c | |
parent | 10.3 (diff) | |
parent | pull in latest 10.4 from upstream (diff) | |
download | FreeFileSync-10.4.tar.gz FreeFileSync-10.4.tar.bz2 FreeFileSync-10.4.zip |
Merge branch '10.4' into 'master'10.4
pull in latest 10.4 from upstream
See merge request opensource-tracking/FreeFileSync!1
127 files changed, 2884 insertions, 2594 deletions
diff --git a/Changelog.txt b/Changelog.txt index 81106189..90955af9 100755 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,3 +1,15 @@ +FreeFileSync 10.4 [2018-09-09] +------------------------------ +Allow overriding log folder path for gui and batch runs +Fixed RealTimeSync not triggering when using volume path by name +Fixed reading FTP folders including wildcard chars +Fixed image overlay graphics glitch (Linux) +Don't show error if versioning folder is not yet existing +Fixed crash when removing folder pair just before comparison (F5) +Fixed crash when parent folder of newly-moved file is deleted after comparison +Fixed statistics when folder containing moved files is found missing + + FreeFileSync 10.3 [2018-08-07] ------------------------------ New log panel showing details about the last operation diff --git a/FreeFileSync/Build/Help/images/comparison-settings.png b/FreeFileSync/Build/Help/images/comparison-settings.png Binary files differindex 242f558a..9f638a4e 100755 --- a/FreeFileSync/Build/Help/images/comparison-settings.png +++ b/FreeFileSync/Build/Help/images/comparison-settings.png diff --git a/FreeFileSync/Build/Help/images/synchronization-settings.png b/FreeFileSync/Build/Help/images/synchronization-settings.png Binary files differindex 501b3db1..ae2b4be3 100755 --- a/FreeFileSync/Build/Help/images/synchronization-settings.png +++ b/FreeFileSync/Build/Help/images/synchronization-settings.png diff --git a/FreeFileSync/Build/Languages/arabic.lng b/FreeFileSync/Build/Languages/arabic.lng index d6250172..97143023 100755 --- a/FreeFileSync/Build/Languages/arabic.lng +++ b/FreeFileSync/Build/Languages/arabic.lng @@ -100,8 +100,8 @@ <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 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> @@ -342,6 +342,9 @@ <source>Update attributes on right</source> <target>تحديث السمات على اليسار</target> +<source>Warning</source> +<target>تحذير</target> + <source>Items processed:</source> <target>معالجة العناصر:</target> @@ -351,6 +354,9 @@ <source>Total time:</source> <target>مجموع الوقت:</target> +<source>Stopped</source> +<target>توقف</target> + <source>Cleaning up log files:</source> <target>تنظيف ملفات السجل:</target> @@ -394,6 +400,15 @@ <source>Unable to connect to %x.</source> <target>لا يمكن الاتصال بـ %x.</target> +<source>Completed successfully</source> +<target>تم بنجاح</target> + +<source>Completed with warnings</source> +<target>تم بتحذيرات</target> + +<source>Completed with errors</source> +<target>تم بأخطاء</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>لا يمكن الوصول إلى خدمة "نسخ الظل لوحدة التخزين".</target> @@ -702,8 +717,8 @@ Actual: %y bytes <source>3. Press 'Start'.</source> <target>3. اضغط على 'ابدأ'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>للبدء قم باستيراد ملف .ffs_batch.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>للبدء قم باستيراد ملف "ffs_batch".</target> <source>Folders to watch:</source> <target>المجلدات للمتابعة:</target> @@ -785,24 +800,9 @@ The command is triggered if: <source>System: Shut down</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> @@ -858,6 +858,9 @@ The command is triggered if: <source>Last sync</source> <target>أخر مزامنة</target> +<source>Log</source> +<target>السجل</target> + <source>Folder</source> <target>المجلد</target> @@ -933,6 +936,9 @@ The command is triggered if: <source>Save as &batch job...</source> <target>&حفظ كمهمة دفعية...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>بدأ الم&قارنة</target> @@ -1331,6 +1337,9 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>إعادة إظهار جميع التنبهات و نوافذ الحوار التي تم إخفاؤها</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>تخصيص القائمة المحلية:</target> @@ -1421,6 +1430,15 @@ This guarantees a consistent state even in case of a serious error. <source>Highlight Configurations</source> <target>تمييز التكوينات</target> +<source>Info</source> +<target>معلومات</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>اختيار الجميع</target> + <source>&Options</source> <target>&خيارات</target> @@ -1674,21 +1692,12 @@ This guarantees a consistent state even in case of a serious error. <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> @@ -1926,6 +1935,9 @@ This guarantees a consistent state even in case of a serious error. <source>Unable to register to receive system messages.</source> <target>تعذر التسجيل لاستقبال رسائل النظام.</target> +<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source> +<target>خيار التحميل %x متوفر فقط فى نسخة التبرعات من FreeFileSync.</target> + <source>Cannot find system function %x.</source> <target>لا يمكن العثور على وظيفة نظام %x.</target> @@ -2090,9 +2102,6 @@ This guarantees a consistent state even in case of a serious error. <source>Please choose the local installation type or select a different folder for installation.</source> <target>الرجاء اختيار نوع التثبيت المحلي أو اختيار مجلد آخر للتثبيت.</target> -<source>The %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/bulgarian.lng b/FreeFileSync/Build/Languages/bulgarian.lng index 7ee65a5c..935fa43f 100755 --- a/FreeFileSync/Build/Languages/bulgarian.lng +++ b/FreeFileSync/Build/Languages/bulgarian.lng @@ -100,8 +100,8 @@ <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 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> @@ -334,6 +334,9 @@ <source>Update attributes on right</source> <target>Актуализирай атрибутите на десния елемент</target> +<source>Warning</source> +<target>Предупреждение</target> + <source>Items processed:</source> <target>Обработени елементи:</target> @@ -343,6 +346,9 @@ <source>Total time:</source> <target>Общо време:</target> +<source>Stopped</source> +<target>Спряно</target> + <source>Cleaning up log files:</source> <target>Почистване на log-файловете:</target> @@ -382,6 +388,15 @@ <source>Unable to connect to %x.</source> <target>Не може да се свърже със %x.</target> +<source>Completed successfully</source> +<target>Завърши успешно</target> + +<source>Completed with warnings</source> +<target>Завърши с предупреждения</target> + +<source>Completed with errors</source> +<target>Завърши с грешки</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Не може да получи достъп до услуга Volume Shadow Copy.</target> @@ -457,12 +472,6 @@ <source>Creating a Volume Shadow Copy for %x...</source> <target>Създава се Volume Shadow Copy за %x...</target> -<source>Searching for excess file versions:</source> -<target>Търси излишни файлови версии:</target> - -<source>Removing excess file versions:</source> -<target>Премахва излишни файлови версии:</target> - <source>Cannot find folder %x.</source> <target>Не е намерена папка %x.</target> @@ -514,6 +523,12 @@ <source>Generating database...</source> <target>Създава база данни...</target> +<source>Searching for excess file versions:</source> +<target>Търси излишни файлови версии:</target> + +<source>Removing excess file versions:</source> +<target>Премахва излишни файлови версии:</target> + <source>Unable to create time stamp for versioning:</source> <target>Не може да маркира времето на версиите:</target> @@ -678,8 +693,8 @@ Actual: %y bytes <source>3. Press 'Start'.</source> <target>3. Натиснете 'Старт'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>За старт просто импортирайте файл .ffs_batch.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>За старт просто импортирайте файл "ffs_batch".</target> <source>Folders to watch:</source> <target>Папки за следене:</target> @@ -761,24 +776,9 @@ The command is triggered if: <source>System: Shut down</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> @@ -830,6 +830,9 @@ The command is triggered if: <source>Last sync</source> <target>Последна синхронизация</target> +<source>Log</source> +<target>Протокол</target> + <source>Folder</source> <target>Папка</target> @@ -905,6 +908,9 @@ The command is triggered if: <source>Save as &batch job...</source> <target>Запази &като пакетна задача...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>Почни &сравняване</target> @@ -1303,6 +1309,9 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Покажи всички постоянно скрити диалози и предупреждения отново</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>Настрой контекстното меню:</target> @@ -1393,6 +1402,15 @@ This guarantees a consistent state even in case of a serious error. <source>Highlight Configurations</source> <target>Маркирай Конфигурациите</target> +<source>Info</source> +<target>Информация</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Маркирай всичко</target> + <source>&Options</source> <target>&Опции</target> @@ -1630,21 +1648,12 @@ This guarantees a consistent state even in case of a serious error. <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> @@ -1870,6 +1879,9 @@ This guarantees a consistent state even in case of a serious error. <source>Unable to register to receive system messages.</source> <target>Не се регистрира за получаване на системни съобщения.</target> +<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source> +<target>Опцията %x инсталиране е възможна само за FreeFileSync Дарителско Издание.</target> + <source>Cannot find system function %x.</source> <target>Не намира системната функция %x.</target> @@ -2026,9 +2038,6 @@ This guarantees a consistent state even in case of a serious error. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Моля, изберете локален тип инсталация или пък друга папка за инсталация.</target> -<source>The %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 223d2b7d..a4350d39 100755 --- a/FreeFileSync/Build/Languages/chinese_simple.lng +++ b/FreeFileSync/Build/Languages/chinese_simple.lng @@ -100,8 +100,8 @@ <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 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> @@ -235,10 +235,10 @@ <target>数据库文件已损坏:</target> <source>Cannot write file %x.</source> -<target>无法写入文件 %x .</target> +<target>无法写入文件 %x.</target> <source>Cannot read file %x.</source> -<target>无法读取文件 %x .</target> +<target>无法读取文件 %x.</target> <source>The database files do not yet contain information about the last synchronization.</source> <target>此数据库文件并未包含 有关最后同步的信息.</target> @@ -332,6 +332,9 @@ <source>Update attributes on right</source> <target>更新右侧的文件属性</target> +<source>Warning</source> +<target>警告</target> + <source>Items processed:</source> <target>已处理的项目:</target> @@ -341,6 +344,9 @@ <source>Total time:</source> <target>总共时间:</target> +<source>Stopped</source> +<target>已停止</target> + <source>Cleaning up log files:</source> <target>正在清理日志文件:</target> @@ -379,6 +385,15 @@ <source>Unable to connect to %x.</source> <target>无法连接到 %x.</target> +<source>Completed successfully</source> +<target>成功完成</target> + +<source>Completed with warnings</source> +<target>已完成但有警告</target> + +<source>Completed with errors</source> +<target>已完成但有错误</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>无法访问卷影复制服务.</target> @@ -672,8 +687,8 @@ Actual: %y bytes <source>3. Press 'Start'.</source> <target>3. 点击'开始'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>要开始只需导入一个 .ffs_batch文件.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>要开始只需导入一个 "ffs_batch"文件.</target> <source>Folders to watch:</source> <target>要监视的文件夹:</target> @@ -755,24 +770,9 @@ The command is triggered if: <source>System: Shut down</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> @@ -823,6 +823,9 @@ The command is triggered if: <source>Last sync</source> <target>最后同步</target> +<source>Log</source> +<target>日志</target> + <source>Folder</source> <target>文件夹</target> @@ -898,6 +901,9 @@ The command is triggered if: <source>Save as &batch job...</source> <target>另存为批处理作业(&B)...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>开始比较(&C)</target> @@ -1293,6 +1299,9 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>重新显示所有被永久性隐藏的 对话框和警告信息</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>自定义右键菜单:</target> @@ -1383,6 +1392,15 @@ This guarantees a consistent state even in case of a serious error. <source>Highlight Configurations</source> <target>突出显示配置文件</target> +<source>Info</source> +<target>信息</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>选择全部</target> + <source>&Options</source> <target>选项(&O)</target> @@ -1512,7 +1530,7 @@ This guarantees a consistent state even in case of a serious error. <target>FreeFileSync批处理文件</target> <source>Do you want to save changes to %x?</source> -<target>是否要保存修改到 %x ?</target> +<target>是否要保存修改到 %x?</target> <source>Never save &changes</source> <target>不再保存更改(&C)</target> @@ -1616,21 +1634,12 @@ This guarantees a consistent state even in case of a serious error. <source>Comparing content...</source> <target>正在比较文件内容...</target> -<source>Info</source> -<target>信息</target> - -<source>Select all</source> -<target>选择全部</target> - <source>&Continue</source> <target>继续(&C)</target> <source>Progress</source> <target>进度</target> -<source>Log</source> -<target>日志</target> - <source>Thank you, %x, for your donation and support!</source> <target>谢谢你, %x , 对我们的捐款和支持!</target> @@ -1853,6 +1862,9 @@ This guarantees a consistent state even in case of a serious error. <source>Unable to register to receive system messages.</source> <target>无法注册以接收系统信息.</target> +<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source> +<target>安装选项 %x 只在 FreeFileSync 捐赠版有效.</target> + <source>Cannot find system function %x.</source> <target>无法找到系统功能 %x.</target> @@ -2007,9 +2019,6 @@ This guarantees a consistent state even in case of a serious error. <source>Please choose the local installation type or select a different folder for installation.</source> <target>请选择本地安装类型, 或选择用于安装的不同文件夹.</target> -<source>The %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 c93a07a5..2dc930c4 100755 --- a/FreeFileSync/Build/Languages/chinese_traditional.lng +++ b/FreeFileSync/Build/Languages/chinese_traditional.lng @@ -100,8 +100,8 @@ <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 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> @@ -332,6 +332,9 @@ <source>Update attributes on right</source> <target>更新右邊的屬性</target> +<source>Warning</source> +<target>警告</target> + <source>Items processed:</source> <target>已處理項目:</target> @@ -341,6 +344,9 @@ <source>Total time:</source> <target>全部時間:</target> +<source>Stopped</source> +<target>已停止</target> + <source>Cleaning up log files:</source> <target>清除日誌檔:</target> @@ -379,6 +385,15 @@ <source>Unable to connect to %x.</source> <target>無法連接到 %x。</target> +<source>Completed successfully</source> +<target>成功完成</target> + +<source>Completed with warnings</source> +<target>已完成但有警告</target> + +<source>Completed with errors</source> +<target>已完成但有錯誤</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>無法存取磁碟區陰影複製服務。</target> @@ -454,12 +469,6 @@ <source>Creating a Volume Shadow Copy for %x...</source> <target>正在建立磁碟區陰影複製為 %x…</target> -<source>Searching for excess file versions:</source> -<target>正在搜尋多餘的檔案版本:</target> - -<source>Removing excess file versions:</source> -<target>正在刪除多餘的檔案版本:</target> - <source>Cannot find folder %x.</source> <target>找不到資料夾 %x。</target> @@ -511,6 +520,12 @@ <source>Generating database...</source> <target>正在產生資料庫…</target> +<source>Searching for excess file versions:</source> +<target>正在搜尋多餘的檔案版本:</target> + +<source>Removing excess file versions:</source> +<target>正在刪除多餘的檔案版本:</target> + <source>Unable to create time stamp for versioning:</source> <target>無法建立時間戳記的版本控制:</target> @@ -672,8 +687,8 @@ Actual: %y bytes <source>3. Press 'Start'.</source> <target>3. 按下「開始」。</target> -<source>To get started just import a .ffs_batch file.</source> -<target>若要開始只要導入一個.ffs_batch檔。</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>若要開始只要導入一個"ffs_batch"檔。</target> <source>Folders to watch:</source> <target>要監看的資料夾:</target> @@ -755,24 +770,9 @@ The command is triggered if: <source>System: Shut down</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> @@ -823,6 +823,9 @@ The command is triggered if: <source>Last sync</source> <target>上次同步</target> +<source>Log</source> +<target>日誌</target> + <source>Folder</source> <target>資料夾</target> @@ -898,6 +901,9 @@ The command is triggered if: <source>Save as &batch job...</source> <target>另存為批次工作(&B)…</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>開始比對(&C)</target> @@ -1296,6 +1302,9 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>再次顯示所有永久 隱藏的對話框和警告訊息</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>自訂內容功能表:</target> @@ -1386,6 +1395,15 @@ This guarantees a consistent state even in case of a serious error. <source>Highlight Configurations</source> <target>突顯配置</target> +<source>Info</source> +<target>訊息</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>全選</target> + <source>&Options</source> <target>選項(&O)</target> @@ -1619,21 +1637,12 @@ This guarantees a consistent state even in case of a serious error. <source>Comparing content...</source> <target>正在比對内容…</target> -<source>Info</source> -<target>訊息</target> - -<source>Select all</source> -<target>全選</target> - <source>&Continue</source> <target>繼續(&C)</target> <source>Progress</source> <target>進度</target> -<source>Log</source> -<target>日誌</target> - <source>Thank you, %x, for your donation and support!</source> <target>%x, 感謝您的贊助與支持!</target> @@ -1856,6 +1865,9 @@ This guarantees a consistent state even in case of a serious error. <source>Unable to register to receive system messages.</source> <target>無法註冊以接收系統訊息。</target> +<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source> +<target>%x 安裝選項僅在FreeFileSync贊助版可用。</target> + <source>Cannot find system function %x.</source> <target>找不到系統功能 %x。</target> @@ -2010,9 +2022,6 @@ This guarantees a consistent state even in case of a serious error. <source>Please choose the local installation type or select a different folder for installation.</source> <target>請選擇本機安裝類型或選擇不同的安裝資料夾。</target> -<source>The %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 85ce9399..78107ef3 100755 --- a/FreeFileSync/Build/Languages/croatian.lng +++ b/FreeFileSync/Build/Languages/croatian.lng @@ -100,8 +100,8 @@ <source>global config file:</source> <target>globalna config datoteka:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>Bilo koji broj FreeFileSync .ffs_gui i/ili .ffs_batch konfiguracijskih datoteka.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>Bilo koji broj FreeFileSync "ffs_gui" i/ili "ffs_batch" konfiguracijskih datoteka.</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Bilo koji broj alternativnih parova mapa za najmanje jednu config datoteku.</target> @@ -336,6 +336,9 @@ <source>Update attributes on right</source> <target>Osvježi atribute desno</target> +<source>Warning</source> +<target>Upozorenje</target> + <source>Items processed:</source> <target>Obrađene stavke:</target> @@ -345,6 +348,9 @@ <source>Total time:</source> <target>Ukupno vrijeme:</target> +<source>Stopped</source> +<target>Zaustavljeno</target> + <source>Cleaning up log files:</source> <target>Brisanje log datoteka:</target> @@ -385,6 +391,15 @@ <source>Unable to connect to %x.</source> <target>Nije moguće povezivanje na %x.</target> +<source>Completed successfully</source> +<target>Dovršeno uspješno</target> + +<source>Completed with warnings</source> +<target>Dovršeno s upozorenjima</target> + +<source>Completed with errors</source> +<target>Dovršeno s pogreškama</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Ne mogu pristupiti Voulme Shadow Copy servisu.</target> @@ -460,12 +475,6 @@ <source>Creating a Volume Shadow Copy for %x...</source> <target>Kreiranje Volume Shadow Copy za %x...</target> -<source>Searching for excess file versions:</source> -<target>Traženje višestrukih verzija datoteka:</target> - -<source>Removing excess file versions:</source> -<target>Uklanjanje višestrukih verzija datoteka:</target> - <source>Cannot find folder %x.</source> <target>Ne mogu pronaći mapu %x.</target> @@ -517,6 +526,12 @@ <source>Generating database...</source> <target>Izrađivanje baze podataka...</target> +<source>Searching for excess file versions:</source> +<target>Traženje višestrukih verzija datoteka:</target> + +<source>Removing excess file versions:</source> +<target>Uklanjanje višestrukih verzija datoteka:</target> + <source>Unable to create time stamp for versioning:</source> <target>Nije moguća izrada vremenske oznake za označavanje:</target> @@ -684,8 +699,8 @@ Stvarno: %y bajta <source>3. Press 'Start'.</source> <target>3. Pretisnite 'Start'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Da biste započeli jednostavno uvezite .ffs_batch datoteku.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Da biste započeli jednostavno uvezite "ffs_batch" datoteku.</target> <source>Folders to watch:</source> <target>Mape za nadziranje:</target> @@ -767,24 +782,9 @@ Naredba će biti pokrenuta ako se: <source>System: Shut down</source> <target>Sistem: Isključivanje</target> -<source>Stopped</source> -<target>Zaustavljeno</target> - -<source>Completed with errors</source> -<target>Dovršeno s pogreškama</target> - -<source>Completed with warnings</source> -<target>Dovršeno s upozorenjima</target> - -<source>Warning</source> -<target>Upozorenje</target> - <source>Nothing to synchronize</source> <target>Ništa za sinkronizirati</target> -<source>Completed successfully</source> -<target>Dovršeno uspješno</target> - <source>Executing command %x</source> <target>Izvršavanje naredbe %x</target> @@ -837,6 +837,9 @@ Naredba će biti pokrenuta ako se: <source>Last sync</source> <target>Zadnja sinkr</target> +<source>Log</source> +<target>Izvješće</target> + <source>Folder</source> <target>Mapa</target> @@ -912,6 +915,9 @@ Naredba će biti pokrenuta ako se: <source>Save as &batch job...</source> <target>Spremi kao &Slijedni zadatak...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>Započni &usporedbu</target> @@ -1137,7 +1143,7 @@ Naredba će biti pokrenuta ako se: <target>Broj pokušaja:</target> <source>Delay (in seconds):</source> -<target>Odgoda (u sekundama) :</target> +<target>Odgoda (u sekundama):</target> <source>Run a command after synchronization:</source> <target>Pokreni naredbu nakon sinkronizacije:</target> @@ -1310,6 +1316,9 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Prikaži sve trajno skrivene prozore i poruke upozorenja ponovno</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>Prilagodite kontekstni izbornik:</target> @@ -1400,6 +1409,15 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. <source>Highlight Configurations</source> <target>Istakni postavke</target> +<source>Info</source> +<target>Info</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Odaberi sve</target> + <source>&Options</source> <target>&Opcije</target> @@ -1641,21 +1659,12 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. <source>Comparing content...</source> <target>Uspoređujem sadržaj...</target> -<source>Info</source> -<target>Info</target> - -<source>Select all</source> -<target>Odaberi sve</target> - <source>&Continue</source> <target>&Nastavi</target> <source>Progress</source> <target>Napredak</target> -<source>Log</source> -<target>Izvješće</target> - <source>Thank you, %x, for your donation and support!</source> <target>Hvala Vam, %x, na vašoj donaciji i podršci!</target> @@ -1884,6 +1893,9 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. <source>Unable to register to receive system messages.</source> <target>Nije moguće registriranje za primanje sistemskih poruka.</target> +<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>Cannot find system function %x.</source> <target>Ne mogu pronaći sistemsku funkciju %x.</target> @@ -2042,9 +2054,6 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Molimo odaberite lokalnu instalaciju ili odaberite drugu mapu za instalaciju.</target> -<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 7bf97103..3403389a 100755 --- a/FreeFileSync/Build/Languages/czech.lng +++ b/FreeFileSync/Build/Languages/czech.lng @@ -50,7 +50,7 @@ <target>Mazání symbolického odkazu %x</target> <source>Checking recycle bin availability for folder %x...</source> -<target>Kontrola Koše na složku %x ...</target> +<target>Kontrola Koše na složku %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>Pro některé složky nelze použít Koš a proto následující soubory budou odstraněny trvale, bez možnosti obnovy:</target> @@ -100,8 +100,8 @@ <source>global config file:</source> <target>konfigurační soubory:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>Libovolný počet konfiguračních souborů FreeFileSync typu .ffs_gui a/nebo .ffs_batch.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>Libovolný počet konfiguračních souborů FreeFileSync typu "ffs_gui" a/nebo "ffs_batch".</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Libovolný počet alternativních párů adresářů na alespoň jednu konfiguraci.</target> @@ -336,6 +336,9 @@ <source>Update attributes on right</source> <target>Nastavit vlastnosti vpravo</target> +<source>Warning</source> +<target>Varování</target> + <source>Items processed:</source> <target>Zpracováno položek:</target> @@ -345,6 +348,9 @@ <source>Total time:</source> <target>Celkový čas:</target> +<source>Stopped</source> +<target>Zastaveno</target> + <source>Cleaning up log files:</source> <target>Vyčištění žurnálů:</target> @@ -385,6 +391,15 @@ <source>Unable to connect to %x.</source> <target>Nepodařilo se připojit k %x.</target> +<source>Completed successfully</source> +<target>Úspěšně dokončeno</target> + +<source>Completed with warnings</source> +<target>Dokončeno s varováním</target> + +<source>Completed with errors</source> +<target>Dokončeno s chybou</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Nepodařil se přístup ke službě Stínové kopie.</target> @@ -460,12 +475,6 @@ <source>Creating a Volume Shadow Copy for %x...</source> <target>Vytváření Stínové kopie pro %x...</target> -<source>Searching for excess file versions:</source> -<target>Hledání nadbytečných verzí:</target> - -<source>Removing excess file versions:</source> -<target>Odstraňování nadbytečných verzí:</target> - <source>Cannot find folder %x.</source> <target>Nelze najít složku %x.</target> @@ -517,6 +526,12 @@ <source>Generating database...</source> <target>Vytváření databáze...</target> +<source>Searching for excess file versions:</source> +<target>Hledání nadbytečných verzí:</target> + +<source>Removing excess file versions:</source> +<target>Odstraňování nadbytečných verzí:</target> + <source>Unable to create time stamp for versioning:</source> <target>Nelze vytvořit časové značky verzování:</target> @@ -684,8 +699,8 @@ Aktuálně: %y b <source>3. Press 'Start'.</source> <target>3. Zmáčkněte 'Start'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Můžete načíst také konfigurační soubor .ffs_batch.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Můžete načíst také konfigurační soubor "ffs_batch".</target> <source>Folders to watch:</source> <target>Sledovaná složka:</target> @@ -767,24 +782,9 @@ Příkaz je spuštěn když: <source>System: Shut down</source> <target>Vypnutí počítače</target> -<source>Stopped</source> -<target>Zastaveno</target> - -<source>Completed with errors</source> -<target>Dokončeno s chybou</target> - -<source>Completed with warnings</source> -<target>Dokončeno s varováním</target> - -<source>Warning</source> -<target>Varování</target> - <source>Nothing to synchronize</source> <target>Není co synchronizovat</target> -<source>Completed successfully</source> -<target>Úspěšně dokončeno</target> - <source>Executing command %x</source> <target>Spuštění příkazu %x</target> @@ -837,6 +837,9 @@ Příkaz je spuštěn když: <source>Last sync</source> <target>Naposledy</target> +<source>Log</source> +<target>Žurnál zpracování</target> + <source>Folder</source> <target>Složka</target> @@ -912,6 +915,9 @@ Příkaz je spuštěn když: <source>Save as &batch job...</source> <target>Uložit jako &dávku...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>Začít &porovnání</target> @@ -1307,6 +1313,9 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Zobrazit znovu všechny trvale skryté dialogy a varovná hlášení</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>Přizpůsobit kontextovou nabídku:</target> @@ -1397,6 +1406,15 @@ This guarantees a consistent state even in case of a serious error. <source>Highlight Configurations</source> <target>Zvýraznění položek</target> +<source>Info</source> +<target>Info</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Vybrat vše</target> + <source>&Options</source> <target>Nastavení &programu</target> @@ -1621,7 +1639,7 @@ This guarantees a consistent state even in case of a serious error. <target>Seznam souborů exportován</target> <source>Searching for program updates...</source> -<target>Hledání aktualizací programu ...</target> +<target>Hledání aktualizací programu...</target> <source>Paused</source> <target>Pauza</target> @@ -1638,21 +1656,12 @@ This guarantees a consistent state even in case of a serious error. <source>Comparing content...</source> <target>Porovnávání obsahu...</target> -<source>Info</source> -<target>Info</target> - -<source>Select all</source> -<target>Vybrat vše</target> - <source>&Continue</source> <target>&Pokračovat</target> <source>Progress</source> <target>Průběh</target> -<source>Log</source> -<target>Žurnál zpracování</target> - <source>Thank you, %x, for your donation and support!</source> <target>Děkuji, %x, za příspěvek a podporu!</target> @@ -1881,6 +1890,9 @@ This guarantees a consistent state even in case of a serious error. <source>Unable to register to receive system messages.</source> <target>Nepodařilo se zaregistrovat k odběru systémových zpráv.</target> +<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>Cannot find system function %x.</source> <target>Nelze najít systémovou funkci %x.</target> @@ -2039,9 +2051,6 @@ This guarantees a consistent state even in case of a serious error. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Prosím zvolte lokální typ instalace nebo vyberte jinou složku.</target> -<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 d2212c78..fe6ed455 100755 --- a/FreeFileSync/Build/Languages/danish.lng +++ b/FreeFileSync/Build/Languages/danish.lng @@ -100,8 +100,8 @@ <source>global config file:</source> <target>global indstillingsfil:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>Vilkårligt antal FreeFileSync .ffs_gui og/eller .ffs_batch indstillingsfiler.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>Vilkårligt antal FreeFileSync "ffs_gui" og/eller "ffs_batch" indstillingsfiler.</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Vilkårligt antal alternative mappepar til højst en indstillingsfil.</target> @@ -334,6 +334,9 @@ <source>Update attributes on right</source> <target>Opdater attributter mod højre</target> +<source>Warning</source> +<target>Advarsel</target> + <source>Items processed:</source> <target>Emner behandlet:</target> @@ -343,6 +346,9 @@ <source>Total time:</source> <target>Samlet tid:</target> +<source>Stopped</source> +<target>Afbrudt</target> + <source>Cleaning up log files:</source> <target>Rydder logfiler:</target> @@ -382,6 +388,15 @@ <source>Unable to connect to %x.</source> <target>Kan ikke kontakte %x.</target> +<source>Completed successfully</source> +<target>Gennemført</target> + +<source>Completed with warnings</source> +<target>Gennemført med advarsler</target> + +<source>Completed with errors</source> +<target>Gennemført med fejl</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>VSS tjenesten er ikke tilgængelig.</target> @@ -678,8 +693,8 @@ Aktuel: %y byte <source>3. Press 'Start'.</source> <target>3. Klik 'Start'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Importer en .ffs_batchfil (Fil > Åben...) for at komme igang.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Importer en "ffs_batch"fil (Fil > Åben...) for at komme igang.</target> <source>Folders to watch:</source> <target>Jobbets mapper:</target> @@ -761,24 +776,9 @@ Kommandoen udføres hvis: <source>System: Shut down</source> <target>System: Luk</target> -<source>Stopped</source> -<target>Afbrudt</target> - -<source>Completed with errors</source> -<target>Gennemført med fejl</target> - -<source>Completed with warnings</source> -<target>Gennemført med advarsler</target> - -<source>Warning</source> -<target>Advarsel</target> - <source>Nothing to synchronize</source> <target>Alt er synkroniseret</target> -<source>Completed successfully</source> -<target>Gennemført</target> - <source>Executing command %x</source> <target>Kører kommandoen %x</target> @@ -830,6 +830,9 @@ Kommandoen udføres hvis: <source>Last sync</source> <target>Sidste synk</target> +<source>Log</source> +<target>Log</target> + <source>Folder</source> <target>Mappe</target> @@ -905,6 +908,9 @@ Kommandoen udføres hvis: <source>Save as &batch job...</source> <target>Gem som &batchfil...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>Start &analyse</target> @@ -1303,6 +1309,9 @@ Sikrer processen ved alvorlige fejl. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Vis skjulte advarsler og beskeder igen</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>Tilpas kontekstmenu:</target> @@ -1393,6 +1402,15 @@ Sikrer processen ved alvorlige fejl. <source>Highlight Configurations</source> <target>Fremhæv indstillinger</target> +<source>Info</source> +<target>Info</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Vælg alt</target> + <source>&Options</source> <target>&Indstillinger</target> @@ -1630,21 +1648,12 @@ Sikrer processen ved alvorlige fejl. <source>Comparing content...</source> <target>Analyserer indhold...</target> -<source>Info</source> -<target>Info</target> - -<source>Select all</source> -<target>Vælg alt</target> - <source>&Continue</source> <target>&Fortsæt</target> <source>Progress</source> <target>Fremskridt</target> -<source>Log</source> -<target>Log</target> - <source>Thank you, %x, for your donation and support!</source> <target>Tak %x for donationen og støtten!</target> @@ -1870,6 +1879,9 @@ Sikrer processen ved alvorlige fejl. <source>Unable to register to receive system messages.</source> <target>Kunne ikke registrere modtagelse af systembeskeder.</target> +<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>Cannot find system function %x.</source> <target>Kan ikke finde systemfunktionen %x.</target> @@ -2026,9 +2038,6 @@ Sikrer processen ved alvorlige fejl. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Installer normalt, eller vælg en anden mappe.</target> -<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 8828441e..b68a5d70 100755 --- a/FreeFileSync/Build/Languages/dutch.lng +++ b/FreeFileSync/Build/Languages/dutch.lng @@ -100,8 +100,8 @@ <source>global config file:</source> <target>globaal configuratiebestand:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>Een willekeurig aantal FreeFileSync. ffs_gui en/of .ffs_batch configuratiebestanden.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>Een willekeurig aantal FreeFileSync. ffs_gui en/of "ffs_batch" configuratiebestanden.</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Een willekeurig aantal alternatieve mapparen voor maximaal een configuratiebestand.</target> @@ -334,6 +334,9 @@ <source>Update attributes on right</source> <target>Update kenmerken aan de rechterzijde</target> +<source>Warning</source> +<target>Waarschuwing</target> + <source>Items processed:</source> <target>Items verwerkt:</target> @@ -343,6 +346,9 @@ <source>Total time:</source> <target>Totale tijd:</target> +<source>Stopped</source> +<target>Gestopt</target> + <source>Cleaning up log files:</source> <target>Logbestanden opschonen:</target> @@ -382,6 +388,15 @@ <source>Unable to connect to %x.</source> <target>Kan geen verbinding maken met %x.</target> +<source>Completed successfully</source> +<target>Succesvol voltooid</target> + +<source>Completed with warnings</source> +<target>Voltooid met waarschuwingen</target> + +<source>Completed with errors</source> +<target>Voltooid met fouten</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Kan geen toegang krijgen tot de Volume Shadow Copy Service.</target> @@ -457,12 +472,6 @@ <source>Creating a Volume Shadow Copy for %x...</source> <target>Maken van een volume schaduwkopie voor %x...</target> -<source>Searching for excess file versions:</source> -<target>Zoeken naar overtollige bestandsversies:</target> - -<source>Removing excess file versions:</source> -<target>Overmatige bestandsversies verwijderen:</target> - <source>Cannot find folder %x.</source> <target>Kan de map %x niet vinden.</target> @@ -514,6 +523,12 @@ <source>Generating database...</source> <target>Genereren database...</target> +<source>Searching for excess file versions:</source> +<target>Zoeken naar overtollige bestandsversies:</target> + +<source>Removing excess file versions:</source> +<target>Overmatige bestandsversies verwijderen:</target> + <source>Unable to create time stamp for versioning:</source> <target>Kan geen tijdstempel maken voor versiebeheer:</target> @@ -678,8 +693,8 @@ Werkelijk: %y bytes <source>3. Press 'Start'.</source> <target>3. Druk op 'Start'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Om te beginnen gewoon een .ffs_batch-bestand importeren.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Om te beginnen gewoon een "ffs_batch"-bestand importeren.</target> <source>Folders to watch:</source> <target>Mappen om te bekijken:</target> @@ -761,24 +776,9 @@ De opdracht wordt geactiveerd als: <source>System: Shut down</source> <target>Systeem: Uitschakelen</target> -<source>Stopped</source> -<target>Gestopt</target> - -<source>Completed with errors</source> -<target>Voltooid met fouten</target> - -<source>Completed with warnings</source> -<target>Voltooid met waarschuwingen</target> - -<source>Warning</source> -<target>Waarschuwing</target> - <source>Nothing to synchronize</source> <target>Niets om te synchroniseren</target> -<source>Completed successfully</source> -<target>Succesvol voltooid</target> - <source>Executing command %x</source> <target>Opdracht %x uitvoeren</target> @@ -830,6 +830,9 @@ De opdracht wordt geactiveerd als: <source>Last sync</source> <target>Laatste synchronisatie</target> +<source>Log</source> +<target>Logboek</target> + <source>Folder</source> <target>Map</target> @@ -905,6 +908,9 @@ De opdracht wordt geactiveerd als: <source>Save as &batch job...</source> <target>Opslaan als &batchverwerking...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>Start &vergelijking</target> @@ -1303,6 +1309,9 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Alle permanent verborgen dialogen en waarschuwingsberichten opnieuw weergeven</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>Contextmenu aanpassen:</target> @@ -1393,6 +1402,15 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. <source>Highlight Configurations</source> <target>Markeer Configuraties</target> +<source>Info</source> +<target>Info</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Alles selecteren</target> + <source>&Options</source> <target>&Opties</target> @@ -1630,21 +1648,12 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. <source>Comparing content...</source> <target>Inhoud vergelijken...</target> -<source>Info</source> -<target>Info</target> - -<source>Select all</source> -<target>Alles selecteren</target> - <source>&Continue</source> <target>&Doorgaan</target> <source>Progress</source> <target>Voortgang</target> -<source>Log</source> -<target>Logboek</target> - <source>Thank you, %x, for your donation and support!</source> <target>Dank u, %x, voor uw donatie en ondersteuning!</target> @@ -1870,6 +1879,9 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. <source>Unable to register to receive system messages.</source> <target>Niet in staat om de ontvangen systeemberichten te registreren.</target> +<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>Cannot find system function %x.</source> <target>Kan de systeemfunctie %x niet vinden.</target> @@ -2026,9 +2038,6 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Kies voor een lokale installatie of selecteer een andere map voor de installatie.</target> -<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 Editie 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 5adc3a6d..23785c8e 100755 --- a/FreeFileSync/Build/Languages/english_uk.lng +++ b/FreeFileSync/Build/Languages/english_uk.lng @@ -7,6 +7,15 @@ <plural_definition>n == 1 ? 0 : 1</plural_definition> </header> +<source>No log entries</source> +<target></target> + +<source>Remove old log files after x days:</source> +<target></target> + +<source>Show &log</source> +<target></target> + <source>Both sides have changed since last synchronization.</source> <target>Both sides have changed since last synchronisation.</target> @@ -100,8 +109,8 @@ <source>global config file:</source> <target>global config file:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Any number of alternative directory pairs for at most one config file.</target> @@ -127,6 +136,9 @@ <source>The folders are created automatically when needed.</source> <target>The folders are created automatically when needed.</target> +<source>Scanning:</source> +<target>Scanning:</target> + <source>Comparison finished:</source> <target>Comparison finished:</target> @@ -331,6 +343,9 @@ <source>Update attributes on right</source> <target>Update attributes on right</target> +<source>Warning</source> +<target>Warning</target> + <source>Items processed:</source> <target>Elements processed:</target> @@ -340,6 +355,9 @@ <source>Total time:</source> <target>Total time:</target> +<source>Stopped</source> +<target>Stopped</target> + <source>Cleaning up log files:</source> <target>Cleaning up log files:</target> @@ -358,9 +376,6 @@ <pluralform>%x threads</pluralform> </target> -<source>Scanning:</source> -<target>Scanning:</target> - <source>Cannot read directory %x.</source> <target>Cannot read directory %x.</target> @@ -382,6 +397,15 @@ <source>Unable to connect to %x.</source> <target>Unable to connect to %x.</target> +<source>Completed successfully</source> +<target>Completed successfully</target> + +<source>Completed with warnings</source> +<target>Completed with warnings</target> + +<source>Completed with errors</source> +<target>Completed with errors</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Cannot access the Volume Shadow Copy Service.</target> @@ -678,8 +702,8 @@ Actual: %y bytes <source>3. Press 'Start'.</source> <target>3. Press 'Start'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>To get started just import a .ffs_batch file.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>To get started just import a "ffs_batch" file.</target> <source>Folders to watch:</source> <target>Folders to watch:</target> @@ -761,24 +785,9 @@ The command is triggered if: <source>System: Shut down</source> <target>System: Shut down</target> -<source>Stopped</source> -<target>Stopped</target> - -<source>Completed with errors</source> -<target>Completed with errors</target> - -<source>Completed with warnings</source> -<target>Completed with warnings</target> - -<source>Warning</source> -<target>Warning</target> - <source>Nothing to synchronize</source> <target>Nothing to synchronise</target> -<source>Completed successfully</source> -<target>Completed successfully</target> - <source>Executing command %x</source> <target>Executing command %x</target> @@ -830,6 +839,9 @@ The command is triggered if: <source>Last sync</source> <target>Last sync</target> +<source>Log</source> +<target>Log</target> + <source>Folder</source> <target>Folder</target> @@ -1393,6 +1405,12 @@ This guarantees a consistent state even in case of a serious error. <source>Highlight Configurations</source> <target>Highlight Configurations</target> +<source>Info</source> +<target>Info</target> + +<source>Select all</source> +<target>Select all</target> + <source>&Options</source> <target>&Options</target> @@ -1630,21 +1648,12 @@ This guarantees a consistent state even in case of a serious error. <source>Comparing content...</source> <target>Comparing content...</target> -<source>Info</source> -<target>Info</target> - -<source>Select all</source> -<target>Select all</target> - <source>&Continue</source> <target>&Continue</target> <source>Progress</source> <target>Progress</target> -<source>Log</source> -<target>Log</target> - <source>Thank you, %x, for your donation and support!</source> <target>Thank you, %x, for your donation and support!</target> @@ -1870,6 +1879,9 @@ This guarantees a consistent state even in case of a serious error. <source>Unable to register to receive system messages.</source> <target>Unable to register to receive system messages.</target> +<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>Cannot find system function %x.</source> <target>Cannot find system function %x.</target> @@ -2026,9 +2038,6 @@ This guarantees a consistent state even in case of a serious error. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Please choose the local installation type or select a different folder for installation.</target> -<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/french.lng b/FreeFileSync/Build/Languages/french.lng index 69c779fd..fe53e352 100755 --- a/FreeFileSync/Build/Languages/french.lng +++ b/FreeFileSync/Build/Languages/french.lng @@ -11,7 +11,7 @@ <target>Les deux côtés ont changé depuis la dernière synchronisation.</target> <source>Cannot determine sync-direction:</source> -<target>Impossible de déterminer le sens de la synchronisation :</target> +<target>Impossible de déterminer le sens de la synchronisation :</target> <source>No change since last synchronization.</source> <target>Aucun changement depuis la dernière synchronisation.</target> @@ -20,7 +20,7 @@ <target>L'entrée de la base de données n'est pas en phase compte tenu des paramètres actuels.</target> <source>Setting default synchronization directions: Old files will be overwritten with newer files.</source> -<target>Directions de la synchronisation par défaut : les anciens fichiers seront remplacés par les nouveaux.</target> +<target>Directions de la synchronisation par défaut : les anciens fichiers seront remplacés par les nouveaux.</target> <source>Creating file %x</source> <target>Création du fichier %x</target> @@ -50,10 +50,10 @@ <target>Suppression du lien symbolique %x</target> <source>Checking recycle bin availability for folder %x...</source> -<target>Contrôle de la disponibilité de la Corbeille pour le dossier %x ...</target> +<target>Contrôle de la disponibilité de la Corbeille pour le dossier %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>La corbeille n'est pas supportée par les dossiers suivants. Les fichiers supprimés ou modifiés ne pourront pas être restaurés :</target> +<target>La corbeille n'est pas supportée par les dossiers suivants. Les fichiers supprimés ou modifiés ne pourront pas être restaurés :</target> <source>An exception occurred</source> <target>Une erreur s'est produite</target> @@ -89,19 +89,19 @@ <target>Ligne de commande</target> <source>Syntax:</source> -<target>Syntaxe :</target> +<target>Syntaxe :</target> <source>config files:</source> -<target>fichier de configuration :</target> +<target>fichier de configuration :</target> <source>directory</source> <target>répertoire</target> <source>global config file:</source> -<target>fichier de configuration globale :</target> +<target>fichier de configuration globale :</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>N'importe quel nombre de fichiers FreeFileSync .ffs_gui et/ou .ffs_batch.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>N'importe quel nombre de fichiers FreeFileSync "ffs_gui" et/ou "ffs_batch".</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>N'importe quel nombre de paires de répertoires distincts pour au plus un fichier de configuration.</target> @@ -119,19 +119,19 @@ <target>Impossible de charger le fichier %x.</target> <source>Cannot find the following folders:</source> -<target>Impossible de trouver les dossiers suivants :</target> +<target>Impossible de trouver les dossiers suivants :</target> <source>The following folders do not yet exist:</source> -<target>Les dossiers suivants n'existent plus :</target> +<target>Les dossiers suivants n'existent plus :</target> <source>The folders are created automatically when needed.</source> <target>Les dossiers sont créés automatiquement quand cela est nécessaire.</target> <source>Scanning:</source> -<target>Lecture en cours :</target> +<target>Lecture en cours :</target> <source>Comparison finished:</source> -<target>Comparaison terminée :</target> +<target>Comparaison terminée :</target> <source> <pluralform>1 item found</pluralform> @@ -146,13 +146,13 @@ <target>Le fichier %x a une date invalide.</target> <source>Date:</source> -<target>Date :</target> +<target>Date :</target> <source>Files have the same date but a different size.</source> <target>Les fichiers ont la même date mais une taille différente.</target> <source>Size:</source> -<target>Taille :</target> +<target>Taille :</target> <source>Content comparison was skipped for excluded files.</source> <target>La comparaison des contenus est sautée pour les fichiers exclus.</target> @@ -167,7 +167,7 @@ <target>Comparaison du contenu des fichiers %x</target> <source>Generating file list...</source> -<target>Génération de la liste des fichiers...</target> +<target>Génération de la liste des fichiers ...</target> <source>Fail-safe file copy</source> <target>Copie de fichiers sécurisée</target> @@ -200,7 +200,7 @@ <target>Vérification de la copie des fichiers</target> <source>Using non-default global settings:</source> -<target>Utilisation des paramètres globaux particuliers :</target> +<target>Utilisation des paramètres globaux particuliers :</target> <source>A folder input field is empty.</source> <target>Un champ dossier est vide.</target> @@ -209,7 +209,7 @@ <target>Le dossier correspondant sera considéré comme vide.</target> <source>Exclude:</source> -<target>Exclure :</target> +<target>Exclure :</target> <source>One base folder of a folder pair is contained in the other one.</source> <target>Un dossier défini dans la paire des dossiers est contenu dans l'autre.</target> @@ -218,7 +218,7 @@ <target>Le dossier sera exclu de la synchronisation par le filtre.</target> <source>Calculating sync directions...</source> -<target>Evaluation du sens des synchronisations ...</target> +<target>Evaluation du sens des synchronisations ...</target> <source>Out of memory.</source> <target>Mémoire insuffisante.</target> @@ -227,13 +227,13 @@ <target>La base de données %x n'est pas compatible.</target> <source>Initial synchronization:</source> -<target>Première synchronisation :</target> +<target>Première synchronisation :</target> <source>Database file %x does not yet exist.</source> <target>La base de données %x n'existe plus.</target> <source>Database file is corrupted:</source> -<target>La base de données est abîmée :</target> +<target>La base de données est abîmée :</target> <source>Cannot write file %x.</source> <target>Impossible d'écrire le fichier %x.</target> @@ -245,13 +245,13 @@ <target>La base de données ne contient plus aujourd'hui d'informations sur la dernière synchronisation.</target> <source>Loading file %x...</source> -<target>Chargement du fichier %x ...</target> +<target>Chargement du fichier %x ...</target> <source>Saving file %x...</source> -<target>Enregistrement du fichier %x ...</target> +<target>Enregistrement du fichier %x ...</target> <source>Searching for folder %x...</source> -<target>Recherche du dossier %x ...</target> +<target>Recherche du dossier %x ...</target> <source>Timeout while searching for folder %x.</source> <target>La recherche du dossier %x a expirée.</target> @@ -263,13 +263,13 @@ <target>Impossible de lire les attributs du fichier %x.</target> <source>Waiting while directory is locked:</source> -<target>En attente tant que le répertoire est verrouillé :</target> +<target>En attente tant que le répertoire est verrouillé :</target> <source>Lock owner:</source> -<target>Propriétaire du verrou :</target> +<target>Propriétaire du verrou :</target> <source>Detecting abandoned lock...</source> -<target>Détection de verrouillage abandonné ...</target> +<target>Détection de verrouillage abandonné ...</target> <source> <pluralform>1 sec</pluralform> @@ -334,23 +334,29 @@ <source>Update attributes on right</source> <target>Mise à jour des attributs à droite</target> +<source>Warning</source> +<target>Attention</target> + <source>Items processed:</source> -<target>Élements traités :</target> +<target>Élements traités :</target> <source>Items remaining:</source> -<target>Élements restants :</target> +<target>Élements restants :</target> <source>Total time:</source> -<target>Durée totale :</target> +<target>Durée totale :</target> + +<source>Stopped</source> +<target>Arrêté</target> <source>Cleaning up log files:</source> -<target>Nettoyage des journaux :</target> +<target>Nettoyage des journaux :</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> <source>Cannot set directory locks for the following folders:</source> -<target>Impossible de verrouiller les répertoires des dossiers suivants :</target> +<target>Impossible de verrouiller les répertoires des dossiers suivants :</target> <source> <pluralform>1 thread</pluralform> @@ -382,6 +388,15 @@ <source>Unable to connect to %x.</source> <target>Impossible de se connecter à %x.</target> +<source>Completed successfully</source> +<target>Terminé avec succès</target> + +<source>Completed with warnings</source> +<target>Terminé avec observations</target> + +<source>Completed with errors</source> +<target>Terminé avec erreurs</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Impossible d'accéder au service Volume Shadow Copy.</target> @@ -416,7 +431,7 @@ <target>Personnaliser</target> <source>Multiple...</source> -<target>Multiple ...</target> +<target>Multiple ...</target> <source>Cannot write file attributes of %x.</source> <target>Impossible d'écrire les attributs de fichier de %x.</target> @@ -428,7 +443,7 @@ <target>%x et %y ont des contenus différents.</target> <source>Data verification error:</source> -<target>Erreur de contrôle des données :</target> +<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> @@ -455,13 +470,7 @@ <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> - -<source>Searching for excess file versions:</source> -<target>Recherche des versions de fichiers excédentaires :</target> - -<source>Removing excess file versions:</source> -<target>Suppression des versions de fichiers excédentaires :</target> +<target>Création d'un Volume Shadow Copy pour %x ...</target> <source>Cannot find folder %x.</source> <target>Dossier %x introuvable.</target> @@ -479,19 +488,19 @@ <target>Veuillez entrer le dossier destinataire pour la gestion des versions.</target> <source>The following items have unresolved conflicts and will not be synchronized:</source> -<target>Les éléments suivants sont en conflits non résolus et ne seront pas synchronisés :</target> +<target>Les éléments suivants sont en conflits non résolus et ne seront pas synchronisés :</target> <source>The following folders are significantly different. Please check that the correct folders are selected for synchronization.</source> <target>Les dossiers suivants sont assez différents. Veuillez contrôler que ce sont les bons dossiers qui sont sélectionnés pour la synchronisation.</target> <source>Not enough free disk space available in:</source> -<target>Espace disque insuffisant sur :</target> +<target>Espace disque insuffisant sur :</target> <source>Required:</source> -<target>Requis :</target> +<target>Requis :</target> <source>Available:</source> -<target>Disponible :</target> +<target>Disponible :</target> <source>Some files will be synchronized as part of multiple base folders.</source> <target>Certains fichiers seront synchronisés comme éléments de plusieurs dossiers de base.</target> @@ -500,22 +509,28 @@ <target>Pour éviter les conflits, configurez les filtres d'exclusion afin que chaque fichier mis à jour ne soit traité que par un seul dossier de base.</target> <source>Versioning folder:</source> -<target>Dossier de gestion des versions :</target> +<target>Dossier de gestion des versions :</target> <source>Base folder:</source> -<target>Dossier de base :</target> +<target>Dossier de base :</target> <source>The versioning folder is contained in a base folder.</source> <target>Le dossier de version est situé dans le dossier de base.</target> <source>Synchronizing folder pair:</source> -<target>Synchronisation de la paire de dossiers :</target> +<target>Synchronisation de la paire de dossiers :</target> <source>Generating database...</source> -<target>Génération de la base de données...</target> +<target>Génération de la base de données ...</target> + +<source>Searching for excess file versions:</source> +<target>Recherche des versions de fichiers excédentaires :</target> + +<source>Removing excess file versions:</source> +<target>Suppression des versions de fichiers excédentaires :</target> <source>Unable to create time stamp for versioning:</source> -<target>Impossible de générer l'horodatage pour la gestion des versions :</target> +<target>Impossible de générer l'horodatage pour la gestion des versions :</target> <source> Unexpected size of data stream. @@ -523,9 +538,9 @@ Expected: %x bytes Actual: %y bytes </source> <target> -Le flux de données a une taille inattendue : -Attendu : %x octets -Trouvé : %y octets +Le flux de données a une taille inattendue : +Attendu : %x octets +Trouvé : %y octets </target> <source>Cannot write permissions of %x.</source> @@ -577,7 +592,7 @@ Trouvé : %y octets <target>Impossible de trouver le périphérique %x.</target> <source>Type of item %x is not supported:</source> -<target>Le type de l'élément %x n'est pas accepté :</target> +<target>Le type de l'élément %x n'est pas accepté :</target> <source>Cannot delete symbolic link %x.</source> <target>Impossible de supprimer le lien symbolique %x.</target> @@ -586,7 +601,7 @@ Trouvé : %y octets <target>Impossible de calculer l'espace libre du disque %x.</target> <source>Incorrect command line:</source> -<target>Ligne de commande incorrecte :</target> +<target>Ligne de commande incorrecte :</target> <source>The server does not support authentication via %x.</source> <target>Le serveur refuse l'authentification par %x.</target> @@ -613,7 +628,7 @@ Trouvé : %y octets </target> <source>Active connections: %x</source> -<target>Connexions actives : %x</target> +<target>Connexions actives : %x</target> <source>Failed to open SFTP channel number %x.</source> <target>Impossible d'ouvrir le port SFTP %x.</target> @@ -646,10 +661,10 @@ Trouvé : %y octets <target>&Nouveau</target> <source>&Open...</source> -<target>&Ouvrir...</target> +<target>&Ouvrir ...</target> <source>Save &as...</source> -<target>S&auvegarder sous...</target> +<target>S&auvegarder sous ...</target> <source>E&xit</source> <target>&Quitter</target> @@ -667,7 +682,7 @@ Trouvé : %y octets <target>&Aide</target> <source>Usage:</source> -<target>Utilisation :</target> +<target>Utilisation :</target> <source>1. Select folders to watch.</source> <target>1. Choisir les dossiers à examiner.</target> @@ -678,11 +693,11 @@ Trouvé : %y octets <source>3. Press 'Start'.</source> <target>3. Cliquez sur 'Démarrer'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Pour démarrer, il faut importer un fichier .ffs_batch.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Pour démarrer, il faut importer un fichier "ffs_batch".</target> <source>Folders to watch:</source> -<target>Dossiers à surveiller :</target> +<target>Dossiers à surveiller :</target> <source>Add folder</source> <target>Ajout d'un dossier</target> @@ -694,13 +709,13 @@ Trouvé : %y octets <target>Parcourir</target> <source>Idle time (in seconds):</source> -<target>Durée d'inactivité (en secondes) :</target> +<target>Durée d'inactivité (en secondes) :</target> <source>Idle time between last detected change and execution of command</source> <target>Délai entre le dernier changement détecté et la dernière exécution de la commande</target> <source>Command line:</source> -<target>Ligne de commande :</target> +<target>Ligne de commande :</target> <source> The command is triggered if: @@ -708,9 +723,9 @@ The command is triggered if: - new folders arrive (e.g. USB stick insert) </source> <target> -La commande est déclenchée si : +La commande est déclenchée si : - un fichier ou un dossier est modifié -- un nouveau dossier apparait (par exemple : insertion d'une clé USB) +- un nouveau dossier apparait (par exemple : insertion d'une clé USB) </target> <source>Start</source> @@ -720,7 +735,7 @@ La commande est déclenchée si : <target>A propos de</target> <source>Build: %x</source> -<target>Généré : %x</target> +<target>Généré : %x</target> <source>All files</source> <target>Tous les fichiers</target> @@ -729,13 +744,13 @@ La commande est déclenchée si : <target>Synchronisation Automatique</target> <source>The %x protocol does not support directory monitoring:</source> -<target>Le protocole %x ne supporte pas le contrôle d'anuaire :</target> +<target>Le protocole %x ne supporte pas le contrôle d'anuaire :</target> <source>Directory monitoring active</source> <target>Gestion des répertoires active</target> <source>Waiting until directory is available:</source> -<target>En attente de la disponibilité du répertoire :</target> +<target>En attente de la disponibilité du répertoire :</target> <source>&Restore</source> <target>&Restaurer</target> @@ -750,35 +765,20 @@ La commande est déclenchée si : <target>&Réessayer</target> <source>Loading...</source> -<target>Chargement ...</target> +<target>Chargement ...</target> <source>job name</source> <target>nom du job</target> <source>System: Sleep</source> -<target>Système : Mise en veille</target> +<target>Système : Mise en veille</target> <source>System: Shut down</source> -<target>Système : Arrêt</target> - -<source>Stopped</source> -<target>Arrêté</target> - -<source>Completed with errors</source> -<target>Terminé avec erreurs</target> - -<source>Completed with warnings</source> -<target>Terminé avec observations</target> - -<source>Warning</source> -<target>Attention</target> +<target>Système : Arrêt</target> <source>Nothing to synchronize</source> <target>Rien à synchroniser</target> -<source>Completed successfully</source> -<target>Terminé avec succès</target> - <source>Executing command %x</source> <target>Exécution de la commande %x</target> @@ -804,7 +804,7 @@ La commande est déclenchée si : <target>&Tout ignorer</target> <source>Retrying operation...</source> -<target>Opération retentée ...</target> +<target>Opération retentée ...</target> <source>Serious Error</source> <target>Erreur Grave</target> @@ -830,6 +830,9 @@ La commande est déclenchée si : <source>Last sync</source> <target>Dernière synchro</target> +<source>Log</source> +<target>Log</target> + <source>Folder</source> <target>Dossier</target> @@ -903,7 +906,10 @@ La commande est déclenchée si : <target>&Sauvegarder</target> <source>Save as &batch job...</source> -<target>Enregistrer en tant que fichier de &commandes ...</target> +<target>Enregistrer en tant que fichier de &commandes ...</target> + +<source>Show &log</source> +<target></target> <source>Start &comparison</source> <target>Démarrer la &comparaison</target> @@ -930,10 +936,10 @@ La commande est déclenchée si : <target>&Langue</target> <source>&Find...</source> -<target>&Rechercher...</target> +<target>&Rechercher ...</target> <source>&Export file list...</source> -<target>&Exportation de la liste des fichiers ...</target> +<target>&Exportation de la liste des fichiers ...</target> <source>&Reset layout</source> <target>&Réinitialiser la disposition</target> @@ -972,7 +978,7 @@ La commande est déclenchée si : <target>Fermer la barre de recherche</target> <source>Find:</source> -<target>Rechercher :</target> +<target>Rechercher :</target> <source>Match case</source> <target>Respect de la casse</target> @@ -981,22 +987,22 @@ La commande est déclenchée si : <target>Nouveau</target> <source>Open...</source> -<target>Ouvrir ...</target> +<target>Ouvrir ...</target> <source>Save</source> <target>Sauvegarder</target> <source>Save as...</source> -<target>Sauvegarder sous ...</target> +<target>Sauvegarder sous ...</target> <source>View type:</source> -<target>Type de vue :</target> +<target>Type de vue :</target> <source>Select view:</source> -<target>Choisir la vue :</target> +<target>Choisir la vue :</target> <source>Statistics:</source> -<target>Statistiques :</target> +<target>Statistiques :</target> <source>Number of files and folders that will be deleted</source> <target>Nombre de fichiers et de dossiers qui seront supprimés</target> @@ -1014,19 +1020,19 @@ La commande est déclenchée si : <target>Coordonne la paire de dossiers</target> <source>Folder pair:</source> -<target>Paire de dossiers :</target> +<target>Paire de dossiers :</target> <source>Main settings:</source> -<target>Paramètres principaux :</target> +<target>Paramètres principaux :</target> <source>Use local settings:</source> -<target>Utiliser les paramètres locaux :</target> +<target>Utiliser les paramètres locaux :</target> <source>Select a variant:</source> -<target>Choisir une variante :</target> +<target>Choisir une variante :</target> <source>Include &symbolic links:</source> -<target>Inclure les liens &symboliques :</target> +<target>Inclure les liens &symboliques :</target> <source>&Follow</source> <target>&Poursuivre</target> @@ -1044,25 +1050,25 @@ La commande est déclenchée si : <target>Liste des décalages de temps UTC à ignorer</target> <source>Example:</source> -<target>Exemple :</target> +<target>Exemple :</target> <source>Handle daylight saving time</source> <target>Gérer l'heure d'été</target> <source>Performance improvements:</source> -<target>Amélioration des performances :</target> +<target>Amélioration des performances :</target> <source>Parallel file operations:</source> -<target>Opérations sur les fichiers parallèles :</target> +<target>Opérations sur les fichiers parallèles :</target> <source>How to get best performance?</source> -<target>Comment améliorer les performances ?</target> +<target>Comment améliorer les performances ?</target> <source>Local settings:</source> -<target>Paramètres locaux :</target> +<target>Paramètres locaux :</target> <source>Include:</source> -<target>Inclure :</target> +<target>Inclure :</target> <source>Show examples</source> <target>Afficher les exemples</target> @@ -1071,16 +1077,16 @@ La commande est déclenchée si : <target>Choisissez les règles de filtrage pour exclure certains fichiers de la synchronisation. Entrez les chemins des fichiers par rapport à leur paire de dossiers.</target> <source>File size:</source> -<target>Taille du fichier :</target> +<target>Taille du fichier :</target> <source>Minimum:</source> -<target>Minimum :</target> +<target>Minimum :</target> <source>Maximum:</source> -<target>Maximum :</target> +<target>Maximum :</target> <source>Time span:</source> -<target>Intervalle de temps :</target> +<target>Intervalle de temps :</target> <source>C&lear</source> <target>&Effacer</target> @@ -1100,7 +1106,7 @@ La commande est déclenchée si : </target> <source>Delete files:</source> -<target>Supprimer les fichiers :</target> +<target>Supprimer les fichiers :</target> <source>&Recycle bin</source> <target>&Corbeille</target> @@ -1115,43 +1121,43 @@ La commande est déclenchée si : <target>Déplacer les fichiers vers un dossier utilisateur</target> <source>Naming convention:</source> -<target>Convention de nommage :</target> +<target>Convention de nommage :</target> <source>Limit file versions:</source> -<target>Limiter les versions de fichiers :</target> +<target>Limiter les versions de fichiers :</target> <source>Last x days:</source> -<target>Derniers x jours :</target> +<target>Derniers x jours :</target> <source>Ignore errors</source> <target>Ignorer les erreurs</target> <source>Retry count:</source> -<target>Nombre de tentatives :</target> +<target>Nombre de tentatives :</target> <source>Delay (in seconds):</source> -<target>Délai (en secondes) :</target> +<target>Délai (en secondes) :</target> <source>Run a command after synchronization:</source> -<target>Exécuter une commande après la synchronisation :</target> +<target>Exécuter une commande après la synchronisation :</target> <source>OK</source> <target>OK</target> <source>Enter your login details:</source> -<target>Entrez vos identifiants :</target> +<target>Entrez vos identifiants :</target> <source>Connection type:</source> -<target>Type de connexion :</target> +<target>Type de connexion :</target> <source>Server name or IP address:</source> -<target>Nom du serveur ou adresse IP :</target> +<target>Nom du serveur ou adresse IP :</target> <source>Port:</source> -<target>Port :</target> +<target>Port :</target> <source>Encryption:</source> -<target>Cryptage :</target> +<target>Cryptage :</target> <source>&Disabled</source> <target>&Désactivé</target> @@ -1160,7 +1166,7 @@ La commande est déclenchée si : <target>&Explicite SSL/TLS</target> <source>Authentication:</source> -<target>Authentication :</target> +<target>Authentication :</target> <source>&Password</source> <target>Mot de &Passe</target> @@ -1172,46 +1178,46 @@ La commande est déclenchée si : <target>Agent &SSH</target> <source>User name:</source> -<target>Nom de l'utilisateur :</target> +<target>Nom de l'utilisateur :</target> <source>Private key file:</source> -<target>Fichier clé personnel :</target> +<target>Fichier clé personnel :</target> <source>&Show password</source> <target>&Afficher le mot de passe</target> <source>Directory on server:</source> -<target>Répertoire sur le serveur :</target> +<target>Répertoire sur le serveur :</target> <source>SFTP channels per connection:</source> -<target>Ports SFTP par connexion :</target> +<target>Ports SFTP par connexion :</target> <source>Detect server limit</source> <target>Détection des limites du serveur</target> <source>Select a directory on the server:</source> -<target>Choisir un répertoire sur le serveur :</target> +<target>Choisir un répertoire sur le serveur :</target> <source>Select Folder</source> <target>Choisir un Dossier</target> <source>Start synchronization now?</source> -<target>Démarrer la synchronisation maintenant ?</target> +<target>Démarrer la synchronisation maintenant ?</target> <source>Variant:</source> -<target>Variante :</target> +<target>Variante :</target> <source>&Don't show this dialog again</source> <target>&Ne plus afficher cette boîte de dialogue</target> <source>Items found:</source> -<target>Élements trouvés :</target> +<target>Élements trouvés :</target> <source>Time remaining:</source> -<target>Temps restant :</target> +<target>Temps restant :</target> <source>Time elapsed:</source> -<target>Temps écoulé :</target> +<target>Temps écoulé :</target> <source>Bytes</source> <target>Octets</target> @@ -1220,13 +1226,13 @@ La commande est déclenchée si : <target>Eléments</target> <source>Synchronizing...</source> -<target>Synchronisation en cours ...</target> +<target>Synchronisation en cours ...</target> <source>Minimize to notification area</source> <target>Réduction à la zone de notification</target> <source>When finished:</source> -<target>A la fin :</target> +<target>A la fin :</target> <source>Auto-close</source> <target>Fermeture automatique</target> @@ -1241,7 +1247,7 @@ La commande est déclenchée si : <target>Arrêt</target> <source>Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x</source> -<target>Créer un fichier batch pour une synchronisation non assitée. Pour démarrer, double-cliquez sur ce fichier ou planifiez-le dans le gestionnaire des tâches : %x</target> +<target>Créer un fichier batch pour une synchronisation non assitée. Pour démarrer, double-cliquez sur ce fichier ou planifiez-le dans le gestionnaire des tâches : %x</target> <source>Progress dialog:</source> <target>Fenêtre de progression:</target> @@ -1262,13 +1268,13 @@ La commande est déclenchée si : <target>Arrêter la synchronisation à la première erreur</target> <source>Save log:</source> -<target>Sauvegarde du fichier log :</target> +<target>Sauvegarde du fichier log :</target> <source>Limit number of log files:</source> -<target>Limiter le nombre de journaux :</target> +<target>Limiter le nombre de journaux :</target> <source>How can I schedule a batch job?</source> -<target>Comment planifier un fichier de commandes ?</target> +<target>Comment planifier un fichier de commandes ?</target> <source>&Keep relative paths</source> <target>&Conserver les chemins relatifs</target> @@ -1303,8 +1309,11 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Réafficher en permanence les boîtes de dialogue et les avertissements</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> -<target>Personnaliser le menu contextuel :</target> +<target>Personnaliser le menu contextuel :</target> <source>Description</source> <target>Description</target> @@ -1325,7 +1334,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <target>Email</target> <source>If you like FreeFileSync:</source> -<target>Si vous aimez FreeFileSync :</target> +<target>Si vous aimez FreeFileSync :</target> <source>Support with a donation</source> <target>Soutenir avec un don</target> @@ -1337,37 +1346,37 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <target>Détails pour les donations</target> <source>Source code written in C++ using:</source> -<target>Code source écrit en C++ utilisant :</target> +<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> <source>Many thanks for localization:</source> -<target>Un grand merci pour la traduction à :</target> +<target>Un grand merci pour la traduction à :</target> <source>Activate the FreeFileSync Donation Edition by one of the following methods:</source> -<target>Activez la FreeFileSync Donation Edition par l'une des méthodes suivantes :</target> +<target>Activez la FreeFileSync Donation Edition par l'une des méthodes suivantes :</target> <source>1. Activate via internet now:</source> -<target>1. Activez par internet maintenant :</target> +<target>1. Activez par internet maintenant :</target> <source>Activate online</source> <target>Activez en ligne</target> <source>2. Retrieve an offline activation key from the following URL:</source> -<target>2. Récupérer une clé d'activation hors ligne à partir de l'URL suivante :</target> +<target>2. Récupérer une clé d'activation hors ligne à partir de l'URL suivante :</target> <source>&Copy to clipboard</source> <target>& Copie dans le presse-papiers</target> <source>Enter activation key:</source> -<target>Entrez la clé d'activation :</target> +<target>Entrez la clé d'activation :</target> <source>Activate offline</source> <target>Activez hors ligne</target> <source>Highlight configurations that have not been run for more than the following number of days:</source> -<target>Signaler les configurations non exécutées depuis le nombre de jours suivant :</target> +<target>Signaler les configurations non exécutées depuis le nombre de jours suivant :</target> <source>Synchronization Settings</source> <target>Configuration de la Synchronisation</target> @@ -1393,6 +1402,15 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <source>Highlight Configurations</source> <target>Signaler les configurations</target> +<source>Info</source> +<target>Info</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Tout sélectionner</target> + <source>&Options</source> <target>&Options</target> @@ -1421,7 +1439,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <target>&Afficher les détails</target> <source>FreeFileSync %x is available!</source> -<target>FreeFileSync %x est disponible !</target> +<target>FreeFileSync %x est disponible !</target> <source>Local path not available for %x.</source> <target>Chemin local invalide pour %x.</target> @@ -1434,8 +1452,8 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <pluralform>Do you really want to execute the command %y for %x items?</pluralform> </source> <target> -<pluralform>Êtes-vous sûr de vouloir exécuter la commande %y pour %x élément ?</pluralform> -<pluralform>Êtes-vous sûr de vouloir exécuter la commande %y pour %x éléments ?</pluralform> +<pluralform>Êtes-vous sûr de vouloir exécuter la commande %y pour %x élément ?</pluralform> +<pluralform>Êtes-vous sûr de vouloir exécuter la commande %y pour %x éléments ?</pluralform> </target> <source>&Execute</source> @@ -1469,16 +1487,16 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. </target> <source>Set direction:</source> -<target>Choix de la direction :</target> +<target>Choix de la direction :</target> <source>multiple selection</source> <target>sélection multiple</target> <source>Include via filter:</source> -<target>Inclure à l'aide du filtre :</target> +<target>Inclure à l'aide du filtre :</target> <source>Exclude via filter:</source> -<target>Exclure à l'aide du filtre :</target> +<target>Exclure à l'aide du filtre :</target> <source>Include temporarily</source> <target>Inclure temporairement</target> @@ -1487,7 +1505,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <target>Exclure temporairement</target> <source>&Copy to...</source> -<target>&Copier vers ...</target> +<target>&Copier vers ...</target> <source>&Delete</source> <target>&Supprimer</target> @@ -1499,7 +1517,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <target>Exclure tout</target> <source>Show icons:</source> -<target>Afficher les icônes :</target> +<target>Afficher les icônes :</target> <source>Small</source> <target>Petit</target> @@ -1511,7 +1529,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <target>Grand</target> <source>Select time span...</source> -<target>Choisir un intervalle de temps ...</target> +<target>Choisir un intervalle de temps ...</target> <source>Donation Edition</source> <target>Edition Donation</target> @@ -1526,7 +1544,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <target>FreeFileSync batch</target> <source>Do you want to save changes to %x?</source> -<target>Voulez-vous enregistrer les modifications dans %x ?</target> +<target>Voulez-vous enregistrer les modifications dans %x ?</target> <source>Never save &changes</source> <target>Ne jamais sauvegarder les &modifications</target> @@ -1538,7 +1556,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <target>Masquer la configuration</target> <source>Highlight...</source> -<target>Signaler ...</target> +<target>Signaler ...</target> <source>Clear filter</source> <target>Effacer les filtres</target> @@ -1613,28 +1631,22 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <target>Liste des fichiers exportée</target> <source>Searching for program updates...</source> -<target>Recherche de mises à jour ...</target> +<target>Recherche de mises à jour ...</target> <source>Paused</source> <target>En pause</target> <source>Stop requested...</source> -<target>Arrêt demandé ...</target> +<target>Arrêt demandé ...</target> <source>Initializing...</source> -<target>Initialisation ...</target> +<target>Initialisation ...</target> <source>Scanning...</source> -<target>Lecture en cours ...</target> +<target>Lecture en cours ...</target> <source>Comparing content...</source> -<target>Comparaison du contenu ...</target> - -<source>Info</source> -<target>Info</target> - -<source>Select all</source> -<target>Tout sélectionner</target> +<target>Comparaison du contenu ...</target> <source>&Continue</source> <target>&Continuer</target> @@ -1642,23 +1654,20 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <source>Progress</source> <target>Progression</target> -<source>Log</source> -<target>Log</target> - <source>Thank you, %x, for your donation and support!</source> -<target>Merci %x pour votre don et votre aide !</target> +<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> +<target>Plage recommandée :</target> <source>Password:</source> -<target>Mot de passe :</target> +<target>Mot de passe :</target> <source>Key password:</source> -<target>Clé du mot de passe :</target> +<target>Clé du mot de passe :</target> <source>Please enter a file path.</source> <target>Veuillez entrer un chemin d'accès.</target> @@ -1668,8 +1677,8 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <pluralform>Copy the following %x items to another folder?</pluralform> </source> <target> -<pluralform>Copier %x élément dans un autre dossier ?</pluralform> -<pluralform>Copier les %x éléments suivants dans un autre dossier ?</pluralform> +<pluralform>Copier %x élément dans un autre dossier ?</pluralform> +<pluralform>Copier les %x éléments suivants dans un autre dossier ?</pluralform> </target> <source>Please enter a target folder.</source> @@ -1680,8 +1689,8 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <pluralform>Do you really want to move the following %x items to the recycle bin?</pluralform> </source> <target> -<pluralform>Etes-vous sûr de vouloir mettre à la Corbeille %x élément suivant ?</pluralform> -<pluralform>Etes-vous sûr de vouloir mettre à la Corbeille les %x éléments suivants ?</pluralform> +<pluralform>Etes-vous sûr de vouloir mettre à la Corbeille %x élément suivant ?</pluralform> +<pluralform>Etes-vous sûr de vouloir mettre à la Corbeille les %x éléments suivants ?</pluralform> </target> <source>Move</source> @@ -1692,15 +1701,15 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <pluralform>Do you really want to delete the following %x items?</pluralform> </source> <target> -<pluralform>Voulez-vous vraiment supprimer %x élément ?</pluralform> -<pluralform>Voulez-vous vraiment supprimer ces %x éléments ?</pluralform> +<pluralform>Voulez-vous vraiment supprimer %x élément ?</pluralform> +<pluralform>Voulez-vous vraiment supprimer ces %x éléments ?</pluralform> </target> <source>Copy DACL, SACL, Owner, Group</source> <target>Copie DACL, SACL, propriétaire et groupe</target> <source>Integrate external applications into context menu. The following macros are available:</source> -<target>Inclure les applications externes dans le menu contextuel. Les macros suivantes sont disponibles :</target> +<target>Inclure les applications externes dans le menu contextuel. Les macros suivantes sont disponibles :</target> <source>Full file or folder path</source> <target>Chemin complet du fichier ou du dossier</target> @@ -1721,7 +1730,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <target>Afficher toutes les boîtes de dialogue</target> <source>Downloading update...</source> -<target>Téléchargement de la mise à jour ...</target> +<target>Téléchargement de la mise à jour ...</target> <source>Identify equal files by comparing modification time and size.</source> <target>Reconnaître les fichiers identiques à l'aide de leur taille et de leur date.</target> @@ -1793,13 +1802,13 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <target>Ajouter un horodatage à chaque nom de fichier</target> <source>On completion:</source> -<target>A la fin :</target> +<target>A la fin :</target> <source>On errors:</source> -<target>En cas d'erreur :</target> +<target>En cas d'erreur :</target> <source>On success:</source> -<target>En cas de succès :</target> +<target>En cas de succès :</target> <source>Main config</source> <target>Configuration principale</target> @@ -1826,13 +1835,13 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <target>Impossible de retrouver les informations de mise à jour.</target> <source>Automatic updates:</source> -<target>Mise à jour automatique :</target> +<target>Mise à jour automatique :</target> <source>Check for Program Updates</source> <target>Recherche des Mises à Jour</target> <source>Auto-update now or download manually from the FreeFileSync home page?</source> -<target>Mise à jour automatique maintenant ou téléchargement manuel à partir du site de FreeFileSync ?</target> +<target>Mise à jour automatique maintenant ou téléchargement manuel à partir du site de FreeFileSync ?</target> <source>&Auto-update</source> <target>Mise à jour &automatique</target> @@ -1841,7 +1850,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <target>&Téléchargement</target> <source>Download now?</source> -<target>Télécharger maintenant ?</target> +<target>Télécharger maintenant ?</target> <source>&Download</source> <target>&Télécharger</target> @@ -1850,7 +1859,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <target>FreeFileSync est à jour.</target> <source>Cannot find current FreeFileSync version number online. A newer version is likely available. Check manually now?</source> -<target>Le numéro de la version courante de FreeFileSync est introuvable sur internet. Une nouvelle version est probablement disponible. Voulez-vous vérifier maintenant ?</target> +<target>Le numéro de la version courante de FreeFileSync est introuvable sur internet. Une nouvelle version est probablement disponible. Voulez-vous vérifier maintenant ?</target> <source>&Check</source> <target>&Contrôle</target> @@ -1870,6 +1879,9 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <source>Unable to register to receive system messages.</source> <target>Impossible d'enregistrer la réception des messages système.</target> +<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>Cannot find system function %x.</source> <target>Impossible de trouver la fonction système %x.</target> @@ -1880,7 +1892,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <target>Impossible de gérer le répertoire %x.</target> <source>The file is locked by another process:</source> -<target>Le fichier est verrouillé par un autre process :</target> +<target>Le fichier est verrouillé par un autre process :</target> <source>Cannot read security context of %x.</source> <target>Impossible de lire les paramètres de sécurité de %x.</target> @@ -1943,7 +1955,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <target>Échec de la vérification de la Corbeille pour le dossier %x.</target> <source>The following XML elements could not be read:</source> -<target>Les éléments XML suivants ne peuvent être lus :</target> +<target>Les éléments XML suivants ne peuvent être lus :</target> <source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> <target>Le fichier de configuration %x est incomplet. Les éléments manquants sont fixés à leur valeur par défaut.</target> @@ -1955,7 +1967,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <target>Sélectionnez les composants que vous voulez installer.</target> <source>Select installation type:</source> -<target>Choisir le type d'installation :</target> +<target>Choisir le type d'installation :</target> <source>Local</source> <target>Locale</target> @@ -1982,10 +1994,10 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <target>Copier seulement les fichiers</target> <source>Choose a directory for installation:</source> -<target>Choisir le répertoire d'installation :</target> +<target>Choisir le répertoire d'installation :</target> <source>Create shortcuts:</source> -<target>Création de raccourcis sur :</target> +<target>Création de raccourcis sur :</target> <source>Desktop</source> <target>le Bureau</target> @@ -2026,9 +2038,6 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Veuillez choisir le type d'installation locale ou sélectionner un autre dossier pour cette installation.</target> -<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 9739cf40..51d22174 100755 --- a/FreeFileSync/Build/Languages/german.lng +++ b/FreeFileSync/Build/Languages/german.lng @@ -7,14 +7,11 @@ <plural_definition>n == 1 ? 0 : 1</plural_definition> </header> -<source>No log entries</source> -<target>Keine Protokolleinträge</target> - -<source>Remove old log files after x days:</source> -<target>Alte Protokolldateien nach x Tagen entfernen:</target> +<source>Default log path:</source> +<target>Standardprotokollpfad:</target> -<source>Show &log</source> -<target>&Protokoll zeigen</target> +<source>&Override default log path:</source> +<target>&Standardprotokollpfad überschreiben:</target> <source>Both sides have changed since last synchronization.</source> <target>Beide Seiten wurden seit der letzten Synchronisation verändert.</target> @@ -109,8 +106,8 @@ <source>global config file:</source> <target>Globale Konfigurationsdatei:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>Beliebige Anzahl von FreeFileSync .ffs_gui und/oder .ffs_batch Konfigurationsdateien.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>Beliebige Anzahl von FreeFileSync "ffs_gui" und/oder "ffs_batch" Konfigurationsdateien.</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Beliebige Anzahl von alternativen Verzeichnispaaren für maximal eine Konfigurationsdatei.</target> @@ -642,24 +639,6 @@ Tatsächlich: %y bytes <source>Failed to open SFTP channel number %x.</source> <target>Der SFTP Kanal Nummer %x konnte nicht geöffnet werden.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>1 Byte</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>Drag && drop</source> <target>Drag && Drop</target> @@ -702,8 +681,8 @@ Tatsächlich: %y bytes <source>3. Press 'Start'.</source> <target>3. 'Start' drücken.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Zum Schnelleinstieg einfach eine .ffs_batch Datei importieren.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Zum Schnelleinstieg einfach eine "ffs_batch" Datei importieren.</target> <source>Folders to watch:</source> <target>Zu überwachende Ordner:</target> @@ -773,6 +752,24 @@ Die Befehlszeile wird ausgelöst, wenn: <source>&Retry</source> <target>&Wiederholen</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>1 Byte</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>Loading...</source> <target>Lade...</target> @@ -917,6 +914,9 @@ Die Befehlszeile wird ausgelöst, wenn: <source>Save as &batch job...</source> <target>Speichern als Batch&auftrag...</target> +<source>Show &log</source> +<target>&Protokoll zeigen</target> + <source>Start &comparison</source> <target>&Vergleich starten</target> @@ -1061,6 +1061,15 @@ Die Befehlszeile wird ausgelöst, wenn: <source>Handle daylight saving time</source> <target>Sommerzeit berücksichtigen</target> +<source>Ignore errors</source> +<target>Fehler ignorieren</target> + +<source>Retry count:</source> +<target>Wiederholungen:</target> + +<source>Delay (in seconds):</source> +<target>Verzögerung (in Sekunden):</target> + <source>Performance improvements:</source> <target>Leistungsverbesserungen:</target> @@ -1135,15 +1144,6 @@ Die Befehlszeile wird ausgelöst, wenn: <source>Last x days:</source> <target>Letzte x Tage:</target> -<source>Ignore errors</source> -<target>Fehler ignorieren</target> - -<source>Retry count:</source> -<target>Wiederholungen:</target> - -<source>Delay (in seconds):</source> -<target>Verzögerung (in Sekunden):</target> - <source>Run a command after synchronization:</source> <target>Befehl nach Synchronisation ausführen:</target> @@ -1273,12 +1273,6 @@ Die Befehlszeile wird ausgelöst, wenn: <source>Stop synchronization at first error</source> <target>Synchronisation beim ersten Fehler stoppen</target> -<source>Save log:</source> -<target>Protokoll speichern:</target> - -<source>Limit number of log files:</source> -<target>Anzahl der Protokolldateien begrenzen:</target> - <source>How can I schedule a batch job?</source> <target>Wie können Batchaufträge in den Taskplaner eingetragen werden?</target> @@ -1315,6 +1309,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Alle dauerhaft versteckten Fenster und Warnmeldungen wieder anzeigen</target> +<source>Remove old log files after x days:</source> +<target>Alte Protokolldateien nach x Tagen entfernen:</target> + <source>Customize context menu:</source> <target>Kontextmenü anpassen:</target> @@ -1408,6 +1405,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. <source>Info</source> <target>Info</target> +<source>No log entries</source> +<target>Keine Protokolleinträge</target> + <source>Select all</source> <target>Alle auswählen</target> @@ -1879,6 +1879,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. <source>Unable to register to receive system messages.</source> <target>Die Registrierung zum Empfang von Systemmeldungen ist fehlgeschlagen.</target> +<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>Cannot find system function %x.</source> <target>Die Systemfunktion %x wurde nicht gefunden.</target> @@ -2035,9 +2038,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Bitte wählen Sie den lokalen Installationstyp oder einen anderen Ordner für die Installation.</target> -<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 ffb88275..c09db39b 100755 --- a/FreeFileSync/Build/Languages/greek.lng +++ b/FreeFileSync/Build/Languages/greek.lng @@ -100,8 +100,8 @@ <source>global config file:</source> <target>αρχείο γενικών ρυθμίσεων:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>Οποιοσδήποτε αριθμός από αρχεία παραμέτρων .ffs_gui ή/και .ffs_batch του FreeFileSync.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>Οποιοσδήποτε αριθμός από αρχεία παραμέτρων "ffs_gui" ή/και "ffs_batch" του FreeFileSync.</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Οποιοσδήποτε αριθμός από εναλλακτικά ζεύγη υποκαταλόγων για το πολύ ένα αρχείο ρυθμίσεων.</target> @@ -334,6 +334,9 @@ <source>Update attributes on right</source> <target>Ενημέρωση των χαρακτηριστικών στα δεξιά</target> +<source>Warning</source> +<target>Προειδοποίηση</target> + <source>Items processed:</source> <target>Επεξεργάστηκαν στοιχεία:</target> @@ -343,6 +346,9 @@ <source>Total time:</source> <target>Συνολική διάρκεια:</target> +<source>Stopped</source> +<target>Διακοπή</target> + <source>Cleaning up log files:</source> <target>Καθάρισμα αρχείων καταγραφής:</target> @@ -382,6 +388,15 @@ <source>Unable to connect to %x.</source> <target>Δεν μπορεί να πραγματοποιηθεί σύνδεση με το %x.</target> +<source>Completed successfully</source> +<target>Ολοκληρώθηκε επιτυχώς</target> + +<source>Completed with warnings</source> +<target>Ολοκληρώθηκε με προειδοποιήσεις</target> + +<source>Completed with errors</source> +<target>Ολοκληρώθηκε με σφάλματα</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Δεν είναι δυνατή η πρόσβαση στην Υπηρεσία Σκιώδους Αντίγραφου Τόμου.</target> @@ -678,8 +693,8 @@ Actual: %y bytes <source>3. Press 'Start'.</source> <target>3. Πατήστε το 'Έναρξη'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Για να ξεκινήσετε μπορείτε απλά να εισάγετε ένα αρχείο .ffs_batch.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Για να ξεκινήσετε μπορείτε απλά να εισάγετε ένα αρχείο "ffs_batch".</target> <source>Folders to watch:</source> <target>Φάκελοι για παρακολούθηση:</target> @@ -761,24 +776,9 @@ The command is triggered if: <source>System: Shut down</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> @@ -830,6 +830,9 @@ The command is triggered if: <source>Last sync</source> <target>Τελ. συγχρ.</target> +<source>Log</source> +<target>Καταγραφή</target> + <source>Folder</source> <target>Φάκελος</target> @@ -905,6 +908,9 @@ The command is triggered if: <source>Save as &batch job...</source> <target>Αποθήκευση ως δέσ&μη ενεργειών...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>Έ&ναρξη σύγκρισης</target> @@ -1303,6 +1309,9 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Επανεμφάνιση όλων των μόνιμα κρυμμένων ειδοποιήσεων και προειδοποιητικών μηνυμάτων</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>Προσαρμογή μενού περιβάλλοντος:</target> @@ -1393,6 +1402,15 @@ This guarantees a consistent state even in case of a serious error. <source>Highlight Configurations</source> <target>Επισήμανση Παραμέτρων</target> +<source>Info</source> +<target>Πληροφορίες</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Επιλογή όλων</target> + <source>&Options</source> <target>&Επιλογές</target> @@ -1630,21 +1648,12 @@ This guarantees a consistent state even in case of a serious error. <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> @@ -1870,6 +1879,9 @@ This guarantees a consistent state even in case of a serious error. <source>Unable to register to receive system messages.</source> <target>Δεν μπορεί να καταχωρηθεί η λήψη μηνυμάτων συστήματος.</target> +<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source> +<target>Η επιλογή εγκατάστασης %x είναι διαθέσιμη μόνο στην Έκδοση Δωρητή του FreeFileSync.</target> + <source>Cannot find system function %x.</source> <target>Δεν μπορεί να βρεθεί η λειτουργία συστήματος %x.</target> @@ -2026,9 +2038,6 @@ This guarantees a consistent state even in case of a serious error. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Παρακαλούμε επιλέξτε την τοπική εγκατάσταση ή επιλέξτε έναν διαφορετικό φάκελο για εγκατάσταση.</target> -<source>The %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 261eed73..7885afd1 100755 --- a/FreeFileSync/Build/Languages/hebrew.lng +++ b/FreeFileSync/Build/Languages/hebrew.lng @@ -7,6 +7,15 @@ <plural_definition>n == 1 ? 0 : 1</plural_definition> </header> +<source>No log entries</source> +<target></target> + +<source>Remove old log files after x days:</source> +<target></target> + +<source>Show &log</source> +<target></target> + <source>Both sides have changed since last synchronization.</source> <target>שני הצדדים שונו מאז הסנכרון האחרון.</target> @@ -100,8 +109,8 @@ <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 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> @@ -110,7 +119,7 @@ <target>פתח את התצורה הבאה לעריכה בלבד מבלי להפעיל אותה.</target> <source>Path to an alternate GlobalSettings.xml file.</source> -<target>נתיב אל קובץ GlobalSettingd.xml אלטרנטיבי.</target> +<target>נתיב אל קובץ GlobalSettings.xml אלטרנטיבי.</target> <source>Installation files are corrupted. Please reinstall FreeFileSync.</source> <target>קבצי ההתקנה פגומים. בבקשה התקן מחדש את FreeFileSync.</target> @@ -334,6 +343,9 @@ <source>Update attributes on right</source> <target>עדכן תכונות בצד שמאל</target> +<source>Warning</source> +<target>אזהרה</target> + <source>Items processed:</source> <target>אלמנטים עובדו:</target> @@ -343,6 +355,9 @@ <source>Total time:</source> <target>זמן כולל:</target> +<source>Stopped</source> +<target>נעצר</target> + <source>Cleaning up log files:</source> <target>מנקה קבצי יומן:</target> @@ -382,6 +397,15 @@ <source>Unable to connect to %x.</source> <target>לא יכול להתחבר אל %x.</target> +<source>Completed successfully</source> +<target>הסתיים בהצלחה</target> + +<source>Completed with warnings</source> +<target>הסתיים עם אזהרות</target> + +<source>Completed with errors</source> +<target>הסתיים עם שגיאות</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>לא ניתן לגשת אל שרות Volume Shadow Copy.</target> @@ -678,8 +702,8 @@ Actual: %y bytes <source>3. Press 'Start'.</source> <target>3. לחץ 'הפעל'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>בכדי להתחיל יבא קובץ .ffs_batch.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>בכדי להתחיל יבא קובץ "ffs_batch".</target> <source>Folders to watch:</source> <target>תיקיות לצפייה:</target> @@ -761,24 +785,9 @@ The command is triggered if: <source>System: Shut down</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> @@ -830,6 +839,9 @@ The command is triggered if: <source>Last sync</source> <target>סינכרון אחרון</target> +<source>Log</source> +<target>יומן</target> + <source>Folder</source> <target>תיקייה</target> @@ -1393,6 +1405,12 @@ This guarantees a consistent state even in case of a serious error. <source>Highlight Configurations</source> <target>הדגש תצורות</target> +<source>Info</source> +<target>מידע</target> + +<source>Select all</source> +<target>בחר הכל</target> + <source>&Options</source> <target>&אפשרויות</target> @@ -1421,7 +1439,7 @@ This guarantees a consistent state even in case of a serious error. <target>&הראה פרטים</target> <source>FreeFileSync %x is available!</source> -<target>FreeFileSync %x זמין !</target> +<target>FreeFileSync %x זמין!</target> <source>Local path not available for %x.</source> <target>נתיב מקומי לא זמין עבור %x.</target> @@ -1622,7 +1640,7 @@ This guarantees a consistent state even in case of a serious error. <target>עצור בקשה...</target> <source>Initializing...</source> -<target>מאתחל ...</target> +<target>מאתחל...</target> <source>Scanning...</source> <target>סורק...</target> @@ -1630,21 +1648,12 @@ This guarantees a consistent state even in case of a serious error. <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> @@ -1870,6 +1879,9 @@ This guarantees a consistent state even in case of a serious error. <source>Unable to register to receive system messages.</source> <target>לא ניתן להרשם לקבלת הודעות מערכת.</target> +<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source> +<target>אפשרת התקנה %x זמינה רק במהדורת תרומות של FreeFileSync.</target> + <source>Cannot find system function %x.</source> <target>לא יכול למצוא פונקצית מערכת %x.</target> @@ -2026,9 +2038,6 @@ This guarantees a consistent state even in case of a serious error. <source>Please choose the local installation type or select a different folder for installation.</source> <target>בבקשה בחר התקנה מקומית או בחר תיקייה אחרת להתקנה.</target> -<source>The %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 43847881..67a3bfcc 100755 --- a/FreeFileSync/Build/Languages/hindi.lng +++ b/FreeFileSync/Build/Languages/hindi.lng @@ -100,8 +100,8 @@ <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 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> @@ -334,6 +334,9 @@ <source>Update attributes on right</source> <target>दाईं तरफ के गुण अद्यतन करें</target> +<source>Warning</source> +<target>चेतावनी</target> + <source>Items processed:</source> <target>संसाधित आइटम्स:</target> @@ -343,6 +346,9 @@ <source>Total time:</source> <target>कुल समय:</target> +<source>Stopped</source> +<target>रुका</target> + <source>Cleaning up log files:</source> <target>लॉग फाइलों की सफ़ाई जारी:</target> @@ -382,6 +388,15 @@ <source>Unable to connect to %x.</source> <target>%x से कनेक्ट करने में असमर्थ।</target> +<source>Completed successfully</source> +<target>सफलतापूर्वक पूर्ण हुआ</target> + +<source>Completed with warnings</source> +<target>चेतावनियों सहित पूर्ण हुआ</target> + +<source>Completed with errors</source> +<target>त्रुटियों सहित पूर्ण हुआ</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>वॉल्यूम शॅडो प्रतिलिपि सर्व्हिस पहुँच प्राप्त नहीं है।</target> @@ -678,8 +693,8 @@ Actual: %y bytes <source>3. Press 'Start'.</source> <target>3. 'प्रारंभ' दबाएं।</target> -<source>To get started just import a .ffs_batch file.</source> -<target>प्रारंभ करने के लिए केवल कोई .ffs_batch फ़ाइल आयात करें।</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>प्रारंभ करने के लिए केवल कोई "ffs_batch" फ़ाइल आयात करें।</target> <source>Folders to watch:</source> <target>इन फ़ोलडर्स की निगरानी होगी:</target> @@ -761,24 +776,9 @@ The command is triggered if: <source>System: Shut down</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> @@ -830,6 +830,9 @@ The command is triggered if: <source>Last sync</source> <target>पिछला सिंक</target> +<source>Log</source> +<target>लॉग</target> + <source>Folder</source> <target>निर्देशिका</target> @@ -905,6 +908,9 @@ The command is triggered if: <source>Save as &batch job...</source> <target>बॅच जॉब के रूप में सहेजें (&b)...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>तुलना शुरू करें (&c)</target> @@ -1303,6 +1309,9 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>स्थायी रूप से छिपाये संवाद बॉक्सेस और चेतावनी संदेश फिर से दिखाएं</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>प्रासंगिक मेनू अनुकूलित करें:</target> @@ -1393,6 +1402,15 @@ This guarantees a consistent state even in case of a serious error. <source>Highlight Configurations</source> <target>कॉन्फ़िगरेशन्स हाइलाइट करें</target> +<source>Info</source> +<target>जानकारी</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>सभी चुने</target> + <source>&Options</source> <target>विकल्प (&O)</target> @@ -1630,21 +1648,12 @@ This guarantees a consistent state even in case of a serious error. <source>Comparing content...</source> <target>सामग्री की तुलना हो रही है...</target> -<source>Info</source> -<target>जानकारी</target> - -<source>Select all</source> -<target>सभी चुने</target> - <source>&Continue</source> <target>जारी रखें (&C)</target> <source>Progress</source> <target>प्रगति</target> -<source>Log</source> -<target>लॉग</target> - <source>Thank you, %x, for your donation and support!</source> <target>धन्यवाद, %x, आपके दान और समर्थन के लिए!</target> @@ -1870,6 +1879,9 @@ This guarantees a consistent state even in case of a serious error. <source>Unable to register to receive system messages.</source> <target>सिस्टम संदेशों को प्राप्त करने के लिए पंजीकृत करने में असमर्थ।</target> +<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source> +<target>%x स्थापना विकल्प केवल FreeFileSync दान संस्करण में ही उपलब्ध है।</target> + <source>Cannot find system function %x.</source> <target>सिस्टम फ़ंकशन %x ढूंढ नहीं सकते।</target> @@ -2026,9 +2038,6 @@ This guarantees a consistent state even in case of a serious error. <source>Please choose the local installation type or select a different folder for installation.</source> <target>स्थापना के लिए कृपया स्थानीय स्थापना प्रकार चयन करें या कोई और निर्देशिका चुनें।</target> -<source>The %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 acc1a85a..5135b1d6 100755 --- a/FreeFileSync/Build/Languages/hungarian.lng +++ b/FreeFileSync/Build/Languages/hungarian.lng @@ -100,8 +100,8 @@ <source>global config file:</source> <target>globális konfigurációs állomány:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>Tetszőleges számú .ffs_gui és/vagy .ffs_batch konfigurációs állomány készíthető a FreeFileSync-hez.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>Tetszőleges számú "ffs_gui" és/vagy "ffs_batch" konfigurációs állomány készíthető a FreeFileSync-hez.</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Tetszőleges számú alternatív könyvtár-pár legfeljebb egy konfigurációs állományban.</target> @@ -334,6 +334,9 @@ <source>Update attributes on right</source> <target>Attribútumok frissítése a jobb oldalon</target> +<source>Warning</source> +<target>Figyelmeztetés</target> + <source>Items processed:</source> <target>Feldolgozott elemek száma:</target> @@ -343,6 +346,9 @@ <source>Total time:</source> <target>Összes időszükséglet:</target> +<source>Stopped</source> +<target>Leállítva</target> + <source>Cleaning up log files:</source> <target>A log állományok törlése:</target> @@ -382,6 +388,15 @@ <source>Unable to connect to %x.</source> <target>Nem képes %x-hez kapcsolódni.</target> +<source>Completed successfully</source> +<target>Sikeresen végrehajtva</target> + +<source>Completed with warnings</source> +<target>Figyelmeztetések mellett végrehajtva</target> + +<source>Completed with errors</source> +<target>Hibák mellett végrehajtva</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Nem elérhető a Kötet Árnyék-másolat szolgáltatás.</target> @@ -457,12 +472,6 @@ <source>Creating a Volume Shadow Copy for %x...</source> <target>%x számára árnyékmásolat-kötetet készítése...</target> -<source>Searching for excess file versions:</source> -<target>A felesleges fájl-verziók keresése:</target> - -<source>Removing excess file versions:</source> -<target>A felesleges fájl-verziók törlése:</target> - <source>Cannot find folder %x.</source> <target>%x könyvtárat nem találom.</target> @@ -514,6 +523,12 @@ <source>Generating database...</source> <target>Adatbázis generálása...</target> +<source>Searching for excess file versions:</source> +<target>A felesleges fájl-verziók keresése:</target> + +<source>Removing excess file versions:</source> +<target>A felesleges fájl-verziók törlése:</target> + <source>Unable to create time stamp for versioning:</source> <target>Nem képes időbélyegzés létrehozására a verzióképzéshez:</target> @@ -678,8 +693,8 @@ Tényleges: %y bájt <source>3. Press 'Start'.</source> <target>3. Nyomd meg a Start gombot.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Az induláshoz importáljon egy .ffs_batch állományt.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Az induláshoz importáljon egy "ffs_batch" állományt.</target> <source>Folders to watch:</source> <target>Figyelendő könyvtárak:</target> @@ -761,24 +776,9 @@ A parancs végrehajtódik, ha: <source>System: Shut down</source> <target>Rendszer: Leállítva</target> -<source>Stopped</source> -<target>Leállítva</target> - -<source>Completed with errors</source> -<target>Hibák mellett végrehajtva</target> - -<source>Completed with warnings</source> -<target>Figyelmeztetések mellett végrehajtva</target> - -<source>Warning</source> -<target>Figyelmeztetés</target> - <source>Nothing to synchronize</source> <target>Nincs mit szinkronizálni</target> -<source>Completed successfully</source> -<target>Sikeresen végrehajtva</target> - <source>Executing command %x</source> <target>%x parancs végrehajtása</target> @@ -830,6 +830,9 @@ A parancs végrehajtódik, ha: <source>Last sync</source> <target>Legutóbbi szinkronizálás</target> +<source>Log</source> +<target>Napló</target> + <source>Folder</source> <target>Könyvtár</target> @@ -905,6 +908,9 @@ A parancs végrehajtódik, ha: <source>Save as &batch job...</source> <target>Mentse &kötegelt feladatként...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>Kezdje az &összehasonlítást</target> @@ -1303,6 +1309,9 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Mutassa újra az összes ideiglenesen rejtett párbeszédablakot és figyelmeztetést</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>Környezeti menü testreszabása:</target> @@ -1393,6 +1402,15 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. <source>Highlight Configurations</source> <target>Jelölje ki a beállításokat</target> +<source>Info</source> +<target>Információ</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Összeset kiválasztja</target> + <source>&Options</source> <target>&Beállítások</target> @@ -1630,21 +1648,12 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. <source>Comparing content...</source> <target>Tartalom összehasonlítása...</target> -<source>Info</source> -<target>Információ</target> - -<source>Select all</source> -<target>Összeset kiválasztja</target> - <source>&Continue</source> <target>&Folytat</target> <source>Progress</source> <target>Haladás</target> -<source>Log</source> -<target>Napló</target> - <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> @@ -1870,6 +1879,9 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. <source>Unable to register to receive system messages.</source> <target>Nem tud regisztrálni, hogy megkapja a rendszerüzeneteket.</target> +<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source> +<target>%x telepítési opció csak a FreeFileSync Támogatói Kiadásában érhető el.</target> + <source>Cannot find system function %x.</source> <target>%x rendszerfunkció nem elérhető.</target> @@ -2026,9 +2038,6 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. <source>Please choose the local installation type or select a different folder for installation.</source> <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 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 446fefc4..7064f93f 100755 --- a/FreeFileSync/Build/Languages/italian.lng +++ b/FreeFileSync/Build/Languages/italian.lng @@ -100,8 +100,8 @@ <source>global config file:</source> <target>file di configurazione globale:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>Qualsiasi numero di FreeFileSync.ffs_gui e/o File di configurazione .ffs_batch.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>Qualsiasi numero di FreeFileSync"ffs_gui" e/o File di configurazione "ffs_batch".</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Qualsiasi numero di coppie di directory alternative per al massimo un file di configurazione.</target> @@ -269,7 +269,7 @@ <target>Bloccare il proprietario:</target> <source>Detecting abandoned lock...</source> -<target>Rilevamento blocco abbandonato ...</target> +<target>Rilevamento blocco abbandonato...</target> <source> <pluralform>1 sec</pluralform> @@ -334,6 +334,9 @@ <source>Update attributes on right</source> <target>Aggiorna attributi a destra</target> +<source>Warning</source> +<target>Attenzione</target> + <source>Items processed:</source> <target>Oggetti processati:</target> @@ -343,6 +346,9 @@ <source>Total time:</source> <target>Tempo totale:</target> +<source>Stopped</source> +<target>Arrestato</target> + <source>Cleaning up log files:</source> <target>Pulizia dei file di registro:</target> @@ -382,6 +388,15 @@ <source>Unable to connect to %x.</source> <target>Impossibile connettersi a %x.</target> +<source>Completed successfully</source> +<target>Completato con successo</target> + +<source>Completed with warnings</source> +<target>Completato con avvisi</target> + +<source>Completed with errors</source> +<target>Completato con errori</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Impossibile accedere al Volume Shadow Copy Service.</target> @@ -678,8 +693,8 @@ Attuale: %y byte <source>3. Press 'Start'.</source> <target>3. Premi 'Avvio'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Per iniziare è sufficiente importare un file con estensione .ffs_batch.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Per iniziare è sufficiente importare un file con estensione "ffs_batch".</target> <source>Folders to watch:</source> <target>Cartelle da guardare:</target> @@ -761,24 +776,9 @@ Il comando è attivato se: <source>System: Shut down</source> <target>Sistema: Spento</target> -<source>Stopped</source> -<target>Arrestato</target> - -<source>Completed with errors</source> -<target>Completato con errori</target> - -<source>Completed with warnings</source> -<target>Completato con avvisi</target> - -<source>Warning</source> -<target>Attenzione</target> - <source>Nothing to synchronize</source> <target>Non c'è nulla da sincronizzare</target> -<source>Completed successfully</source> -<target>Completato con successo</target> - <source>Executing command %x</source> <target>Esecuzione del comando %x</target> @@ -830,6 +830,9 @@ Il comando è attivato se: <source>Last sync</source> <target>Ultima sincronizzazione</target> +<source>Log</source> +<target>Log</target> + <source>Folder</source> <target>Cartella</target> @@ -905,6 +908,9 @@ Il comando è attivato se: <source>Save as &batch job...</source> <target>Salva come &processo batch...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>Avvio &confronto</target> @@ -930,7 +936,7 @@ Il comando è attivato se: <target>&Lingua</target> <source>&Find...</source> -<target>&Trova ...</target> +<target>&Trova...</target> <source>&Export file list...</source> <target>&Esporta l'elenco dei file...</target> @@ -987,7 +993,7 @@ Il comando è attivato se: <target>Salva</target> <source>Save as...</source> -<target>Salva come ...</target> +<target>Salva come...</target> <source>View type:</source> <target>Visualizza tipo:</target> @@ -1303,6 +1309,9 @@ Questo garantisce uno stato consistente anche in caso di errore grave. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Mostra di nuovo tutti i dialoghi nascosti in modo permanente e i messaggi di allarme</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>Personalizzare menu contestuale:</target> @@ -1393,6 +1402,15 @@ Questo garantisce uno stato consistente anche in caso di errore grave. <source>Highlight Configurations</source> <target>Evidenzia le configurazioni</target> +<source>Info</source> +<target>Info</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Seleziona tutto</target> + <source>&Options</source> <target>&Opzioni</target> @@ -1619,7 +1637,7 @@ Questo garantisce uno stato consistente anche in caso di errore grave. <target>In pausa</target> <source>Stop requested...</source> -<target>Stop richiesto ...</target> +<target>Stop richiesto...</target> <source>Initializing...</source> <target>Inizializzazione...</target> @@ -1630,21 +1648,12 @@ Questo garantisce uno stato consistente anche in caso di errore grave. <source>Comparing content...</source> <target>Comparazione del contenuto...</target> -<source>Info</source> -<target>Info</target> - -<source>Select all</source> -<target>Seleziona tutto</target> - <source>&Continue</source> <target>&Continuare</target> <source>Progress</source> <target>Avanzamento</target> -<source>Log</source> -<target>Log</target> - <source>Thank you, %x, for your donation and support!</source> <target>Grazie, %x, per la vostra donazione e il supporto!</target> @@ -1870,6 +1879,9 @@ Questo garantisce uno stato consistente anche in caso di errore grave. <source>Unable to register to receive system messages.</source> <target>Impossibile registrarsi per ricevere i messaggi di sistema.</target> +<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>Cannot find system function %x.</source> <target>Impossibile trovare la funzione di sistema %x.</target> @@ -2026,9 +2038,6 @@ Questo garantisce uno stato consistente anche in caso di errore grave. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Scegliere il tipo di installazione locale o selezionare una cartella diversa per l'installazione.</target> -<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 b63b0a12..c1ca09e4 100755 --- a/FreeFileSync/Build/Languages/japanese.lng +++ b/FreeFileSync/Build/Languages/japanese.lng @@ -100,8 +100,8 @@ <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 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> @@ -110,7 +110,7 @@ <target>選択された構成を実行しないで編集用に開きます.</target> <source>Path to an alternate GlobalSettings.xml file.</source> -<target>代替グローバル設定.xml ファイルのパス.</target> +<target></target> <source>Installation files are corrupted. Please reinstall FreeFileSync.</source> <target>インストール ファイルが破損しています、FreeFileSync を再インストールしてください.</target> @@ -332,6 +332,9 @@ <source>Update attributes on right</source> <target>右の属性を更新</target> +<source>Warning</source> +<target>警告</target> + <source>Items processed:</source> <target>処理された要素:</target> @@ -341,6 +344,9 @@ <source>Total time:</source> <target>合計時間:</target> +<source>Stopped</source> +<target>停止</target> + <source>Cleaning up log files:</source> <target>ログファイルのクリーン:</target> @@ -379,6 +385,15 @@ <source>Unable to connect to %x.</source> <target>%x に接続できません.</target> +<source>Completed successfully</source> +<target>正常に完了しました</target> + +<source>Completed with warnings</source> +<target>警告で終了</target> + +<source>Completed with errors</source> +<target>エラーで終了</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>ボリュームシャドウコピーサービスにアクセス出来ません.</target> @@ -672,8 +687,8 @@ Actual: %y bytes <source>3. Press 'Start'.</source> <target>3. 'スタート'をクリック.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>一括ファイル(.ffs)からインポートして開始.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target></target> <source>Folders to watch:</source> <target>監視するフォルダ:</target> @@ -755,24 +770,9 @@ The command is triggered if: <source>System: Shut down</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> @@ -823,6 +823,9 @@ The command is triggered if: <source>Last sync</source> <target>前回の同期</target> +<source>Log</source> +<target>ログ</target> + <source>Folder</source> <target>フォルダ</target> @@ -898,6 +901,9 @@ The command is triggered if: <source>Save as &batch job...</source> <target>一括ジョブで保存(&B)...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>比較を開始(&C)</target> @@ -1296,6 +1302,9 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>非表示にしたすべてのダイアログと警告メッセージを再表示</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>コンテキストメニューのカスタマイズ:</target> @@ -1386,6 +1395,15 @@ This guarantees a consistent state even in case of a serious error. <source>Highlight Configurations</source> <target>構成の強調表示</target> +<source>Info</source> +<target>情報</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>すべて選択</target> + <source>&Options</source> <target>設定(&O)</target> @@ -1619,21 +1637,12 @@ This guarantees a consistent state even in case of a serious error. <source>Comparing content...</source> <target>内容を比較中...</target> -<source>Info</source> -<target>情報</target> - -<source>Select all</source> -<target>すべて選択</target> - <source>&Continue</source> <target>続行(&C)</target> <source>Progress</source> <target>進行状況</target> -<source>Log</source> -<target>ログ</target> - <source>Thank you, %x, for your donation and support!</source> <target>ありがとう %x, あなたの寄付と支援に感謝します!!</target> @@ -1856,6 +1865,9 @@ This guarantees a consistent state even in case of a serious error. <source>Unable to register to receive system messages.</source> <target>システム受信メッセージに登録できません.</target> +<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source> +<target>インストール オプション %x は、FreeFileSync 寄付版でのみ利用可能です.</target> + <source>Cannot find system function %x.</source> <target>システム関数 %x がみつかりません.</target> @@ -2010,9 +2022,6 @@ This guarantees a consistent state even in case of a serious error. <source>Please choose the local installation type or select a different folder for installation.</source> <target>ローカル インストールを選択するか、別のインストール先を選択してください.</target> -<source>The %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 8529fa42..811c7c51 100755 --- a/FreeFileSync/Build/Languages/korean.lng +++ b/FreeFileSync/Build/Languages/korean.lng @@ -100,8 +100,8 @@ <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 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>최대 1개 설정파일에 대한 대체 디렉터리 페어 개수.</target> @@ -332,6 +332,9 @@ <source>Update attributes on right</source> <target>우측 속성 업데이트</target> +<source>Warning</source> +<target>경고</target> + <source>Items processed:</source> <target>처리된 항목:</target> @@ -341,6 +344,9 @@ <source>Total time:</source> <target>전체 시간:</target> +<source>Stopped</source> +<target>중단</target> + <source>Cleaning up log files:</source> <target>로그 파일 정리 중:</target> @@ -379,6 +385,15 @@ <source>Unable to connect to %x.</source> <target>%x에 연결할 수 없습니다.</target> +<source>Completed successfully</source> +<target>성공적으로 완료됨</target> + +<source>Completed with warnings</source> +<target>경고와 함께 완료됨</target> + +<source>Completed with errors</source> +<target>오류와 함께 완료됨</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>볼륨 섀도 복사본 서비스에 접근할 수 없습니다.</target> @@ -672,8 +687,8 @@ Actual: %y bytes <source>3. Press 'Start'.</source> <target>3. '시작'을 누르세요.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>시작하려면 .ffs_batch file을 가져 오십시오.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>시작하려면 "ffs_batch" file을 가져 오십시오.</target> <source>Folders to watch:</source> <target>감시할 폴더:</target> @@ -755,24 +770,9 @@ The command is triggered if: <source>System: Shut down</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> @@ -823,6 +823,9 @@ The command is triggered if: <source>Last sync</source> <target>마지막 동기화</target> +<source>Log</source> +<target>로그</target> + <source>Folder</source> <target>폴더</target> @@ -898,6 +901,9 @@ The command is triggered if: <source>Save as &batch job...</source> <target>일괄 작업으로 저장(&b)...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>비교 시작(&C)</target> @@ -1296,6 +1302,9 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>영구적으로 숨겨진 모든 대화 상자 및 경고 메세지 다시 보이기</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>컨텍스트 메뉴 커스터마이즈 (사용자 정의):</target> @@ -1386,6 +1395,15 @@ This guarantees a consistent state even in case of a serious error. <source>Highlight Configurations</source> <target>강조 표시 구성</target> +<source>Info</source> +<target>정보</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>모두 선택</target> + <source>&Options</source> <target>옵션(&O)</target> @@ -1619,21 +1637,12 @@ This guarantees a consistent state even in case of a serious error. <source>Comparing content...</source> <target>내용 비교 중...</target> -<source>Info</source> -<target>정보</target> - -<source>Select all</source> -<target>모두 선택</target> - <source>&Continue</source> <target>계속(&C)</target> <source>Progress</source> <target>진행</target> -<source>Log</source> -<target>로그</target> - <source>Thank you, %x, for your donation and support!</source> <target>%x님의 기부와 지원에 감사드립니다!</target> @@ -1856,6 +1865,9 @@ This guarantees a consistent state even in case of a serious error. <source>Unable to register to receive system messages.</source> <target>시스템 메시지 수신을 위한 등록을 할 수 없습니다.</target> +<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source> +<target>%x 설치 옵션은 FreeFileSync 기부자 에디션에서만 사용 가능합니다.</target> + <source>Cannot find system function %x.</source> <target>시스템 함수 %x을(를) 찾을 수 없습니다.</target> @@ -2010,9 +2022,6 @@ This guarantees a consistent state even in case of a serious error. <source>Please choose the local installation type or select a different folder for installation.</source> <target>로컬 설치 유형을 선택하거나 설치할 다른 폴더를 선택하십시오.</target> -<source>The %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/lithuanian.lng b/FreeFileSync/Build/Languages/lithuanian.lng index 14b24d25..efb1c91f 100755 --- a/FreeFileSync/Build/Languages/lithuanian.lng +++ b/FreeFileSync/Build/Languages/lithuanian.lng @@ -100,8 +100,8 @@ <source>global config file:</source> <target>bendrinis konfigūracinis failas:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>Neribotas kiekis FreeFileSync .ffs_gui ir/arba .ffs_batch konfigūracinių failų.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>Neribotas kiekis FreeFileSync "ffs_gui" ir/arba "ffs_batch" konfigūracinių failų.</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Bet koks skaičius alternatyvių katalogų grupuojams tik su vienu konfigūraciniu failu.</target> @@ -336,6 +336,9 @@ <source>Update attributes on right</source> <target>Atnaujinti atributus dešinėje</target> +<source>Warning</source> +<target>Perspėjimas</target> + <source>Items processed:</source> <target>Elementų apdorota:</target> @@ -345,6 +348,9 @@ <source>Total time:</source> <target>Visas laikas:</target> +<source>Stopped</source> +<target>Sustabdyta</target> + <source>Cleaning up log files:</source> <target>Žurnalinių failų valymas:</target> @@ -385,6 +391,15 @@ <source>Unable to connect to %x.</source> <target>Nepavyko prisijungti prie %x.</target> +<source>Completed successfully</source> +<target>Sėkmingai užbaigtas</target> + +<source>Completed with warnings</source> +<target>Užbaigtas su perspėjimais</target> + +<source>Completed with errors</source> +<target>Užbaigtas su klaidomis</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Duomenų Šešėlinės Kopijos Paslauga nepasiekiama.</target> @@ -684,8 +699,8 @@ Esamas: %y baitai <source>3. Press 'Start'.</source> <target>3. Spauskite „Pradėti“'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Kad pradėti tiesiog importuokite .ffs_batch failą.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Kad pradėti tiesiog importuokite "ffs_batch" failą.</target> <source>Folders to watch:</source> <target>Stebimi aplankai:</target> @@ -767,24 +782,9 @@ Komanda inicijuojama jei: <source>System: Shut down</source> <target>Sistema: Išjungti</target> -<source>Stopped</source> -<target>Sustabdyta</target> - -<source>Completed with errors</source> -<target>Užbaigtas su klaidomis</target> - -<source>Completed with warnings</source> -<target>Užbaigtas su perspėjimais</target> - -<source>Warning</source> -<target>Perspėjimas</target> - <source>Nothing to synchronize</source> <target>Nėra ko suvienodinti</target> -<source>Completed successfully</source> -<target>Sėkmingai užbaigtas</target> - <source>Executing command %x</source> <target>Vykdyti komandą %x</target> @@ -837,6 +837,9 @@ Komanda inicijuojama jei: <source>Last sync</source> <target>Vėliausias</target> +<source>Log</source> +<target>Žurnalas</target> + <source>Folder</source> <target>Aplankas</target> @@ -912,6 +915,9 @@ Komanda inicijuojama jei: <source>Save as &batch job...</source> <target>Išsaugoti kaip &užduočių paketą...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>Pradėti &palyginimą</target> @@ -1310,6 +1316,9 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Vėl rodyti visus paslėptus dialogo langus ir įspėjamuosius pranešimus</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>Pagrindinio meniu pasirinkimai:</target> @@ -1400,6 +1409,15 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. <source>Highlight Configurations</source> <target>Pažymėti Nustatymus</target> +<source>Info</source> +<target>Informacija</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Pažymėti visus</target> + <source>&Options</source> <target>&Parinktys</target> @@ -1641,21 +1659,12 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. <source>Comparing content...</source> <target>Palyginimo turinys...</target> -<source>Info</source> -<target>Informacija</target> - -<source>Select all</source> -<target>Pažymėti visus</target> - <source>&Continue</source> <target>&Tęsti</target> <source>Progress</source> <target>Pokytis</target> -<source>Log</source> -<target>Žurnalas</target> - <source>Thank you, %x, for your donation and support!</source> <target>Ačiū, %x, už jūsų auką, bei parėmimą!</target> @@ -1884,6 +1893,9 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. <source>Unable to register to receive system messages.</source> <target>Nepavyko aktyvuoti sisteminių žinučių gavimo funkciją.</target> +<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>Cannot find system function %x.</source> <target>Nepavyksta rasti sisteminės funkcijos %x.</target> @@ -2042,9 +2054,6 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Prašome pasirinkti vietinio įdiegimo metodą arba pakeiskite aplaką.</target> -<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 1a82aa13..134e2215 100755 --- a/FreeFileSync/Build/Languages/norwegian.lng +++ b/FreeFileSync/Build/Languages/norwegian.lng @@ -7,6 +7,15 @@ <plural_definition>n == 1 ? 0 : 1</plural_definition> </header> +<source>No log entries</source> +<target></target> + +<source>Remove old log files after x days:</source> +<target></target> + +<source>Show &log</source> +<target></target> + <source>Both sides have changed since last synchronization.</source> <target>Begge sider er endret siden siste synkronisering.</target> @@ -100,8 +109,8 @@ <source>global config file:</source> <target>global innstillingsfil:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>Vilkårlig antall FreeFileSync .ffs_gui og/eller .ffs_batch innstillingsfiler.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>Vilkårlig antall FreeFileSync "ffs_gui" og/eller "ffs_batch" innstillingsfiler.</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Et ubegrenset antall alternative kataloger for maksimalt én konfigurasjonsfil.</target> @@ -245,7 +254,7 @@ <target>Databasefilene inneholder ennå ikke informasjon om siste synkronisering.</target> <source>Loading file %x...</source> -<target>Laster fil %x ...</target> +<target>Laster fil %x...</target> <source>Saving file %x...</source> <target>Lagrer filen %x...</target> @@ -334,6 +343,9 @@ <source>Update attributes on right</source> <target>Oppdater attributter til høyre</target> +<source>Warning</source> +<target>Advarsel</target> + <source>Items processed:</source> <target>Elementer behandlet:</target> @@ -343,6 +355,9 @@ <source>Total time:</source> <target>Samlet tid:</target> +<source>Stopped</source> +<target>Stoppet</target> + <source>Cleaning up log files:</source> <target>Sletter loggfiler:</target> @@ -382,6 +397,15 @@ <source>Unable to connect to %x.</source> <target>Kan ikke koble til %x.</target> +<source>Completed successfully</source> +<target>Fullførte feilfritt</target> + +<source>Completed with warnings</source> +<target>Avsluttet med advarsler</target> + +<source>Completed with errors</source> +<target>Avsluttet med feil</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Volume Shadow Copy-tjenesten er ikke tilgjengelig.</target> @@ -678,8 +702,8 @@ Faktisk: %y bytes <source>3. Press 'Start'.</source> <target>3. Klikk 'Start'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Importer en .ffs_batchfil for å komme igang.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Importer en "ffs_batch"fil for å komme igang.</target> <source>Folders to watch:</source> <target>Mapper å se på:</target> @@ -761,24 +785,9 @@ Kommandoen utføres hvis: <source>System: Shut down</source> <target>System: Avslutt</target> -<source>Stopped</source> -<target>Stoppet</target> - -<source>Completed with errors</source> -<target>Avsluttet med feil</target> - -<source>Completed with warnings</source> -<target>Avsluttet med advarsler</target> - -<source>Warning</source> -<target>Advarsel</target> - <source>Nothing to synchronize</source> <target>Alt er synkronisert</target> -<source>Completed successfully</source> -<target>Fullførte feilfritt</target> - <source>Executing command %x</source> <target>Utfør kommandoen %x</target> @@ -830,6 +839,9 @@ Kommandoen utføres hvis: <source>Last sync</source> <target>Sist synkronisert</target> +<source>Log</source> +<target>Logg</target> + <source>Folder</source> <target>Mappe</target> @@ -1268,7 +1280,7 @@ Kommandoen utføres hvis: <target>Begrens antall loggfiler:</target> <source>How can I schedule a batch job?</source> -<target>Hvordan kan jeg lage en batchfil ?</target> +<target>Hvordan kan jeg lage en batchfil?</target> <source>&Keep relative paths</source> <target>&Behold tilknyttede stier</target> @@ -1393,6 +1405,12 @@ Sikrer prosessen ved alvorlige feil. <source>Highlight Configurations</source> <target>Uthev konfigurasjoner</target> +<source>Info</source> +<target>Info</target> + +<source>Select all</source> +<target>Velg alt</target> + <source>&Options</source> <target>&Valg</target> @@ -1630,21 +1648,12 @@ Sikrer prosessen ved alvorlige feil. <source>Comparing content...</source> <target>Sammenligner innhold...</target> -<source>Info</source> -<target>Info</target> - -<source>Select all</source> -<target>Velg alt</target> - <source>&Continue</source> <target>&Fortsett</target> <source>Progress</source> <target>Fremskritt</target> -<source>Log</source> -<target>Logg</target> - <source>Thank you, %x, for your donation and support!</source> <target>Takk, %x, for ditt bidrag og støtte!</target> @@ -1680,8 +1689,8 @@ Sikrer prosessen ved alvorlige feil. <pluralform>Do you really want to move the following %x items to the recycle bin?</pluralform> </source> <target> -<pluralform>Vil du virkelig flytte følgende element til papirkurven ?</pluralform> -<pluralform>Vil du virkelig flytte følgende %x elementer til papirkurven ?</pluralform> +<pluralform>Vil du virkelig flytte følgende element til papirkurven?</pluralform> +<pluralform>Vil du virkelig flytte følgende %x elementer til papirkurven?</pluralform> </target> <source>Move</source> @@ -1692,8 +1701,8 @@ Sikrer prosessen ved alvorlige feil. <pluralform>Do you really want to delete the following %x items?</pluralform> </source> <target> -<pluralform>Vil du virkelig slette følgende element ?</pluralform> -<pluralform>Vil du virkelig slette følgende %x element ?</pluralform> +<pluralform>Vil du virkelig slette følgende element?</pluralform> +<pluralform>Vil du virkelig slette følgende %x element?</pluralform> </target> <source>Copy DACL, SACL, Owner, Group</source> @@ -1841,7 +1850,7 @@ Sikrer prosessen ved alvorlige feil. <target>&Hjemmeside</target> <source>Download now?</source> -<target>Last ned nå ?</target> +<target>Last ned nå?</target> <source>&Download</source> <target>&Last ned</target> @@ -1870,6 +1879,9 @@ Sikrer prosessen ved alvorlige feil. <source>Unable to register to receive system messages.</source> <target>Kunne ikke registrere for å motta systembeskjeder.</target> +<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>Cannot find system function %x.</source> <target>Kan ikke finne systemfunksjonen %x.</target> @@ -1946,7 +1958,7 @@ Sikrer prosessen ved alvorlige feil. <target>De følgende XML-elementer kunne ikke leses:</target> <source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>Konfigurasjonsfil %x er ufullstendig. De manglende elementene vil bli satt til standardverdiene .</target> +<target>Konfigurasjonsfil %x er ufullstendig. De manglende elementene vil bli satt til standardverdiene.</target> <source>Prepare installation</source> <target>Forbered installering</target> @@ -2026,9 +2038,6 @@ Sikrer prosessen ved alvorlige feil. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Velg den lokale installasjonstype eller velge en annen mappe for installasjon.</target> -<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 72e1a41a..e3dd61a9 100755 --- a/FreeFileSync/Build/Languages/polish.lng +++ b/FreeFileSync/Build/Languages/polish.lng @@ -100,8 +100,8 @@ <source>global config file:</source> <target>globalny plik konfiguracyjny:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>Dowolna ilość plików konfiguracyjnych FreeFileSync .ffs_gui/.ffs_batch.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>Dowolna ilość plików konfiguracyjnych FreeFileSync "ffs_gui"/"ffs_batch".</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Dowolna liczba alternatywnych par katalogów dla najwyżej jednego pliku konfiguracyjnego.</target> @@ -336,6 +336,9 @@ <source>Update attributes on right</source> <target>Aktualizuj atrybuty po prawej stronie</target> +<source>Warning</source> +<target>Ostrzeżenie</target> + <source>Items processed:</source> <target>Przetworzone elementy:</target> @@ -345,6 +348,9 @@ <source>Total time:</source> <target>Całkowity czas:</target> +<source>Stopped</source> +<target>Zatrzymana</target> + <source>Cleaning up log files:</source> <target>Czyszczenie plików logów:</target> @@ -385,6 +391,15 @@ <source>Unable to connect to %x.</source> <target>Błąd połączenia do %x.</target> +<source>Completed successfully</source> +<target>Zakończono bez błędów</target> + +<source>Completed with warnings</source> +<target>Zakończono z ostrzeżeniami</target> + +<source>Completed with errors</source> +<target>Zakończono z błędami</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Nie można uzyskać dostępu do usługi Volume Shadow Copy.</target> @@ -684,8 +699,8 @@ Przesłany: %y bajtów <source>3. Press 'Start'.</source> <target>3. Wciśnij 'Start'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Aby rozpocząć, zaimportuj plik .ffs_batch.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Aby rozpocząć, zaimportuj plik "ffs_batch".</target> <source>Folders to watch:</source> <target>Katalogi do obserwowania:</target> @@ -767,24 +782,9 @@ Komenda jest wykonywana gdy: <source>System: Shut down</source> <target>System: Wyłączenie</target> -<source>Stopped</source> -<target>Zatrzymana</target> - -<source>Completed with errors</source> -<target>Zakończono z błędami</target> - -<source>Completed with warnings</source> -<target>Zakończono z ostrzeżeniami</target> - -<source>Warning</source> -<target>Ostrzeżenie</target> - <source>Nothing to synchronize</source> <target>Brak plików do synchronizacji</target> -<source>Completed successfully</source> -<target>Zakończono bez błędów</target> - <source>Executing command %x</source> <target>Wykonywanie polecenia %x</target> @@ -837,6 +837,9 @@ Komenda jest wykonywana gdy: <source>Last sync</source> <target>Ostatnia synchronizacja</target> +<source>Log</source> +<target>Log</target> + <source>Folder</source> <target>Katalog</target> @@ -912,6 +915,9 @@ Komenda jest wykonywana gdy: <source>Save as &batch job...</source> <target>Zapisz w trybie &wsadowym...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>Rozpo&cznij porównywanie</target> @@ -1310,6 +1316,9 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp <source>Show all permanently hidden dialogs and warning messages again</source> <target>Przywróć wszystkie, stale ukryte dialogi i powiadomienia</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>Dostosuj menu kontekstowe:</target> @@ -1400,6 +1409,15 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp <source>Highlight Configurations</source> <target>Pokaż konfiguracje</target> +<source>Info</source> +<target>Info</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Zaznacz wszystko</target> + <source>&Options</source> <target>&Opcje</target> @@ -1641,21 +1659,12 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp <source>Comparing content...</source> <target>Porównywanie zawartości...</target> -<source>Info</source> -<target>Info</target> - -<source>Select all</source> -<target>Zaznacz wszystko</target> - <source>&Continue</source> <target>&Kontynuuj</target> <source>Progress</source> <target>Postęp</target> -<source>Log</source> -<target>Log</target> - <source>Thank you, %x, for your donation and support!</source> <target>Dziękujemy %x za Twoje wsparcie!</target> @@ -1884,6 +1893,9 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp <source>Unable to register to receive system messages.</source> <target>Błąd podczas rejestrowania do odbioru komunikatów systemowych.</target> +<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source> +<target>Opcja instalacyjna %x jest dostępna wyłącznie w wersji FreeFileSync Donation Edition.</target> + <source>Cannot find system function %x.</source> <target>Nie można odnaleźć funkcji systemowej %x.</target> @@ -2042,9 +2054,6 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp <source>Please choose the local installation type or select a different folder for installation.</source> <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>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 7479c61a..83c0ad21 100755 --- a/FreeFileSync/Build/Languages/portuguese.lng +++ b/FreeFileSync/Build/Languages/portuguese.lng @@ -100,8 +100,8 @@ <source>global config file:</source> <target>ficheiro de configuração global:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>Qualquer número de ficheiros de configuração .ffs_gui e/ou .ffs_batch do FreeFileSync.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>Qualquer número de ficheiros de configuração "ffs_gui" e/ou "ffs_batch" do FreeFileSync.</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Qualquer número de pares de directórios alternativos para apenas um ficheiro de configuração.</target> @@ -334,6 +334,9 @@ <source>Update attributes on right</source> <target>Actualizar atributos à direita</target> +<source>Warning</source> +<target>Atenção</target> + <source>Items processed:</source> <target>Elementos processados:</target> @@ -343,6 +346,9 @@ <source>Total time:</source> <target>Tempo total:</target> +<source>Stopped</source> +<target>Parado</target> + <source>Cleaning up log files:</source> <target>A limpar ficheiros de registo:</target> @@ -382,6 +388,15 @@ <source>Unable to connect to %x.</source> <target>Não é possível conectar-se em %x.</target> +<source>Completed successfully</source> +<target>Concluído com sucesso</target> + +<source>Completed with warnings</source> +<target>Concluído com avisos</target> + +<source>Completed with errors</source> +<target>Concluído com erros</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Não é possível aceder ao serviço Volume Shadow Copy.</target> @@ -678,8 +693,8 @@ Actual: %y bytes <source>3. Press 'Start'.</source> <target>3. Pressionar 'Iniciar'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Para começar basta importar um ficheiro .ffs_batch.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Para começar basta importar um ficheiro "ffs_batch".</target> <source>Folders to watch:</source> <target>Pastas a verificar:</target> @@ -761,24 +776,9 @@ O comando é executado se: <source>System: Shut down</source> <target>Desligar</target> -<source>Stopped</source> -<target>Parado</target> - -<source>Completed with errors</source> -<target>Concluído com erros</target> - -<source>Completed with warnings</source> -<target>Concluído com avisos</target> - -<source>Warning</source> -<target>Atenção</target> - <source>Nothing to synchronize</source> <target>Nada a sincronizar</target> -<source>Completed successfully</source> -<target>Concluído com sucesso</target> - <source>Executing command %x</source> <target>A executar comando %x</target> @@ -830,6 +830,9 @@ O comando é executado se: <source>Last sync</source> <target>Última sincronia</target> +<source>Log</source> +<target>Log</target> + <source>Folder</source> <target>Pasta</target> @@ -905,6 +908,9 @@ O comando é executado se: <source>Save as &batch job...</source> <target>Guardar como &batch...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>Iniciar &comparação</target> @@ -1303,6 +1309,9 @@ Isto garante um estado consistente mesmo em caso de falha grave. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Mostrar todos os diálogos escondidos permanentemente e mensagens de aviso novamente</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>Personalizar menu de contexto:</target> @@ -1393,6 +1402,15 @@ Isto garante um estado consistente mesmo em caso de falha grave. <source>Highlight Configurations</source> <target>Realçar Configurações</target> +<source>Info</source> +<target>Info</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Seleccionar tudo</target> + <source>&Options</source> <target>&Opções</target> @@ -1630,21 +1648,12 @@ Isto garante um estado consistente mesmo em caso de falha grave. <source>Comparing content...</source> <target>A comparar...</target> -<source>Info</source> -<target>Info</target> - -<source>Select all</source> -<target>Seleccionar tudo</target> - <source>&Continue</source> <target>&Continuar</target> <source>Progress</source> <target>Progresso</target> -<source>Log</source> -<target>Log</target> - <source>Thank you, %x, for your donation and support!</source> <target>Obrigado, %x, pela sua doação e suporte!</target> @@ -1870,6 +1879,9 @@ Isto garante um estado consistente mesmo em caso de falha grave. <source>Unable to register to receive system messages.</source> <target>Não foi possível registar para receber mensagens do sistema.</target> +<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>Cannot find system function %x.</source> <target>Não é possível encontrar a função do sistema %x.</target> @@ -2026,9 +2038,6 @@ Isto garante um estado consistente mesmo em caso de falha grave. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Por favor, escolha o tipo do local de instalação ou seleccione uma pasta diferente para instalar.</target> -<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 3d02293d..9b336566 100755 --- a/FreeFileSync/Build/Languages/portuguese_br.lng +++ b/FreeFileSync/Build/Languages/portuguese_br.lng @@ -100,8 +100,8 @@ <source>global config file:</source> <target>arquivo de configuração global:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>Qualquer número de arquivos FreeFileSync .ffs e/ou de tarefa em lotes .ffs_batch.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>Qualquer número de arquivos FreeFileSync "ffs_gui" e/ou de tarefa em lotes "ffs_batch".</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Qualquer número de pares alternativos de diretórios para no máximo um arquivo de configuração.</target> @@ -334,6 +334,9 @@ <source>Update attributes on right</source> <target>Atualizar atributos à direita</target> +<source>Warning</source> +<target>Aviso</target> + <source>Items processed:</source> <target>Elementos processados:</target> @@ -343,6 +346,9 @@ <source>Total time:</source> <target>Tempo total:</target> +<source>Stopped</source> +<target>Interrompido</target> + <source>Cleaning up log files:</source> <target>Limpando arquivos de log:</target> @@ -382,6 +388,15 @@ <source>Unable to connect to %x.</source> <target>Não foi possível conectar a %x.</target> +<source>Completed successfully</source> +<target>Concluído com sucesso</target> + +<source>Completed with warnings</source> +<target>Concluído com avisos</target> + +<source>Completed with errors</source> +<target>Concluído com erros</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Não é possível acessar o Serviço de Cópias de Sombra de Volume.</target> @@ -678,8 +693,8 @@ Atual: %y bytes <source>3. Press 'Start'.</source> <target>3. Pressionar 'Iniciar'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Para iniciar importe um arquivo .ffs_batch.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Para iniciar importe um arquivo "ffs_batch".</target> <source>Folders to watch:</source> <target>Pastas para monitorar:</target> @@ -761,24 +776,9 @@ O comando é disparado se: <source>System: Shut down</source> <target>Sistema: Desligar</target> -<source>Stopped</source> -<target>Interrompido</target> - -<source>Completed with errors</source> -<target>Concluído com erros</target> - -<source>Completed with warnings</source> -<target>Concluído com avisos</target> - -<source>Warning</source> -<target>Aviso</target> - <source>Nothing to synchronize</source> <target>Nada para sincronizar</target> -<source>Completed successfully</source> -<target>Concluído com sucesso</target> - <source>Executing command %x</source> <target>Executando comando %x</target> @@ -830,6 +830,9 @@ O comando é disparado se: <source>Last sync</source> <target>Últ. Sinc.</target> +<source>Log</source> +<target>Log</target> + <source>Folder</source> <target>Pasta</target> @@ -905,6 +908,9 @@ O comando é disparado se: <source>Save as &batch job...</source> <target>Salvar como &tarefa em lotes...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>Iniciar &Comparação</target> @@ -1303,6 +1309,9 @@ Isto garante um estado consistente mesmo em caso de erro grave. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Mostrar todas as caixas de diálogo e as mensagens de aviso permanentemente ocultadas</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>Personalizar menu de contexto:</target> @@ -1393,6 +1402,15 @@ Isto garante um estado consistente mesmo em caso de erro grave. <source>Highlight Configurations</source> <target>Realçar Configurações</target> +<source>Info</source> +<target>Informações</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Selecionar todos</target> + <source>&Options</source> <target>&Opções</target> @@ -1630,21 +1648,12 @@ Isto garante um estado consistente mesmo em caso de erro grave. <source>Comparing content...</source> <target>Comparando conteúdo...</target> -<source>Info</source> -<target>Informações</target> - -<source>Select all</source> -<target>Selecionar todos</target> - <source>&Continue</source> <target>&Continuar</target> <source>Progress</source> <target>Progresso</target> -<source>Log</source> -<target>Log</target> - <source>Thank you, %x, for your donation and support!</source> <target>Obrigado, %x, pela sua doação e suporte!</target> @@ -1870,6 +1879,9 @@ Isto garante um estado consistente mesmo em caso de erro grave. <source>Unable to register to receive system messages.</source> <target>Não é possível registrar para receber mensagens do sistema.</target> +<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>Cannot find system function %x.</source> <target>Não é possível localizar a função de sistema %x.</target> @@ -2026,9 +2038,6 @@ Isto garante um estado consistente mesmo em caso de erro grave. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Escolha o tipo de instalação local ou selecione uma pasta diferente para instalação.</target> -<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 f9de2796..7a970005 100755 --- a/FreeFileSync/Build/Languages/romanian.lng +++ b/FreeFileSync/Build/Languages/romanian.lng @@ -100,8 +100,8 @@ <source>global config file:</source> <target>filă de configurare globală:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>Orice număr de file de configurare pentru FreeFileSync, de tipul .ffs_gui sau/și .ffs_batch.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>Orice număr de file de configurare pentru FreeFileSync, de tipul "ffs_gui" sau/și "ffs_batch".</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Orice număr de perechi alternative de dosare pentru cel mult o filă de configurare.</target> @@ -336,6 +336,9 @@ <source>Update attributes on right</source> <target>Actualizează atributele în partea dreaptă</target> +<source>Warning</source> +<target>Atenție</target> + <source>Items processed:</source> <target>Elemente Procesate:</target> @@ -345,6 +348,9 @@ <source>Total time:</source> <target>Timp Total:</target> +<source>Stopped</source> +<target>Oprită</target> + <source>Cleaning up log files:</source> <target>Curăț filele de jurnalizare:</target> @@ -385,6 +391,15 @@ <source>Unable to connect to %x.</source> <target>Nu mă pot conecta la %x.</target> +<source>Completed successfully</source> +<target>Realizată cu succes</target> + +<source>Completed with warnings</source> +<target>Realizată cu atenționări</target> + +<source>Completed with errors</source> +<target>Realizată cu erori</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Nu pot accesa Serviciul de Conservare a Volumelor [Volume Shadow Copy].</target> @@ -684,8 +699,8 @@ Actuală: %y baiți <source>3. Press 'Start'.</source> <target>3. Apasă pe 'Pornește'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Pentru a începe, importă o filă de tipul .ffs_batch.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Pentru a începe, importă o filă de tipul "ffs_batch".</target> <source>Folders to watch:</source> <target>Dosare de Monitorizat:</target> @@ -767,24 +782,9 @@ Comanda este declanșată dacă: <source>System: Shut down</source> <target>Sistem: Închide PC-ul [Shut down]</target> -<source>Stopped</source> -<target>Oprită</target> - -<source>Completed with errors</source> -<target>Realizată cu erori</target> - -<source>Completed with warnings</source> -<target>Realizată cu atenționări</target> - -<source>Warning</source> -<target>Atenție</target> - <source>Nothing to synchronize</source> <target>Nu e nimic de sincronizat</target> -<source>Completed successfully</source> -<target>Realizată cu succes</target> - <source>Executing command %x</source> <target>Execut comanda %x</target> @@ -837,6 +837,9 @@ Comanda este declanșată dacă: <source>Last sync</source> <target>Ultima Sincr.</target> +<source>Log</source> +<target>Jurnal</target> + <source>Folder</source> <target>Dosar</target> @@ -912,6 +915,9 @@ Comanda este declanșată dacă: <source>Save as &batch job...</source> <target>Salvea&ză ca Sarcină Set...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>Pornește &Compararea</target> @@ -1310,6 +1316,9 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției <source>Show all permanently hidden dialogs and warning messages again</source> <target>Sînt arătate din nou dialogurile și mesajele de eroare care au fost ascunse permanent</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>Personalizează meniul contextual:</target> @@ -1400,6 +1409,15 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției <source>Highlight Configurations</source> <target>Evidențiază Configurațiile</target> +<source>Info</source> +<target>Informații</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Selectează Tot</target> + <source>&Options</source> <target>&Opțiuni</target> @@ -1641,21 +1659,12 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției <source>Comparing content...</source> <target>Compar conținutul...</target> -<source>Info</source> -<target>Informații</target> - -<source>Select all</source> -<target>Selectează Tot</target> - <source>&Continue</source> <target>&Continuă</target> <source>Progress</source> <target>Progres</target> -<source>Log</source> -<target>Jurnal</target> - <source>Thank you, %x, for your donation and support!</source> <target>Mulțumesc %x, pentru donație și sprijin!</target> @@ -1855,7 +1864,7 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției <target>&Situl Softului</target> <source>Download now?</source> -<target>Vrei s-o descarci acum ?</target> +<target>Vrei s-o descarci acum?</target> <source>&Download</source> <target>&Descarcă</target> @@ -1884,6 +1893,9 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției <source>Unable to register to receive system messages.</source> <target>Nu pot înregistra softul pentru a primi mesaje de la sistemul de operare.</target> +<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>Cannot find system function %x.</source> <target>Nu pot găsi funcția de sistem %x.</target> @@ -2042,9 +2054,6 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției <source>Please choose the local installation type or select a different folder for installation.</source> <target>Alege instalarea locală sau selectează un dosar diferit pentru instalare.</target> -<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 643c49a5..17566e71 100755 --- a/FreeFileSync/Build/Languages/russian.lng +++ b/FreeFileSync/Build/Languages/russian.lng @@ -100,8 +100,8 @@ <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 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> @@ -336,6 +336,9 @@ <source>Update attributes on right</source> <target>Обновление атрибутов справа</target> +<source>Warning</source> +<target>Внимание</target> + <source>Items processed:</source> <target>Элементов обработано:</target> @@ -345,6 +348,9 @@ <source>Total time:</source> <target>Общее время:</target> +<source>Stopped</source> +<target>Остановлено</target> + <source>Cleaning up log files:</source> <target>Очистка лог-файлов (журналов):</target> @@ -385,6 +391,15 @@ <source>Unable to connect to %x.</source> <target>Невозможно соединиться с %x.</target> +<source>Completed successfully</source> +<target>Выполнено успешно</target> + +<source>Completed with warnings</source> +<target>Выполнено с предупреждениями</target> + +<source>Completed with errors</source> +<target>Выполнено с ошибками</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Невозможно получить доступ к службе Теневого Копирования Тома.</target> @@ -684,8 +699,8 @@ Actual: %y bytes <source>3. Press 'Start'.</source> <target>3. Нажмите 'Старт'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Для запуска просто импортируйте файл .ffs_batch.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Для запуска просто импортируйте файл "ffs_batch".</target> <source>Folders to watch:</source> <target>Папки для наблюдения:</target> @@ -767,24 +782,9 @@ The command is triggered if: <source>System: Shut down</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> @@ -837,6 +837,9 @@ The command is triggered if: <source>Last sync</source> <target>Последняя синхронизация</target> +<source>Log</source> +<target>Лог (журнал)</target> + <source>Folder</source> <target>Папка</target> @@ -912,6 +915,9 @@ The command is triggered if: <source>Save as &batch job...</source> <target>Сохранить как пакетное &задание...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>Начать с&равнение</target> @@ -1310,6 +1316,9 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Показать все скрытые окна и сообщения с предупреждениями снова</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>Кастомизация контекстного меню:</target> @@ -1400,6 +1409,15 @@ This guarantees a consistent state even in case of a serious error. <source>Highlight Configurations</source> <target>Выделение конфигураций</target> +<source>Info</source> +<target>Информация</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Выделить все</target> + <source>&Options</source> <target>&Настройки</target> @@ -1641,21 +1659,12 @@ This guarantees a consistent state even in case of a serious error. <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> @@ -1884,6 +1893,9 @@ This guarantees a consistent state even in case of a serious error. <source>Unable to register to receive system messages.</source> <target>Невозможно зарегистрироваться для получения системных сообщений.</target> +<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source> +<target>%x опция установки доступна только в платной версии FreeFileSync.</target> + <source>Cannot find system function %x.</source> <target>Невозможно найти системную функцию %x.</target> @@ -2042,9 +2054,6 @@ This guarantees a consistent state even in case of a serious error. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Пожалуйста, выберите локальный тип установки или выберите другую папку для установки.</target> -<source>The %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 82d56212..4d296d17 100755 --- a/FreeFileSync/Build/Languages/slovak.lng +++ b/FreeFileSync/Build/Languages/slovak.lng @@ -50,7 +50,7 @@ <target>Zmazanie symbolického odkazu %x</target> <source>Checking recycle bin availability for folder %x...</source> -<target>Kontrola Koša pre priečinok %x ...</target> +<target>Kontrola Koša pre priečinok %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>Premiestnenie do Koša nie je možné pri nasledujúcich priečinkoch. Zmazané alebo prepísané súbory nebude možné obnoviť:</target> @@ -100,8 +100,8 @@ <source>global config file:</source> <target>globálny konfiguračný súbor:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>Ľubovolný počet konfiguračných súborov FreeFileSync typu .ffs_gui a/alebo .ffs_batch.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>Ľubovolný počet konfiguračných súborov FreeFileSync typu "ffs_gui" a/alebo "ffs_batch".</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Ľubovolný počet alternatívnych dvojíc adresárov na aspoň jednu konfiguráciu.</target> @@ -336,6 +336,9 @@ <source>Update attributes on right</source> <target>Aktualizovať atribúty napravo</target> +<source>Warning</source> +<target>Varovanie</target> + <source>Items processed:</source> <target>Spracovaných položiek:</target> @@ -345,6 +348,9 @@ <source>Total time:</source> <target>Celkový čas:</target> +<source>Stopped</source> +<target>Zastavené</target> + <source>Cleaning up log files:</source> <target>Odstránenie log súborov:</target> @@ -385,6 +391,15 @@ <source>Unable to connect to %x.</source> <target>Nie je možné vytvoriť pripojenie k %x.</target> +<source>Completed successfully</source> +<target>Dokončenie bolo úspešné</target> + +<source>Completed with warnings</source> +<target>Ukončené s varovaniami</target> + +<source>Completed with errors</source> +<target>Ukončené s chybami</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Nie je prístup k službe Tieňové kópie.</target> @@ -684,8 +699,8 @@ Aktuálne: %y b <source>3. Press 'Start'.</source> <target>3. Stlačte 'Štart'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Môžete načítať tiež konfiguračný súbor .ffs_batch.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Môžete načítať tiež konfiguračný súbor "ffs_batch".</target> <source>Folders to watch:</source> <target>Sledované priečinka:</target> @@ -767,24 +782,9 @@ Príkaz bude spustení ak: <source>System: Shut down</source> <target>Systém: Vypnúť</target> -<source>Stopped</source> -<target>Zastavené</target> - -<source>Completed with errors</source> -<target>Ukončené s chybami</target> - -<source>Completed with warnings</source> -<target>Ukončené s varovaniami</target> - -<source>Warning</source> -<target>Varovanie</target> - <source>Nothing to synchronize</source> <target>Nie je čo synchronizovať</target> -<source>Completed successfully</source> -<target>Dokončenie bolo úspešné</target> - <source>Executing command %x</source> <target>Spúšťací príkaz %x</target> @@ -837,6 +837,9 @@ Príkaz bude spustení ak: <source>Last sync</source> <target>Posledná synchronizácia</target> +<source>Log</source> +<target>Záznam spracovania</target> + <source>Folder</source> <target>Priečinok</target> @@ -912,6 +915,9 @@ Príkaz bude spustení ak: <source>Save as &batch job...</source> <target>Uložiť ako &dávku...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>Spustiť &porovnanie</target> @@ -1307,6 +1313,9 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Zobraziť znovu všetky trvale skryté dialógy a varovné hlásenia</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>Prispôsobiť kontextovú ponuku:</target> @@ -1397,6 +1406,15 @@ This guarantees a consistent state even in case of a serious error. <source>Highlight Configurations</source> <target>Zvýrazniť konfigurácie</target> +<source>Info</source> +<target>Info</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Vybrať všetko</target> + <source>&Options</source> <target>Nastavenie &programu</target> @@ -1621,7 +1639,7 @@ This guarantees a consistent state even in case of a serious error. <target>Zoznam súborov bol exportovaný</target> <source>Searching for program updates...</source> -<target>Hľadanie aktualizácií programu ...</target> +<target>Hľadanie aktualizácií programu...</target> <source>Paused</source> <target>Pauza</target> @@ -1638,21 +1656,12 @@ This guarantees a consistent state even in case of a serious error. <source>Comparing content...</source> <target>Porovnávanie obsahu...</target> -<source>Info</source> -<target>Info</target> - -<source>Select all</source> -<target>Vybrať všetko</target> - <source>&Continue</source> <target>&Pokračovať</target> <source>Progress</source> <target>Priebeh</target> -<source>Log</source> -<target>Záznam spracovania</target> - <source>Thank you, %x, for your donation and support!</source> <target>Ďakujem, %x, za dar a podporu!</target> @@ -1881,6 +1890,9 @@ This guarantees a consistent state even in case of a serious error. <source>Unable to register to receive system messages.</source> <target>Nepodarilo sa registrovať k odberu systémových správ.</target> +<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>Cannot find system function %x.</source> <target>Nie je možné nájsť systémovú funkciu %x.</target> @@ -2039,9 +2051,6 @@ This guarantees a consistent state even in case of a serious error. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Prosím zvoľte lokálny typ inštalácie alebo vyberte iný priečinok pre inštaláciu.</target> -<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 161410d4..8dc2eea4 100755 --- a/FreeFileSync/Build/Languages/slovenian.lng +++ b/FreeFileSync/Build/Languages/slovenian.lng @@ -100,8 +100,8 @@ <source>global config file:</source> <target>datoteka z globalnimi konfiguracijami:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>Poljubno število FreeFileSync .ffs_gui in/ali .ffs_batch nastavitvenih datotek.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>Poljubno število FreeFileSync "ffs_gui" in/ali "ffs_batch" nastavitvenih datotek.</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Poljubno število alternativnih parov imenikov za največ eno nastavitveno datoteko.</target> @@ -338,6 +338,9 @@ <source>Update attributes on right</source> <target>Posodobi atribute na desni strani</target> +<source>Warning</source> +<target>Opozorilo</target> + <source>Items processed:</source> <target>Obdelanih postavk:</target> @@ -347,6 +350,9 @@ <source>Total time:</source> <target>Celoten čas:</target> +<source>Stopped</source> +<target>Ustavljeno</target> + <source>Cleaning up log files:</source> <target>Čiščenje datotek dnevnika:</target> @@ -388,6 +394,15 @@ <source>Unable to connect to %x.</source> <target>Ne morem povezati na %x.</target> +<source>Completed successfully</source> +<target>Uspešno končano</target> + +<source>Completed with warnings</source> +<target>Dokončano z opozorili</target> + +<source>Completed with errors</source> +<target>Dokončano z napakami</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Ne morem dostopati do Volume Shadov Copy storitve.</target> @@ -690,8 +705,8 @@ Dejansko: %y bajtov <source>3. Press 'Start'.</source> <target>3. Pritisnite 'Začni'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Če želite začeti, uvozite datoteko .ffs_batch.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Če želite začeti, uvozite datoteko "ffs_batch".</target> <source>Folders to watch:</source> <target>Mape za pregled:</target> @@ -773,24 +788,9 @@ Ukaz se sproži če: <source>System: Shut down</source> <target>Sistem: Izključi računalnik</target> -<source>Stopped</source> -<target>Ustavljeno</target> - -<source>Completed with errors</source> -<target>Dokončano z napakami</target> - -<source>Completed with warnings</source> -<target>Dokončano z opozorili</target> - -<source>Warning</source> -<target>Opozorilo</target> - <source>Nothing to synchronize</source> <target>Nič za sinhroniziranje</target> -<source>Completed successfully</source> -<target>Uspešno končano</target> - <source>Executing command %x</source> <target>Izvedba ukaza %x</target> @@ -844,6 +844,9 @@ Ukaz se sproži če: <source>Last sync</source> <target>Zadnja sinhronizacija</target> +<source>Log</source> +<target>Dnevnik</target> + <source>Folder</source> <target>Mapa</target> @@ -919,6 +922,9 @@ Ukaz se sproži če: <source>Save as &batch job...</source> <target>Shrani kot paketno op&ravilo...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>Začni &primerjavo</target> @@ -1317,6 +1323,9 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Prikaži vsa trajno skrita pogovorna okna in opozorilna sporočila</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>Prilagodi kontekstni meni:</target> @@ -1407,6 +1416,15 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. <source>Highlight Configurations</source> <target>Označite konfiguracije</target> +<source>Info</source> +<target>Info</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Izberi vse</target> + <source>&Options</source> <target>&Možnosti</target> @@ -1652,21 +1670,12 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. <source>Comparing content...</source> <target>Primerjam vsebino...</target> -<source>Info</source> -<target>Info</target> - -<source>Select all</source> -<target>Izberi vse</target> - <source>&Continue</source> <target>&Nadaljuj</target> <source>Progress</source> <target>Napredek</target> -<source>Log</source> -<target>Dnevnik</target> - <source>Thank you, %x, for your donation and support!</source> <target>Najlepša hvala, %x, za vašo donacijo in podporo!</target> @@ -1898,6 +1907,9 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. <source>Unable to register to receive system messages.</source> <target>Sistemskih sporočil ni mogoče registrirati.</target> +<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>Cannot find system function %x.</source> <target>Sistemske funkcije ni mogoče najti %x.</target> @@ -2058,9 +2070,6 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Prosimo izberite vrsto lokalne namestitve ali pa izberite drugo mapo za namestitev.</target> -<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 4389baa6..d539e749 100755 --- a/FreeFileSync/Build/Languages/spanish.lng +++ b/FreeFileSync/Build/Languages/spanish.lng @@ -100,8 +100,8 @@ <source>global config file:</source> <target>archivo de config. global:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>Cualquier número de archivos de configuración de FreeFileSync (.ffs_gui ó .ffs_batch).</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>Cualquier número de archivos de configuración de FreeFileSync ("ffs_gui" ó "ffs_batch").</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Cualquier número de pares de directorios alternativos para un archivo de configuración como máximo.</target> @@ -334,6 +334,9 @@ <source>Update attributes on right</source> <target>Actualizar atributos en la derecha</target> +<source>Warning</source> +<target>Atención</target> + <source>Items processed:</source> <target>Elementos procesados:</target> @@ -343,6 +346,9 @@ <source>Total time:</source> <target>Tiempo total:</target> +<source>Stopped</source> +<target>Detenido</target> + <source>Cleaning up log files:</source> <target>Limpiando archivos de registro:</target> @@ -382,6 +388,15 @@ <source>Unable to connect to %x.</source> <target>No se puede conectar a %x.</target> +<source>Completed successfully</source> +<target>Completado con éxito</target> + +<source>Completed with warnings</source> +<target>Completado con avisos</target> + +<source>Completed with errors</source> +<target>Completado con errores</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>No se puede acceder al servicio de Instantánea de volumen.</target> @@ -678,8 +693,8 @@ Reales: %y bytes <source>3. Press 'Start'.</source> <target>3. Presione 'Inicio'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Para comenzar, importe un archivo .ffs_batch.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Para comenzar, importe un archivo "ffs_batch".</target> <source>Folders to watch:</source> <target>Carpetas para examinar:</target> @@ -761,24 +776,9 @@ El comando es disparado si: <source>System: Shut down</source> <target>Sistema: apagar</target> -<source>Stopped</source> -<target>Detenido</target> - -<source>Completed with errors</source> -<target>Completado con errores</target> - -<source>Completed with warnings</source> -<target>Completado con avisos</target> - -<source>Warning</source> -<target>Atención</target> - <source>Nothing to synchronize</source> <target>Nada que sincronizar</target> -<source>Completed successfully</source> -<target>Completado con éxito</target> - <source>Executing command %x</source> <target>Ejecución del comando %x</target> @@ -830,6 +830,9 @@ El comando es disparado si: <source>Last sync</source> <target>Última sincronización</target> +<source>Log</source> +<target>Registro</target> + <source>Folder</source> <target>Carpeta</target> @@ -905,6 +908,9 @@ El comando es disparado si: <source>Save as &batch job...</source> <target>Guardar como tarea por &lotes...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>Iniciar la &comparación</target> @@ -1241,10 +1247,10 @@ El comando es disparado si: <target>Detener</target> <source>Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x</source> -<target>Crear tarea por lotes para sincronización desatendida. Para iniciar, haga doble clic en este archivo o prográmelo en el planificador de tareas : %x</target> +<target>Crear tarea por lotes para sincronización desatendida. Para iniciar, haga doble clic en este archivo o prográmelo en el planificador de tareas: %x</target> <source>Progress dialog:</source> -<target>Diálogo de progreso:</target> +<target>Diálogo de progreson:</target> <source>Run minimized</source> <target>Ejecutar minimizado</target> @@ -1303,8 +1309,11 @@ Esto garantiza un estado coherente incluso en caso de error grave. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Volver a mostrar todos los diálogos y mensajes de advertencia que fueron permanentemente ocultados</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> -<target>Personalizar menú contextual :</target> +<target>Personalizar menú contextual:</target> <source>Description</source> <target>Descripción</target> @@ -1313,7 +1322,7 @@ Esto garantiza un estado coherente incluso en caso de error grave. <target>&Configuración predeterminada</target> <source>Feedback and suggestions are welcome</source> -<target>Tus comentarios y sugerencias son bienvenidos :</target> +<target>Tus comentarios y sugerencias son bienvenidos:</target> <source>Home page</source> <target>Página de inicio</target> @@ -1337,13 +1346,13 @@ Esto garantiza un estado coherente incluso en caso de error grave. <target>Detalles para donación</target> <source>Source code written in C++ using:</source> -<target>Código fuente C++ con soporte de :</target> +<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> +<target>Publicado bajo régimen de derechos GNU General Public License:</target> <source>Many thanks for localization:</source> -<target>Agradecimientos a los traductores :</target> +<target>Agradecimientos a los traductores:</target> <source>Activate the FreeFileSync Donation Edition by one of the following methods:</source> <target>Active FreeFileSync Donation Edition por uno de los métodos siguientes:</target> @@ -1355,7 +1364,7 @@ Esto garantiza un estado coherente incluso en caso de error grave. <target>Activar en línea</target> <source>2. Retrieve an offline activation key from the following URL:</source> -<target>2. Recuperar una clave de activación sin conexión desde la dirección URL siguiente :</target> +<target>2. Recuperar una clave de activación sin conexión desde la dirección URL siguiente:</target> <source>&Copy to clipboard</source> <target>&Copiar al portapapeles</target> @@ -1393,6 +1402,15 @@ Esto garantiza un estado coherente incluso en caso de error grave. <source>Highlight Configurations</source> <target>Resaltar configuraciones</target> +<source>Info</source> +<target>Info</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Seleccionar todo</target> + <source>&Options</source> <target>&Opciones</target> @@ -1630,21 +1648,12 @@ Esto garantiza un estado coherente incluso en caso de error grave. <source>Comparing content...</source> <target>Comparando contenido...</target> -<source>Info</source> -<target>Info</target> - -<source>Select all</source> -<target>Seleccionar todo</target> - <source>&Continue</source> <target>&Continuar</target> <source>Progress</source> <target>Progreso</target> -<source>Log</source> -<target>Registro</target> - <source>Thank you, %x, for your donation and support!</source> <target>¡Muchas gracias, %x, por su contribución y ayuda!</target> @@ -1870,6 +1879,9 @@ Esto garantiza un estado coherente incluso en caso de error grave. <source>Unable to register to receive system messages.</source> <target>No es posible registrar la recepción de mensajes sistema.</target> +<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>Cannot find system function %x.</source> <target>No se puede encontrar la función del sistema %x.</target> @@ -1943,7 +1955,7 @@ Esto garantiza un estado coherente incluso en caso de error grave. <target>Fallo al comprobar la papelera de reciclaje para la carpeta %x.</target> <source>The following XML elements could not be read:</source> -<target>No se pueden leer los siguientes elementos XML :</target> +<target>No se pueden leer los siguientes elementos XML:</target> <source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> <target>El archivo de configuración %x está incompleto. Los elementos ausentes se definirán con valores predeterminados.</target> @@ -2026,9 +2038,6 @@ Esto garantiza un estado coherente incluso en caso de error grave. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Elija el tipo de instalación local o seleccione otra carpeta de instalación.</target> -<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 10151ff7..ee21b7ee 100755 --- a/FreeFileSync/Build/Languages/swedish.lng +++ b/FreeFileSync/Build/Languages/swedish.lng @@ -100,8 +100,8 @@ <source>global config file:</source> <target>övergripande konfigurationsfil:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>Valfritt antal FreeFileSync .ffs_gui och/eller .ffs_batch konfigurationsfiler.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>Valfritt antal FreeFileSync "ffs_gui" och/eller "ffs_batch" konfigurationsfiler.</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Valfritt antal alternativa katalogpar för som mest, en konfigurationsfil.</target> @@ -334,6 +334,9 @@ <source>Update attributes on right</source> <target>Uppdatera attribut på höger sida</target> +<source>Warning</source> +<target>Varning</target> + <source>Items processed:</source> <target>Processade poster:</target> @@ -343,6 +346,9 @@ <source>Total time:</source> <target>Total tid:</target> +<source>Stopped</source> +<target>Stoppad</target> + <source>Cleaning up log files:</source> <target>Städar upp loggfiler:</target> @@ -382,6 +388,15 @@ <source>Unable to connect to %x.</source> <target>Kan inte ansluta till %x.</target> +<source>Completed successfully</source> +<target>Korrekt slutförd</target> + +<source>Completed with warnings</source> +<target>Slutförd med varningar</target> + +<source>Completed with errors</source> +<target>Slutförd med fel</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Kan inte komma åt tjänsten 'Volume Shadow Copy'.</target> @@ -678,8 +693,8 @@ Aktuell: %y byte <source>3. Press 'Start'.</source> <target>3. Tryck 'Start'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Importera en .ffs_batch-fil för att komma igång.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Importera en "ffs_batch"-fil för att komma igång.</target> <source>Folders to watch:</source> <target>Mappar att övervaka:</target> @@ -761,24 +776,9 @@ Kommandot triggas om: <source>System: Shut down</source> <target>System: Stäng av</target> -<source>Stopped</source> -<target>Stoppad</target> - -<source>Completed with errors</source> -<target>Slutförd med fel</target> - -<source>Completed with warnings</source> -<target>Slutförd med varningar</target> - -<source>Warning</source> -<target>Varning</target> - <source>Nothing to synchronize</source> <target>Det finns inget att synkronisera</target> -<source>Completed successfully</source> -<target>Korrekt slutförd</target> - <source>Executing command %x</source> <target>Kör kommandot %x</target> @@ -830,6 +830,9 @@ Kommandot triggas om: <source>Last sync</source> <target>Senaste synkronisering</target> +<source>Log</source> +<target>Logg</target> + <source>Folder</source> <target>Mapp</target> @@ -905,6 +908,9 @@ Kommandot triggas om: <source>Save as &batch job...</source> <target>Spara som &batch-fil...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>&Jämför</target> @@ -1303,6 +1309,9 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Visa alla permanent dolda dialoger och varningsmeddelanden igen</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>Anpassad kontextmeny:</target> @@ -1393,6 +1402,15 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. <source>Highlight Configurations</source> <target>Färgmarkera konfigurationer</target> +<source>Info</source> +<target>Info</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Markera alla</target> + <source>&Options</source> <target>&Alternativ</target> @@ -1630,21 +1648,12 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. <source>Comparing content...</source> <target>Jämför innehåll...</target> -<source>Info</source> -<target>Info</target> - -<source>Select all</source> -<target>Markera alla</target> - <source>&Continue</source> <target>&Fortsätt</target> <source>Progress</source> <target>Förlopp</target> -<source>Log</source> -<target>Logg</target> - <source>Thank you, %x, for your donation and support!</source> <target>Tack %x, för din donation och ditt stöd!</target> @@ -1870,6 +1879,9 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. <source>Unable to register to receive system messages.</source> <target>Det gick inte att registrera mottagning av systemmeddelanden.</target> +<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>Cannot find system function %x.</source> <target>Kan inte hitta systemfunktion %x.</target> @@ -2026,9 +2038,6 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Välj lokal installationstyp eller välj en annan mapp för installationen.</target> -<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 a61cd83b..e4c3a9fd 100755 --- a/FreeFileSync/Build/Languages/turkish.lng +++ b/FreeFileSync/Build/Languages/turkish.lng @@ -100,8 +100,8 @@ <source>global config file:</source> <target>genel yapılandırma dosyası:</target> -<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> -<target>FreeFileSync .ffs_gui ya da .ffs_batch yapılandırma dosyalarının sayısı.</target> +<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> +<target>FreeFileSync "ffs_gui" ya da "ffs_batch" yapılandırma dosyalarının sayısı.</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>En fazla bir yapılandırma dosyası için herhangi bir sayıda alternatif klasör çifti.</target> @@ -334,6 +334,9 @@ <source>Update attributes on right</source> <target>Sağdaki öznitelikler güncellensin</target> +<source>Warning</source> +<target>Uyarı</target> + <source>Items processed:</source> <target>İşlenen öge:</target> @@ -343,6 +346,9 @@ <source>Total time:</source> <target>Toplam süre:</target> +<source>Stopped</source> +<target>Durduruldu</target> + <source>Cleaning up log files:</source> <target>Günlük dosyaları temizleniyor:</target> @@ -382,6 +388,15 @@ <source>Unable to connect to %x.</source> <target>%x üzerine bağlanılamadı.</target> +<source>Completed successfully</source> +<target>Tamamlandı</target> + +<source>Completed with warnings</source> +<target>Uyarılar ile tamamlandı</target> + +<source>Completed with errors</source> +<target>Sorunlar ile tamamlandı</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Birim Gölge Hizmetine erişilemiyor.</target> @@ -479,7 +494,7 @@ <target>Şu klasörler birbirinden çok farklı. Lütfen eşitleme için doğru klasörleri seçtiğinizden emin olun.</target> <source>Not enough free disk space available in:</source> -<target>Şurada yeterli disk alanı yok :</target> +<target>Şurada yeterli disk alanı yok:</target> <source>Required:</source> <target>Zorunlu:</target> @@ -678,8 +693,8 @@ Gerçekleşen: %y bayt <source>3. Press 'Start'.</source> <target>3. 'Başlat' düğmesine tıklayın.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>.ffs_batch dosyasını yükleyerek başlayabilirsiniz.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>"ffs_batch" dosyasını yükleyerek başlayabilirsiniz.</target> <source>Folders to watch:</source> <target>İzlenecek Klasörler:</target> @@ -761,24 +776,9 @@ Komut şu durumlarda yürütülür: <source>System: Shut down</source> <target>Sistem: Kapat</target> -<source>Stopped</source> -<target>Durduruldu</target> - -<source>Completed with errors</source> -<target>Sorunlar ile tamamlandı</target> - -<source>Completed with warnings</source> -<target>Uyarılar ile tamamlandı</target> - -<source>Warning</source> -<target>Uyarı</target> - <source>Nothing to synchronize</source> <target>Eşitlenecek bir şey yok</target> -<source>Completed successfully</source> -<target>Tamamlandı</target> - <source>Executing command %x</source> <target>%x komutu yürütülüyor</target> @@ -830,6 +830,9 @@ Komut şu durumlarda yürütülür: <source>Last sync</source> <target>Son eşitleme</target> +<source>Log</source> +<target>Günlük</target> + <source>Folder</source> <target>Klasör</target> @@ -905,6 +908,9 @@ Komut şu durumlarda yürütülür: <source>Save as &batch job...</source> <target>&Toplu İş Olarak Kaydet...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>&Karşılaştırmayı Başlat</target> @@ -1303,6 +1309,9 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y <source>Show all permanently hidden dialogs and warning messages again</source> <target>Kalıcı olarak gizlenmiş tüm ileti ve uyarılar yeniden görüntülenir</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>Sağ Tık Menüsü Uyarlamaları:</target> @@ -1393,6 +1402,15 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y <source>Highlight Configurations</source> <target>Yapılandırmalar Vurgulansın</target> +<source>Info</source> +<target>Bilgi</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Tümünü Seç</target> + <source>&Options</source> <target>&Ayarlar</target> @@ -1630,21 +1648,12 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y <source>Comparing content...</source> <target>İçerik karşılaştırılıyor...</target> -<source>Info</source> -<target>Bilgi</target> - -<source>Select all</source> -<target>Tümünü Seç</target> - <source>&Continue</source> <target>&Devam</target> <source>Progress</source> <target>İlerleme</target> -<source>Log</source> -<target>Günlük</target> - <source>Thank you, %x, for your donation and support!</source> <target>Sevgili %x, bağışın ve desteğin için teşekkürler!</target> @@ -1870,6 +1879,9 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y <source>Unable to register to receive system messages.</source> <target>Sistem iletilerini alabilmek için gerekli kayıt eklenemedi.</target> +<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>Cannot find system function %x.</source> <target>%x sistem işlevi bulunamadı.</target> @@ -2026,9 +2038,6 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y <source>Please choose the local installation type or select a different folder for installation.</source> <target>Kurulum için farklı bir klasör ya da yerel kurulum türünü seçin.</target> -<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 46342c15..a095fe89 100755 --- a/FreeFileSync/Build/Languages/ukrainian.lng +++ b/FreeFileSync/Build/Languages/ukrainian.lng @@ -100,8 +100,8 @@ <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 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> @@ -336,6 +336,9 @@ <source>Update attributes on right</source> <target>Оновити атрибути праворуч</target> +<source>Warning</source> +<target>Увага</target> + <source>Items processed:</source> <target>Елементів оброблено:</target> @@ -345,6 +348,9 @@ <source>Total time:</source> <target>Загальний час:</target> +<source>Stopped</source> +<target>Зупинено</target> + <source>Cleaning up log files:</source> <target>Очищення файлів журналу:</target> @@ -385,6 +391,15 @@ <source>Unable to connect to %x.</source> <target>Не вдається з'єднатися з %x.</target> +<source>Completed successfully</source> +<target>Завершено успішно</target> + +<source>Completed with warnings</source> +<target>Завершено з попередженнями</target> + +<source>Completed with errors</source> +<target>Завершено з помилками</target> + <source>Cannot access the Volume Shadow Copy Service.</source> <target>Не вдається отримати доступ до послуги Тіньового Копіювання Тому.</target> @@ -684,8 +699,8 @@ Actual: %y bytes <source>3. Press 'Start'.</source> <target>3. Натисніть 'Запуск'.</target> -<source>To get started just import a .ffs_batch file.</source> -<target>Щоб запустити імпортуйте .ffs_batch файл.</target> +<source>To get started just import a "ffs_batch" file.</source> +<target>Щоб запустити імпортуйте "ffs_batch" файл.</target> <source>Folders to watch:</source> <target>Папки для спостереження:</target> @@ -767,24 +782,9 @@ The command is triggered if: <source>System: Shut down</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> @@ -837,6 +837,9 @@ The command is triggered if: <source>Last sync</source> <target>Остання синхронізація</target> +<source>Log</source> +<target>Лог</target> + <source>Folder</source> <target>Папка</target> @@ -912,6 +915,9 @@ The command is triggered if: <source>Save as &batch job...</source> <target>Зберегти як &пакетне завдання...</target> +<source>Show &log</source> +<target></target> + <source>Start &comparison</source> <target>Запуск по&рівняння</target> @@ -1310,6 +1316,9 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Показати всі сховані діалоги і повідомлення з попередженнями знову</target> +<source>Remove old log files after x days:</source> +<target></target> + <source>Customize context menu:</source> <target>Налаштування контекстного меню:</target> @@ -1400,6 +1409,15 @@ This guarantees a consistent state even in case of a serious error. <source>Highlight Configurations</source> <target>Налаштування виділення</target> +<source>Info</source> +<target>Інформація</target> + +<source>No log entries</source> +<target></target> + +<source>Select all</source> +<target>Виділити все</target> + <source>&Options</source> <target>&Опції</target> @@ -1624,7 +1642,7 @@ This guarantees a consistent state even in case of a serious error. <target>Список файлів експортовано</target> <source>Searching for program updates...</source> -<target>Пошук оновлень програми ...</target> +<target>Пошук оновлень програми...</target> <source>Paused</source> <target>Призупинено</target> @@ -1641,21 +1659,12 @@ This guarantees a consistent state even in case of a serious error. <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> @@ -1884,6 +1893,9 @@ This guarantees a consistent state even in case of a serious error. <source>Unable to register to receive system messages.</source> <target>Не вдається зареєструватися для отримання системних повідомлень.</target> +<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source> +<target>Варіант установки %x доступний тільки у FreeFileSync Donation Edition.</target> + <source>Cannot find system function %x.</source> <target>Не вдається знайти системну функцію %x.</target> @@ -2042,9 +2054,6 @@ This guarantees a consistent state even in case of a serious error. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Будь ласка, виберіть локальний тип інсталяції чи іншу папку для встановлення.</target> -<source>The %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 Binary files differindex 48cee7b5..4fe5268b 100755 --- a/FreeFileSync/Build/Resources.zip +++ b/FreeFileSync/Build/Resources.zip diff --git a/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp b/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp index 97314b4d..0f895246 100755 --- a/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp +++ b/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp @@ -7,7 +7,6 @@ #include "folder_selector2.h" #include <zen/thread.h> #include <zen/file_access.h> -#include <zen/optional.h> #include <wx/dirdlg.h> #include <wx/scrolwin.h> #include <wx+/popup_dlg.h> @@ -31,8 +30,10 @@ void setFolderPath(const Zstring& dirpath, wxTextCtrl* txtCtrl, wxWindow& toolti const Zstring folderPathFmt = fff::getResolvedFilePath(dirpath); //may block when resolving [<volume name>] - tooltipWnd.SetToolTip(nullptr); //workaround wxComboBox bug http://trac.wxwidgets.org/ticket/10512 / http://trac.wxwidgets.org/ticket/12659 - tooltipWnd.SetToolTip(utfTo<wxString>(folderPathFmt)); //who knows when the real bugfix reaches mere mortals via an official release... + if (folderPathFmt.empty()) + tooltipWnd.UnsetToolTip(); //wxGTK doesn't allow wxToolTip with empty text! + else + tooltipWnd.SetToolTip(utfTo<wxString>(folderPathFmt)); if (staticText) //change static box label only if there is a real difference to what is shown in wxTextCtrl anyway staticText->SetLabel(equalFilePath(appendSeparator(trimCpy(dirpath)), appendSeparator(folderPathFmt)) ? wxString(_("Drag && drop")) : utfTo<wxString>(folderPathFmt)); @@ -103,7 +104,7 @@ void FolderSelector2::onFilesDropped(FileDropEvent& event) try { if (getItemType(itemPath) == ItemType::FILE) //throw FileError - if (Opt<Zstring> parentPath = getParentFolderPath(itemPath)) + if (std::optional<Zstring> parentPath = getParentFolderPath(itemPath)) itemPath = *parentPath; } catch (FileError&) {} //e.g. good for inactive mapped network shares, not so nice for C:\pagefile.sys diff --git a/FreeFileSync/Source/RealTimeSync/gui_generated.cpp b/FreeFileSync/Source/RealTimeSync/gui_generated.cpp index d0f1a137..aec55f7c 100755 --- a/FreeFileSync/Source/RealTimeSync/gui_generated.cpp +++ b/FreeFileSync/Source/RealTimeSync/gui_generated.cpp @@ -87,7 +87,7 @@ MainDlgGenerated::MainDlgGenerated( wxWindow* parent, wxWindowID id, const wxStr bSizer161->Add( bSizer16, 0, 0, 5 ); - m_staticText811 = new wxStaticText( this, wxID_ANY, _("To get started just import a .ffs_batch file."), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText811 = new wxStaticText( this, wxID_ANY, _("To get started just import a \"ffs_batch\" file."), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText811->Wrap( -1 ); m_staticText811->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); diff --git a/FreeFileSync/Source/RealTimeSync/monitor.cpp b/FreeFileSync/Source/RealTimeSync/monitor.cpp index a586ce4d..84e3e363 100755 --- a/FreeFileSync/Source/RealTimeSync/monitor.cpp +++ b/FreeFileSync/Source/RealTimeSync/monitor.cpp @@ -21,79 +21,82 @@ namespace const std::chrono::seconds FOLDER_EXISTENCE_CHECK_INTERVAL(1); -std::set<Zstring, LessFilePath> getFormattedDirs(const std::vector<Zstring>& folderPathPhrases) //throw FileError -{ - std::set<Zstring, LessFilePath> folderPaths; //make unique - - for (const Zstring& phrase : folderPathPhrases) - { - //hopefully clear enough now: https://freefilesync.org/forum/viewtopic.php?t=4302 - auto checkProtocol = [&](const Zstring& protoName) - { - if (startsWith(trimCpy(phrase), protoName + Zstr(":"), CmpAsciiNoCase())) - throw FileError(replaceCpy(_("The %x protocol does not support directory monitoring:"), L"%x", utfTo<std::wstring>(protoName)) + L"\n\n" + fmtPath(phrase)); - }; - checkProtocol(Zstr("FTP")); // - checkProtocol(Zstr("SFTP")); //throw FileError - checkProtocol(Zstr("MTP")); // - - //make unique: no need to resolve duplicate phrases more than once! (consider "[volume name]" syntax) -> shouldn't this be already buffered by OS? - folderPaths.insert(fff::getResolvedFilePath(phrase)); - } - - return folderPaths; -} - - //wait until all directories become available (again) + logs in network share std::set<Zstring, LessFilePath> waitForMissingDirs(const std::vector<Zstring>& folderPathPhrases, //throw FileError const std::function<void(const Zstring& folderPath)>& requestUiRefresh, std::chrono::milliseconds cbInterval) { + //early failure! check for unsupported folder paths: + for (const Zstring& protoName : { Zstr("FTP"), Zstr("SFTP"), Zstr("MTP") }) + for (const Zstring& phrase : folderPathPhrases) + //hopefully clear enough now: https://freefilesync.org/forum/viewtopic.php?t=4302 + if (startsWith(trimCpy(phrase), protoName + Zstr(":"), CmpAsciiNoCase())) + throw FileError(replaceCpy(_("The %x protocol does not support directory monitoring:"), L"%x", utfTo<std::wstring>(protoName)) + L"\n\n" + fmtPath(phrase)); + for (;;) { - //support specifying volume by name => call getResolvedFilePath() repeatedly - std::set<Zstring, LessFilePath> folderPaths = getFormattedDirs(folderPathPhrases); //throw FileError + struct FolderInfo + { + Zstring folderPathPhrase; + std::future<bool> folderAvailable; + }; + std::map<Zstring, FolderInfo, LessFilePath> folderInfos; //folderPath => FolderInfo - std::vector<std::pair<Zstring, std::future<bool>>> futureInfo; - //start all folder checks asynchronously (non-existent network path may block) - for (const Zstring& folderPath : folderPaths) - futureInfo.emplace_back(folderPath, runAsync([folderPath] + for (const Zstring& phrase : folderPathPhrases) { - //2. check dir availability - return dirAvailable(folderPath); - })); + const Zstring& folderPath = fff::getResolvedFilePath(phrase); - bool allAvailable = true; + //start all folder checks asynchronously (non-existent network path may block) + if (folderInfos.find(folderPath) == folderInfos.end()) + folderInfos[folderPath] = { phrase, runAsync([folderPath]{ return dirAvailable(folderPath); }) }; + } - for (auto& item : futureInfo) + std::set<Zstring, LessFilePath> availablePaths; + std::set<Zstring, LessFilePath> missingPathPhrases; + for (auto& item : folderInfos) { const Zstring& folderPath = item.first; - std::future<bool>& ftDirAvailable = item.second; + std::future<bool>& folderAvailable = item.second.folderAvailable; - for (;;) - { - while (ftDirAvailable.wait_for(cbInterval) != std::future_status::ready) - requestUiRefresh(folderPath); //throw X + while (folderAvailable.wait_for(cbInterval) != std::future_status::ready) + requestUiRefresh(folderPath); //throw X - if (ftDirAvailable.get()) - break; + if (folderAvailable.get()) + availablePaths.insert(folderPath); + else + missingPathPhrases.insert(item.second.folderPathPhrase); + } + if (missingPathPhrases.empty()) + return availablePaths; //only return when all folders were found on *first* try! + + auto delayUntil = std::chrono::steady_clock::now() + FOLDER_EXISTENCE_CHECK_INTERVAL; - //wait until folder is available: do not needlessly poll all others again! - allAvailable = false; + + for (const Zstring& folderPathPhrase : missingPathPhrases) + for (;;) + { + //support specifying volume by name => call getResolvedFilePath() repeatedly + const Zstring folderPath = fff::getResolvedFilePath(folderPathPhrase); //wait some time... - const auto delayUntil = std::chrono::steady_clock::now() + FOLDER_EXISTENCE_CHECK_INTERVAL; for (auto now = std::chrono::steady_clock::now(); now < delayUntil; now = std::chrono::steady_clock::now()) { requestUiRefresh(folderPath); //throw X std::this_thread::sleep_for(cbInterval); } - ftDirAvailable = runAsync([folderPath] { return dirAvailable(folderPath); }); + std::future<bool> folderAvailable = runAsync([folderPath] + { + return dirAvailable(folderPath); + }); + + while (folderAvailable.wait_for(cbInterval) != std::future_status::ready) + requestUiRefresh(folderPath); //throw X + + if (folderAvailable.get()) + break; + //else: wait until folder is available: do not needlessly poll existing folders again! + delayUntil = std::chrono::steady_clock::now() + FOLDER_EXISTENCE_CHECK_INTERVAL; } - } - if (allAvailable) //only return when all folders were found on *first* try! - return folderPaths; } } diff --git a/FreeFileSync/Source/base/algorithm.cpp b/FreeFileSync/Source/base/algorithm.cpp index a91d1130..044349b3 100755 --- a/FreeFileSync/Source/base/algorithm.cpp +++ b/FreeFileSync/Source/base/algorithm.cpp @@ -676,7 +676,7 @@ void fff::redetermineSyncDirection(const DirectionConfig& dirCfg, //throw FileEr BaseFolderPair& baseFolder, const std::function<void(const std::wstring& msg)>& notifyStatus) { - Opt<FileError> dbLoadError; //defer until after default directions have been set! + std::optional<FileError> dbLoadError; //defer until after default directions have been set! //try to load sync-database files std::shared_ptr<InSyncFolder> lastSyncState; @@ -730,7 +730,7 @@ void fff::redetermineSyncDirection(const MainConfiguration& mainCfg, //throw Fil if (folderCmp.size() != directCfgs.size()) throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); - Opt<FileError> dbLoadError; //defer until after default directions have been set! + std::optional<FileError> dbLoadError; //defer until after default directions have been set! for (auto it = folderCmp.begin(); it != folderCmp.end(); ++it) try @@ -1123,8 +1123,8 @@ void fff::applyTimeSpanFilter(FolderComparison& folderCmp, time_t timeFrom, time } -Opt<PathDependency> fff::getPathDependency(const AbstractPath& basePathL, const HardFilter& filterL, - const AbstractPath& basePathR, const HardFilter& filterR) +std::optional<PathDependency> fff::getPathDependency(const AbstractPath& basePathL, const HardFilter& filterL, + const AbstractPath& basePathR, const HardFilter& filterR) { if (!AFS::isNullPath(basePathL) && !AFS::isNullPath(basePathR)) { @@ -1159,7 +1159,7 @@ Opt<PathDependency> fff::getPathDependency(const AbstractPath& basePathL, const } } } - return NoValue(); + return {}; } //############################################################################################################ @@ -1211,12 +1211,12 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT { //start deleting existing target as required by copyFileTransactional(): //best amortized performance if "target existing" is the most common case - Opt<FileError> deletionError; + std::exception_ptr deletionError; auto tryDeleteTargetItem = [&] { if (overwriteIfExists) try { AFS::removeFilePlain(targetPath); /*throw FileError*/ } - catch (const FileError& e) { deletionError = e; } //probably "not existing" error, defer evaluation + catch (FileError&) { deletionError = std::current_exception(); } //probably "not existing" error, defer evaluation //else: copyFileTransactional() undefined behavior (fail/overwrite/auto-rename) }; @@ -1231,7 +1231,7 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT if (ps.relPath.empty()) //already existing { if (deletionError) - throw* deletionError; + std::rethrow_exception(deletionError); } else if (ps.relPath.size() > 1) //parent folder missing { diff --git a/FreeFileSync/Source/base/algorithm.h b/FreeFileSync/Source/base/algorithm.h index 178e056e..7e073945 100755 --- a/FreeFileSync/Source/base/algorithm.h +++ b/FreeFileSync/Source/base/algorithm.h @@ -48,8 +48,8 @@ struct PathDependency AbstractPath basePathChild; 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); +std::optional<PathDependency> getPathDependency(const AbstractPath& basePathL, const HardFilter& filterL, + const AbstractPath& basePathR, const HardFilter& filterR); std::pair<std::wstring, int> getSelectedItemsAsString( //returns string with item names and total count of selected(!) items, NOT total files/dirs! const std::vector<const FileSystemObject*>& selectionLeft, //all pointers need to be bound! diff --git a/FreeFileSync/Source/base/application.cpp b/FreeFileSync/Source/base/application.cpp index 9ccb0b05..a160ff06 100755 --- a/FreeFileSync/Source/base/application.cpp +++ b/FreeFileSync/Source/base/application.cpp @@ -23,6 +23,7 @@ #include "generate_logfile.h" #include "../ui/batch_status_handler.h" #include "../ui/main_dlg.h" +//#include "../fs/concrete.h" #include <gtk/gtk.h> @@ -252,7 +253,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs) try { if (getItemType(itemPath) == ItemType::FILE) //throw FileError - if (Opt<Zstring> parentPath = getParentFolderPath(itemPath)) + if (std::optional<Zstring> parentPath = getParentFolderPath(itemPath)) return *parentPath; } catch (FileError&) {} @@ -330,7 +331,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs) { return lpc != LocalPairConfig{ lpc.folderPathPhraseLeft, lpc.folderPathPhraseRight, - NoValue(), NoValue(), FilterConfig() }; + std::nullopt, std::nullopt, FilterConfig() }; }; auto replaceDirectories = [&](MainConfiguration& mainCfg) @@ -353,7 +354,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs) } else mainCfg.additionalPairs.push_back({ dirPathPhrasePairs[i].first, dirPathPhrasePairs[i].second, - NoValue(), NoValue(), FilterConfig() }); + std::nullopt, std::nullopt, FilterConfig() }); } return true; }; @@ -487,7 +488,7 @@ void showSyntaxHelp() L"\n" + _("config files:") + L"\n" + - _("Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.") + L"\n\n" + + _("Any number of FreeFileSync \"ffs_gui\" and/or \"ffs_batch\" configuration files.") + L"\n\n" + L"-DirPair " + _("directory") + L" " + _("directory") + L"\n" + _("Any number of alternative directory pairs for at most one config file.") + L"\n\n" + @@ -547,7 +548,7 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat const std::map<AbstractPath, size_t>& deviceParallelOps = batchCfg.mainCfg.deviceParallelOps; - std::set<Zstring, LessFilePath> logFilePathsToKeep; + std::set<AbstractPath> logFilePathsToKeep; for (const ConfigFileItem& item : globalCfg.gui.mainDlg.cfgFileHistory) logFilePathsToKeep.insert(item.logFilePath); @@ -559,8 +560,6 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat extractJobName(cfgFilePath), globalCfg.soundFileSyncFinished, syncStartTime, - batchCfg.batchExCfg.altLogfileCountMax, - batchCfg.batchExCfg.altLogFolderPathPhrase, batchCfg.mainCfg.ignoreErrors, batchCfg.batchExCfg.batchErrorHandling, batchCfg.mainCfg.automaticRetryCount, @@ -570,18 +569,6 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat batchCfg.batchExCfg.postSyncAction); try { - warn_static("consider for removal after FFS 10.3 release") -#if 1 - if (batchCfg.batchExCfg.altLogfileCountMax != 0) - { - if (!trimCpy(batchCfg.batchExCfg.altLogFolderPathPhrase).empty()) - statusHandler.reportWarning(replaceCpy(L"Beginning with FreeFileSync 10.3 the batch-specific log folder path %x will not be used.\n" - L"Instead all synchronization logs will be written into " + fmtPath(getDefaultLogFolderPath()) + - L"\n(See Menu -> Tools: Options; re-save this configuation to remove this warning)", - L"%x", fmtPath(batchCfg.batchExCfg.altLogFolderPathPhrase)), globalCfg.warnDlgs.warnBatchLoggingDeprecated); - } -#endif - //inform about (important) non-default global settings logNonDefaultSettings(globalCfg, statusHandler); //throw AbortProcess @@ -615,7 +602,7 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat } catch (AbortProcess&) {} //exit used by statusHandler - BatchStatusHandler::Result r = statusHandler.reportFinalStatus(globalCfg.logfilesMaxAgeDays, logFilePathsToKeep); //noexcept + BatchStatusHandler::Result r = statusHandler.reportFinalStatus(batchCfg.mainCfg.altLogFolderPathPhrase, globalCfg.logfilesMaxAgeDays, logFilePathsToKeep); //noexcept //---------------------------------------------------------------------- raiseReturnCode(returnCode, mapToReturnCode(r.finalStatus)); @@ -626,8 +613,8 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat { if (r.finalStatus != SyncResult::ABORTED) cfi.lastSyncTime = std::chrono::system_clock::to_time_t(syncStartTime); - assert(!r.logFilePath.empty()); - if (!r.logFilePath.empty()) + assert(!AFS::isNullPath(r.logFilePath)); + if (!AFS::isNullPath(r.logFilePath)) { cfi.logFilePath = r.logFilePath; cfi.logResult = r.finalStatus; diff --git a/FreeFileSync/Source/base/comparison.cpp b/FreeFileSync/Source/base/comparison.cpp index 00303c17..84b392cf 100755 --- a/FreeFileSync/Source/base/comparison.cpp +++ b/FreeFileSync/Source/base/comparison.cpp @@ -69,7 +69,7 @@ struct ResolvedBaseFolders ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& fpCfgList, const std::map<AbstractPath, size_t>& deviceParallelOps, - int folderAccessTimeout, + std::chrono::seconds folderAccessTimeout, bool allowUserInteraction, bool& warnFolderNotExisting, ProcessCallback& callback /*throw X*/) @@ -940,7 +940,7 @@ void fff::logNonDefaultSettings(const XmlGlobalSettings& activeSettings, Process changedSettingsMsg += L"\n " + _("File time tolerance") + L" - " + numberTo<std::wstring>(activeSettings.fileTimeTolerance); if (activeSettings.folderAccessTimeout != defaultSettings.folderAccessTimeout) - changedSettingsMsg += L"\n " + _("Folder access timeout") + L" - " + numberTo<std::wstring>(activeSettings.folderAccessTimeout); + changedSettingsMsg += L"\n " + _("Folder access timeout") + L" - " + numberTo<std::wstring>(activeSettings.folderAccessTimeout.count()); if (activeSettings.runWithBackgroundPriority != defaultSettings.runWithBackgroundPriority) changedSettingsMsg += L"\n " + _("Run with background priority") + L" - " + (activeSettings.runWithBackgroundPriority ? _("Enabled") : _("Disabled")); @@ -960,7 +960,7 @@ FolderComparison fff::compare(WarningDialogs& warnings, int fileTimeTolerance, bool allowUserInteraction, bool runWithBackgroundPriority, - int folderAccessTimeout, + std::chrono::seconds folderAccessTimeout, bool createDirLocks, std::unique_ptr<LockHolder>& dirLocks, const std::vector<FolderPairCfg>& fpCfgList, @@ -1035,8 +1035,8 @@ FolderComparison fff::compare(WarningDialogs& warnings, std::wstring msg; for (const auto& w : workLoad) - if (Opt<PathDependency> pd = getPathDependency(w.first.folderPathLeft, *w.second.filter.nameFilter, - w.first.folderPathRight, *w.second.filter.nameFilter)) + if (std::optional<PathDependency> pd = getPathDependency(w.first.folderPathLeft, *w.second.filter.nameFilter, + w.first.folderPathRight, *w.second.filter.nameFilter)) { msg += L"\n\n" + AFS::getDisplayPath(w.first.folderPathLeft) + L"\n" + @@ -1057,7 +1057,7 @@ FolderComparison fff::compare(WarningDialogs& warnings, { std::set<Zstring, LessFilePath> dirPathsExisting; for (const AbstractPath& folderPath : resInfo.existingBaseFolders) - if (Opt<Zstring> nativePath = AFS::getNativeItemPath(folderPath)) //restrict directory locking to native paths until further + if (std::optional<Zstring> nativePath = AFS::getNativeItemPath(folderPath)) //restrict directory locking to native paths until further dirPathsExisting.insert(*nativePath); dirLocks = std::make_unique<LockHolder>(dirPathsExisting, warnings.warnDirectoryLockFailed, callback); diff --git a/FreeFileSync/Source/base/comparison.h b/FreeFileSync/Source/base/comparison.h index 356dbe89..4c93a3ba 100755 --- a/FreeFileSync/Source/base/comparison.h +++ b/FreeFileSync/Source/base/comparison.h @@ -55,7 +55,7 @@ FolderComparison compare(WarningDialogs& warnings, int fileTimeTolerance, bool allowUserInteraction, bool runWithBackgroundPriority, - int folderAccessTimeout, + std::chrono::seconds folderAccessTimeout, bool createDirLocks, std::unique_ptr<LockHolder>& dirLocks, //out const std::vector<FolderPairCfg>& fpCfgList, diff --git a/FreeFileSync/Source/base/dir_exist_async.h b/FreeFileSync/Source/base/dir_exist_async.h index 345b29c2..acd79a69 100755 --- a/FreeFileSync/Source/base/dir_exist_async.h +++ b/FreeFileSync/Source/base/dir_exist_async.h @@ -30,7 +30,7 @@ struct FolderStatus }; FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath>& folderPaths, const std::map<AbstractPath, size_t>& deviceParallelOps, - int folderAccessTimeout, bool allowUserInteraction, + std::chrono::seconds folderAccessTimeout, bool allowUserInteraction, ProcessCallback& procCallback /*throw X*/) { using namespace zen; @@ -83,7 +83,7 @@ FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath>& folderPath procCallback.reportStatus(replaceCpy(_("Searching for folder %x..."), L"%x", displayPathFmt)); //throw X - while (std::chrono::steady_clock::now() < startTime + std::chrono::seconds(folderAccessTimeout) && + while (std::chrono::steady_clock::now() < startTime + folderAccessTimeout && fi.second.wait_for(UI_UPDATE_INTERVAL / 2) != std::future_status::ready) procCallback.requestUiRefresh(); //throw X diff --git a/FreeFileSync/Source/base/dir_lock.cpp b/FreeFileSync/Source/base/dir_lock.cpp index ab38ed53..161fb35e 100755 --- a/FreeFileSync/Source/base/dir_lock.cpp +++ b/FreeFileSync/Source/base/dir_lock.cpp @@ -12,9 +12,6 @@ #include <zen/guid.h> #include <zen/file_access.h> #include <zen/file_io.h> -#include <zen/optional.h> -//#include <wx/log.h> -//#include <wx/app.h> #include <fcntl.h> //open() #include <sys/stat.h> // @@ -46,7 +43,7 @@ public: void operator()() const //throw ThreadInterruption { - const Opt<Zstring> parentDirPath = getParentFolderPath(lockFilePath_); + const std::optional<Zstring> parentDirPath = getParentFolderPath(lockFilePath_); setCurrentThreadName(("DirLock: " + (parentDirPath ? utfTo<std::string>(*parentDirPath) : "")).c_str()); for (;;) @@ -91,10 +88,10 @@ Zstring abandonedLockDeletionName(const Zstring& lockFilePath) //make sure to NO using SessionId = pid_t; //return ppid on Windows, sid on Linux/Mac, "no value" if process corresponding to "processId" is not existing -Opt<SessionId> getSessionId(ProcessId processId) //throw FileError +std::optional<SessionId> getSessionId(ProcessId processId) //throw FileError { if (::kill(processId, 0) != 0) //sig == 0: no signal sent, just existence check - return NoValue(); + return {}; const pid_t procSid = ::getsid(processId); //NOT to be confused with "login session", e.g. not stable on OS X!!! if (procSid < 0) //pids are never negative, empiric proof: https://linux.die.net/man/2/wait @@ -151,7 +148,7 @@ LockInformation getLockInfoFromCurrentProcess() //throw FileError lockInfo.userId = numberTo<std::string>(userIdNo) + "(" + pwsEntry->pw_name + ")"; //follow Linux naming convention "1000(zenju)" - Opt<SessionId> sessionIdTmp = getSessionId(lockInfo.processId); //throw FileError + std::optional<SessionId> sessionIdTmp = getSessionId(lockInfo.processId); //throw FileError if (!sessionIdTmp) throw FileError(_("Cannot get process information."), L"no session id found"); //should not happen? lockInfo.sessionId = *sessionIdTmp; @@ -237,7 +234,7 @@ ProcessStatus getProcessStatus(const LockInformation& lockInfo) //throw FileErro lockInfo.processId == localInfo.processId) //obscure, but possible: deletion failed or a lock file is "stolen" and put back while the program is running return ProcessStatus::ITS_US; - if (Opt<SessionId> sessionId = getSessionId(lockInfo.processId)) //throw FileError + if (std::optional<SessionId> sessionId = getSessionId(lockInfo.processId)) //throw FileError return *sessionId == lockInfo.sessionId ? ProcessStatus::RUNNING : ProcessStatus::NOT_RUNNING; return ProcessStatus::NOT_RUNNING; } diff --git a/FreeFileSync/Source/base/file_hierarchy.cpp b/FreeFileSync/Source/base/file_hierarchy.cpp index a10646be..88f7f152 100755 --- a/FreeFileSync/Source/base/file_hierarchy.cpp +++ b/FreeFileSync/Source/base/file_hierarchy.cpp @@ -20,8 +20,8 @@ std::wstring fff::getShortDisplayNameForFolderPair(const AbstractPath& itemPathL AbstractPath tmpPathR = itemPathR; for (;;) { - Opt<AbstractPath> parentPathL = AFS::getParentFolderPath(tmpPathL); - Opt<AbstractPath> parentPathR = AFS::getParentFolderPath(tmpPathR); + std::optional<AbstractPath> parentPathL = AFS::getParentFolderPath(tmpPathL); + std::optional<AbstractPath> parentPathR = AFS::getParentFolderPath(tmpPathR); if (!parentPathL || !parentPathR) break; @@ -289,23 +289,24 @@ SyncOperation FilePair::applyMoveOptimization(SyncOperation op) const note: as long as we consider "create + delete" cases only, detection of renamed files, should be fine even for "binary" comparison variant! */ if (moveFileRef_) - if (auto refFile = dynamic_cast<const FilePair*>(FileSystemObject::retrieve(moveFileRef_))) //we expect a "FilePair", but only need a "FileSystemObject" - { - SyncOperation opRef = refFile->FileSystemObject::getSyncOperation(); //do *not* make a virtual call! - - if (op == SO_CREATE_NEW_LEFT && - opRef == SO_DELETE_LEFT) - op = SO_MOVE_LEFT_TO; - else if (op == SO_DELETE_LEFT && - opRef == SO_CREATE_NEW_LEFT) - op = SO_MOVE_LEFT_FROM; - else if (op == SO_CREATE_NEW_RIGHT && - opRef == SO_DELETE_RIGHT) - op = SO_MOVE_RIGHT_TO; - else if (op == SO_DELETE_RIGHT && - opRef == SO_CREATE_NEW_RIGHT) - op = SO_MOVE_RIGHT_FROM; - } + if (auto refFile = dynamic_cast<const FilePair*>(FileSystemObject::retrieve(moveFileRef_))) //we expect a "FilePair", but only need a "FileSystemObject" here + if (refFile->moveFileRef_ == getId()) //both ends should agree... + { + const SyncOperation opRef = refFile->FileSystemObject::getSyncOperation(); //do *not* make a virtual call! + + if (op == SO_CREATE_NEW_LEFT && + opRef == SO_DELETE_LEFT) + op = SO_MOVE_LEFT_TO; + else if (op == SO_DELETE_LEFT && + opRef == SO_CREATE_NEW_LEFT) + op = SO_MOVE_LEFT_FROM; + else if (op == SO_CREATE_NEW_RIGHT && + opRef == SO_DELETE_RIGHT) + op = SO_MOVE_RIGHT_TO; + else if (op == SO_DELETE_RIGHT && + opRef == SO_CREATE_NEW_RIGHT) + op = SO_MOVE_RIGHT_FROM; + } return op; } @@ -476,6 +477,7 @@ std::wstring fff::getSyncOpDescription(const FileSystemObject& fsObj) if (auto sourceFile = dynamic_cast<const FilePair*>(&fsObj)) if (auto targetFile = dynamic_cast<const FilePair*>(FileSystemObject::retrieve(sourceFile->getMoveRef()))) { + assert(targetFile->getMoveRef() == sourceFile->getId()); const bool onLeft = op == SO_MOVE_LEFT_FROM || op == SO_MOVE_LEFT_TO; const bool isSource = op == SO_MOVE_LEFT_FROM || op == SO_MOVE_RIGHT_FROM; diff --git a/FreeFileSync/Source/base/file_hierarchy.h b/FreeFileSync/Source/base/file_hierarchy.h index 1bae389f..0ea4a3bb 100755 --- a/FreeFileSync/Source/base/file_hierarchy.h +++ b/FreeFileSync/Source/base/file_hierarchy.h @@ -445,7 +445,7 @@ public: virtual SyncOperation getSyncOperation() const; std::wstring getSyncOpConflict() const; //return conflict when determining sync direction or (still unresolved) conflict during categorization - template <SelectedSide side> void removeObject(); //removes file or directory (recursively!) without physically removing the element: used by manual deletion + template <SelectedSide side> void removeObject(); //removes file or directory (recursively!) without physically removing the element: used by manual deletion const ContainerObject& parent() const { return parent_; } /**/ ContainerObject& parent() { return parent_; } @@ -543,9 +543,9 @@ private: void flip () override; void removeObjectL() override; void removeObjectR() override; - void notifySyncCfgChanged() override { syncOpBuffered_ = zen::NoValue(); FileSystemObject::notifySyncCfgChanged(); ContainerObject::notifySyncCfgChanged(); } + void notifySyncCfgChanged() override { syncOpBuffered_ = {}; FileSystemObject::notifySyncCfgChanged(); ContainerObject::notifySyncCfgChanged(); } - mutable zen::Opt<SyncOperation> syncOpBuffered_; //determining sync-op for directory may be expensive as it depends on child-objects => buffer + mutable std::optional<SyncOperation> syncOpBuffered_; //determining sync-op for directory may be expensive as it depends on child-objects => buffer FolderAttributes attrL_; FolderAttributes attrR_; diff --git a/FreeFileSync/Source/base/generate_logfile.cpp b/FreeFileSync/Source/base/generate_logfile.cpp index 221441b1..cbad8a4c 100755 --- a/FreeFileSync/Source/base/generate_logfile.cpp +++ b/FreeFileSync/Source/base/generate_logfile.cpp @@ -254,7 +254,7 @@ void limitLogfileCount(const AbstractPath& logFolderPath, //throw FileError tc.hour = 0; return localToTimeT(tc); //returns -1 on error => swallow => no versions trimmed by versionMaxAgeDays }(); - const time_t cutOffTime = lastMidnightTime - logfilesMaxAgeDays * 24 * 3600; + const time_t cutOffTime = lastMidnightTime - static_cast<time_t>(logfilesMaxAgeDays) * 24 * 3600; std::exception_ptr firstError; @@ -289,7 +289,7 @@ MessageType fff::getFinalMsgType(SyncResult finalStatus) case SyncResult::FINISHED_WITH_WARNINGS: return MSG_TYPE_WARNING; case SyncResult::FINISHED_WITH_ERROR: - case SyncResult::ABORTED: + case SyncResult::ABORTED: //= user cancel; *not* a MSG_TYPE_FATAL_ERROR! return MSG_TYPE_ERROR; } assert(false); @@ -297,21 +297,19 @@ MessageType fff::getFinalMsgType(SyncResult finalStatus) } -Zstring fff::saveLogFile(const ProcessSummary& summary, //throw FileError - const ErrorLog& log, - const std::chrono::system_clock::time_point& syncStartTime, - int logfilesMaxAgeDays, - const std::set<Zstring, LessFilePath>& logFilePathsToKeep, - const std::function<void(const std::wstring& msg)>& notifyStatus /*throw X*/) +AbstractPath fff::saveLogFile(const ProcessSummary& summary, //throw FileError + const ErrorLog& log, + const std::chrono::system_clock::time_point& syncStartTime, + const Zstring& altLogFolderPathPhrase, //optional + int logfilesMaxAgeDays, + const std::set<AbstractPath>& logFilePathsToKeep, + const std::function<void(const std::wstring& msg)>& notifyStatus /*throw X*/) { - //let's keep our log handling abstract; we might need it some time - const AbstractPath logFolderPath = createAbstractPath(getDefaultLogFolderPath()); + AbstractPath logFolderPath = createAbstractPath(altLogFolderPathPhrase); + if (AFS::isNullPath(logFolderPath)) + logFolderPath = createAbstractPath(getDefaultLogFolderPath()); - std::set<AbstractPath> abstractLogFilePathsToKeep; - for (const Zstring& filePath : logFilePathsToKeep) - abstractLogFilePathsToKeep.insert(createAbstractPath(filePath)); - - Opt<AbstractPath> logFilePath; + std::optional<AbstractPath> logFilePath; std::exception_ptr firstError; try { @@ -321,12 +319,12 @@ Zstring fff::saveLogFile(const ProcessSummary& summary, //throw FileError try { - limitLogfileCount(logFolderPath, logfilesMaxAgeDays, abstractLogFilePathsToKeep, notifyStatus); //throw FileError, X + limitLogfileCount(logFolderPath, logfilesMaxAgeDays, logFilePathsToKeep, notifyStatus); //throw FileError, X } catch (const FileError&) { if (!firstError) firstError = std::current_exception(); }; if (firstError) //late failure! std::rethrow_exception(firstError); - return *AFS::getNativeItemPath(*logFilePath); //logFilePath *is* native because getDefaultLogFolderPath() is! + return *logFilePath; } diff --git a/FreeFileSync/Source/base/generate_logfile.h b/FreeFileSync/Source/base/generate_logfile.h index 8b321c8b..dbb3cbd0 100755 --- a/FreeFileSync/Source/base/generate_logfile.h +++ b/FreeFileSync/Source/base/generate_logfile.h @@ -11,6 +11,7 @@ #include <zen/error_log.h> #include "return_codes.h" #include "status_handler.h" +#include "../fs/abstract.h" namespace fff @@ -18,12 +19,13 @@ namespace fff Zstring getDefaultLogFolderPath(); -Zstring saveLogFile(const ProcessSummary& summary, //throw FileError - const zen::ErrorLog& log, - const std::chrono::system_clock::time_point& syncStartTime, - int logfilesMaxAgeDays, - const std::set<Zstring, LessFilePath>& logFilePathsToKeep, - const std::function<void(const std::wstring& msg)>& notifyStatus /*throw X*/); +AbstractPath saveLogFile(const ProcessSummary& summary, //throw FileError + const zen::ErrorLog& log, + const std::chrono::system_clock::time_point& syncStartTime, + const Zstring& altLogFolderPathPhrase, //optional + int logfilesMaxAgeDays, + const std::set<AbstractPath>& logFilePathsToKeep, + const std::function<void(const std::wstring& msg)>& notifyStatus /*throw X*/); zen::MessageType getFinalMsgType(SyncResult finalStatus); } diff --git a/FreeFileSync/Source/base/icon_buffer.cpp b/FreeFileSync/Source/base/icon_buffer.cpp index 6c030bb5..a784f9e0 100755 --- a/FreeFileSync/Source/base/icon_buffer.cpp +++ b/FreeFileSync/Source/base/icon_buffer.cpp @@ -134,14 +134,14 @@ public: } //must be called by main thread only! => wxBitmap is NOT thread-safe like an int (non-atomic ref-count!!!) - Opt<wxBitmap> retrieve(const AbstractPath& filePath) + std::optional<wxBitmap> retrieve(const AbstractPath& filePath) { assert(runningMainThread()); std::lock_guard<std::mutex> dummy(lockIconList_); auto it = iconList.find(filePath); if (it == iconList.end()) - return NoValue(); + return {}; markAsHot(it); @@ -249,7 +249,7 @@ private: struct IconData { IconData() {} - IconData(IconData&& tmp) : iconRaw(std::move(tmp.iconRaw)), iconFmt(std::move(tmp.iconFmt)), prev(tmp.prev), next(tmp.next) {} + IconData(IconData&& tmp) noexcept : iconRaw(std::move(tmp.iconRaw)), iconFmt(std::move(tmp.iconFmt)), prev(tmp.prev), next(tmp.next) {} ImageHolder iconRaw; //native icon representation: may be used by any thread @@ -336,15 +336,15 @@ bool IconBuffer::readyForRetrieval(const AbstractPath& filePath) } -Opt<wxBitmap> IconBuffer::retrieveFileIcon(const AbstractPath& filePath) +std::optional<wxBitmap> IconBuffer::retrieveFileIcon(const AbstractPath& filePath) { - if (Opt<wxBitmap> ico = pimpl_->buffer.retrieve(filePath)) + if (std::optional<wxBitmap> ico = pimpl_->buffer.retrieve(filePath)) return ico; //since this icon seems important right now, we don't want to wait until next setWorkload() to start retrieving pimpl_->workload.add(filePath); pimpl_->buffer.limitSize(); - return NoValue(); + return {}; } diff --git a/FreeFileSync/Source/base/icon_buffer.h b/FreeFileSync/Source/base/icon_buffer.h index f0ad78e4..2f5e4e60 100755 --- a/FreeFileSync/Source/base/icon_buffer.h +++ b/FreeFileSync/Source/base/icon_buffer.h @@ -10,7 +10,6 @@ #include <vector> #include <memory> #include <zen/zstring.h> -#include <zen/optional.h> #include <wx/bitmap.h> #include "../fs/abstract.h" @@ -35,7 +34,7 @@ public: void setWorkload (const std::vector<AbstractPath>& load); //(re-)set new workload of icons to be retrieved; bool readyForRetrieval(const AbstractPath& filePath); - zen::Opt<wxBitmap> retrieveFileIcon (const AbstractPath& filePath); //... and mark as hot + std::optional<wxBitmap> retrieveFileIcon (const AbstractPath& filePath); //... and mark as hot wxBitmap getIconByExtension(const Zstring& filePath); //...and add to buffer //retrieveFileIcon() + getIconByExtension() are safe to call from within WM_PAINT handler! no COM calls (...on calling thread) diff --git a/FreeFileSync/Source/base/localization.cpp b/FreeFileSync/Source/base/localization.cpp index bc27e6ea..28f19e33 100755 --- a/FreeFileSync/Source/base/localization.cpp +++ b/FreeFileSync/Source/base/localization.cpp @@ -39,18 +39,19 @@ public: std::wstring translate(const std::wstring& text) const override { //look for translation in buffer table - auto it = transMapping.find(text); - if (it != transMapping.end() && !it->second.empty()) + auto it = transMapping_.find(text); + if (it != transMapping_.end() && !it->second.empty()) return it->second; return text; //fallback } std::wstring translate(const std::wstring& singular, const std::wstring& plural, int64_t n) const override { - auto it = transMappingPl.find({ singular, plural }); - if (it != transMappingPl.end()) + auto it = transMappingPl_.find({ singular, plural }); + if (it != transMappingPl_.end()) { - const size_t formNo = pluralParser->getForm(n); + const size_t formNo = pluralParser_->getForm(n); + assert(formNo < it->second.size()); if (formNo < it->second.size()) return replaceCpy(it->second[formNo], L"%x", formatNumber(n)); } @@ -61,9 +62,9 @@ private: using Translation = std::unordered_map<std::wstring, std::wstring>; //hash_map is 15% faster than std::map on GCC using TranslationPlural = std::map<std::pair<std::wstring, std::wstring>, std::vector<std::wstring>>; - Translation transMapping; //map original text |-> translation - TranslationPlural transMappingPl; - std::unique_ptr<plural::PluralForm> pluralParser; //bound! + Translation transMapping_; //map original text |-> translation + TranslationPlural transMappingPl_; + std::unique_ptr<plural::PluralForm> pluralParser_; //bound! const wxLanguage langId_; }; @@ -82,30 +83,31 @@ FFSTranslation::FFSTranslation(const Zstring& lngFilePath, wxLanguage langId) : } lng::TransHeader header; - lng::TranslationMap transInput; - lng::TranslationPluralMap transPluralInput; - lng::parseLng(inputStream, header, transInput, transPluralInput); //throw ParsingError + lng::TranslationMap transUtf; + lng::TranslationPluralMap transPluralUtf; + lng::parseLng(inputStream, header, transUtf, transPluralUtf); //throw ParsingError - for (const auto& item : transInput) + pluralParser_ = std::make_unique<plural::PluralForm>(header.pluralDefinition); //throw plural::ParsingError + + for (const auto& item : transUtf) { - const std::wstring original = utfTo<std::wstring>(item.first); - const std::wstring translation = utfTo<std::wstring>(item.second); - transMapping.emplace(original, translation); + std::wstring original = utfTo<std::wstring>(item.first); + std::wstring translation = utfTo<std::wstring>(item.second); + + transMapping_.emplace(std::move(original), std::move(translation)); } - for (const auto& item : transPluralInput) + for (const auto& item : transPluralUtf) { - const std::wstring engSingular = utfTo<std::wstring>(item.first.first); - const std::wstring engPlural = utfTo<std::wstring>(item.first.second); + std::wstring engSingular = utfTo<std::wstring>(item.first.first); + std::wstring engPlural = utfTo<std::wstring>(item.first.second); - std::vector<std::wstring> plFormsWide; + std::vector<std::wstring> pluralForms; for (const std::string& pf : item.second) - plFormsWide.push_back(utfTo<std::wstring>(pf)); + pluralForms.push_back(utfTo<std::wstring>(pf)); - transMappingPl.insert({ { engSingular, engPlural }, plFormsWide }); + transMappingPl_.insert({ { std::move(engSingular), std::move(engPlural) }, std::move(pluralForms) }); } - - pluralParser = std::make_unique<plural::PluralForm>(header.pluralDefinition); //throw plural::ParsingError } diff --git a/FreeFileSync/Source/base/parallel_scan.cpp b/FreeFileSync/Source/base/parallel_scan.cpp index 805c4223..cc432462 100755 --- a/FreeFileSync/Source/base/parallel_scan.cpp +++ b/FreeFileSync/Source/base/parallel_scan.cpp @@ -172,8 +172,8 @@ public: AFS::TraverserCallback::HandleError rv = *errorResponse_; - errorRequest_ = NoValue(); - errorResponse_ = NoValue(); + errorRequest_ = {}; + errorResponse_ = {}; dummy.unlock(); //optimization for condition_variable::notify_all() conditionReadyForNewRequest_.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796 @@ -294,8 +294,8 @@ private: 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<AFS::TraverserCallback::HandleError> errorResponse_; + std::optional<std::pair<std::wstring, size_t>> errorRequest_; //error message + retry number + std::optional<AFS::TraverserCallback::HandleError> errorResponse_; size_t threadsToFinish_; //can't use activeThreadIdxs_.size() which is locked by different mutex! //also note: activeThreadIdxs_.size() may be 0 during worker thread construction! @@ -390,8 +390,6 @@ void DirCallback::onFile(const AFS::FileInfo& fi) //throw ThreadInterruption const Zstring fileRelPath = parentRelPathPf_ + fi.itemName; - warn_static("why call reportCurrentFile() per file at all? should be sufficient to do per folder only!") - //update status information no matter whether item is excluded or not! if (cfg_.acb.mayReportCurrentFile(cfg_.threadIdx, cfg_.lastReportTime)) cfg_.acb.reportCurrentFile(AFS::getDisplayPath(AFS::appendRelPath(cfg_.baseFolderPath, fileRelPath))); diff --git a/FreeFileSync/Source/base/parallel_scan.h b/FreeFileSync/Source/base/parallel_scan.h index fd9bc242..bbe1071f 100755 --- a/FreeFileSync/Source/base/parallel_scan.h +++ b/FreeFileSync/Source/base/parallel_scan.h @@ -43,7 +43,7 @@ struct DirectoryValue { FolderContainer folderCont; - //relative paths (or empty string for root) for directories that could not be read (completely), e.g. access denied, or temporal network drop + //relative paths (or empty string for root) for directories that could not be read (completely), e.g. access denied, or temporary network drop std::map<Zstring, std::wstring, LessFilePath> failedFolderReads; //with corresponding error message //relative paths (never empty) for failure to read single file/dir/symlink with corresponding error message diff --git a/FreeFileSync/Source/base/parse_lng.h b/FreeFileSync/Source/base/parse_lng.h index 0ddc86c0..b48af8b2 100755 --- a/FreeFileSync/Source/base/parse_lng.h +++ b/FreeFileSync/Source/base/parse_lng.h @@ -13,7 +13,6 @@ #include <memory> #include <map> #include <set> -//#include <sstream> #include <stdexcept> #include <string> #include <vector> @@ -477,69 +476,68 @@ private: checkPlaceholder("%y"); checkPlaceholder("%z"); - auto ampersandTokenCount = [](const std::string& str) -> size_t - { - const std::string tmp = replaceCpy(str, "&&", ""); //make sure to not catch && which windows resolves as just one & for display! - return std::count(tmp.begin(), tmp.end(), '&'); - }; + //if source is a one-liner, so should be the translation + if (!contains(original, '\n') && contains(translation, '\n')) + throw ParsingError({ L"Source text is a one-liner, but translation consists of multiple lines", scn_.posRow(), scn_.posCol() }); //if source contains ampersand to mark menu accellerator key, so must translation - const size_t ampCountOrig = ampersandTokenCount(original); - if (ampCountOrig != ampersandTokenCount(translation) || - ampCountOrig > 1) + const size_t ampCount = ampersandTokenCount(original); + if (ampCount > 1 || ampCount != ampersandTokenCount(translation)) throw ParsingError({ L"Source and translation both need exactly one & character to mark a menu item access key or none at all", scn_.posRow(), scn_.posCol() }); //ampersand at the end makes buggy wxWidgets crash miserably - if (ampCountOrig > 0) - if ((endsWith(original, "&") && !endsWith(original, "&&")) || - (endsWith(translation, "&") && !endsWith(translation, "&&"))) - throw ParsingError({ L"The & character to mark a menu item access key must not occur at the end of a string", scn_.posRow(), scn_.posCol() }); + if (endsWithSingleAmp(original) || endsWithSingleAmp(translation)) + throw ParsingError({ L"The & character to mark a menu item access key must not occur at the end of a string", scn_.posRow(), scn_.posCol() }); //if source ends with colon, so must translation (note: character seems to be universally used, even for asian and arabic languages) - if (endsWith(original, ":") && - !endsWith(translation, ":") && - !endsWith(translation, "\xef\xbc\x9a")) //chinese colon + if (endsWith(original, ":") && !endsWithColon(translation)) throw ParsingError({ L"Source text ends with a colon character \":\", but translation does not", scn_.posRow(), scn_.posCol() }); - auto endsWithSingleDot = [](const std::string& s) { return endsWith(s, ".") && !endsWith(s, ".."); }; - //if source ends with a period, so must translation (note: character seems to be universally used, even for asian and arabic languages) - if (endsWithSingleDot(original) && - !endsWithSingleDot(translation) && - !endsWith(translation, "\xe0\xa5\xa4") && //hindi period - !endsWith(translation, "\xe3\x80\x82")) //chinese period + if (endsWithSingleDot(original) && !endsWithSingleDot(translation)) throw ParsingError({ L"Source text ends with a punctuation mark character \".\", but translation does not", scn_.posRow(), scn_.posCol() }); //if source ends with an ellipsis, so must translation (note: character seems to be universally used, even for asian and arabic languages) - if (endsWith(original, "...") && - !endsWith(translation, "...") && - !endsWith(translation, "\xe2\x80\xa6")) //narrow ellipsis (spanish?) + if (endsWithEllipsis(original) && !endsWithEllipsis(translation)) throw ParsingError({ L"Source text ends with an ellipsis \"...\", but translation does not", scn_.posRow(), scn_.posCol() }); - //if source is a one-liner, so should be the translation - if (!contains(original, '\n') && contains(translation, '\n')) - throw ParsingError({ L"Source text is a one-liner, but translation consists of multiple lines", scn_.posRow(), scn_.posCol() }); - - //check for correct FFS brand names - if (contains(original, "FreeFileSync") && !contains(translation, "FreeFileSync")) - throw ParsingError({ L"Misspelled \"FreeFileSync\" in translation", scn_.posRow(), scn_.posCol() }); - if (contains(original, "RealTimeSync") && !contains(translation, "RealTimeSync")) - throw ParsingError({ L"Misspelled \"RealTimeSync\" in translation", scn_.posRow(), scn_.posCol() }); + //check for not-to-be-translated texts + for (const char* fixedStr : { "FreeFileSync", "RealTimeSync", "ffs_gui", "ffs_batch", "ffs_tmp", "GlobalSettings.xml" }) + if (contains(original, fixedStr) && !contains(translation, fixedStr)) + throw ParsingError({ replaceCpy<std::wstring>(L"Misspelled \"%x\" in translation", L"%x", utfTo<std::wstring>(fixedStr)), scn_.posRow(), scn_.posCol() }); + + //some languages (French!) put a space before punctuation mark => must be a no-brake space! + for (const char punctChar : std::string(".!?:;$#")) + if (contains(original, std::string(" ") + punctChar) || + contains(translation, std::string(" ") + punctChar)) + throw ParsingError({ replaceCpy<std::wstring>(L"Text contains a space before the \"%x\" character. Are line-breaks really allowed here?" + " Maybe this should be a \"non-breaking space\" (Windows: Alt 0160 UTF8: 0xC2 0xA0)?", + L"%x", utfTo<std::wstring>(punctChar)), scn_.posRow(), scn_.posCol() }); } } void validateTranslation(const SingularPluralPair& original, const PluralForms& translation, const plural::PluralFormInfo& pluralInfo) //throw ParsingError { using namespace zen; + + if (original.first.empty() || original.second.empty()) + throw ParsingError({ L"Translation source text is empty", scn_.posRow(), scn_.posCol() }); + + const std::vector<std::string> allTexts = [&] + { + std::vector<std::string> at{ original.first, original.second }; + at.insert(at.end(), translation.begin(), translation.end()); + return at; + }(); + + for (const std::string& str : allTexts) + if (!isValidUtf(str)) + throw ParsingError({ L"Text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol() }); + //check the primary placeholder is existing at least for the second english text if (!contains(original.second, "%x")) throw ParsingError({ L"Plural form source text does not contain %x placeholder", scn_.posRow(), scn_.posCol() }); - if (!isValidUtf(original.first) || !isValidUtf(original.second)) - throw ParsingError({ L"Translation source text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol() }); - if (std::any_of(translation.begin(), translation.end(), [](const std::string& pform) { return !isValidUtf(pform); })) - throw ParsingError({ L"Translation text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol() }); - if (!translation.empty()) { //check for invalid number of plural forms @@ -578,29 +576,108 @@ private: auto checkSecondaryPlaceholder = [&](const std::string& placeholder) { - //make sure secondary placeholder is used in both source texts (or none) - if (zen::contains(original.first, placeholder) || - zen::contains(original.second, placeholder)) - { - if (!zen::contains(original.first, placeholder) || - !zen::contains(original.second, placeholder)) - throw ParsingError({ zen::replaceCpy<std::wstring>(L"Placeholder %x missing in plural form source", L"%x", zen::utfTo<std::wstring>(placeholder)), scn_.posRow(), scn_.posCol() }); - - //secondary placeholder is required for all plural forms - if (std::any_of(translation.begin(), translation.end(), [&](const std::string& pform) { return !zen::contains(pform, placeholder); })) - throw ParsingError({ zen::replaceCpy<std::wstring>(L"Placeholder %x missing in plural form translation", L"%x", zen::utfTo<std::wstring>(placeholder)), scn_.posRow(), scn_.posCol() }); - } + //make sure secondary placeholder is used for both source texts (or none) and all plural forms + if (contains(original.first, placeholder) || + contains(original.second, placeholder)) + for (const std::string& str : allTexts) + if (!contains(str, placeholder)) + throw ParsingError({ zen::replaceCpy<std::wstring>(L"Placeholder %x missing in text", L"%x", zen::utfTo<std::wstring>(placeholder)), scn_.posRow(), scn_.posCol() }); }; checkSecondaryPlaceholder("%y"); checkSecondaryPlaceholder("%z"); //if source is a one-liner, so should be the translation if (!contains(original.first, '\n') && !contains(original.second, '\n') && - std::any_of(translation.begin(), translation.end(), [](const std::string& pform) { return contains(pform, '\n'); })) - throw ParsingError({ L"Source text is a one-liner, but at least one plural form translation consists of multiple lines", scn_.posRow(), scn_.posCol() }); + /**/std::any_of(translation.begin(), translation.end(), [](const std::string& pform) { return contains(pform, '\n'); })) + /**/throw ParsingError({ L"Source text is a one-liner, but at least one plural form translation consists of multiple lines", scn_.posRow(), scn_.posCol() }); + + //if source contains ampersand to mark menu accellerator key, so must translation + const size_t ampCount = ampersandTokenCount(original.first); + for (const std::string& str : allTexts) + if (ampCount > 1 || ampersandTokenCount(str) != ampCount) + throw ParsingError({ L"Source and translation both need exactly one & character to mark a menu item access key or none at all", scn_.posRow(), scn_.posCol() }); + + //ampersand at the end makes buggy wxWidgets crash miserably + for (const std::string& str : allTexts) + if (endsWithSingleAmp(str)) + throw ParsingError({ L"The & character to mark a menu item access key must not occur at the end of a string", scn_.posRow(), scn_.posCol() }); + + //if source ends with colon, so must translation (note: character seems to be universally used, even for asian and arabic languages) + if (endsWith(original.first, ":") || endsWith(original.second, ":")) + for (const std::string& str : allTexts) + if (!endsWithColon(str)) + throw ParsingError({ L"Source text ends with a colon character \":\", but translation does not", scn_.posRow(), scn_.posCol() }); + + //if source ends with a period, so must translation (note: character seems to be universally used, even for asian and arabic languages) + if (endsWithSingleDot(original.first) || endsWithSingleDot(original.second)) + for (const std::string& str : allTexts) + if (!endsWithSingleDot(str)) + throw ParsingError({ L"Source text ends with a punctuation mark character \".\", but translation does not", scn_.posRow(), scn_.posCol() }); + + //if source ends with an ellipsis, so must translation (note: character seems to be universally used, even for asian and arabic languages) + if (endsWithEllipsis(original.first) || endsWithEllipsis(original.second)) + for (const std::string& str : allTexts) + if (!endsWithEllipsis(str)) + throw ParsingError({ L"Source text ends with an ellipsis \"...\", but translation does not", scn_.posRow(), scn_.posCol() }); + + //check for not-to-be-translated texts + for (const char* fixedStr : { "FreeFileSync", "RealTimeSync", "ffs_gui", "ffs_batch", "ffs_tmp", "GlobalSettings.xml" }) + if (contains(original.first, fixedStr) || contains(original.second, fixedStr)) + for (const std::string& str : allTexts) + if (!contains(str, fixedStr)) + throw ParsingError({ replaceCpy<std::wstring>(L"Misspelled \"%x\" in translation", L"%x", utfTo<std::wstring>(fixedStr)), scn_.posRow(), scn_.posCol() }); + + //some languages (French!) put a space before punctuation mark => must be a no-brake space! + for (const char punctChar : std::string(".!?:;$#")) + for (const std::string& str : allTexts) + if (contains(str, std::string(" ") + punctChar)) + throw ParsingError({ replaceCpy<std::wstring>(L"Text contains a space before the \"%x\" character. Are line-breaks really allowed here?" + " Maybe this should be a \"non-breaking space\" (Windows: Alt 0160 UTF8: 0xC2 0xA0)?", + L"%x", utfTo<std::wstring>(punctChar)), scn_.posRow(), scn_.posCol() }); } } + //helper + static size_t ampersandTokenCount(const std::string& str) + { + using namespace zen; + const std::string tmp = replaceCpy(str, "&&", ""); //make sure to not catch && which windows resolves as just one & for display! + return std::count(tmp.begin(), tmp.end(), '&'); + } + + static bool endsWithSingleAmp(const std::string& s) + { + using namespace zen; + return endsWith(s, "&") && !endsWith(s, "&&"); + } + + static bool endsWithEllipsis(const std::string& s) + { + using namespace zen; + return endsWith(s, "...") || + endsWith(s, "\xe2\x80\xa6"); //narrow ellipsis (spanish?) + } + + static bool endsWithColon(const std::string& s) + { + using namespace zen; + return endsWith(s, ":") || + endsWith(s, "\xef\xbc\x9a"); //chinese colon + } + + static bool endsWithSingleDot(const std::string& s) + { + using namespace zen; + return (endsWith(s, ".") || + endsWith(s, "\xe0\xa5\xa4") || //hindi period + endsWith(s, "\xe3\x80\x82")) //chinese period + && + (!endsWith(s, "..") && + !endsWith(s, "\xe0\xa5\xa4\xe0\xa5\xa4") && //hindi period + !endsWith(s, "\xe3\x80\x82\xe3\x80\x82")); //chinese period + } + + void nextToken() { tk_ = scn_.nextToken(); } const Token& token() const { return tk_; } diff --git a/FreeFileSync/Source/base/perf_check.cpp b/FreeFileSync/Source/base/perf_check.cpp index ae4a8cc8..7ea523e8 100755 --- a/FreeFileSync/Source/base/perf_check.cpp +++ b/FreeFileSync/Source/base/perf_check.cpp @@ -20,9 +20,9 @@ PerfCheck::PerfCheck(std::chrono::milliseconds windowSizeRemTime, windowMax_(std::max(windowSizeRemTime, windowSizeSpeed)) {} -void PerfCheck::addSample(std::chrono::nanoseconds timeElapsed, int itemsCurrent, double dataCurrent) +void PerfCheck::addSample(std::chrono::nanoseconds timeElapsed, int itemsCurrent, double bytesCurrent) { - samples_.insert(samples_.end(), { timeElapsed, { itemsCurrent, dataCurrent }}); //use fact that time is monotonously ascending + samples_.insert(samples_.end(), { timeElapsed, { itemsCurrent, bytesCurrent }}); //use fact that time is monotonously ascending //remove all records earlier than "now - windowMax" auto it = samples_.upper_bound(timeElapsed - windowMax_); @@ -49,49 +49,38 @@ std::tuple<double /*timeDelta*/, int /*itemsDelta*/, double /*bytesDelta*/> Perf } -Opt<double> PerfCheck::getRemainingTimeSec(double dataRemaining) const +std::optional<double> PerfCheck::getRemainingTimeSec(double bytesRemaining) const { - double timeDelta = 0; - int itemsDelta = 0; - double bytesDelta = 0; - std::tie(timeDelta, itemsDelta, bytesDelta) = getBlockDeltas(windowSizeRemTime_); - //const auto [timeDelta, itemsDelta, bytesDelta] = getBlockDeltas(windowSizeRemTime_); C++17 + const auto [timeDelta, itemsDelta, bytesDelta] = getBlockDeltas(windowSizeRemTime_); //objects model logical operations *NOT* disk accesses, so we better play safe and use "bytes" only! - //http://sourceforge.net/p/freefilesync/feature-requests/197/ if (!numeric::isNull(bytesDelta)) //sign(dataRemaining) != sign(bytesDelta) usually an error, so show it! - return dataRemaining * timeDelta / bytesDelta; + return bytesRemaining * timeDelta / bytesDelta; - return NoValue(); + return {}; } -Opt<std::wstring> PerfCheck::getBytesPerSecond() const +std::optional<std::wstring> PerfCheck::getBytesPerSecond() const { - double timeDelta = 0; - int itemsDelta = 0; - double bytesDelta = 0; - std::tie(timeDelta, itemsDelta, bytesDelta) = getBlockDeltas(windowSizeSpeed_); + const auto [timeDelta, itemsDelta, bytesDelta] = getBlockDeltas(windowSizeSpeed_); if (!numeric::isNull(timeDelta)) return formatFilesizeShort(numeric::round(bytesDelta / timeDelta)) + _("/sec"); - return NoValue(); + return {}; } -Opt<std::wstring> PerfCheck::getItemsPerSecond() const +std::optional<std::wstring> PerfCheck::getItemsPerSecond() const { - double timeDelta = 0; - int itemsDelta = 0; - double bytesDelta = 0; - std::tie(timeDelta, itemsDelta, bytesDelta) = getBlockDeltas(windowSizeSpeed_); + const auto [timeDelta, itemsDelta, bytesDelta] = getBlockDeltas(windowSizeSpeed_); if (!numeric::isNull(timeDelta)) return replaceCpy(_("%x items/sec"), L"%x", formatTwoDigitPrecision(itemsDelta / timeDelta)); - return NoValue(); + return {}; } diff --git a/FreeFileSync/Source/base/perf_check.h b/FreeFileSync/Source/base/perf_check.h index b4845a90..a00aae84 100755 --- a/FreeFileSync/Source/base/perf_check.h +++ b/FreeFileSync/Source/base/perf_check.h @@ -10,7 +10,7 @@ #include <map> #include <chrono> #include <string> -#include <zen/optional.h> +#include <zen/legacy_compiler.h> //#includes <optional> namespace fff @@ -21,11 +21,11 @@ public: PerfCheck(std::chrono::milliseconds windowSizeRemTime, std::chrono::milliseconds windowSizeSpeed); - void addSample(std::chrono::nanoseconds timeElapsed, int itemsCurrent, double dataCurrent); + void addSample(std::chrono::nanoseconds timeElapsed, int itemsCurrent, double bytesCurrent); - zen::Opt<double> getRemainingTimeSec(double dataRemaining) const; - zen::Opt<std::wstring> getBytesPerSecond() const; //for window - zen::Opt<std::wstring> getItemsPerSecond() const; // + std::optional<double> getRemainingTimeSec(double bytesRemaining) const; + std::optional<std::wstring> getBytesPerSecond() const; //for window + std::optional<std::wstring> getItemsPerSecond() const; // private: struct Record diff --git a/FreeFileSync/Source/base/process_xml.cpp b/FreeFileSync/Source/base/process_xml.cpp index e9a6fd47..da7fa2e7 100755 --- a/FreeFileSync/Source/base/process_xml.cpp +++ b/FreeFileSync/Source/base/process_xml.cpp @@ -9,22 +9,22 @@ #include <zen/file_access.h> #include <zen/file_io.h> #include <zen/xml_io.h> -#include <zen/optional.h> #include <zen/time.h> #include <wx/intl.h> #include "ffs_paths.h" -//#include "../fs/concrete.h" +#include "../fs/concrete.h" using namespace zen; using namespace fff; //functionally needed for correct overload resolution!!! +//using AFS = AbstractFileSystem; namespace { //------------------------------------------------------------------------------------------------------------------------------- -const int XML_FORMAT_VER_GLOBAL = 10; //2018-07-27 -const int XML_FORMAT_VER_FFS_CFG = 13; //2018-07-14 +const int XML_FORMAT_VER_GLOBAL = 11; //2018-09-09 +const int XML_FORMAT_VER_FFS_CFG = 14; //2018-08-13 //------------------------------------------------------------------------------------------------------------------------------- } @@ -914,9 +914,9 @@ bool readStruc(const XmlElement& input, ConfigFileItem& value) const bool rv3 = in.attribute("LastSync", value.lastSyncTime); - Zstring logPathRaw; - const bool rv4 = in.attribute("LogPath", logPathRaw); - if (rv4) value.logFilePath = resolveFreeFileSyncDriveMacro(logPathRaw); + Zstring logPathPhrase; + const bool rv4 = in.attribute("LogPath", logPathPhrase); + if (rv4) value.logFilePath = createAbstractPath(resolveFreeFileSyncDriveMacro(logPathPhrase)); return rv1 && rv2 && rv3 && rv4; } @@ -928,7 +928,11 @@ void writeStruc(const ConfigFileItem& value, XmlElement& output) out.attribute("Result", value.logResult); out.attribute("CfgPath", substituteFreeFileSyncDriveLetter(value.cfgFilePath)); out.attribute("LastSync", value.lastSyncTime); - out.attribute("LogPath", substituteFreeFileSyncDriveLetter(value.logFilePath)); + + if (std::optional<Zstring> nativePath = AFS::getNativeItemPath(value.logFilePath)) + out.attribute("LogPath", substituteFreeFileSyncDriveLetter(*nativePath)); + else + out.attribute("LogPath", AFS::getInitPathPhrase(value.logFilePath)); } //TODO: remove after migration! 2018-07-27 @@ -1227,7 +1231,6 @@ void readConfig(const XmlIn& in, MainConfiguration& mainCfg, int formatVer) if (formatVer < 8) inMain["OnCompletion"](mainCfg.postSyncCommand); else - { //TODO: remove if parameter migration after some time! 2018-02-24 if (formatVer < 10) inMain["IgnoreErrors"](mainCfg.ignoreErrors); @@ -1238,6 +1241,17 @@ void readConfig(const XmlIn& in, MainConfiguration& mainCfg, int formatVer) inMain["Errors"].attribute("Delay", mainCfg.automaticRetryDelay); } + //TODO: remove if parameter migration after some time! 2018-08-13 + if (formatVer < 14) + ; //path will be extracted from BatchExclusiveConfig + else + inMain["LogFolder"](mainCfg.altLogFolderPathPhrase); + + //TODO: remove if parameter migration after some time! 2017-10-24 + if (formatVer < 8) + inMain["OnCompletion"](mainCfg.postSyncCommand); + else + { inMain["PostSyncCommand"](mainCfg.postSyncCommand); inMain["PostSyncCommand"].attribute("Condition", mainCfg.postSyncCondition); } @@ -1318,18 +1332,6 @@ void readConfig(const XmlIn& in, BatchExclusiveConfig& cfg, int formatVer) } else inBatchCfg["PostSyncAction"](cfg.postSyncAction); - - //TODO: remove if clause after migration! 2018-07-12 - if (formatVer < 13) - { - inBatchCfg["LogfileFolder"](cfg.altLogFolderPathPhrase); - inBatchCfg["LogfileFolder"].attribute("Limit", cfg.altLogfileCountMax); - } - else - { - inBatchCfg["LogfileFolder"](cfg.altLogFolderPathPhrase); - inBatchCfg["LogfileFolder"].attribute("MaxCount", cfg.altLogfileCountMax); - } } @@ -1338,6 +1340,13 @@ void readConfig(const XmlIn& in, XmlBatchConfig& cfg, int formatVer) readConfig(in, cfg.mainCfg, formatVer); readConfig(in, cfg.batchExCfg, formatVer); + //TODO: remove if clause after migration! 2018-08-13 + if (formatVer < 14) + { + XmlIn inBatchCfg = in[formatVer < 10 ? "BatchConfig" : "Batch"]; + inBatchCfg["LogfileFolder"](cfg.mainCfg.altLogFolderPathPhrase); + } + //TODO: remove if clause after migration! 2017-10-24 if (formatVer < 8) { @@ -1396,6 +1405,11 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& cfg, int formatVer) inGeneral["NotificationSound" ].attribute("SyncFinished", cfg.soundFileSyncFinished); inGeneral["ProgressDialog" ].attribute("AutoClose", cfg.autoCloseProgressDialog); + //TODO: remove if parameter migration after some time! 2018-08-13 + if (formatVer < 14) + if (cfg.logfilesMaxAgeDays == 14) //default value was too small + cfg.logfilesMaxAgeDays = XmlGlobalSettings().logfilesMaxAgeDays; + //TODO: remove old parameter after migration! 2018-02-04 if (formatVer < 8) { @@ -1431,7 +1445,6 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& cfg, int formatVer) inOpt["WarnDependentBaseFolders" ].attribute("Show", cfg.warnDlgs.warnDependentBaseFolders); inOpt["WarnDirectoryLockFailed" ].attribute("Show", cfg.warnDlgs.warnDirectoryLockFailed); inOpt["WarnVersioningFolderPartOfSync"].attribute("Show", cfg.warnDlgs.warnVersioningFolderPartOfSync); - inOpt["WarnBatchLoggingDeprecated" ].attribute("Show", cfg.warnDlgs.warnBatchLoggingDeprecated); } //gui specific global settings (optional) @@ -1445,22 +1458,27 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& cfg, int formatVer) inWnd.attribute("PosY", cfg.gui.mainDlg.dlgPos.y); inWnd.attribute("Maximized", cfg.gui.mainDlg.isMaximized); - XmlIn inCopyTo = inWnd["ManualCopyTo"]; - inCopyTo.attribute("KeepRelativePaths", cfg.gui.mainDlg.copyToCfg.keepRelPaths); - inCopyTo.attribute("OverwriteIfExists", cfg.gui.mainDlg.copyToCfg.overwriteIfExists); - - XmlIn inCopyToHistory = inCopyTo["FolderHistory"]; - inCopyToHistory(cfg.gui.mainDlg.copyToCfg.folderHistory); - inCopyToHistory.attribute("LastUsedPath", cfg.gui.mainDlg.copyToCfg.lastUsedPath); - inCopyToHistory.attribute("MaxSize", cfg.gui.mainDlg.copyToCfg.historySizeMax); + //########################################################### //TODO: remove old parameter after migration! 2018-02-04 if (formatVer < 8) inWnd["CaseSensitiveSearch"].attribute("Enabled", cfg.gui.mainDlg.textSearchRespectCase); else - inWnd["Search"].attribute("CaseSensitive", cfg.gui.mainDlg.textSearchRespectCase); + //TODO: remove if parameter migration after some time! 2018-09-09 + if (formatVer < 11) + inWnd["Search"].attribute("CaseSensitive", cfg.gui.mainDlg.textSearchRespectCase); + else + inWnd["SearchPanel"].attribute("CaseSensitive", cfg.gui.mainDlg.textSearchRespectCase); + + //TODO: remove if parameter migration after some time! 2018-09-09 + if (formatVer < 11) + inWnd["FolderPairsVisible" ].attribute("Max", cfg.gui.mainDlg.maxFolderPairsVisible); - inWnd["FolderPairsVisible" ].attribute("Max", cfg.gui.mainDlg.maxFolderPairsVisible); + //TODO: remove if parameter migration after some time! 2018-09-09 + if (formatVer < 11) + ; + else + inWnd["FolderHistory" ].attribute("MaxSize", cfg.gui.mainDlg.folderHistItemsMax); //########################################################### @@ -1485,7 +1503,7 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& cfg, int formatVer) inGui["ConfigHistory"](cfgHist); for (const Zstring& cfgPath : cfgHist) - cfg.gui.mainDlg.cfgFileHistory.emplace_back(cfgPath, 0, Zstring(), SyncResult::FINISHED_WITH_SUCCESS); + cfg.gui.mainDlg.cfgFileHistory.emplace_back(cfgPath, 0, getNullPath(), SyncResult::FINISHED_WITH_SUCCESS); } //TODO: remove after migration! 2018-07-27 else if (formatVer < 10) @@ -1496,7 +1514,7 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& cfg, int formatVer) inConfig["Configurations"](cfgFileHistory); for (const ConfigFileItemV9& item : cfgFileHistory) - cfg.gui.mainDlg.cfgFileHistory.emplace_back(item.filePath, item.lastSyncTime, Zstring(), SyncResult::FINISHED_WITH_SUCCESS); + cfg.gui.mainDlg.cfgFileHistory.emplace_back(item.filePath, item.lastSyncTime, getNullPath(), SyncResult::FINISHED_WITH_SUCCESS); } else { @@ -1540,7 +1558,16 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& cfg, int formatVer) inFileGrid.attribute("ShowIcons", cfg.gui.mainDlg.showIcons); inFileGrid.attribute("IconSize", cfg.gui.mainDlg.iconSize); inFileGrid.attribute("SashOffset", cfg.gui.mainDlg.sashOffset); - inFileGrid.attribute("HistoryMaxSize", cfg.gui.mainDlg.folderHistItemsMax); + + //TODO: remove if parameter migration after some time! 2018-09-09 + if (formatVer < 11) + ; + else + inFileGrid.attribute("MaxFolderPairsShown", cfg.gui.mainDlg.maxFolderPairsVisible); + + //TODO: remove if parameter migration after some time! 2018-09-09 + if (formatVer < 11) + inFileGrid.attribute("HistoryMaxSize", cfg.gui.mainDlg.folderHistItemsMax); inFileGrid["ColumnsLeft"].attribute("PathFormat", cfg.gui.mainDlg.itemPathFormatLeftGrid); inFileGrid["ColumnsLeft"](cfg.gui.mainDlg.columnAttribLeft); @@ -1560,6 +1587,19 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& cfg, int formatVer) inGui["FolderHistoryLeft"].attribute("MaxSize", cfg.gui.mainDlg.folderHistItemsMax); } + //TODO: remove if parameter migration after some time! 2018-09-09 + if (formatVer < 11) + if (cfg.gui.mainDlg.folderHistItemsMax == 15) //default value was too small + cfg.gui.mainDlg.folderHistItemsMax = XmlGlobalSettings().gui.mainDlg.folderHistItemsMax; + + //########################################################### + XmlIn inCopyTo = inWnd["ManualCopyTo"]; + inCopyTo.attribute("KeepRelativePaths", cfg.gui.mainDlg.copyToCfg.keepRelPaths); + inCopyTo.attribute("OverwriteIfExists", cfg.gui.mainDlg.copyToCfg.overwriteIfExists); + + XmlIn inCopyToHistory = inCopyTo["FolderHistory"]; + inCopyToHistory(cfg.gui.mainDlg.copyToCfg.folderHistory); + inCopyToHistory.attribute("LastUsedPath", cfg.gui.mainDlg.copyToCfg.lastUsedPath); //########################################################### inWnd["DefaultViewFilter"](cfg.gui.mainDlg.viewFilterDefault); @@ -1955,6 +1995,8 @@ void writeConfig(const MainConfiguration& mainCfg, XmlOut& out) outMain["Errors"].attribute("Retry", mainCfg.automaticRetryCount); outMain["Errors"].attribute("Delay", mainCfg.automaticRetryDelay); + outMain["LogFolder"](mainCfg.altLogFolderPathPhrase); + outMain["PostSyncCommand"](mainCfg.postSyncCommand); outMain["PostSyncCommand"].attribute("Condition", mainCfg.postSyncCondition); } @@ -1979,8 +2021,6 @@ void writeConfig(const BatchExclusiveConfig& cfg, XmlOut& out) outBatchCfg["ProgressDialog"].attribute("AutoClose", cfg.autoCloseSummary); outBatchCfg["ErrorDialog" ](cfg.batchErrorHandling); outBatchCfg["PostSyncAction"](cfg.postSyncAction); - outBatchCfg["LogfileFolder"](cfg.altLogFolderPathPhrase); - outBatchCfg["LogfileFolder"].attribute("MaxCount", cfg.altLogfileCountMax); } @@ -2025,7 +2065,6 @@ void writeConfig(const XmlGlobalSettings& cfg, XmlOut& out) outOpt["WarnDependentBaseFolders" ].attribute("Show", cfg.warnDlgs.warnDependentBaseFolders); outOpt["WarnDirectoryLockFailed" ].attribute("Show", cfg.warnDlgs.warnDirectoryLockFailed); outOpt["WarnVersioningFolderPartOfSync"].attribute("Show", cfg.warnDlgs.warnVersioningFolderPartOfSync); - outOpt["WarnBatchLoggingDeprecated" ].attribute("Show", cfg.warnDlgs.warnBatchLoggingDeprecated); //gui specific global settings (optional) XmlOut outGui = out["Gui"]; @@ -2038,18 +2077,9 @@ void writeConfig(const XmlGlobalSettings& cfg, XmlOut& out) outWnd.attribute("PosY", cfg.gui.mainDlg.dlgPos.y); outWnd.attribute("Maximized", cfg.gui.mainDlg.isMaximized); - XmlOut outCopyTo = outWnd["ManualCopyTo"]; - outCopyTo.attribute("KeepRelativePaths", cfg.gui.mainDlg.copyToCfg.keepRelPaths); - outCopyTo.attribute("OverwriteIfExists", cfg.gui.mainDlg.copyToCfg.overwriteIfExists); - - XmlOut outCopyToHistory = outCopyTo["FolderHistory"]; - outCopyToHistory(cfg.gui.mainDlg.copyToCfg.folderHistory); - outCopyToHistory.attribute("LastUsedPath", cfg.gui.mainDlg.copyToCfg.lastUsedPath); - outCopyToHistory.attribute("MaxSize", cfg.gui.mainDlg.copyToCfg.historySizeMax); - - outWnd["Search" ].attribute("CaseSensitive", cfg.gui.mainDlg.textSearchRespectCase); - outWnd["FolderPairsVisible"].attribute("Max", cfg.gui.mainDlg.maxFolderPairsVisible); - + //########################################################### + outWnd["SearchPanel" ].attribute("CaseSensitive", cfg.gui.mainDlg.textSearchRespectCase); + outWnd["FolderHistory"].attribute("MaxSize", cfg.gui.mainDlg.folderHistItemsMax); //########################################################### XmlOut outConfig = outWnd["ConfigPanel"]; @@ -2084,7 +2114,7 @@ void writeConfig(const XmlGlobalSettings& cfg, XmlOut& out) outFileGrid.attribute("ShowIcons", cfg.gui.mainDlg.showIcons); outFileGrid.attribute("IconSize", cfg.gui.mainDlg.iconSize); outFileGrid.attribute("SashOffset", cfg.gui.mainDlg.sashOffset); - outFileGrid.attribute("HistoryMaxSize", cfg.gui.mainDlg.folderHistItemsMax); + outFileGrid.attribute("MaxFolderPairsShown", cfg.gui.mainDlg.maxFolderPairsVisible); outFileGrid["ColumnsLeft"].attribute("PathFormat", cfg.gui.mainDlg.itemPathFormatLeftGrid); outFileGrid["ColumnsLeft"](cfg.gui.mainDlg.columnAttribLeft); @@ -2097,6 +2127,14 @@ void writeConfig(const XmlGlobalSettings& cfg, XmlOut& out) outFileGrid["FolderHistoryRight"](cfg.gui.mainDlg.folderHistoryRight); //########################################################### + XmlOut outCopyTo = outWnd["ManualCopyTo"]; + outCopyTo.attribute("KeepRelativePaths", cfg.gui.mainDlg.copyToCfg.keepRelPaths); + outCopyTo.attribute("OverwriteIfExists", cfg.gui.mainDlg.copyToCfg.overwriteIfExists); + + XmlOut outCopyToHistory = outCopyTo["FolderHistory"]; + outCopyToHistory(cfg.gui.mainDlg.copyToCfg.folderHistory); + outCopyToHistory.attribute("LastUsedPath", cfg.gui.mainDlg.copyToCfg.lastUsedPath); + //########################################################### outWnd["DefaultViewFilter"](cfg.gui.mainDlg.viewFilterDefault); outWnd["Perspective" ](cfg.gui.mainDlg.guiPerspectiveLast); diff --git a/FreeFileSync/Source/base/process_xml.h b/FreeFileSync/Source/base/process_xml.h index cd11a3b8..17cb884a 100755 --- a/FreeFileSync/Source/base/process_xml.h +++ b/FreeFileSync/Source/base/process_xml.h @@ -72,11 +72,6 @@ struct BatchExclusiveConfig bool runMinimized = false; bool autoCloseSummary = false; PostSyncAction postSyncAction = PostSyncAction::NONE; - warn_static("consider for removal after FFS 10.3 release") -#if 1 - Zstring altLogFolderPathPhrase; //store log file copy (in addition to %appdata%\FreeFileSync\Logs): MANDATORY if altLogfileCountMax != 0 - int altLogfileCountMax = 0; //max log file count; 0 := don't save logfiles; < 0 := no limit -#endif }; @@ -115,10 +110,6 @@ struct WarningDialogs bool warnInputFieldEmpty = true; bool warnDirectoryLockFailed = true; bool warnVersioningFolderPartOfSync = true; - warn_static("consider for removal after FFS 10.3 release") -#if 1 - bool warnBatchLoggingDeprecated = true; -#endif }; inline bool operator==(const WarningDialogs& lhs, const WarningDialogs& rhs) { @@ -132,8 +123,7 @@ inline bool operator==(const WarningDialogs& lhs, const WarningDialogs& rhs) lhs.warnRecyclerMissing == rhs.warnRecyclerMissing && lhs.warnInputFieldEmpty == rhs.warnInputFieldEmpty && lhs.warnDirectoryLockFailed == rhs.warnDirectoryLockFailed && - lhs.warnVersioningFolderPartOfSync == rhs.warnVersioningFolderPartOfSync && - lhs.warnBatchLoggingDeprecated == rhs.warnBatchLoggingDeprecated; + lhs.warnVersioningFolderPartOfSync == rhs.warnVersioningFolderPartOfSync; } inline bool operator!=(const WarningDialogs& lhs, const WarningDialogs& rhs) { return !(lhs == rhs); } @@ -185,11 +175,11 @@ struct XmlGlobalSettings bool copyFilePermissions = false; int fileTimeTolerance = 2; //max. allowed file time deviation; < 0 means unlimited tolerance; default 2s: FAT vs NTFS - int folderAccessTimeout = 20; //unit: [s]; consider CD-ROM insert or hard disk spin up time from sleep + std::chrono::seconds folderAccessTimeout{20}; //consider CD-ROM insert or hard disk spin up time from sleep bool runWithBackgroundPriority = false; bool createLockFile = true; bool verifyFileCopy = false; - int logfilesMaxAgeDays = 14; //<= 0 := no limit; for log files under %appdata%\FreeFileSync\Logs + int logfilesMaxAgeDays = 30; //<= 0 := no limit; for log files under %AppData%\FreeFileSync\Logs Zstring soundFileCompareFinished; Zstring soundFileSyncFinished = Zstr("gong.wav"); @@ -208,15 +198,6 @@ struct XmlGlobalSettings wxSize dlgSize; bool isMaximized = false; - struct - { - bool keepRelPaths = false; - bool overwriteIfExists = false; - Zstring lastUsedPath; - std::vector<Zstring> folderHistory; - size_t historySizeMax = 15; - } copyToCfg; - bool textSearchRespectCase = false; //good default for Linux, too! int maxFolderPairsVisible = 6; @@ -234,9 +215,18 @@ struct XmlGlobalSettings bool treeGridLastSortAscending = getDefaultSortDirection(treeGridLastSortColumnDefault); // std::vector<ColAttributesTree> treeGridColumnAttribs = getTreeGridDefaultColAttribs(); + size_t folderHistItemsMax = 20; + + struct + { + bool keepRelPaths = false; + bool overwriteIfExists = false; + Zstring lastUsedPath; + std::vector<Zstring> folderHistory; + } copyToCfg; + std::vector<Zstring> folderHistoryLeft; std::vector<Zstring> folderHistoryRight; - size_t folderHistItemsMax = 15; bool showIcons = true; FileIconSize iconSize = ICON_SIZE_SMALL; int sashOffset = 0; diff --git a/FreeFileSync/Source/base/resolve_path.cpp b/FreeFileSync/Source/base/resolve_path.cpp index b40b91ce..f8eae4d9 100755 --- a/FreeFileSync/Source/base/resolve_path.cpp +++ b/FreeFileSync/Source/base/resolve_path.cpp @@ -4,7 +4,6 @@ #include <zen/time.h> #include <zen/thread.h> #include <zen/utf.h> -#include <zen/optional.h> #include <zen/scope_guard.h> #include <zen/globals.h> #include <zen/file_access.h> @@ -17,13 +16,13 @@ using namespace zen; namespace { -Opt<Zstring> getEnvironmentVar(const Zstring& name) +std::optional<Zstring> getEnvironmentVar(const Zstring& name) { assert(runningMainThread()); //getenv() is not thread-safe! const char* buffer = ::getenv(name.c_str()); //no extended error reporting if (!buffer) - return NoValue(); + return {}; Zstring value(buffer); //some postprocessing: @@ -56,7 +55,7 @@ Zstring resolveRelativePath(const Zstring& relativePath) */ if (startsWith(relativePath, "~/") || relativePath == "~") { - Opt<Zstring> homeDir = getEnvironmentVar("HOME"); + std::optional<Zstring> homeDir = getEnvironmentVar("HOME"); if (!homeDir) return relativePath; //error! no further processing! @@ -79,7 +78,7 @@ Zstring resolveRelativePath(const Zstring& relativePath) //returns value if resolved -Opt<Zstring> tryResolveMacro(const Zstring& macro) //macro without %-characters +std::optional<Zstring> tryResolveMacro(const Zstring& macro) //macro without %-characters { //there exist environment variables named %TIME%, %DATE% so check for our internal macros first! if (strEqual(macro, Zstr("time"), CmpAsciiNoCase())) @@ -111,11 +110,11 @@ Opt<Zstring> tryResolveMacro(const Zstring& macro) //macro without %-characters if (resolveTimePhrase(Zstr("sec" ), Zstr("%S"))) return timeStr; //try to resolve as environment variable - if (Opt<Zstring> value = getEnvironmentVar(macro)) + if (std::optional<Zstring> value = getEnvironmentVar(macro)) return *value; - return NoValue(); + return {}; } const Zchar MACRO_SEP = Zstr('%'); @@ -133,7 +132,7 @@ Zstring fff::expandMacros(const Zstring& text) Zstring potentialMacro = beforeFirst(rest, MACRO_SEP, IF_MISSING_RETURN_NONE); Zstring postfix = afterFirst (rest, MACRO_SEP, IF_MISSING_RETURN_NONE); //text == prefix + MACRO_SEP + potentialMacro + MACRO_SEP + postfix - if (Opt<Zstring> value = tryResolveMacro(potentialMacro)) + if (std::optional<Zstring> value = tryResolveMacro(potentialMacro)) return prefix + *value + expandMacros(postfix); else return prefix + MACRO_SEP + potentialMacro + expandMacros(MACRO_SEP + postfix); @@ -183,7 +182,7 @@ void getDirectoryAliasesRecursive(const Zstring& pathPhrase, std::set<Zstring, L //get list of useful variables auto addEnvVar = [&](const Zstring& envName) { - if (Opt<Zstring> value = getEnvironmentVar(envName)) + if (std::optional<Zstring> value = getEnvironmentVar(envName)) macroList.emplace_back(envName, *value); }; addEnvVar("HOME"); //Linux: /home/<user> Mac: /Users/<user> @@ -236,7 +235,7 @@ Zstring fff::getResolvedFilePath(const Zstring& pathPhrase) //noexcept //remove leading/trailing whitespace before allowing misinterpretation in applyLongPathPrefix() trim(path, true, false); - //don't remove all whitespace from right, e.g. 0xa0 may be used as part of folder name + //don't remove all whitespace from right, e.g. 0xa0 may be used as part of a folder name trim(path, false, true, [](Zchar c) { return c == Zstr(' '); }); @@ -257,7 +256,7 @@ Zstring fff::getResolvedFilePath(const Zstring& pathPhrase) //noexcept path = resolveRelativePath(path); //remove trailing slash, unless volume root: - if (Opt<PathComponents> pc = parsePathComponents(path)) + if (std::optional<PathComponents> pc = parsePathComponents(path)) { if (pc->relPath.empty()) path = pc->rootPath; diff --git a/FreeFileSync/Source/base/status_handler.h b/FreeFileSync/Source/base/status_handler.h index 4145b795..9a3c0948 100755 --- a/FreeFileSync/Source/base/status_handler.h +++ b/FreeFileSync/Source/base/status_handler.h @@ -64,7 +64,7 @@ struct Statistics virtual ProgressStats getStatsCurrent(ProcessCallback::Phase phase) const = 0; virtual ProgressStats getStatsTotal (ProcessCallback::Phase phase) const = 0; - virtual zen::Opt<AbortTrigger> getAbortStatus() const = 0; + virtual std::optional<AbortTrigger> getAbortStatus() const = 0; virtual const std::wstring& currentStatusText() const = 0; }; @@ -155,7 +155,7 @@ public: const std::wstring& currentStatusText() const override { return statusText_; } - zen::Opt<AbortTrigger> getAbortStatus() const override { return abortRequested_; } + std::optional<AbortTrigger> getAbortStatus() const override { return abortRequested_; } private: void updateData(std::vector<ProgressStats>& num, int itemsDelta, int64_t bytesDelta) @@ -190,17 +190,17 @@ private: std::vector<ProgressStats> statsTotal_ = std::vector<ProgressStats>(4); // std::wstring statusText_; - zen::Opt<AbortTrigger> abortRequested_; + std::optional<AbortTrigger> abortRequested_; }; //------------------------------------------------------------------------------------------ inline -void delayAndCountDown(const std::wstring& operationName, size_t delayInSec, const std::function<void(const std::wstring& msg)>& notifyStatus) +void delayAndCountDown(const std::wstring& operationName, std::chrono::seconds delay, const std::function<void(const std::wstring& msg)>& notifyStatus) { assert(notifyStatus && !zen::endsWith(operationName, L".")); - const auto delayUntil = std::chrono::steady_clock::now() + std::chrono::seconds(delayInSec); + const auto delayUntil = std::chrono::steady_clock::now() + delay; for (auto now = std::chrono::steady_clock::now(); now < delayUntil; now = std::chrono::steady_clock::now()) { const auto timeMs = std::chrono::duration_cast<std::chrono::milliseconds>(delayUntil - now).count(); diff --git a/FreeFileSync/Source/base/status_handler_impl.h b/FreeFileSync/Source/base/status_handler_impl.h index d152149f..21f108de 100755 --- a/FreeFileSync/Source/base/status_handler_impl.h +++ b/FreeFileSync/Source/base/status_handler_impl.h @@ -7,7 +7,6 @@ #ifndef STATUS_HANDLER_IMPL_H_07682758976 #define STATUS_HANDLER_IMPL_H_07682758976 -#include <zen/optional.h> #include <zen/file_error.h> #include <zen/thread.h> #include "process_callback.h" @@ -40,6 +39,7 @@ public: std::lock_guard<std::mutex> dummy(lockCurrentStatus_); if (ThreadStatus* ts = getThreadStatus()) //call while holding "lockCurrentStatus_" lock!! ts->statusMsg = msg; + else assert(false); } zen::interruptionPoint(); //throw ThreadInterruption } @@ -80,8 +80,8 @@ public: ProcessCallback::Response rv = *errorResponse_; - errorRequest_ = zen::NoValue(); - errorResponse_ = zen::NoValue(); + errorRequest_ = {}; + errorResponse_ = {}; dummy.unlock(); //optimization for condition_variable::notify_all() conditionReadyForNewRequest_.notify_all(); //=> spurious wake-up for AsyncCallback::logInfo() @@ -111,7 +111,7 @@ public: if (logInfoRequest_) { cb.logInfo(*logInfoRequest_); - logInfoRequest_ = zen::NoValue(); + logInfoRequest_ = {}; conditionReadyForNewRequest_.notify_all(); //=> spurious wake-up for AsyncCallback::reportError() } if (finishNowRequest_) @@ -131,6 +131,7 @@ public: void notifyTaskBegin(size_t prio) //noexcept { assert(!zen::runningMainThread()); + assert(!getThreadStatus()); const uint64_t threadId = zen::getThreadId(); std::lock_guard<std::mutex> dummy(lockCurrentStatus_); @@ -196,10 +197,9 @@ private: const uint64_t threadId = zen::getThreadId(); for (auto& sbp : statusByPriority_) - for (ThreadStatus& ts : sbp) //thread cound is (hopefully) small enough so that linear search won't hurt perf + for (ThreadStatus& ts : sbp) //thread count is (hopefully) small enough so that linear search won't hurt perf if (ts.threadId == threadId) return &ts; - assert(false); return nullptr; } @@ -274,9 +274,9 @@ private: std::condition_variable conditionReadyForNewRequest_; std::condition_variable conditionNewRequest; std::condition_variable conditionHaveResponse_; - zen::Opt<ErrorInfo> errorRequest_; - zen::Opt<ProcessCallback::Response> errorResponse_; - zen::Opt<std::wstring> logInfoRequest_; + std::optional<ErrorInfo> errorRequest_; + std::optional<ProcessCallback::Response> errorResponse_; + std::optional<std::wstring> logInfoRequest_; bool finishNowRequest_ = false; //---- status updates ---- diff --git a/FreeFileSync/Source/base/structures.cpp b/FreeFileSync/Source/base/structures.cpp index bc9699f9..19c39aac 100755 --- a/FreeFileSync/Source/base/structures.cpp +++ b/FreeFileSync/Source/base/structures.cpp @@ -593,11 +593,11 @@ MainConfiguration fff::merge(const std::vector<MainConfiguration>& mainCfgs) //if local config matches output global config we don't need local one if (lpc.localCmpCfg && effectivelyEqual(*lpc.localCmpCfg, cmpCfgHead)) - lpc.localCmpCfg = NoValue(); + lpc.localCmpCfg = {}; if (lpc.localSyncCfg && effectivelyEqual(*lpc.localSyncCfg, syncCfgHead)) - lpc.localSyncCfg = NoValue(); + lpc.localSyncCfg = {}; if (allFiltersEqual) //use global filter in this case lpc.localFilter = FilterConfig(); @@ -625,6 +625,13 @@ MainConfiguration fff::merge(const std::vector<MainConfiguration>& mainCfgs) cfgOut.automaticRetryDelay = std::max_element(mainCfgs.begin(), mainCfgs.end(), [](const MainConfiguration& lhs, const MainConfiguration& rhs) { return lhs.automaticRetryDelay < rhs.automaticRetryDelay; })->automaticRetryDelay; + for (const MainConfiguration& mainCfg : mainCfgs) + if (!mainCfg.altLogFolderPathPhrase.empty()) + { + cfgOut.altLogFolderPathPhrase = mainCfg.altLogFolderPathPhrase; + break; + } + //cfgOut.postSyncCommand = mainCfgs[0].postSyncCommand; -> better leave at default ... !? //cfgOut.postSyncCondition = mainCfgs[0].postSyncCondition; -> return cfgOut; diff --git a/FreeFileSync/Source/base/structures.h b/FreeFileSync/Source/base/structures.h index 7f0ad9e1..8668acd2 100755 --- a/FreeFileSync/Source/base/structures.h +++ b/FreeFileSync/Source/base/structures.h @@ -9,8 +9,8 @@ #include <vector> #include <memory> +#include <chrono> #include <zen/zstring.h> -#include <zen/optional.h> #include "../fs/abstract.h" @@ -38,9 +38,9 @@ enum class SymLinkHandling enum class SyncDirection : unsigned char //save space for use in FileSystemObject! { + NONE, LEFT, - RIGHT, - NONE + RIGHT }; @@ -356,8 +356,8 @@ struct LocalPairConfig //enhanced folder pairs with (optional) alternate configu LocalPairConfig(const Zstring& phraseLeft, const Zstring& phraseRight, - const zen::Opt<CompConfig>& cmpCfg, - const zen::Opt<SyncConfig>& syncCfg, + const std::optional<CompConfig>& cmpCfg, + const std::optional<SyncConfig>& syncCfg, const FilterConfig& filter) : folderPathPhraseLeft (phraseLeft), folderPathPhraseRight(phraseRight), @@ -368,8 +368,8 @@ struct LocalPairConfig //enhanced folder pairs with (optional) alternate configu Zstring folderPathPhraseLeft; //unresolved directory names as entered by user! Zstring folderPathPhraseRight; // - zen::Opt<CompConfig> localCmpCfg; - zen::Opt<SyncConfig> localSyncCfg; + std::optional<CompConfig> localCmpCfg; + std::optional<SyncConfig> localSyncCfg; FilterConfig localFilter; }; @@ -407,7 +407,9 @@ struct MainConfiguration bool ignoreErrors = false; //true: errors will still be logged size_t automaticRetryCount = 0; - size_t automaticRetryDelay = 5; //unit: [sec] + std::chrono::seconds automaticRetryDelay{5}; + + Zstring altLogFolderPathPhrase; //fill to use different log file folder (other than the default %appdata%\FreeFileSync\Logs) Zstring postSyncCommand; //user-defined command line PostSyncCondition postSyncCondition = PostSyncCondition::COMPLETION; @@ -434,6 +436,7 @@ bool operator==(const MainConfiguration& lhs, const MainConfiguration& rhs) lhs.ignoreErrors == rhs.ignoreErrors && lhs.automaticRetryCount == rhs.automaticRetryCount && lhs.automaticRetryDelay == rhs.automaticRetryDelay && + lhs.altLogFolderPathPhrase == rhs.altLogFolderPathPhrase && lhs.postSyncCommand == rhs.postSyncCommand && lhs.postSyncCondition == rhs.postSyncCondition; } diff --git a/FreeFileSync/Source/base/synchronization.cpp b/FreeFileSync/Source/base/synchronization.cpp index 96244abd..d55a57a8 100755 --- a/FreeFileSync/Source/base/synchronization.cpp +++ b/FreeFileSync/Source/base/synchronization.cpp @@ -387,7 +387,7 @@ std::vector<FolderPairSyncCfg> fff::extractSyncCfg(const MainConfiguration& main namespace { inline -Opt<SelectedSide> getTargetDirection(SyncOperation syncOp) +std::optional<SelectedSide> getTargetDirection(SyncOperation syncOp) { switch (syncOp) { @@ -412,7 +412,7 @@ Opt<SelectedSide> getTargetDirection(SyncOperation syncOp) case SO_UNRESOLVED_CONFLICT: break; //nothing to do } - return NoValue(); + return {}; } @@ -456,7 +456,7 @@ void verifyFiles(const AbstractPath& sourcePath, const AbstractPath& targetPath, { //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)) + if (std::optional<Zstring> nativeTargetPath = AFS::getNativeItemPath(targetPath)) flushFileBuffers(*nativeTargetPath); //throw FileError if (!filesHaveSameContent(sourcePath, targetPath, notifyUnbufferedIO)) //throw FileError @@ -485,7 +485,7 @@ AFS::ItemType getItemType(const AbstractPath& ap, std::mutex& singleThread) //th { return parallelScope([ap] { return AFS::getItemType(ap); /*throw FileError*/ }, singleThread); } inline -Opt<AFS::ItemType> getItemTypeIfExists(const AbstractPath& ap, std::mutex& singleThread) //throw FileError +std::optional<AFS::ItemType> getItemTypeIfExists(const AbstractPath& ap, std::mutex& singleThread) //throw FileError { return parallelScope([ap] { return AFS::getItemTypeIfExists(ap); /*throw FileError*/ }, singleThread); } inline @@ -963,11 +963,16 @@ private: RingBuffer<Workload::WorkItems> getFolderLevelWorkItems(PassNo pass, ContainerObject& parentFolder, Workload& workload); - template <SelectedSide side> - void setup2StepMove(FilePair& sourceObj, FilePair& targetObj); //throw FileError, ThreadInterruption - bool createParentFolder(FileSystemObject& fsObj); //throw FileError, ThreadInterruption - template <SelectedSide side> - void resolveMoveConflicts(FilePair& sourceObj, FilePair& targetObj); //throw FileError, ThreadInterruption + enum class CmtfStatus //CreateMoveTargetFolderStatus + { + AVAILABLE, + NAME_CLASH, + SOURCE_MISSING + }; + template <SelectedSide side> void setup2StepMove(FilePair& sourceFile, FilePair& targetFile); //throw FileError, ThreadInterruption + template <SelectedSide side> CmtfStatus createMoveTargetFolder(FileSystemObject& fsObj); //throw FileError, ThreadInterruption + template <SelectedSide side> void resolveMoveConflicts(FilePair& sourceFile, FilePair& targetFile); //throw FileError, ThreadInterruption + void prepareFileMove(FilePair& file); //throw ThreadInterruption void synchronizeFile(FilePair& file); // @@ -1028,9 +1033,9 @@ private: | ================= ============= | ... | GUI <-- |Main Thread| \|/ \|/ -Callback ============= ------------------- - | Workload | - ------------------- +Callback ============= -------------------- + | Workload | + -------------------- 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) @@ -1167,12 +1172,12 @@ bool haveNameClash(const Zstring& shortname, const List& m) template <SelectedSide side> -void FolderPairSyncer::setup2StepMove(FilePair& sourceObj, //throw FileError, ThreadInterruption - FilePair& targetObj) +void FolderPairSyncer::setup2StepMove(FilePair& sourceFile, //throw FileError, ThreadInterruption + FilePair& targetFile) { //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()))); - const Zstring fileName = sourceObj.getItemName<side>(); + const Zstring fileName = sourceFile.getItemName<side>(); auto it = find_last(fileName.begin(), fileName.end(), Zstr('.')); //gracefully handle case of missing "." const Zstring sourceRelPathTmp = Zstring(fileName.begin(), it) + Zstr('.') + shortGuid + AFS::TEMP_FILE_ENDING; @@ -1181,55 +1186,121 @@ void FolderPairSyncer::setup2StepMove(FilePair& sourceObj, //throw FileError, Th //the very same (.ffs_tmp) name and is copied before the second step of the move is executed //good news: even in this pathologic case, this may only prevent the copy of the other file, but not this move - const AbstractPath sourcePathTmp = AFS::appendRelPath(sourceObj.base().getAbstractPath<side>(), sourceRelPathTmp); + const AbstractPath sourcePathTmp = AFS::appendRelPath(sourceFile.base().getAbstractPath<side>(), sourceRelPathTmp); reportInfo(txtMovingFileXtoY_, //ThreadInterruption - AFS::getDisplayPath(sourceObj.getAbstractPath<side>()), + AFS::getDisplayPath(sourceFile.getAbstractPath<side>()), AFS::getDisplayPath(sourcePathTmp)); - parallel::renameItem(sourceObj.getAbstractPath<side>(), sourcePathTmp, singleThread_); //throw FileError, (ErrorDifferentVolume) + parallel::renameItem(sourceFile.getAbstractPath<side>(), sourcePathTmp, singleThread_); //throw FileError, (ErrorDifferentVolume) //TODO: prepare2StepMove: consider ErrorDifferentVolume! e.g. symlink aliasing! //update file hierarchy - FilePair& tempFile = sourceObj.base().addSubFile<side>(afterLast(sourceRelPathTmp, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL), sourceObj.getAttributes<side>()); + FilePair& tempFile = sourceFile.base().addSubFile<side>(afterLast(sourceRelPathTmp, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL), sourceFile.getAttributes<side>()); static_assert(std::is_same_v<ContainerObject::FileList, std::list<FilePair>>, "ATTENTION: we're adding to the file list WHILE looping over it! This is only working because std::list iterators are not invalidated by insertion!"); - sourceObj.removeObject<side>(); //remove only *after* evaluating "sourceObj, side"! + sourceFile.removeObject<side>(); //remove only *after* evaluating "sourceFile, side"! //note: this new item is *not* considered at the end of 0th pass because "!sourceWillBeDeleted && !haveNameClash" //prepare move in second pass tempFile.setSyncDir(side == LEFT_SIDE ? SyncDirection::LEFT : SyncDirection::RIGHT); - targetObj.setMoveRef(tempFile .getId()); - tempFile .setMoveRef(targetObj.getId()); + targetFile.setMoveRef(tempFile .getId()); + tempFile .setMoveRef(targetFile.getId()); //NO statistics update! } -//return "false" on name clash -bool FolderPairSyncer::createParentFolder(FileSystemObject& fsObj) //throw FileError, ThreadInterruption +//returns: CmtfStatus::AVAILABLE +// CmtfStatus::NAME_CLASH +// CmtfStatus::SOURCE_MISSING +template <SelectedSide side> +auto FolderPairSyncer::createMoveTargetFolder(FileSystemObject& fsObj) -> CmtfStatus //throw FileError, ThreadInterruption { if (auto parentFolder = dynamic_cast<FolderPair*>(&fsObj.parent())) { - if (!createParentFolder(*parentFolder)) - return false; + const CmtfStatus cmtfs = createMoveTargetFolder<side>(*parentFolder); + if (cmtfs != CmtfStatus::AVAILABLE) + return cmtfs; //detect (and try to resolve) file type conflicts: 1. symlinks 2. files const Zstring& shortname = parentFolder->getPairItemName(); if (haveNameClash(shortname, parentFolder->parent().refSubLinks()) || haveNameClash(shortname, parentFolder->parent().refSubFiles())) - return false; + return CmtfStatus::NAME_CLASH; + + //-------- create parent folder if needed -------------- + constexpr SelectedSide sideSrc = OtherSide<side>::value; + assert(!parentFolder->isEmpty<sideSrc>()); + + switch (parentFolder->getSyncOperation()) + { + case SO_CREATE_NEW_LEFT: + case SO_CREATE_NEW_RIGHT: + { + const AbstractPath targetPath = parentFolder->getAbstractPath<side>(); + reportInfo(txtCreatingFolder_, AFS::getDisplayPath(targetPath)); //throw ThreadInterruption + + //shallow-"copying" a folder might not fail if source is missing, so we need to check this first: + if (parallel::getItemTypeIfExists(parentFolder->getAbstractPath<sideSrc>(), singleThread_)) //throw FileError + { + AsyncItemStatReporter statReporter(1, 0, acb_); + try //target existing: undefined behavior! (fail/overwrite) + { + parallel::copyNewFolder(parentFolder->getAbstractPath<sideSrc>(), targetPath, copyFilePermissions_, singleThread_); //throw FileError + } + catch (FileError&) + { + bool folderAlreadyExists = false; + try { folderAlreadyExists = parallel::getItemType(targetPath, singleThread_) == AFS::ItemType::FOLDER; } /*throw FileError*/ catch (FileError&) {} + if (!folderAlreadyExists) //previous exception is more relevant; good enough? https://freefilesync.org/forum/viewtopic.php?t=5266 + throw; + } + statReporter.reportDelta(1, 0); + + parentFolder->setSyncedTo<side>(parentFolder->getItemName<sideSrc>(), + false /*isSymlinkTrg*/, + parentFolder->isFollowedSymlink<sideSrc>()); + } + else //source deleted meanwhile... + { + //attention when fixing statistics due to missing folder: child items may be scheduled for move, so deletion will have move references flip back to copy + delete! + const SyncStatistics statsBefore(parentFolder->base()); //=> don't bother considering move operations, just calculate over the whole tree + parentFolder->removeObject<sideSrc>(); //DON'T physically delete child objects while we're still evaluating them, e.g. fsObj, and in caller code!!! + const SyncStatistics statsAfter(parentFolder->base()); - //in this context "parentFolder" cannot be scheduled for deletion since it contains a "move target"! - //note: if parentFolder were deleted, we'd end up destroying "fsObj"! - assert(parentFolder->getSyncOperation() != SO_DELETE_LEFT && - parentFolder->getSyncOperation() != SO_DELETE_RIGHT); + acb_.updateDataProcessed(1, 0); //even if the source item does not exist anymore, significant I/O work was done => report + acb_.updateDataTotal(getCUD(statsAfter) - getCUD(statsBefore) + 1, statsAfter.getBytesToProcess() - statsBefore.getBytesToProcess()); //noexcept - synchronizeFolder(*parentFolder); //throw FileError, ThreadInterruption + reportInfo(txtSourceItemNotFound_, AFS::getDisplayPath(parentFolder->getAbstractPath<sideSrc>())); //throw ThreadInterruption + return CmtfStatus::SOURCE_MISSING; + } + } + break; + + case SO_DO_NOTHING: //!isEmpty<side>(); see FolderPair::getSyncOperation() + case SO_UNRESOLVED_CONFLICT: // + case SO_OVERWRITE_LEFT: //possible: e.g. manually-resolved dir-traversal conflict + case SO_OVERWRITE_RIGHT: // + case SO_COPY_METADATA_TO_LEFT: + case SO_COPY_METADATA_TO_RIGHT: + case SO_EQUAL: + assert(!parentFolder->isEmpty<side>()); + break; //we're good + + case SO_DELETE_LEFT: //not possible in the context of planning to move a child item, see FolderPair::getSyncOperation() + case SO_DELETE_RIGHT: // + case SO_MOVE_LEFT_FROM: //status not possible for folder + case SO_MOVE_RIGHT_FROM: // + case SO_MOVE_LEFT_TO: // + case SO_MOVE_RIGHT_TO: // + assert(false); + break; + } } - return true; + return CmtfStatus::AVAILABLE; } @@ -1243,12 +1314,11 @@ void FolderPairSyncer::resolveMoveConflicts(FilePair& sourceFile, //throw FileEr const bool sourceWillBeDeleted = [&] { if (auto parentFolder = dynamic_cast<const FolderPair*>(&sourceFile.parent())) - { switch (parentFolder->getSyncOperation()) //evaluate comparison result and sync direction { case SO_DELETE_LEFT: case SO_DELETE_RIGHT: - return true; //we need to do something about it + return true; //we need to do something about this! case SO_MOVE_LEFT_FROM: case SO_MOVE_RIGHT_FROM: case SO_MOVE_LEFT_TO: @@ -1264,7 +1334,6 @@ void FolderPairSyncer::resolveMoveConflicts(FilePair& sourceFile, //throw FileEr case SO_COPY_METADATA_TO_RIGHT: break; } - } return false; }(); @@ -1278,15 +1347,16 @@ void FolderPairSyncer::resolveMoveConflicts(FilePair& sourceFile, //throw FileEr { //prepare for move now: - revert to 2-step move on name clashes if (haveNameClash(targetFile) || - !createParentFolder(targetFile)) //throw FileError, ThreadInterruption + createMoveTargetFolder<side>(targetFile) == CmtfStatus::NAME_CLASH) //throw FileError, ThreadInterruption return setup2StepMove<side>(sourceFile, targetFile); //throw FileError, ThreadInterruption //finally start move! this should work now: synchronizeFile(targetFile); //throw FileError, ThreadInterruption - //FolderPairSyncer::synchronizeFileInt() is *not* expecting SO_MOVE_LEFT_FROM/SO_MOVE_RIGHT_FROM => start move from targetFile, not sourceFile! + //- FolderPairSyncer::synchronizeFileInt() is *not* expecting SO_MOVE_LEFT_FROM/SO_MOVE_RIGHT_FROM => start move from targetFile, not sourceFile! + //- function call will be NOOP if CmtfStatus::SOURCE_MISSING } //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!!! + //note: this also applies for new "move sources" from two-step sub-routine!!! } @@ -1300,7 +1370,7 @@ void FolderPairSyncer::prepareFileMove(FilePair& file) //throw ThreadInterruptio if (FilePair* targetObj = dynamic_cast<FilePair*>(FileSystemObject::retrieve(file.getMoveRef()))) { FilePair* sourceObj = &file; - assert(dynamic_cast<FilePair*>(FileSystemObject::retrieve(targetObj->getMoveRef())) == sourceObj); + assert(targetObj->getMoveRef() == sourceObj->getId()); const std::wstring errMsg = tryReportingError([&] //throw ThreadInterruption { @@ -1314,20 +1384,19 @@ void FolderPairSyncer::prepareFileMove(FilePair& file) //throw ThreadInterruptio { //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(); + const auto [itemsBefore, bytesBefore] = getStats(); sourceObj->setMoveRef(nullptr); targetObj->setMoveRef(nullptr); - const auto statAfter = getStats(); + const auto [itemsAfter, bytesAfter] = getStats(); + //fix statistics total to match "copy + delete" - acb_.updateDataTotal(statAfter.first - statBefore.first, statAfter.second - statBefore.second); //noexcept + acb_.updateDataTotal(itemsAfter - itemsBefore, bytesAfter - bytesBefore); //noexcept } } else assert(false); @@ -1491,7 +1560,7 @@ void FolderPairSyncer::synchronizeFile(FilePair& file) //throw FileError, Thread { const SyncOperation syncOp = file.getSyncOperation(); - if (Opt<SelectedSide> sideTrg = getTargetDirection(syncOp)) + if (std::optional<SelectedSide> sideTrg = getTargetDirection(syncOp)) { if (*sideTrg == LEFT_SIDE) synchronizeFileInt<LEFT_SIDE>(file, syncOp); @@ -1549,8 +1618,7 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp) if (sourceWasDeleted) { - //even if the source item does not exist anymore, significant I/O work was done => report - statReporter.reportDelta(1, 0); + statReporter.reportDelta(1, 0); //even if the source item does not exist anymore, significant I/O work was done => report file.removeObject<sideSrc>(); //source deleted meanwhile...nothing was done (logical point of view!) reportInfo(txtSourceItemNotFound_, AFS::getDisplayPath(file.getAbstractPath<sideSrc>())); //throw ThreadInterruption @@ -1578,6 +1646,7 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp) if (FilePair* moveFrom = dynamic_cast<FilePair*>(FileSystemObject::retrieve(file.getMoveRef()))) { FilePair* moveTo = &file; + assert(moveFrom->getMoveRef() == moveTo->getId()); assert((moveFrom->getSyncOperation() == SO_MOVE_LEFT_FROM && moveTo->getSyncOperation() == SO_MOVE_LEFT_TO && sideTrg == LEFT_SIDE) || (moveFrom->getSyncOperation() == SO_MOVE_RIGHT_FROM && moveTo->getSyncOperation() == SO_MOVE_RIGHT_TO && sideTrg == RIGHT_SIDE)); @@ -1715,7 +1784,7 @@ void FolderPairSyncer::synchronizeLink(SymlinkPair& link) //throw FileError, Thr { const SyncOperation syncOp = link.getSyncOperation(); - if (Opt<SelectedSide> sideTrg = getTargetDirection(syncOp)) + if (std::optional<SelectedSide> sideTrg = getTargetDirection(syncOp)) { if (*sideTrg == LEFT_SIDE) synchronizeLinkInt<LEFT_SIDE>(link, syncOp); @@ -1859,7 +1928,7 @@ void FolderPairSyncer::synchronizeFolder(FolderPair& folder) //throw FileError, { const SyncOperation syncOp = folder.getSyncOperation(); - if (Opt<SelectedSide> sideTrg = getTargetDirection(syncOp)) + if (std::optional<SelectedSide> sideTrg = getTargetDirection(syncOp)) { if (*sideTrg == LEFT_SIDE) synchronizeFolderInt<LEFT_SIDE>(folder, syncOp); @@ -1915,17 +1984,16 @@ void FolderPairSyncer::synchronizeFolderInt(FolderPair& folder, SyncOperation sy } else //source deleted meanwhile... { - const SyncStatistics subStats(folder); - AsyncItemStatReporter statReporter(1 + getCUD(subStats), subStats.getBytesToProcess(), acb_); - - //even if the source item does not exist anymore, significant I/O work was done => report - statReporter.reportDelta(1, 0); - - //remove only *after* evaluating folder!! + //attention when fixing statistics due to missing folder: child items may be scheduled for move, so deletion will have move references flip back to copy + delete! + const SyncStatistics statsBefore(folder.base()); //=> don't bother considering move operations, just calculate over the whole tree folder.refSubFiles ().clear(); // folder.refSubLinks ().clear(); //update FolderPair folder.refSubFolders().clear(); // folder.removeObject<sideSrc>(); // + const SyncStatistics statsAfter(folder.base()); + + acb_.updateDataProcessed(1, 0); //even if the source item does not exist anymore, significant I/O work was done => report + acb_.updateDataTotal(getCUD(statsAfter) - getCUD(statsBefore) + 1, statsAfter.getBytesToProcess() - statsBefore.getBytesToProcess()); //noexcept reportInfo(txtSourceItemNotFound_, AFS::getDisplayPath(folder.getAbstractPath<sideSrc>())); //throw ThreadInterruption } @@ -2002,8 +2070,7 @@ AFS::FileCopyResult FolderPairSyncer::copyFileWithCallback(const FileDescriptor& const AFS::FileCopyResult result = parallel::copyFileTransactional(sourcePathTmp, sourceAttr, //throw FileError, ErrorFileLocked targetPath, copyFilePermissions_, - failSafeFileCopy_, - [&] + failSafeFileCopy_, [&] { if (onDeleteTargetFile) //running *outside* singleThread_ lock! => onDeleteTargetFile-callback expects lock being held: { @@ -2042,7 +2109,7 @@ AFS::FileCopyResult FolderPairSyncer::copyFileWithCallback(const FileDescriptor& //########################################################################################### template <SelectedSide side> -bool baseFolderDrop(BaseFolderPair& baseFolder, int folderAccessTimeout, ProcessCallback& callback) +bool baseFolderDrop(BaseFolderPair& baseFolder, std::chrono::seconds folderAccessTimeout, ProcessCallback& callback) { const AbstractPath folderPath = baseFolder.getAbstractPath<side>(); @@ -2070,7 +2137,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, bool copyFilePermissions, int folderAccessTimeout, ProcessCallback& callback) //return false if fatal error occurred +bool createBaseFolder(BaseFolderPair& baseFolder, bool copyFilePermissions, std::chrono::seconds folderAccessTimeout, ProcessCallback& callback) //return false if fatal error occurred { static const SelectedSide sideSrc = OtherSide<side>::value; const AbstractPath baseFolderPath = baseFolder.getAbstractPath<side>(); @@ -2094,10 +2161,10 @@ bool createBaseFolder(BaseFolderPair& baseFolder, bool copyFilePermissions, int { if (baseFolder.isAvailable<sideSrc>()) //copy file permissions { - if (Opt<AbstractPath> parentPath = AFS::getParentFolderPath(baseFolderPath)) + if (std::optional<AbstractPath> parentPath = AFS::getParentFolderPath(baseFolderPath)) if (AFS::getParentFolderPath(*parentPath)) //not device root AFS::createFolderIfMissingRecursion(*parentPath); //throw FileError - + AFS::copyNewFolder(baseFolder.getAbstractPath<sideSrc>(), baseFolderPath, copyFilePermissions); //throw FileError } else @@ -2139,7 +2206,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime bool copyFilePermissions, bool failSafeFileCopy, bool runWithBackgroundPriority, - int folderAccessTimeout, + std::chrono::seconds folderAccessTimeout, const std::vector<FolderPairSyncCfg>& syncConfig, FolderComparison& folderCmp, const std::map<AbstractPath, size_t>& deviceParallelOps, @@ -2431,8 +2498,8 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime if (std::get<bool>(*it)) //write access for (auto it2 = readWriteCheckBaseFolders.begin(); it2 != readWriteCheckBaseFolders.end(); ++it2) if (!std::get<bool>(*it2) || it < it2) //avoid duplicate comparisons - if (Opt<PathDependency> pd = getPathDependency(std::get<AbstractPath>(*it), *std::get<const HardFilter*>(*it), - std::get<AbstractPath>(*it2), *std::get<const HardFilter*>(*it2))) + if (std::optional<PathDependency> pd = getPathDependency(std::get<AbstractPath>(*it), *std::get<const HardFilter*>(*it), + std::get<AbstractPath>(*it2), *std::get<const HardFilter*>(*it2))) { dependentFolders.insert(pd->basePathParent); dependentFolders.insert(pd->basePathChild); @@ -2458,7 +2525,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime 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)) + if (std::optional<PathDependency> pd = getPathDependency(versioningFolderPath, NullFilter(), item.first, *item.second)) { std::wstring line = L"\n\n" + _("Versioning folder:") + L" \t" + AFS::getDisplayPath(versioningFolderPath) + L"\n" + _("Base folder:") + L" \t" + AFS::getDisplayPath(item.first); @@ -2632,7 +2699,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime //----------------------------------------------------------------------------------------------------- - applyVersioningLimit(versionLimitFolders, deviceParallelOps, callback); //throw X + applyVersioningLimit(versionLimitFolders, folderAccessTimeout, deviceParallelOps, callback); //throw X //------------------- show warnings after end of synchronization -------------------------------------- diff --git a/FreeFileSync/Source/base/synchronization.h b/FreeFileSync/Source/base/synchronization.h index dcfc0114..3ac1291e 100755 --- a/FreeFileSync/Source/base/synchronization.h +++ b/FreeFileSync/Source/base/synchronization.h @@ -92,7 +92,7 @@ void synchronize(const std::chrono::system_clock::time_point& syncStartTime, bool copyFilePermissions, bool failSafeFileCopy, bool runWithBackgroundPriority, - int folderAccessTimeout, + std::chrono::seconds folderAccessTimeout, const std::vector<FolderPairSyncCfg>& syncConfig, //CONTRACT: syncConfig and folderCmp correspond row-wise! FolderComparison& folderCmp, // const std::map<AbstractPath, size_t>& deviceParallelOps, diff --git a/FreeFileSync/Source/base/versioning.cpp b/FreeFileSync/Source/base/versioning.cpp index b8344b44..c5bc2aa6 100755 --- a/FreeFileSync/Source/base/versioning.cpp +++ b/FreeFileSync/Source/base/versioning.cpp @@ -1,6 +1,7 @@ #include "versioning.h" #include "parallel_scan.h" #include "status_handler_impl.h" +#include "dir_exist_async.h" using namespace zen; using namespace fff; @@ -80,7 +81,8 @@ AbstractPath FileVersioner::generateVersionedPath(const Zstring& relativePath) c break; case VersioningStyle::TIMESTAMP_FILE: //assemble time-stamped version name versionedRelPath = relativePath + Zstr(' ') + timeStamp_ + getDotExtension(relativePath); - assert(impl::parseVersionedFileName(versionedRelPath) == std::pair(syncStartTime_, relativePath)); + assert(impl::parseVersionedFileName(afterLast(versionedRelPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)) == + std::pair(syncStartTime_, afterLast(relativePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL))); (void)syncStartTime_; //silence clang's "unused variable" arning break; } @@ -109,7 +111,7 @@ void moveExistingItemToVersioning(const AbstractPath& sourcePath, const Abstract auto fixTargetPathIssues = [&](const FileError& prevEx) //throw FileError { - Opt<AFS::PathStatus> psTmp; + std::optional<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; @@ -189,7 +191,7 @@ void moveExistingItemToVersioning(const AbstractPath& sourcePath, const Abstract void FileVersioner::revisionFile(const FileDescriptor& fileDescr, const Zstring& relativePath, const IOCallback& notifyUnbufferedIO) const //throw FileError { - if (Opt<AFS::ItemType> type = AFS::getItemTypeIfExists(fileDescr.path)) //throw FileError + if (std::optional<AFS::ItemType> type = AFS::getItemTypeIfExists(fileDescr.path)) //throw FileError { if (*type == AFS::ItemType::SYMLINK) revisionSymlinkImpl(fileDescr.path, relativePath, nullptr /*onBeforeMove*/); //throw FileError @@ -251,7 +253,7 @@ void FileVersioner::revisionFolder(const AbstractPath& folderPath, const Zstring 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 (std::optional<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! revisionSymlinkImpl(folderPath, relativePath, onBeforeFileMove); //throw FileError @@ -311,9 +313,9 @@ namespace { struct VersionInfo { - time_t versionTime; + time_t versionTime = 0; AbstractPath filePath; - bool isSymlink; + bool isSymlink = false; }; using VersionInfoMap = std::map<Zstring, std::vector<VersionInfo>, LessFilePath>; //relPathOrig => <version infos> @@ -411,16 +413,46 @@ bool fff::operator<(const VersioningLimitFolder& lhs, const VersioningLimitFolde void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolders, + std::chrono::seconds folderAccessTimeout, const std::map<AbstractPath, size_t>& deviceParallelOps, ProcessCallback& callback /*throw X*/) { - warn_static("what if folder does not yet exist?") + //--------- determine existing folder paths for traversal --------- + std::set<DirectoryKey> foldersToRead; + { + std::set<AbstractPath> folderPaths; + for (const VersioningLimitFolder& vlf : limitFolders) + if (vlf.versionMaxAgeDays > 0 || vlf.versionCountMax > 0) //only analyze versioning folders when needed! + folderPaths.emplace(vlf.versioningFolderPath); + + //we don't want to show an error if version path does not yet exist! + tryReportingError([&] + { + const FolderStatus status = getFolderStatusNonBlocking(folderPaths, deviceParallelOps, //re-check *all* directories on each try! + folderAccessTimeout, false /*allowUserInteraction*/, callback); //throw X + + foldersToRead.clear(); + for (const AbstractPath& folderPath : status.existing) + foldersToRead.emplace(DirectoryKey({ folderPath, std::make_shared<NullFilter>(), SymLinkHandling::DIRECT })); + + if (!status.failedChecks.empty()) + { + std::wstring msg = _("Cannot find the following folders:") + L"\n"; + + for (const auto& fc : status.failedChecks) + msg += L"\n" + AFS::getDisplayPath(fc.first); + + msg += L"\n___________________________________________"; + for (const auto& fc : status.failedChecks) + msg += L"\n\n" + replaceCpy(fc.second.toString(), L"\n\n", L"\n"); + + throw FileError(msg); + } + }, callback); //throw X + } //--------- traverse all versioning folders --------- - std::set<DirectoryKey> foldersToRead; - for (const VersioningLimitFolder& vlf : limitFolders) - if (vlf.versionMaxAgeDays > 0 || vlf.versionCountMax > 0) //only analyze versioning folders when needed! - foldersToRead.emplace(DirectoryKey({ vlf.versioningFolderPath, std::make_shared<NullFilter>(), SymLinkHandling::DIRECT })); + std::map<DirectoryKey, DirectoryValue> folderBuf; auto onError = [&](const std::wstring& msg, size_t retryNumber) { @@ -443,8 +475,6 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde callback.reportStatus(textScanning + statusLine); //throw X }; - std::map<DirectoryKey, DirectoryValue> folderBuf; - parallelDeviceTraversal(foldersToRead, folderBuf, deviceParallelOps, onError, onStatusUpdate, //throw X @@ -491,15 +521,17 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde }(); for (const VersioningLimitFolder& vlf : limitFolders) - if (vlf.versionMaxAgeDays > 0 || vlf.versionCountMax > 0) //NOT redundant regarding the same check above - for (auto& item : versionDetails.find(vlf.versioningFolderPath)->second) //exists after construction above! + { + auto it = versionDetails.find(vlf.versioningFolderPath); + if (it != versionDetails.end()) + for (auto& item : it->second) { std::vector<VersionInfo>& versions = item.second; size_t versionsToKeep = versions.size(); if (vlf.versionMaxAgeDays > 0) { - const time_t cutOffTime = lastMidnightTime - vlf.versionMaxAgeDays * 24 * 3600; + const time_t cutOffTime = lastMidnightTime - static_cast<time_t>(vlf.versionMaxAgeDays) * 24 * 3600; versionsToKeep = std::count_if(versions.begin(), versions.end(), [cutOffTime](const VersionInfo& vi) { return vi.versionTime >= cutOffTime; }); @@ -521,6 +553,7 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde }); } } + } //--------- remove excess file versions --------- Protected<std::map<AbstractPath, size_t>&> folderItemCountShared(folderItemCount); @@ -528,7 +561,7 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde const std::wstring textDeletingFolder = _("Deleting folder %x"); ParallelWorkItem deleteEmptyFolderTask; - deleteEmptyFolderTask = [&textDeletingFolder, &folderItemCountShared, &deleteEmptyFolderTask](ParallelContext& ctx) /*throw ThreadInterruption*/ + deleteEmptyFolderTask = [&textDeletingFolder, &folderItemCountShared, &deleteEmptyFolderTask](ParallelContext& ctx) //throw ThreadInterruption { const std::wstring errMsg = tryReportingError([&] //throw ThreadInterruption { @@ -537,7 +570,7 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde }, ctx.acb); if (errMsg.empty()) - if (Opt<AbstractPath> parentPath = AFS::getParentFolderPath(ctx.itemPath)) + if (std::optional<AbstractPath> parentPath = AFS::getParentFolderPath(ctx.itemPath)) { bool scheduleDelete = false; folderItemCountShared.access([&](auto& folderItemCount2) { scheduleDelete = --folderItemCount2[*parentPath] == 0; }); @@ -553,7 +586,7 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde parallelWorkload.emplace_back(item.first, deleteEmptyFolderTask); for (const auto& item : itemsToDelete) - parallelWorkload.emplace_back(item.first, [isSymlink = item.second, &textRemoving, &folderItemCountShared, &deleteEmptyFolderTask](ParallelContext& ctx) /*throw ThreadInterruption*/ + parallelWorkload.emplace_back(item.first, [isSymlink = item.second, &textRemoving, &folderItemCountShared, &deleteEmptyFolderTask](ParallelContext& ctx) //throw ThreadInterruption { const std::wstring errMsg = tryReportingError([&] //throw ThreadInterruption { @@ -565,7 +598,7 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde }, ctx.acb); if (errMsg.empty()) - if (Opt<AbstractPath> parentPath = AFS::getParentFolderPath(ctx.itemPath)) + if (std::optional<AbstractPath> parentPath = AFS::getParentFolderPath(ctx.itemPath)) { bool scheduleDelete = false; folderItemCountShared.access([&](auto& folderItemCount2) { scheduleDelete = --folderItemCount2[*parentPath] == 0; }); @@ -573,6 +606,8 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde ctx.scheduleExtraTask(AfsPath(AFS::getRootRelativePath(*parentPath)), deleteEmptyFolderTask); //throw ThreadInterruption assert(AFS::getRootPath(*parentPath) == AFS::getRootPath(ctx.itemPath)); } + + warn_static("get rid of scheduleExtraTask and recursively delete parent folders!? need scheduleExtraTask for something else?") }); massParallelExecute(parallelWorkload, deviceParallelOps, "Versioning Limit", callback /*throw X*/); diff --git a/FreeFileSync/Source/base/versioning.h b/FreeFileSync/Source/base/versioning.h index 835ba67e..a2743e94 100755 --- a/FreeFileSync/Source/base/versioning.h +++ b/FreeFileSync/Source/base/versioning.h @@ -103,6 +103,7 @@ bool operator<(const VersioningLimitFolder& lhs, const VersioningLimitFolder& rh void applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolders, + std::chrono::seconds folderAccessTimeout, const std::map<AbstractPath, size_t>& deviceParallelOps, ProcessCallback& callback /*throw X*/); diff --git a/FreeFileSync/Source/fs/abstract.cpp b/FreeFileSync/Source/fs/abstract.cpp index d53019be..e43de243 100755 --- a/FreeFileSync/Source/fs/abstract.cpp +++ b/FreeFileSync/Source/fs/abstract.cpp @@ -44,19 +44,19 @@ int AFS::compareAbstractPath(const AbstractPath& lhs, const AbstractPath& rhs) } -Opt<AbstractPath> AFS::getParentFolderPath(const AbstractPath& ap) +std::optional<AbstractPath> AFS::getParentFolderPath(const AbstractPath& ap) { - if (const Opt<AfsPath> parentAfsPath = getParentAfsPath(ap.afsPath)) + if (const std::optional<AfsPath> parentAfsPath = getParentAfsPath(ap.afsPath)) return AbstractPath(ap.afs, *parentAfsPath); - return NoValue(); + return {}; } -Opt<AfsPath> AFS::getParentAfsPath(const AfsPath& afsPath) +std::optional<AfsPath> AFS::getParentAfsPath(const AfsPath& afsPath) { if (afsPath.value.empty()) - return NoValue(); + return {}; return AfsPath(beforeLast(afsPath.value, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); } @@ -127,7 +127,7 @@ AFS::FileCopyResult AFS::copyFileAsStream(const AfsPath& afsPathSource, const St StreamAttributes attrSourceNew = {}; //try to get the most current attributes if possible (input file might have changed after comparison!) - if (Opt<StreamAttributes> attr = streamIn->getAttributesBuffered()) //throw FileError + if (std::optional<StreamAttributes> attr = streamIn->getAttributesBuffered()) //throw FileError attrSourceNew = *attr; //Native/MTP else //use more stale ones: attrSourceNew = attrSource; //SFTP/FTP @@ -148,7 +148,7 @@ AFS::FileCopyResult AFS::copyFileAsStream(const AfsPath& afsPathSource, const St L"%x", numberTo<std::wstring>(2 * attrSourceNew.fileSize)), L"%y", numberTo<std::wstring>(totalUnbufferedIO)) + L" [notifyUnbufferedIO]"); - Opt<FileError> errorModTime; + std::optional<FileError> errorModTime; try { /* @@ -210,7 +210,7 @@ AFS::FileCopyResult AFS::copyFileTransactional(const AbstractPath& apSource, con if (transactionalCopy) { - Opt<AbstractPath> parentPath = AFS::getParentFolderPath(apTarget); + std::optional<AbstractPath> parentPath = AFS::getParentFolderPath(apTarget); if (!parentPath) throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(AFS::getDisplayPath(apTarget))), L"Path is device root."); const Zstring fileName = AFS::getItemName(apTarget); @@ -305,7 +305,7 @@ void AFS::createFolderIfMissingRecursion(const AbstractPath& ap) //throw FileErr //essentially a(n abstract) duplicate of zen::getPathStatus() AFS::PathStatusImpl AFS::getPathStatusViaFolderTraversal(const AfsPath& afsPath) const //throw FileError { - const Opt<AfsPath> parentAfsPath = getParentAfsPath(afsPath); + const std::optional<AfsPath> parentAfsPath = getParentAfsPath(afsPath); try { return { getItemType(afsPath), afsPath, {} }; //throw FileError @@ -339,12 +339,12 @@ AFS::PathStatusImpl AFS::getPathStatusViaFolderTraversal(const AfsPath& afsPath) } -Opt<AFS::ItemType> AFS::getItemTypeIfExists(const AbstractPath& ap) //throw FileError +std::optional<AFS::ItemType> AFS::getItemTypeIfExists(const AbstractPath& ap) //throw FileError { const PathStatus ps = getPathStatus(ap); //throw FileError if (ps.relPath.empty()) return ps.existingType; - return NoValue(); + return {}; } @@ -355,62 +355,54 @@ AFS::PathStatus AFS::getPathStatus(const AbstractPath& ap) //throw FileError } -namespace -{ -void removeFolderIfExistsRecursionImpl(const AbstractPath& folderPath, //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! +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 object! { - - //deferred recursion => save stack space and allow deletion of extremely deep hierarchies! - std::vector<Zstring> fileNames; - std::vector<Zstring> folderNames; - std::vector<Zstring> symlinkNames; - - AFS::traverseFolderFlat(folderPath, //throw FileError - [&](const AFS::FileInfo& fi) { fileNames .push_back(fi.itemName); }, - [&](const AFS::FolderInfo& fi) { folderNames .push_back(fi.itemName); }, - [&](const AFS::SymlinkInfo& si) { symlinkNames.push_back(si.itemName); }); - - - for (const Zstring& fileName : fileNames) + std::function<void(const AbstractPath& folderPath)> removeFolderRecursionImpl; + removeFolderRecursionImpl = [&onBeforeFileDeletion, &onBeforeFolderDeletion, &removeFolderRecursionImpl](const AbstractPath& folderPath) //throw FileError { - const AbstractPath filePath = AFS::appendRelPath(folderPath, fileName); - if (onBeforeFileDeletion) - onBeforeFileDeletion(AFS::getDisplayPath(filePath)); + //deferred recursion => save stack space and allow deletion of extremely deep hierarchies! + std::vector<Zstring> fileNames; + std::vector<Zstring> folderNames; + std::vector<Zstring> symlinkNames; - AFS::removeFilePlain(filePath); //throw FileError - } + AFS::traverseFolderFlat(folderPath, //throw FileError + [&](const AFS::FileInfo& fi) { fileNames .push_back(fi.itemName); }, + [&](const AFS::FolderInfo& fi) { folderNames .push_back(fi.itemName); }, + [&](const AFS::SymlinkInfo& si) { symlinkNames.push_back(si.itemName); }); - for (const Zstring& symlinkName : symlinkNames) - { - const AbstractPath linkPath = AFS::appendRelPath(folderPath, symlinkName); - if (onBeforeFileDeletion) - onBeforeFileDeletion(AFS::getDisplayPath(linkPath)); + for (const Zstring& fileName : fileNames) + { + const AbstractPath filePath = AFS::appendRelPath(folderPath, fileName); + if (onBeforeFileDeletion) + onBeforeFileDeletion(AFS::getDisplayPath(filePath)); - AFS::removeSymlinkPlain(linkPath); //throw FileError - } + AFS::removeFilePlain(filePath); //throw FileError + } - for (const Zstring& folderName : folderNames) - removeFolderIfExistsRecursionImpl(AFS::appendRelPath(folderPath, folderName), //throw FileError - onBeforeFileDeletion, onBeforeFolderDeletion); + for (const Zstring& symlinkName : symlinkNames) + { + const AbstractPath linkPath = AFS::appendRelPath(folderPath, symlinkName); + if (onBeforeFileDeletion) + onBeforeFileDeletion(AFS::getDisplayPath(linkPath)); //throw X - if (onBeforeFolderDeletion) - onBeforeFolderDeletion(AFS::getDisplayPath(folderPath)); + AFS::removeSymlinkPlain(linkPath); //throw FileError + } - AFS::removeFolderPlain(folderPath); //throw FileError -} -} + for (const Zstring& folderName : folderNames) + removeFolderRecursionImpl(AFS::appendRelPath(folderPath, folderName)); //throw FileError + if (onBeforeFolderDeletion) + onBeforeFolderDeletion(AFS::getDisplayPath(folderPath)); //throw X -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 object! -{ + AFS::removeFolderPlain(folderPath); //throw FileError + }; + //-------------------------------------------------------------------------------------------------------------- warn_static("what about parallelOps?") //no error situation if directory is not existing! manual deletion relies on it! - if (Opt<ItemType> type = AFS::getItemTypeIfExists(ap)) //throw FileError + if (std::optional<ItemType> type = AFS::getItemTypeIfExists(ap)) //throw FileError { if (*type == AFS::ItemType::SYMLINK) { @@ -420,7 +412,7 @@ void AFS::removeFolderIfExistsRecursion(const AbstractPath& ap, //throw FileErro AFS::removeSymlinkPlain(ap); //throw FileError } else - removeFolderIfExistsRecursionImpl(ap, onBeforeFileDeletion, onBeforeFolderDeletion); //throw FileError + removeFolderRecursionImpl(ap); //throw FileError } 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 d5b08c73..887f6b8f 100755 --- a/FreeFileSync/Source/fs/abstract.h +++ b/FreeFileSync/Source/fs/abstract.h @@ -10,7 +10,6 @@ #include <functional> #include <zen/file_error.h> #include <zen/zstring.h> -#include <zen/optional.h> #include <zen/serialize.h> //InputStream/OutputStream support buffered stream concept #include <wx+/image_holder.h> //NOT a wxWidgets dependency! @@ -52,19 +51,19 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t 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); } + static bool isNullPath(const AbstractPath& ap) { return ap.afs->isNullFileSystem() /*&& ap.afsPath.value.empty()*/; } static std::wstring getDisplayPath(const AbstractPath& ap) { return ap.afs->getDisplayPath(ap.afsPath); } - static bool isNullPath(const AbstractPath& ap) { return ap.afs->isNullFileSystem() /*&& ap.afsPath.value.empty()*/; } + static Zstring getInitPathPhrase(const AbstractPath& ap) { return ap.afs->getInitPathPhrase(ap.afsPath); } + + static std::optional<Zstring> getNativeItemPath(const AbstractPath& ap) { return ap.afs->getNativeItemPath(ap.afsPath); } static AbstractPath appendRelPath(const AbstractPath& ap, const Zstring& relPath); static Zstring getItemName(const AbstractPath& ap) { assert(getParentFolderPath(ap)); return getItemName(ap.afsPath); } - static zen::Opt<Zstring> getNativeItemPath(const AbstractPath& ap) { return ap.afs->getNativeItemPath(ap.afsPath); } - - static zen::Opt<AbstractPath> getParentFolderPath(const AbstractPath& ap); + static std::optional<AbstractPath> getParentFolderPath(const AbstractPath& ap); static AbstractPath getRootPath (const AbstractPath& ap) { return AbstractPath(ap.afs, AfsPath()); } static Zstring getRootRelativePath(const AbstractPath& ap) { return ap.afsPath.value; } @@ -85,7 +84,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t //(hopefully) fast: does not distinguish between error/not existing static ItemType getItemType(const AbstractPath& ap) { return ap.afs->getItemType(ap.afsPath); } //throw FileError //execute potentially SLOW folder traversal but distinguish error/not existing - static zen::Opt<ItemType> getItemTypeIfExists(const AbstractPath& ap); //throw FileError + static std::optional<ItemType> getItemTypeIfExists(const AbstractPath& ap); //throw FileError static PathStatus getPathStatus(const AbstractPath& ap); //throw FileError //---------------------------------------------------------------------------------------------------------------- @@ -137,7 +136,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t virtual size_t getBlockSize() const = 0; //non-zero block size is AFS contract! it's implementer's job to always give a reasonable buffer size! //only returns attributes if they are already buffered within stream handle and determination would be otherwise expensive (e.g. FTP/SFTP): - virtual zen::Opt<StreamAttributes> getAttributesBuffered() = 0; //throw FileError + virtual std::optional<StreamAttributes> getAttributesBuffered() = 0; //throw FileError }; struct OutputStreamImpl @@ -159,7 +158,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t std::unique_ptr<OutputStreamImpl> outStream_; //bound! const AbstractPath filePath_; bool finalizeSucceeded_ = false; - zen::Opt<uint64_t> bytesExpected_; + std::optional<uint64_t> bytesExpected_; uint64_t bytesWrittenTotal_ = 0; }; @@ -247,7 +246,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t time_t modTime = 0; //number of seconds since Jan. 1st 1970 UTC FileId sourceFileId; FileId targetFileId; - zen::Opt<zen::FileError> errorModTime; //failure to set modification time + std::optional<zen::FileError> errorModTime; //failure to set modification time }; //symlink handling: follow @@ -302,7 +301,7 @@ protected: //grant derived classes access to AbstractPath: static AfsPath getAfsPath(const AbstractPath& ap) { return ap.afsPath; } static Zstring getItemName(const AfsPath& afsPath) { using namespace zen; return afterLast(afsPath.value, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); } - static zen::Opt<AfsPath> getParentAfsPath(const AfsPath& afsPath); + static std::optional<AfsPath> getParentAfsPath(const AfsPath& afsPath); struct PathStatusImpl { @@ -324,7 +323,7 @@ protected: //grant derived classes access to AbstractPath: 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(); }; + virtual std::optional<Zstring> getNativeItemPath(const AfsPath& afsPath) const { return {}; }; virtual Zstring getInitPathPhrase(const AfsPath& afsPath) const = 0; diff --git a/FreeFileSync/Source/fs/concrete_impl.h b/FreeFileSync/Source/fs/concrete_impl.h index 03e381bc..e08d9574 100755 --- a/FreeFileSync/Source/fs/concrete_impl.h +++ b/FreeFileSync/Source/fs/concrete_impl.h @@ -65,7 +65,7 @@ public: TaskScheduler(size_t threadCount, const std::string& groupName) : threadGroup_(zen::ThreadGroup<std::function<void()>>(threadCount, groupName)) {} - ~TaskScheduler() { threadGroup_ = zen::NoValue(); } //TaskScheduler must out-live threadGroup! (captured "this") + ~TaskScheduler() { threadGroup_ = {}; } //TaskScheduler must out-live threadGroup! (captured "this") //context of controlling thread, non-blocking: template <class Function> @@ -121,7 +121,7 @@ private: conditionNewResult_.notify_all(); } - zen::Opt<zen::ThreadGroup<std::function<void()>>> threadGroup_; + std::optional<zen::ThreadGroup<std::function<void()>>> threadGroup_; std::mutex lockResult_; size_t resultsPending_ = 0; diff --git a/FreeFileSync/Source/fs/native.cpp b/FreeFileSync/Source/fs/native.cpp index ad8f9be1..fc260d7f 100755 --- a/FreeFileSync/Source/fs/native.cpp +++ b/FreeFileSync/Source/fs/native.cpp @@ -308,14 +308,14 @@ struct InputStreamNative : public AbstractFileSystem::InputStream size_t read(void* buffer, size_t bytesToRead) override { return fi_.read(buffer, bytesToRead); } //throw FileError, ErrorFileLocked, X; return "bytesToRead" bytes unless end of stream! size_t getBlockSize() const override { return fi_.getBlockSize(); } //non-zero block size is AFS contract! - Opt<AFS::StreamAttributes> getAttributesBuffered() override; //throw FileError + std::optional<AFS::StreamAttributes> getAttributesBuffered() override; //throw FileError private: FileInput fi_; }; -Opt<AFS::StreamAttributes> InputStreamNative::getAttributesBuffered() //throw FileError +std::optional<AFS::StreamAttributes> InputStreamNative::getAttributesBuffered() //throw FileError { const FileAttribs fileAttr = getFileAttributes(fi_.getHandle(), fi_.getFilePath()); //throw FileError @@ -364,7 +364,7 @@ public: private: Zstring getNativePath(const AfsPath& afsPath) const { return appendPaths(rootPath_, afsPath.value, FILE_NAME_SEPARATOR); } - Opt<Zstring> getNativeItemPath(const AfsPath& afsPath) const override { return getNativePath(afsPath); } + std::optional<Zstring> getNativeItemPath(const AfsPath& afsPath) const override { return getNativePath(afsPath); } Zstring getInitPathPhrase(const AfsPath& afsPath) const override { return getNativePath(afsPath); } @@ -441,7 +441,7 @@ private: const Zstring nativePath = getNativePath(afsPath); const Zstring resolvedPath = zen::getSymlinkResolvedPath(nativePath); //throw FileError - const Opt<zen::PathComponents> comp = parsePathComponents(resolvedPath); + const std::optional<zen::PathComponents> comp = parsePathComponents(resolvedPath); if (!comp) throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(nativePath)), replaceCpy<std::wstring>(L"Invalid path %x.", L"%x", fmtPath(resolvedPath))); @@ -627,7 +627,7 @@ bool RecycleSessionNative::recycleItem(const AbstractPath& itemPath, const Zstri { assert(!startsWith(logicalRelPath, FILE_NAME_SEPARATOR)); - Opt<Zstring> itemPathNative = AFS::getNativeItemPath(itemPath); + std::optional<Zstring> itemPathNative = AFS::getNativeItemPath(itemPath); if (!itemPathNative) throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); @@ -668,7 +668,7 @@ AbstractPath fff::createItemPathNative(const Zstring& itemPathPhrase) //noexcept AbstractPath fff::createItemPathNativeNoFormatting(const Zstring& nativePath) //noexcept { - if (const Opt<PathComponents> comp = parsePathComponents(nativePath)) + if (const std::optional<PathComponents> comp = parsePathComponents(nativePath)) return AbstractPath(std::make_shared<NativeFileSystem>(comp->rootPath), AfsPath(comp->relPath)); else //path syntax broken return AbstractPath(std::make_shared<NativeFileSystem>(nativePath), AfsPath()); diff --git a/FreeFileSync/Source/fs/native.h b/FreeFileSync/Source/fs/native.h index d7d8c1bb..657109b9 100755 --- a/FreeFileSync/Source/fs/native.h +++ b/FreeFileSync/Source/fs/native.h @@ -15,6 +15,9 @@ bool acceptsItemPathPhraseNative (const Zstring& itemPathPhrase); //noexcept AbstractPath createItemPathNative(const Zstring& itemPathPhrase); //noexcept AbstractPath createItemPathNativeNoFormatting(const Zstring& nativePath); //noexcept + +inline +AbstractPath getNullPath() { return createItemPathNativeNoFormatting(Zstring()); } } #endif //FS_NATIVE_183247018532434563465 diff --git a/FreeFileSync/Source/ui/batch_config.cpp b/FreeFileSync/Source/ui/batch_config.cpp index 83d297d5..e0b248de 100755 --- a/FreeFileSync/Source/ui/batch_config.cpp +++ b/FreeFileSync/Source/ui/batch_config.cpp @@ -15,7 +15,6 @@ #include "gui_generated.h" #include "folder_selector.h" #include "../base/help_provider.h" -#include "../base/generate_logfile.h" using namespace zen; @@ -50,9 +49,6 @@ private: void OnHelpScheduleBatch(wxHyperlinkEvent& event) override { displayHelpEntry(L"schedule-a-batch-job", this); } - void OnToggleGenerateLogfile(wxCommandEvent& event) override { updateGui(); } - void OnToggleLogfilesLimit (wxCommandEvent& event) override { updateGui(); } - void onLocalKeyEvent(wxKeyEvent& event); void updateGui(); //re-evaluate gui after config changes @@ -63,8 +59,6 @@ private: //output-only parameters BatchDialogConfig& dlgCfgOut_; - std::unique_ptr<FolderSelector> logfileDir_; //always bound, solve circular compile-time dependency - EnumDescrList<PostSyncAction> enumPostSyncAction_; }; @@ -79,17 +73,8 @@ BatchDialog::BatchDialog(wxWindow* parent, BatchDialogConfig& dlgCfg) : m_staticTextHeader->SetLabel(replaceCpy(m_staticTextHeader->GetLabel(), L"%x", L"FreeFileSync.exe <" + _("job name") + L">.ffs_batch")); m_staticTextHeader->Wrap(fastFromDIP(520)); - m_spinCtrlLogfileLimit->SetMinSize(wxSize(fastFromDIP(70), -1)); //Hack: set size (why does wxWindow::Size() not work?) - m_bitmapBatchJob->SetBitmap(getResourceImage(L"file_batch")); - 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())); - enumPostSyncAction_. add(PostSyncAction::NONE, L""). add(PostSyncAction::SLEEP, _("System: Sleep")). @@ -97,15 +82,6 @@ BatchDialog::BatchDialog(wxWindow* parent, BatchDialogConfig& dlgCfg) : setConfig(dlgCfg); - warn_static("consider for removal after FFS 10.3 release") -#if 1 - m_panelLogfile->Hide(); - m_bitmapLogFile->Hide(); - m_checkBoxSaveLog->Hide(); - m_checkBoxLogfilesLimit->Hide(); - m_spinCtrlLogfileLimit->Hide(); -#endif - //enable dialog-specific key events Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(BatchDialog::onLocalKeyEvent), nullptr, this); @@ -127,11 +103,6 @@ void BatchDialog::updateGui() //re-evaluate gui after config changes m_radioBtnErrorDialogCancel->Enable(!dlgCfg.ignoreErrors); m_bitmapMinimizeToTray->SetBitmap(dlgCfg.batchExCfg.runMinimized ? getResourceImage(L"minimize_to_tray") : greyScale(getResourceImage(L"minimize_to_tray"))); - - m_panelLogfile ->Enable (m_checkBoxSaveLog->GetValue()); //enabled status is *not* directly dependent from resolved config! (but transitively) - m_bitmapLogFile->SetBitmap(m_checkBoxSaveLog->GetValue() ? getResourceImage(L"log_file") : greyScale(getResourceImage(L"log_file"))); - m_checkBoxLogfilesLimit->Enable(m_checkBoxSaveLog->GetValue()); - m_spinCtrlLogfileLimit ->Enable(m_checkBoxSaveLog->GetValue() && m_checkBoxLogfilesLimit->GetValue()); } @@ -158,12 +129,6 @@ void BatchDialog::setConfig(const BatchDialogConfig& dlgCfg) m_checkBoxAutoClose ->SetValue(dlgCfg.batchExCfg.autoCloseSummary); setEnumVal(enumPostSyncAction_, *m_choicePostSyncAction, dlgCfg.batchExCfg.postSyncAction); - logfileDir_->setPath(dlgCfg.batchExCfg.altLogFolderPathPhrase); - m_checkBoxSaveLog ->SetValue(dlgCfg.batchExCfg.altLogfileCountMax != 0); - m_checkBoxLogfilesLimit->SetValue(dlgCfg.batchExCfg.altLogfileCountMax > 0); - m_spinCtrlLogfileLimit ->SetValue(dlgCfg.batchExCfg.altLogfileCountMax > 0 ? dlgCfg.batchExCfg.altLogfileCountMax : 100); - //attention: emits a "change value" event!! => updateGui() called implicitly! - updateGui(); //re-evaluate gui after config changes } @@ -179,14 +144,6 @@ BatchDialogConfig BatchDialog::getConfig() const dlgCfg.batchExCfg.autoCloseSummary = m_checkBoxAutoClose ->GetValue(); dlgCfg.batchExCfg.postSyncAction = getEnumVal(enumPostSyncAction_, *m_choicePostSyncAction); - dlgCfg.batchExCfg.altLogFolderPathPhrase = utfTo<Zstring>(logfileDir_->getPath()); - dlgCfg.batchExCfg.altLogfileCountMax = m_checkBoxSaveLog->GetValue() ? (m_checkBoxLogfilesLimit->GetValue() ? m_spinCtrlLogfileLimit->GetValue() : -1) : 0; - - warn_static("consider for removal after FFS 10.3 release") -#if 1 - dlgCfg.batchExCfg.altLogfileCountMax = 0; -#endif - return dlgCfg; } @@ -199,20 +156,10 @@ void BatchDialog::onLocalKeyEvent(wxKeyEvent& event) void BatchDialog::OnSaveBatchJob(wxCommandEvent& event) { - BatchDialogConfig dlgCfg = getConfig(); + //BatchDialogConfig dlgCfg = getConfig(); //------- parameter validation (BEFORE writing output!) ------- - warn_static("consider for removal after FFS 10.3 release") -#if 0 - if (dlgCfg.batchExCfg.altLogfileCountMax != 0 && - trimCpy(dlgCfg.batchExCfg.altLogFolderPathPhrase).empty()) - { - showNotificationDialog(this, DialogInfoType::INFO, PopupDialogCfg().setMainInstructions(_("A folder input field is empty."))); - //don't show error icon to follow "Windows' encouraging tone" - m_logFolderPath->SetFocus(); - return; - } -#endif + //------------------------------------------------------------- dlgCfgOut_ = getConfig(); diff --git a/FreeFileSync/Source/ui/batch_status_handler.cpp b/FreeFileSync/Source/ui/batch_status_handler.cpp index 04201a9d..a4e45c50 100755 --- a/FreeFileSync/Source/ui/batch_status_handler.cpp +++ b/FreeFileSync/Source/ui/batch_status_handler.cpp @@ -22,12 +22,10 @@ BatchStatusHandler::BatchStatusHandler(bool showProgress, const std::wstring& jobName, const Zstring& soundFileSyncComplete, const std::chrono::system_clock::time_point& startTime, - int altLogfileCountMax, //0: logging inactive; < 0: no limit - const Zstring& altLogFolderPathPhrase, bool ignoreErrors, BatchErrorHandling batchErrorHandling, size_t automaticRetryCount, - size_t automaticRetryDelay, + std::chrono::seconds automaticRetryDelay, const Zstring& postSyncCommand, PostSyncCondition postSyncCondition, PostSyncAction postSyncAction) : @@ -52,9 +50,7 @@ jobName, soundFileSyncComplete, ignoreErrors, automaticRetryCount, [&] jobName_(jobName), startTime_(startTime), postSyncCommand_(postSyncCommand), - postSyncCondition_(postSyncCondition), - altLogfileCountMax_(altLogfileCountMax), - altLogFolderPathPhrase_(altLogFolderPathPhrase) + postSyncCondition_(postSyncCondition) { //ATTENTION: "progressDlg_" is an unmanaged resource!!! However, at this point we already consider construction complete! => //ZEN_ON_SCOPE_FAIL( cleanup(); ); //destructor call would lead to member double clean-up!!! @@ -73,7 +69,7 @@ BatchStatusHandler::~BatchStatusHandler() } -BatchStatusHandler::Result BatchStatusHandler::reportFinalStatus(int logfilesMaxAgeDays, const std::set<Zstring, LessFilePath>& logFilePathsToKeep) //noexcept!! +BatchStatusHandler::Result BatchStatusHandler::reportFinalStatus(const Zstring& altLogFolderPathPhrase, int logfilesMaxAgeDays, const std::set<AbstractPath>& logFilePathsToKeep) //noexcept!! { const auto totalTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - startTime_); @@ -139,29 +135,21 @@ BatchStatusHandler::Result BatchStatusHandler::reportFinalStatus(int logfilesMax //----------------- always save log under %appdata%\FreeFileSync\Logs ------------------------ //create not before destruction: 1. avoid issues with FFS trying to sync open log file 2. simplify transactional retry on failure 3. include status in log file name without rename // 4. failure to write to particular stream must not be retried! - Zstring logFilePath; + AbstractPath logFilePath = getNullPath(); try { //do NOT use tryReportingError()! saving log files should not be cancellable! auto notifyStatusNoThrow = [&](const std::wstring& msg) { try { reportStatus(msg); /*throw X*/ } catch (...) {} }; - logFilePath = saveLogFile(summary, errorLog_, startTime_, logfilesMaxAgeDays, logFilePathsToKeep, notifyStatusNoThrow /*throw X*/); //throw FileError + logFilePath = saveLogFile(summary, errorLog_, startTime_, altLogFolderPathPhrase, logfilesMaxAgeDays, logFilePathsToKeep, notifyStatusNoThrow /*throw X*/); //throw FileError } catch (const FileError& e) { errorLog_.logMsg(e.toString(), MSG_TYPE_ERROR); } - warn_static("consider for removal after FFS 10.3 release") -#if 1 - ////save additional logfile copy to user-defined path if requested - //doSaveLogFile(altLogFolderPathPhrase_, altLogfileCountMax_); - (void)altLogFolderPathPhrase_; - (void)altLogfileCountMax_; -#endif - //execute post sync command *after* writing log files, so that user can refer to the log via the command! if (!commandLine.empty()) try { //---------------------------------------------------------------------- - ::wxSetEnv(L"logfile_path", utfTo<wxString>(logFilePath)); + ::wxSetEnv(L"logfile_path", AFS::getDisplayPath(logFilePath)); //---------------------------------------------------------------------- //use ExecutionType::ASYNC until there is reason not to: https://freefilesync.org/forum/viewtopic.php?t=31 shellExecute(expandMacros(commandLine), ExecutionType::ASYNC); //throw FileError @@ -191,7 +179,7 @@ BatchStatusHandler::Result BatchStatusHandler::reportFinalStatus(int logfilesMax if (progressDlg_->getWindowIfVisible()) try { - delayAndCountDown(operationName, 5 /*delayInSec*/, notifyStatusThrowOnCancel); //throw X + delayAndCountDown(operationName, std::chrono::seconds(5), notifyStatusThrowOnCancel); //throw X } catch (...) { return false; } diff --git a/FreeFileSync/Source/ui/batch_status_handler.h b/FreeFileSync/Source/ui/batch_status_handler.h index 3ed11e4d..847e03d6 100755 --- a/FreeFileSync/Source/ui/batch_status_handler.h +++ b/FreeFileSync/Source/ui/batch_status_handler.h @@ -26,12 +26,10 @@ public: const std::wstring& jobName, //should not be empty for a batch job! const Zstring& soundFileSyncComplete, const std::chrono::system_clock::time_point& startTime, - int altLogfileCountMax, //0: logging inactive; < 0: no limit - const Zstring& altLogFolderPathPhrase, bool ignoreErrors, BatchErrorHandling batchErrorHandling, size_t automaticRetryCount, - size_t automaticRetryDelay, + std::chrono::seconds automaticRetryDelay, const Zstring& postSyncCommand, PostSyncCondition postSyncCondition, PostSyncAction postSyncAction); //noexcept!! @@ -50,9 +48,9 @@ public: { SyncResult finalStatus; bool switchToGuiRequested; - Zstring logFilePath; + AbstractPath logFilePath; }; - Result reportFinalStatus(int logfilesMaxAgeDays, const std::set<Zstring, LessFilePath>& logFilePathsToKeep); //noexcept!! + Result reportFinalStatus(const Zstring& altLogFolderPathPhrase, int logfilesMaxAgeDays, const std::set<AbstractPath>& logFilePathsToKeep); //noexcept!! private: void onProgressDialogTerminate(); @@ -63,7 +61,7 @@ private: zen::ErrorLog errorLog_; //list of non-resolved errors and warnings const size_t automaticRetryCount_; - const size_t automaticRetryDelay_; + const std::chrono::seconds automaticRetryDelay_; SyncProgressDialog* progressDlg_; //managed to have shorter lifetime than this handler! @@ -72,9 +70,6 @@ private: const Zstring postSyncCommand_; const PostSyncCondition postSyncCondition_; - - const int altLogfileCountMax_; - const Zstring altLogFolderPathPhrase_; }; } diff --git a/FreeFileSync/Source/ui/cfg_grid.cpp b/FreeFileSync/Source/ui/cfg_grid.cpp index 939050d6..72acf4a6 100755 --- a/FreeFileSync/Source/ui/cfg_grid.cpp +++ b/FreeFileSync/Source/ui/cfg_grid.cpp @@ -15,9 +15,11 @@ #include <wx/settings.h> #include "../base/icon_buffer.h" #include "../base/ffs_paths.h" +//#include "../fs/mtp.h" using namespace zen; using namespace fff; +using AFS = AbstractFileSystem; Zstring fff::getLastRunConfigPath() @@ -131,7 +133,7 @@ void ConfigView::setLastRunStats(const std::vector<Zstring>& filePaths, const La if (lastRun.result != SyncResult::ABORTED) it->second.cfgItem.lastSyncTime = lastRun.lastRunTime; - if (!lastRun.logFilePath.empty()) + if (!AFS::isNullPath(lastRun.logFilePath)) { it->second.cfgItem.logFilePath = lastRun.logFilePath; it->second.cfgItem.logResult = lastRun.result; @@ -184,8 +186,8 @@ void ConfigView::sortListViewImpl() if (lhs->second.isLastRunCfg != rhs->second.isLastRunCfg) return lhs->second.isLastRunCfg < rhs->second.isLastRunCfg; //"last session" label should be (always) last - const bool hasLogL = !lhs->second.cfgItem.logFilePath.empty(); - const bool hasLogR = !rhs->second.cfgItem.logFilePath.empty(); + const bool hasLogL = !AFS::isNullPath(lhs->second.cfgItem.logFilePath); + const bool hasLogR = !AFS::isNullPath(rhs->second.cfgItem.logFilePath); if (hasLogL != hasLogR) return hasLogL > hasLogR; //move sync jobs that were never run to the back @@ -288,7 +290,7 @@ private: case ColumnTypeCfg::LAST_LOG: if (!item->isLastRunCfg && - !item->cfgItem.logFilePath.empty()) + !AFS::isNullPath(item->cfgItem.logFilePath)) return getFinalStatusLabel(item->cfgItem.logResult); break; } @@ -360,7 +362,7 @@ private: case ColumnTypeCfg::LAST_LOG: if (!item->isLastRunCfg && - !item->cfgItem.logFilePath.empty()) + !AFS::isNullPath(item->cfgItem.logFilePath)) { const wxBitmap statusIcon = [&] { @@ -423,7 +425,8 @@ private: case ColumnTypeCfg::LAST_LOG: if (!item->isLastRunCfg && - !item->cfgItem.logFilePath.empty()) + !AFS::isNullPath(item->cfgItem.logFilePath) && + AFS::getNativeItemPath(item->cfgItem.logFilePath)) return static_cast<HoverArea>(HoverAreaLog::LINK); break; } @@ -506,8 +509,8 @@ private: case ColumnTypeCfg::LAST_LOG: if (!item->isLastRunCfg && - !item->cfgItem.logFilePath.empty()) - return getFinalStatusLabel(item->cfgItem.logResult) + SPACED_DASH + utfTo<std::wstring>(item->cfgItem.logFilePath); + !AFS::isNullPath(item->cfgItem.logFilePath)) + return getFinalStatusLabel(item->cfgItem.logResult) + SPACED_DASH + AFS::getDisplayPath(item->cfgItem.logFilePath); break; } return std::wstring(); @@ -519,10 +522,13 @@ private: switch (static_cast<HoverAreaLog>(event.hoverArea_)) { case HoverAreaLog::LINK: - assert(!item->cfgItem.logFilePath.empty()); //see getRowMouseHover() try { - openWithDefaultApplication(item->cfgItem.logFilePath); //throw FileError + if (std::optional<Zstring> nativePath = AFS::getNativeItemPath(item->cfgItem.logFilePath)) + openWithDefaultApplication(*nativePath); //throw FileError + else + assert(false); + assert(!AFS::isNullPath(item->cfgItem.logFilePath)); //see getRowMouseHover() } catch (const FileError& e) { showNotificationDialog(&grid_, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); } return; @@ -532,11 +538,11 @@ private: void onMouseLeftDouble(GridClickEvent& event) { - switch (static_cast<HoverAreaLog>(event.hoverArea_)) - { - case HoverAreaLog::LINK: - return; //swallow event here before MainDialog considers it as a request to start comparison - } + switch (static_cast<HoverAreaLog>(event.hoverArea_)) + { + case HoverAreaLog::LINK: + return; //swallow event here before MainDialog considers it as a request to start comparison + } event.Skip(); } @@ -576,7 +582,7 @@ void cfggrid::addAndSelect(Grid& grid, const std::vector<Zstring>& filePaths, bo grid.clearSelection(GridEventPolicy::DENY); const std::set<Zstring, LessFilePath> pathsSorted(filePaths.begin(), filePaths.end()); - Opt<size_t> selectionTopRow; + std::optional<size_t> selectionTopRow; for (size_t i = 0; i < grid.getRowCount(); ++i) if (pathsSorted.find(getDataView(grid).getItem(i)->cfgItem.cfgFilePath) != pathsSorted.end()) diff --git a/FreeFileSync/Source/ui/cfg_grid.h b/FreeFileSync/Source/ui/cfg_grid.h index fc99a40d..37947867 100755 --- a/FreeFileSync/Source/ui/cfg_grid.h +++ b/FreeFileSync/Source/ui/cfg_grid.h @@ -11,6 +11,7 @@ #include <wx+/dc.h> #include <zen/zstring.h> #include "../base/return_codes.h" +#include "../fs/native.h" namespace fff @@ -20,7 +21,7 @@ struct ConfigFileItem ConfigFileItem() {} ConfigFileItem(const Zstring& filePath, time_t syncTime, - const Zstring& logPath, + const AbstractPath& logPath, SyncResult result) : cfgFilePath(filePath), lastSyncTime(syncTime), @@ -28,9 +29,9 @@ struct ConfigFileItem logResult(result) {} Zstring cfgFilePath; - time_t lastSyncTime = 0; //last COMPLETED sync (aborted syncs don't count) - Zstring logFilePath; //ANY last sync attempt (including aborted syncs) - SyncResult logResult = SyncResult::ABORTED; // + time_t lastSyncTime = 0; //last COMPLETED sync (aborted syncs don't count) + AbstractPath logFilePath = getNullPath(); //ANY last sync attempt (including aborted syncs) + SyncResult logResult = SyncResult::ABORTED; // }; @@ -96,9 +97,9 @@ public: struct LastRunStats { - time_t lastRunTime = 0; - SyncResult result = SyncResult::ABORTED; - Zstring logFilePath; //optional + time_t lastRunTime = 0; + SyncResult result = SyncResult::ABORTED; + AbstractPath logFilePath; //optional }; void setLastRunStats(const std::vector<Zstring>& filePaths, const LastRunStats& lastRun); diff --git a/FreeFileSync/Source/ui/file_grid.cpp b/FreeFileSync/Source/ui/file_grid.cpp index cc1c5ece..1f634cd4 100755 --- a/FreeFileSync/Source/ui/file_grid.cpp +++ b/FreeFileSync/Source/ui/file_grid.cpp @@ -544,7 +544,7 @@ private: break; case IconInfo::ICON_PATH: - if (Opt<wxBitmap> tmpIco = iconMgr_->refIconBuffer().retrieveFileIcon(ii.fsObj->template getAbstractPath<side>())) + if (std::optional<wxBitmap> tmpIco = iconMgr_->refIconBuffer().retrieveFileIcon(ii.fsObj->template getAbstractPath<side>())) fileIcon = *tmpIco; else { @@ -679,17 +679,12 @@ private: //draw sort marker if (getGridDataView()) - { - auto sortInfo = getGridDataView()->getSortInfo(); - if (sortInfo) - { + if (auto sortInfo = getGridDataView()->getSortInfo()) if (colType == static_cast<ColumnType>(sortInfo->type) && (side == LEFT_SIDE) == sortInfo->onLeft) { const wxBitmap& marker = getResourceImage(sortInfo->ascending ? L"sort_ascending" : L"sort_descending"); drawBitmapRtlNoMirror(dc, marker, rectInner, wxALIGN_CENTER_HORIZONTAL); } - } - } } struct IconInfo @@ -769,7 +764,7 @@ private: ItemPathFormat itemPathFormat_ = ItemPathFormat::FULL_PATH; std::vector<char> failedLoads_; //effectively a vector<bool> of size "number of rows" - Opt<wxBitmap> renderBuf_; //avoid costs of recreating this temporary variable + std::optional<wxBitmap> renderBuf_; //avoid costs of recreating this temporary variable }; @@ -1285,7 +1280,7 @@ private: bool highlightSyncAction_ = false; bool selectionInProgress_ = false; - Opt<wxBitmap> renderBuf_; //avoid costs of recreating this temporary variable + std::optional<wxBitmap> renderBuf_; //avoid costs of recreating this temporary variable Tooltip toolTip_; wxImage notch_ = getResourceImage(L"notch").ConvertToImage(); }; diff --git a/FreeFileSync/Source/ui/file_view.cpp b/FreeFileSync/Source/ui/file_view.cpp index 09986aac..1d602540 100755 --- a/FreeFileSync/Source/ui/file_view.cpp +++ b/FreeFileSync/Source/ui/file_view.cpp @@ -176,7 +176,7 @@ FileView::StatusSyncPreview FileView::updateSyncPreview(bool showExcluded, //map { StatusSyncPreview output; - updateView([&](const FileSystemObject& fsObj) -> bool + updateView([&](const FileSystemObject& fsObj) { if (!fsObj.isActive()) { @@ -322,7 +322,7 @@ void FileView::setData(FolderComparison& folderCmp) //clear everything std::vector<FileSystemObject::ObjectId>().swap(viewRef_); //free mem std::vector<RefIndex>().swap(sortedRef_); // - currentSort_ = NoValue(); + currentSort_ = {}; folderPairCount_ = std::count_if(begin(folderCmp), end(folderCmp), [](const BaseFolderPair& baseObj) //count non-empty pairs to distinguish single/multiple folder pair cases diff --git a/FreeFileSync/Source/ui/file_view.h b/FreeFileSync/Source/ui/file_view.h index c87590e7..2bef757c 100755 --- a/FreeFileSync/Source/ui/file_view.h +++ b/FreeFileSync/Source/ui/file_view.h @@ -9,6 +9,7 @@ #include <vector> #include <unordered_map> +#include <zen/stl_tools.h> #include "file_grid_attr.h" #include "../base/file_hierarchy.h" @@ -108,7 +109,7 @@ public: bool onLeft = false; bool ascending = false; }; - const SortInfo* getSortInfo() const { return currentSort_.get(); } //return nullptr if currently not sorted + const SortInfo* getSortInfo() const { return zen::get(currentSort_); } //return nullptr if currently not sorted ptrdiff_t findRowDirect(FileSystemObject::ObjectIdConst objId) const; // find an object's row position on view list directly, return < 0 if not found ptrdiff_t findRowFirstChild(const ContainerObject* hierObj) const; // find first child of FolderPair or BaseFolderPair *on sorted sub view* @@ -172,7 +173,7 @@ private: template <bool ascending> struct LessSyncDirection; - zen::Opt<SortInfo> currentSort_; + std::optional<SortInfo> currentSort_; }; diff --git a/FreeFileSync/Source/ui/folder_pair.h b/FreeFileSync/Source/ui/folder_pair.h index 4ac801dd..357de4d3 100755 --- a/FreeFileSync/Source/ui/folder_pair.h +++ b/FreeFileSync/Source/ui/folder_pair.h @@ -28,7 +28,7 @@ template <class GuiPanel> class FolderPairPanelBasic : private wxEvtHandler { public: - void setConfig(const zen::Opt<CompConfig>& compConfig, const zen::Opt<SyncConfig>& syncCfg, const FilterConfig& filter) + void setConfig(const std::optional<CompConfig>& compConfig, const std::optional<SyncConfig>& syncCfg, const FilterConfig& filter) { localCmpCfg_ = compConfig; localSyncCfg_ = syncCfg; @@ -36,8 +36,8 @@ public: refreshButtons(); } - zen::Opt<CompConfig> getCompConfig () const { return localCmpCfg_; } - zen::Opt<SyncConfig> getSyncConfig () const { return localSyncCfg_; } + std::optional<CompConfig> getCompConfig () const { return localCmpCfg_; } + std::optional<SyncConfig> getSyncConfig () const { return localSyncCfg_; } FilterConfig getFilterConfig() const { return localFilter_; } @@ -95,7 +95,7 @@ private: { auto removeLocalCompCfg = [&] { - this->localCmpCfg_ = zen::NoValue(); //"this->" galore: workaround GCC compiler bugs + this->localCmpCfg_ = {}; //"this->" galore: workaround GCC compiler bugs this->refreshButtons(); this->onLocalCompCfgChange(); }; @@ -109,7 +109,7 @@ private: { auto removeLocalSyncCfg = [&] { - this->localSyncCfg_ = zen::NoValue(); + this->localSyncCfg_ = {}; this->refreshButtons(); this->onLocalSyncCfgChange(); }; @@ -161,8 +161,8 @@ private: GuiPanel& basicPanel_; //panel to be enhanced by this template //alternate configuration attached to it - zen::Opt<CompConfig> localCmpCfg_; - zen::Opt<SyncConfig> localSyncCfg_; + std::optional<CompConfig> localCmpCfg_; + std::optional<SyncConfig> localSyncCfg_; FilterConfig localFilter_; }; } diff --git a/FreeFileSync/Source/ui/folder_selector.cpp b/FreeFileSync/Source/ui/folder_selector.cpp index babef8bb..0d68ccad 100755 --- a/FreeFileSync/Source/ui/folder_selector.cpp +++ b/FreeFileSync/Source/ui/folder_selector.cpp @@ -36,8 +36,10 @@ void setFolderPathPhrase(const Zstring& folderPathPhrase, FolderHistoryBox* comb const Zstring folderPathPhraseFmt = AFS::getInitPathPhrase(createAbstractPath(folderPathPhrase)); //noexcept //may block when resolving [<volume name>] - tooltipWnd.SetToolTip(nullptr); //workaround wxComboBox bug http://trac.wxwidgets.org/ticket/10512 / http://trac.wxwidgets.org/ticket/12659 - tooltipWnd.SetToolTip(utfTo<wxString>(folderPathPhraseFmt)); //who knows when the real bugfix reaches mere mortals via an official release... + if (folderPathPhraseFmt.empty()) + tooltipWnd.UnsetToolTip(); //wxGTK doesn't allow wxToolTip with empty text! + else + tooltipWnd.SetToolTip(utfTo<wxString>(folderPathPhraseFmt)); if (staticText) { @@ -143,7 +145,7 @@ void FolderSelector::onItemPathDropped(FileDropEvent& event) try { if (AFS::getItemType(itemPath) == AFS::ItemType::FILE) //throw FileError - if (Opt<AbstractPath> parentPath = AFS::getParentFolderPath(itemPath)) + if (std::optional<AbstractPath> parentPath = AFS::getParentFolderPath(itemPath)) return AFS::getInitPathPhrase(*parentPath); } catch (FileError&) {} //e.g. good for inactive mapped network shares, not so nice for C:\pagefile.sys @@ -198,7 +200,7 @@ void FolderSelector::onSelectFolder(wxCommandEvent& event) { const AbstractPath folderPath = createItemPathNative(folderPathPhrase); if (folderExistsTimed(folderPath)) - if (Opt<Zstring> nativeFolderPath = AFS::getNativeItemPath(folderPath)) + if (std::optional<Zstring> nativeFolderPath = AFS::getNativeItemPath(folderPath)) defaultFolderPath = *nativeFolderPath; } } diff --git a/FreeFileSync/Source/ui/gui_generated.cpp b/FreeFileSync/Source/ui/gui_generated.cpp index 820b5f6f..00b3ff64 100755 --- a/FreeFileSync/Source/ui/gui_generated.cpp +++ b/FreeFileSync/Source/ui/gui_generated.cpp @@ -1498,6 +1498,61 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w m_staticline331 = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); bSizer159->Add( m_staticline331, 0, wxEXPAND, 5 ); + bSizerCompMisc = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer2781; + bSizer2781 = new wxBoxSizer( wxHORIZONTAL ); + + wxFlexGridSizer* fgSizer61; + fgSizer61 = new wxFlexGridSizer( 0, 2, 5, 5 ); + fgSizer61->SetFlexibleDirection( wxBOTH ); + fgSizer61->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_bitmapIgnoreErrors = new wxStaticBitmap( m_panelComparisonSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer61->Add( m_bitmapIgnoreErrors, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_checkBoxIgnoreErrors = new wxCheckBox( m_panelComparisonSettings, wxID_ANY, _("Ignore errors"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); + fgSizer61->Add( m_checkBoxIgnoreErrors, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_bitmapRetryErrors = new wxStaticBitmap( m_panelComparisonSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer61->Add( m_bitmapRetryErrors, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_checkBoxAutoRetry = new wxCheckBox( m_panelComparisonSettings, wxID_ANY, _("Automatic retry"), wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer61->Add( m_checkBoxAutoRetry, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer2781->Add( fgSizer61, 0, wxALIGN_CENTER_VERTICAL|wxALL, 10 ); + + fgSizerAutoRetry = new wxFlexGridSizer( 0, 2, 5, 10 ); + fgSizerAutoRetry->SetFlexibleDirection( wxBOTH ); + fgSizerAutoRetry->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_staticText96 = new wxStaticText( m_panelComparisonSettings, wxID_ANY, _("Retry count:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText96->Wrap( -1 ); + fgSizerAutoRetry->Add( m_staticText96, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextAutoRetryDelay = new wxStaticText( m_panelComparisonSettings, wxID_ANY, _("Delay (in seconds):"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextAutoRetryDelay->Wrap( -1 ); + fgSizerAutoRetry->Add( m_staticTextAutoRetryDelay, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_spinCtrlAutoRetryCount = new wxSpinCtrl( m_panelComparisonSettings, 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_panelComparisonSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1, -1 ), wxSP_ARROW_KEYS, 0, 2000000000, 0 ); + fgSizerAutoRetry->Add( m_spinCtrlAutoRetryDelay, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer2781->Add( fgSizerAutoRetry, 0, wxTOP|wxBOTTOM|wxRIGHT|wxALIGN_CENTER_VERTICAL, 10 ); + + + bSizerCompMisc->Add( bSizer2781, 0, wxEXPAND, 5 ); + + m_staticline3311 = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizerCompMisc->Add( m_staticline3311, 0, wxEXPAND, 5 ); + + + bSizer159->Add( bSizerCompMisc, 0, wxEXPAND, 5 ); + bSizer2561->Add( bSizer159, 1, wxEXPAND, 5 ); @@ -1855,6 +1910,9 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w bSizer237->Add( bSizer235, 0, wxALL, 5 ); + + bSizer237->Add( 10, 0, 0, 0, 5 ); + wxBoxSizer* bSizer238; bSizer238 = new wxBoxSizer( wxVERTICAL ); @@ -1956,7 +2014,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w bSizerSyncDirHolder->Add( m_staticTextSyncVarDescription, 0, wxALL|wxALIGN_CENTER_VERTICAL, 10 ); - bSizer238->Add( bSizerSyncDirHolder, 0, wxALL, 10 ); + bSizer238->Add( bSizerSyncDirHolder, 0, wxTOP|wxBOTTOM|wxRIGHT, 10 ); bSizer238->Add( 0, 0, 1, wxEXPAND, 5 ); @@ -2090,10 +2148,10 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w bSizer156->Add( m_buttonSelectVersioningFolder, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonSelectAltFolder = new wxBitmapButton( m_panelVersioning, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), wxBU_AUTODRAW ); - m_bpButtonSelectAltFolder->SetToolTip( _("Access online storage") ); + m_bpButtonSelectVersioningAltFolder = new wxBitmapButton( m_panelVersioning, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), wxBU_AUTODRAW ); + m_bpButtonSelectVersioningAltFolder->SetToolTip( _("Access online storage") ); - bSizer156->Add( m_bpButtonSelectAltFolder, 0, wxEXPAND, 5 ); + bSizer156->Add( m_bpButtonSelectVersioningAltFolder, 0, wxEXPAND, 5 ); bSizer253->Add( bSizer156, 0, wxEXPAND, 5 ); @@ -2212,78 +2270,83 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w m_staticline582 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); bSizer232->Add( m_staticline582, 0, wxEXPAND, 5 ); - bSizerMiscConfig = new wxBoxSizer( wxHORIZONTAL ); + bSizerSyncMisc = new wxBoxSizer( wxHORIZONTAL ); - wxFlexGridSizer* fgSizer61; - fgSizer61 = new wxFlexGridSizer( 0, 2, 5, 5 ); - fgSizer61->SetFlexibleDirection( wxBOTH ); - fgSizer61->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + wxBoxSizer* bSizer2372; + bSizer2372 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapIgnoreErrors = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - fgSizer61->Add( m_bitmapIgnoreErrors, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + m_panelLogfile = new wxPanel( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelLogfile->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_checkBoxIgnoreErrors = new wxCheckBox( m_panelSyncSettings, wxID_ANY, _("Ignore errors"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); - fgSizer61->Add( m_checkBoxIgnoreErrors, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + wxBoxSizer* bSizer1912; + bSizer1912 = new wxBoxSizer( wxVERTICAL ); - m_bitmapRetryErrors = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - fgSizer61->Add( m_bitmapRetryErrors, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + wxBoxSizer* bSizer279; + bSizer279 = new wxBoxSizer( wxHORIZONTAL ); - m_checkBoxAutoRetry = new wxCheckBox( m_panelSyncSettings, wxID_ANY, _("Automatic retry"), wxDefaultPosition, wxDefaultSize, 0 ); - fgSizer61->Add( m_checkBoxAutoRetry, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + m_bitmapLogFile = new wxStaticBitmap( m_panelLogfile, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizer279->Add( m_bitmapLogFile, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + m_checkBoxSaveLog = new wxCheckBox( m_panelLogfile, wxID_ANY, _("&Override default log path:"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer279->Add( m_checkBoxSaveLog, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - bSizerMiscConfig->Add( fgSizer61, 0, wxALIGN_CENTER_VERTICAL|wxALL, 10 ); + m_buttonSelectLogFolder = new wxButton( m_panelLogfile, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonSelectLogFolder->SetToolTip( _("Select a folder") ); - fgSizerAutoRetry = new wxFlexGridSizer( 0, 2, 5, 10 ); - fgSizerAutoRetry->SetFlexibleDirection( wxBOTH ); - fgSizerAutoRetry->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + bSizer279->Add( m_buttonSelectLogFolder, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_staticText96 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("Retry count:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText96->Wrap( -1 ); - fgSizerAutoRetry->Add( m_staticText96, 0, wxALIGN_CENTER_VERTICAL, 5 ); + m_bpButtonSelectAltLogFolder = new wxBitmapButton( m_panelLogfile, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), wxBU_AUTODRAW ); + m_bpButtonSelectAltLogFolder->SetToolTip( _("Access online storage") ); - m_staticTextAutoRetryDelay = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("Delay (in seconds):"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextAutoRetryDelay->Wrap( -1 ); - fgSizerAutoRetry->Add( m_staticTextAutoRetryDelay, 0, wxALIGN_CENTER_VERTICAL, 5 ); + bSizer279->Add( m_bpButtonSelectAltLogFolder, 0, wxEXPAND, 5 ); - 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 ); - fgSizerAutoRetry->Add( m_spinCtrlAutoRetryDelay, 0, wxALIGN_CENTER_VERTICAL, 5 ); + bSizer1912->Add( bSizer279, 0, wxEXPAND, 5 ); + + m_logFolderPath = new fff::FolderHistoryBox( m_panelLogfile, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizer1912->Add( m_logFolderPath, 0, wxEXPAND|wxTOP, 5 ); - bSizerMiscConfig->Add( fgSizerAutoRetry, 0, wxTOP|wxBOTTOM|wxRIGHT|wxALIGN_CENTER_VERTICAL, 10 ); + m_panelLogfile->SetSizer( bSizer1912 ); + m_panelLogfile->Layout(); + bSizer1912->Fit( m_panelLogfile ); + bSizer2372->Add( m_panelLogfile, 1, 0, 5 ); + + + bSizerSyncMisc->Add( bSizer2372, 1, wxALL|wxALIGN_CENTER_VERTICAL, 10 ); m_staticline57 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizerMiscConfig->Add( m_staticline57, 0, wxEXPAND, 5 ); + bSizerSyncMisc->Add( m_staticline57, 0, wxEXPAND, 5 ); wxBoxSizer* bSizer247; bSizer247 = new wxBoxSizer( wxVERTICAL ); + wxBoxSizer* bSizer251; + bSizer251 = new wxBoxSizer( wxHORIZONTAL ); + m_staticText89 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("Run a command after synchronization:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText89->Wrap( -1 ); - bSizer247->Add( m_staticText89, 0, wxBOTTOM, 5 ); + bSizer251->Add( m_staticText89, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - wxBoxSizer* bSizer251; - bSizer251 = new wxBoxSizer( wxHORIZONTAL ); + + bSizer251->Add( 0, 0, 1, 0, 5 ); wxArrayString m_choicePostSyncConditionChoices; m_choicePostSyncCondition = new wxChoice( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choicePostSyncConditionChoices, 0 ); m_choicePostSyncCondition->SetSelection( 0 ); bSizer251->Add( m_choicePostSyncCondition, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_comboBoxPostSyncCommand = new fff::CommandBox( m_panelSyncSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizer251->Add( m_comboBoxPostSyncCommand, 1, wxALIGN_CENTER_VERTICAL, 5 ); - bSizer247->Add( bSizer251, 0, wxEXPAND, 5 ); + m_comboBoxPostSyncCommand = new fff::CommandBox( m_panelSyncSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizer247->Add( m_comboBoxPostSyncCommand, 0, wxTOP|wxEXPAND, 5 ); + - bSizerMiscConfig->Add( bSizer247, 1, wxALL|wxALIGN_CENTER_VERTICAL, 10 ); + bSizerSyncMisc->Add( bSizer247, 1, wxALL|wxALIGN_CENTER_VERTICAL, 10 ); - bSizer232->Add( bSizerMiscConfig, 1, wxEXPAND, 5 ); + bSizer232->Add( bSizerSyncMisc, 0, wxEXPAND, 5 ); m_panelSyncSettings->SetSizer( bSizer232 ); @@ -2338,6 +2401,8 @@ 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_checkBoxIgnoreErrors->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnToggleIgnoreErrors ), NULL, this ); + m_checkBoxAutoRetry->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnToggleAutoRetry ), 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 ); @@ -2371,8 +2436,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w m_checkBoxVersionMaxDays->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnToggleVersioningLimit ), NULL, this ); m_checkBoxVersionCountMin->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnToggleVersioningLimit ), NULL, this ); m_checkBoxVersionCountMax->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnToggleVersioningLimit ), NULL, this ); - m_checkBoxIgnoreErrors->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnToggleIgnoreErrors ), NULL, this ); - m_checkBoxAutoRetry->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnToggleAutoRetry ), NULL, this ); + m_checkBoxSaveLog->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnToggleSaveLogfile ), NULL, this ); m_buttonOkay->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnOkay ), NULL, this ); m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnCancel ), NULL, this ); } @@ -3714,64 +3778,6 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS m_staticline25 = new wxStaticLine( m_panel35, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); bSizer172->Add( m_staticline25, 0, wxEXPAND, 5 ); - wxBoxSizer* bSizer237; - bSizer237 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapLogFile = new wxStaticBitmap( m_panel35, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizer237->Add( m_bitmapLogFile, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - wxBoxSizer* bSizer191; - bSizer191 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer238; - bSizer238 = new wxBoxSizer( wxHORIZONTAL ); - - m_checkBoxSaveLog = new wxCheckBox( m_panel35, wxID_ANY, _("Save log:"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer238->Add( m_checkBoxSaveLog, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer238->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_checkBoxLogfilesLimit = new wxCheckBox( m_panel35, wxID_ANY, _("Limit number of log files:"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer238->Add( m_checkBoxLogfilesLimit, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_spinCtrlLogfileLimit = new wxSpinCtrl( m_panel35, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1, -1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 ); - bSizer238->Add( m_spinCtrlLogfileLimit, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer191->Add( bSizer238, 0, wxEXPAND, 5 ); - - m_panelLogfile = new wxPanel( m_panel35, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelLogfile->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer1721; - bSizer1721 = new wxBoxSizer( wxHORIZONTAL ); - - m_logFolderPath = new fff::FolderHistoryBox( m_panelLogfile, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizer1721->Add( m_logFolderPath, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - m_buttonSelectLogFolder = new wxButton( m_panelLogfile, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); - m_buttonSelectLogFolder->SetToolTip( _("Select a folder") ); - - bSizer1721->Add( m_buttonSelectLogFolder, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_bpButtonSelectAltLogFolder = new wxBitmapButton( m_panelLogfile, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), wxBU_AUTODRAW ); - m_bpButtonSelectAltLogFolder->SetToolTip( _("Access online storage") ); - - bSizer1721->Add( m_bpButtonSelectAltLogFolder, 0, wxEXPAND, 5 ); - - - m_panelLogfile->SetSizer( bSizer1721 ); - m_panelLogfile->Layout(); - bSizer1721->Fit( m_panelLogfile ); - bSizer191->Add( m_panelLogfile, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 ); - - - bSizer237->Add( bSizer191, 1, 0, 5 ); - - - bSizer172->Add( bSizer237, 0, wxEXPAND|wxALL, 5 ); - m_hyperlink17 = new wxHyperlinkCtrl( m_panel35, wxID_ANY, _("How can I schedule a batch job?"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); bSizer172->Add( m_hyperlink17, 0, wxALL, 10 ); @@ -3811,8 +3817,6 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS m_checkBoxIgnoreErrors->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnToggleIgnoreErrors ), NULL, this ); m_radioBtnErrorDialogShow->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( BatchDlgGenerated::OnErrorDialogShow ), NULL, this ); m_radioBtnErrorDialogCancel->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( BatchDlgGenerated::OnErrorDialogCancel ), NULL, this ); - m_checkBoxSaveLog->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnToggleGenerateLogfile ), NULL, this ); - m_checkBoxLogfilesLimit->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnToggleLogfilesLimit ), NULL, this ); m_hyperlink17->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( BatchDlgGenerated::OnHelpScheduleBatch ), NULL, this ); m_buttonSaveAs->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnSaveBatchJob ), NULL, this ); m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnCancel ), NULL, this ); @@ -4190,28 +4194,32 @@ OptionsDlgGenerated::OptionsDlgGenerated( wxWindow* parent, wxWindowID id, const bSizer258 = new wxBoxSizer( wxHORIZONTAL ); m_bitmapLogFile = new wxStaticBitmap( m_panel39, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizer258->Add( m_bitmapLogFile, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + bSizer258->Add( m_bitmapLogFile, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticText163 = new wxStaticText( m_panel39, wxID_ANY, _("Default log path:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText163->Wrap( -1 ); + bSizer258->Add( m_staticText163, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_hyperlinkLogFolder = new wxHyperlinkCtrl( m_panel39, wxID_ANY, _("dummy"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); bSizer258->Add( m_hyperlinkLogFolder, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); - bSizer259->Add( bSizer258, 0, wxEXPAND|wxALL, 5 ); + bSizer259->Add( bSizer258, 0, wxALL|wxEXPAND, 5 ); - wxBoxSizer* bSizer260; - bSizer260 = new wxBoxSizer( wxHORIZONTAL ); + wxBoxSizer* bSizer282; + bSizer282 = new wxBoxSizer( wxHORIZONTAL ); m_checkBoxLogFilesMaxAge = new wxCheckBox( m_panel39, wxID_ANY, _("Remove old log files after x days:"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer260->Add( m_checkBoxLogFilesMaxAge, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); + bSizer282->Add( m_checkBoxLogFilesMaxAge, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); m_spinCtrlLogFilesMaxAge = new wxSpinCtrl( m_panel39, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1, -1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 ); - bSizer260->Add( m_spinCtrlLogFilesMaxAge, 0, wxALIGN_CENTER_VERTICAL, 5 ); + bSizer282->Add( m_spinCtrlLogFilesMaxAge, 0, wxALIGN_CENTER_VERTICAL, 5 ); - bSizer259->Add( bSizer260, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + bSizer259->Add( bSizer282, 0, wxALL, 5 ); - bSizer166->Add( bSizer259, 0, wxALL, 5 ); + bSizer166->Add( bSizer259, 0, wxALL|wxEXPAND, 5 ); m_staticline361 = new wxStaticLine( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); bSizer166->Add( m_staticline361, 0, wxEXPAND, 5 ); diff --git a/FreeFileSync/Source/ui/gui_generated.h b/FreeFileSync/Source/ui/gui_generated.h index 7f6b8843..757634e8 100755 --- a/FreeFileSync/Source/ui/gui_generated.h +++ b/FreeFileSync/Source/ui/gui_generated.h @@ -342,6 +342,16 @@ protected: wxHyperlinkCtrl* m_hyperlink241; wxStaticLine* m_staticline441; wxStaticLine* m_staticline331; + wxBoxSizer* bSizerCompMisc; + wxStaticBitmap* m_bitmapIgnoreErrors; + wxCheckBox* m_checkBoxIgnoreErrors; + wxCheckBox* m_checkBoxAutoRetry; + wxFlexGridSizer* fgSizerAutoRetry; + wxStaticText* m_staticText96; + wxStaticText* m_staticTextAutoRetryDelay; + wxSpinCtrl* m_spinCtrlAutoRetryCount; + wxSpinCtrl* m_spinCtrlAutoRetryDelay; + wxStaticLine* m_staticline3311; wxStaticLine* m_staticlinePerformance; wxBoxSizer* bSizerPerformance; wxStaticText* m_staticTextPerfDeRequired; @@ -449,15 +459,11 @@ protected: wxSpinCtrl* m_spinCtrlVersionCountMin; wxSpinCtrl* m_spinCtrlVersionCountMax; wxStaticLine* m_staticline582; - wxBoxSizer* bSizerMiscConfig; - wxStaticBitmap* m_bitmapIgnoreErrors; - wxCheckBox* m_checkBoxIgnoreErrors; - wxCheckBox* m_checkBoxAutoRetry; - wxFlexGridSizer* fgSizerAutoRetry; - wxStaticText* m_staticText96; - wxStaticText* m_staticTextAutoRetryDelay; - wxSpinCtrl* m_spinCtrlAutoRetryCount; - wxSpinCtrl* m_spinCtrlAutoRetryDelay; + wxBoxSizer* bSizerSyncMisc; + wxPanel* m_panelLogfile; + wxStaticBitmap* m_bitmapLogFile; + wxCheckBox* m_checkBoxSaveLog; + wxButton* m_buttonSelectLogFolder; wxStaticLine* m_staticline57; wxStaticText* m_staticText89; fff::CommandBox* m_comboBoxPostSyncCommand; @@ -480,6 +486,8 @@ protected: virtual void OnHelpComparisonSettings( wxHyperlinkEvent& event ) { event.Skip(); } virtual void onlTimeShiftKeyDown( wxKeyEvent& event ) { event.Skip(); } virtual void OnHelpTimeShift( wxHyperlinkEvent& event ) { event.Skip(); } + virtual void OnToggleIgnoreErrors( wxCommandEvent& event ) { event.Skip(); } + virtual void OnToggleAutoRetry( wxCommandEvent& event ) { event.Skip(); } virtual void OnHelpPerformance( wxHyperlinkEvent& event ) { event.Skip(); } virtual void OnChangeFilterOption( wxCommandEvent& event ) { event.Skip(); } virtual void OnHelpShowExamples( wxHyperlinkEvent& event ) { event.Skip(); } @@ -507,16 +515,17 @@ protected: virtual void OnHelpVersioning( wxHyperlinkEvent& event ) { event.Skip(); } virtual void OnChanegVersioningStyle( wxCommandEvent& event ) { event.Skip(); } virtual void OnToggleVersioningLimit( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToggleIgnoreErrors( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToggleAutoRetry( wxCommandEvent& event ) { event.Skip(); } + virtual void OnToggleSaveLogfile( wxCommandEvent& event ) { event.Skip(); } virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); } virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } public: - wxStaticText* m_staticTextFilterDescr; - wxBitmapButton* m_bpButtonSelectAltFolder; wxStaticBitmap* m_bitmapRetryErrors; + wxStaticText* m_staticTextFilterDescr; + wxBitmapButton* m_bpButtonSelectVersioningAltFolder; + wxBitmapButton* m_bpButtonSelectAltLogFolder; + fff::FolderHistoryBox* m_logFolderPath; wxChoice* m_choicePostSyncCondition; ConfigDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Synchronization Settings"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); @@ -841,12 +850,6 @@ protected: wxStaticText* m_staticText137; wxStaticLine* m_staticline262; wxStaticLine* m_staticline25; - wxStaticBitmap* m_bitmapLogFile; - wxCheckBox* m_checkBoxSaveLog; - wxCheckBox* m_checkBoxLogfilesLimit; - wxSpinCtrl* m_spinCtrlLogfileLimit; - wxPanel* m_panelLogfile; - wxButton* m_buttonSelectLogFolder; wxHyperlinkCtrl* m_hyperlink17; wxStaticLine* m_staticline13; wxBoxSizer* bSizerStdButtons; @@ -859,8 +862,6 @@ protected: virtual void OnToggleIgnoreErrors( wxCommandEvent& event ) { event.Skip(); } virtual void OnErrorDialogShow( wxCommandEvent& event ) { event.Skip(); } virtual void OnErrorDialogCancel( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToggleGenerateLogfile( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToggleLogfilesLimit( wxCommandEvent& event ) { event.Skip(); } virtual void OnHelpScheduleBatch( wxHyperlinkEvent& event ) { event.Skip(); } virtual void OnSaveBatchJob( wxCommandEvent& event ) { event.Skip(); } virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } @@ -869,8 +870,6 @@ protected: public: wxCheckBox* m_checkBoxAutoClose; wxChoice* m_choicePostSyncAction; - fff::FolderHistoryBox* m_logFolderPath; - wxBitmapButton* m_bpButtonSelectAltLogFolder; BatchDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Save as a Batch Job"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); ~BatchDlgGenerated(); @@ -979,6 +978,7 @@ protected: zen::BitmapTextButton* m_buttonResetDialogs; wxStaticLine* m_staticline191; wxStaticBitmap* m_bitmapLogFile; + wxStaticText* m_staticText163; wxHyperlinkCtrl* m_hyperlinkLogFolder; wxCheckBox* m_checkBoxLogFilesMaxAge; wxSpinCtrl* m_spinCtrlLogFilesMaxAge; diff --git a/FreeFileSync/Source/ui/gui_status_handler.cpp b/FreeFileSync/Source/ui/gui_status_handler.cpp index 5abf3931..53391ea3 100755 --- a/FreeFileSync/Source/ui/gui_status_handler.cpp +++ b/FreeFileSync/Source/ui/gui_status_handler.cpp @@ -25,7 +25,7 @@ StatusHandlerTemporaryPanel::StatusHandlerTemporaryPanel(MainDialog& dlg, const std::chrono::system_clock::time_point& startTime, bool ignoreErrors, size_t automaticRetryCount, - size_t automaticRetryDelay) : + std::chrono::seconds automaticRetryDelay) : mainDlg_(dlg), automaticRetryCount_(automaticRetryCount), automaticRetryDelay_(automaticRetryDelay), @@ -314,7 +314,7 @@ StatusHandlerFloatingDialog::StatusHandlerFloatingDialog(wxFrame* parentDlg, const std::chrono::system_clock::time_point& startTime, bool ignoreErrors, size_t automaticRetryCount, - size_t automaticRetryDelay, + std::chrono::seconds automaticRetryDelay, const std::wstring& jobName, const Zstring& soundFileSyncComplete, const Zstring& postSyncCommand, @@ -338,7 +338,7 @@ StatusHandlerFloatingDialog::~StatusHandlerFloatingDialog() } -StatusHandlerFloatingDialog::Result StatusHandlerFloatingDialog::reportFinalStatus(int logfilesMaxAgeDays, const std::set<Zstring, LessFilePath>& logFilePathsToKeep) +StatusHandlerFloatingDialog::Result StatusHandlerFloatingDialog::reportFinalStatus(const Zstring& altLogFolderPathPhrase, int logfilesMaxAgeDays, const std::set<AbstractPath>& logFilePathsToKeep) { const auto totalTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - startTime_); @@ -402,12 +402,12 @@ StatusHandlerFloatingDialog::Result StatusHandlerFloatingDialog::reportFinalStat errorLog_.logMsg(replaceCpy(_("Executing command %x"), L"%x", fmtPath(commandLine)), MSG_TYPE_INFO); //----------------- always save log under %appdata%\FreeFileSync\Logs ------------------------ - Zstring logFilePath; + AbstractPath logFilePath = getNullPath(); try { //do NOT use tryReportingError()! saving log files should not be cancellable! auto notifyStatusNoThrow = [&](const std::wstring& msg) { try { reportStatus(msg); /*throw X*/ } catch (...) {} }; - logFilePath = saveLogFile(summary, errorLog_, startTime_, logfilesMaxAgeDays, logFilePathsToKeep, notifyStatusNoThrow /*throw (X)*/); //throw FileError + logFilePath = saveLogFile(summary, errorLog_, startTime_, altLogFolderPathPhrase, logfilesMaxAgeDays, logFilePathsToKeep, notifyStatusNoThrow /*throw (X)*/); //throw FileError } catch (const FileError& e) { errorLog_.logMsg(e.toString(), MSG_TYPE_ERROR); } @@ -416,7 +416,7 @@ StatusHandlerFloatingDialog::Result StatusHandlerFloatingDialog::reportFinalStat try { //---------------------------------------------------------------------- - ::wxSetEnv(L"logfile_path", utfTo<wxString>(logFilePath)); + ::wxSetEnv(L"logfile_path", AFS::getDisplayPath(logFilePath)); //---------------------------------------------------------------------- //use ExecutionType::ASYNC until there is reason not to: https://freefilesync.org/forum/viewtopic.php?t=31 shellExecute(expandMacros(commandLine), ExecutionType::ASYNC); //throw FileError @@ -440,7 +440,7 @@ StatusHandlerFloatingDialog::Result StatusHandlerFloatingDialog::reportFinalStat if (progressDlg_->getWindowIfVisible()) try { - delayAndCountDown(operationName, 5 /*delayInSec*/, notifyStatusThrowOnCancel); //throw X + delayAndCountDown(operationName, std::chrono::seconds(5), notifyStatusThrowOnCancel); //throw X } catch (...) { return false; } diff --git a/FreeFileSync/Source/ui/gui_status_handler.h b/FreeFileSync/Source/ui/gui_status_handler.h index 73942f0d..768d4d2e 100755 --- a/FreeFileSync/Source/ui/gui_status_handler.h +++ b/FreeFileSync/Source/ui/gui_status_handler.h @@ -22,7 +22,7 @@ namespace fff class StatusHandlerTemporaryPanel : private wxEvtHandler, public StatusHandler { public: - StatusHandlerTemporaryPanel(MainDialog& dlg, const std::chrono::system_clock::time_point& startTime, bool ignoreErrors, size_t automaticRetryCount, size_t automaticRetryDelay); + StatusHandlerTemporaryPanel(MainDialog& dlg, const std::chrono::system_clock::time_point& startTime, bool ignoreErrors, size_t automaticRetryCount, std::chrono::seconds automaticRetryDelay); ~StatusHandlerTemporaryPanel(); void initNewPhase (int itemsTotal, int64_t bytesTotal, Phase phaseID) override; // @@ -47,7 +47,7 @@ private: MainDialog& mainDlg_; zen::ErrorLog errorLog_; const size_t automaticRetryCount_; - const size_t automaticRetryDelay_; + const std::chrono::seconds automaticRetryDelay_; const std::chrono::system_clock::time_point startTime_; }; @@ -60,7 +60,7 @@ public: const std::chrono::system_clock::time_point& startTime, bool ignoreErrors, size_t automaticRetryCount, - size_t automaticRetryDelay, + std::chrono::seconds automaticRetryDelay, const std::wstring& jobName, const Zstring& soundFileSyncComplete, const Zstring& postSyncCommand, @@ -82,9 +82,9 @@ public: ProcessSummary summary; std::shared_ptr<const zen::ErrorLog> errorLog; bool exitAfterSync; - Zstring logFilePath; + AbstractPath logFilePath; }; - Result reportFinalStatus(int logfilesMaxAgeDays, const std::set<Zstring, LessFilePath>& logFilePathsToKeep); //noexcept!! + Result reportFinalStatus(const Zstring& altLogFolderPathPhrase, int logfilesMaxAgeDays, const std::set<AbstractPath>& logFilePathsToKeep); //noexcept!! private: void onProgressDialogTerminate(); @@ -92,7 +92,7 @@ private: SyncProgressDialog* progressDlg_; //managed to have shorter lifetime than this handler! zen::ErrorLog errorLog_; const size_t automaticRetryCount_; - const size_t automaticRetryDelay_; + const std::chrono::seconds automaticRetryDelay_; const std::wstring jobName_; const std::chrono::system_clock::time_point startTime_; const Zstring postSyncCommand_; diff --git a/FreeFileSync/Source/ui/log_panel.cpp b/FreeFileSync/Source/ui/log_panel.cpp index 545bc594..7f84a9ed 100755 --- a/FreeFileSync/Source/ui/log_panel.cpp +++ b/FreeFileSync/Source/ui/log_panel.cpp @@ -65,7 +65,7 @@ public: bool firstLine = false; //if LogEntry::message spans multiple rows }; - Opt<LogEntryView> getEntry(size_t row) const + std::optional<LogEntryView> getEntry(size_t row) const { if (row < viewRef_.size()) { @@ -78,7 +78,7 @@ public: output.firstLine = line.rowNumber_ == 0; //this is virtually always correct, unless first line of the original message is empty! return output; } - return NoValue(); + return {}; } void updateView(int includedTypes) //MSG_TYPE_INFO | MSG_TYPE_WARNING, ect. see error_log.h @@ -160,7 +160,7 @@ public: std::wstring getValue(size_t row, ColumnType colType) const override { - if (Opt<MessageView::LogEntryView> entry = msgView_.getEntry(row)) + if (std::optional<MessageView::LogEntryView> entry = msgView_.getEntry(row)) switch (static_cast<ColumnTypeMsg>(colType)) { case ColumnTypeMsg::TIME: @@ -198,7 +198,7 @@ public: wxDCPenChanger dummy2(dc, getColorGridLine()); const bool drawBottomLine = [&] //don't separate multi-line messages { - if (Opt<MessageView::LogEntryView> nextEntry = msgView_.getEntry(row + 1)) + if (std::optional<MessageView::LogEntryView> nextEntry = msgView_.getEntry(row + 1)) return nextEntry->firstLine; return true; }(); @@ -211,7 +211,7 @@ public: } //-------------------------------------------------------- - if (Opt<MessageView::LogEntryView> entry = msgView_.getEntry(row)) + if (std::optional<MessageView::LogEntryView> entry = msgView_.getEntry(row)) switch (static_cast<ColumnTypeMsg>(colType)) { case ColumnTypeMsg::TIME: diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp index 5a78c05e..5d608955 100755 --- a/FreeFileSync/Source/ui/main_dlg.cpp +++ b/FreeFileSync/Source/ui/main_dlg.cpp @@ -23,8 +23,8 @@ #include <wx+/no_flicker.h> #include <wx+/rtl.h> #include <wx+/font_size.h> -#include <wx+/focus.h> #include <wx+/popup_dlg.h> +#include <wx+/focus.h> #include <wx+/image_resources.h> #include "cfg_grid.h" #include "version_check.h" @@ -287,12 +287,12 @@ void MainDialog::create(const Zstring& globalConfigFilePath) GetFirstResult<std::false_type> firstUnavailableFile; for (const Zstring& filePath : cfgFilePaths) - firstUnavailableFile.addJob([filePath]() -> Opt<std::false_type> + firstUnavailableFile.addJob([filePath]() -> std::optional<std::false_type> { assert(!filePath.empty()); if (!fileAvailable(filePath)) return std::false_type(); - return NoValue(); + return {}; }); //potentially slow network access: give all checks 500ms to finish @@ -401,7 +401,7 @@ MainDialog::MainDialog(const Zstring& globalConfigFilePath, m_bpButtonAddPair ->SetBitmapLabel(getResourceImage(L"item_add")); m_bpButtonHideSearch ->SetBitmapLabel(getResourceImage(L"close_panel")); - m_bpButtonShowLog ->SetBitmapLabel(getResourceImage(L"log_file_small")); + m_bpButtonShowLog ->SetBitmapLabel(getResourceImage(L"log_file")); m_textCtrlSearchTxt->SetMinSize(wxSize(fastFromDIP(220), -1)); @@ -763,12 +763,12 @@ MainDialog::MainDialog(const Zstring& globalConfigFilePath, //check existence of all directories in parallel! GetFirstResult<std::false_type> firstMissingDir; for (const AbstractPath& folderPath : folderPathsToCheck) - firstMissingDir.addJob([folderPath]() -> Opt<std::false_type> + firstMissingDir.addJob([folderPath]() -> std::optional<std::false_type> { try { if (AFS::getItemType(folderPath) != AFS::ItemType::FILE) //throw FileError - return NoValue(); + return {}; } catch (FileError&) {} return std::false_type(); @@ -791,7 +791,7 @@ MainDialog::MainDialog(const Zstring& globalConfigFilePath, MainDialog::~MainDialog() { - Opt<FileError> firstError; + std::optional<FileError> firstError; try //save "GlobalSettings.xml" { writeConfig(getGlobalCfgBeforeExit(), globalConfigFilePath_); //throw FileError @@ -1095,17 +1095,15 @@ void MainDialog::copySelectionToClipboard(const std::vector<const Grid*>& gridRe //perf: wxString doesn't model exponential growth and is unsuitable for large data sets Zstringw clipboardString; - auto addSelection = [&](const Grid& grid) - { - if (auto prov = grid.getDataProvider()) + for (const Grid* grid : gridRefs) + if (auto prov = grid->getDataProvider()) { - std::vector<Grid::ColAttributes> colAttr = grid.getColumnConfig(); + std::vector<Grid::ColAttributes> colAttr = grid->getColumnConfig(); erase_if(colAttr, [](const Grid::ColAttributes& ca) { return !ca.visible; }); if (!colAttr.empty()) - for (size_t row : grid.getSelectedRows()) + for (size_t row : grid->getSelectedRows()) { - std::for_each(colAttr.begin(), colAttr.end() - 1, - [&](const Grid::ColAttributes& ca) + std::for_each(colAttr.begin(), colAttr.end() - 1, [&](const Grid::ColAttributes& ca) { clipboardString += copyStringTo<Zstringw>(prov->getValue(row, ca.type)); clipboardString += L'\t'; @@ -1114,12 +1112,7 @@ void MainDialog::copySelectionToClipboard(const std::vector<const Grid*>& gridRe clipboardString += L'\n'; } } - }; - for (const Grid* gr : gridRefs) - addSelection(*gr); - - //finally write to clipboard if (wxClipboard::Get()->Open()) { ZEN_ON_SCOPE_EXIT(wxClipboard::Get()->Close()); @@ -1160,18 +1153,12 @@ std::vector<FileSystemObject*> MainDialog::getTreeSelection() const if (auto root = dynamic_cast<const TreeView::RootNode*>(node.get())) { //selecting root means "select everything", *ignoring* current view filter! - BaseFolderPair& baseDir = root->baseFolder; - - std::vector<FileSystemObject*> dirsFilesAndLinks; - - for (FileSystemObject& fsObj : baseDir.refSubFolders()) //no need to explicitly add child elements! - dirsFilesAndLinks.push_back(&fsObj); - for (FileSystemObject& fsObj : baseDir.refSubFiles()) - dirsFilesAndLinks.push_back(&fsObj); - for (FileSystemObject& fsObj : baseDir.refSubLinks()) - dirsFilesAndLinks.push_back(&fsObj); - - append(output, dirsFilesAndLinks); + for (FileSystemObject& fsObj : root->baseFolder.refSubFolders()) //no need to explicitly add child elements! + output.push_back(&fsObj); + for (FileSystemObject& fsObj : root->baseFolder.refSubFiles()) + output.push_back(&fsObj); + for (FileSystemObject& fsObj : root->baseFolder.refSubLinks()) + output.push_back(&fsObj); } else if (auto dir = dynamic_cast<const TreeView::DirNode*>(node.get())) output.push_back(&(dir->folder)); @@ -1200,7 +1187,7 @@ void MainDialog::copyToAlternateFolder(const std::vector<FileSystemObject*>& sel rowsLeftTmp, rowsRightTmp, globalCfg_.gui.mainDlg.copyToCfg.lastUsedPath, globalCfg_.gui.mainDlg.copyToCfg.folderHistory, - globalCfg_.gui.mainDlg.copyToCfg.historySizeMax, + globalCfg_.gui.mainDlg.folderHistItemsMax, globalCfg_.gui.mainDlg.copyToCfg.keepRelPaths, globalCfg_.gui.mainDlg.copyToCfg.overwriteIfExists) != ReturnSmallDlg::BUTTON_OKAY) return; @@ -3370,6 +3357,7 @@ void MainDialog::showConfigDialog(SyncConfigPanel panelToShow, int localPairInde globalPairCfg.miscCfg.ignoreErrors = currentCfg_.mainCfg.ignoreErrors; globalPairCfg.miscCfg.automaticRetryCount = currentCfg_.mainCfg.automaticRetryCount; globalPairCfg.miscCfg.automaticRetryDelay = currentCfg_.mainCfg.automaticRetryDelay; + globalPairCfg.miscCfg.altLogFolderPathPhrase = currentCfg_.mainCfg.altLogFolderPathPhrase; globalPairCfg.miscCfg.postSyncCommand = currentCfg_.mainCfg.postSyncCommand; globalPairCfg.miscCfg.postSyncCondition = currentCfg_.mainCfg.postSyncCondition; globalPairCfg.miscCfg.commandHistory = globalCfg_.gui.commandHistory; @@ -3412,6 +3400,7 @@ void MainDialog::showConfigDialog(SyncConfigPanel panelToShow, int localPairInde currentCfg_.mainCfg.ignoreErrors = globalPairCfg.miscCfg.ignoreErrors; currentCfg_.mainCfg.automaticRetryCount = globalPairCfg.miscCfg.automaticRetryCount; currentCfg_.mainCfg.automaticRetryDelay = globalPairCfg.miscCfg.automaticRetryDelay; + currentCfg_.mainCfg.altLogFolderPathPhrase = globalPairCfg.miscCfg.altLogFolderPathPhrase; currentCfg_.mainCfg.postSyncCommand = globalPairCfg.miscCfg.postSyncCommand; currentCfg_.mainCfg.postSyncCondition = globalPairCfg.miscCfg.postSyncCondition; globalCfg_.gui.commandHistory = globalPairCfg.miscCfg.commandHistory; @@ -3451,6 +3440,7 @@ void MainDialog::showConfigDialog(SyncConfigPanel panelToShow, int localPairInde globalPairCfg.miscCfg.ignoreErrors != globalPairCfgOld.miscCfg.ignoreErrors || globalPairCfg.miscCfg.automaticRetryCount != globalPairCfgOld.miscCfg.automaticRetryCount || globalPairCfg.miscCfg.automaticRetryDelay != globalPairCfgOld.miscCfg.automaticRetryDelay || + globalPairCfg.miscCfg.altLogFolderPathPhrase != globalPairCfgOld.miscCfg.altLogFolderPathPhrase || globalPairCfg.miscCfg.postSyncCommand != globalPairCfgOld.miscCfg.postSyncCommand || globalPairCfg.miscCfg.postSyncCondition != globalPairCfgOld.miscCfg.postSyncCondition; /**/ //globalPairCfg.miscCfg.commandHistory != globalPairCfgOld.miscCfg.commandHistory; @@ -3744,7 +3734,8 @@ void MainDialog::OnCompare(wxCommandEvent& event) folderHistoryLeft_ ->addItem(utfTo<Zstring>(m_folderPathLeft ->GetValue())); folderHistoryRight_->addItem(utfTo<Zstring>(m_folderPathRight->GetValue())); - if (fp.getFocus() == m_buttonCompare) + assert(m_buttonCompare->GetId() != wxID_ANY); + if (fp.getFocusId() == m_buttonCompare->GetId()) fp.setFocus(m_buttonSync); //prepare status information @@ -3754,7 +3745,7 @@ void MainDialog::OnCompare(wxCommandEvent& event) //update last sync date for selected cfg files https://freefilesync.org/forum/viewtopic.php?t=4991 if (r.summary.finalStatus == SyncResult::FINISHED_WITH_SUCCESS) - updateConfigLastRunStats(std::chrono::system_clock::to_time_t(startTime), r.summary.finalStatus, Zstring() /*logFilePath*/); + updateConfigLastRunStats(std::chrono::system_clock::to_time_t(startTime), r.summary.finalStatus, getNullPath() /*logFilePath*/); } } @@ -3880,8 +3871,8 @@ void MainDialog::OnStartSync(wxCommandEvent& event) } const std::map<AbstractPath, size_t>& deviceParallelOps = guiCfg.mainCfg.deviceParallelOps; - - std::set<Zstring, LessFilePath> logFilePathsToKeep; + + std::set<AbstractPath> logFilePathsToKeep; for (const ConfigFileItem& item : cfggrid::getDataView(*m_gridCfgHistory).get()) logFilePathsToKeep.insert(item.logFilePath); @@ -3923,11 +3914,11 @@ void MainDialog::OnStartSync(wxCommandEvent& event) for (auto it = begin(folderCmp_); it != end(folderCmp_); ++it) { if (it->isAvailable<LEFT_SIDE>()) //do NOT check directory existence again! - if (Opt<Zstring> nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath<LEFT_SIDE>())) //restrict directory locking to native paths until further + if (std::optional<Zstring> nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath<LEFT_SIDE>())) //restrict directory locking to native paths until further availableDirPaths.insert(*nativeFolderPath); if (it->isAvailable<RIGHT_SIDE>()) - if (Opt<Zstring> nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath<RIGHT_SIDE>())) + if (std::optional<Zstring> nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath<RIGHT_SIDE>())) availableDirPaths.insert(*nativeFolderPath); } dirLocks = std::make_unique<LockHolder>(availableDirPaths, globalCfg_.warnDlgs.warnDirectoryLockFailed, statusHandler); //throw AbortProcess @@ -3949,7 +3940,7 @@ void MainDialog::OnStartSync(wxCommandEvent& event) } catch (AbortProcess&) {} - StatusHandlerFloatingDialog::Result r = statusHandler.reportFinalStatus(globalCfg_.logfilesMaxAgeDays, logFilePathsToKeep); //noexcept + StatusHandlerFloatingDialog::Result r = statusHandler.reportFinalStatus(guiCfg.mainCfg.altLogFolderPathPhrase, globalCfg_.logfilesMaxAgeDays, logFilePathsToKeep); //noexcept //--------------------------------------------------------------------------- setLastOperationLog(r.summary, r.errorLog); @@ -3970,7 +3961,7 @@ void MainDialog::OnStartSync(wxCommandEvent& event) } -void MainDialog::updateConfigLastRunStats(time_t lastRunTime, SyncResult result, const Zstring& logFilePath) +void MainDialog::updateConfigLastRunStats(time_t lastRunTime, SyncResult result, const AbstractPath& logFilePath) { cfggrid::getDataView(*m_gridCfgHistory).setLastRunStats(activeConfigFiles_, { lastRunTime, result, logFilePath }); @@ -4040,7 +4031,7 @@ void MainDialog::setLastOperationLog(const ProcessSummary& summary, const std::s logPanel_->setLog(errorLog); m_panelLog->Layout(); - setImage(*m_bpButtonShowLog, layOver(getResourceImage(L"log_file_small"), statusOverlayImage, wxALIGN_BOTTOM | wxALIGN_RIGHT)); + setImage(*m_bpButtonShowLog, layOver(getResourceImage(L"log_file"), statusOverlayImage, wxALIGN_BOTTOM | wxALIGN_RIGHT)); m_bpButtonShowLog->Show(static_cast<bool>(errorLog)); } @@ -4243,7 +4234,7 @@ void MainDialog::updateGridViewData() if (m_bpButtonViewTypeSyncAction->isActive()) { - const FileView::StatusSyncPreview result = filegrid::getDataView(*m_gridMainC).updateSyncPreview(m_bpButtonShowExcluded ->isActive(), + const FileView::StatusSyncPreview result = filegrid::getDataView(*m_gridMainC).updateSyncPreview(m_bpButtonShowExcluded->isActive(), m_bpButtonShowCreateLeft ->isActive(), m_bpButtonShowCreateRight->isActive(), m_bpButtonShowDeleteLeft ->isActive(), @@ -4281,7 +4272,7 @@ void MainDialog::updateGridViewData() } else { - const FileView::StatusCmpResult result = filegrid::getDataView(*m_gridMainC).updateCmpResult(m_bpButtonShowExcluded ->isActive(), + const FileView::StatusCmpResult result = filegrid::getDataView(*m_gridMainC).updateCmpResult(m_bpButtonShowExcluded->isActive(), m_bpButtonShowLeftOnly ->isActive(), m_bpButtonShowRightOnly ->isActive(), m_bpButtonShowLeftNewer ->isActive(), @@ -4441,10 +4432,11 @@ void MainDialog::showFindPanel() //CTRL + F or F3 with empty search phrase m_textCtrlSearchTxt->SelectAll(); - wxWindow* focus = wxWindow::FindFocus(); //restore when closing panel! - if (!isComponentOf(focus, m_panelSearch)) - focusWindowAfterSearch_ = focus == &m_gridMainR->getMainWin() ? focus : &m_gridMainL->getMainWin(); - //don't save pointer to arbitrary window: it might not exist anymore when hideFindPanel() uses it!!! (e.g. some folder pair panel) + if (wxWindow* focus = wxWindow::FindFocus()) //restore when closing panel! + if (!isComponentOf(focus, m_panelSearch)) + focusIdAfterSearch_ = focus->GetId(); + //don't save wxWindow* to arbitrary window: it might not exist anymore when hideFindPanel() uses it!!! (e.g. some folder pair panel) + m_textCtrlSearchTxt->SetFocus(); } @@ -4454,11 +4446,9 @@ void MainDialog::hideFindPanel() auiMgr_.GetPane(m_panelSearch).Hide(); auiMgr_.Update(); - if (focusWindowAfterSearch_) - { - focusWindowAfterSearch_->SetFocus(); - focusWindowAfterSearch_ = nullptr; - } + if (wxWindow* oldFocusWin = wxWindow::FindWindowById(focusIdAfterSearch_)) + oldFocusWin->SetFocus(); + focusIdAfterSearch_ = wxID_ANY; } @@ -4475,7 +4465,7 @@ void MainDialog::startFindNext(bool searchAscending) //F3 or ENTER in m_textCtrl Grid* grid2 = m_gridMainR; wxWindow* focus = wxWindow::FindFocus(); - if ((isComponentOf(focus, m_panelSearch) ? focusWindowAfterSearch_ : focus) == &m_gridMainR->getMainWin()) + if ((isComponentOf(focus, m_panelSearch) ? focusIdAfterSearch_ : focus->GetId()) == m_gridMainR->getMainWin().GetId()) std::swap(grid1, grid2); //select side to start search at grid cursor position wxBeginBusyCursor(wxHOURGLASS_CURSOR); @@ -4490,7 +4480,7 @@ void MainDialog::startFindNext(bool searchAscending) //F3 or ENTER in m_textCtrl filegrid::setScrollMaster(*grid); grid->setGridCursor(result.second, GridEventPolicy::ALLOW); - focusWindowAfterSearch_ = &grid->getMainWin(); + focusIdAfterSearch_ = grid->getMainWin().GetId(); if (!isComponentOf(wxWindow::FindFocus(), m_panelSearch)) grid->getMainWin().SetFocus(); @@ -4724,9 +4714,10 @@ void MainDialog::recalcMaxFolderPairsVisible() 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) + //assert(firstPairHeight > 0 && addPairHeight > 0); -> wxWindows::GetSize() returns 0 if main window is minimized during sync! Test with "When finished: Exit" + + if (addPairCountLast_ && firstPairHeight > 0 && addPairHeight > 0) { const double addPairCountCurrent = (m_panelDirectoryPairs->GetSize().y - firstPairHeight) / (1.0 * addPairHeight); //include m_panelDirectoryPairs window borders! diff --git a/FreeFileSync/Source/ui/main_dlg.h b/FreeFileSync/Source/ui/main_dlg.h index a91a100c..70a5fdb7 100755 --- a/FreeFileSync/Source/ui/main_dlg.h +++ b/FreeFileSync/Source/ui/main_dlg.h @@ -226,7 +226,7 @@ private: void showConfigDialog(SyncConfigPanel panelToShow, int localPairIndexToShow); - void updateConfigLastRunStats(time_t lastRunTime, SyncResult result, const Zstring& logFilePath); + void updateConfigLastRunStats(time_t lastRunTime, SyncResult result, const AbstractPath& logFilePath); void setLastOperationLog(const ProcessSummary& summary, const std::shared_ptr<const zen::ErrorLog>& errorLog); void showLogPanel(bool show); @@ -312,7 +312,7 @@ private: std::unique_ptr<FolderPairFirst> firstFolderPair_; //always bound!!! std::vector<FolderPairPanel*> additionalFolderPairs_; //additional pairs to the first pair - zen::Opt<double> addPairCountLast_; + std::optional<double> addPairCountLast_; //------------------------------------- //*********************************************** @@ -343,7 +343,7 @@ private: std::unique_ptr<FilterConfig> filterCfgOnClipboard_; //copy/paste of filter config - wxWindow* focusWindowAfterSearch_ = nullptr; //used to restore focus after search panel is closed + wxWindowID focusIdAfterSearch_ = wxID_ANY; //used to restore focus after search panel is closed bool localKeyEventsEnabled_ = true; bool allowMainDialogClose_ = true; //e.g. do NOT allow close while sync is running => crash!!! diff --git a/FreeFileSync/Source/ui/progress_indicator.cpp b/FreeFileSync/Source/ui/progress_indicator.cpp index 7f9850b7..6737996c 100755 --- a/FreeFileSync/Source/ui/progress_indicator.cpp +++ b/FreeFileSync/Source/ui/progress_indicator.cpp @@ -9,28 +9,20 @@ #include <wx/imaglist.h> #include <wx/wupdlock.h> #include <wx/sound.h> -//#include <wx/dcclient.h> -//#include <wx/dataobj.h> //wxTextDataObject #include <wx/app.h> #include <zen/basic_math.h> #include <zen/format_unit.h> #include <zen/scope_guard.h> -//#include <wx+/grid.h> #include <wx+/toggle_button.h> #include <wx+/image_tools.h> #include <wx+/graph.h> -//#include <wx+/context_menu.h> #include <wx+/no_flicker.h> #include <wx+/font_size.h> #include <wx+/std_button_layout.h> -//#include <wx+/popup_dlg.h> -//#include <wx+/image_resources.h> #include <zen/file_access.h> #include <zen/thread.h> #include <zen/perf.h> -//#include <wx+/rtl.h> #include <wx+/choice_enum.h> -//#include <wx+/focus.h> #include "gui_generated.h" #include "../base/ffs_paths.h" #include "../base/perf_check.h" @@ -384,14 +376,14 @@ void CompareProgressDialog::Impl::updateProgressGui() perf_.addSample(timeElapsed, itemsCurrent, bytesCurrent); //current speed -> Win 7 copy uses 1 sec update interval instead - Opt<std::wstring> bps = perf_.getBytesPerSecond(); - Opt<std::wstring> ips = perf_.getItemsPerSecond(); + std::optional<std::wstring> bps = perf_.getBytesPerSecond(); + std::optional<std::wstring> ips = perf_.getItemsPerSecond(); m_panelProgressGraph->setAttributes(m_panelProgressGraph->getAttributes().setCornerText(bps ? *bps : L"", Graph2D::CORNER_TOP_LEFT)); m_panelProgressGraph->setAttributes(m_panelProgressGraph->getAttributes().setCornerText(ips ? *ips : L"", Graph2D::CORNER_BOTTOM_LEFT)); //remaining time: display with relative error of 10% - based on samples taken every 0.5 sec only //-> call more often than once per second to correctly show last few seconds countdown, but don't call too often to avoid occasional jitter - Opt<double> remTimeSec = perf_.getRemainingTimeSec(bytesTotal - bytesCurrent); + std::optional<double> remTimeSec = perf_.getRemainingTimeSec(bytesTotal - bytesCurrent); setText(*m_staticTextTimeRemaining, remTimeSec ? formatRemainingTime(*remTimeSec) : L"-", &layoutChanged); } @@ -480,7 +472,7 @@ private: std::chrono::duration<double>(upperEnd).count() }; } - Opt<CurvePoint> getLessEq(double x) const override //x: seconds since begin + std::optional<CurvePoint> getLessEq(double x) const override //x: seconds since begin { const auto timeX = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::duration<double>(x)); //round down @@ -493,13 +485,13 @@ private: //find first key > x, then go one step back: => samples must be a std::map, NOT std::multimap!!! auto it = samples_.upper_bound(timeX); if (it == samples_.begin()) - return NoValue(); + return {}; //=> samples not empty in this context --it; return CurvePoint(std::chrono::duration<double>(it->first).count(), it->second); } - Opt<CurvePoint> getGreaterEq(double x) const override + std::optional<CurvePoint> getGreaterEq(double x) const override { const std::chrono::nanoseconds timeX(static_cast<std::chrono::nanoseconds::rep>(std::ceil(x * (1000 * 1000 * 1000)))); //round up! @@ -511,7 +503,7 @@ private: auto it = samples_.lower_bound(timeX); if (it == samples_.end()) - return NoValue(); + return {}; return CurvePoint(std::chrono::duration<double>(it->first).count(), it->second); } @@ -1134,8 +1126,8 @@ void SyncProgressDialogImpl<TopLevelDialog>::updateProgressGui(bool allowYield) perf_.addSample(timeElapsed, itemsCurrent, bytesCurrent); //current speed -> Win 7 copy uses 1 sec update interval instead - Opt<std::wstring> bps = perf_.getBytesPerSecond(); - Opt<std::wstring> ips = perf_.getItemsPerSecond(); + std::optional<std::wstring> bps = perf_.getBytesPerSecond(); + std::optional<std::wstring> ips = perf_.getItemsPerSecond(); pnl_.m_panelGraphBytes->setAttributes(pnl_.m_panelGraphBytes->getAttributes().setCornerText(bps ? *bps : L"", Graph2D::CORNER_TOP_LEFT)); pnl_.m_panelGraphItems->setAttributes(pnl_.m_panelGraphItems->getAttributes().setCornerText(ips ? *ips : L"", Graph2D::CORNER_TOP_LEFT)); @@ -1149,7 +1141,7 @@ void SyncProgressDialogImpl<TopLevelDialog>::updateProgressGui(bool allowYield) { //remaining time: display with relative error of 10% - based on samples taken every 0.5 sec only //-> call more often than once per second to correctly show last few seconds countdown, but don't call too often to avoid occasional jitter - Opt<double> remTimeSec = perf_.getRemainingTimeSec(bytesTotal - bytesCurrent); + std::optional<double> remTimeSec = perf_.getRemainingTimeSec(bytesTotal - bytesCurrent); setText(*pnl_.m_staticTextTimeRemaining, remTimeSec ? formatRemainingTime(*remTimeSec) : L"-", &layoutChanged); //update estimated total time marker with precision of "10% remaining time" only to avoid needless jumping around: diff --git a/FreeFileSync/Source/ui/small_dlgs.cpp b/FreeFileSync/Source/ui/small_dlgs.cpp index 630429c0..08eefc02 100755 --- a/FreeFileSync/Source/ui/small_dlgs.cpp +++ b/FreeFileSync/Source/ui/small_dlgs.cpp @@ -584,7 +584,7 @@ OptionsDlg::OptionsDlg(wxWindow* parent, XmlGlobalSettings& globalSettings) : //setMainInstructionFont(*m_staticTextHeader); m_gridCustomCommand->SetTabBehaviour(wxGrid::Tab_Leave); - m_bitmapLogFile->SetBitmap(getResourceImage(L"log_file_small")); + m_bitmapLogFile->SetBitmap(getResourceImage(L"log_file_sicon")); m_spinCtrlLogFilesMaxAge->SetMinSize(wxSize(fastFromDIP(70), -1)); //Hack: set size (why does wxWindow::Size() not work?) m_hyperlinkLogFolder->SetLabel(utfTo<wxString>(getDefaultLogFolderPath())); setRelativeFontSize(*m_hyperlinkLogFolder, 1.2); @@ -686,6 +686,10 @@ void OptionsDlg::OnDefault(wxCommandEvent& event) m_checkBoxCopyPermissions->SetValue(defaultCfg_.copyFilePermissions); setExtApp(defaultCfg_.gui.externalApps); + + m_checkBoxLogFilesMaxAge->SetValue(defaultCfg_.logfilesMaxAgeDays > 0); + m_spinCtrlLogFilesMaxAge->SetValue(defaultCfg_.logfilesMaxAgeDays > 0 ? defaultCfg_.logfilesMaxAgeDays : 14); + updateGui(); } diff --git a/FreeFileSync/Source/ui/sync_cfg.cpp b/FreeFileSync/Source/ui/sync_cfg.cpp index 84750453..f5bb6399 100755 --- a/FreeFileSync/Source/ui/sync_cfg.cpp +++ b/FreeFileSync/Source/ui/sync_cfg.cpp @@ -22,6 +22,7 @@ #include "../base/file_hierarchy.h" #include "../base/help_provider.h" #include "../base/norm_filter.h" +#include "../base/generate_logfile.h" #include "../fs/concrete.h" @@ -70,6 +71,9 @@ private: void OnHelpPerformance (wxHyperlinkEvent& event) override { displayHelpEntry(L"performance", this); } void OnToggleLocalCompSettings(wxCommandEvent& event) override { updateCompGui(); updateSyncGui(); /*affects sync settings, too!*/ } + void OnToggleIgnoreErrors (wxCommandEvent& event) override { updateMiscGui(); } + void OnToggleAutoRetry (wxCommandEvent& event) override { updateMiscGui(); } + void OnCompByTimeSize (wxCommandEvent& event) override { localCmpVar_ = CompareVariant::TIME_SIZE; updateCompGui(); updateSyncGui(); } // void OnCompByContent (wxCommandEvent& event) override { localCmpVar_ = CompareVariant::CONTENT; updateCompGui(); updateSyncGui(); } //affects sync settings, too! void OnCompBySize (wxCommandEvent& event) override { localCmpVar_ = CompareVariant::SIZE; updateCompGui(); updateSyncGui(); } // @@ -79,13 +83,16 @@ private: void OnChangeCompOption (wxCommandEvent& event) override { updateCompGui(); } void onlTimeShiftKeyDown (wxKeyEvent& event) override; - Opt<CompConfig> getCompConfig() const; + std::optional<CompConfig> getCompConfig() const; void setCompConfig(const CompConfig* compCfg); void updateCompGui(); CompareVariant localCmpVar_ = CompareVariant::TIME_SIZE; + std::set<AbstractPath> devicePathsForEdit_; //helper data for deviceParallelOps + std::map<AbstractPath, size_t> deviceParallelOps_; // + //------------- filter panel -------------------------- void OnHelpShowExamples(wxHyperlinkEvent& event) override { displayHelpEntry(L"exclude-items", this); } void OnChangeFilterOption(wxCommandEvent& event) override { updateFilterGui(); } @@ -111,6 +118,7 @@ private: void OnToggleDetectMovedFiles (wxCommandEvent& event) override { directionCfg_.detectMovedFiles = !directionCfg_.detectMovedFiles; updateSyncGui(); } //parameter NOT owned by checkbox! void OnChanegVersioningStyle (wxCommandEvent& event) override { updateSyncGui(); } void OnToggleVersioningLimit (wxCommandEvent& event) override { updateSyncGui(); } + void OnToggleSaveLogfile (wxCommandEvent& event) override { updateMiscGui(); } void OnSyncTwoWayDouble(wxMouseEvent& event) override; void OnSyncMirrorDouble(wxMouseEvent& event) override; @@ -131,33 +139,32 @@ private: void OnHelpDetectMovedFiles(wxHyperlinkEvent& event) override { displayHelpEntry(L"synchronization-settings", this); } void OnHelpVersioning (wxHyperlinkEvent& event) override { displayHelpEntry(L"versioning", this); } - Opt<SyncConfig> getSyncConfig() const; + std::optional<SyncConfig> getSyncConfig() const; void setSyncConfig(const SyncConfig* syncCfg); void updateSyncGui(); + //parameters with ownership NOT within GUI controls! + DirectionConfig directionCfg_; + DeletionPolicy handleDeletion_ = DeletionPolicy::RECYCLER; //use Recycler, delete permanently or move to user-defined location + + const std::function<size_t(const Zstring& folderPathPhrase)> getDeviceParallelOps_; + const std::function<void (const Zstring& folderPathPhrase, size_t parallelOps)> setDeviceParallelOps_; + + FolderSelector versioningFolder_; + EnumDescrList<VersioningStyle> enumVersioningStyle_; + + FolderSelector logfileDir_; + EnumDescrList<PostSyncCondition> enumPostSyncCondition_; //----------------------------------------------------- - void OnToggleIgnoreErrors(wxCommandEvent& event) override { updateMiscGui(); } - void OnToggleAutoRetry (wxCommandEvent& event) override { updateMiscGui(); } - 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 - - EnumDescrList<VersioningStyle> enumVersioningStyle_; - FolderSelector versioningFolder_; - //----------------------------------------------------- void selectFolderPairConfig(int newPairIndexToShow); @@ -223,17 +230,16 @@ ConfigDialog::ConfigDialog(wxWindow* parent, std::vector<LocalPairConfig>& localPairConfig, size_t commandHistItemsMax) : ConfigDlgGenerated(parent), - versioningFolder_(*m_panelVersioning, *m_buttonSelectVersioningFolder, *m_bpButtonSelectAltFolder, *m_versioningFolderPath, nullptr /*staticText*/, nullptr /*dropWindow2*/, - nullptr /*droppedPathsFilter*/, - [this](const Zstring& folderPathPhrase) //getDeviceParallelOps() + + getDeviceParallelOps_([this](const Zstring& folderPathPhrase) { assert(selectedPairIndexToShow_ == -1 || makeUnsigned(selectedPairIndexToShow_) < localPairCfg_.size()); const auto& deviceParallelOps = selectedPairIndexToShow_ < 0 ? getMiscSyncOptions().deviceParallelOps : globalPairCfg_.miscCfg.deviceParallelOps; //ternary-WTF! return getDeviceParallelOps(deviceParallelOps, folderPathPhrase); -}, +}), -[this](const Zstring& folderPathPhrase, size_t parallelOps) //setDeviceParallelOps() +setDeviceParallelOps_([this](const Zstring& folderPathPhrase, size_t parallelOps) //setDeviceParallelOps() { assert(selectedPairIndexToShow_ == -1 || makeUnsigned(selectedPairIndexToShow_) < localPairCfg_.size()); if (selectedPairIndexToShow_ < 0) @@ -245,6 +251,13 @@ ConfigDialog::ConfigDialog(wxWindow* parent, else setDeviceParallelOps(globalPairCfg_.miscCfg.deviceParallelOps, folderPathPhrase, parallelOps); }), + +versioningFolder_(*m_panelVersioning, *m_buttonSelectVersioningFolder, *m_bpButtonSelectVersioningAltFolder, *m_versioningFolderPath, + nullptr /*staticText*/, nullptr /*dropWindow2*/, nullptr /*droppedPathsFilter*/, getDeviceParallelOps_, setDeviceParallelOps_), + +logfileDir_(*m_panelLogfile, *m_buttonSelectLogFolder, *m_bpButtonSelectAltLogFolder, *m_logFolderPath, + nullptr /*staticText*/, nullptr /*dropWindow2*/, nullptr /*droppedPathsFilter*/, getDeviceParallelOps_, setDeviceParallelOps_), + globalPairCfgOut_(globalPairCfg), localPairCfgOut_(localPairConfig), globalPairCfg_(globalPairCfg), @@ -298,6 +311,9 @@ commandHistItemsMax_(commandHistItemsMax) m_panelPerfHeader ->Enable(perfPanelActive_); m_staticTextPerfParallelOps->Enable(perfPanelActive_); + m_spinCtrlAutoRetryCount->SetMinSize(wxSize(fastFromDIP(60), -1)); //Hack: set size (why does wxWindow::Size() not work?) + m_spinCtrlAutoRetryDelay->SetMinSize(wxSize(fastFromDIP(60), -1)); // + //------------- filter panel -------------------------- m_textCtrlInclude->SetMinSize(wxSize(fastFromDIP(280), -1)); @@ -360,9 +376,6 @@ commandHistItemsMax_(commandHistItemsMax) m_spinCtrlVersionCountMin->SetMinSize(wxSize(fastFromDIP(60), -1)); //Hack: set size (why does wxWindow::Size() not work?) m_spinCtrlVersionCountMax->SetMinSize(wxSize(fastFromDIP(60), -1)); // - m_spinCtrlAutoRetryCount->SetMinSize(wxSize(fastFromDIP(60), -1)); //Hack: set size (why does wxWindow::Size() not work?) - m_spinCtrlAutoRetryDelay->SetMinSize(wxSize(fastFromDIP(60), -1)); // - enumPostSyncCondition_. add(PostSyncCondition::COMPLETION, _("On completion:")). add(PostSyncCondition::ERRORS, _("On errors:")). @@ -398,8 +411,8 @@ commandHistItemsMax_(commandHistItemsMax) //temporarily set main config as reference for window height calculations: globalPairCfg_ = GlobalPairConfig(); globalPairCfg_.syncCfg.directionCfg.var = DirectionConfig::MIRROR; // - globalPairCfg_.syncCfg.handleDeletion = DeletionPolicy::VERSIONING; //set tentatively for sync dir height calculation below - globalPairCfg_.syncCfg.versioningFolderPhrase = Zstr("dummy"); // + globalPairCfg_.syncCfg.handleDeletion = DeletionPolicy::VERSIONING; // + globalPairCfg_.syncCfg.versioningFolderPhrase = Zstr("dummy"); //set tentatively for sync dir height calculation below globalPairCfg_.syncCfg.versioningStyle = VersioningStyle::TIMESTAMP_FILE; // globalPairCfg_.syncCfg.versionMaxAgeDays = 30; // @@ -552,10 +565,10 @@ void ConfigDialog::onlTimeShiftKeyDown(wxKeyEvent& event) } -Opt<CompConfig> ConfigDialog::getCompConfig() const +std::optional<CompConfig> ConfigDialog::getCompConfig() const { if (!m_checkBoxUseLocalCmpOptions->GetValue()) - return NoValue(); + return {}; CompConfig compCfg; compCfg.compareVar = localCmpVar_; @@ -931,10 +944,10 @@ void updateSyncDirectionIcons(const DirectionConfig& directionCfg, } -Opt<SyncConfig> ConfigDialog::getSyncConfig() const +std::optional<SyncConfig> ConfigDialog::getSyncConfig() const { if (!m_checkBoxUseLocalSyncOptions->GetValue()) - return NoValue(); + return {}; SyncConfig syncCfg; syncCfg.directionCfg = directionCfg_; @@ -1134,12 +1147,13 @@ void ConfigDialog::updateSyncGui() m_spinCtrlVersionCountMin->Show(showLimitCtrls); m_spinCtrlVersionCountMax->Show(showLimitCtrls); + m_staticTextLimitVersions->Enable(enableLimitCtrls); m_checkBoxVersionMaxDays ->Enable(enableLimitCtrls); m_checkBoxVersionCountMin->Enable(enableLimitCtrls && m_checkBoxVersionMaxDays->GetValue()); m_checkBoxVersionCountMax->Enable(enableLimitCtrls); m_spinCtrlVersionMaxDays ->Enable(enableLimitCtrls && m_checkBoxVersionMaxDays ->GetValue()); - m_spinCtrlVersionCountMin->Enable(enableLimitCtrls && m_checkBoxVersionCountMin->GetValue() && m_checkBoxVersionMaxDays->GetValue()); + m_spinCtrlVersionCountMin->Enable(enableLimitCtrls && m_checkBoxVersionMaxDays->GetValue() && m_checkBoxVersionCountMin->GetValue()); m_spinCtrlVersionCountMax->Enable(enableLimitCtrls && m_checkBoxVersionCountMax->GetValue()); } @@ -1168,11 +1182,15 @@ MiscSyncConfig ConfigDialog::getMiscSyncOptions() const //---------------------------------------------------------------------------- miscCfg.ignoreErrors = m_checkBoxIgnoreErrors ->GetValue(); miscCfg.automaticRetryCount = m_checkBoxAutoRetry ->GetValue() ? m_spinCtrlAutoRetryCount->GetValue() : 0; - miscCfg.automaticRetryDelay = m_spinCtrlAutoRetryDelay->GetValue(); + miscCfg.automaticRetryDelay = std::chrono::seconds(m_spinCtrlAutoRetryDelay->GetValue()); + //---------------------------------------------------------------------------- + miscCfg.altLogFolderPathPhrase = m_checkBoxSaveLog->GetValue() ? utfTo<Zstring>(logfileDir_.getPath()) : Zstring(); miscCfg.postSyncCommand = m_comboBoxPostSyncCommand->getValue(); miscCfg.postSyncCondition = getEnumVal(enumPostSyncCondition_, *m_choicePostSyncCondition), miscCfg.commandHistory = m_comboBoxPostSyncCommand->getHistory(); + //---------------------------------------------------------------------------- + return miscCfg; } @@ -1221,11 +1239,16 @@ void ConfigDialog::setMiscSyncOptions(const MiscSyncConfig& miscCfg) 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); + m_spinCtrlAutoRetryDelay->SetValue(miscCfg.automaticRetryDelay.count()); + //---------------------------------------------------------------------------- + m_checkBoxSaveLog->SetValue(!trimCpy(miscCfg.altLogFolderPathPhrase).empty()); + logfileDir_.setPath(m_checkBoxSaveLog->GetValue() ? miscCfg.altLogFolderPathPhrase : getDefaultLogFolderPath()); + //can't use logfileDir_.setBackgroundText(): no text shown when control is disabled! m_comboBoxPostSyncCommand->setValue(miscCfg.postSyncCommand); setEnumVal(enumPostSyncCondition_, *m_choicePostSyncCondition, miscCfg.postSyncCondition), m_comboBoxPostSyncCommand->setHistory(miscCfg.commandHistory, commandHistItemsMax_); + //---------------------------------------------------------------------------- updateMiscGui(); } @@ -1235,13 +1258,19 @@ 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"))); fgSizerAutoRetry->Show(miscCfg.automaticRetryCount > 0); - bSizerMiscConfig->Layout(); + bSizerCompMisc->Layout(); + //---------------------------------------------------------------------------- + + m_bitmapLogFile->SetBitmap(m_checkBoxSaveLog->GetValue() ? getResourceImage(L"log_file_sicon") : greyScale(getResourceImage(L"log_file_sicon"))); + m_logFolderPath ->Enable(m_checkBoxSaveLog->GetValue()); // + m_buttonSelectLogFolder ->Show(m_checkBoxSaveLog->GetValue()); //enabled status is *not* directly dependent from resolved config! (but transitively) + + m_panelSyncSettings->Layout(); //after showing/hiding m_buttonSelectLogFolder } @@ -1271,7 +1300,8 @@ void ConfigDialog::selectFolderPairConfig(int newPairIndexToShow) //misc bSizerPerformance ->Show(mainConfigSelected); //caveat: recursively shows hidden child items! m_staticlinePerformance->Show(mainConfigSelected); - bSizerMiscConfig ->Show(mainConfigSelected); + bSizerCompMisc ->Show(mainConfigSelected); + bSizerSyncMisc ->Show(mainConfigSelected); if (mainConfigSelected) m_staticTextPerfDeRequired->Show(!perfPanelActive_); //keep after bSizerPerformance->Show() if (mainConfigSelected) m_staticlinePerfDeRequired->Show(!perfPanelActive_); // @@ -1311,8 +1341,8 @@ void ConfigDialog::selectFolderPairConfig(int newPairIndexToShow) } else { - setCompConfig (localPairCfg_[selectedPairIndexToShow_].localCmpCfg .get()); - setSyncConfig (localPairCfg_[selectedPairIndexToShow_].localSyncCfg.get()); + setCompConfig (get(localPairCfg_[selectedPairIndexToShow_].localCmpCfg )); + setSyncConfig (get(localPairCfg_[selectedPairIndexToShow_].localSyncCfg)); setFilterConfig(localPairCfg_[selectedPairIndexToShow_].localFilter); } } @@ -1322,8 +1352,8 @@ bool ConfigDialog::unselectFolderPairConfig() { assert(selectedPairIndexToShow_ == -1 || makeUnsigned(selectedPairIndexToShow_) < localPairCfg_.size()); - Opt<CompConfig> compCfg = getCompConfig(); - Opt<SyncConfig> syncCfg = getSyncConfig(); + std::optional<CompConfig> compCfg = getCompConfig(); + std::optional<SyncConfig> syncCfg = getSyncConfig(); FilterConfig filterCfg = getFilterConfig(); //------- parameter validation (BEFORE writing output!) ------- @@ -1334,7 +1364,7 @@ bool ConfigDialog::unselectFolderPairConfig() if (syncCfg && syncCfg->handleDeletion == DeletionPolicy::VERSIONING) { - if (trimCpy(syncCfg->versioningFolderPhrase).empty()) + if (AFS::isNullPath(createAbstractPath(syncCfg->versioningFolderPhrase))) { m_notebook->ChangeSelection(static_cast<size_t>(SyncConfigPanel::SYNC)); showNotificationDialog(this, DialogInfoType::INFO, PopupDialogCfg().setMainInstructions(_("Please enter a target folder for versioning."))); diff --git a/FreeFileSync/Source/ui/sync_cfg.h b/FreeFileSync/Source/ui/sync_cfg.h index bf64fcb4..e660c3a0 100755 --- a/FreeFileSync/Source/ui/sync_cfg.h +++ b/FreeFileSync/Source/ui/sync_cfg.h @@ -24,9 +24,9 @@ struct ReturnSyncConfig enum class SyncConfigPanel { - COMPARISON = 0, // - FILTER = 1, //used as zero-based notebook page index! - SYNC = 2, // + COMPARISON = 0, //used as zero-based notebook page index! + FILTER, + SYNC }; struct MiscSyncConfig @@ -34,7 +34,8 @@ struct MiscSyncConfig std::map<AbstractPath, size_t> deviceParallelOps; bool ignoreErrors = false; size_t automaticRetryCount = 0; - size_t automaticRetryDelay = 0; + std::chrono::seconds automaticRetryDelay{0}; + Zstring altLogFolderPathPhrase; Zstring postSyncCommand; PostSyncCondition postSyncCondition = PostSyncCondition::COMPLETION; std::vector<Zstring> commandHistory; @@ -42,9 +43,9 @@ struct MiscSyncConfig struct GlobalPairConfig { - CompConfig cmpCfg; - SyncConfig syncCfg; - FilterConfig filter; + CompConfig cmpCfg; + SyncConfig syncCfg; + FilterConfig filter; MiscSyncConfig miscCfg; }; diff --git a/FreeFileSync/Source/ui/tree_grid.cpp b/FreeFileSync/Source/ui/tree_grid.cpp index 7383fbc1..268cb6e4 100755 --- a/FreeFileSync/Source/ui/tree_grid.cpp +++ b/FreeFileSync/Source/ui/tree_grid.cpp @@ -1193,7 +1193,7 @@ private: const int widthNodeStatus_; const wxBitmap rootBmp_; - Opt<wxBitmap> renderBuf_; //avoid costs of recreating this temporary variable + std::optional<wxBitmap> renderBuf_; //avoid costs of recreating this temporary variable Grid& grid_; bool showPercentBar_ = true; diff --git a/FreeFileSync/Source/ui/tree_grid.h b/FreeFileSync/Source/ui/tree_grid.h index 6bbfb446..027271af 100755 --- a/FreeFileSync/Source/ui/tree_grid.h +++ b/FreeFileSync/Source/ui/tree_grid.h @@ -8,7 +8,6 @@ #define TREE_VIEW_H_841703190201835280256673425 #include <functional> -#include <zen/optional.h> #include <wx+/grid.h> #include "tree_grid_attr.h" #include "../base/file_hierarchy.h" diff --git a/FreeFileSync/Source/ui/version_check.cpp b/FreeFileSync/Source/ui/version_check.cpp index 47863f06..7052dc23 100755 --- a/FreeFileSync/Source/ui/version_check.cpp +++ b/FreeFileSync/Source/ui/version_check.cpp @@ -245,10 +245,10 @@ std::shared_ptr<UpdateCheckResultPrep> fff::automaticUpdateCheckPrepare() struct fff::UpdateCheckResult { - UpdateCheckResult(const std::string& ver, const Opt<zen::SysError>& err, bool alive) : onlineVersion(ver), error(err), internetIsAlive(alive) {} + UpdateCheckResult(const std::string& ver, const std::optional<zen::SysError>& err, bool alive) : onlineVersion(ver), error(err), internetIsAlive(alive) {} std::string onlineVersion; - Opt<zen::SysError> error; + std::optional<zen::SysError> error; bool internetIsAlive = false; }; @@ -258,7 +258,7 @@ std::shared_ptr<UpdateCheckResult> fff::automaticUpdateCheckRunAsync(const Updat try { const std::string onlineVersion = getOnlineVersion(resultPrep->postParameters); //throw SysError - return std::make_shared<UpdateCheckResult>(onlineVersion, NoValue(), true); + return std::make_shared<UpdateCheckResult>(onlineVersion, std::nullopt, true); } catch (const zen::SysError& e) { diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h index 63deee3e..0c47fff0 100755 --- a/FreeFileSync/Source/version/version.h +++ b/FreeFileSync/Source/version/version.h @@ -3,7 +3,7 @@ namespace fff { -const char ffsVersion[] = "10.3"; //internal linkage! +const char ffsVersion[] = "10.4"; //internal linkage! const char FFS_VERSION_SEPARATOR = '.'; } diff --git a/wx+/choice_enum.h b/wx+/choice_enum.h index 97c40b68..2c424b9f 100755 --- a/wx+/choice_enum.h +++ b/wx+/choice_enum.h @@ -77,7 +77,9 @@ void setEnumVal(const EnumDescrList<Enum>& mapping, wxChoice& ctrl, Enum value) { selectedPos = it - mapping.descrList.begin(); - if (!it->second.second.empty()) + if (it->second.second.empty()) + ctrl.UnsetToolTip(); + else ctrl.SetToolTip(it->second.second); } } @@ -8,7 +8,6 @@ #define DC_H_4987123956832143243214 #include <unordered_map> -#include <zen/optional.h> #include <zen/basic_math.h> #include <wx/dcbuffer.h> //for macro: wxALWAYS_NATIVE_DOUBLE_BUFFER #include <wx/dcscreen.h> @@ -104,7 +103,7 @@ private: //associate "active" clipping area with each DC static std::unordered_map<wxDC*, wxRect>& refDcToAreaMap() { static std::unordered_map<wxDC*, wxRect> clippingAreas; return clippingAreas; } - Opt<wxRect> oldRect_; + std::optional<wxRect> oldRect_; wxDC& dc_; }; @@ -114,13 +113,13 @@ private: #endif #if wxALWAYS_NATIVE_DOUBLE_BUFFER -struct BufferedPaintDC : public wxPaintDC { BufferedPaintDC(wxWindow& wnd, Opt<wxBitmap>& buffer) : wxPaintDC(&wnd) {} }; +struct BufferedPaintDC : public wxPaintDC { BufferedPaintDC(wxWindow& wnd, std::optional<wxBitmap>& buffer) : wxPaintDC(&wnd) {} }; #else class BufferedPaintDC : public wxMemoryDC { public: - BufferedPaintDC(wxWindow& wnd, Opt<wxBitmap>& buffer) : buffer_(buffer), paintDc_(&wnd) + BufferedPaintDC(wxWindow& wnd, std::optional<wxBitmap>& buffer) : buffer_(buffer), paintDc_(&wnd) { const wxSize clientSize = wnd.GetClientSize(); if (clientSize.GetWidth() > 0 && clientSize.GetHeight() > 0) //wxBitmap asserts this!! width may be 0; test case "Grid::CornerWin": compare both sides, then change config @@ -134,7 +133,7 @@ public: SetLayoutDirection(wxLayout_RightToLeft); } else - buffer = NoValue(); + buffer = {}; } ~BufferedPaintDC() @@ -153,7 +152,7 @@ public: } private: - Opt<wxBitmap>& buffer_; + std::optional<wxBitmap>& buffer_; wxPaintDC paintDc_; }; #endif diff --git a/wx+/focus.h b/wx+/focus.h index cd99d010..e2daef79 100755 --- a/wx+/focus.h +++ b/wx+/focus.h @@ -44,22 +44,37 @@ Preserving input focus has to be more clever than: */ struct FocusPreserver { + FocusPreserver() + { + if (wxWindow* win = wxWindow::FindFocus()) + setFocus(win); + } + ~FocusPreserver() { //wxTopLevelWindow::IsActive() does NOT call Win32 ::GetActiveWindow()! //Instead it checks if ::GetFocus() is set somewhere inside the top level //Note: Both Win32 active and focus windows are *thread-local* values, while foreground window is global! https://blogs.msdn.microsoft.com/oldnewthing/20131016-00/?p=2913 - if (oldFocus_) - if (wxTopLevelWindow* topWin = getTopLevelWindow(oldFocus_)) - if (topWin->IsActive()) //Linux/macOS: already behaves just like ::GetForegroundWindow() on Windows! - oldFocus_->SetFocus(); + + if (oldFocusId_ != wxID_ANY) + if (wxWindow* oldFocusWin = wxWindow::FindWindowById(oldFocusId_)) + if (wxTopLevelWindow* topWin = getTopLevelWindow(oldFocusWin)) + if (topWin->IsActive()) //Linux/macOS: already behaves just like ::GetForegroundWindow() on Windows! + oldFocusWin->SetFocus(); } - wxWindow* getFocus() const { return oldFocus_; } - void setFocus(wxWindow* win) { oldFocus_ = win; } + wxWindowID getFocusId() const { return oldFocusId_; } + + void setFocus(wxWindow* win) + { + oldFocusId_ = win->GetId(); + assert(oldFocusId_ != wxID_ANY); + } private: - wxWindow* oldFocus_ = wxWindow::FindFocus(); + wxWindowID oldFocusId_ = wxID_ANY; + //don't store wxWindow* which may be dangling during ~FocusPreserver()! + //test: click on delete folder pair and immediately press F5 => focus window (= FP del button) is defer-deleted during sync }; } diff --git a/wx+/graph.cpp b/wx+/graph.cpp index b006cda0..38846523 100755 --- a/wx+/graph.cpp +++ b/wx+/graph.cpp @@ -387,8 +387,8 @@ std::vector<CurvePoint> SparseCurveData::getPoints(double minX, double maxX, con for (int i = posFrom; i <= posTo; ++i) { const double x = cvrtX.screenToReal(i); - Opt<CurvePoint> ptLe = getLessEq(x); - Opt<CurvePoint> ptGe = getGreaterEq(x); + std::optional<CurvePoint> ptLe = getLessEq(x); + std::optional<CurvePoint> ptGe = getGreaterEq(x); //both non-existent and invalid return values are mapped to out of expected range: => check on posLe/posGe NOT ptLe/ptGe in the following! const int posLe = ptLe ? cvrtX.realToScreenRound(ptLe->x) : i + 1; const int posGe = ptGe ? cvrtX.realToScreenRound(ptGe->x) : i - 1; diff --git a/wx+/graph.h b/wx+/graph.h index e0c2c12b..f1ae5d5a 100755 --- a/wx+/graph.h +++ b/wx+/graph.h @@ -14,7 +14,6 @@ #include <wx/settings.h> #include <wx/bitmap.h> #include <zen/string_tools.h> -#include <zen/optional.h> //elegant 2D graph as wxPanel specialization @@ -63,8 +62,8 @@ struct SparseCurveData : public CurveData { SparseCurveData(bool addSteps = false) : addSteps_(addSteps) {} //addSteps: add points to get a staircase effect or connect points via a direct line - virtual Opt<CurvePoint> getLessEq (double x) const = 0; - virtual Opt<CurvePoint> getGreaterEq(double x) const = 0; + virtual std::optional<CurvePoint> getLessEq (double x) const = 0; + virtual std::optional<CurvePoint> getGreaterEq(double x) const = 0; private: std::vector<CurvePoint> getPoints(double minX, double maxX, const wxSize& areaSizePx) const override; @@ -80,21 +79,21 @@ struct ArrayCurveData : public SparseCurveData private: std::pair<double, double> getRangeX() const override { const size_t sz = getSize(); return { 0.0, sz == 0 ? 0.0 : sz - 1.0}; } - Opt<CurvePoint> getLessEq(double x) const override + std::optional<CurvePoint> getLessEq(double x) const override { const size_t sz = getSize(); const size_t pos = std::min<ptrdiff_t>(std::floor(x), sz - 1); //[!] expect unsigned underflow if empty! if (pos < sz) return CurvePoint(pos, getValue(pos)); - return NoValue(); + return {}; } - Opt<CurvePoint> getGreaterEq(double x) const override + std::optional<CurvePoint> getGreaterEq(double x) const override { const size_t pos = std::max<ptrdiff_t>(std::ceil(x), 0); //[!] use std::max with signed type! if (pos < getSize()) return CurvePoint(pos, getValue(pos)); - return NoValue(); + return {}; } }; @@ -246,7 +245,7 @@ public: MainAttributes& setMinY(double newMinY) { minY = newMinY; return *this; } MainAttributes& setMaxY(double newMaxY) { maxY = newMaxY; return *this; } - MainAttributes& setAutoSize() { minX = maxX = minY = maxY = NoValue(); return *this; } + MainAttributes& setAutoSize() { minX = maxX = minY = maxY = {}; return *this; } MainAttributes& setLabelX(PosLabelX posX, int height = -1, std::shared_ptr<LabelFormatter> newLabelFmt = nullptr) { @@ -272,18 +271,18 @@ public: private: friend class Graph2D; - Opt<double> minX; //x-range to visualize - Opt<double> maxX; // + std::optional<double> minX; //x-range to visualize + std::optional<double> maxX; // - Opt<double> minY; //y-range to visualize - Opt<double> maxY; // + std::optional<double> minY; //y-range to visualize + std::optional<double> maxY; // PosLabelX labelposX = LABEL_X_BOTTOM; - Opt<int> xLabelHeight; + std::optional<int> xLabelHeight; std::shared_ptr<LabelFormatter> labelFmtX = std::make_shared<DecimalNumberFormatter>(); PosLabelY labelposY = LABEL_Y_LEFT; - Opt<int> yLabelWidth; + std::optional<int> yLabelWidth; std::shared_ptr<LabelFormatter> labelFmtY = std::make_shared<DecimalNumberFormatter>(); std::map<PosCorner, wxString> cornerTexts; @@ -337,7 +336,7 @@ private: MainAttributes attr_; //global attributes - Opt<wxBitmap> doubleBuffer_; + std::optional<wxBitmap> doubleBuffer_; using CurveList = std::vector<std::pair<std::shared_ptr<CurveData>, CurveAttributes>>; CurveList curves_; diff --git a/wx+/grid.cpp b/wx+/grid.cpp index 52ee6e6b..65311ffa 100755 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -310,23 +310,21 @@ public: protected: void setToolTip(const std::wstring& text) //proper fix for wxWindow { - wxToolTip* tt = GetToolTip(); - - const wxString oldText = tt ? tt->GetTip() : wxString(); - if (text != oldText) + if (text != GetToolTipText()) { if (text.empty()) - SetToolTip(nullptr); //wxGTK doesn't allow wxToolTip with empty text! + UnsetToolTip(); //wxGTK doesn't allow wxToolTip with empty text! else { - //wxWidgets bug: tooltip multiline property is defined by first tooltip text containing newlines or not (same is true for maximum width) + wxToolTip* tt = GetToolTip(); if (!tt) - SetToolTip(new wxToolTip(L"a b\n\ - a b")); //ugly, but working (on Windows) - tt = GetToolTip(); //should be bound by now - assert(tt); - if (tt) - tt->SetTip(text); + { + //wxWidgets bug: tooltip multiline property is defined by first tooltip text containing newlines or not (same is true for maximum width) + tt = new wxToolTip(L"a b\n\ + a b"); //ugly, but working (on Windows) + SetToolTip(tt); //pass ownership + } + tt->SetTip(text); } } } @@ -391,7 +389,7 @@ private: } Grid& parent_; - Opt<wxBitmap> doubleBuffer_; + std::optional<wxBitmap> doubleBuffer_; }; //---------------------------------------------------------------------------------------------------------------- @@ -689,12 +687,12 @@ private: activeResizing_.reset(); activeClickOrMove_.reset(); - if (Opt<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition())) + if (std::optional<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition())) { if (action->wantResize) { if (!event.LeftDClick()) //double-clicks never seem to arrive here; why is this checked at all??? - if (Opt<int> colWidth = refParent().getColWidth(action->col)) + if (std::optional<int> colWidth = refParent().getColWidth(action->col)) activeResizing_ = std::make_unique<ColumnResizing>(*this, action->col, *colWidth, event.GetPosition().x); } else //a move or single click @@ -724,7 +722,7 @@ private: } else //notify single label click { - if (const Opt<ColumnType> colType = refParent().colToType(activeClickOrMove_->getColumnFrom())) + if (const std::optional<ColumnType> colType = refParent().colToType(activeClickOrMove_->getColumnFrom())) sendEventNow(GridLabelClickEvent(EVENT_GRID_COL_LABEL_MOUSE_LEFT, event, *colType)); } activeClickOrMove_.reset(); @@ -745,7 +743,7 @@ private: void onMouseLeftDouble(wxMouseEvent& event) override { - if (Opt<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition())) + if (std::optional<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition())) if (action->wantResize) { //auto-size visible range on double-click @@ -790,7 +788,7 @@ private: } else { - if (const Opt<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition())) + if (const std::optional<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition())) { highlightCol_ = action->col; @@ -801,7 +799,7 @@ private: } else { - highlightCol_ = NoValue(); + highlightCol_ = {}; SetCursor(*wxSTANDARD_CURSOR); } } @@ -823,7 +821,7 @@ private: void onLeaveWindow(wxMouseEvent& event) override { - highlightCol_ = NoValue(); //wxEVT_LEAVE_WINDOW does not respect mouse capture! -> however highlight_ is drawn unconditionally during move/resize! + highlightCol_ = {}; //wxEVT_LEAVE_WINDOW does not respect mouse capture! -> however highlight_ is drawn unconditionally during move/resize! Refresh(); event.Skip(); @@ -831,9 +829,9 @@ private: void onMouseRightDown(wxMouseEvent& event) override { - if (const Opt<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition())) + if (const std::optional<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition())) { - if (const Opt<ColumnType> colType = refParent().colToType(action->col)) + if (const std::optional<ColumnType> colType = refParent().colToType(action->col)) sendEventNow(GridLabelClickEvent(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, event, *colType)); //notify right click else assert(false); } @@ -847,7 +845,7 @@ private: std::unique_ptr<ColumnResizing> activeResizing_; std::unique_ptr<ColumnMove> activeClickOrMove_; - Opt<size_t> highlightCol_; //column during mouse-over + std::optional<size_t> highlightCol_; //column during mouse-over }; //---------------------------------------------------------------------------------------------------------------- @@ -1080,8 +1078,8 @@ private: activeSelection_.reset(); } - highlight_.row = -1; - Refresh(); + highlight_.row = -1; + Refresh(); //event.Skip(); -> we DID handle it! } @@ -1146,8 +1144,8 @@ private: size_t getStartRow () const { return rowStart_; } size_t getCurrentRow () const { return rowCurrent_; } bool isPositiveSelect() const { return positiveSelect_; } //are we selecting or unselecting? - bool gridWasCleared () const { return gridWasCleared_; } - + bool gridWasCleared () const { return gridWasCleared_; } + const GridClickEvent& getFirstClick() const { return firstClick_; } void evalMousePos() @@ -1823,7 +1821,7 @@ wxWindow& Grid::getMainWin () { return *mainWin_; } const wxWindow& Grid::getMainWin() const { return *mainWin_; } -Opt<Grid::ColAction> Grid::clientPosToColumnAction(const wxPoint& pos) const +std::optional<Grid::ColAction> Grid::clientPosToColumnAction(const wxPoint& pos) const { const int absPosX = CalcUnscrolledPosition(pos).x; if (absPosX >= 0) @@ -1851,7 +1849,7 @@ Opt<Grid::ColAction> Grid::clientPosToColumnAction(const wxPoint& pos) const } } } - return NoValue(); + return {}; } @@ -12,7 +12,6 @@ #include <vector> #include <wx/scrolwin.h> #include <zen/basic_math.h> -#include <zen/optional.h> //a user-friendly, extensible and high-performance grid control @@ -52,13 +51,13 @@ struct GridSelectEvent : public wxCommandEvent { GridSelectEvent(size_t rowFirst, size_t rowLast, bool positive, const GridClickEvent* mouseClick) : wxCommandEvent(EVENT_GRID_SELECT_RANGE), rowFirst_(rowFirst), rowLast_(rowLast), positive_(positive), - mouseClick_(mouseClick ? *mouseClick : Opt<GridClickEvent>()) { assert(rowFirst <= rowLast); } + mouseClick_(mouseClick ? *mouseClick : std::optional<GridClickEvent>()) { assert(rowFirst <= rowLast); } GridSelectEvent* Clone() const override { return new GridSelectEvent(*this); } const size_t rowFirst_; //selected range: [rowFirst_, rowLast_) const size_t rowLast_; const bool positive_; //"false" when clearing selection! - const Opt<GridClickEvent> mouseClick_; //filled unless selection was performed via keyboard shortcuts + const std::optional<GridClickEvent> mouseClick_; //filled unless selection was performed via keyboard shortcuts }; struct GridLabelClickEvent : public wxMouseEvent @@ -299,12 +298,12 @@ private: int getColWidthsSum(int mainWinWidth) const; std::vector<int> getColStretchedWidths(int clientWidth) const; //final width = (normalized) (stretchedWidth + offset) - Opt<int> getColWidth(size_t col) const + std::optional<int> getColWidth(size_t col) const { const auto& widths = getColWidths(); if (col < widths.size()) return widths[col].width; - return NoValue(); + return {}; } void setColumnWidth(int width, size_t col, GridEventPolicy columnResizeEventPolicy, bool notifyAsync = false); @@ -321,7 +320,7 @@ private: bool wantResize = false; //"!wantResize" means "move" or "single click" size_t col = 0; }; - Opt<ColAction> clientPosToColumnAction(const wxPoint& pos) const; + std::optional<ColAction> clientPosToColumnAction(const wxPoint& pos) const; void moveColumn(size_t colFrom, size_t colTo); ptrdiff_t clientPosToMoveTargetColumn(const wxPoint& pos) const; //return < 0 on error @@ -363,18 +362,10 @@ private: template <class ColAttrReal> std::vector<ColAttrReal> makeConsistent(const std::vector<ColAttrReal>& attribs, const std::vector<ColAttrReal>& defaults) { - using ColTypeReal = decltype(ColAttrReal().type); - std::vector<ColAttrReal> output; - - std::set<ColTypeReal> usedTypes; //remove duplicates - auto appendUnique = [&](const std::vector<ColAttrReal>& attr) - { - std::copy_if(attr.begin(), attr.end(), std::back_inserter(output), - [&](const ColAttrReal& a) { return usedTypes.insert(a.type).second; }); - }; - appendUnique(attribs); - appendUnique(defaults); //make sure each type is existing! - + std::vector<ColAttrReal> output = attribs; + //make sure each type is existing! + output.insert(output.end(), defaults.begin(), defaults.end()); + removeDuplicates(output, [](const ColAttrReal& lhs, const ColAttrReal& rhs) { return lhs.type < rhs.type; }); return output; } diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp index bbeeff8b..d0449fe5 100755 --- a/wx+/image_resources.cpp +++ b/wx+/image_resources.cpp @@ -106,7 +106,7 @@ class DpiParallelScaler public: DpiParallelScaler(int hqScale) : hqScale_(hqScale) { assert(hqScale > 1); } - ~DpiParallelScaler() { threadGroup_ = zen::NoValue(); } //DpiParallelScaler must out-live threadGroup!!! + ~DpiParallelScaler() { threadGroup_ = {}; } //DpiParallelScaler must out-live threadGroup!!! void add(const wxString& name, const wxImage& img) { @@ -141,7 +141,7 @@ private: Protected<std::vector<std::pair<std::wstring, ImageHolder>>> result_; using TaskType = FunctionReturnTypeT<decltype(&getScalerTask)>; - Opt<ThreadGroup<TaskType>> threadGroup_{ ThreadGroup<TaskType>(std::max<int>(std::thread::hardware_concurrency(), 1), "xBRZ Scaler") }; + std::optional<ThreadGroup<TaskType>> threadGroup_{ ThreadGroup<TaskType>(std::max<int>(std::thread::hardware_concurrency(), 1), "xBRZ Scaler") }; //hardware_concurrency() == 0 if "not computable or well defined" }; diff --git a/wx+/image_tools.cpp b/wx+/image_tools.cpp index 0cb0e328..4748a590 100755 --- a/wx+/image_tools.cpp +++ b/wx+/image_tools.cpp @@ -14,38 +14,45 @@ using namespace zen; namespace { -void writeToImage(const wxImage& source, wxImage& target, const wxPoint& pos) +void writeToImage(wxImage& output, const wxImage& top, const wxPoint& pos) { - const int srcWidth = source.GetWidth (); - const int srcHeight = source.GetHeight(); - const int trgWidth = target.GetWidth (); + const int topWidth = top.GetWidth (); + const int topHeight = top.GetHeight(); + const int outWidth = output.GetWidth(); - if (srcWidth > 0 && srcHeight > 0) - { - assert(0 <= pos.x && pos.x + srcWidth <= trgWidth ); //draw area must be a - assert(0 <= pos.y && pos.y + srcHeight <= target.GetHeight()); //subset of target image! - assert(target.HasAlpha()); + assert(0 <= pos.x && pos.x + topWidth <= outWidth ); //draw area must be a + assert(0 <= pos.y && pos.y + topHeight <= output.GetHeight()); //subset of output image! + assert(top.HasAlpha() && output.HasAlpha()); - { - const unsigned char* sourcePtr = source.GetData(); - unsigned char* targetPtr = target.GetData() + 3 * (pos.x + pos.y * trgWidth); + //https://en.wikipedia.org/wiki/Alpha_compositing + const unsigned char* topRgb = top.GetData(); + const unsigned char* topAlpha = top.GetAlpha(); - for (int row = 0; row < srcHeight; ++row) - ::memcpy(targetPtr + 3 * row * trgWidth, sourcePtr + 3 * row * srcWidth, 3 * srcWidth); - } + for (int y = 0; y < topHeight; ++y) + { + unsigned char* outRgb = output.GetData () + 3 * (pos.x + (pos.y + y) * outWidth); + unsigned char* outAlpha = output.GetAlpha() + pos.x + (pos.y + y) * outWidth; - //handle alpha channel + for (int x = 0; x < topWidth; ++x) { - unsigned char* targetPtr = target.GetAlpha() + pos.x + pos.y * trgWidth; - if (source.HasAlpha()) + const int w1 = *topAlpha; //alpha-composition interpreted as weighted average + const int w2 = *outAlpha * (255 - w1) / 255; + const int wSum = w1 + w2; + + auto calcColor = [w1, w2, wSum](unsigned char colTop, unsigned char colBot) { - const unsigned char* sourcePtr = source.GetAlpha(); - for (int row = 0; row < srcHeight; ++row) - ::memcpy(targetPtr + row * trgWidth, sourcePtr + row * srcWidth, srcWidth); - } - else - for (int row = 0; row < srcHeight; ++row) - ::memset(targetPtr + row * trgWidth, wxIMAGE_ALPHA_OPAQUE, srcWidth); + return static_cast<unsigned char>(wSum == 0 ? 0 : (colTop * w1 + colBot * w2) / wSum); + }; + outRgb[0] = calcColor(topRgb[0], outRgb[0]); + outRgb[1] = calcColor(topRgb[1], outRgb[1]); + outRgb[2] = calcColor(topRgb[2], outRgb[2]); + + *outAlpha = static_cast<unsigned char>(wSum); + + topRgb += 3; + outRgb += 3; + ++topAlpha; + ++outAlpha; } } } @@ -64,16 +71,12 @@ wxImage zen::stackImages(const wxImage& img1, const wxImage& img2, ImageStackLay int width = std::max(img1Width, img2Width); int height = std::max(img1Height, img2Height); - switch (dir) - { - case ImageStackLayout::HORIZONTAL: - width = img1Width + gap + img2Width; - break; - case ImageStackLayout::VERTICAL: - height = img1Height + gap + img2Height; - break; - } + if (dir == ImageStackLayout::HORIZONTAL) + width = img1Width + gap + img2Width; + else + height = img1Height + gap + img2Height; + wxImage output(width, height); output.SetAlpha(); ::memset(output.GetAlpha(), wxIMAGE_ALPHA_TRANSPARENT, width * height); @@ -96,13 +99,13 @@ wxImage zen::stackImages(const wxImage& img1, const wxImage& img2, ImageStackLay switch (dir) { case ImageStackLayout::HORIZONTAL: - writeToImage(img1, output, wxPoint(0, calcPos(img1Height, height))); - writeToImage(img2, output, wxPoint(img1Width + gap, calcPos(img2Height, height))); + writeToImage(output, img1, wxPoint(0, calcPos(img1Height, height))); + writeToImage(output, img2, wxPoint(img1Width + gap, calcPos(img2Height, height))); break; case ImageStackLayout::VERTICAL: - writeToImage(img1, output, wxPoint(calcPos(img1Width, width), 0)); - writeToImage(img2, output, wxPoint(calcPos(img2Width, width), img1Height + gap)); + writeToImage(output, img1, wxPoint(calcPos(img1Width, width), 0)); + writeToImage(output, img2, wxPoint(calcPos(img2Width, width), img1Height + gap)); break; } return output; @@ -111,26 +114,6 @@ wxImage zen::stackImages(const wxImage& img1, const wxImage& img2, ImageStackLay namespace { -void calcAlphaForBlackWhiteImage(wxImage& image) //assume black text on white background -{ - assert(image.HasAlpha()); - if (unsigned char* alphaPtr = image.GetAlpha()) - { - const int pixelCount = image.GetWidth() * image.GetHeight(); - const unsigned char* dataPtr = image.GetData(); - for (int i = 0; i < pixelCount; ++ i) - { - const unsigned char r = *dataPtr++; - const unsigned char g = *dataPtr++; - const unsigned char b = *dataPtr++; - - //black(0,0,0) becomes fully opaque(255), while white(255,255,255) becomes transparent(0) - alphaPtr[i] = static_cast<unsigned char>((255 - r + 255 - g + 255 - b) / 3); //mixed mode arithmetics! - } - } -} - - std::vector<std::pair<wxString, wxSize>> getTextExtentInfo(const wxString& text, const wxFont& font) { wxMemoryDC dc; //the context used for bitmaps @@ -201,32 +184,39 @@ wxImage zen::createImageFromText(const wxString& text, const wxFont& font, const wxImage output(newBitmap.ConvertToImage()); output.SetAlpha(); - calcAlphaForBlackWhiteImage(output); - - //apply actual text color - unsigned char* dataPtr = output.GetData(); + unsigned char* rgb = output.GetData(); + unsigned char* alpha = output.GetAlpha(); const int pixelCount = output.GetWidth() * output.GetHeight(); - for (int i = 0; i < pixelCount; ++ i) + + for (int i = 0; i < pixelCount; ++i) { - *dataPtr++ = col.Red(); - *dataPtr++ = col.Green(); - *dataPtr++ = col.Blue(); + //black(0,0,0) becomes wxIMAGE_ALPHA_OPAQUE(255), while white(255,255,255) becomes wxIMAGE_ALPHA_TRANSPARENT(0) + *alpha++ = static_cast<unsigned char>((255 - rgb[0] + 255 - rgb[1] + 255 - rgb[2]) / 3); //mixed mode arithmetics! + + rgb[0] = col.Red (); // + rgb[1] = col.Green(); //apply actual text color + rgb[2] = col.Blue (); // + + rgb += 3; } return output; } -wxBitmap zen::layOver(const wxBitmap& background, const wxBitmap& foreground, int alignment) +wxBitmap zen::layOver(const wxBitmap& back, const wxBitmap& front, int alignment) { - if (!foreground.IsOk()) return background; + if (!front.IsOk()) return back; + + const int width = std::max(back.GetWidth(), front.GetWidth()); + const int height = std::max(back.GetHeight(), front.GetHeight()); - assert(foreground.HasAlpha() == background.HasAlpha()); //we don't support mixed-mode brittleness! + assert(front.HasAlpha() == back.HasAlpha()); //we don't support mixed-mode brittleness! const int offsetX = [&] { if (alignment & wxALIGN_RIGHT) - return background.GetWidth() - foreground.GetWidth(); + return back.GetWidth() - front.GetWidth(); if (alignment & wxALIGN_CENTER_HORIZONTAL) - return (background.GetWidth() - foreground.GetWidth()) / 2; + return (back.GetWidth() - front.GetWidth()) / 2; static_assert(wxALIGN_LEFT == 0); return 0; @@ -235,19 +225,22 @@ wxBitmap zen::layOver(const wxBitmap& background, const wxBitmap& foreground, i const int offsetY = [&] { if (alignment & wxALIGN_BOTTOM) - return background.GetHeight() - foreground.GetHeight(); + return back.GetHeight() - front.GetHeight(); if (alignment & wxALIGN_CENTER_VERTICAL) - return (background.GetHeight() - foreground.GetHeight()) / 2; + return (back.GetHeight() - front.GetHeight()) / 2; static_assert(wxALIGN_TOP == 0); return 0; }(); - wxBitmap output(background.ConvertToImage()); //attention: wxBitmap/wxImage use ref-counting without copy on write! - { - wxMemoryDC dc(output); - dc.DrawBitmap(foreground, offsetX, offsetY); - } + //can't use wxMemoryDC and wxDC::DrawBitmap(): no alpha channel support on wxGTK! + wxImage output(width, height); + output.SetAlpha(); + ::memset(output.GetAlpha(), wxIMAGE_ALPHA_TRANSPARENT, width * height); + + const wxPoint posBack(std::max(-offsetX, 0), std::max(-offsetY, 0)); + writeToImage(output, back .ConvertToImage(), posBack); + writeToImage(output, front.ConvertToImage(), posBack + wxPoint(offsetX, offsetY)); return output; } diff --git a/wx+/image_tools.h b/wx+/image_tools.h index ca82a031..06d7f0ba 100755 --- a/wx+/image_tools.h +++ b/wx+/image_tools.h @@ -34,7 +34,7 @@ wxImage stackImages(const wxImage& img1, const wxImage& img2, ImageStackLayout d wxImage createImageFromText(const wxString& text, const wxFont& font, const wxColor& col, ImageStackAlignment textAlign = ImageStackAlignment::LEFT); //CENTER/LEFT/RIGHT -wxBitmap layOver(const wxBitmap& background, const wxBitmap& foreground, int alignment = wxALIGN_CENTER); +wxBitmap layOver(const wxBitmap& back, const wxBitmap& front, int alignment = wxALIGN_CENTER); wxImage greyScale(const wxImage& img); //greyscale + brightness adaption wxBitmap greyScale(const wxBitmap& bmp); // @@ -7,7 +7,6 @@ #ifndef RTL_H_0183487180058718273432148 #define RTL_H_0183487180058718273432148 -#include <zen/optional.h> #include <wx/dcmemory.h> #include <wx/image.h> #include <wx/app.h> @@ -16,7 +15,7 @@ namespace zen { //functions supporting right-to-left GUI layout -void drawBitmapRtlMirror (wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, Opt<wxBitmap>& buffer); +void drawBitmapRtlMirror (wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, std::optional<wxBitmap>& buffer); void drawBitmapRtlNoMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment); //wxDC::DrawIcon DOES mirror by default -> implement RTL support when needed @@ -56,7 +55,7 @@ void drawBitmapAligned(wxDC& dc, const wxBitmap& image, const wxRect& rect, int inline -void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, Opt<wxBitmap>& buffer) +void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, std::optional<wxBitmap>& buffer) { if (dc.GetLayoutDirection() == wxLayout_RightToLeft) { diff --git a/xBRZ/src/xbrz.cpp b/xBRZ/src/xbrz.cpp index b8065f5d..f37d1a01 100755 --- a/xBRZ/src/xbrz.cpp +++ b/xBRZ/src/xbrz.cpp @@ -228,6 +228,8 @@ double distYCbCrBuffered(uint32_t pix1, uint32_t pix2) } + + enum BlendType { BLEND_NONE = 0, @@ -358,8 +360,6 @@ template <> inline unsigned char rotateBlendInfo<ROT_180>(unsigned char b) { ret template <> inline unsigned char rotateBlendInfo<ROT_270>(unsigned char b) { return ((b << 6) | (b >> 2)) & 0xff; } - - /* input kernel area naming convention: ------------- diff --git a/zen/file_access.cpp b/zen/file_access.cpp index 9c351e25..a81fdae0 100755 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -30,9 +30,9 @@ using namespace zen; -Opt<PathComponents> zen::parsePathComponents(const Zstring& itemPath) +std::optional<PathComponents> zen::parsePathComponents(const Zstring& itemPath) { - auto doParse = [&](int sepCountVolumeRoot, bool rootWithSep) -> Opt<PathComponents> + auto doParse = [&](int sepCountVolumeRoot, bool rootWithSep) -> std::optional<PathComponents> { const Zstring itemPathFmt = appendSeparator(itemPath); //simplify analysis of root without separator, e.g. \\server-name\share int sepCount = 0; @@ -47,7 +47,7 @@ Opt<PathComponents> zen::parsePathComponents(const Zstring& itemPath) return PathComponents({ rootPath, relPath }); } - return NoValue(); + return {}; }; if (startsWith(itemPath, "/")) @@ -72,17 +72,17 @@ Opt<PathComponents> zen::parsePathComponents(const Zstring& itemPath) } //we do NOT support relative paths! - return NoValue(); + return {}; } -Opt<Zstring> zen::getParentFolderPath(const Zstring& itemPath) +std::optional<Zstring> zen::getParentFolderPath(const Zstring& itemPath) { - if (const Opt<PathComponents> comp = parsePathComponents(itemPath)) + if (const std::optional<PathComponents> comp = parsePathComponents(itemPath)) { if (comp->relPath.empty()) - return NoValue(); + return {}; const Zstring parentRelPath = beforeLast(comp->relPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); if (parentRelPath.empty()) @@ -90,7 +90,7 @@ Opt<Zstring> zen::getParentFolderPath(const Zstring& itemPath) return appendSeparator(comp->rootPath) + parentRelPath; } assert(false); - return NoValue(); + return {}; } @@ -110,7 +110,7 @@ ItemType zen::getItemType(const Zstring& itemPath) //throw FileError PathStatus zen::getPathStatus(const Zstring& itemPath) //throw FileError { - const Opt<Zstring> parentPath = getParentFolderPath(itemPath); + const std::optional<Zstring> parentPath = getParentFolderPath(itemPath); try { return { getItemType(itemPath), itemPath, {} }; //throw FileError @@ -145,12 +145,12 @@ PathStatus zen::getPathStatus(const Zstring& itemPath) //throw FileError } -Opt<ItemType> zen::getItemTypeIfExists(const Zstring& itemPath) //throw FileError +std::optional<ItemType> zen::getItemTypeIfExists(const Zstring& itemPath) //throw FileError { const PathStatus ps = getPathStatus(itemPath); //throw FileError if (ps.relPath.empty()) return ps.existingType; - return NoValue(); + return {}; } @@ -672,7 +672,7 @@ FileCopyResult copyFileOsSpecific(const Zstring& sourceFile, //throw FileError, //close output file handle before setting file time; also good place to catch errors when closing stream! fileOut.finalize(); //throw FileError, (X) essentially a close() since buffers were already flushed - Opt<FileError> errorModTime; + std::optional<FileError> errorModTime; try { //we cannot set the target file times (::futimes) while the file descriptor is still open after a write operation: diff --git a/zen/file_access.h b/zen/file_access.h index c62ddc98..916f23f5 100755 --- a/zen/file_access.h +++ b/zen/file_access.h @@ -23,9 +23,9 @@ struct PathComponents Zstring rootPath; //itemPath = rootPath + (FILE_NAME_SEPARATOR?) + relPath Zstring relPath; // }; -Opt<PathComponents> parsePathComponents(const Zstring& itemPath); //no value on failure +std::optional<PathComponents> parsePathComponents(const Zstring& itemPath); //no value on failure -Opt<Zstring> getParentFolderPath(const Zstring& itemPath); +std::optional<Zstring> getParentFolderPath(const Zstring& itemPath); //POSITIVE existence checks; if false: 1. item not existing 2. different type 3.device access error or similar bool fileAvailable(const Zstring& filePath); //noexcept @@ -42,7 +42,7 @@ enum class ItemType //(hopefully) fast: does not distinguish between error/not existing ItemType getItemType (const Zstring& itemPath); //throw FileError //execute potentially SLOW folder traversal but distinguish error/not existing -Opt<ItemType> getItemTypeIfExists(const Zstring& itemPath); //throw FileError +std::optional<ItemType> getItemTypeIfExists(const Zstring& itemPath); //throw FileError struct PathStatus { @@ -97,7 +97,7 @@ struct FileCopyResult time_t modTime = 0; //number of seconds since Jan. 1st 1970 UTC FileId sourceFileId; FileId targetFileId; - Opt<FileError> errorModTime; //failure to set modification time + std::optional<FileError> errorModTime; //failure to set modification time }; FileCopyResult copyNewFile(const Zstring& sourceFile, const Zstring& targetFile, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked diff --git a/zen/format_unit.h b/zen/format_unit.h index 23ab33fb..3686f39c 100755 --- a/zen/format_unit.h +++ b/zen/format_unit.h @@ -9,7 +9,6 @@ #include <string> #include <cstdint> -#include "optional.h" #include "string_tools.h" diff --git a/zen/legacy_compiler.h b/zen/legacy_compiler.h index 5a0013b3..e9d50b97 100755 --- a/zen/legacy_compiler.h +++ b/zen/legacy_compiler.h @@ -7,82 +7,13 @@ #ifndef LEGACY_COMPILER_H_839567308565656789 #define LEGACY_COMPILER_H_839567308565656789 - #include <memory> - #include <cstddef> //std::byte - + #include <optional> namespace std { //https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html //https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations -#ifndef __cpp_lib_type_trait_variable_templates //GCC 7.1 - template<class T, class U> constexpr bool is_same_v = is_same<T, U>::value; - template<class T> constexpr bool is_const_v = is_const<T>::value; - template<class T> constexpr bool is_signed_v = is_signed<T>::value; - template<class T> constexpr bool is_trivially_destructible_v = is_trivially_destructible<T>::value; -#endif - -#if __GNUC__ < 7 || (__GNUC__ == 7 && __GNUC_MINOR__ < 1) //GCC doesn't define __cpp_lib_raw_memory_algorithms -template<class T> void destroy_at(T* p) { p->~T(); } - -template< class ForwardIt > -void destroy(ForwardIt first, ForwardIt last) -{ - for (; first != last; ++first) - std::destroy_at(std::addressof(*first)); -} - -template<class InputIt, class ForwardIt> -ForwardIt uninitialized_move(InputIt first, InputIt last, ForwardIt trg_first) -{ - typedef typename std::iterator_traits<ForwardIt>::value_type Value; - ForwardIt current = trg_first; - try - { - for (; first != last; ++first, ++current) - ::new (static_cast<void*>(std::addressof(*current))) Value(std::move(*first)); - return current; - } - catch (...) - { - for (; trg_first != current; ++trg_first) - trg_first->~Value(); - throw; - } -} -#endif - -#ifndef __cpp_lib_apply //GCC 7.1 -template <class F, class T0, class T1, class T2> -constexpr decltype(auto) apply(F&& f, std::tuple<T0, T1, T2>& t) { return f(std::get<0>(t), std::get<1>(t), std::get<2>(t)); } -#endif - -#if __GNUC__ < 7 || (__GNUC__ == 7 && __GNUC_MINOR__ < 1) //__cpp_lib_byte not defined before GCC 7.3 but supported earlier - typedef unsigned char byte; -#endif - -#ifndef __cpp_lib_bool_constant //GCC 6.1 - template<bool B> using bool_constant = integral_constant<bool, B>; -#endif - -//================================================================================ - -} -namespace __cxxabiv1 -{ -struct __cxa_eh_globals; -extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept; -} -namespace std -{ -inline int uncaught_exceptions_legacy_hack() noexcept -{ - return *(reinterpret_cast<unsigned int*>(static_cast<char*>(static_cast<void*>(__cxxabiv1::__cxa_get_globals())) + sizeof(void*))); -} -#ifndef __cpp_lib_uncaught_exceptions //GCC 6.1 -inline int uncaught_exceptions() noexcept { return uncaught_exceptions_legacy_hack(); } -#endif } diff --git a/zen/optional.h b/zen/optional.h deleted file mode 100755 index e4605c5f..00000000 --- a/zen/optional.h +++ /dev/null @@ -1,114 +0,0 @@ -// ***************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * -// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * -// ***************************************************************************** - -#ifndef OPTIONAL_H_2857428578342203589 -#define OPTIONAL_H_2857428578342203589 - -#include <type_traits> - - -namespace zen -{ -/* -Optional return value without heap memory allocation! - -> interface like a pointer, performance like a value - - Usage: - ------ - Opt<MyEnum> someFunction(); -{ - if (allIsWell) - return enumVal; - else - return NoValue(); -} - - Opt<MyEnum> optValue = someFunction(); - if (optValue) - ... use *optValue ... -*/ - -struct NoValue {}; - -template <class T> -class Opt -{ -public: - Opt() {} - Opt(NoValue) {} - Opt(const T& val) : valid_(true) { new (&rawMem_) T(val); } //throw X - Opt( T&& tmp) : valid_(true) { new (&rawMem_) T(std::move(tmp)); } - - Opt(const Opt& other) : valid_(other.valid_) - { - if (const T* val = other.get()) - new (&rawMem_) T(*val); //throw X - } - - ~Opt() { if (T* val = get()) val->~T(); } - - Opt& operator=(const Opt& other) //strong exception-safety iff T::operator=() is strongly exception-safe - { - if (T* val = get()) - { - if (const T* valOther = other.get()) - *val = *valOther; //throw X - else - { - valid_ = false; - val->~T(); - } - } - else if (const T* valOther = other.get()) - { - new (&rawMem_) T(*valOther); //throw X - valid_ = true; - } - return *this; - } - - Opt& operator=(NoValue) //support assignment to Opt<const T> - { - if (T* val = get()) - { - valid_ = false; - val->~T(); - } - return *this; - } - - explicit operator bool() const { return valid_; } //thank you, C++11!!! - - const T* get() const { return valid_ ? reinterpret_cast<const T*>(&rawMem_) : nullptr; } - T* get() { return valid_ ? reinterpret_cast< T*>(&rawMem_) : nullptr; } - - const T& operator*() const { return *get(); } - /**/ T& operator*() { return *get(); } - - const T* operator->() const { return get(); } - /**/ T* operator->() { return get(); } - -private: - std::aligned_storage_t<sizeof(T), alignof(T)> rawMem_; //don't require T to be default-constructible! - bool valid_ = false; -}; - - -template <class T> inline -bool operator==(const Opt<T>& lhs, const Opt<T>& rhs) -{ - if (static_cast<bool>(lhs) != static_cast<bool>(rhs)) - return false; - if (!lhs) - return true; - return *lhs == *rhs; -} -template <class T> inline -bool operator!=(const Opt<T>& lhs, const Opt<T>& rhs) { return !(lhs == rhs); } - -} - -#endif //OPTIONAL_H_2857428578342203589 diff --git a/zen/recycler.cpp b/zen/recycler.cpp index 30b37cb2..8b4389a7 100755 --- a/zen/recycler.cpp +++ b/zen/recycler.cpp @@ -27,7 +27,7 @@ bool zen::recycleOrDeleteIfExists(const Zstring& itemPath) //throw FileError if (!::g_file_trash(file, nullptr, &error)) { - const Opt<ItemType> type = getItemTypeIfExists(itemPath); //throw FileError + const std::optional<ItemType> type = getItemTypeIfExists(itemPath); //throw FileError if (!type) return false; diff --git a/zen/ring_buffer.h b/zen/ring_buffer.h index 6debd84e..232e17da 100755 --- a/zen/ring_buffer.h +++ b/zen/ring_buffer.h @@ -10,8 +10,8 @@ #include <cassert> #include <vector> #include <stdexcept> -#include "type_traits.h" #include "scope_guard.h" +#include "string_tools.h" namespace zen diff --git a/zen/shell_execute.h b/zen/shell_execute.h index a0e5634b..0802fbcb 100755 --- a/zen/shell_execute.h +++ b/zen/shell_execute.h @@ -73,7 +73,7 @@ void shellExecute(const Zstring& command, ExecutionType type) //throw FileError inline void openWithDefaultApplication(const Zstring& itemPath) //throw FileError { - shellExecute("xdg-open \"" + itemPath + "\"", ExecutionType::ASYNC); // + shellExecute("xdg-open \"" + itemPath + "\"", ExecutionType::ASYNC); // } } diff --git a/zen/socket.h b/zen/socket.h index c92071e2..e551a5ba 100755 --- a/zen/socket.h +++ b/zen/socket.h @@ -53,7 +53,7 @@ public: return testSocket; }; - Opt<SysError> firstError; + std::optional<SysError> firstError; for (const auto* /*::addrinfo*/ si = servinfo; si; si = si->ai_next) try { diff --git a/zen/stl_tools.h b/zen/stl_tools.h index 7365392f..be9bf710 100755 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -12,7 +12,7 @@ #include <vector> #include <memory> #include <algorithm> -#include "type_traits.h" +#include "string_traits.h" #include "build_info.h" @@ -42,8 +42,11 @@ void append(std::map<KeyType, ValueType, LessType, Alloc>& m, const C& c); template <class T, class Alloc> void removeDuplicates(std::vector<T, Alloc>& v); +template <class T, class Alloc, class CompLess> +void removeDuplicates(std::vector<T, Alloc>& v, CompLess less); + //binary search returning an iterator -template <class Iterator, class T, typename CompLess> +template <class Iterator, class T, class CompLess> Iterator binary_search(Iterator first, Iterator last, const T& value, CompLess less); template <class BidirectionalIterator, class T> @@ -70,6 +73,9 @@ struct StringHash }; +//why, oh wy is there no std::optional<T>::get()??? +template <class T> inline T* get( std::optional<T>& opt) { return opt ? &*opt : nullptr; } +template <class T> inline const T* get(const std::optional<T>& opt) { return opt ? &*opt : nullptr; } @@ -117,15 +123,29 @@ template <class KeyType, class ValueType, class LessType, class Alloc, class C> void append(std::map<KeyType, ValueType, LessType, Alloc>& m, const C& c) { m.insert(c.begin(), c.end()); } +template <class T, class Alloc, class CompLess, class CompEqual> inline +void removeDuplicates(std::vector<T, Alloc>& v, CompLess less, CompEqual eq) +{ + std::sort(v.begin(), v.end(), less); + v.erase(std::unique(v.begin(), v.end(), eq), v.end()); +} + + +template <class T, class Alloc, class CompLess> inline +void removeDuplicates(std::vector<T, Alloc>& v, CompLess less) +{ + removeDuplicates(v, less, [&](const auto& lhs, const auto& rhs) { return !less(lhs, rhs) && !less(rhs, lhs); }); +} + + template <class T, class Alloc> inline void removeDuplicates(std::vector<T, Alloc>& v) { - std::sort(v.begin(), v.end()); - v.erase(std::unique(v.begin(), v.end()), v.end()); + removeDuplicates(v, std::less<T>(), std::equal_to<T>()); } -template <class Iterator, class T, typename CompLess> inline +template <class Iterator, class T, class CompLess> inline Iterator binary_search(Iterator first, Iterator last, const T& value, CompLess less) { static_assert(std::is_same_v<typename std::iterator_traits<Iterator>::iterator_category, std::random_access_iterator_tag>); diff --git a/zen/thread.h b/zen/thread.h index 5828d07a..7f3d216c 100755 --- a/zen/thread.h +++ b/zen/thread.h @@ -11,7 +11,6 @@ #include <future> #include "scope_guard.h" #include "ring_buffer.h" -#include "optional.h" #include "string_tools.h" @@ -97,13 +96,13 @@ public: GetFirstResult(); template <class Fun> - void addJob(Fun&& f); //f must return a zen::Opt<T> containing a value if successful + void addJob(Fun&& f); //f must return a std::optional<T> containing a value if successful template <class Duration> bool timedWait(const Duration& duration) const; //true: "get()" is ready, false: time elapsed //return first value or none if all jobs failed; blocks until result is ready! - Opt<T> get() const; //may be called only once! + std::optional<T> get() const; //may be called only once! private: class AsyncResult; @@ -323,7 +322,7 @@ class GetFirstResult<T>::AsyncResult { public: //context: worker threads - void reportFinished(Opt<T>&& result) + void reportFinished(std::optional<T>&& result) { { std::lock_guard<std::mutex> dummy(lockResult_); @@ -342,7 +341,7 @@ public: return conditionJobDone_.wait_for(dummy, duration, [&] { return this->jobDone(jobsTotal); }); } - Opt<T> getResult(size_t jobsTotal) + std::optional<T> getResult(size_t jobsTotal) { std::unique_lock<std::mutex> dummy(lockResult_); conditionJobDone_.wait(dummy, [&] { return this->jobDone(jobsTotal); }); @@ -355,7 +354,7 @@ private: std::mutex lockResult_; size_t jobsFinished_ = 0; // - Opt<T> result_; //our condition is: "have result" or "jobsFinished_ == jobsTotal" + std::optional<T> result_; //our condition is: "have result" or "jobsFinished_ == jobsTotal" std::condition_variable conditionJobDone_; }; @@ -367,7 +366,7 @@ GetFirstResult<T>::GetFirstResult() : asyncResult_(std::make_shared<AsyncResult> template <class T> template <class Fun> inline -void GetFirstResult<T>::addJob(Fun&& f) //f must return a zen::Opt<T> containing a value on success +void GetFirstResult<T>::addJob(Fun&& f) //f must return a std::optional<T> containing a value on success { std::thread t([asyncResult = this->asyncResult_, f = std::forward<Fun>(f)] { asyncResult->reportFinished(f()); }); ++jobsTotal_; @@ -381,7 +380,7 @@ bool GetFirstResult<T>::timedWait(const Duration& duration) const { return async template <class T> inline -Opt<T> GetFirstResult<T>::get() const { return asyncResult_->getResult(jobsTotal_); } +std::optional<T> GetFirstResult<T>::get() const { return asyncResult_->getResult(jobsTotal_); } //------------------------------------------------------------------------------------------ @@ -10,7 +10,6 @@ #include <cstdint> #include <iterator> #include "string_tools.h" //copyStringTo -#include "optional.h" namespace zen @@ -96,10 +95,10 @@ class Utf16Decoder public: Utf16Decoder(const Char16* str, size_t len) : it_(str), last_(str + len) {} - Opt<CodePoint> getNext() + std::optional<CodePoint> getNext() { if (it_ == last_) - return NoValue(); + return {}; const Char16 ch = *it_++; CodePoint cp = ch; @@ -190,10 +189,10 @@ class Utf8Decoder public: Utf8Decoder(const Char8* str, size_t len) : it_(str), last_(str + len) {} - Opt<CodePoint> getNext() + std::optional<CodePoint> getNext() { if (it_ == last_) - return NoValue(); + return std::nullopt; //GCC 8.2 bug: -Wmaybe-uninitialized for "return {};" const Char8 ch = *it_++; CodePoint cp = ch; @@ -268,7 +267,7 @@ class UtfDecoderImpl<CharType, 1> //UTF8-char { public: UtfDecoderImpl(const CharType* str, size_t len) : decoder_(reinterpret_cast<const Char8*>(str), len) {} - Opt<CodePoint> getNext() { return decoder_.getNext(); } + std::optional<CodePoint> getNext() { return decoder_.getNext(); } private: Utf8Decoder decoder_; }; @@ -279,7 +278,7 @@ class UtfDecoderImpl<CharType, 2> //Windows: UTF16-wchar_t { public: UtfDecoderImpl(const CharType* str, size_t len) : decoder_(reinterpret_cast<const Char16*>(str), len) {} - Opt<CodePoint> getNext() { return decoder_.getNext(); } + std::optional<CodePoint> getNext() { return decoder_.getNext(); } private: Utf16Decoder decoder_; }; @@ -290,10 +289,10 @@ class UtfDecoderImpl<CharType, 4> //other OS: UTF32-wchar_t { public: UtfDecoderImpl(const CharType* str, size_t len) : it_(reinterpret_cast<const CodePoint*>(str)), last_(it_ + len) {} - Opt<CodePoint> getNext() + std::optional<CodePoint> getNext() { if (it_ == last_) - return NoValue(); + return {}; return *it_++; } private: @@ -314,7 +313,7 @@ bool isValidUtf(const UtfString& str) using namespace impl; UtfDecoder<GetCharTypeT<UtfString>> decoder(strBegin(str), strLength(str)); - while (Opt<CodePoint> cp = decoder.getNext()) + while (std::optional<CodePoint> cp = decoder.getNext()) if (*cp == REPLACEMENT_CHAR) return false; @@ -344,7 +343,7 @@ UtfString getUnicodeSubstring(const UtfString& str, size_t uniPosFirst, size_t u return output; UtfDecoder<CharType> decoder(strBegin(str), strLength(str)); - for (size_t uniPos = 0; Opt<CodePoint> cp = decoder.getNext(); ++uniPos) //[!] declaration in condition part of the for-loop + for (size_t uniPos = 0; std::optional<CodePoint> cp = decoder.getNext(); ++uniPos) //[!] declaration in condition part of the for-loop if (uniPosFirst <= uniPos) { if (uniPos >= uniPosLast) @@ -368,7 +367,7 @@ TargetString utfTo(const SourceString& str, std::false_type) TargetString output; UtfDecoder<CharSrc> decoder(strBegin(str), strLength(str)); - while (Opt<CodePoint> cp = decoder.getNext()) + while (std::optional<CodePoint> cp = decoder.getNext()) codePointToUtf<CharTrg>(*cp, [&](CharTrg c) { output += c; }); return output; diff --git a/zen/zstring.cpp b/zen/zstring.cpp index 1fe31eaf..8bf77a0b 100755 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -49,8 +49,8 @@ int compareNoCaseUtf8(const char* lhs, size_t lhsLen, const char* rhs, size_t rh impl::UtfDecoder<char> decR(rhs, rhsLen); for (;;) { - const Opt<impl::CodePoint> cpL = decL.getNext(); - const Opt<impl::CodePoint> cpR = decR.getNext(); + const std::optional<impl::CodePoint> cpL = decL.getNext(); + const std::optional<impl::CodePoint> cpR = decR.getNext(); if (!cpL || !cpR) return static_cast<int>(!cpR) - static_cast<int>(!cpL); diff --git a/zen/zstring.h b/zen/zstring.h index 3938cef1..7fa21335 100755 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -95,7 +95,7 @@ const wchar_t LTR_MARK = L'\u200E'; //UTF-8: E2 80 8E const wchar_t RTL_MARK = L'\u200F'; //UTF-8: E2 80 8F const wchar_t ELLIPSIS = L'\u2026'; //"..." const wchar_t MULT_SIGN = L'\u00D7'; //fancy "x" - +//const wchar_t NOBREAK_SPACE = L'\u00A0'; diff --git a/zenXml/zenxml/cvrt_struc.h b/zenXml/zenxml/cvrt_struc.h index 11795107..85c5d8d0 100755 --- a/zenXml/zenxml/cvrt_struc.h +++ b/zenXml/zenxml/cvrt_struc.h @@ -65,21 +65,19 @@ using IsStlContainer = std::bool_constant< impl_2384343::HasMember_insert <T>::value>; -namespace impl_2384343 +template <class T> +struct IsStlPair { -ZEN_INIT_DETECT_MEMBER_TYPE(first_type); -ZEN_INIT_DETECT_MEMBER_TYPE(second_type); - -ZEN_INIT_DETECT_MEMBER(first) //we don't know the exact declaration of the member attribute: may be in a base class! -ZEN_INIT_DETECT_MEMBER(second) // -} - -template <typename T> -using IsStlPair = std::bool_constant< - impl_2384343::HasMemberType_first_type <T>::value && - impl_2384343::HasMemberType_second_type<T>::value && - impl_2384343::HasMember_first <T>::value && - impl_2384343::HasMember_second <T>::value>; +private: + using Yes = char[1]; + using No = char[2]; + + template <class T1, class T2> + static Yes& isPair(const std::pair<T1, T2>&); + static No& isPair(...); +public: + enum { value = sizeof(isPair(std::declval<T>())) == sizeof(Yes) }; +}; //###################################################################################### diff --git a/zenXml/zenxml/cvrt_text.h b/zenXml/zenxml/cvrt_text.h index 32a961b2..c6dab8c2 100755 --- a/zenXml/zenxml/cvrt_text.h +++ b/zenXml/zenxml/cvrt_text.h @@ -7,6 +7,7 @@ #ifndef CVRT_TEXT_H_018727339083427097434 #define CVRT_TEXT_H_018727339083427097434 +#include <chrono> #include <zen/utf.h> #include <zen/string_tools.h> @@ -101,21 +102,37 @@ template <class T> void writeText(const T& value, std::string& output); //------------------------------ implementation ------------------------------------- +template <class T> +struct IsChronoDuration +{ +private: + using Yes = char[1]; + using No = char[2]; + + template <class Rep, class Period> + static Yes& isDuration(std::chrono::duration<Rep, Period>); + static No& isDuration(...); +public: + enum { value = sizeof(isDuration(std::declval<T>())) == sizeof(Yes) }; +}; + //Conversion from arbitrary types to text (for use with XML elements and attributes) enum TextType { TEXT_TYPE_BOOL, TEXT_TYPE_NUMBER, + TEXT_TYPE_CHRONO, TEXT_TYPE_STRING, TEXT_TYPE_OTHER, }; template <class T> struct GetTextType : std::integral_constant<TextType, - std::is_same_v<T, bool> ? TEXT_TYPE_BOOL : - IsStringLikeV<T> ? TEXT_TYPE_STRING : //string before number to correctly handle char/wchar_t -> this was an issue with Loki only! - IsArithmetic<T>::value ? TEXT_TYPE_NUMBER : // + std::is_same_v<T, bool> ? TEXT_TYPE_BOOL : + IsStringLikeV<T> ? TEXT_TYPE_STRING : //string before number to correctly handle char/wchar_t -> this was an issue with Loki only! + IsArithmetic<T>::value ? TEXT_TYPE_NUMBER : // + IsChronoDuration<T>::value ? TEXT_TYPE_CHRONO : TEXT_TYPE_OTHER> {}; //###################################################################################### @@ -165,6 +182,20 @@ struct ConvertText<T, TEXT_TYPE_NUMBER> } }; +template <class T> +struct ConvertText<T, TEXT_TYPE_CHRONO> +{ + void writeText(const T& value, std::string& output) const + { + output = numberTo<std::string>(value.count()); + } + bool readText(const std::string& input, T& value) const + { + value = T(stringTo<typename T::rep>(input)); + return true; + } +}; + //partial specialization: handle conversion for all string-like types! template <class T> struct ConvertText<T, TEXT_TYPE_STRING> diff --git a/zenXml/zenxml/dom.h b/zenXml/zenxml/dom.h index 8793d5bd..d95f5aa1 100755 --- a/zenXml/zenxml/dom.h +++ b/zenXml/zenxml/dom.h @@ -237,7 +237,7 @@ public: std::pair<AttrIter, AttrIter> getAttributes() const { return { attributes_.begin(), attributes_.end() }; } //swap two elements while keeping references to parent. -> disabled documentation extraction - void swapSubtree(XmlElement& other) + void swapSubtree(XmlElement& other) noexcept { name_ .swap(other.name_); value_ .swap(other.value_); @@ -283,8 +283,8 @@ public: ///Default constructor setting up an empty XML document with a standard declaration: <?xml version="1.0" encoding="utf-8" ?> XmlDoc() {} - XmlDoc(XmlDoc&& tmp) { swap(tmp); } - XmlDoc& operator=(XmlDoc&& tmp) { swap(tmp); return *this; } + XmlDoc(XmlDoc&& tmp) noexcept { swap(tmp); } + XmlDoc& operator=(XmlDoc&& tmp) noexcept { swap(tmp); return *this; } //Setup an empty XML document /** @@ -342,7 +342,7 @@ public: void setStandalone(const String& standalone) { standalone_ = utfTo<std::string>(standalone); } //Transactionally swap two elements. -> disabled documentation extraction - void swap(XmlDoc& other) + void swap(XmlDoc& other) noexcept { version_ .swap(other.version_); encoding_ .swap(other.encoding_); |