From eb5d3e5df99de2c3d8da2e8bc7b12ed427465dba Mon Sep 17 00:00:00 2001 From: B Stack Date: Sun, 9 Sep 2018 18:53:23 -0400 Subject: pull in latest 10.4 from upstream --- Changelog.txt | 12 + .../Build/Help/images/comparison-settings.png | Bin 16588 -> 27085 bytes .../Build/Help/images/synchronization-settings.png | Bin 29684 -> 33975 bytes FreeFileSync/Build/Languages/arabic.lng | 71 ++-- FreeFileSync/Build/Languages/bulgarian.lng | 83 +++-- FreeFileSync/Build/Languages/chinese_simple.lng | 77 ++-- .../Build/Languages/chinese_traditional.lng | 83 +++-- FreeFileSync/Build/Languages/croatian.lng | 85 +++-- FreeFileSync/Build/Languages/czech.lng | 87 +++-- FreeFileSync/Build/Languages/danish.lng | 71 ++-- FreeFileSync/Build/Languages/dutch.lng | 83 +++-- FreeFileSync/Build/Languages/english_uk.lng | 77 ++-- FreeFileSync/Build/Languages/french.lng | 413 +++++++++++---------- FreeFileSync/Build/Languages/german.lng | 94 ++--- FreeFileSync/Build/Languages/greek.lng | 71 ++-- FreeFileSync/Build/Languages/hebrew.lng | 77 ++-- FreeFileSync/Build/Languages/hindi.lng | 71 ++-- FreeFileSync/Build/Languages/hungarian.lng | 83 +++-- FreeFileSync/Build/Languages/italian.lng | 79 ++-- FreeFileSync/Build/Languages/japanese.lng | 73 ++-- FreeFileSync/Build/Languages/korean.lng | 71 ++-- FreeFileSync/Build/Languages/lithuanian.lng | 71 ++-- FreeFileSync/Build/Languages/norwegian.lng | 87 +++-- FreeFileSync/Build/Languages/polish.lng | 71 ++-- FreeFileSync/Build/Languages/portuguese.lng | 71 ++-- FreeFileSync/Build/Languages/portuguese_br.lng | 71 ++-- FreeFileSync/Build/Languages/romanian.lng | 73 ++-- FreeFileSync/Build/Languages/russian.lng | 71 ++-- FreeFileSync/Build/Languages/slovak.lng | 75 ++-- FreeFileSync/Build/Languages/slovenian.lng | 71 ++-- FreeFileSync/Build/Languages/spanish.lng | 89 +++-- FreeFileSync/Build/Languages/swedish.lng | 71 ++-- FreeFileSync/Build/Languages/turkish.lng | 73 ++-- FreeFileSync/Build/Languages/ukrainian.lng | 73 ++-- FreeFileSync/Build/Resources.zip | Bin 319245 -> 318024 bytes .../Source/RealTimeSync/folder_selector2.cpp | 9 +- FreeFileSync/Source/RealTimeSync/gui_generated.cpp | 2 +- FreeFileSync/Source/RealTimeSync/monitor.cpp | 101 ++--- FreeFileSync/Source/base/algorithm.cpp | 16 +- FreeFileSync/Source/base/algorithm.h | 4 +- FreeFileSync/Source/base/application.cpp | 31 +- FreeFileSync/Source/base/comparison.cpp | 12 +- FreeFileSync/Source/base/comparison.h | 2 +- FreeFileSync/Source/base/dir_exist_async.h | 4 +- FreeFileSync/Source/base/dir_lock.cpp | 13 +- FreeFileSync/Source/base/file_hierarchy.cpp | 40 +- FreeFileSync/Source/base/file_hierarchy.h | 6 +- FreeFileSync/Source/base/generate_logfile.cpp | 32 +- FreeFileSync/Source/base/generate_logfile.h | 14 +- FreeFileSync/Source/base/icon_buffer.cpp | 12 +- FreeFileSync/Source/base/icon_buffer.h | 3 +- FreeFileSync/Source/base/localization.cpp | 48 +-- FreeFileSync/Source/base/parallel_scan.cpp | 10 +- FreeFileSync/Source/base/parallel_scan.h | 2 +- FreeFileSync/Source/base/parse_lng.h | 183 ++++++--- FreeFileSync/Source/base/perf_check.cpp | 35 +- FreeFileSync/Source/base/perf_check.h | 10 +- FreeFileSync/Source/base/process_xml.cpp | 140 ++++--- FreeFileSync/Source/base/process_xml.h | 36 +- FreeFileSync/Source/base/resolve_path.cpp | 21 +- FreeFileSync/Source/base/status_handler.h | 10 +- FreeFileSync/Source/base/status_handler_impl.h | 18 +- FreeFileSync/Source/base/structures.cpp | 11 +- FreeFileSync/Source/base/structures.h | 19 +- FreeFileSync/Source/base/synchronization.cpp | 203 ++++++---- FreeFileSync/Source/base/synchronization.h | 2 +- FreeFileSync/Source/base/versioning.cpp | 75 +++- FreeFileSync/Source/base/versioning.h | 1 + FreeFileSync/Source/fs/abstract.cpp | 102 +++-- FreeFileSync/Source/fs/abstract.h | 23 +- FreeFileSync/Source/fs/concrete_impl.h | 4 +- FreeFileSync/Source/fs/native.cpp | 12 +- FreeFileSync/Source/fs/native.h | 3 + FreeFileSync/Source/ui/batch_config.cpp | 57 +-- FreeFileSync/Source/ui/batch_status_handler.cpp | 26 +- FreeFileSync/Source/ui/batch_status_handler.h | 13 +- FreeFileSync/Source/ui/cfg_grid.cpp | 38 +- FreeFileSync/Source/ui/cfg_grid.h | 15 +- FreeFileSync/Source/ui/file_grid.cpp | 13 +- FreeFileSync/Source/ui/file_view.cpp | 4 +- FreeFileSync/Source/ui/file_view.h | 5 +- FreeFileSync/Source/ui/folder_pair.h | 14 +- FreeFileSync/Source/ui/folder_selector.cpp | 10 +- FreeFileSync/Source/ui/gui_generated.cpp | 230 ++++++------ FreeFileSync/Source/ui/gui_generated.h | 46 +-- FreeFileSync/Source/ui/gui_status_handler.cpp | 14 +- FreeFileSync/Source/ui/gui_status_handler.h | 12 +- FreeFileSync/Source/ui/log_panel.cpp | 10 +- FreeFileSync/Source/ui/main_dlg.cpp | 103 +++-- FreeFileSync/Source/ui/main_dlg.h | 6 +- FreeFileSync/Source/ui/progress_indicator.cpp | 28 +- FreeFileSync/Source/ui/small_dlgs.cpp | 6 +- FreeFileSync/Source/ui/sync_cfg.cpp | 110 ++++-- FreeFileSync/Source/ui/sync_cfg.h | 15 +- FreeFileSync/Source/ui/tree_grid.cpp | 2 +- FreeFileSync/Source/ui/tree_grid.h | 1 - FreeFileSync/Source/ui/version_check.cpp | 6 +- FreeFileSync/Source/version/version.h | 2 +- wx+/choice_enum.h | 4 +- wx+/dc.h | 11 +- wx+/focus.h | 29 +- wx+/graph.cpp | 4 +- wx+/graph.h | 29 +- wx+/grid.cpp | 56 ++- wx+/grid.h | 27 +- wx+/image_resources.cpp | 4 +- wx+/image_tools.cpp | 149 ++++---- wx+/image_tools.h | 2 +- wx+/rtl.h | 5 +- xBRZ/src/xbrz.cpp | 4 +- zen/file_access.cpp | 24 +- zen/file_access.h | 8 +- zen/format_unit.h | 1 - zen/legacy_compiler.h | 71 +--- zen/optional.h | 114 ------ zen/recycler.cpp | 2 +- zen/ring_buffer.h | 2 +- zen/shell_execute.h | 2 +- zen/socket.h | 2 +- zen/stl_tools.h | 30 +- zen/thread.h | 15 +- zen/utf.h | 23 +- zen/zstring.cpp | 4 +- zen/zstring.h | 2 +- zenXml/zenxml/cvrt_struc.h | 26 +- zenXml/zenxml/cvrt_text.h | 37 +- zenXml/zenxml/dom.h | 8 +- 127 files changed, 2884 insertions(+), 2594 deletions(-) delete mode 100755 zen/optional.h 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 index 242f558a..9f638a4e 100755 Binary files a/FreeFileSync/Build/Help/images/comparison-settings.png and b/FreeFileSync/Build/Help/images/comparison-settings.png differ diff --git a/FreeFileSync/Build/Help/images/synchronization-settings.png b/FreeFileSync/Build/Help/images/synchronization-settings.png index 501b3db1..ae2b4be3 100755 Binary files a/FreeFileSync/Build/Help/images/synchronization-settings.png and b/FreeFileSync/Build/Help/images/synchronization-settings.png differ 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 @@ global config file: ملف الخيارات العام: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -أي عدد من ملفات تكوين FreeFileSync بامتداد .ffs_gui أو/و .ffs_batch. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +أي عدد من ملفات تكوين FreeFileSync بامتداد "ffs_gui" أو/و "ffs_batch". Any number of alternative directory pairs for at most one config file. أي عدد من أزواج المسارات البديلة من أجل ملف خيارات واحد. @@ -342,6 +342,9 @@ Update attributes on right تحديث السمات على اليسار +Warning +تحذير + Items processed: معالجة العناصر: @@ -351,6 +354,9 @@ Total time: مجموع الوقت: +Stopped +توقف + Cleaning up log files: تنظيف ملفات السجل: @@ -394,6 +400,15 @@ Unable to connect to %x. لا يمكن الاتصال بـ %x. +Completed successfully +تم بنجاح + +Completed with warnings +تم بتحذيرات + +Completed with errors +تم بأخطاء + Cannot access the Volume Shadow Copy Service. لا يمكن الوصول إلى خدمة "نسخ الظل لوحدة التخزين". @@ -702,8 +717,8 @@ Actual: %y bytes 3. Press 'Start'. 3. اضغط على 'ابدأ'. -To get started just import a .ffs_batch file. -للبدء قم باستيراد ملف .ffs_batch. +To get started just import a "ffs_batch" file. +للبدء قم باستيراد ملف "ffs_batch". Folders to watch: المجلدات للمتابعة: @@ -785,24 +800,9 @@ The command is triggered if: System: Shut down النظام: إيقاف التشغيل -Stopped -توقف - -Completed with errors -تم بأخطاء - -Completed with warnings -تم بتحذيرات - -Warning -تحذير - Nothing to synchronize لا يوجد شيء للمزامنة -Completed successfully -تم بنجاح - Executing command %x تنفيذ الأمر %x @@ -858,6 +858,9 @@ The command is triggered if: Last sync أخر مزامنة +Log +السجل + Folder المجلد @@ -933,6 +936,9 @@ The command is triggered if: Save as &batch job... &حفظ كمهمة دفعية... +Show &log + + Start &comparison بدأ الم&قارنة @@ -1331,6 +1337,9 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again إعادة إظهار جميع التنبهات و نوافذ الحوار التي تم إخفاؤها +Remove old log files after x days: + + Customize context menu: تخصيص القائمة المحلية: @@ -1421,6 +1430,15 @@ This guarantees a consistent state even in case of a serious error. Highlight Configurations تمييز التكوينات +Info +معلومات + +No log entries + + +Select all +اختيار الجميع + &Options &خيارات @@ -1674,21 +1692,12 @@ This guarantees a consistent state even in case of a serious error. Comparing content... مقارنة المحتوى... -Info -معلومات - -Select all -اختيار الجميع - &Continue &مواصلة Progress التقدم -Log -السجل - Thank you, %x, for your donation and support! شكرا لك، %x, للتبرع والدعم. @@ -1926,6 +1935,9 @@ This guarantees a consistent state even in case of a serious error. Unable to register to receive system messages. تعذر التسجيل لاستقبال رسائل النظام. +The %x installation option is only available in the FreeFileSync Donation Edition. +خيار التحميل %x متوفر فقط فى نسخة التبرعات من FreeFileSync. + Cannot find system function %x. لا يمكن العثور على وظيفة نظام %x. @@ -2090,9 +2102,6 @@ This guarantees a consistent state even in case of a serious error. Please choose the local installation type or select a different folder for installation. الرجاء اختيار نوع التثبيت المحلي أو اختيار مجلد آخر للتثبيت. -The %x installation option is only available in the FreeFileSync Donation Edition. -خيار التحميل %x متوفر فقط فى نسخة التبرعات من FreeFileSync. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. احصل على نسخة التبرعات بالميزات الأضافية وساعد ليكون FreeFileSync خالى من الأعلانات. 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 @@ global config file: глобален конфигурационен файл: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Произволен брой FreeFileSync-конфигурационни-файлове .ffs_gui и/или .ffs_batch. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Произволен брой FreeFileSync-конфигурационни-файлове "ffs_gui" и/или "ffs_batch". Any number of alternative directory pairs for at most one config file. Произволен брой алтернативни двойки директории за най-много един конфигурационен файл. @@ -334,6 +334,9 @@ Update attributes on right Актуализирай атрибутите на десния елемент +Warning +Предупреждение + Items processed: Обработени елементи: @@ -343,6 +346,9 @@ Total time: Общо време: +Stopped +Спряно + Cleaning up log files: Почистване на log-файловете: @@ -382,6 +388,15 @@ Unable to connect to %x. Не може да се свърже със %x. +Completed successfully +Завърши успешно + +Completed with warnings +Завърши с предупреждения + +Completed with errors +Завърши с грешки + Cannot access the Volume Shadow Copy Service. Не може да получи достъп до услуга Volume Shadow Copy. @@ -457,12 +472,6 @@ Creating a Volume Shadow Copy for %x... Създава се Volume Shadow Copy за %x... -Searching for excess file versions: -Търси излишни файлови версии: - -Removing excess file versions: -Премахва излишни файлови версии: - Cannot find folder %x. Не е намерена папка %x. @@ -514,6 +523,12 @@ Generating database... Създава база данни... +Searching for excess file versions: +Търси излишни файлови версии: + +Removing excess file versions: +Премахва излишни файлови версии: + Unable to create time stamp for versioning: Не може да маркира времето на версиите: @@ -678,8 +693,8 @@ Actual: %y bytes 3. Press 'Start'. 3. Натиснете 'Старт'. -To get started just import a .ffs_batch file. -За старт просто импортирайте файл .ffs_batch. +To get started just import a "ffs_batch" file. +За старт просто импортирайте файл "ffs_batch". Folders to watch: Папки за следене: @@ -761,24 +776,9 @@ The command is triggered if: System: Shut down Система: Изключване -Stopped -Спряно - -Completed with errors -Завърши с грешки - -Completed with warnings -Завърши с предупреждения - -Warning -Предупреждение - Nothing to synchronize Няма нищо за синхронизиране -Completed successfully -Завърши успешно - Executing command %x Изпълнява команда %x @@ -830,6 +830,9 @@ The command is triggered if: Last sync Последна синхронизация +Log +Протокол + Folder Папка @@ -905,6 +908,9 @@ The command is triggered if: Save as &batch job... Запази &като пакетна задача... +Show &log + + Start &comparison Почни &сравняване @@ -1303,6 +1309,9 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again Покажи всички постоянно скрити диалози и предупреждения отново +Remove old log files after x days: + + Customize context menu: Настрой контекстното меню: @@ -1393,6 +1402,15 @@ This guarantees a consistent state even in case of a serious error. Highlight Configurations Маркирай Конфигурациите +Info +Информация + +No log entries + + +Select all +Маркирай всичко + &Options &Опции @@ -1630,21 +1648,12 @@ This guarantees a consistent state even in case of a serious error. Comparing content... Сравнява съдържанието на файлове... -Info -Информация - -Select all -Маркирай всичко - &Continue &Продължи Progress Прогрес -Log -Протокол - Thank you, %x, for your donation and support! %x, благодаря за Вашето дарение и подкрепа! @@ -1870,6 +1879,9 @@ This guarantees a consistent state even in case of a serious error. Unable to register to receive system messages. Не се регистрира за получаване на системни съобщения. +The %x installation option is only available in the FreeFileSync Donation Edition. +Опцията %x инсталиране е възможна само за FreeFileSync Дарителско Издание. + Cannot find system function %x. Не намира системната функция %x. @@ -2026,9 +2038,6 @@ This guarantees a consistent state even in case of a serious error. Please choose the local installation type or select a different folder for installation. Моля, изберете локален тип инсталация или пък друга папка за инсталация. -The %x installation option is only available in the FreeFileSync Donation Edition. -Опцията %x инсталиране е възможна само за FreeFileSync Дарителско Издание. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Вземете Дарителско Издание с бонуси и помогнете FreeFileSync да остане свободна. 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 @@ global config file: 全局配置文件: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -任意数目的 FreeFileSync .ffs_gui 和/或 .ffs_batch 配置文件. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +任意数目的 FreeFileSync "ffs_gui" 和/或 "ffs_batch" 配置文件. Any number of alternative directory pairs for at most one config file. 任意数目的替代目录对的最多一个配置文件. @@ -235,10 +235,10 @@ 数据库文件已损坏: Cannot write file %x. -无法写入文件 %x . +无法写入文件 %x. Cannot read file %x. -无法读取文件 %x . +无法读取文件 %x. The database files do not yet contain information about the last synchronization. 此数据库文件并未包含 有关最后同步的信息. @@ -332,6 +332,9 @@ Update attributes on right 更新右侧的文件属性 +Warning +警告 + Items processed: 已处理的项目: @@ -341,6 +344,9 @@ Total time: 总共时间: +Stopped +已停止 + Cleaning up log files: 正在清理日志文件: @@ -379,6 +385,15 @@ Unable to connect to %x. 无法连接到 %x. +Completed successfully +成功完成 + +Completed with warnings +已完成但有警告 + +Completed with errors +已完成但有错误 + Cannot access the Volume Shadow Copy Service. 无法访问卷影复制服务. @@ -672,8 +687,8 @@ Actual: %y bytes 3. Press 'Start'. 3. 点击'开始'. -To get started just import a .ffs_batch file. -要开始只需导入一个 .ffs_batch文件. +To get started just import a "ffs_batch" file. +要开始只需导入一个 "ffs_batch"文件. Folders to watch: 要监视的文件夹: @@ -755,24 +770,9 @@ The command is triggered if: System: Shut down 系统: 关机 -Stopped -已停止 - -Completed with errors -已完成但有错误 - -Completed with warnings -已完成但有警告 - -Warning -警告 - Nothing to synchronize 没有什么可同步 -Completed successfully -成功完成 - Executing command %x 正在执行命令 %x @@ -823,6 +823,9 @@ The command is triggered if: Last sync 最后同步 +Log +日志 + Folder 文件夹 @@ -898,6 +901,9 @@ The command is triggered if: Save as &batch job... 另存为批处理作业(&B)... +Show &log + + Start &comparison 开始比较(&C) @@ -1293,6 +1299,9 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again 重新显示所有被永久性隐藏的 对话框和警告信息 +Remove old log files after x days: + + Customize context menu: 自定义右键菜单: @@ -1383,6 +1392,15 @@ This guarantees a consistent state even in case of a serious error. Highlight Configurations 突出显示配置文件 +Info +信息 + +No log entries + + +Select all +选择全部 + &Options 选项(&O) @@ -1512,7 +1530,7 @@ This guarantees a consistent state even in case of a serious error. FreeFileSync批处理文件 Do you want to save changes to %x? -是否要保存修改到 %x ? +是否要保存修改到 %x? Never save &changes 不再保存更改(&C) @@ -1616,21 +1634,12 @@ This guarantees a consistent state even in case of a serious error. Comparing content... 正在比较文件内容... -Info -信息 - -Select all -选择全部 - &Continue 继续(&C) Progress 进度 -Log -日志 - Thank you, %x, for your donation and support! 谢谢你, %x , 对我们的捐款和支持! @@ -1853,6 +1862,9 @@ This guarantees a consistent state even in case of a serious error. Unable to register to receive system messages. 无法注册以接收系统信息. +The %x installation option is only available in the FreeFileSync Donation Edition. +安装选项 %x 只在 FreeFileSync 捐赠版有效. + Cannot find system function %x. 无法找到系统功能 %x. @@ -2007,9 +2019,6 @@ This guarantees a consistent state even in case of a serious error. Please choose the local installation type or select a different folder for installation. 请选择本地安装类型, 或选择用于安装的不同文件夹. -The %x installation option is only available in the FreeFileSync Donation Edition. -安装选项 %x 只在 FreeFileSync 捐赠版有效. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. 获取有额外功能的捐赠版本, 并帮助保持 FreeFileSync 无广告. 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 @@ global config file: 全域配置檔案: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -FreeFileSync .ffs_gui和(或).ffs_batch配置檔案的任意數量。 +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +FreeFileSync "ffs_gui"和(或)"ffs_batch"配置檔案的任意數量。 Any number of alternative directory pairs for at most one config file. 任意數量的備用配對目錄為最多一個配置檔案。 @@ -332,6 +332,9 @@ Update attributes on right 更新右邊的屬性 +Warning +警告 + Items processed: 已處理項目: @@ -341,6 +344,9 @@ Total time: 全部時間: +Stopped +已停止 + Cleaning up log files: 清除日誌檔: @@ -379,6 +385,15 @@ Unable to connect to %x. 無法連接到 %x。 +Completed successfully +成功完成 + +Completed with warnings +已完成但有警告 + +Completed with errors +已完成但有錯誤 + Cannot access the Volume Shadow Copy Service. 無法存取磁碟區陰影複製服務。 @@ -454,12 +469,6 @@ Creating a Volume Shadow Copy for %x... 正在建立磁碟區陰影複製為 %x… -Searching for excess file versions: -正在搜尋多餘的檔案版本: - -Removing excess file versions: -正在刪除多餘的檔案版本: - Cannot find folder %x. 找不到資料夾 %x。 @@ -511,6 +520,12 @@ Generating database... 正在產生資料庫… +Searching for excess file versions: +正在搜尋多餘的檔案版本: + +Removing excess file versions: +正在刪除多餘的檔案版本: + Unable to create time stamp for versioning: 無法建立時間戳記的版本控制: @@ -672,8 +687,8 @@ Actual: %y bytes 3. Press 'Start'. 3. 按下「開始」。 -To get started just import a .ffs_batch file. -若要開始只要導入一個.ffs_batch檔。 +To get started just import a "ffs_batch" file. +若要開始只要導入一個"ffs_batch"檔。 Folders to watch: 要監看的資料夾: @@ -755,24 +770,9 @@ The command is triggered if: System: Shut down 系統:關機 -Stopped -已停止 - -Completed with errors -已完成但有錯誤 - -Completed with warnings -已完成但有警告 - -Warning -警告 - Nothing to synchronize 沒有東西可以同步 -Completed successfully -成功完成 - Executing command %x 執行命令 %x @@ -823,6 +823,9 @@ The command is triggered if: Last sync 上次同步 +Log +日誌 + Folder 資料夾 @@ -898,6 +901,9 @@ The command is triggered if: Save as &batch job... 另存為批次工作(&B)… +Show &log + + Start &comparison 開始比對(&C) @@ -1296,6 +1302,9 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again 再次顯示所有永久 隱藏的對話框和警告訊息 +Remove old log files after x days: + + Customize context menu: 自訂內容功能表: @@ -1386,6 +1395,15 @@ This guarantees a consistent state even in case of a serious error. Highlight Configurations 突顯配置 +Info +訊息 + +No log entries + + +Select all +全選 + &Options 選項(&O) @@ -1619,21 +1637,12 @@ This guarantees a consistent state even in case of a serious error. Comparing content... 正在比對内容… -Info -訊息 - -Select all -全選 - &Continue 繼續(&C) Progress 進度 -Log -日誌 - Thank you, %x, for your donation and support! %x, 感謝您的贊助與支持! @@ -1856,6 +1865,9 @@ This guarantees a consistent state even in case of a serious error. Unable to register to receive system messages. 無法註冊以接收系統訊息。 +The %x installation option is only available in the FreeFileSync Donation Edition. +%x 安裝選項僅在FreeFileSync贊助版可用。 + Cannot find system function %x. 找不到系統功能 %x。 @@ -2010,9 +2022,6 @@ This guarantees a consistent state even in case of a serious error. Please choose the local installation type or select a different folder for installation. 請選擇本機安裝類型或選擇不同的安裝資料夾。 -The %x installation option is only available in the FreeFileSync Donation Edition. -%x 安裝選項僅在FreeFileSync贊助版可用。 - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. 取得贊助版的獎勵功能,並協助維持FreeFileSync無廣告。 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 @@ global config file: globalna config datoteka: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Bilo koji broj FreeFileSync .ffs_gui i/ili .ffs_batch konfiguracijskih datoteka. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Bilo koji broj FreeFileSync "ffs_gui" i/ili "ffs_batch" konfiguracijskih datoteka. Any number of alternative directory pairs for at most one config file. Bilo koji broj alternativnih parova mapa za najmanje jednu config datoteku. @@ -336,6 +336,9 @@ Update attributes on right Osvježi atribute desno +Warning +Upozorenje + Items processed: Obrađene stavke: @@ -345,6 +348,9 @@ Total time: Ukupno vrijeme: +Stopped +Zaustavljeno + Cleaning up log files: Brisanje log datoteka: @@ -385,6 +391,15 @@ Unable to connect to %x. Nije moguće povezivanje na %x. +Completed successfully +Dovršeno uspješno + +Completed with warnings +Dovršeno s upozorenjima + +Completed with errors +Dovršeno s pogreškama + Cannot access the Volume Shadow Copy Service. Ne mogu pristupiti Voulme Shadow Copy servisu. @@ -460,12 +475,6 @@ Creating a Volume Shadow Copy for %x... Kreiranje Volume Shadow Copy za %x... -Searching for excess file versions: -Traženje višestrukih verzija datoteka: - -Removing excess file versions: -Uklanjanje višestrukih verzija datoteka: - Cannot find folder %x. Ne mogu pronaći mapu %x. @@ -517,6 +526,12 @@ Generating database... Izrađivanje baze podataka... +Searching for excess file versions: +Traženje višestrukih verzija datoteka: + +Removing excess file versions: +Uklanjanje višestrukih verzija datoteka: + Unable to create time stamp for versioning: Nije moguća izrada vremenske oznake za označavanje: @@ -684,8 +699,8 @@ Stvarno: %y bajta 3. Press 'Start'. 3. Pretisnite 'Start'. -To get started just import a .ffs_batch file. -Da biste započeli jednostavno uvezite .ffs_batch datoteku. +To get started just import a "ffs_batch" file. +Da biste započeli jednostavno uvezite "ffs_batch" datoteku. Folders to watch: Mape za nadziranje: @@ -767,24 +782,9 @@ Naredba će biti pokrenuta ako se: System: Shut down Sistem: Isključivanje -Stopped -Zaustavljeno - -Completed with errors -Dovršeno s pogreškama - -Completed with warnings -Dovršeno s upozorenjima - -Warning -Upozorenje - Nothing to synchronize Ništa za sinkronizirati -Completed successfully -Dovršeno uspješno - Executing command %x Izvršavanje naredbe %x @@ -837,6 +837,9 @@ Naredba će biti pokrenuta ako se: Last sync Zadnja sinkr +Log +Izvješće + Folder Mapa @@ -912,6 +915,9 @@ Naredba će biti pokrenuta ako se: Save as &batch job... Spremi kao &Slijedni zadatak... +Show &log + + Start &comparison Započni &usporedbu @@ -1137,7 +1143,7 @@ Naredba će biti pokrenuta ako se: Broj pokušaja: Delay (in seconds): -Odgoda (u sekundama) : +Odgoda (u sekundama): Run a command after synchronization: Pokreni naredbu nakon sinkronizacije: @@ -1310,6 +1316,9 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. Show all permanently hidden dialogs and warning messages again Prikaži sve trajno skrivene prozore i poruke upozorenja ponovno +Remove old log files after x days: + + Customize context menu: Prilagodite kontekstni izbornik: @@ -1400,6 +1409,15 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. Highlight Configurations Istakni postavke +Info +Info + +No log entries + + +Select all +Odaberi sve + &Options &Opcije @@ -1641,21 +1659,12 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. Comparing content... Uspoređujem sadržaj... -Info -Info - -Select all -Odaberi sve - &Continue &Nastavi Progress Napredak -Log -Izvješće - Thank you, %x, for your donation and support! Hvala Vam, %x, na vašoj donaciji i podršci! @@ -1884,6 +1893,9 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. Unable to register to receive system messages. Nije moguće registriranje za primanje sistemskih poruka. +The %x installation option is only available in the FreeFileSync Donation Edition. +%x opcija instalacije je dostupna samo u FreeFileSync Donatorskoj verziji. + Cannot find system function %x. Ne mogu pronaći sistemsku funkciju %x. @@ -2042,9 +2054,6 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. Please choose the local installation type or select a different folder for installation. Molimo odaberite lokalnu instalaciju ili odaberite drugu mapu za instalaciju. -The %x installation option is only available in the FreeFileSync Donation Edition. -%x opcija instalacije je dostupna samo u FreeFileSync Donatorskoj verziji. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Preuzmite donatorsku verziju sa bonus opcijama i pomognite da FreeFileSync ostane bez reklama. 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 @@ Mazání symbolického odkazu %x Checking recycle bin availability for folder %x... -Kontrola Koše na složku %x ... +Kontrola Koše na složku %x... The recycle bin is not supported by the following folders. Deleted or overwritten files will not be able to be restored: Pro některé složky nelze použít Koš a proto následující soubory budou odstraněny trvale, bez možnosti obnovy: @@ -100,8 +100,8 @@ global config file: konfigurační soubory: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Libovolný počet konfiguračních souborů FreeFileSync typu .ffs_gui a/nebo .ffs_batch. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Libovolný počet konfiguračních souborů FreeFileSync typu "ffs_gui" a/nebo "ffs_batch". Any number of alternative directory pairs for at most one config file. Libovolný počet alternativních párů adresářů na alespoň jednu konfiguraci. @@ -336,6 +336,9 @@ Update attributes on right Nastavit vlastnosti vpravo +Warning +Varování + Items processed: Zpracováno položek: @@ -345,6 +348,9 @@ Total time: Celkový čas: +Stopped +Zastaveno + Cleaning up log files: Vyčištění žurnálů: @@ -385,6 +391,15 @@ Unable to connect to %x. Nepodařilo se připojit k %x. +Completed successfully +Úspěšně dokončeno + +Completed with warnings +Dokončeno s varováním + +Completed with errors +Dokončeno s chybou + Cannot access the Volume Shadow Copy Service. Nepodařil se přístup ke službě Stínové kopie. @@ -460,12 +475,6 @@ Creating a Volume Shadow Copy for %x... Vytváření Stínové kopie pro %x... -Searching for excess file versions: -Hledání nadbytečných verzí: - -Removing excess file versions: -Odstraňování nadbytečných verzí: - Cannot find folder %x. Nelze najít složku %x. @@ -517,6 +526,12 @@ Generating database... Vytváření databáze... +Searching for excess file versions: +Hledání nadbytečných verzí: + +Removing excess file versions: +Odstraňování nadbytečných verzí: + Unable to create time stamp for versioning: Nelze vytvořit časové značky verzování: @@ -684,8 +699,8 @@ Aktuálně: %y b 3. Press 'Start'. 3. Zmáčkněte 'Start'. -To get started just import a .ffs_batch file. -Můžete načíst také konfigurační soubor .ffs_batch. +To get started just import a "ffs_batch" file. +Můžete načíst také konfigurační soubor "ffs_batch". Folders to watch: Sledovaná složka: @@ -767,24 +782,9 @@ Příkaz je spuštěn když: System: Shut down Vypnutí počítače -Stopped -Zastaveno - -Completed with errors -Dokončeno s chybou - -Completed with warnings -Dokončeno s varováním - -Warning -Varování - Nothing to synchronize Není co synchronizovat -Completed successfully -Úspěšně dokončeno - Executing command %x Spuštění příkazu %x @@ -837,6 +837,9 @@ Příkaz je spuštěn když: Last sync Naposledy +Log +Žurnál zpracování + Folder Složka @@ -912,6 +915,9 @@ Příkaz je spuštěn když: Save as &batch job... Uložit jako &dávku... +Show &log + + Start &comparison Začít &porovnání @@ -1307,6 +1313,9 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again Zobrazit znovu všechny trvale skryté dialogy a varovná hlášení +Remove old log files after x days: + + Customize context menu: Přizpůsobit kontextovou nabídku: @@ -1397,6 +1406,15 @@ This guarantees a consistent state even in case of a serious error. Highlight Configurations Zvýraznění položek +Info +Info + +No log entries + + +Select all +Vybrat vše + &Options Nastavení &programu @@ -1621,7 +1639,7 @@ This guarantees a consistent state even in case of a serious error. Seznam souborů exportován Searching for program updates... -Hledání aktualizací programu ... +Hledání aktualizací programu... Paused Pauza @@ -1638,21 +1656,12 @@ This guarantees a consistent state even in case of a serious error. Comparing content... Porovnávání obsahu... -Info -Info - -Select all -Vybrat vše - &Continue &Pokračovat Progress Průběh -Log -Žurnál zpracování - Thank you, %x, for your donation and support! Děkuji, %x, za příspěvek a podporu! @@ -1881,6 +1890,9 @@ This guarantees a consistent state even in case of a serious error. Unable to register to receive system messages. Nepodařilo se zaregistrovat k odběru systémových zpráv. +The %x installation option is only available in the FreeFileSync Donation Edition. +%x instalace je k dispozici pouze v předplacené verzi FreeFileSync Donation Edition. + Cannot find system function %x. Nelze najít systémovou funkci %x. @@ -2039,9 +2051,6 @@ This guarantees a consistent state even in case of a serious error. Please choose the local installation type or select a different folder for installation. Prosím zvolte lokální typ instalace nebo vyberte jinou složku. -The %x installation option is only available in the FreeFileSync Donation Edition. -%x instalace je k dispozici pouze v předplacené verzi FreeFileSync Donation Edition. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Získat předplacenou verzi s funikcemi navíc a pomoci tím udržet FreeFileSync bez reklam. 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 @@ global config file: global indstillingsfil: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Vilkårligt antal FreeFileSync .ffs_gui og/eller .ffs_batch indstillingsfiler. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Vilkårligt antal FreeFileSync "ffs_gui" og/eller "ffs_batch" indstillingsfiler. Any number of alternative directory pairs for at most one config file. Vilkårligt antal alternative mappepar til højst en indstillingsfil. @@ -334,6 +334,9 @@ Update attributes on right Opdater attributter mod højre +Warning +Advarsel + Items processed: Emner behandlet: @@ -343,6 +346,9 @@ Total time: Samlet tid: +Stopped +Afbrudt + Cleaning up log files: Rydder logfiler: @@ -382,6 +388,15 @@ Unable to connect to %x. Kan ikke kontakte %x. +Completed successfully +Gennemført + +Completed with warnings +Gennemført med advarsler + +Completed with errors +Gennemført med fejl + Cannot access the Volume Shadow Copy Service. VSS tjenesten er ikke tilgængelig. @@ -678,8 +693,8 @@ Aktuel: %y byte 3. Press 'Start'. 3. Klik 'Start'. -To get started just import a .ffs_batch file. -Importer en .ffs_batchfil (Fil > Åben...) for at komme igang. +To get started just import a "ffs_batch" file. +Importer en "ffs_batch"fil (Fil > Åben...) for at komme igang. Folders to watch: Jobbets mapper: @@ -761,24 +776,9 @@ Kommandoen udføres hvis: System: Shut down System: Luk -Stopped -Afbrudt - -Completed with errors -Gennemført med fejl - -Completed with warnings -Gennemført med advarsler - -Warning -Advarsel - Nothing to synchronize Alt er synkroniseret -Completed successfully -Gennemført - Executing command %x Kører kommandoen %x @@ -830,6 +830,9 @@ Kommandoen udføres hvis: Last sync Sidste synk +Log +Log + Folder Mappe @@ -905,6 +908,9 @@ Kommandoen udføres hvis: Save as &batch job... Gem som &batchfil... +Show &log + + Start &comparison Start &analyse @@ -1303,6 +1309,9 @@ Sikrer processen ved alvorlige fejl. Show all permanently hidden dialogs and warning messages again Vis skjulte advarsler og beskeder igen +Remove old log files after x days: + + Customize context menu: Tilpas kontekstmenu: @@ -1393,6 +1402,15 @@ Sikrer processen ved alvorlige fejl. Highlight Configurations Fremhæv indstillinger +Info +Info + +No log entries + + +Select all +Vælg alt + &Options &Indstillinger @@ -1630,21 +1648,12 @@ Sikrer processen ved alvorlige fejl. Comparing content... Analyserer indhold... -Info -Info - -Select all -Vælg alt - &Continue &Fortsæt Progress Fremskridt -Log -Log - Thank you, %x, for your donation and support! Tak %x for donationen og støtten! @@ -1870,6 +1879,9 @@ Sikrer processen ved alvorlige fejl. Unable to register to receive system messages. Kunne ikke registrere modtagelse af systembeskeder. +The %x installation option is only available in the FreeFileSync Donation Edition. +Installationsmuligheden %x er kun mulig i FreeFileSync Donation Edition. + Cannot find system function %x. Kan ikke finde systemfunktionen %x. @@ -2026,9 +2038,6 @@ Sikrer processen ved alvorlige fejl. Please choose the local installation type or select a different folder for installation. Installer normalt, eller vælg en anden mappe. -The %x installation option is only available in the FreeFileSync Donation Edition. -Installationsmuligheden %x er kun mulig i FreeFileSync Donation Edition. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Hent donationsudgave med flere funktioner, og hold FreeFileSync fri for annoncer. 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 @@ global config file: globaal configuratiebestand: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Een willekeurig aantal FreeFileSync. ffs_gui en/of .ffs_batch configuratiebestanden. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Een willekeurig aantal FreeFileSync. ffs_gui en/of "ffs_batch" configuratiebestanden. Any number of alternative directory pairs for at most one config file. Een willekeurig aantal alternatieve mapparen voor maximaal een configuratiebestand. @@ -334,6 +334,9 @@ Update attributes on right Update kenmerken aan de rechterzijde +Warning +Waarschuwing + Items processed: Items verwerkt: @@ -343,6 +346,9 @@ Total time: Totale tijd: +Stopped +Gestopt + Cleaning up log files: Logbestanden opschonen: @@ -382,6 +388,15 @@ Unable to connect to %x. Kan geen verbinding maken met %x. +Completed successfully +Succesvol voltooid + +Completed with warnings +Voltooid met waarschuwingen + +Completed with errors +Voltooid met fouten + Cannot access the Volume Shadow Copy Service. Kan geen toegang krijgen tot de Volume Shadow Copy Service. @@ -457,12 +472,6 @@ Creating a Volume Shadow Copy for %x... Maken van een volume schaduwkopie voor %x... -Searching for excess file versions: -Zoeken naar overtollige bestandsversies: - -Removing excess file versions: -Overmatige bestandsversies verwijderen: - Cannot find folder %x. Kan de map %x niet vinden. @@ -514,6 +523,12 @@ Generating database... Genereren database... +Searching for excess file versions: +Zoeken naar overtollige bestandsversies: + +Removing excess file versions: +Overmatige bestandsversies verwijderen: + Unable to create time stamp for versioning: Kan geen tijdstempel maken voor versiebeheer: @@ -678,8 +693,8 @@ Werkelijk: %y bytes 3. Press 'Start'. 3. Druk op 'Start'. -To get started just import a .ffs_batch file. -Om te beginnen gewoon een .ffs_batch-bestand importeren. +To get started just import a "ffs_batch" file. +Om te beginnen gewoon een "ffs_batch"-bestand importeren. Folders to watch: Mappen om te bekijken: @@ -761,24 +776,9 @@ De opdracht wordt geactiveerd als: System: Shut down Systeem: Uitschakelen -Stopped -Gestopt - -Completed with errors -Voltooid met fouten - -Completed with warnings -Voltooid met waarschuwingen - -Warning -Waarschuwing - Nothing to synchronize Niets om te synchroniseren -Completed successfully -Succesvol voltooid - Executing command %x Opdracht %x uitvoeren @@ -830,6 +830,9 @@ De opdracht wordt geactiveerd als: Last sync Laatste synchronisatie +Log +Logboek + Folder Map @@ -905,6 +908,9 @@ De opdracht wordt geactiveerd als: Save as &batch job... Opslaan als &batchverwerking... +Show &log + + Start &comparison Start &vergelijking @@ -1303,6 +1309,9 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. Show all permanently hidden dialogs and warning messages again Alle permanent verborgen dialogen en waarschuwingsberichten opnieuw weergeven +Remove old log files after x days: + + Customize context menu: Contextmenu aanpassen: @@ -1393,6 +1402,15 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. Highlight Configurations Markeer Configuraties +Info +Info + +No log entries + + +Select all +Alles selecteren + &Options &Opties @@ -1630,21 +1648,12 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. Comparing content... Inhoud vergelijken... -Info -Info - -Select all -Alles selecteren - &Continue &Doorgaan Progress Voortgang -Log -Logboek - Thank you, %x, for your donation and support! Dank u, %x, voor uw donatie en ondersteuning! @@ -1870,6 +1879,9 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. Unable to register to receive system messages. Niet in staat om de ontvangen systeemberichten te registreren. +The %x installation option is only available in the FreeFileSync Donation Edition. +De %x installatieoptie is alleen beschikbaar in de FreeFileSync Donatie Editie. + Cannot find system function %x. Kan de systeemfunctie %x niet vinden. @@ -2026,9 +2038,6 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. Please choose the local installation type or select a different folder for installation. Kies voor een lokale installatie of selecteer een andere map voor de installatie. -The %x installation option is only available in the FreeFileSync Donation Edition. -De %x installatieoptie is alleen beschikbaar in de FreeFileSync Donatie Editie. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Ontvang de Donatie Editie met bonuseigenschappen en help FreeFileSync advertentie-vrij te houden. 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 @@ n == 1 ? 0 : 1 +No log entries + + +Remove old log files after x days: + + +Show &log + + Both sides have changed since last synchronization. Both sides have changed since last synchronisation. @@ -100,8 +109,8 @@ global config file: global config file: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. Any number of alternative directory pairs for at most one config file. Any number of alternative directory pairs for at most one config file. @@ -127,6 +136,9 @@ The folders are created automatically when needed. The folders are created automatically when needed. +Scanning: +Scanning: + Comparison finished: Comparison finished: @@ -331,6 +343,9 @@ Update attributes on right Update attributes on right +Warning +Warning + Items processed: Elements processed: @@ -340,6 +355,9 @@ Total time: Total time: +Stopped +Stopped + Cleaning up log files: Cleaning up log files: @@ -358,9 +376,6 @@ %x threads -Scanning: -Scanning: - Cannot read directory %x. Cannot read directory %x. @@ -382,6 +397,15 @@ Unable to connect to %x. Unable to connect to %x. +Completed successfully +Completed successfully + +Completed with warnings +Completed with warnings + +Completed with errors +Completed with errors + Cannot access the Volume Shadow Copy Service. Cannot access the Volume Shadow Copy Service. @@ -678,8 +702,8 @@ Actual: %y bytes 3. Press 'Start'. 3. Press 'Start'. -To get started just import a .ffs_batch file. -To get started just import a .ffs_batch file. +To get started just import a "ffs_batch" file. +To get started just import a "ffs_batch" file. Folders to watch: Folders to watch: @@ -761,24 +785,9 @@ The command is triggered if: System: Shut down System: Shut down -Stopped -Stopped - -Completed with errors -Completed with errors - -Completed with warnings -Completed with warnings - -Warning -Warning - Nothing to synchronize Nothing to synchronise -Completed successfully -Completed successfully - Executing command %x Executing command %x @@ -830,6 +839,9 @@ The command is triggered if: Last sync Last sync +Log +Log + Folder Folder @@ -1393,6 +1405,12 @@ This guarantees a consistent state even in case of a serious error. Highlight Configurations Highlight Configurations +Info +Info + +Select all +Select all + &Options &Options @@ -1630,21 +1648,12 @@ This guarantees a consistent state even in case of a serious error. Comparing content... Comparing content... -Info -Info - -Select all -Select all - &Continue &Continue Progress Progress -Log -Log - Thank you, %x, for your donation and support! Thank you, %x, for your donation and support! @@ -1870,6 +1879,9 @@ This guarantees a consistent state even in case of a serious error. Unable to register to receive system messages. Unable to register to receive system messages. +The %x installation option is only available in the FreeFileSync Donation Edition. +The %x installation option is only available in the FreeFileSync Donation Edition. + Cannot find system function %x. Cannot find system function %x. @@ -2026,9 +2038,6 @@ This guarantees a consistent state even in case of a serious error. Please choose the local installation type or select a different folder for installation. Please choose the local installation type or select a different folder for installation. -The %x installation option is only available in the FreeFileSync Donation Edition. -The %x installation option is only available in the FreeFileSync Donation Edition. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. 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 @@ Les deux côtés ont changé depuis la dernière synchronisation. Cannot determine sync-direction: -Impossible de déterminer le sens de la synchronisation : +Impossible de déterminer le sens de la synchronisation : No change since last synchronization. Aucun changement depuis la dernière synchronisation. @@ -20,7 +20,7 @@ L'entrée de la base de données n'est pas en phase compte tenu des paramètres actuels. Setting default synchronization directions: Old files will be overwritten with newer files. -Directions de la synchronisation par défaut : les anciens fichiers seront remplacés par les nouveaux. +Directions de la synchronisation par défaut : les anciens fichiers seront remplacés par les nouveaux. Creating file %x Création du fichier %x @@ -50,10 +50,10 @@ Suppression du lien symbolique %x Checking recycle bin availability for folder %x... -Contrôle de la disponibilité de la Corbeille pour le dossier %x ... +Contrôle de la disponibilité de la Corbeille pour le dossier %x ... The recycle bin is not supported by the following folders. Deleted or overwritten files will not be able to be restored: -La corbeille n'est pas supportée par les dossiers suivants. Les fichiers supprimés ou modifiés ne pourront pas être restaurés : +La corbeille n'est pas supportée par les dossiers suivants. Les fichiers supprimés ou modifiés ne pourront pas être restaurés : An exception occurred Une erreur s'est produite @@ -89,19 +89,19 @@ Ligne de commande Syntax: -Syntaxe : +Syntaxe : config files: -fichier de configuration : +fichier de configuration : directory répertoire global config file: -fichier de configuration globale : +fichier de configuration globale : -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -N'importe quel nombre de fichiers FreeFileSync .ffs_gui et/ou .ffs_batch. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +N'importe quel nombre de fichiers FreeFileSync "ffs_gui" et/ou "ffs_batch". Any number of alternative directory pairs for at most one config file. N'importe quel nombre de paires de répertoires distincts pour au plus un fichier de configuration. @@ -119,19 +119,19 @@ Impossible de charger le fichier %x. Cannot find the following folders: -Impossible de trouver les dossiers suivants : +Impossible de trouver les dossiers suivants : The following folders do not yet exist: -Les dossiers suivants n'existent plus : +Les dossiers suivants n'existent plus : The folders are created automatically when needed. Les dossiers sont créés automatiquement quand cela est nécessaire. Scanning: -Lecture en cours : +Lecture en cours : Comparison finished: -Comparaison terminée : +Comparaison terminée : 1 item found @@ -146,13 +146,13 @@ Le fichier %x a une date invalide. Date: -Date : +Date : Files have the same date but a different size. Les fichiers ont la même date mais une taille différente. Size: -Taille : +Taille : Content comparison was skipped for excluded files. La comparaison des contenus est sautée pour les fichiers exclus. @@ -167,7 +167,7 @@ Comparaison du contenu des fichiers %x Generating file list... -Génération de la liste des fichiers... +Génération de la liste des fichiers ... Fail-safe file copy Copie de fichiers sécurisée @@ -200,7 +200,7 @@ Vérification de la copie des fichiers Using non-default global settings: -Utilisation des paramètres globaux particuliers : +Utilisation des paramètres globaux particuliers : A folder input field is empty. Un champ dossier est vide. @@ -209,7 +209,7 @@ Le dossier correspondant sera considéré comme vide. Exclude: -Exclure : +Exclure : One base folder of a folder pair is contained in the other one. Un dossier défini dans la paire des dossiers est contenu dans l'autre. @@ -218,7 +218,7 @@ Le dossier sera exclu de la synchronisation par le filtre. Calculating sync directions... -Evaluation du sens des synchronisations ... +Evaluation du sens des synchronisations ... Out of memory. Mémoire insuffisante. @@ -227,13 +227,13 @@ La base de données %x n'est pas compatible. Initial synchronization: -Première synchronisation : +Première synchronisation : Database file %x does not yet exist. La base de données %x n'existe plus. Database file is corrupted: -La base de données est abîmée : +La base de données est abîmée : Cannot write file %x. Impossible d'écrire le fichier %x. @@ -245,13 +245,13 @@ La base de données ne contient plus aujourd'hui d'informations sur la dernière synchronisation. Loading file %x... -Chargement du fichier %x ... +Chargement du fichier %x ... Saving file %x... -Enregistrement du fichier %x ... +Enregistrement du fichier %x ... Searching for folder %x... -Recherche du dossier %x ... +Recherche du dossier %x ... Timeout while searching for folder %x. La recherche du dossier %x a expirée. @@ -263,13 +263,13 @@ Impossible de lire les attributs du fichier %x. Waiting while directory is locked: -En attente tant que le répertoire est verrouillé : +En attente tant que le répertoire est verrouillé : Lock owner: -Propriétaire du verrou : +Propriétaire du verrou : Detecting abandoned lock... -Détection de verrouillage abandonné ... +Détection de verrouillage abandonné ... 1 sec @@ -334,23 +334,29 @@ Update attributes on right Mise à jour des attributs à droite +Warning +Attention + Items processed: -Élements traités : +Élements traités : Items remaining: -Élements restants : +Élements restants : Total time: -Durée totale : +Durée totale : + +Stopped +Arrêté Cleaning up log files: -Nettoyage des journaux : +Nettoyage des journaux : Error parsing file %x, row %y, column %z. Erreur lors de l'analyse du fichier %x, ligne %y, colonne %z. Cannot set directory locks for the following folders: -Impossible de verrouiller les répertoires des dossiers suivants : +Impossible de verrouiller les répertoires des dossiers suivants : 1 thread @@ -382,6 +388,15 @@ Unable to connect to %x. Impossible de se connecter à %x. +Completed successfully +Terminé avec succès + +Completed with warnings +Terminé avec observations + +Completed with errors +Terminé avec erreurs + Cannot access the Volume Shadow Copy Service. Impossible d'accéder au service Volume Shadow Copy. @@ -416,7 +431,7 @@ Personnaliser Multiple... -Multiple ... +Multiple ... Cannot write file attributes of %x. Impossible d'écrire les attributs de fichier de %x. @@ -428,7 +443,7 @@ %x et %y ont des contenus différents. Data verification error: -Erreur de contrôle des données : +Erreur de contrôle des données : Moving file %x to %y Déplacement du fichier %x vers %y @@ -455,13 +470,7 @@ L'élément source %x n'a pas été trouvé Creating a Volume Shadow Copy for %x... -Création d'un Volume Shadow Copy pour %x ... - -Searching for excess file versions: -Recherche des versions de fichiers excédentaires : - -Removing excess file versions: -Suppression des versions de fichiers excédentaires : +Création d'un Volume Shadow Copy pour %x ... Cannot find folder %x. Dossier %x introuvable. @@ -479,19 +488,19 @@ Veuillez entrer le dossier destinataire pour la gestion des versions. The following items have unresolved conflicts and will not be synchronized: -Les éléments suivants sont en conflits non résolus et ne seront pas synchronisés : +Les éléments suivants sont en conflits non résolus et ne seront pas synchronisés : The following folders are significantly different. Please check that the correct folders are selected for synchronization. Les dossiers suivants sont assez différents. Veuillez contrôler que ce sont les bons dossiers qui sont sélectionnés pour la synchronisation. Not enough free disk space available in: -Espace disque insuffisant sur : +Espace disque insuffisant sur : Required: -Requis : +Requis : Available: -Disponible : +Disponible : Some files will be synchronized as part of multiple base folders. Certains fichiers seront synchronisés comme éléments de plusieurs dossiers de base. @@ -500,22 +509,28 @@ 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. Versioning folder: -Dossier de gestion des versions : +Dossier de gestion des versions : Base folder: -Dossier de base : +Dossier de base : The versioning folder is contained in a base folder. Le dossier de version est situé dans le dossier de base. Synchronizing folder pair: -Synchronisation de la paire de dossiers : +Synchronisation de la paire de dossiers : Generating database... -Génération de la base de données... +Génération de la base de données ... + +Searching for excess file versions: +Recherche des versions de fichiers excédentaires : + +Removing excess file versions: +Suppression des versions de fichiers excédentaires : Unable to create time stamp for versioning: -Impossible de générer l'horodatage pour la gestion des versions : +Impossible de générer l'horodatage pour la gestion des versions : Unexpected size of data stream. @@ -523,9 +538,9 @@ Expected: %x bytes Actual: %y bytes -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 Cannot write permissions of %x. @@ -577,7 +592,7 @@ Trouvé : %y octets Impossible de trouver le périphérique %x. Type of item %x is not supported: -Le type de l'élément %x n'est pas accepté : +Le type de l'élément %x n'est pas accepté : Cannot delete symbolic link %x. Impossible de supprimer le lien symbolique %x. @@ -586,7 +601,7 @@ Trouvé : %y octets Impossible de calculer l'espace libre du disque %x. Incorrect command line: -Ligne de commande incorrecte : +Ligne de commande incorrecte : The server does not support authentication via %x. Le serveur refuse l'authentification par %x. @@ -613,7 +628,7 @@ Trouvé : %y octets Active connections: %x -Connexions actives : %x +Connexions actives : %x Failed to open SFTP channel number %x. Impossible d'ouvrir le port SFTP %x. @@ -646,10 +661,10 @@ Trouvé : %y octets &Nouveau &Open... -&Ouvrir... +&Ouvrir ... Save &as... -S&auvegarder sous... +S&auvegarder sous ... E&xit &Quitter @@ -667,7 +682,7 @@ Trouvé : %y octets &Aide Usage: -Utilisation : +Utilisation : 1. Select folders to watch. 1. Choisir les dossiers à examiner. @@ -678,11 +693,11 @@ Trouvé : %y octets 3. Press 'Start'. 3. Cliquez sur 'Démarrer'. -To get started just import a .ffs_batch file. -Pour démarrer, il faut importer un fichier .ffs_batch. +To get started just import a "ffs_batch" file. +Pour démarrer, il faut importer un fichier "ffs_batch". Folders to watch: -Dossiers à surveiller : +Dossiers à surveiller : Add folder Ajout d'un dossier @@ -694,13 +709,13 @@ Trouvé : %y octets Parcourir Idle time (in seconds): -Durée d'inactivité (en secondes) : +Durée d'inactivité (en secondes) : Idle time between last detected change and execution of command Délai entre le dernier changement détecté et la dernière exécution de la commande Command line: -Ligne de commande : +Ligne de commande : The command is triggered if: @@ -708,9 +723,9 @@ The command is triggered if: - new folders arrive (e.g. USB stick insert) -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) Start @@ -720,7 +735,7 @@ La commande est déclenchée si : A propos de Build: %x -Généré : %x +Généré : %x All files Tous les fichiers @@ -729,13 +744,13 @@ La commande est déclenchée si : Synchronisation Automatique The %x protocol does not support directory monitoring: -Le protocole %x ne supporte pas le contrôle d'anuaire : +Le protocole %x ne supporte pas le contrôle d'anuaire : Directory monitoring active Gestion des répertoires active Waiting until directory is available: -En attente de la disponibilité du répertoire : +En attente de la disponibilité du répertoire : &Restore &Restaurer @@ -750,35 +765,20 @@ La commande est déclenchée si : &Réessayer Loading... -Chargement ... +Chargement ... job name nom du job System: Sleep -Système : Mise en veille +Système : Mise en veille System: Shut down -Système : Arrêt - -Stopped -Arrêté - -Completed with errors -Terminé avec erreurs - -Completed with warnings -Terminé avec observations - -Warning -Attention +Système : Arrêt Nothing to synchronize Rien à synchroniser -Completed successfully -Terminé avec succès - Executing command %x Exécution de la commande %x @@ -804,7 +804,7 @@ La commande est déclenchée si : &Tout ignorer Retrying operation... -Opération retentée ... +Opération retentée ... Serious Error Erreur Grave @@ -830,6 +830,9 @@ La commande est déclenchée si : Last sync Dernière synchro +Log +Log + Folder Dossier @@ -903,7 +906,10 @@ La commande est déclenchée si : &Sauvegarder Save as &batch job... -Enregistrer en tant que fichier de &commandes ... +Enregistrer en tant que fichier de &commandes ... + +Show &log + Start &comparison Démarrer la &comparaison @@ -930,10 +936,10 @@ La commande est déclenchée si : &Langue &Find... -&Rechercher... +&Rechercher ... &Export file list... -&Exportation de la liste des fichiers ... +&Exportation de la liste des fichiers ... &Reset layout &Réinitialiser la disposition @@ -972,7 +978,7 @@ La commande est déclenchée si : Fermer la barre de recherche Find: -Rechercher : +Rechercher : Match case Respect de la casse @@ -981,22 +987,22 @@ La commande est déclenchée si : Nouveau Open... -Ouvrir ... +Ouvrir ... Save Sauvegarder Save as... -Sauvegarder sous ... +Sauvegarder sous ... View type: -Type de vue : +Type de vue : Select view: -Choisir la vue : +Choisir la vue : Statistics: -Statistiques : +Statistiques : Number of files and folders that will be deleted Nombre de fichiers et de dossiers qui seront supprimés @@ -1014,19 +1020,19 @@ La commande est déclenchée si : Coordonne la paire de dossiers Folder pair: -Paire de dossiers : +Paire de dossiers : Main settings: -Paramètres principaux : +Paramètres principaux : Use local settings: -Utiliser les paramètres locaux : +Utiliser les paramètres locaux : Select a variant: -Choisir une variante : +Choisir une variante : Include &symbolic links: -Inclure les liens &symboliques : +Inclure les liens &symboliques : &Follow &Poursuivre @@ -1044,25 +1050,25 @@ La commande est déclenchée si : Liste des décalages de temps UTC à ignorer Example: -Exemple : +Exemple : Handle daylight saving time Gérer l'heure d'été Performance improvements: -Amélioration des performances : +Amélioration des performances : Parallel file operations: -Opérations sur les fichiers parallèles : +Opérations sur les fichiers parallèles : How to get best performance? -Comment améliorer les performances ? +Comment améliorer les performances ? Local settings: -Paramètres locaux : +Paramètres locaux : Include: -Inclure : +Inclure : Show examples Afficher les exemples @@ -1071,16 +1077,16 @@ La commande est déclenchée si : Choisissez les règles de filtrage pour exclure certains fichiers de la synchronisation. Entrez les chemins des fichiers par rapport à leur paire de dossiers. File size: -Taille du fichier : +Taille du fichier : Minimum: -Minimum : +Minimum : Maximum: -Maximum : +Maximum : Time span: -Intervalle de temps : +Intervalle de temps : C&lear &Effacer @@ -1100,7 +1106,7 @@ La commande est déclenchée si : Delete files: -Supprimer les fichiers : +Supprimer les fichiers : &Recycle bin &Corbeille @@ -1115,43 +1121,43 @@ La commande est déclenchée si : Déplacer les fichiers vers un dossier utilisateur Naming convention: -Convention de nommage : +Convention de nommage : Limit file versions: -Limiter les versions de fichiers : +Limiter les versions de fichiers : Last x days: -Derniers x jours : +Derniers x jours : Ignore errors Ignorer les erreurs Retry count: -Nombre de tentatives : +Nombre de tentatives : Delay (in seconds): -Délai (en secondes) : +Délai (en secondes) : Run a command after synchronization: -Exécuter une commande après la synchronisation : +Exécuter une commande après la synchronisation : OK OK Enter your login details: -Entrez vos identifiants : +Entrez vos identifiants : Connection type: -Type de connexion : +Type de connexion : Server name or IP address: -Nom du serveur ou adresse IP : +Nom du serveur ou adresse IP : Port: -Port : +Port : Encryption: -Cryptage : +Cryptage : &Disabled &Désactivé @@ -1160,7 +1166,7 @@ La commande est déclenchée si : &Explicite SSL/TLS Authentication: -Authentication : +Authentication : &Password Mot de &Passe @@ -1172,46 +1178,46 @@ La commande est déclenchée si : Agent &SSH User name: -Nom de l'utilisateur : +Nom de l'utilisateur : Private key file: -Fichier clé personnel : +Fichier clé personnel : &Show password &Afficher le mot de passe Directory on server: -Répertoire sur le serveur : +Répertoire sur le serveur : SFTP channels per connection: -Ports SFTP par connexion : +Ports SFTP par connexion : Detect server limit Détection des limites du serveur Select a directory on the server: -Choisir un répertoire sur le serveur : +Choisir un répertoire sur le serveur : Select Folder Choisir un Dossier Start synchronization now? -Démarrer la synchronisation maintenant ? +Démarrer la synchronisation maintenant ? Variant: -Variante : +Variante : &Don't show this dialog again &Ne plus afficher cette boîte de dialogue Items found: -Élements trouvés : +Élements trouvés : Time remaining: -Temps restant : +Temps restant : Time elapsed: -Temps écoulé : +Temps écoulé : Bytes Octets @@ -1220,13 +1226,13 @@ La commande est déclenchée si : Eléments Synchronizing... -Synchronisation en cours ... +Synchronisation en cours ... Minimize to notification area Réduction à la zone de notification When finished: -A la fin : +A la fin : Auto-close Fermeture automatique @@ -1241,7 +1247,7 @@ La commande est déclenchée si : Arrêt Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -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 +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 Progress dialog: Fenêtre de progression: @@ -1262,13 +1268,13 @@ La commande est déclenchée si : Arrêter la synchronisation à la première erreur Save log: -Sauvegarde du fichier log : +Sauvegarde du fichier log : Limit number of log files: -Limiter le nombre de journaux : +Limiter le nombre de journaux : How can I schedule a batch job? -Comment planifier un fichier de commandes ? +Comment planifier un fichier de commandes ? &Keep relative paths &Conserver les chemins relatifs @@ -1303,8 +1309,11 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Show all permanently hidden dialogs and warning messages again Réafficher en permanence les boîtes de dialogue et les avertissements +Remove old log files after x days: + + Customize context menu: -Personnaliser le menu contextuel : +Personnaliser le menu contextuel : Description Description @@ -1325,7 +1334,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Email If you like FreeFileSync: -Si vous aimez FreeFileSync : +Si vous aimez FreeFileSync : Support with a donation Soutenir avec un don @@ -1337,37 +1346,37 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Détails pour les donations Source code written in C++ using: -Code source écrit en C++ utilisant : +Code source écrit en C++ utilisant : Published under the GNU General Public License Publié sous licence GNU General Public License Many thanks for localization: -Un grand merci pour la traduction à : +Un grand merci pour la traduction à : Activate the FreeFileSync Donation Edition by one of the following methods: -Activez la FreeFileSync Donation Edition par l'une des méthodes suivantes : +Activez la FreeFileSync Donation Edition par l'une des méthodes suivantes : 1. Activate via internet now: -1. Activez par internet maintenant : +1. Activez par internet maintenant : Activate online Activez en ligne 2. Retrieve an offline activation key from the following URL: -2. Récupérer une clé d'activation hors ligne à partir de l'URL suivante : +2. Récupérer une clé d'activation hors ligne à partir de l'URL suivante : &Copy to clipboard & Copie dans le presse-papiers Enter activation key: -Entrez la clé d'activation : +Entrez la clé d'activation : Activate offline Activez hors ligne Highlight configurations that have not been run for more than the following number of days: -Signaler les configurations non exécutées depuis le nombre de jours suivant : +Signaler les configurations non exécutées depuis le nombre de jours suivant : Synchronization Settings Configuration de la Synchronisation @@ -1393,6 +1402,15 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Highlight Configurations Signaler les configurations +Info +Info + +No log entries + + +Select all +Tout sélectionner + &Options &Options @@ -1421,7 +1439,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. &Afficher les détails FreeFileSync %x is available! -FreeFileSync %x est disponible ! +FreeFileSync %x est disponible ! Local path not available for %x. Chemin local invalide pour %x. @@ -1434,8 +1452,8 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Do you really want to execute the command %y for %x items? -Êtes-vous sûr de vouloir exécuter la commande %y pour %x élément ? -Êtes-vous sûr de vouloir exécuter la commande %y pour %x éléments ? +Êtes-vous sûr de vouloir exécuter la commande %y pour %x élément ? +Êtes-vous sûr de vouloir exécuter la commande %y pour %x éléments ? &Execute @@ -1469,16 +1487,16 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Set direction: -Choix de la direction : +Choix de la direction : multiple selection sélection multiple Include via filter: -Inclure à l'aide du filtre : +Inclure à l'aide du filtre : Exclude via filter: -Exclure à l'aide du filtre : +Exclure à l'aide du filtre : Include temporarily Inclure temporairement @@ -1487,7 +1505,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Exclure temporairement &Copy to... -&Copier vers ... +&Copier vers ... &Delete &Supprimer @@ -1499,7 +1517,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Exclure tout Show icons: -Afficher les icônes : +Afficher les icônes : Small Petit @@ -1511,7 +1529,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Grand Select time span... -Choisir un intervalle de temps ... +Choisir un intervalle de temps ... Donation Edition Edition Donation @@ -1526,7 +1544,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. FreeFileSync batch Do you want to save changes to %x? -Voulez-vous enregistrer les modifications dans %x ? +Voulez-vous enregistrer les modifications dans %x ? Never save &changes Ne jamais sauvegarder les &modifications @@ -1538,7 +1556,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Masquer la configuration Highlight... -Signaler ... +Signaler ... Clear filter Effacer les filtres @@ -1613,28 +1631,22 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Liste des fichiers exportée Searching for program updates... -Recherche de mises à jour ... +Recherche de mises à jour ... Paused En pause Stop requested... -Arrêt demandé ... +Arrêt demandé ... Initializing... -Initialisation ... +Initialisation ... Scanning... -Lecture en cours ... +Lecture en cours ... Comparing content... -Comparaison du contenu ... - -Info -Info - -Select all -Tout sélectionner +Comparaison du contenu ... &Continue &Continuer @@ -1642,23 +1654,20 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Progress Progression -Log -Log - Thank you, %x, for your donation and support! -Merci %x pour votre don et votre aide ! +Merci %x pour votre don et votre aide ! Connections Connexions Recommended range: -Plage recommandée : +Plage recommandée : Password: -Mot de passe : +Mot de passe : Key password: -Clé du mot de passe : +Clé du mot de passe : Please enter a file path. Veuillez entrer un chemin d'accès. @@ -1668,8 +1677,8 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Copy the following %x items to another folder? -Copier %x élément dans un autre dossier ? -Copier les %x éléments suivants dans un autre dossier ? +Copier %x élément dans un autre dossier ? +Copier les %x éléments suivants dans un autre dossier ? Please enter a target folder. @@ -1680,8 +1689,8 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Do you really want to move the following %x items to the recycle bin? -Etes-vous sûr de vouloir mettre à la Corbeille %x élément suivant ? -Etes-vous sûr de vouloir mettre à la Corbeille les %x éléments suivants ? +Etes-vous sûr de vouloir mettre à la Corbeille %x élément suivant ? +Etes-vous sûr de vouloir mettre à la Corbeille les %x éléments suivants ? Move @@ -1692,15 +1701,15 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Do you really want to delete the following %x items? -Voulez-vous vraiment supprimer %x élément ? -Voulez-vous vraiment supprimer ces %x éléments ? +Voulez-vous vraiment supprimer %x élément ? +Voulez-vous vraiment supprimer ces %x éléments ? Copy DACL, SACL, Owner, Group Copie DACL, SACL, propriétaire et groupe Integrate external applications into context menu. The following macros are available: -Inclure les applications externes dans le menu contextuel. Les macros suivantes sont disponibles : +Inclure les applications externes dans le menu contextuel. Les macros suivantes sont disponibles : Full file or folder path Chemin complet du fichier ou du dossier @@ -1721,7 +1730,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Afficher toutes les boîtes de dialogue Downloading update... -Téléchargement de la mise à jour ... +Téléchargement de la mise à jour ... Identify equal files by comparing modification time and size. Reconnaître les fichiers identiques à l'aide de leur taille et de leur date. @@ -1793,13 +1802,13 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Ajouter un horodatage à chaque nom de fichier On completion: -A la fin : +A la fin : On errors: -En cas d'erreur : +En cas d'erreur : On success: -En cas de succès : +En cas de succès : Main config Configuration principale @@ -1826,13 +1835,13 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Impossible de retrouver les informations de mise à jour. Automatic updates: -Mise à jour automatique : +Mise à jour automatique : Check for Program Updates Recherche des Mises à Jour Auto-update now or download manually from the FreeFileSync home page? -Mise à jour automatique maintenant ou téléchargement manuel à partir du site de FreeFileSync ? +Mise à jour automatique maintenant ou téléchargement manuel à partir du site de FreeFileSync ? &Auto-update Mise à jour &automatique @@ -1841,7 +1850,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. &Téléchargement Download now? -Télécharger maintenant ? +Télécharger maintenant ? &Download &Télécharger @@ -1850,7 +1859,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. FreeFileSync est à jour. Cannot find current FreeFileSync version number online. A newer version is likely available. Check manually now? -Le numéro de la version courante de FreeFileSync est introuvable sur internet. Une nouvelle version est probablement disponible. Voulez-vous vérifier maintenant ? +Le numéro de la version courante de FreeFileSync est introuvable sur internet. Une nouvelle version est probablement disponible. Voulez-vous vérifier maintenant ? &Check &Contrôle @@ -1870,6 +1879,9 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Unable to register to receive system messages. Impossible d'enregistrer la réception des messages système. +The %x installation option is only available in the FreeFileSync Donation Edition. +L'option d'installation %x n'est disponible que dans FreeFileSync Donation Edition. + Cannot find system function %x. Impossible de trouver la fonction système %x. @@ -1880,7 +1892,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Impossible de gérer le répertoire %x. The file is locked by another process: -Le fichier est verrouillé par un autre process : +Le fichier est verrouillé par un autre process : Cannot read security context of %x. Impossible de lire les paramètres de sécurité de %x. @@ -1943,7 +1955,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Échec de la vérification de la Corbeille pour le dossier %x. The following XML elements could not be read: -Les éléments XML suivants ne peuvent être lus : +Les éléments XML suivants ne peuvent être lus : Configuration file %x is incomplete. The missing elements will be set to their default values. Le fichier de configuration %x est incomplet. Les éléments manquants sont fixés à leur valeur par défaut. @@ -1955,7 +1967,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Sélectionnez les composants que vous voulez installer. Select installation type: -Choisir le type d'installation : +Choisir le type d'installation : Local Locale @@ -1982,10 +1994,10 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Copier seulement les fichiers Choose a directory for installation: -Choisir le répertoire d'installation : +Choisir le répertoire d'installation : Create shortcuts: -Création de raccourcis sur : +Création de raccourcis sur : Desktop le Bureau @@ -2026,9 +2038,6 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Please choose the local installation type or select a different folder for installation. Veuillez choisir le type d'installation locale ou sélectionner un autre dossier pour cette installation. -The %x installation option is only available in the FreeFileSync Donation Edition. -L'option d'installation %x n'est disponible que dans FreeFileSync Donation Edition. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Obtenez l'Édition Don avec des fonctionnalités bonus et aidez FreeFileSync à rester sans publicité. 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 @@ n == 1 ? 0 : 1 -No log entries -Keine Protokolleinträge - -Remove old log files after x days: -Alte Protokolldateien nach x Tagen entfernen: +Default log path: +Standardprotokollpfad: -Show &log -&Protokoll zeigen +&Override default log path: +&Standardprotokollpfad überschreiben: Both sides have changed since last synchronization. Beide Seiten wurden seit der letzten Synchronisation verändert. @@ -109,8 +106,8 @@ global config file: Globale Konfigurationsdatei: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Beliebige Anzahl von FreeFileSync .ffs_gui und/oder .ffs_batch Konfigurationsdateien. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Beliebige Anzahl von FreeFileSync "ffs_gui" und/oder "ffs_batch" Konfigurationsdateien. Any number of alternative directory pairs for at most one config file. Beliebige Anzahl von alternativen Verzeichnispaaren für maximal eine Konfigurationsdatei. @@ -642,24 +639,6 @@ Tatsächlich: %y bytes Failed to open SFTP channel number %x. Der SFTP Kanal Nummer %x konnte nicht geöffnet werden. - -1 byte -%x bytes - - -1 Byte -%x Bytes - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop Drag && Drop @@ -702,8 +681,8 @@ Tatsächlich: %y bytes 3. Press 'Start'. 3. 'Start' drücken. -To get started just import a .ffs_batch file. -Zum Schnelleinstieg einfach eine .ffs_batch Datei importieren. +To get started just import a "ffs_batch" file. +Zum Schnelleinstieg einfach eine "ffs_batch" Datei importieren. Folders to watch: Zu überwachende Ordner: @@ -773,6 +752,24 @@ Die Befehlszeile wird ausgelöst, wenn: &Retry &Wiederholen + +1 byte +%x bytes + + +1 Byte +%x Bytes + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... Lade... @@ -917,6 +914,9 @@ Die Befehlszeile wird ausgelöst, wenn: Save as &batch job... Speichern als Batch&auftrag... +Show &log +&Protokoll zeigen + Start &comparison &Vergleich starten @@ -1061,6 +1061,15 @@ Die Befehlszeile wird ausgelöst, wenn: Handle daylight saving time Sommerzeit berücksichtigen +Ignore errors +Fehler ignorieren + +Retry count: +Wiederholungen: + +Delay (in seconds): +Verzögerung (in Sekunden): + Performance improvements: Leistungsverbesserungen: @@ -1135,15 +1144,6 @@ Die Befehlszeile wird ausgelöst, wenn: Last x days: Letzte x Tage: -Ignore errors -Fehler ignorieren - -Retry count: -Wiederholungen: - -Delay (in seconds): -Verzögerung (in Sekunden): - Run a command after synchronization: Befehl nach Synchronisation ausführen: @@ -1273,12 +1273,6 @@ Die Befehlszeile wird ausgelöst, wenn: Stop synchronization at first error Synchronisation beim ersten Fehler stoppen -Save log: -Protokoll speichern: - -Limit number of log files: -Anzahl der Protokolldateien begrenzen: - How can I schedule a batch job? Wie können Batchaufträge in den Taskplaner eingetragen werden? @@ -1315,6 +1309,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. Show all permanently hidden dialogs and warning messages again Alle dauerhaft versteckten Fenster und Warnmeldungen wieder anzeigen +Remove old log files after x days: +Alte Protokolldateien nach x Tagen entfernen: + Customize context menu: Kontextmenü anpassen: @@ -1408,6 +1405,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. Info Info +No log entries +Keine Protokolleinträge + Select all Alle auswählen @@ -1879,6 +1879,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. Unable to register to receive system messages. Die Registrierung zum Empfang von Systemmeldungen ist fehlgeschlagen. +The %x installation option is only available in the FreeFileSync Donation Edition. +Die %x Installationsoption ist nur in der FreeFileSync Spendenversion verfügbar. + Cannot find system function %x. Die Systemfunktion %x wurde nicht gefunden. @@ -2035,9 +2038,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. Please choose the local installation type or select a different folder for installation. Bitte wählen Sie den lokalen Installationstyp oder einen anderen Ordner für die Installation. -The %x installation option is only available in the FreeFileSync Donation Edition. -Die %x Installationsoption ist nur in der FreeFileSync Spendenversion verfügbar. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Jetzt die Spendenversion mit Bonusfunktionen holen und mithelfen, FreeFileSync werbefrei zu halten. 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 @@ global config file: αρχείο γενικών ρυθμίσεων: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Οποιοσδήποτε αριθμός από αρχεία παραμέτρων .ffs_gui ή/και .ffs_batch του FreeFileSync. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Οποιοσδήποτε αριθμός από αρχεία παραμέτρων "ffs_gui" ή/και "ffs_batch" του FreeFileSync. Any number of alternative directory pairs for at most one config file. Οποιοσδήποτε αριθμός από εναλλακτικά ζεύγη υποκαταλόγων για το πολύ ένα αρχείο ρυθμίσεων. @@ -334,6 +334,9 @@ Update attributes on right Ενημέρωση των χαρακτηριστικών στα δεξιά +Warning +Προειδοποίηση + Items processed: Επεξεργάστηκαν στοιχεία: @@ -343,6 +346,9 @@ Total time: Συνολική διάρκεια: +Stopped +Διακοπή + Cleaning up log files: Καθάρισμα αρχείων καταγραφής: @@ -382,6 +388,15 @@ Unable to connect to %x. Δεν μπορεί να πραγματοποιηθεί σύνδεση με το %x. +Completed successfully +Ολοκληρώθηκε επιτυχώς + +Completed with warnings +Ολοκληρώθηκε με προειδοποιήσεις + +Completed with errors +Ολοκληρώθηκε με σφάλματα + Cannot access the Volume Shadow Copy Service. Δεν είναι δυνατή η πρόσβαση στην Υπηρεσία Σκιώδους Αντίγραφου Τόμου. @@ -678,8 +693,8 @@ Actual: %y bytes 3. Press 'Start'. 3. Πατήστε το 'Έναρξη'. -To get started just import a .ffs_batch file. -Για να ξεκινήσετε μπορείτε απλά να εισάγετε ένα αρχείο .ffs_batch. +To get started just import a "ffs_batch" file. +Για να ξεκινήσετε μπορείτε απλά να εισάγετε ένα αρχείο "ffs_batch". Folders to watch: Φάκελοι για παρακολούθηση: @@ -761,24 +776,9 @@ The command is triggered if: System: Shut down Σύστημα: Τερματισμός λειτουργίας -Stopped -Διακοπή - -Completed with errors -Ολοκληρώθηκε με σφάλματα - -Completed with warnings -Ολοκληρώθηκε με προειδοποιήσεις - -Warning -Προειδοποίηση - Nothing to synchronize Δεν υπάρχει τίποτα προς συγχρονισμό -Completed successfully -Ολοκληρώθηκε επιτυχώς - Executing command %x Εκτέλεση εντολής %x @@ -830,6 +830,9 @@ The command is triggered if: Last sync Τελ. συγχρ. +Log +Καταγραφή + Folder Φάκελος @@ -905,6 +908,9 @@ The command is triggered if: Save as &batch job... Αποθήκευση ως δέσ&μη ενεργειών... +Show &log + + Start &comparison Έ&ναρξη σύγκρισης @@ -1303,6 +1309,9 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again Επανεμφάνιση όλων των μόνιμα κρυμμένων ειδοποιήσεων και προειδοποιητικών μηνυμάτων +Remove old log files after x days: + + Customize context menu: Προσαρμογή μενού περιβάλλοντος: @@ -1393,6 +1402,15 @@ This guarantees a consistent state even in case of a serious error. Highlight Configurations Επισήμανση Παραμέτρων +Info +Πληροφορίες + +No log entries + + +Select all +Επιλογή όλων + &Options &Επιλογές @@ -1630,21 +1648,12 @@ This guarantees a consistent state even in case of a serious error. Comparing content... Σύγκριση του περιεχομένου... -Info -Πληροφορίες - -Select all -Επιλογή όλων - &Continue &Συνέχεια Progress Πρόοδος -Log -Καταγραφή - Thank you, %x, for your donation and support! Σας ευχαριστούμε, %x, για τη δωρεά και την υποστήριξή σας! @@ -1870,6 +1879,9 @@ This guarantees a consistent state even in case of a serious error. Unable to register to receive system messages. Δεν μπορεί να καταχωρηθεί η λήψη μηνυμάτων συστήματος. +The %x installation option is only available in the FreeFileSync Donation Edition. +Η επιλογή εγκατάστασης %x είναι διαθέσιμη μόνο στην Έκδοση Δωρητή του FreeFileSync. + Cannot find system function %x. Δεν μπορεί να βρεθεί η λειτουργία συστήματος %x. @@ -2026,9 +2038,6 @@ This guarantees a consistent state even in case of a serious error. Please choose the local installation type or select a different folder for installation. Παρακαλούμε επιλέξτε την τοπική εγκατάσταση ή επιλέξτε έναν διαφορετικό φάκελο για εγκατάσταση. -The %x installation option is only available in the FreeFileSync Donation Edition. -Η επιλογή εγκατάστασης %x είναι διαθέσιμη μόνο στην Έκδοση Δωρητή του FreeFileSync. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Αγοράστε την Έκδοση Δωρητή με περισσότερες δυνατότητες και βοηθήστε να μείνει το FreeFileSync χωρίς διαφημίσεις. 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 @@ n == 1 ? 0 : 1 +No log entries + + +Remove old log files after x days: + + +Show &log + + Both sides have changed since last synchronization. שני הצדדים שונו מאז הסנכרון האחרון. @@ -100,8 +109,8 @@ global config file: קבצי תצורה גלובליים: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -כל כמות של קבצי FreeFileSync .ffs_gui ו\או קבצי קונפיגורציה .ffs_batch. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +כל כמות של קבצי FreeFileSync "ffs_gui" ו\או קבצי קונפיגורציה "ffs_batch". Any number of alternative directory pairs for at most one config file. כל כמות של זוגות מחיצות אלטרנטיביות עבור קובץ תצורה אחד לפחות. @@ -110,7 +119,7 @@ פתח את התצורה הבאה לעריכה בלבד מבלי להפעיל אותה. Path to an alternate GlobalSettings.xml file. -נתיב אל קובץ GlobalSettingd.xml אלטרנטיבי. +נתיב אל קובץ GlobalSettings.xml אלטרנטיבי. Installation files are corrupted. Please reinstall FreeFileSync. קבצי ההתקנה פגומים. בבקשה התקן מחדש את FreeFileSync. @@ -334,6 +343,9 @@ Update attributes on right עדכן תכונות בצד שמאל +Warning +אזהרה + Items processed: אלמנטים עובדו: @@ -343,6 +355,9 @@ Total time: זמן כולל: +Stopped +נעצר + Cleaning up log files: מנקה קבצי יומן: @@ -382,6 +397,15 @@ Unable to connect to %x. לא יכול להתחבר אל %x. +Completed successfully +הסתיים בהצלחה + +Completed with warnings +הסתיים עם אזהרות + +Completed with errors +הסתיים עם שגיאות + Cannot access the Volume Shadow Copy Service. לא ניתן לגשת אל שרות Volume Shadow Copy. @@ -678,8 +702,8 @@ Actual: %y bytes 3. Press 'Start'. 3. לחץ 'הפעל'. -To get started just import a .ffs_batch file. -בכדי להתחיל יבא קובץ .ffs_batch. +To get started just import a "ffs_batch" file. +בכדי להתחיל יבא קובץ "ffs_batch". Folders to watch: תיקיות לצפייה: @@ -761,24 +785,9 @@ The command is triggered if: System: Shut down מערכת: כיבוי -Stopped -נעצר - -Completed with errors -הסתיים עם שגיאות - -Completed with warnings -הסתיים עם אזהרות - -Warning -אזהרה - Nothing to synchronize אין מה לסנכרן -Completed successfully -הסתיים בהצלחה - Executing command %x מבצע פקודה %x @@ -830,6 +839,9 @@ The command is triggered if: Last sync סינכרון אחרון +Log +יומן + Folder תיקייה @@ -1393,6 +1405,12 @@ This guarantees a consistent state even in case of a serious error. Highlight Configurations הדגש תצורות +Info +מידע + +Select all +בחר הכל + &Options &אפשרויות @@ -1421,7 +1439,7 @@ This guarantees a consistent state even in case of a serious error. &הראה פרטים FreeFileSync %x is available! -FreeFileSync %x זמין ! +FreeFileSync %x זמין! Local path not available for %x. נתיב מקומי לא זמין עבור %x. @@ -1622,7 +1640,7 @@ This guarantees a consistent state even in case of a serious error. עצור בקשה... Initializing... -מאתחל ... +מאתחל... Scanning... סורק... @@ -1630,21 +1648,12 @@ This guarantees a consistent state even in case of a serious error. Comparing content... משווה תכולה... -Info -מידע - -Select all -בחר הכל - &Continue &המשך Progress התקדמות -Log -יומן - Thank you, %x, for your donation and support! תודה לך %x, עבור תרומתך ותמיכתך! @@ -1870,6 +1879,9 @@ This guarantees a consistent state even in case of a serious error. Unable to register to receive system messages. לא ניתן להרשם לקבלת הודעות מערכת. +The %x installation option is only available in the FreeFileSync Donation Edition. +אפשרת התקנה %x זמינה רק במהדורת תרומות של FreeFileSync. + Cannot find system function %x. לא יכול למצוא פונקצית מערכת %x. @@ -2026,9 +2038,6 @@ This guarantees a consistent state even in case of a serious error. Please choose the local installation type or select a different folder for installation. בבקשה בחר התקנה מקומית או בחר תיקייה אחרת להתקנה. -The %x installation option is only available in the FreeFileSync Donation Edition. -אפשרת התקנה %x זמינה רק במהדורת תרומות של FreeFileSync. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. קבל את מהדורת התרומות עם תכונות נוספות ועזור לשמר את FreeFileSync נטול-פרסומות. 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 @@ global config file: वैश्विक कॉन्फ़िग फ़ाइल: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -कितने भी FreeFileSync .ffs_gui और/या .ffs_batch कॉन्फ़िगरेशन फ़ाइल्स। +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +कितने भी FreeFileSync "ffs_gui" और/या "ffs_batch" कॉन्फ़िगरेशन फ़ाइल्स। Any number of alternative directory pairs for at most one config file. कितने भी वैकलपिक निर्देशिका जोडे अधिक से अधिक एक कॉन्फ़िग फ़ाइल के लिए। @@ -334,6 +334,9 @@ Update attributes on right दाईं तरफ के गुण अद्यतन करें +Warning +चेतावनी + Items processed: संसाधित आइटम्स: @@ -343,6 +346,9 @@ Total time: कुल समय: +Stopped +रुका + Cleaning up log files: लॉग फाइलों की सफ़ाई जारी: @@ -382,6 +388,15 @@ Unable to connect to %x. %x से कनेक्ट करने में असमर्थ। +Completed successfully +सफलतापूर्वक पूर्ण हुआ + +Completed with warnings +चेतावनियों सहित पूर्ण हुआ + +Completed with errors +त्रुटियों सहित पूर्ण हुआ + Cannot access the Volume Shadow Copy Service. वॉल्यूम शॅडो प्रतिलिपि सर्व्हिस पहुँच प्राप्त नहीं है। @@ -678,8 +693,8 @@ Actual: %y bytes 3. Press 'Start'. 3. 'प्रारंभ' दबाएं। -To get started just import a .ffs_batch file. -प्रारंभ करने के लिए केवल कोई .ffs_batch फ़ाइल आयात करें। +To get started just import a "ffs_batch" file. +प्रारंभ करने के लिए केवल कोई "ffs_batch" फ़ाइल आयात करें। Folders to watch: इन फ़ोलडर्स की निगरानी होगी: @@ -761,24 +776,9 @@ The command is triggered if: System: Shut down सिस्टम: बंद करें -Stopped -रुका - -Completed with errors -त्रुटियों सहित पूर्ण हुआ - -Completed with warnings -चेतावनियों सहित पूर्ण हुआ - -Warning -चेतावनी - Nothing to synchronize सिंक्रनाइज़ करने के लिए कुछ नहीं -Completed successfully -सफलतापूर्वक पूर्ण हुआ - Executing command %x कार्यान्वित आदेश %x @@ -830,6 +830,9 @@ The command is triggered if: Last sync पिछला सिंक +Log +लॉग + Folder निर्देशिका @@ -905,6 +908,9 @@ The command is triggered if: Save as &batch job... बॅच जॉब के रूप में सहेजें (&b)... +Show &log + + Start &comparison तुलना शुरू करें (&c) @@ -1303,6 +1309,9 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again स्थायी रूप से छिपाये संवाद बॉक्सेस और चेतावनी संदेश फिर से दिखाएं +Remove old log files after x days: + + Customize context menu: प्रासंगिक मेनू अनुकूलित करें: @@ -1393,6 +1402,15 @@ This guarantees a consistent state even in case of a serious error. Highlight Configurations कॉन्फ़िगरेशन्स हाइलाइट करें +Info +जानकारी + +No log entries + + +Select all +सभी चुने + &Options विकल्प (&O) @@ -1630,21 +1648,12 @@ This guarantees a consistent state even in case of a serious error. Comparing content... सामग्री की तुलना हो रही है... -Info -जानकारी - -Select all -सभी चुने - &Continue जारी रखें (&C) Progress प्रगति -Log -लॉग - Thank you, %x, for your donation and support! धन्यवाद, %x, आपके दान और समर्थन के लिए! @@ -1870,6 +1879,9 @@ This guarantees a consistent state even in case of a serious error. Unable to register to receive system messages. सिस्टम संदेशों को प्राप्त करने के लिए पंजीकृत करने में असमर्थ। +The %x installation option is only available in the FreeFileSync Donation Edition. +%x स्थापना विकल्प केवल FreeFileSync दान संस्‍करण में ही उपलब्ध है। + Cannot find system function %x. सिस्टम फ़ंकशन %x ढूंढ नहीं सकते। @@ -2026,9 +2038,6 @@ This guarantees a consistent state even in case of a serious error. Please choose the local installation type or select a different folder for installation. स्थापना के लिए कृपया स्थानीय स्थापना प्रकार चयन करें या कोई और निर्देशिका चुनें। -The %x installation option is only available in the FreeFileSync Donation Edition. -%x स्थापना विकल्प केवल FreeFileSync दान संस्‍करण में ही उपलब्ध है। - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. दान संस्‍करण बोनस सुविधाओं के साथ प्राप्त करें और FreeFileSync विज्ञापन-रहित रखने में मदद करें। 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 @@ global config file: globális konfigurációs állomány: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Tetszőleges számú .ffs_gui és/vagy .ffs_batch konfigurációs állomány készíthető a FreeFileSync-hez. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Tetszőleges számú "ffs_gui" és/vagy "ffs_batch" konfigurációs állomány készíthető a FreeFileSync-hez. Any number of alternative directory pairs for at most one config file. Tetszőleges számú alternatív könyvtár-pár legfeljebb egy konfigurációs állományban. @@ -334,6 +334,9 @@ Update attributes on right Attribútumok frissítése a jobb oldalon +Warning +Figyelmeztetés + Items processed: Feldolgozott elemek száma: @@ -343,6 +346,9 @@ Total time: Összes időszükséglet: +Stopped +Leállítva + Cleaning up log files: A log állományok törlése: @@ -382,6 +388,15 @@ Unable to connect to %x. Nem képes %x-hez kapcsolódni. +Completed successfully +Sikeresen végrehajtva + +Completed with warnings +Figyelmeztetések mellett végrehajtva + +Completed with errors +Hibák mellett végrehajtva + Cannot access the Volume Shadow Copy Service. Nem elérhető a Kötet Árnyék-másolat szolgáltatás. @@ -457,12 +472,6 @@ Creating a Volume Shadow Copy for %x... %x számára árnyékmásolat-kötetet készítése... -Searching for excess file versions: -A felesleges fájl-verziók keresése: - -Removing excess file versions: -A felesleges fájl-verziók törlése: - Cannot find folder %x. %x könyvtárat nem találom. @@ -514,6 +523,12 @@ Generating database... Adatbázis generálása... +Searching for excess file versions: +A felesleges fájl-verziók keresése: + +Removing excess file versions: +A felesleges fájl-verziók törlése: + Unable to create time stamp for versioning: Nem képes időbélyegzés létrehozására a verzióképzéshez: @@ -678,8 +693,8 @@ Tényleges: %y bájt 3. Press 'Start'. 3. Nyomd meg a Start gombot. -To get started just import a .ffs_batch file. -Az induláshoz importáljon egy .ffs_batch állományt. +To get started just import a "ffs_batch" file. +Az induláshoz importáljon egy "ffs_batch" állományt. Folders to watch: Figyelendő könyvtárak: @@ -761,24 +776,9 @@ A parancs végrehajtódik, ha: System: Shut down Rendszer: Leállítva -Stopped -Leállítva - -Completed with errors -Hibák mellett végrehajtva - -Completed with warnings -Figyelmeztetések mellett végrehajtva - -Warning -Figyelmeztetés - Nothing to synchronize Nincs mit szinkronizálni -Completed successfully -Sikeresen végrehajtva - Executing command %x %x parancs végrehajtása @@ -830,6 +830,9 @@ A parancs végrehajtódik, ha: Last sync Legutóbbi szinkronizálás +Log +Napló + Folder Könyvtár @@ -905,6 +908,9 @@ A parancs végrehajtódik, ha: Save as &batch job... Mentse &kötegelt feladatként... +Show &log + + Start &comparison Kezdje az &összehasonlítást @@ -1303,6 +1309,9 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. Show all permanently hidden dialogs and warning messages again Mutassa újra az összes ideiglenesen rejtett párbeszédablakot és figyelmeztetést +Remove old log files after x days: + + Customize context menu: Környezeti menü testreszabása: @@ -1393,6 +1402,15 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. Highlight Configurations Jelölje ki a beállításokat +Info +Információ + +No log entries + + +Select all +Összeset kiválasztja + &Options &Beállítások @@ -1630,21 +1648,12 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. Comparing content... Tartalom összehasonlítása... -Info -Információ - -Select all -Összeset kiválasztja - &Continue &Folytat Progress Haladás -Log -Napló - Thank you, %x, for your donation and support! %x, köszönöm Önnek adományát és támogatását! @@ -1870,6 +1879,9 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. Unable to register to receive system messages. Nem tud regisztrálni, hogy megkapja a rendszerüzeneteket. +The %x installation option is only available in the FreeFileSync Donation Edition. +%x telepítési opció csak a FreeFileSync Támogatói Kiadásában érhető el. + Cannot find system function %x. %x rendszerfunkció nem elérhető. @@ -2026,9 +2038,6 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. Please choose the local installation type or select a different folder for installation. Válassza a helyi telepítési módot vagy válasszon másik könytárat a telepítéshez. -The %x installation option is only available in the FreeFileSync Donation Edition. -%x telepítési opció csak a FreeFileSync Támogatói Kiadásában érhető el. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Használd a Támogatói Kiadást bónusz szolgáltatásokkal és segits reklám-mentesen tartani a FreeFileSync-et. 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 @@ global config file: file di configurazione globale: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Qualsiasi numero di FreeFileSync.ffs_gui e/o File di configurazione .ffs_batch. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Qualsiasi numero di FreeFileSync"ffs_gui" e/o File di configurazione "ffs_batch". Any number of alternative directory pairs for at most one config file. Qualsiasi numero di coppie di directory alternative per al massimo un file di configurazione. @@ -269,7 +269,7 @@ Bloccare il proprietario: Detecting abandoned lock... -Rilevamento blocco abbandonato ... +Rilevamento blocco abbandonato... 1 sec @@ -334,6 +334,9 @@ Update attributes on right Aggiorna attributi a destra +Warning +Attenzione + Items processed: Oggetti processati: @@ -343,6 +346,9 @@ Total time: Tempo totale: +Stopped +Arrestato + Cleaning up log files: Pulizia dei file di registro: @@ -382,6 +388,15 @@ Unable to connect to %x. Impossibile connettersi a %x. +Completed successfully +Completato con successo + +Completed with warnings +Completato con avvisi + +Completed with errors +Completato con errori + Cannot access the Volume Shadow Copy Service. Impossibile accedere al Volume Shadow Copy Service. @@ -678,8 +693,8 @@ Attuale: %y byte 3. Press 'Start'. 3. Premi 'Avvio'. -To get started just import a .ffs_batch file. -Per iniziare è sufficiente importare un file con estensione .ffs_batch. +To get started just import a "ffs_batch" file. +Per iniziare è sufficiente importare un file con estensione "ffs_batch". Folders to watch: Cartelle da guardare: @@ -761,24 +776,9 @@ Il comando è attivato se: System: Shut down Sistema: Spento -Stopped -Arrestato - -Completed with errors -Completato con errori - -Completed with warnings -Completato con avvisi - -Warning -Attenzione - Nothing to synchronize Non c'è nulla da sincronizzare -Completed successfully -Completato con successo - Executing command %x Esecuzione del comando %x @@ -830,6 +830,9 @@ Il comando è attivato se: Last sync Ultima sincronizzazione +Log +Log + Folder Cartella @@ -905,6 +908,9 @@ Il comando è attivato se: Save as &batch job... Salva come &processo batch... +Show &log + + Start &comparison Avvio &confronto @@ -930,7 +936,7 @@ Il comando è attivato se: &Lingua &Find... -&Trova ... +&Trova... &Export file list... &Esporta l'elenco dei file... @@ -987,7 +993,7 @@ Il comando è attivato se: Salva Save as... -Salva come ... +Salva come... View type: Visualizza tipo: @@ -1303,6 +1309,9 @@ Questo garantisce uno stato consistente anche in caso di errore grave. Show all permanently hidden dialogs and warning messages again Mostra di nuovo tutti i dialoghi nascosti in modo permanente e i messaggi di allarme +Remove old log files after x days: + + Customize context menu: Personalizzare menu contestuale: @@ -1393,6 +1402,15 @@ Questo garantisce uno stato consistente anche in caso di errore grave. Highlight Configurations Evidenzia le configurazioni +Info +Info + +No log entries + + +Select all +Seleziona tutto + &Options &Opzioni @@ -1619,7 +1637,7 @@ Questo garantisce uno stato consistente anche in caso di errore grave. In pausa Stop requested... -Stop richiesto ... +Stop richiesto... Initializing... Inizializzazione... @@ -1630,21 +1648,12 @@ Questo garantisce uno stato consistente anche in caso di errore grave. Comparing content... Comparazione del contenuto... -Info -Info - -Select all -Seleziona tutto - &Continue &Continuare Progress Avanzamento -Log -Log - Thank you, %x, for your donation and support! Grazie, %x, per la vostra donazione e il supporto! @@ -1870,6 +1879,9 @@ Questo garantisce uno stato consistente anche in caso di errore grave. Unable to register to receive system messages. Impossibile registrarsi per ricevere i messaggi di sistema. +The %x installation option is only available in the FreeFileSync Donation Edition. +L'opzione di installazione %x è disponibile solo nell'edizione di donazione FreeFileSync. + Cannot find system function %x. Impossibile trovare la funzione di sistema %x. @@ -2026,9 +2038,6 @@ Questo garantisce uno stato consistente anche in caso di errore grave. Please choose the local installation type or select a different folder for installation. Scegliere il tipo di installazione locale o selezionare una cartella diversa per l'installazione. -The %x installation option is only available in the FreeFileSync Donation Edition. -L'opzione di installazione %x è disponibile solo nell'edizione di donazione FreeFileSync. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Ottieni l'Edizione Donazione con funzioni bonus e aiuto per mantenere FreeFileSync senza pubblicità. 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 @@ global config file: グローバル構成ファイル: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -任意の数の FreeFileSync 構成設定ファイル(.ffs_gui および/または .ffs_batch). +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +任意の数の FreeFileSync 構成設定ファイル("ffs_gui" および/または "ffs_batch"). Any number of alternative directory pairs for at most one config file. 任意の数の代替ディレクトリペア(ひとつの構成ファイル). @@ -110,7 +110,7 @@ 選択された構成を実行しないで編集用に開きます. Path to an alternate GlobalSettings.xml file. -代替グローバル設定.xml ファイルのパス. + Installation files are corrupted. Please reinstall FreeFileSync. インストール ファイルが破損しています、FreeFileSync を再インストールしてください. @@ -332,6 +332,9 @@ Update attributes on right 右の属性を更新 +Warning +警告 + Items processed: 処理された要素: @@ -341,6 +344,9 @@ Total time: 合計時間: +Stopped +停止 + Cleaning up log files: ログファイルのクリーン: @@ -379,6 +385,15 @@ Unable to connect to %x. %x に接続できません. +Completed successfully +正常に完了しました + +Completed with warnings +警告で終了 + +Completed with errors +エラーで終了 + Cannot access the Volume Shadow Copy Service. ボリュームシャドウコピーサービスにアクセス出来ません. @@ -672,8 +687,8 @@ Actual: %y bytes 3. Press 'Start'. 3. 'スタート'をクリック. -To get started just import a .ffs_batch file. -一括ファイル(.ffs)からインポートして開始. +To get started just import a "ffs_batch" file. + Folders to watch: 監視するフォルダ: @@ -755,24 +770,9 @@ The command is triggered if: System: Shut down システム: シャットダウン -Stopped -停止 - -Completed with errors -エラーで終了 - -Completed with warnings -警告で終了 - -Warning -警告 - Nothing to synchronize 同期対象がありません -Completed successfully -正常に完了しました - Executing command %x コマンド %x を実行中 @@ -823,6 +823,9 @@ The command is triggered if: Last sync 前回の同期 +Log +ログ + Folder フォルダ @@ -898,6 +901,9 @@ The command is triggered if: Save as &batch job... 一括ジョブで保存(&B)... +Show &log + + Start &comparison 比較を開始(&C) @@ -1296,6 +1302,9 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again 非表示にしたすべてのダイアログと警告メッセージを再表示 +Remove old log files after x days: + + Customize context menu: コンテキストメニューのカスタマイズ: @@ -1386,6 +1395,15 @@ This guarantees a consistent state even in case of a serious error. Highlight Configurations 構成の強調表示 +Info +情報 + +No log entries + + +Select all +すべて選択 + &Options 設定(&O) @@ -1619,21 +1637,12 @@ This guarantees a consistent state even in case of a serious error. Comparing content... 内容を比較中... -Info -情報 - -Select all -すべて選択 - &Continue 続行(&C) Progress 進行状況 -Log -ログ - Thank you, %x, for your donation and support! ありがとう %x, あなたの寄付と支援に感謝します!! @@ -1856,6 +1865,9 @@ This guarantees a consistent state even in case of a serious error. Unable to register to receive system messages. システム受信メッセージに登録できません. +The %x installation option is only available in the FreeFileSync Donation Edition. +インストール オプション %x は、FreeFileSync 寄付版でのみ利用可能です. + Cannot find system function %x. システム関数 %x がみつかりません. @@ -2010,9 +2022,6 @@ This guarantees a consistent state even in case of a serious error. Please choose the local installation type or select a different folder for installation. ローカル インストールを選択するか、別のインストール先を選択してください. -The %x installation option is only available in the FreeFileSync Donation Edition. -インストール オプション %x は、FreeFileSync 寄付版でのみ利用可能です. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. 寄付をすることで広告が一切無く、ボーナス機能が付いた FreeFileSync を使用できます. 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 @@ global config file: 전체 설정 파일: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -FreeFileSync .ffs_gui 또는 .ffs_batch 설정 파일 개수. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +FreeFileSync "ffs_gui" 또는 "ffs_batch" 설정 파일 개수. Any number of alternative directory pairs for at most one config file. 최대 1개 설정파일에 대한 대체 디렉터리 페어 개수. @@ -332,6 +332,9 @@ Update attributes on right 우측 속성 업데이트 +Warning +경고 + Items processed: 처리된 항목: @@ -341,6 +344,9 @@ Total time: 전체 시간: +Stopped +중단 + Cleaning up log files: 로그 파일 정리 중: @@ -379,6 +385,15 @@ Unable to connect to %x. %x에 연결할 수 없습니다. +Completed successfully +성공적으로 완료됨 + +Completed with warnings +경고와 함께 완료됨 + +Completed with errors +오류와 함께 완료됨 + Cannot access the Volume Shadow Copy Service. 볼륨 섀도 복사본 서비스에 접근할 수 없습니다. @@ -672,8 +687,8 @@ Actual: %y bytes 3. Press 'Start'. 3. '시작'을 누르세요. -To get started just import a .ffs_batch file. -시작하려면 .ffs_batch file을 가져 오십시오. +To get started just import a "ffs_batch" file. +시작하려면 "ffs_batch" file을 가져 오십시오. Folders to watch: 감시할 폴더: @@ -755,24 +770,9 @@ The command is triggered if: System: Shut down 시스템: 종료 -Stopped -중단 - -Completed with errors -오류와 함께 완료됨 - -Completed with warnings -경고와 함께 완료됨 - -Warning -경고 - Nothing to synchronize 동기화 할 항목이 없습니다 -Completed successfully -성공적으로 완료됨 - Executing command %x %x 명령 실행 중 @@ -823,6 +823,9 @@ The command is triggered if: Last sync 마지막 동기화 +Log +로그 + Folder 폴더 @@ -898,6 +901,9 @@ The command is triggered if: Save as &batch job... 일괄 작업으로 저장(&b)... +Show &log + + Start &comparison 비교 시작(&C) @@ -1296,6 +1302,9 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again 영구적으로 숨겨진 모든 대화 상자 및 경고 메세지 다시 보이기 +Remove old log files after x days: + + Customize context menu: 컨텍스트 메뉴 커스터마이즈 (사용자 정의): @@ -1386,6 +1395,15 @@ This guarantees a consistent state even in case of a serious error. Highlight Configurations 강조 표시 구성 +Info +정보 + +No log entries + + +Select all +모두 선택 + &Options 옵션(&O) @@ -1619,21 +1637,12 @@ This guarantees a consistent state even in case of a serious error. Comparing content... 내용 비교 중... -Info -정보 - -Select all -모두 선택 - &Continue 계속(&C) Progress 진행 -Log -로그 - Thank you, %x, for your donation and support! %x님의 기부와 지원에 감사드립니다! @@ -1856,6 +1865,9 @@ This guarantees a consistent state even in case of a serious error. Unable to register to receive system messages. 시스템 메시지 수신을 위한 등록을 할 수 없습니다. +The %x installation option is only available in the FreeFileSync Donation Edition. +%x 설치 옵션은 FreeFileSync 기부자 에디션에서만 사용 가능합니다. + Cannot find system function %x. 시스템 함수 %x을(를) 찾을 수 없습니다. @@ -2010,9 +2022,6 @@ This guarantees a consistent state even in case of a serious error. Please choose the local installation type or select a different folder for installation. 로컬 설치 유형을 선택하거나 설치할 다른 폴더를 선택하십시오. -The %x installation option is only available in the FreeFileSync Donation Edition. -%x 설치 옵션은 FreeFileSync 기부자 에디션에서만 사용 가능합니다. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. 보너스 기능이 있는 기부자 에디션을 사용하시어 광고없는 FreeFileSync가 유지되도록 도와주세요. 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 @@ global config file: bendrinis konfigūracinis failas: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Neribotas kiekis FreeFileSync .ffs_gui ir/arba .ffs_batch konfigūracinių failų. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Neribotas kiekis FreeFileSync "ffs_gui" ir/arba "ffs_batch" konfigūracinių failų. Any number of alternative directory pairs for at most one config file. Bet koks skaičius alternatyvių katalogų grupuojams tik su vienu konfigūraciniu failu. @@ -336,6 +336,9 @@ Update attributes on right Atnaujinti atributus dešinėje +Warning +Perspėjimas + Items processed: Elementų apdorota: @@ -345,6 +348,9 @@ Total time: Visas laikas: +Stopped +Sustabdyta + Cleaning up log files: Žurnalinių failų valymas: @@ -385,6 +391,15 @@ Unable to connect to %x. Nepavyko prisijungti prie %x. +Completed successfully +Sėkmingai užbaigtas + +Completed with warnings +Užbaigtas su perspėjimais + +Completed with errors +Užbaigtas su klaidomis + Cannot access the Volume Shadow Copy Service. Duomenų Šešėlinės Kopijos Paslauga nepasiekiama. @@ -684,8 +699,8 @@ Esamas: %y baitai 3. Press 'Start'. 3. Spauskite „Pradėti“'. -To get started just import a .ffs_batch file. -Kad pradėti tiesiog importuokite .ffs_batch failą. +To get started just import a "ffs_batch" file. +Kad pradėti tiesiog importuokite "ffs_batch" failą. Folders to watch: Stebimi aplankai: @@ -767,24 +782,9 @@ Komanda inicijuojama jei: System: Shut down Sistema: Išjungti -Stopped -Sustabdyta - -Completed with errors -Užbaigtas su klaidomis - -Completed with warnings -Užbaigtas su perspėjimais - -Warning -Perspėjimas - Nothing to synchronize Nėra ko suvienodinti -Completed successfully -Sėkmingai užbaigtas - Executing command %x Vykdyti komandą %x @@ -837,6 +837,9 @@ Komanda inicijuojama jei: Last sync Vėliausias +Log +Žurnalas + Folder Aplankas @@ -912,6 +915,9 @@ Komanda inicijuojama jei: Save as &batch job... Išsaugoti kaip &užduočių paketą... +Show &log + + Start &comparison Pradėti &palyginimą @@ -1310,6 +1316,9 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. Show all permanently hidden dialogs and warning messages again Vėl rodyti visus paslėptus dialogo langus ir įspėjamuosius pranešimus +Remove old log files after x days: + + Customize context menu: Pagrindinio meniu pasirinkimai: @@ -1400,6 +1409,15 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. Highlight Configurations Pažymėti Nustatymus +Info +Informacija + +No log entries + + +Select all +Pažymėti visus + &Options &Parinktys @@ -1641,21 +1659,12 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. Comparing content... Palyginimo turinys... -Info -Informacija - -Select all -Pažymėti visus - &Continue &Tęsti Progress Pokytis -Log -Žurnalas - Thank you, %x, for your donation and support! Ačiū, %x, už jūsų auką, bei parėmimą! @@ -1884,6 +1893,9 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. Unable to register to receive system messages. Nepavyko aktyvuoti sisteminių žinučių gavimo funkciją. +The %x installation option is only available in the FreeFileSync Donation Edition. +%x įdiegiamoji parinktis yra tiktai FreeFileSync Donoro Versijoje. + Cannot find system function %x. Nepavyksta rasti sisteminės funkcijos %x. @@ -2042,9 +2054,6 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. Please choose the local installation type or select a different folder for installation. Prašome pasirinkti vietinio įdiegimo metodą arba pakeiskite aplaką. -The %x installation option is only available in the FreeFileSync Donation Edition. -%x įdiegiamoji parinktis yra tiktai FreeFileSync Donoro Versijoje. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Pasirinkti Parėmimo Versiją su papildomomis funkcijomis ir padėkite išlaikyti FreeFileSync be reklamų. 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 @@ n == 1 ? 0 : 1 +No log entries + + +Remove old log files after x days: + + +Show &log + + Both sides have changed since last synchronization. Begge sider er endret siden siste synkronisering. @@ -100,8 +109,8 @@ global config file: global innstillingsfil: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Vilkårlig antall FreeFileSync .ffs_gui og/eller .ffs_batch innstillingsfiler. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Vilkårlig antall FreeFileSync "ffs_gui" og/eller "ffs_batch" innstillingsfiler. Any number of alternative directory pairs for at most one config file. Et ubegrenset antall alternative kataloger for maksimalt én konfigurasjonsfil. @@ -245,7 +254,7 @@ Databasefilene inneholder ennå ikke informasjon om siste synkronisering. Loading file %x... -Laster fil %x ... +Laster fil %x... Saving file %x... Lagrer filen %x... @@ -334,6 +343,9 @@ Update attributes on right Oppdater attributter til høyre +Warning +Advarsel + Items processed: Elementer behandlet: @@ -343,6 +355,9 @@ Total time: Samlet tid: +Stopped +Stoppet + Cleaning up log files: Sletter loggfiler: @@ -382,6 +397,15 @@ Unable to connect to %x. Kan ikke koble til %x. +Completed successfully +Fullførte feilfritt + +Completed with warnings +Avsluttet med advarsler + +Completed with errors +Avsluttet med feil + Cannot access the Volume Shadow Copy Service. Volume Shadow Copy-tjenesten er ikke tilgjengelig. @@ -678,8 +702,8 @@ Faktisk: %y bytes 3. Press 'Start'. 3. Klikk 'Start'. -To get started just import a .ffs_batch file. -Importer en .ffs_batchfil for å komme igang. +To get started just import a "ffs_batch" file. +Importer en "ffs_batch"fil for å komme igang. Folders to watch: Mapper å se på: @@ -761,24 +785,9 @@ Kommandoen utføres hvis: System: Shut down System: Avslutt -Stopped -Stoppet - -Completed with errors -Avsluttet med feil - -Completed with warnings -Avsluttet med advarsler - -Warning -Advarsel - Nothing to synchronize Alt er synkronisert -Completed successfully -Fullførte feilfritt - Executing command %x Utfør kommandoen %x @@ -830,6 +839,9 @@ Kommandoen utføres hvis: Last sync Sist synkronisert +Log +Logg + Folder Mappe @@ -1268,7 +1280,7 @@ Kommandoen utføres hvis: Begrens antall loggfiler: How can I schedule a batch job? -Hvordan kan jeg lage en batchfil ? +Hvordan kan jeg lage en batchfil? &Keep relative paths &Behold tilknyttede stier @@ -1393,6 +1405,12 @@ Sikrer prosessen ved alvorlige feil. Highlight Configurations Uthev konfigurasjoner +Info +Info + +Select all +Velg alt + &Options &Valg @@ -1630,21 +1648,12 @@ Sikrer prosessen ved alvorlige feil. Comparing content... Sammenligner innhold... -Info -Info - -Select all -Velg alt - &Continue &Fortsett Progress Fremskritt -Log -Logg - Thank you, %x, for your donation and support! Takk, %x, for ditt bidrag og støtte! @@ -1680,8 +1689,8 @@ Sikrer prosessen ved alvorlige feil. Do you really want to move the following %x items to the recycle bin? -Vil du virkelig flytte følgende element til papirkurven ? -Vil du virkelig flytte følgende %x elementer til papirkurven ? +Vil du virkelig flytte følgende element til papirkurven? +Vil du virkelig flytte følgende %x elementer til papirkurven? Move @@ -1692,8 +1701,8 @@ Sikrer prosessen ved alvorlige feil. Do you really want to delete the following %x items? -Vil du virkelig slette følgende element ? -Vil du virkelig slette følgende %x element ? +Vil du virkelig slette følgende element? +Vil du virkelig slette følgende %x element? Copy DACL, SACL, Owner, Group @@ -1841,7 +1850,7 @@ Sikrer prosessen ved alvorlige feil. &Hjemmeside Download now? -Last ned nå ? +Last ned nå? &Download &Last ned @@ -1870,6 +1879,9 @@ Sikrer prosessen ved alvorlige feil. Unable to register to receive system messages. Kunne ikke registrere for å motta systembeskjeder. +The %x installation option is only available in the FreeFileSync Donation Edition. +Installasjonsalternativet %x er bare tilgjengelig i FreeFileSync Donation Edition. + Cannot find system function %x. Kan ikke finne systemfunksjonen %x. @@ -1946,7 +1958,7 @@ Sikrer prosessen ved alvorlige feil. De følgende XML-elementer kunne ikke leses: Configuration file %x is incomplete. The missing elements will be set to their default values. -Konfigurasjonsfil %x er ufullstendig. De manglende elementene vil bli satt til standardverdiene . +Konfigurasjonsfil %x er ufullstendig. De manglende elementene vil bli satt til standardverdiene. Prepare installation Forbered installering @@ -2026,9 +2038,6 @@ Sikrer prosessen ved alvorlige feil. Please choose the local installation type or select a different folder for installation. Velg den lokale installasjonstype eller velge en annen mappe for installasjon. -The %x installation option is only available in the FreeFileSync Donation Edition. -Installasjonsalternativet %x er bare tilgjengelig i FreeFileSync Donation Edition. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Få Donation Edition med bonus-tillegg og hjelp til med å holde FreeFileSync annonsefri. 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 @@ global config file: globalny plik konfiguracyjny: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Dowolna ilość plików konfiguracyjnych FreeFileSync .ffs_gui/.ffs_batch. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Dowolna ilość plików konfiguracyjnych FreeFileSync "ffs_gui"/"ffs_batch". Any number of alternative directory pairs for at most one config file. Dowolna liczba alternatywnych par katalogów dla najwyżej jednego pliku konfiguracyjnego. @@ -336,6 +336,9 @@ Update attributes on right Aktualizuj atrybuty po prawej stronie +Warning +Ostrzeżenie + Items processed: Przetworzone elementy: @@ -345,6 +348,9 @@ Total time: Całkowity czas: +Stopped +Zatrzymana + Cleaning up log files: Czyszczenie plików logów: @@ -385,6 +391,15 @@ Unable to connect to %x. Błąd połączenia do %x. +Completed successfully +Zakończono bez błędów + +Completed with warnings +Zakończono z ostrzeżeniami + +Completed with errors +Zakończono z błędami + Cannot access the Volume Shadow Copy Service. Nie można uzyskać dostępu do usługi Volume Shadow Copy. @@ -684,8 +699,8 @@ Przesłany: %y bajtów 3. Press 'Start'. 3. Wciśnij 'Start'. -To get started just import a .ffs_batch file. -Aby rozpocząć, zaimportuj plik .ffs_batch. +To get started just import a "ffs_batch" file. +Aby rozpocząć, zaimportuj plik "ffs_batch". Folders to watch: Katalogi do obserwowania: @@ -767,24 +782,9 @@ Komenda jest wykonywana gdy: System: Shut down System: Wyłączenie -Stopped -Zatrzymana - -Completed with errors -Zakończono z błędami - -Completed with warnings -Zakończono z ostrzeżeniami - -Warning -Ostrzeżenie - Nothing to synchronize Brak plików do synchronizacji -Completed successfully -Zakończono bez błędów - Executing command %x Wykonywanie polecenia %x @@ -837,6 +837,9 @@ Komenda jest wykonywana gdy: Last sync Ostatnia synchronizacja +Log +Log + Folder Katalog @@ -912,6 +915,9 @@ Komenda jest wykonywana gdy: Save as &batch job... Zapisz w trybie &wsadowym... +Show &log + + Start &comparison Rozpo&cznij porównywanie @@ -1310,6 +1316,9 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp Show all permanently hidden dialogs and warning messages again Przywróć wszystkie, stale ukryte dialogi i powiadomienia +Remove old log files after x days: + + Customize context menu: Dostosuj menu kontekstowe: @@ -1400,6 +1409,15 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp Highlight Configurations Pokaż konfiguracje +Info +Info + +No log entries + + +Select all +Zaznacz wszystko + &Options &Opcje @@ -1641,21 +1659,12 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp Comparing content... Porównywanie zawartości... -Info -Info - -Select all -Zaznacz wszystko - &Continue &Kontynuuj Progress Postęp -Log -Log - Thank you, %x, for your donation and support! Dziękujemy %x za Twoje wsparcie! @@ -1884,6 +1893,9 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp Unable to register to receive system messages. Błąd podczas rejestrowania do odbioru komunikatów systemowych. +The %x installation option is only available in the FreeFileSync Donation Edition. +Opcja instalacyjna %x jest dostępna wyłącznie w wersji FreeFileSync Donation Edition. + Cannot find system function %x. Nie można odnaleźć funkcji systemowej %x. @@ -2042,9 +2054,6 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp Please choose the local installation type or select a different folder for installation. Wybierz lokalny typ instalacji lub określ inny katalog. -The %x installation option is only available in the FreeFileSync Donation Edition. -Opcja instalacyjna %x jest dostępna wyłącznie w wersji FreeFileSync Donation Edition. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Pobierz Donation Edition z funkcjami dodatkowymi i pomóż utrzymać FreeFileSync bez reklam. 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 @@ global config file: ficheiro de configuração global: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Qualquer número de ficheiros de configuração .ffs_gui e/ou .ffs_batch do FreeFileSync. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Qualquer número de ficheiros de configuração "ffs_gui" e/ou "ffs_batch" do FreeFileSync. Any number of alternative directory pairs for at most one config file. Qualquer número de pares de directórios alternativos para apenas um ficheiro de configuração. @@ -334,6 +334,9 @@ Update attributes on right Actualizar atributos à direita +Warning +Atenção + Items processed: Elementos processados: @@ -343,6 +346,9 @@ Total time: Tempo total: +Stopped +Parado + Cleaning up log files: A limpar ficheiros de registo: @@ -382,6 +388,15 @@ Unable to connect to %x. Não é possível conectar-se em %x. +Completed successfully +Concluído com sucesso + +Completed with warnings +Concluído com avisos + +Completed with errors +Concluído com erros + Cannot access the Volume Shadow Copy Service. Não é possível aceder ao serviço Volume Shadow Copy. @@ -678,8 +693,8 @@ Actual: %y bytes 3. Press 'Start'. 3. Pressionar 'Iniciar'. -To get started just import a .ffs_batch file. -Para começar basta importar um ficheiro .ffs_batch. +To get started just import a "ffs_batch" file. +Para começar basta importar um ficheiro "ffs_batch". Folders to watch: Pastas a verificar: @@ -761,24 +776,9 @@ O comando é executado se: System: Shut down Desligar -Stopped -Parado - -Completed with errors -Concluído com erros - -Completed with warnings -Concluído com avisos - -Warning -Atenção - Nothing to synchronize Nada a sincronizar -Completed successfully -Concluído com sucesso - Executing command %x A executar comando %x @@ -830,6 +830,9 @@ O comando é executado se: Last sync Última sincronia +Log +Log + Folder Pasta @@ -905,6 +908,9 @@ O comando é executado se: Save as &batch job... Guardar como &batch... +Show &log + + Start &comparison Iniciar &comparação @@ -1303,6 +1309,9 @@ Isto garante um estado consistente mesmo em caso de falha grave. Show all permanently hidden dialogs and warning messages again Mostrar todos os diálogos escondidos permanentemente e mensagens de aviso novamente +Remove old log files after x days: + + Customize context menu: Personalizar menu de contexto: @@ -1393,6 +1402,15 @@ Isto garante um estado consistente mesmo em caso de falha grave. Highlight Configurations Realçar Configurações +Info +Info + +No log entries + + +Select all +Seleccionar tudo + &Options &Opções @@ -1630,21 +1648,12 @@ Isto garante um estado consistente mesmo em caso de falha grave. Comparing content... A comparar... -Info -Info - -Select all -Seleccionar tudo - &Continue &Continuar Progress Progresso -Log -Log - Thank you, %x, for your donation and support! Obrigado, %x, pela sua doação e suporte! @@ -1870,6 +1879,9 @@ Isto garante um estado consistente mesmo em caso de falha grave. Unable to register to receive system messages. Não foi possível registar para receber mensagens do sistema. +The %x installation option is only available in the FreeFileSync Donation Edition. +A opção de instalação %x está disponível somente no FreeFileSync Donation Edition. + Cannot find system function %x. Não é possível encontrar a função do sistema %x. @@ -2026,9 +2038,6 @@ Isto garante um estado consistente mesmo em caso de falha grave. Please choose the local installation type or select a different folder for installation. Por favor, escolha o tipo do local de instalação ou seleccione uma pasta diferente para instalar. -The %x installation option is only available in the FreeFileSync Donation Edition. -A opção de instalação %x está disponível somente no FreeFileSync Donation Edition. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Obtenha a Donation Edition com recursos bônus e ajude a manter o FreeFileSync sem anúncios. 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 @@ global config file: arquivo de configuração global: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Qualquer número de arquivos FreeFileSync .ffs e/ou de tarefa em lotes .ffs_batch. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Qualquer número de arquivos FreeFileSync "ffs_gui" e/ou de tarefa em lotes "ffs_batch". Any number of alternative directory pairs for at most one config file. Qualquer número de pares alternativos de diretórios para no máximo um arquivo de configuração. @@ -334,6 +334,9 @@ Update attributes on right Atualizar atributos à direita +Warning +Aviso + Items processed: Elementos processados: @@ -343,6 +346,9 @@ Total time: Tempo total: +Stopped +Interrompido + Cleaning up log files: Limpando arquivos de log: @@ -382,6 +388,15 @@ Unable to connect to %x. Não foi possível conectar a %x. +Completed successfully +Concluído com sucesso + +Completed with warnings +Concluído com avisos + +Completed with errors +Concluído com erros + Cannot access the Volume Shadow Copy Service. Não é possível acessar o Serviço de Cópias de Sombra de Volume. @@ -678,8 +693,8 @@ Atual: %y bytes 3. Press 'Start'. 3. Pressionar 'Iniciar'. -To get started just import a .ffs_batch file. -Para iniciar importe um arquivo .ffs_batch. +To get started just import a "ffs_batch" file. +Para iniciar importe um arquivo "ffs_batch". Folders to watch: Pastas para monitorar: @@ -761,24 +776,9 @@ O comando é disparado se: System: Shut down Sistema: Desligar -Stopped -Interrompido - -Completed with errors -Concluído com erros - -Completed with warnings -Concluído com avisos - -Warning -Aviso - Nothing to synchronize Nada para sincronizar -Completed successfully -Concluído com sucesso - Executing command %x Executando comando %x @@ -830,6 +830,9 @@ O comando é disparado se: Last sync Últ. Sinc. +Log +Log + Folder Pasta @@ -905,6 +908,9 @@ O comando é disparado se: Save as &batch job... Salvar como &tarefa em lotes... +Show &log + + Start &comparison Iniciar &Comparação @@ -1303,6 +1309,9 @@ Isto garante um estado consistente mesmo em caso de erro grave. Show all permanently hidden dialogs and warning messages again Mostrar todas as caixas de diálogo e as mensagens de aviso permanentemente ocultadas +Remove old log files after x days: + + Customize context menu: Personalizar menu de contexto: @@ -1393,6 +1402,15 @@ Isto garante um estado consistente mesmo em caso de erro grave. Highlight Configurations Realçar Configurações +Info +Informações + +No log entries + + +Select all +Selecionar todos + &Options &Opções @@ -1630,21 +1648,12 @@ Isto garante um estado consistente mesmo em caso de erro grave. Comparing content... Comparando conteúdo... -Info -Informações - -Select all -Selecionar todos - &Continue &Continuar Progress Progresso -Log -Log - Thank you, %x, for your donation and support! Obrigado, %x, pela sua doação e suporte! @@ -1870,6 +1879,9 @@ Isto garante um estado consistente mesmo em caso de erro grave. Unable to register to receive system messages. Não é possível registrar para receber mensagens do sistema. +The %x installation option is only available in the FreeFileSync Donation Edition. +A opção de instalação %x está disponível apenas na Edição do Doador do FreeFileSync. + Cannot find system function %x. Não é possível localizar a função de sistema %x. @@ -2026,9 +2038,6 @@ Isto garante um estado consistente mesmo em caso de erro grave. Please choose the local installation type or select a different folder for installation. Escolha o tipo de instalação local ou selecione uma pasta diferente para instalação. -The %x installation option is only available in the FreeFileSync Donation Edition. -A opção de instalação %x está disponível apenas na Edição do Doador do FreeFileSync. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Obtenha a Edição do Doador com recursos adicionais e ajude a manter o FreeFileSync livre de anúncios. 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 @@ global config file: filă de configurare globală: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Orice număr de file de configurare pentru FreeFileSync, de tipul .ffs_gui sau/și .ffs_batch. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Orice număr de file de configurare pentru FreeFileSync, de tipul "ffs_gui" sau/și "ffs_batch". Any number of alternative directory pairs for at most one config file. Orice număr de perechi alternative de dosare pentru cel mult o filă de configurare. @@ -336,6 +336,9 @@ Update attributes on right Actualizează atributele în partea dreaptă +Warning +Atenție + Items processed: Elemente Procesate: @@ -345,6 +348,9 @@ Total time: Timp Total: +Stopped +Oprită + Cleaning up log files: Curăț filele de jurnalizare: @@ -385,6 +391,15 @@ Unable to connect to %x. Nu mă pot conecta la %x. +Completed successfully +Realizată cu succes + +Completed with warnings +Realizată cu atenționări + +Completed with errors +Realizată cu erori + Cannot access the Volume Shadow Copy Service. Nu pot accesa Serviciul de Conservare a Volumelor [Volume Shadow Copy]. @@ -684,8 +699,8 @@ Actuală: %y baiți 3. Press 'Start'. 3. Apasă pe 'Pornește'. -To get started just import a .ffs_batch file. -Pentru a începe, importă o filă de tipul .ffs_batch. +To get started just import a "ffs_batch" file. +Pentru a începe, importă o filă de tipul "ffs_batch". Folders to watch: Dosare de Monitorizat: @@ -767,24 +782,9 @@ Comanda este declanșată dacă: System: Shut down Sistem: Închide PC-ul [Shut down] -Stopped -Oprită - -Completed with errors -Realizată cu erori - -Completed with warnings -Realizată cu atenționări - -Warning -Atenție - Nothing to synchronize Nu e nimic de sincronizat -Completed successfully -Realizată cu succes - Executing command %x Execut comanda %x @@ -837,6 +837,9 @@ Comanda este declanșată dacă: Last sync Ultima Sincr. +Log +Jurnal + Folder Dosar @@ -912,6 +915,9 @@ Comanda este declanșată dacă: Save as &batch job... Salvea&ză ca Sarcină Set... +Show &log + + Start &comparison Pornește &Compararea @@ -1310,6 +1316,9 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției Show all permanently hidden dialogs and warning messages again Sînt arătate din nou dialogurile și mesajele de eroare care au fost ascunse permanent +Remove old log files after x days: + + Customize context menu: Personalizează meniul contextual: @@ -1400,6 +1409,15 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției Highlight Configurations Evidențiază Configurațiile +Info +Informații + +No log entries + + +Select all +Selectează Tot + &Options &Opțiuni @@ -1641,21 +1659,12 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției Comparing content... Compar conținutul... -Info -Informații - -Select all -Selectează Tot - &Continue &Continuă Progress Progres -Log -Jurnal - Thank you, %x, for your donation and support! Mulțumesc %x, pentru donație și sprijin! @@ -1855,7 +1864,7 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției &Situl Softului Download now? -Vrei s-o descarci acum ? +Vrei s-o descarci acum? &Download &Descarcă @@ -1884,6 +1893,9 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției Unable to register to receive system messages. Nu pot înregistra softul pentru a primi mesaje de la sistemul de operare. +The %x installation option is only available in the FreeFileSync Donation Edition. +Opțiunea de instalare %x e disponibilă doar pentru FreeFileSync Ediția pentru Donatori. + Cannot find system function %x. Nu pot găsi funcția de sistem %x. @@ -2042,9 +2054,6 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției Please choose the local installation type or select a different folder for installation. Alege instalarea locală sau selectează un dosar diferit pentru instalare. -The %x installation option is only available in the FreeFileSync Donation Edition. -Opțiunea de instalare %x e disponibilă doar pentru FreeFileSync Ediția pentru Donatori. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Obține Ediția pentru Donatori cu funcții suplimentare și ajută la menținerea FreeFileSync fără publicitate. 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 @@ global config file: глобальный конфигурационный файл: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Любое количество FreeFileSync .ffs_gui и/или .ffs_batch конфигурационных файлов. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Любое количество FreeFileSync "ffs_gui" и/или "ffs_batch" конфигурационных файлов. Any number of alternative directory pairs for at most one config file. Любое количество альтернативных пар папок для не более одного конфигурационного файла. @@ -336,6 +336,9 @@ Update attributes on right Обновление атрибутов справа +Warning +Внимание + Items processed: Элементов обработано: @@ -345,6 +348,9 @@ Total time: Общее время: +Stopped +Остановлено + Cleaning up log files: Очистка лог-файлов (журналов): @@ -385,6 +391,15 @@ Unable to connect to %x. Невозможно соединиться с %x. +Completed successfully +Выполнено успешно + +Completed with warnings +Выполнено с предупреждениями + +Completed with errors +Выполнено с ошибками + Cannot access the Volume Shadow Copy Service. Невозможно получить доступ к службе Теневого Копирования Тома. @@ -684,8 +699,8 @@ Actual: %y bytes 3. Press 'Start'. 3. Нажмите 'Старт'. -To get started just import a .ffs_batch file. -Для запуска просто импортируйте файл .ffs_batch. +To get started just import a "ffs_batch" file. +Для запуска просто импортируйте файл "ffs_batch". Folders to watch: Папки для наблюдения: @@ -767,24 +782,9 @@ The command is triggered if: System: Shut down Система: Завершение работы -Stopped -Остановлено - -Completed with errors -Выполнено с ошибками - -Completed with warnings -Выполнено с предупреждениями - -Warning -Внимание - Nothing to synchronize Ничего нет для синхронизации -Completed successfully -Выполнено успешно - Executing command %x Выполнение команды %x @@ -837,6 +837,9 @@ The command is triggered if: Last sync Последняя синхронизация +Log +Лог (журнал) + Folder Папка @@ -912,6 +915,9 @@ The command is triggered if: Save as &batch job... Сохранить как пакетное &задание... +Show &log + + Start &comparison Начать с&равнение @@ -1310,6 +1316,9 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again Показать все скрытые окна и сообщения с предупреждениями снова +Remove old log files after x days: + + Customize context menu: Кастомизация контекстного меню: @@ -1400,6 +1409,15 @@ This guarantees a consistent state even in case of a serious error. Highlight Configurations Выделение конфигураций +Info +Информация + +No log entries + + +Select all +Выделить все + &Options &Настройки @@ -1641,21 +1659,12 @@ This guarantees a consistent state even in case of a serious error. Comparing content... Сравнение содержания... -Info -Информация - -Select all -Выделить все - &Continue &Продолжить Progress Прогресс -Log -Лог (журнал) - Thank you, %x, for your donation and support! Благодарим вас, %x, за пожертвование и поддержку! @@ -1884,6 +1893,9 @@ This guarantees a consistent state even in case of a serious error. Unable to register to receive system messages. Невозможно зарегистрироваться для получения системных сообщений. +The %x installation option is only available in the FreeFileSync Donation Edition. +%x опция установки доступна только в платной версии FreeFileSync. + Cannot find system function %x. Невозможно найти системную функцию %x. @@ -2042,9 +2054,6 @@ This guarantees a consistent state even in case of a serious error. Please choose the local installation type or select a different folder for installation. Пожалуйста, выберите локальный тип установки или выберите другую папку для установки. -The %x installation option is only available in the FreeFileSync Donation Edition. -%x опция установки доступна только в платной версии FreeFileSync. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Приобретите платную версию FreeFileSync с бонусными функциями и помогите сохранить FreeFileSync свободным от рекламы. 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 @@ Zmazanie symbolického odkazu %x Checking recycle bin availability for folder %x... -Kontrola Koša pre priečinok %x ... +Kontrola Koša pre priečinok %x... The recycle bin is not supported by the following folders. Deleted or overwritten files will not be able to be restored: Premiestnenie do Koša nie je možné pri nasledujúcich priečinkoch. Zmazané alebo prepísané súbory nebude možné obnoviť: @@ -100,8 +100,8 @@ global config file: globálny konfiguračný súbor: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Ľubovolný počet konfiguračných súborov FreeFileSync typu .ffs_gui a/alebo .ffs_batch. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Ľubovolný počet konfiguračných súborov FreeFileSync typu "ffs_gui" a/alebo "ffs_batch". Any number of alternative directory pairs for at most one config file. Ľubovolný počet alternatívnych dvojíc adresárov na aspoň jednu konfiguráciu. @@ -336,6 +336,9 @@ Update attributes on right Aktualizovať atribúty napravo +Warning +Varovanie + Items processed: Spracovaných položiek: @@ -345,6 +348,9 @@ Total time: Celkový čas: +Stopped +Zastavené + Cleaning up log files: Odstránenie log súborov: @@ -385,6 +391,15 @@ Unable to connect to %x. Nie je možné vytvoriť pripojenie k %x. +Completed successfully +Dokončenie bolo úspešné + +Completed with warnings +Ukončené s varovaniami + +Completed with errors +Ukončené s chybami + Cannot access the Volume Shadow Copy Service. Nie je prístup k službe Tieňové kópie. @@ -684,8 +699,8 @@ Aktuálne: %y b 3. Press 'Start'. 3. Stlačte 'Štart'. -To get started just import a .ffs_batch file. -Môžete načítať tiež konfiguračný súbor .ffs_batch. +To get started just import a "ffs_batch" file. +Môžete načítať tiež konfiguračný súbor "ffs_batch". Folders to watch: Sledované priečinka: @@ -767,24 +782,9 @@ Príkaz bude spustení ak: System: Shut down Systém: Vypnúť -Stopped -Zastavené - -Completed with errors -Ukončené s chybami - -Completed with warnings -Ukončené s varovaniami - -Warning -Varovanie - Nothing to synchronize Nie je čo synchronizovať -Completed successfully -Dokončenie bolo úspešné - Executing command %x Spúšťací príkaz %x @@ -837,6 +837,9 @@ Príkaz bude spustení ak: Last sync Posledná synchronizácia +Log +Záznam spracovania + Folder Priečinok @@ -912,6 +915,9 @@ Príkaz bude spustení ak: Save as &batch job... Uložiť ako &dávku... +Show &log + + Start &comparison Spustiť &porovnanie @@ -1307,6 +1313,9 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again Zobraziť znovu všetky trvale skryté dialógy a varovné hlásenia +Remove old log files after x days: + + Customize context menu: Prispôsobiť kontextovú ponuku: @@ -1397,6 +1406,15 @@ This guarantees a consistent state even in case of a serious error. Highlight Configurations Zvýrazniť konfigurácie +Info +Info + +No log entries + + +Select all +Vybrať všetko + &Options Nastavenie &programu @@ -1621,7 +1639,7 @@ This guarantees a consistent state even in case of a serious error. Zoznam súborov bol exportovaný Searching for program updates... -Hľadanie aktualizácií programu ... +Hľadanie aktualizácií programu... Paused Pauza @@ -1638,21 +1656,12 @@ This guarantees a consistent state even in case of a serious error. Comparing content... Porovnávanie obsahu... -Info -Info - -Select all -Vybrať všetko - &Continue &Pokračovať Progress Priebeh -Log -Záznam spracovania - Thank you, %x, for your donation and support! Ďakujem, %x, za dar a podporu! @@ -1881,6 +1890,9 @@ This guarantees a consistent state even in case of a serious error. Unable to register to receive system messages. Nepodarilo sa registrovať k odberu systémových správ. +The %x installation option is only available in the FreeFileSync Donation Edition. +Inštalačná možnosť %x je dostupná iba pri FreeFileSync Donation Edition. + Cannot find system function %x. Nie je možné nájsť systémovú funkciu %x. @@ -2039,9 +2051,6 @@ This guarantees a consistent state even in case of a serious error. Please choose the local installation type or select a different folder for installation. Prosím zvoľte lokálny typ inštalácie alebo vyberte iný priečinok pre inštaláciu. -The %x installation option is only available in the FreeFileSync Donation Edition. -Inštalačná možnosť %x je dostupná iba pri FreeFileSync Donation Edition. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Získajte Donation Edition s bonusovými funkciami a pomôžte, aby ostal FreeFileSync bez reklami. 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 @@ global config file: datoteka z globalnimi konfiguracijami: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Poljubno število FreeFileSync .ffs_gui in/ali .ffs_batch nastavitvenih datotek. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Poljubno število FreeFileSync "ffs_gui" in/ali "ffs_batch" nastavitvenih datotek. Any number of alternative directory pairs for at most one config file. Poljubno število alternativnih parov imenikov za največ eno nastavitveno datoteko. @@ -338,6 +338,9 @@ Update attributes on right Posodobi atribute na desni strani +Warning +Opozorilo + Items processed: Obdelanih postavk: @@ -347,6 +350,9 @@ Total time: Celoten čas: +Stopped +Ustavljeno + Cleaning up log files: Čiščenje datotek dnevnika: @@ -388,6 +394,15 @@ Unable to connect to %x. Ne morem povezati na %x. +Completed successfully +Uspešno končano + +Completed with warnings +Dokončano z opozorili + +Completed with errors +Dokončano z napakami + Cannot access the Volume Shadow Copy Service. Ne morem dostopati do Volume Shadov Copy storitve. @@ -690,8 +705,8 @@ Dejansko: %y bajtov 3. Press 'Start'. 3. Pritisnite 'Začni'. -To get started just import a .ffs_batch file. -Če želite začeti, uvozite datoteko .ffs_batch. +To get started just import a "ffs_batch" file. +Če želite začeti, uvozite datoteko "ffs_batch". Folders to watch: Mape za pregled: @@ -773,24 +788,9 @@ Ukaz se sproži če: System: Shut down Sistem: Izključi računalnik -Stopped -Ustavljeno - -Completed with errors -Dokončano z napakami - -Completed with warnings -Dokončano z opozorili - -Warning -Opozorilo - Nothing to synchronize Nič za sinhroniziranje -Completed successfully -Uspešno končano - Executing command %x Izvedba ukaza %x @@ -844,6 +844,9 @@ Ukaz se sproži če: Last sync Zadnja sinhronizacija +Log +Dnevnik + Folder Mapa @@ -919,6 +922,9 @@ Ukaz se sproži če: Save as &batch job... Shrani kot paketno op&ravilo... +Show &log + + Start &comparison Začni &primerjavo @@ -1317,6 +1323,9 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. Show all permanently hidden dialogs and warning messages again Prikaži vsa trajno skrita pogovorna okna in opozorilna sporočila +Remove old log files after x days: + + Customize context menu: Prilagodi kontekstni meni: @@ -1407,6 +1416,15 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. Highlight Configurations Označite konfiguracije +Info +Info + +No log entries + + +Select all +Izberi vse + &Options &Možnosti @@ -1652,21 +1670,12 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. Comparing content... Primerjam vsebino... -Info -Info - -Select all -Izberi vse - &Continue &Nadaljuj Progress Napredek -Log -Dnevnik - Thank you, %x, for your donation and support! Najlepša hvala, %x, za vašo donacijo in podporo! @@ -1898,6 +1907,9 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. Unable to register to receive system messages. Sistemskih sporočil ni mogoče registrirati. +The %x installation option is only available in the FreeFileSync Donation Edition. +Možnost namestitve %x je na voljo samo v FreeFileSync Donation Edition. + Cannot find system function %x. Sistemske funkcije ni mogoče najti %x. @@ -2058,9 +2070,6 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. Please choose the local installation type or select a different folder for installation. Prosimo izberite vrsto lokalne namestitve ali pa izberite drugo mapo za namestitev. -The %x installation option is only available in the FreeFileSync Donation Edition. -Možnost namestitve %x je na voljo samo v FreeFileSync Donation Edition. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Pridobite donacijsko izdajo s bonusnimi funkcijami in pomagajte ohraniti FreeFileSync brez oglasov. 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 @@ global config file: archivo de config. global: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Cualquier número de archivos de configuración de FreeFileSync (.ffs_gui ó .ffs_batch). +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Cualquier número de archivos de configuración de FreeFileSync ("ffs_gui" ó "ffs_batch"). Any number of alternative directory pairs for at most one config file. Cualquier número de pares de directorios alternativos para un archivo de configuración como máximo. @@ -334,6 +334,9 @@ Update attributes on right Actualizar atributos en la derecha +Warning +Atención + Items processed: Elementos procesados: @@ -343,6 +346,9 @@ Total time: Tiempo total: +Stopped +Detenido + Cleaning up log files: Limpiando archivos de registro: @@ -382,6 +388,15 @@ Unable to connect to %x. No se puede conectar a %x. +Completed successfully +Completado con éxito + +Completed with warnings +Completado con avisos + +Completed with errors +Completado con errores + Cannot access the Volume Shadow Copy Service. No se puede acceder al servicio de Instantánea de volumen. @@ -678,8 +693,8 @@ Reales: %y bytes 3. Press 'Start'. 3. Presione 'Inicio'. -To get started just import a .ffs_batch file. -Para comenzar, importe un archivo .ffs_batch. +To get started just import a "ffs_batch" file. +Para comenzar, importe un archivo "ffs_batch". Folders to watch: Carpetas para examinar: @@ -761,24 +776,9 @@ El comando es disparado si: System: Shut down Sistema: apagar -Stopped -Detenido - -Completed with errors -Completado con errores - -Completed with warnings -Completado con avisos - -Warning -Atención - Nothing to synchronize Nada que sincronizar -Completed successfully -Completado con éxito - Executing command %x Ejecución del comando %x @@ -830,6 +830,9 @@ El comando es disparado si: Last sync Última sincronización +Log +Registro + Folder Carpeta @@ -905,6 +908,9 @@ El comando es disparado si: Save as &batch job... Guardar como tarea por &lotes... +Show &log + + Start &comparison Iniciar la &comparación @@ -1241,10 +1247,10 @@ El comando es disparado si: Detener Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -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 +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 Progress dialog: -Diálogo de progreso: +Diálogo de progreson: Run minimized Ejecutar minimizado @@ -1303,8 +1309,11 @@ Esto garantiza un estado coherente incluso en caso de error grave. Show all permanently hidden dialogs and warning messages again Volver a mostrar todos los diálogos y mensajes de advertencia que fueron permanentemente ocultados +Remove old log files after x days: + + Customize context menu: -Personalizar menú contextual : +Personalizar menú contextual: Description Descripción @@ -1313,7 +1322,7 @@ Esto garantiza un estado coherente incluso en caso de error grave. &Configuración predeterminada Feedback and suggestions are welcome -Tus comentarios y sugerencias son bienvenidos : +Tus comentarios y sugerencias son bienvenidos: Home page Página de inicio @@ -1337,13 +1346,13 @@ Esto garantiza un estado coherente incluso en caso de error grave. Detalles para donación Source code written in C++ using: -Código fuente C++ con soporte de : +Código fuente C++ con soporte de: Published under the GNU General Public License -Publicado bajo régimen de derechos GNU General Public License : +Publicado bajo régimen de derechos GNU General Public License: Many thanks for localization: -Agradecimientos a los traductores : +Agradecimientos a los traductores: Activate the FreeFileSync Donation Edition by one of the following methods: Active FreeFileSync Donation Edition por uno de los métodos siguientes: @@ -1355,7 +1364,7 @@ Esto garantiza un estado coherente incluso en caso de error grave. Activar en línea 2. Retrieve an offline activation key from the following URL: -2. Recuperar una clave de activación sin conexión desde la dirección URL siguiente : +2. Recuperar una clave de activación sin conexión desde la dirección URL siguiente: &Copy to clipboard &Copiar al portapapeles @@ -1393,6 +1402,15 @@ Esto garantiza un estado coherente incluso en caso de error grave. Highlight Configurations Resaltar configuraciones +Info +Info + +No log entries + + +Select all +Seleccionar todo + &Options &Opciones @@ -1630,21 +1648,12 @@ Esto garantiza un estado coherente incluso en caso de error grave. Comparing content... Comparando contenido... -Info -Info - -Select all -Seleccionar todo - &Continue &Continuar Progress Progreso -Log -Registro - Thank you, %x, for your donation and support! ¡Muchas gracias, %x, por su contribución y ayuda! @@ -1870,6 +1879,9 @@ Esto garantiza un estado coherente incluso en caso de error grave. Unable to register to receive system messages. No es posible registrar la recepción de mensajes sistema. +The %x installation option is only available in the FreeFileSync Donation Edition. +La opción %x de instalación sólo está disponible para la versión FreeFileSync Donation Edition. + Cannot find system function %x. No se puede encontrar la función del sistema %x. @@ -1943,7 +1955,7 @@ Esto garantiza un estado coherente incluso en caso de error grave. Fallo al comprobar la papelera de reciclaje para la carpeta %x. The following XML elements could not be read: -No se pueden leer los siguientes elementos XML : +No se pueden leer los siguientes elementos XML: Configuration file %x is incomplete. The missing elements will be set to their default values. El archivo de configuración %x está incompleto. Los elementos ausentes se definirán con valores predeterminados. @@ -2026,9 +2038,6 @@ Esto garantiza un estado coherente incluso en caso de error grave. Please choose the local installation type or select a different folder for installation. Elija el tipo de instalación local o seleccione otra carpeta de instalación. -The %x installation option is only available in the FreeFileSync Donation Edition. -La opción %x de instalación sólo está disponible para la versión FreeFileSync Donation Edition. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Obtenga la FreeFileSync Donation Edition con sus características extra y ayude a mantener FreeFileSync libre de anuncios. 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 @@ global config file: övergripande konfigurationsfil: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Valfritt antal FreeFileSync .ffs_gui och/eller .ffs_batch konfigurationsfiler. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Valfritt antal FreeFileSync "ffs_gui" och/eller "ffs_batch" konfigurationsfiler. Any number of alternative directory pairs for at most one config file. Valfritt antal alternativa katalogpar för som mest, en konfigurationsfil. @@ -334,6 +334,9 @@ Update attributes on right Uppdatera attribut på höger sida +Warning +Varning + Items processed: Processade poster: @@ -343,6 +346,9 @@ Total time: Total tid: +Stopped +Stoppad + Cleaning up log files: Städar upp loggfiler: @@ -382,6 +388,15 @@ Unable to connect to %x. Kan inte ansluta till %x. +Completed successfully +Korrekt slutförd + +Completed with warnings +Slutförd med varningar + +Completed with errors +Slutförd med fel + Cannot access the Volume Shadow Copy Service. Kan inte komma åt tjänsten 'Volume Shadow Copy'. @@ -678,8 +693,8 @@ Aktuell: %y byte 3. Press 'Start'. 3. Tryck 'Start'. -To get started just import a .ffs_batch file. -Importera en .ffs_batch-fil för att komma igång. +To get started just import a "ffs_batch" file. +Importera en "ffs_batch"-fil för att komma igång. Folders to watch: Mappar att övervaka: @@ -761,24 +776,9 @@ Kommandot triggas om: System: Shut down System: Stäng av -Stopped -Stoppad - -Completed with errors -Slutförd med fel - -Completed with warnings -Slutförd med varningar - -Warning -Varning - Nothing to synchronize Det finns inget att synkronisera -Completed successfully -Korrekt slutförd - Executing command %x Kör kommandot %x @@ -830,6 +830,9 @@ Kommandot triggas om: Last sync Senaste synkronisering +Log +Logg + Folder Mapp @@ -905,6 +908,9 @@ Kommandot triggas om: Save as &batch job... Spara som &batch-fil... +Show &log + + Start &comparison &Jämför @@ -1303,6 +1309,9 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. Show all permanently hidden dialogs and warning messages again Visa alla permanent dolda dialoger och varningsmeddelanden igen +Remove old log files after x days: + + Customize context menu: Anpassad kontextmeny: @@ -1393,6 +1402,15 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. Highlight Configurations Färgmarkera konfigurationer +Info +Info + +No log entries + + +Select all +Markera alla + &Options &Alternativ @@ -1630,21 +1648,12 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. Comparing content... Jämför innehåll... -Info -Info - -Select all -Markera alla - &Continue &Fortsätt Progress Förlopp -Log -Logg - Thank you, %x, for your donation and support! Tack %x, för din donation och ditt stöd! @@ -1870,6 +1879,9 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. Unable to register to receive system messages. Det gick inte att registrera mottagning av systemmeddelanden. +The %x installation option is only available in the FreeFileSync Donation Edition. +Installationsalternativet %x är endast tillgängligt i FreeFileSync Donation Edition. + Cannot find system function %x. Kan inte hitta systemfunktion %x. @@ -2026,9 +2038,6 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. Please choose the local installation type or select a different folder for installation. Välj lokal installationstyp eller välj en annan mapp för installationen. -The %x installation option is only available in the FreeFileSync Donation Edition. -Installationsalternativet %x är endast tillgängligt i FreeFileSync Donation Edition. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Hämta donationsversionen med bonusfunktioner och hjälp till att hålla FreeFileSync reklamfri. 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 @@ global config file: genel yapılandırma dosyası: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -FreeFileSync .ffs_gui ya da .ffs_batch yapılandırma dosyalarının sayısı. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +FreeFileSync "ffs_gui" ya da "ffs_batch" yapılandırma dosyalarının sayısı. Any number of alternative directory pairs for at most one config file. En fazla bir yapılandırma dosyası için herhangi bir sayıda alternatif klasör çifti. @@ -334,6 +334,9 @@ Update attributes on right Sağdaki öznitelikler güncellensin +Warning +Uyarı + Items processed: İşlenen öge: @@ -343,6 +346,9 @@ Total time: Toplam süre: +Stopped +Durduruldu + Cleaning up log files: Günlük dosyaları temizleniyor: @@ -382,6 +388,15 @@ Unable to connect to %x. %x üzerine bağlanılamadı. +Completed successfully +Tamamlandı + +Completed with warnings +Uyarılar ile tamamlandı + +Completed with errors +Sorunlar ile tamamlandı + Cannot access the Volume Shadow Copy Service. Birim Gölge Hizmetine erişilemiyor. @@ -479,7 +494,7 @@ Şu klasörler birbirinden çok farklı. Lütfen eşitleme için doğru klasörleri seçtiğinizden emin olun. Not enough free disk space available in: -Şurada yeterli disk alanı yok : +Şurada yeterli disk alanı yok: Required: Zorunlu: @@ -678,8 +693,8 @@ Gerçekleşen: %y bayt 3. Press 'Start'. 3. 'Başlat' düğmesine tıklayın. -To get started just import a .ffs_batch file. -.ffs_batch dosyasını yükleyerek başlayabilirsiniz. +To get started just import a "ffs_batch" file. +"ffs_batch" dosyasını yükleyerek başlayabilirsiniz. Folders to watch: İzlenecek Klasörler: @@ -761,24 +776,9 @@ Komut şu durumlarda yürütülür: System: Shut down Sistem: Kapat -Stopped -Durduruldu - -Completed with errors -Sorunlar ile tamamlandı - -Completed with warnings -Uyarılar ile tamamlandı - -Warning -Uyarı - Nothing to synchronize Eşitlenecek bir şey yok -Completed successfully -Tamamlandı - Executing command %x %x komutu yürütülüyor @@ -830,6 +830,9 @@ Komut şu durumlarda yürütülür: Last sync Son eşitleme +Log +Günlük + Folder Klasör @@ -905,6 +908,9 @@ Komut şu durumlarda yürütülür: Save as &batch job... &Toplu İş Olarak Kaydet... +Show &log + + Start &comparison &Karşılaştırmayı Başlat @@ -1303,6 +1309,9 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y Show all permanently hidden dialogs and warning messages again Kalıcı olarak gizlenmiş tüm ileti ve uyarılar yeniden görüntülenir +Remove old log files after x days: + + Customize context menu: Sağ Tık Menüsü Uyarlamaları: @@ -1393,6 +1402,15 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y Highlight Configurations Yapılandırmalar Vurgulansın +Info +Bilgi + +No log entries + + +Select all +Tümünü Seç + &Options &Ayarlar @@ -1630,21 +1648,12 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y Comparing content... İçerik karşılaştırılıyor... -Info -Bilgi - -Select all -Tümünü Seç - &Continue &Devam Progress İlerleme -Log -Günlük - Thank you, %x, for your donation and support! Sevgili %x, bağışın ve desteğin için teşekkürler! @@ -1870,6 +1879,9 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y Unable to register to receive system messages. Sistem iletilerini alabilmek için gerekli kayıt eklenemedi. +The %x installation option is only available in the FreeFileSync Donation Edition. +%x kurulumu yalnız FreeFileSync Donation Sürümü ile yapılabilir. + Cannot find system function %x. %x sistem işlevi bulunamadı. @@ -2026,9 +2038,6 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y Please choose the local installation type or select a different folder for installation. Kurulum için farklı bir klasör ya da yerel kurulum türünü seçin. -The %x installation option is only available in the FreeFileSync Donation Edition. -%x kurulumu yalnız FreeFileSync Donation Sürümü ile yapılabilir. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Hediye özellikleri edinmek ve FreeFileSync yazılımını reklamsız kullanmak için Bağış Sürümünü alın. 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 @@ global config file: глобальний конфігураційний файл: -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Будь-яка кількість FreeFileSync .ffs_gui та/або .ffs_batch файлів конфігурації. +Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. +Будь-яка кількість FreeFileSync "ffs_gui" та/або "ffs_batch" файлів конфігурації. Any number of alternative directory pairs for at most one config file. Будь-яка кількість альтернативних пар папок для не більше одного конфігураційного файлу. @@ -336,6 +336,9 @@ Update attributes on right Оновити атрибути праворуч +Warning +Увага + Items processed: Елементів оброблено: @@ -345,6 +348,9 @@ Total time: Загальний час: +Stopped +Зупинено + Cleaning up log files: Очищення файлів журналу: @@ -385,6 +391,15 @@ Unable to connect to %x. Не вдається з'єднатися з %x. +Completed successfully +Завершено успішно + +Completed with warnings +Завершено з попередженнями + +Completed with errors +Завершено з помилками + Cannot access the Volume Shadow Copy Service. Не вдається отримати доступ до послуги Тіньового Копіювання Тому. @@ -684,8 +699,8 @@ Actual: %y bytes 3. Press 'Start'. 3. Натисніть 'Запуск'. -To get started just import a .ffs_batch file. -Щоб запустити імпортуйте .ffs_batch файл. +To get started just import a "ffs_batch" file. +Щоб запустити імпортуйте "ffs_batch" файл. Folders to watch: Папки для спостереження: @@ -767,24 +782,9 @@ The command is triggered if: System: Shut down Система: Завершення роботи -Stopped -Зупинено - -Completed with errors -Завершено з помилками - -Completed with warnings -Завершено з попередженнями - -Warning -Увага - Nothing to synchronize Нічого синхронізувати -Completed successfully -Завершено успішно - Executing command %x Виконати команду %x @@ -837,6 +837,9 @@ The command is triggered if: Last sync Остання синхронізація +Log +Лог + Folder Папка @@ -912,6 +915,9 @@ The command is triggered if: Save as &batch job... Зберегти як &пакетне завдання... +Show &log + + Start &comparison Запуск по&рівняння @@ -1310,6 +1316,9 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again Показати всі сховані діалоги і повідомлення з попередженнями знову +Remove old log files after x days: + + Customize context menu: Налаштування контекстного меню: @@ -1400,6 +1409,15 @@ This guarantees a consistent state even in case of a serious error. Highlight Configurations Налаштування виділення +Info +Інформація + +No log entries + + +Select all +Виділити все + &Options &Опції @@ -1624,7 +1642,7 @@ This guarantees a consistent state even in case of a serious error. Список файлів експортовано Searching for program updates... -Пошук оновлень програми ... +Пошук оновлень програми... Paused Призупинено @@ -1641,21 +1659,12 @@ This guarantees a consistent state even in case of a serious error. Comparing content... Порівнювання вмісту... -Info -Інформація - -Select all -Виділити все - &Continue &Продовжити Progress Прогрес -Log -Лог - Thank you, %x, for your donation and support! Дякуємо Вам, %x, за ваше пожертвування та підтримку! @@ -1884,6 +1893,9 @@ This guarantees a consistent state even in case of a serious error. Unable to register to receive system messages. Не вдається зареєструватися для отримання системних повідомлень. +The %x installation option is only available in the FreeFileSync Donation Edition. +Варіант установки %x доступний тільки у FreeFileSync Donation Edition. + Cannot find system function %x. Не вдається знайти системну функцію %x. @@ -2042,9 +2054,6 @@ This guarantees a consistent state even in case of a serious error. Please choose the local installation type or select a different folder for installation. Будь ласка, виберіть локальний тип інсталяції чи іншу папку для встановлення. -The %x installation option is only available in the FreeFileSync Donation Edition. -Варіант установки %x доступний тільки у FreeFileSync Donation Edition. - Get the Donation Edition with bonus features and help keep FreeFileSync ad-free. Отримайте Donation Edition з бонусними функціями та допоможіть зберегти FreeFileSync без реклами. diff --git a/FreeFileSync/Build/Resources.zip b/FreeFileSync/Build/Resources.zip index 48cee7b5..4fe5268b 100755 Binary files a/FreeFileSync/Build/Resources.zip and b/FreeFileSync/Build/Resources.zip differ 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 #include -#include #include #include #include @@ -31,8 +30,10 @@ void setFolderPath(const Zstring& dirpath, wxTextCtrl* txtCtrl, wxWindow& toolti const Zstring folderPathFmt = fff::getResolvedFilePath(dirpath); //may block when resolving [] - tooltipWnd.SetToolTip(nullptr); //workaround wxComboBox bug http://trac.wxwidgets.org/ticket/10512 / http://trac.wxwidgets.org/ticket/12659 - tooltipWnd.SetToolTip(utfTo(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(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(folderPathFmt)); @@ -103,7 +104,7 @@ void FolderSelector2::onFilesDropped(FileDropEvent& event) try { if (getItemType(itemPath) == ItemType::FILE) //throw FileError - if (Opt parentPath = getParentFolderPath(itemPath)) + if (std::optional 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 getFormattedDirs(const std::vector& folderPathPhrases) //throw FileError -{ - std::set 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(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 waitForMissingDirs(const std::vector& folderPathPhrases, //throw FileError const std::function& 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(protoName)) + L"\n\n" + fmtPath(phrase)); + for (;;) { - //support specifying volume by name => call getResolvedFilePath() repeatedly - std::set folderPaths = getFormattedDirs(folderPathPhrases); //throw FileError + struct FolderInfo + { + Zstring folderPathPhrase; + std::future folderAvailable; + }; + std::map folderInfos; //folderPath => FolderInfo - std::vector>> 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 availablePaths; + std::set missingPathPhrases; + for (auto& item : folderInfos) { const Zstring& folderPath = item.first; - std::future& ftDirAvailable = item.second; + std::future& 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 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& notifyStatus) { - Opt dbLoadError; //defer until after default directions have been set! + std::optional dbLoadError; //defer until after default directions have been set! //try to load sync-database files std::shared_ptr 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(__LINE__)); - Opt dbLoadError; //defer until after default directions have been set! + std::optional 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 fff::getPathDependency(const AbstractPath& basePathL, const HardFilter& filterL, - const AbstractPath& basePathR, const HardFilter& filterR) +std::optional 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 fff::getPathDependency(const AbstractPath& basePathL, const } } } - return NoValue(); + return {}; } //############################################################################################################ @@ -1211,12 +1211,12 @@ void copyToAlternateFolderFrom(const std::vector& rowsT { //start deleting existing target as required by copyFileTransactional(): //best amortized performance if "target existing" is the most common case - Opt 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& 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 getPathDependency(const AbstractPath& basePathL, const HardFilter& filterL, - const AbstractPath& basePathR, const HardFilter& filterR); +std::optional getPathDependency(const AbstractPath& basePathL, const HardFilter& filterL, + const AbstractPath& basePathR, const HardFilter& filterR); std::pair getSelectedItemsAsString( //returns string with item names and total count of selected(!) items, NOT total files/dirs! const std::vector& 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 @@ -252,7 +253,7 @@ void Application::launch(const std::vector& commandArgs) try { if (getItemType(itemPath) == ItemType::FILE) //throw FileError - if (Opt parentPath = getParentFolderPath(itemPath)) + if (std::optional parentPath = getParentFolderPath(itemPath)) return *parentPath; } catch (FileError&) {} @@ -330,7 +331,7 @@ void Application::launch(const std::vector& 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& 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& deviceParallelOps = batchCfg.mainCfg.deviceParallelOps; - std::set logFilePathsToKeep; + std::set 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& fpCfgList, const std::map& 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(activeSettings.fileTimeTolerance); if (activeSettings.folderAccessTimeout != defaultSettings.folderAccessTimeout) - changedSettingsMsg += L"\n " + _("Folder access timeout") + L" - " + numberTo(activeSettings.folderAccessTimeout); + changedSettingsMsg += L"\n " + _("Folder access timeout") + L" - " + numberTo(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& dirLocks, const std::vector& fpCfgList, @@ -1035,8 +1035,8 @@ FolderComparison fff::compare(WarningDialogs& warnings, std::wstring msg; for (const auto& w : workLoad) - if (Opt pd = getPathDependency(w.first.folderPathLeft, *w.second.filter.nameFilter, - w.first.folderPathRight, *w.second.filter.nameFilter)) + if (std::optional 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 dirPathsExisting; for (const AbstractPath& folderPath : resInfo.existingBaseFolders) - if (Opt nativePath = AFS::getNativeItemPath(folderPath)) //restrict directory locking to native paths until further + if (std::optional nativePath = AFS::getNativeItemPath(folderPath)) //restrict directory locking to native paths until further dirPathsExisting.insert(*nativePath); dirLocks = std::make_unique(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& dirLocks, //out const std::vector& 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& folderPaths, const std::map& 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& 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 #include #include -#include -//#include -//#include #include //open() #include // @@ -46,7 +43,7 @@ public: void operator()() const //throw ThreadInterruption { - const Opt parentDirPath = getParentFolderPath(lockFilePath_); + const std::optional parentDirPath = getParentFolderPath(lockFilePath_); setCurrentThreadName(("DirLock: " + (parentDirPath ? utfTo(*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 getSessionId(ProcessId processId) //throw FileError +std::optional 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(userIdNo) + "(" + pwsEntry->pw_name + ")"; //follow Linux naming convention "1000(zenju)" - Opt sessionIdTmp = getSessionId(lockInfo.processId); //throw FileError + std::optional 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 = getSessionId(lockInfo.processId)) //throw FileError + if (std::optional 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 parentPathL = AFS::getParentFolderPath(tmpPathL); - Opt parentPathR = AFS::getParentFolderPath(tmpPathR); + std::optional parentPathL = AFS::getParentFolderPath(tmpPathL); + std::optional 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(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(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(&fsObj)) if (auto targetFile = dynamic_cast(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 void removeObject(); //removes file or directory (recursively!) without physically removing the element: used by manual deletion + template 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 syncOpBuffered_; //determining sync-op for directory may be expensive as it depends on child-objects => buffer + mutable std::optional 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(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& logFilePathsToKeep, - const std::function& 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& logFilePathsToKeep, + const std::function& 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 abstractLogFilePathsToKeep; - for (const Zstring& filePath : logFilePathsToKeep) - abstractLogFilePathsToKeep.insert(createAbstractPath(filePath)); - - Opt logFilePath; + std::optional 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 #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& logFilePathsToKeep, - const std::function& 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& logFilePathsToKeep, + const std::function& 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 retrieve(const AbstractPath& filePath) + std::optional retrieve(const AbstractPath& filePath) { assert(runningMainThread()); std::lock_guard 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 IconBuffer::retrieveFileIcon(const AbstractPath& filePath) +std::optional IconBuffer::retrieveFileIcon(const AbstractPath& filePath) { - if (Opt ico = pimpl_->buffer.retrieve(filePath)) + if (std::optional 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 #include #include -#include #include #include "../fs/abstract.h" @@ -35,7 +34,7 @@ public: void setWorkload (const std::vector& load); //(re-)set new workload of icons to be retrieved; bool readyForRetrieval(const AbstractPath& filePath); - zen::Opt retrieveFileIcon (const AbstractPath& filePath); //... and mark as hot + std::optional 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; //hash_map is 15% faster than std::map on GCC using TranslationPlural = std::map, std::vector>; - Translation transMapping; //map original text |-> translation - TranslationPlural transMappingPl; - std::unique_ptr pluralParser; //bound! + Translation transMapping_; //map original text |-> translation + TranslationPlural transMappingPl_; + std::unique_ptr 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(header.pluralDefinition); //throw plural::ParsingError + + for (const auto& item : transUtf) { - const std::wstring original = utfTo(item.first); - const std::wstring translation = utfTo(item.second); - transMapping.emplace(original, translation); + std::wstring original = utfTo(item.first); + std::wstring translation = utfTo(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(item.first.first); - const std::wstring engPlural = utfTo(item.first.second); + std::wstring engSingular = utfTo(item.first.first); + std::wstring engPlural = utfTo(item.first.second); - std::vector plFormsWide; + std::vector pluralForms; for (const std::string& pf : item.second) - plFormsWide.push_back(utfTo(pf)); + pluralForms.push_back(utfTo(pf)); - transMappingPl.insert({ { engSingular, engPlural }, plFormsWide }); + transMappingPl_.insert({ { std::move(engSingular), std::move(engPlural) }, std::move(pluralForms) }); } - - pluralParser = std::make_unique(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> errorRequest_; //error message + retry number - Opt errorResponse_; + std::optional> errorRequest_; //error message + retry number + std::optional 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 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 #include #include -//#include #include #include #include @@ -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(L"Misspelled \"%x\" in translation", L"%x", utfTo(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(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(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 allTexts = [&] + { + std::vector 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(L"Placeholder %x missing in plural form source", L"%x", zen::utfTo(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(L"Placeholder %x missing in plural form translation", L"%x", zen::utfTo(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(L"Placeholder %x missing in text", L"%x", zen::utfTo(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(L"Misspelled \"%x\" in translation", L"%x", utfTo(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(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(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 Perf } -Opt PerfCheck::getRemainingTimeSec(double dataRemaining) const +std::optional 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 PerfCheck::getBytesPerSecond() const +std::optional 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 PerfCheck::getItemsPerSecond() const +std::optional 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 #include #include -#include +#include //#includes 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 getRemainingTimeSec(double dataRemaining) const; - zen::Opt getBytesPerSecond() const; //for window - zen::Opt getItemsPerSecond() const; // + std::optional getRemainingTimeSec(double bytesRemaining) const; + std::optional getBytesPerSecond() const; //for window + std::optional 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 #include #include -#include #include #include #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 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); @@ -2096,6 +2126,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); 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 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 treeGridColumnAttribs = getTreeGridDefaultColAttribs(); + size_t folderHistItemsMax = 20; + + struct + { + bool keepRelPaths = false; + bool overwriteIfExists = false; + Zstring lastUsedPath; + std::vector folderHistory; + } copyToCfg; + std::vector folderHistoryLeft; std::vector 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 #include #include -#include #include #include #include @@ -17,13 +16,13 @@ using namespace zen; namespace { -Opt getEnvironmentVar(const Zstring& name) +std::optional 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 homeDir = getEnvironmentVar("HOME"); + std::optional 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 tryResolveMacro(const Zstring& macro) //macro without %-characters +std::optional 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 tryResolveMacro(const Zstring& macro) //macro without %-characters if (resolveTimePhrase(Zstr("sec" ), Zstr("%S"))) return timeStr; //try to resolve as environment variable - if (Opt value = getEnvironmentVar(macro)) + if (std::optional 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 value = tryResolveMacro(potentialMacro)) + if (std::optional 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 value = getEnvironmentVar(envName)) + if (std::optional value = getEnvironmentVar(envName)) macroList.emplace_back(envName, *value); }; addEnvVar("HOME"); //Linux: /home/ Mac: /Users/ @@ -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 pc = parsePathComponents(path)) + if (std::optional 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 getAbortStatus() const = 0; + virtual std::optional 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 getAbortStatus() const override { return abortRequested_; } + std::optional getAbortStatus() const override { return abortRequested_; } private: void updateData(std::vector& num, int itemsDelta, int64_t bytesDelta) @@ -190,17 +190,17 @@ private: std::vector statsTotal_ = std::vector(4); // std::wstring statusText_; - zen::Opt abortRequested_; + std::optional abortRequested_; }; //------------------------------------------------------------------------------------------ inline -void delayAndCountDown(const std::wstring& operationName, size_t delayInSec, const std::function& notifyStatus) +void delayAndCountDown(const std::wstring& operationName, std::chrono::seconds delay, const std::function& 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(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 #include #include #include "process_callback.h" @@ -40,6 +39,7 @@ public: std::lock_guard 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 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 errorRequest_; - zen::Opt errorResponse_; - zen::Opt logInfoRequest_; + std::optional errorRequest_; + std::optional errorResponse_; + std::optional 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& 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& 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 #include +#include #include -#include #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& cmpCfg, - const zen::Opt& syncCfg, + const std::optional& cmpCfg, + const std::optional& 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 localCmpCfg; - zen::Opt localSyncCfg; + std::optional localCmpCfg; + std::optional 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 fff::extractSyncCfg(const MainConfiguration& main namespace { inline -Opt getTargetDirection(SyncOperation syncOp) +std::optional getTargetDirection(SyncOperation syncOp) { switch (syncOp) { @@ -412,7 +412,7 @@ Opt 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 nativeTargetPath = AFS::getNativeItemPath(targetPath)) + if (std::optional 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 getItemTypeIfExists(const AbstractPath& ap, std::mutex& singleThread) //throw FileError +std::optional 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 getFolderLevelWorkItems(PassNo pass, ContainerObject& parentFolder, Workload& workload); - template - void setup2StepMove(FilePair& sourceObj, FilePair& targetObj); //throw FileError, ThreadInterruption - bool createParentFolder(FileSystemObject& fsObj); //throw FileError, ThreadInterruption - template - void resolveMoveConflicts(FilePair& sourceObj, FilePair& targetObj); //throw FileError, ThreadInterruption + enum class CmtfStatus //CreateMoveTargetFolderStatus + { + AVAILABLE, + NAME_CLASH, + SOURCE_MISSING + }; + template void setup2StepMove(FilePair& sourceFile, FilePair& targetFile); //throw FileError, ThreadInterruption + template CmtfStatus createMoveTargetFolder(FileSystemObject& fsObj); //throw FileError, ThreadInterruption + template 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 -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(Zstr("%04x"), static_cast(getCrc16(generateGUID()))); - const Zstring fileName = sourceObj.getItemName(); + const Zstring fileName = sourceFile.getItemName(); 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(), sourceRelPathTmp); + const AbstractPath sourcePathTmp = AFS::appendRelPath(sourceFile.base().getAbstractPath(), sourceRelPathTmp); reportInfo(txtMovingFileXtoY_, //ThreadInterruption - AFS::getDisplayPath(sourceObj.getAbstractPath()), + AFS::getDisplayPath(sourceFile.getAbstractPath()), AFS::getDisplayPath(sourcePathTmp)); - parallel::renameItem(sourceObj.getAbstractPath(), sourcePathTmp, singleThread_); //throw FileError, (ErrorDifferentVolume) + parallel::renameItem(sourceFile.getAbstractPath(), sourcePathTmp, singleThread_); //throw FileError, (ErrorDifferentVolume) //TODO: prepare2StepMove: consider ErrorDifferentVolume! e.g. symlink aliasing! //update file hierarchy - FilePair& tempFile = sourceObj.base().addSubFile(afterLast(sourceRelPathTmp, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL), sourceObj.getAttributes()); + FilePair& tempFile = sourceFile.base().addSubFile(afterLast(sourceRelPathTmp, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL), sourceFile.getAttributes()); static_assert(std::is_same_v>, "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(); //remove only *after* evaluating "sourceObj, side"! + sourceFile.removeObject(); //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 +auto FolderPairSyncer::createMoveTargetFolder(FileSystemObject& fsObj) -> CmtfStatus //throw FileError, ThreadInterruption { if (auto parentFolder = dynamic_cast(&fsObj.parent())) { - if (!createParentFolder(*parentFolder)) - return false; + const CmtfStatus cmtfs = createMoveTargetFolder(*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::value; + assert(!parentFolder->isEmpty()); + + switch (parentFolder->getSyncOperation()) + { + case SO_CREATE_NEW_LEFT: + case SO_CREATE_NEW_RIGHT: + { + const AbstractPath targetPath = parentFolder->getAbstractPath(); + 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(), singleThread_)) //throw FileError + { + AsyncItemStatReporter statReporter(1, 0, acb_); + try //target existing: undefined behavior! (fail/overwrite) + { + parallel::copyNewFolder(parentFolder->getAbstractPath(), 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(parentFolder->getItemName(), + false /*isSymlinkTrg*/, + parentFolder->isFollowedSymlink()); + } + 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(); //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())); //throw ThreadInterruption + return CmtfStatus::SOURCE_MISSING; + } + } + break; + + case SO_DO_NOTHING: //!isEmpty(); 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()); + 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(&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(targetFile) == CmtfStatus::NAME_CLASH) //throw FileError, ThreadInterruption return setup2StepMove(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(FileSystemObject::retrieve(file.getMoveRef()))) { FilePair* sourceObj = &file; - assert(dynamic_cast(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 { 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 sideTrg = getTargetDirection(syncOp)) + if (std::optional sideTrg = getTargetDirection(syncOp)) { if (*sideTrg == LEFT_SIDE) synchronizeFileInt(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(); //source deleted meanwhile...nothing was done (logical point of view!) reportInfo(txtSourceItemNotFound_, AFS::getDisplayPath(file.getAbstractPath())); //throw ThreadInterruption @@ -1578,6 +1646,7 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp) if (FilePair* moveFrom = dynamic_cast(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 sideTrg = getTargetDirection(syncOp)) + if (std::optional sideTrg = getTargetDirection(syncOp)) { if (*sideTrg == LEFT_SIDE) synchronizeLinkInt(link, syncOp); @@ -1859,7 +1928,7 @@ void FolderPairSyncer::synchronizeFolder(FolderPair& folder) //throw FileError, { const SyncOperation syncOp = folder.getSyncOperation(); - if (Opt sideTrg = getTargetDirection(syncOp)) + if (std::optional sideTrg = getTargetDirection(syncOp)) { if (*sideTrg == LEFT_SIDE) synchronizeFolderInt(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(); // + 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())); //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 -bool baseFolderDrop(BaseFolderPair& baseFolder, int folderAccessTimeout, ProcessCallback& callback) +bool baseFolderDrop(BaseFolderPair& baseFolder, std::chrono::seconds folderAccessTimeout, ProcessCallback& callback) { const AbstractPath folderPath = baseFolder.getAbstractPath(); @@ -2070,7 +2137,7 @@ bool baseFolderDrop(BaseFolderPair& baseFolder, int folderAccessTimeout, Process template //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::value; const AbstractPath baseFolderPath = baseFolder.getAbstractPath(); @@ -2094,10 +2161,10 @@ bool createBaseFolder(BaseFolderPair& baseFolder, bool copyFilePermissions, int { if (baseFolder.isAvailable()) //copy file permissions { - if (Opt parentPath = AFS::getParentFolderPath(baseFolderPath)) + if (std::optional parentPath = AFS::getParentFolderPath(baseFolderPath)) if (AFS::getParentFolderPath(*parentPath)) //not device root AFS::createFolderIfMissingRecursion(*parentPath); //throw FileError - + AFS::copyNewFolder(baseFolder.getAbstractPath(), 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& syncConfig, FolderComparison& folderCmp, const std::map& deviceParallelOps, @@ -2431,8 +2498,8 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime if (std::get(*it)) //write access for (auto it2 = readWriteCheckBaseFolders.begin(); it2 != readWriteCheckBaseFolders.end(); ++it2) if (!std::get(*it2) || it < it2) //avoid duplicate comparisons - if (Opt pd = getPathDependency(std::get(*it), *std::get(*it), - std::get(*it2), *std::get(*it2))) + if (std::optional pd = getPathDependency(std::get(*it), *std::get(*it), + std::get(*it2), *std::get(*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 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 pd = getPathDependency(versioningFolderPath, NullFilter(), item.first, *item.second)) + if (std::optional 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& syncConfig, //CONTRACT: syncConfig and folderCmp correspond row-wise! FolderComparison& folderCmp, // const std::map& 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 psTmp; + std::optional 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 type = AFS::getItemTypeIfExists(fileDescr.path)) //throw FileError + if (std::optional 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 type = AFS::getItemTypeIfExists(folderPath)) //throw FileError + if (std::optional 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, LessFilePath>; //relPathOrig => @@ -411,16 +413,46 @@ bool fff::operator<(const VersioningLimitFolder& lhs, const VersioningLimitFolde void fff::applyVersioningLimit(const std::set& limitFolders, + std::chrono::seconds folderAccessTimeout, const std::map& deviceParallelOps, ProcessCallback& callback /*throw X*/) { - warn_static("what if folder does not yet exist?") + //--------- determine existing folder paths for traversal --------- + std::set foldersToRead; + { + std::set 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(), 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 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(), SymLinkHandling::DIRECT })); + std::map folderBuf; auto onError = [&](const std::wstring& msg, size_t retryNumber) { @@ -443,8 +475,6 @@ void fff::applyVersioningLimit(const std::set& limitFolde callback.reportStatus(textScanning + statusLine); //throw X }; - std::map folderBuf; - parallelDeviceTraversal(foldersToRead, folderBuf, deviceParallelOps, onError, onStatusUpdate, //throw X @@ -491,15 +521,17 @@ void fff::applyVersioningLimit(const std::set& 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& 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(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& limitFolde }); } } + } //--------- remove excess file versions --------- Protected&> folderItemCountShared(folderItemCount); @@ -528,7 +561,7 @@ void fff::applyVersioningLimit(const std::set& 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& limitFolde }, ctx.acb); if (errMsg.empty()) - if (Opt parentPath = AFS::getParentFolderPath(ctx.itemPath)) + if (std::optional 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& 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& limitFolde }, ctx.acb); if (errMsg.empty()) - if (Opt parentPath = AFS::getParentFolderPath(ctx.itemPath)) + if (std::optional 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& 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& limitFolders, + std::chrono::seconds folderAccessTimeout, const std::map& 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 AFS::getParentFolderPath(const AbstractPath& ap) +std::optional AFS::getParentFolderPath(const AbstractPath& ap) { - if (const Opt parentAfsPath = getParentAfsPath(ap.afsPath)) + if (const std::optional parentAfsPath = getParentAfsPath(ap.afsPath)) return AbstractPath(ap.afs, *parentAfsPath); - return NoValue(); + return {}; } -Opt AFS::getParentAfsPath(const AfsPath& afsPath) +std::optional 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 attr = streamIn->getAttributesBuffered()) //throw FileError + if (std::optional 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(2 * attrSourceNew.fileSize)), L"%y", numberTo(totalUnbufferedIO)) + L" [notifyUnbufferedIO]"); - Opt errorModTime; + std::optional errorModTime; try { /* @@ -210,7 +210,7 @@ AFS::FileCopyResult AFS::copyFileTransactional(const AbstractPath& apSource, con if (transactionalCopy) { - Opt parentPath = AFS::getParentFolderPath(apTarget); + std::optional 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 parentAfsPath = getParentAfsPath(afsPath); + const std::optional parentAfsPath = getParentAfsPath(afsPath); try { return { getItemType(afsPath), afsPath, {} }; //throw FileError @@ -339,12 +339,12 @@ AFS::PathStatusImpl AFS::getPathStatusViaFolderTraversal(const AfsPath& afsPath) } -Opt AFS::getItemTypeIfExists(const AbstractPath& ap) //throw FileError +std::optional 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& onBeforeFileDeletion, //optional - const std::function& onBeforeFolderDeletion) //one call for each *existing* object! +void AFS::removeFolderIfExistsRecursion(const AbstractPath& ap, //throw FileError + const std::function& onBeforeFileDeletion, //optional + const std::function& onBeforeFolderDeletion) //one call for each object! { - - //deferred recursion => save stack space and allow deletion of extremely deep hierarchies! - std::vector fileNames; - std::vector folderNames; - std::vector 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 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 fileNames; + std::vector folderNames; + std::vector 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& onBeforeFileDeletion, //optional - const std::function& 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 type = AFS::getItemTypeIfExists(ap)) //throw FileError + if (std::optional 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 #include #include -#include #include //InputStream/OutputStream support buffered stream concept #include //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 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 getNativeItemPath(const AbstractPath& ap) { return ap.afs->getNativeItemPath(ap.afsPath); } - - static zen::Opt getParentFolderPath(const AbstractPath& ap); + static std::optional 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 getItemTypeIfExists(const AbstractPath& ap); //throw FileError + static std::optional 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 getAttributesBuffered() = 0; //throw FileError + virtual std::optional getAttributesBuffered() = 0; //throw FileError }; struct OutputStreamImpl @@ -159,7 +158,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t std::unique_ptr outStream_; //bound! const AbstractPath filePath_; bool finalizeSucceeded_ = false; - zen::Opt bytesExpected_; + std::optional 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 errorModTime; //failure to set modification time + std::optional 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 getParentAfsPath(const AfsPath& afsPath); + static std::optional getParentAfsPath(const AfsPath& afsPath); struct PathStatusImpl { @@ -324,7 +323,7 @@ protected: //grant derived classes access to AbstractPath: using TraverserWorkloadImpl = std::vector /*throw X*/>>; private: - virtual zen::Opt getNativeItemPath(const AfsPath& afsPath) const { return zen::NoValue(); }; + virtual std::optional 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>(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 @@ -121,7 +121,7 @@ private: conditionNewResult_.notify_all(); } - zen::Opt>> threadGroup_; + std::optional>> 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 getAttributesBuffered() override; //throw FileError + std::optional getAttributesBuffered() override; //throw FileError private: FileInput fi_; }; -Opt InputStreamNative::getAttributesBuffered() //throw FileError +std::optional 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 getNativeItemPath(const AfsPath& afsPath) const override { return getNativePath(afsPath); } + std::optional 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 comp = parsePathComponents(resolvedPath); + const std::optional comp = parsePathComponents(resolvedPath); if (!comp) throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(nativePath)), replaceCpy(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 itemPathNative = AFS::getNativeItemPath(itemPath); + std::optional itemPathNative = AFS::getNativeItemPath(itemPath); if (!itemPathNative) throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo(__LINE__)); @@ -668,7 +668,7 @@ AbstractPath fff::createItemPathNative(const Zstring& itemPathPhrase) //noexcept AbstractPath fff::createItemPathNativeNoFormatting(const Zstring& nativePath) //noexcept { - if (const Opt comp = parsePathComponents(nativePath)) + if (const std::optional comp = parsePathComponents(nativePath)) return AbstractPath(std::make_shared(comp->rootPath), AfsPath(comp->relPath)); else //path syntax broken return AbstractPath(std::make_shared(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 logfileDir_; //always bound, solve circular compile-time dependency - EnumDescrList 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(*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(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(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& logFilePathsToKeep) //noexcept!! +BatchStatusHandler::Result BatchStatusHandler::reportFinalStatus(const Zstring& altLogFolderPathPhrase, int logfilesMaxAgeDays, const std::set& logFilePathsToKeep) //noexcept!! { const auto totalTime = std::chrono::duration_cast(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(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& logFilePathsToKeep); //noexcept!! + Result reportFinalStatus(const Zstring& altLogFolderPathPhrase, int logfilesMaxAgeDays, const std::set& 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 #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& 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(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(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(event.hoverArea_)) { case HoverAreaLog::LINK: - assert(!item->cfgItem.logFilePath.empty()); //see getRowMouseHover() try { - openWithDefaultApplication(item->cfgItem.logFilePath); //throw FileError + if (std::optional 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(event.hoverArea_)) - { - case HoverAreaLog::LINK: - return; //swallow event here before MainDialog considers it as a request to start comparison - } + switch (static_cast(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& filePaths, bo grid.clearSelection(GridEventPolicy::DENY); const std::set pathsSorted(filePaths.begin(), filePaths.end()); - Opt selectionTopRow; + std::optional 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 #include #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& 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 tmpIco = iconMgr_->refIconBuffer().retrieveFileIcon(ii.fsObj->template getAbstractPath())) + if (std::optional tmpIco = iconMgr_->refIconBuffer().retrieveFileIcon(ii.fsObj->template getAbstractPath())) 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(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 failedLoads_; //effectively a vector of size "number of rows" - Opt renderBuf_; //avoid costs of recreating this temporary variable + std::optional renderBuf_; //avoid costs of recreating this temporary variable }; @@ -1285,7 +1280,7 @@ private: bool highlightSyncAction_ = false; bool selectionInProgress_ = false; - Opt renderBuf_; //avoid costs of recreating this temporary variable + std::optional 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().swap(viewRef_); //free mem std::vector().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 #include +#include #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 struct LessSyncDirection; - zen::Opt currentSort_; + std::optional 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 FolderPairPanelBasic : private wxEvtHandler { public: - void setConfig(const zen::Opt& compConfig, const zen::Opt& syncCfg, const FilterConfig& filter) + void setConfig(const std::optional& compConfig, const std::optional& syncCfg, const FilterConfig& filter) { localCmpCfg_ = compConfig; localSyncCfg_ = syncCfg; @@ -36,8 +36,8 @@ public: refreshButtons(); } - zen::Opt getCompConfig () const { return localCmpCfg_; } - zen::Opt getSyncConfig () const { return localSyncCfg_; } + std::optional getCompConfig () const { return localCmpCfg_; } + std::optional 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 localCmpCfg_; - zen::Opt localSyncCfg_; + std::optional localCmpCfg_; + std::optional 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 [] - tooltipWnd.SetToolTip(nullptr); //workaround wxComboBox bug http://trac.wxwidgets.org/ticket/10512 / http://trac.wxwidgets.org/ticket/12659 - tooltipWnd.SetToolTip(utfTo(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(folderPathPhraseFmt)); if (staticText) { @@ -143,7 +145,7 @@ void FolderSelector::onItemPathDropped(FileDropEvent& event) try { if (AFS::getItemType(itemPath) == AFS::ItemType::FILE) //throw FileError - if (Opt parentPath = AFS::getParentFolderPath(itemPath)) + if (std::optional 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 nativeFolderPath = AFS::getNativeItemPath(folderPath)) + if (std::optional 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& logFilePathsToKeep) +StatusHandlerFloatingDialog::Result StatusHandlerFloatingDialog::reportFinalStatus(const Zstring& altLogFolderPathPhrase, int logfilesMaxAgeDays, const std::set& logFilePathsToKeep) { const auto totalTime = std::chrono::duration_cast(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(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 errorLog; bool exitAfterSync; - Zstring logFilePath; + AbstractPath logFilePath; }; - Result reportFinalStatus(int logfilesMaxAgeDays, const std::set& logFilePathsToKeep); //noexcept!! + Result reportFinalStatus(const Zstring& altLogFolderPathPhrase, int logfilesMaxAgeDays, const std::set& 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 getEntry(size_t row) const + std::optional 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 entry = msgView_.getEntry(row)) + if (std::optional entry = msgView_.getEntry(row)) switch (static_cast(colType)) { case ColumnTypeMsg::TIME: @@ -198,7 +198,7 @@ public: wxDCPenChanger dummy2(dc, getColorGridLine()); const bool drawBottomLine = [&] //don't separate multi-line messages { - if (Opt nextEntry = msgView_.getEntry(row + 1)) + if (std::optional nextEntry = msgView_.getEntry(row + 1)) return nextEntry->firstLine; return true; }(); @@ -211,7 +211,7 @@ public: } //-------------------------------------------------------- - if (Opt entry = msgView_.getEntry(row)) + if (std::optional entry = msgView_.getEntry(row)) switch (static_cast(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 #include #include -#include #include +#include #include #include "cfg_grid.h" #include "version_check.h" @@ -287,12 +287,12 @@ void MainDialog::create(const Zstring& globalConfigFilePath) GetFirstResult firstUnavailableFile; for (const Zstring& filePath : cfgFilePaths) - firstUnavailableFile.addJob([filePath]() -> Opt + firstUnavailableFile.addJob([filePath]() -> std::optional { 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 firstMissingDir; for (const AbstractPath& folderPath : folderPathsToCheck) - firstMissingDir.addJob([folderPath]() -> Opt + firstMissingDir.addJob([folderPath]() -> std::optional { 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 firstError; + std::optional firstError; try //save "GlobalSettings.xml" { writeConfig(getGlobalCfgBeforeExit(), globalConfigFilePath_); //throw FileError @@ -1095,17 +1095,15 @@ void MainDialog::copySelectionToClipboard(const std::vector& 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 colAttr = grid.getColumnConfig(); + std::vector 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(prov->getValue(row, ca.type)); clipboardString += L'\t'; @@ -1114,12 +1112,7 @@ void MainDialog::copySelectionToClipboard(const std::vector& 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 MainDialog::getTreeSelection() const if (auto root = dynamic_cast(node.get())) { //selecting root means "select everything", *ignoring* current view filter! - BaseFolderPair& baseDir = root->baseFolder; - - std::vector 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(node.get())) output.push_back(&(dir->folder)); @@ -1200,7 +1187,7 @@ void MainDialog::copyToAlternateFolder(const std::vector& 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(m_folderPathLeft ->GetValue())); folderHistoryRight_->addItem(utfTo(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& deviceParallelOps = guiCfg.mainCfg.deviceParallelOps; - - std::set logFilePathsToKeep; + + std::set 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()) //do NOT check directory existence again! - if (Opt nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath())) //restrict directory locking to native paths until further + if (std::optional nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath())) //restrict directory locking to native paths until further availableDirPaths.insert(*nativeFolderPath); if (it->isAvailable()) - if (Opt nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath())) + if (std::optional nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath())) availableDirPaths.insert(*nativeFolderPath); } dirLocks = std::make_unique(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(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& errorLog); void showLogPanel(bool show); @@ -312,7 +312,7 @@ private: std::unique_ptr firstFolderPair_; //always bound!!! std::vector additionalFolderPairs_; //additional pairs to the first pair - zen::Opt addPairCountLast_; + std::optional addPairCountLast_; //------------------------------------- //*********************************************** @@ -343,7 +343,7 @@ private: std::unique_ptr 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 #include #include -//#include -//#include //wxTextDataObject #include #include #include #include -//#include #include #include #include -//#include #include #include #include -//#include -//#include #include #include #include -//#include #include -//#include #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 bps = perf_.getBytesPerSecond(); - Opt ips = perf_.getItemsPerSecond(); + std::optional bps = perf_.getBytesPerSecond(); + std::optional 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 remTimeSec = perf_.getRemainingTimeSec(bytesTotal - bytesCurrent); + std::optional remTimeSec = perf_.getRemainingTimeSec(bytesTotal - bytesCurrent); setText(*m_staticTextTimeRemaining, remTimeSec ? formatRemainingTime(*remTimeSec) : L"-", &layoutChanged); } @@ -480,7 +472,7 @@ private: std::chrono::duration(upperEnd).count() }; } - Opt getLessEq(double x) const override //x: seconds since begin + std::optional getLessEq(double x) const override //x: seconds since begin { const auto timeX = std::chrono::duration_cast(std::chrono::duration(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(it->first).count(), it->second); } - Opt getGreaterEq(double x) const override + std::optional getGreaterEq(double x) const override { const std::chrono::nanoseconds timeX(static_cast(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(it->first).count(), it->second); } @@ -1134,8 +1126,8 @@ void SyncProgressDialogImpl::updateProgressGui(bool allowYield) perf_.addSample(timeElapsed, itemsCurrent, bytesCurrent); //current speed -> Win 7 copy uses 1 sec update interval instead - Opt bps = perf_.getBytesPerSecond(); - Opt ips = perf_.getItemsPerSecond(); + std::optional bps = perf_.getBytesPerSecond(); + std::optional 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::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 remTimeSec = perf_.getRemainingTimeSec(bytesTotal - bytesCurrent); + std::optional 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(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 getCompConfig() const; + std::optional getCompConfig() const; void setCompConfig(const CompConfig* compCfg); void updateCompGui(); CompareVariant localCmpVar_ = CompareVariant::TIME_SIZE; + std::set devicePathsForEdit_; //helper data for deviceParallelOps + std::map 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 getSyncConfig() const; + std::optional 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 getDeviceParallelOps_; + const std::function setDeviceParallelOps_; + + FolderSelector versioningFolder_; + EnumDescrList enumVersioningStyle_; + + FolderSelector logfileDir_; + EnumDescrList 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 devicePathsForEdit_; //helper data for deviceParallelOps - std::map 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 enumVersioningStyle_; - FolderSelector versioningFolder_; - //----------------------------------------------------- void selectFolderPairConfig(int newPairIndexToShow); @@ -223,17 +230,16 @@ ConfigDialog::ConfigDialog(wxWindow* parent, std::vector& 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 ConfigDialog::getCompConfig() const +std::optional 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 ConfigDialog::getSyncConfig() const +std::optional 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(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(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 compCfg = getCompConfig(); - Opt syncCfg = getSyncConfig(); + std::optional compCfg = getCompConfig(); + std::optional 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(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 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 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 renderBuf_; //avoid costs of recreating this temporary variable + std::optional 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 -#include #include #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 fff::automaticUpdateCheckPrepare() struct fff::UpdateCheckResult { - UpdateCheckResult(const std::string& ver, const Opt& err, bool alive) : onlineVersion(ver), error(err), internetIsAlive(alive) {} + UpdateCheckResult(const std::string& ver, const std::optional& err, bool alive) : onlineVersion(ver), error(err), internetIsAlive(alive) {} std::string onlineVersion; - Opt error; + std::optional error; bool internetIsAlive = false; }; @@ -258,7 +258,7 @@ std::shared_ptr fff::automaticUpdateCheckRunAsync(const Updat try { const std::string onlineVersion = getOnlineVersion(resultPrep->postParameters); //throw SysError - return std::make_shared(onlineVersion, NoValue(), true); + return std::make_shared(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& 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); } } diff --git a/wx+/dc.h b/wx+/dc.h index 23c70d3f..ff2f81bd 100755 --- a/wx+/dc.h +++ b/wx+/dc.h @@ -8,7 +8,6 @@ #define DC_H_4987123956832143243214 #include -#include #include #include //for macro: wxALWAYS_NATIVE_DOUBLE_BUFFER #include @@ -104,7 +103,7 @@ private: //associate "active" clipping area with each DC static std::unordered_map& refDcToAreaMap() { static std::unordered_map clippingAreas; return clippingAreas; } - Opt oldRect_; + std::optional oldRect_; wxDC& dc_; }; @@ -114,13 +113,13 @@ private: #endif #if wxALWAYS_NATIVE_DOUBLE_BUFFER -struct BufferedPaintDC : public wxPaintDC { BufferedPaintDC(wxWindow& wnd, Opt& buffer) : wxPaintDC(&wnd) {} }; +struct BufferedPaintDC : public wxPaintDC { BufferedPaintDC(wxWindow& wnd, std::optional& buffer) : wxPaintDC(&wnd) {} }; #else class BufferedPaintDC : public wxMemoryDC { public: - BufferedPaintDC(wxWindow& wnd, Opt& buffer) : buffer_(buffer), paintDc_(&wnd) + BufferedPaintDC(wxWindow& wnd, std::optional& 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& buffer_; + std::optional& 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 SparseCurveData::getPoints(double minX, double maxX, con for (int i = posFrom; i <= posTo; ++i) { const double x = cvrtX.screenToReal(i); - Opt ptLe = getLessEq(x); - Opt ptGe = getGreaterEq(x); + std::optional ptLe = getLessEq(x); + std::optional 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 #include #include -#include //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 getLessEq (double x) const = 0; - virtual Opt getGreaterEq(double x) const = 0; + virtual std::optional getLessEq (double x) const = 0; + virtual std::optional getGreaterEq(double x) const = 0; private: std::vector getPoints(double minX, double maxX, const wxSize& areaSizePx) const override; @@ -80,21 +79,21 @@ struct ArrayCurveData : public SparseCurveData private: std::pair getRangeX() const override { const size_t sz = getSize(); return { 0.0, sz == 0 ? 0.0 : sz - 1.0}; } - Opt getLessEq(double x) const override + std::optional getLessEq(double x) const override { const size_t sz = getSize(); const size_t pos = std::min(std::floor(x), sz - 1); //[!] expect unsigned underflow if empty! if (pos < sz) return CurvePoint(pos, getValue(pos)); - return NoValue(); + return {}; } - Opt getGreaterEq(double x) const override + std::optional getGreaterEq(double x) const override { const size_t pos = std::max(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 newLabelFmt = nullptr) { @@ -272,18 +271,18 @@ public: private: friend class Graph2D; - Opt minX; //x-range to visualize - Opt maxX; // + std::optional minX; //x-range to visualize + std::optional maxX; // - Opt minY; //y-range to visualize - Opt maxY; // + std::optional minY; //y-range to visualize + std::optional maxY; // PosLabelX labelposX = LABEL_X_BOTTOM; - Opt xLabelHeight; + std::optional xLabelHeight; std::shared_ptr labelFmtX = std::make_shared(); PosLabelY labelposY = LABEL_Y_LEFT; - Opt yLabelWidth; + std::optional yLabelWidth; std::shared_ptr labelFmtY = std::make_shared(); std::map cornerTexts; @@ -337,7 +336,7 @@ private: MainAttributes attr_; //global attributes - Opt doubleBuffer_; + std::optional doubleBuffer_; using CurveList = std::vector, 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 doubleBuffer_; + std::optional doubleBuffer_; }; //---------------------------------------------------------------------------------------------------------------- @@ -689,12 +687,12 @@ private: activeResizing_.reset(); activeClickOrMove_.reset(); - if (Opt action = refParent().clientPosToColumnAction(event.GetPosition())) + if (std::optional 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 colWidth = refParent().getColWidth(action->col)) + if (std::optional colWidth = refParent().getColWidth(action->col)) activeResizing_ = std::make_unique(*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 colType = refParent().colToType(activeClickOrMove_->getColumnFrom())) + if (const std::optional 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 action = refParent().clientPosToColumnAction(event.GetPosition())) + if (std::optional action = refParent().clientPosToColumnAction(event.GetPosition())) if (action->wantResize) { //auto-size visible range on double-click @@ -790,7 +788,7 @@ private: } else { - if (const Opt action = refParent().clientPosToColumnAction(event.GetPosition())) + if (const std::optional 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 action = refParent().clientPosToColumnAction(event.GetPosition())) + if (const std::optional action = refParent().clientPosToColumnAction(event.GetPosition())) { - if (const Opt colType = refParent().colToType(action->col)) + if (const std::optional 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 activeResizing_; std::unique_ptr activeClickOrMove_; - Opt highlightCol_; //column during mouse-over + std::optional 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::clientPosToColumnAction(const wxPoint& pos) const +std::optional Grid::clientPosToColumnAction(const wxPoint& pos) const { const int absPosX = CalcUnscrolledPosition(pos).x; if (absPosX >= 0) @@ -1851,7 +1849,7 @@ Opt Grid::clientPosToColumnAction(const wxPoint& pos) const } } } - return NoValue(); + return {}; } diff --git a/wx+/grid.h b/wx+/grid.h index b9a04842..732a4fcb 100755 --- a/wx+/grid.h +++ b/wx+/grid.h @@ -12,7 +12,6 @@ #include #include #include -#include //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()) { assert(rowFirst <= rowLast); } + mouseClick_(mouseClick ? *mouseClick : std::optional()) { 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 mouseClick_; //filled unless selection was performed via keyboard shortcuts + const std::optional 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 getColStretchedWidths(int clientWidth) const; //final width = (normalized) (stretchedWidth + offset) - Opt getColWidth(size_t col) const + std::optional 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 clientPosToColumnAction(const wxPoint& pos) const; + std::optional 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 std::vector makeConsistent(const std::vector& attribs, const std::vector& defaults) { - using ColTypeReal = decltype(ColAttrReal().type); - std::vector output; - - std::set usedTypes; //remove duplicates - auto appendUnique = [&](const std::vector& 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 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>> result_; using TaskType = FunctionReturnTypeT; - Opt> threadGroup_{ ThreadGroup(std::max(std::thread::hardware_concurrency(), 1), "xBRZ Scaler") }; + std::optional> threadGroup_{ ThreadGroup(std::max(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(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(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((255 - r + 255 - g + 255 - b) / 3); //mixed mode arithmetics! - } - } -} - - std::vector> 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((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); // diff --git a/wx+/rtl.h b/wx+/rtl.h index e479c5a8..26380f9d 100755 --- a/wx+/rtl.h +++ b/wx+/rtl.h @@ -7,7 +7,6 @@ #ifndef RTL_H_0183487180058718273432148 #define RTL_H_0183487180058718273432148 -#include #include #include #include @@ -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& buffer); +void drawBitmapRtlMirror (wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, std::optional& 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& buffer) +void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, std::optional& 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(unsigned char b) { ret template <> inline unsigned char rotateBlendInfo(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 zen::parsePathComponents(const Zstring& itemPath) +std::optional zen::parsePathComponents(const Zstring& itemPath) { - auto doParse = [&](int sepCountVolumeRoot, bool rootWithSep) -> Opt + auto doParse = [&](int sepCountVolumeRoot, bool rootWithSep) -> std::optional { const Zstring itemPathFmt = appendSeparator(itemPath); //simplify analysis of root without separator, e.g. \\server-name\share int sepCount = 0; @@ -47,7 +47,7 @@ Opt zen::parsePathComponents(const Zstring& itemPath) return PathComponents({ rootPath, relPath }); } - return NoValue(); + return {}; }; if (startsWith(itemPath, "/")) @@ -72,17 +72,17 @@ Opt zen::parsePathComponents(const Zstring& itemPath) } //we do NOT support relative paths! - return NoValue(); + return {}; } -Opt zen::getParentFolderPath(const Zstring& itemPath) +std::optional zen::getParentFolderPath(const Zstring& itemPath) { - if (const Opt comp = parsePathComponents(itemPath)) + if (const std::optional 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 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 parentPath = getParentFolderPath(itemPath); + const std::optional parentPath = getParentFolderPath(itemPath); try { return { getItemType(itemPath), itemPath, {} }; //throw FileError @@ -145,12 +145,12 @@ PathStatus zen::getPathStatus(const Zstring& itemPath) //throw FileError } -Opt zen::getItemTypeIfExists(const Zstring& itemPath) //throw FileError +std::optional 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 errorModTime; + std::optional 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 parsePathComponents(const Zstring& itemPath); //no value on failure +std::optional parsePathComponents(const Zstring& itemPath); //no value on failure -Opt getParentFolderPath(const Zstring& itemPath); +std::optional getParentFolderPath(const Zstring& itemPath); //POSITIVE existence checks; if false: 1. item not existing 2. different type 3.device access error or similar bool fileAvailable(const Zstring& filePath); //noexcept @@ -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 getItemTypeIfExists(const Zstring& itemPath); //throw FileError +std::optional 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 errorModTime; //failure to set modification time + std::optional 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 #include -#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 - #include //std::byte - + #include 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 constexpr bool is_same_v = is_same::value; - template constexpr bool is_const_v = is_const::value; - template constexpr bool is_signed_v = is_signed::value; - template constexpr bool is_trivially_destructible_v = is_trivially_destructible::value; -#endif - -#if __GNUC__ < 7 || (__GNUC__ == 7 && __GNUC_MINOR__ < 1) //GCC doesn't define __cpp_lib_raw_memory_algorithms -template 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 -ForwardIt uninitialized_move(InputIt first, InputIt last, ForwardIt trg_first) -{ - typedef typename std::iterator_traits::value_type Value; - ForwardIt current = trg_first; - try - { - for (; first != last; ++first, ++current) - ::new (static_cast(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 -constexpr decltype(auto) apply(F&& f, std::tuple& 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 using bool_constant = integral_constant; -#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(static_cast(static_cast(__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 - - -namespace zen -{ -/* -Optional return value without heap memory allocation! - -> interface like a pointer, performance like a value - - Usage: - ------ - Opt someFunction(); -{ - if (allIsWell) - return enumVal; - else - return NoValue(); -} - - Opt optValue = someFunction(); - if (optValue) - ... use *optValue ... -*/ - -struct NoValue {}; - -template -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 - { - 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(&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 rawMem_; //don't require T to be default-constructible! - bool valid_ = false; -}; - - -template inline -bool operator==(const Opt& lhs, const Opt& rhs) -{ - if (static_cast(lhs) != static_cast(rhs)) - return false; - if (!lhs) - return true; - return *lhs == *rhs; -} -template inline -bool operator!=(const Opt& lhs, const Opt& 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 type = getItemTypeIfExists(itemPath); //throw FileError + const std::optional 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 #include #include -#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 firstError; + std::optional 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 #include #include -#include "type_traits.h" +#include "string_traits.h" #include "build_info.h" @@ -42,8 +42,11 @@ void append(std::map& m, const C& c); template void removeDuplicates(std::vector& v); +template +void removeDuplicates(std::vector& v, CompLess less); + //binary search returning an iterator -template +template Iterator binary_search(Iterator first, Iterator last, const T& value, CompLess less); template @@ -70,6 +73,9 @@ struct StringHash }; +//why, oh wy is there no std::optional::get()??? +template inline T* get( std::optional& opt) { return opt ? &*opt : nullptr; } +template inline const T* get(const std::optional& opt) { return opt ? &*opt : nullptr; } @@ -117,15 +123,29 @@ template void append(std::map& m, const C& c) { m.insert(c.begin(), c.end()); } +template inline +void removeDuplicates(std::vector& v, CompLess less, CompEqual eq) +{ + std::sort(v.begin(), v.end(), less); + v.erase(std::unique(v.begin(), v.end(), eq), v.end()); +} + + +template inline +void removeDuplicates(std::vector& v, CompLess less) +{ + removeDuplicates(v, less, [&](const auto& lhs, const auto& rhs) { return !less(lhs, rhs) && !less(rhs, lhs); }); +} + + template inline void removeDuplicates(std::vector& v) { - std::sort(v.begin(), v.end()); - v.erase(std::unique(v.begin(), v.end()), v.end()); + removeDuplicates(v, std::less(), std::equal_to()); } -template inline +template inline Iterator binary_search(Iterator first, Iterator last, const T& value, CompLess less) { static_assert(std::is_same_v::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 #include "scope_guard.h" #include "ring_buffer.h" -#include "optional.h" #include "string_tools.h" @@ -97,13 +96,13 @@ public: GetFirstResult(); template - void addJob(Fun&& f); //f must return a zen::Opt containing a value if successful + void addJob(Fun&& f); //f must return a std::optional containing a value if successful template 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 get() const; //may be called only once! + std::optional get() const; //may be called only once! private: class AsyncResult; @@ -323,7 +322,7 @@ class GetFirstResult::AsyncResult { public: //context: worker threads - void reportFinished(Opt&& result) + void reportFinished(std::optional&& result) { { std::lock_guard dummy(lockResult_); @@ -342,7 +341,7 @@ public: return conditionJobDone_.wait_for(dummy, duration, [&] { return this->jobDone(jobsTotal); }); } - Opt getResult(size_t jobsTotal) + std::optional getResult(size_t jobsTotal) { std::unique_lock dummy(lockResult_); conditionJobDone_.wait(dummy, [&] { return this->jobDone(jobsTotal); }); @@ -355,7 +354,7 @@ private: std::mutex lockResult_; size_t jobsFinished_ = 0; // - Opt result_; //our condition is: "have result" or "jobsFinished_ == jobsTotal" + std::optional result_; //our condition is: "have result" or "jobsFinished_ == jobsTotal" std::condition_variable conditionJobDone_; }; @@ -367,7 +366,7 @@ GetFirstResult::GetFirstResult() : asyncResult_(std::make_shared template template inline -void GetFirstResult::addJob(Fun&& f) //f must return a zen::Opt containing a value on success +void GetFirstResult::addJob(Fun&& f) //f must return a std::optional containing a value on success { std::thread t([asyncResult = this->asyncResult_, f = std::forward(f)] { asyncResult->reportFinished(f()); }); ++jobsTotal_; @@ -381,7 +380,7 @@ bool GetFirstResult::timedWait(const Duration& duration) const { return async template inline -Opt GetFirstResult::get() const { return asyncResult_->getResult(jobsTotal_); } +std::optional GetFirstResult::get() const { return asyncResult_->getResult(jobsTotal_); } //------------------------------------------------------------------------------------------ diff --git a/zen/utf.h b/zen/utf.h index 48269416..da6aaf97 100755 --- a/zen/utf.h +++ b/zen/utf.h @@ -10,7 +10,6 @@ #include #include #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 getNext() + std::optional 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 getNext() + std::optional 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 //UTF8-char { public: UtfDecoderImpl(const CharType* str, size_t len) : decoder_(reinterpret_cast(str), len) {} - Opt getNext() { return decoder_.getNext(); } + std::optional getNext() { return decoder_.getNext(); } private: Utf8Decoder decoder_; }; @@ -279,7 +278,7 @@ class UtfDecoderImpl //Windows: UTF16-wchar_t { public: UtfDecoderImpl(const CharType* str, size_t len) : decoder_(reinterpret_cast(str), len) {} - Opt getNext() { return decoder_.getNext(); } + std::optional getNext() { return decoder_.getNext(); } private: Utf16Decoder decoder_; }; @@ -290,10 +289,10 @@ class UtfDecoderImpl //other OS: UTF32-wchar_t { public: UtfDecoderImpl(const CharType* str, size_t len) : it_(reinterpret_cast(str)), last_(it_ + len) {} - Opt getNext() + std::optional getNext() { if (it_ == last_) - return NoValue(); + return {}; return *it_++; } private: @@ -314,7 +313,7 @@ bool isValidUtf(const UtfString& str) using namespace impl; UtfDecoder> decoder(strBegin(str), strLength(str)); - while (Opt cp = decoder.getNext()) + while (std::optional 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 decoder(strBegin(str), strLength(str)); - for (size_t uniPos = 0; Opt cp = decoder.getNext(); ++uniPos) //[!] declaration in condition part of the for-loop + for (size_t uniPos = 0; std::optional 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 decoder(strBegin(str), strLength(str)); - while (Opt cp = decoder.getNext()) + while (std::optional cp = decoder.getNext()) codePointToUtf(*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 decR(rhs, rhsLen); for (;;) { - const Opt cpL = decL.getNext(); - const Opt cpR = decR.getNext(); + const std::optional cpL = decL.getNext(); + const std::optional cpR = decR.getNext(); if (!cpL || !cpR) return static_cast(!cpR) - static_cast(!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 ::value>; -namespace impl_2384343 +template +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 -using IsStlPair = std::bool_constant< - impl_2384343::HasMemberType_first_type ::value && - impl_2384343::HasMemberType_second_type::value && - impl_2384343::HasMember_first ::value && - impl_2384343::HasMember_second ::value>; +private: + using Yes = char[1]; + using No = char[2]; + + template + static Yes& isPair(const std::pair&); + static No& isPair(...); +public: + enum { value = sizeof(isPair(std::declval())) == 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 #include #include @@ -101,21 +102,37 @@ template void writeText(const T& value, std::string& output); //------------------------------ implementation ------------------------------------- +template +struct IsChronoDuration +{ +private: + using Yes = char[1]; + using No = char[2]; + + template + static Yes& isDuration(std::chrono::duration); + static No& isDuration(...); +public: + enum { value = sizeof(isDuration(std::declval())) == 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 struct GetTextType : std::integral_constant ? TEXT_TYPE_BOOL : - IsStringLikeV ? TEXT_TYPE_STRING : //string before number to correctly handle char/wchar_t -> this was an issue with Loki only! - IsArithmetic::value ? TEXT_TYPE_NUMBER : // + std::is_same_v ? TEXT_TYPE_BOOL : + IsStringLikeV ? TEXT_TYPE_STRING : //string before number to correctly handle char/wchar_t -> this was an issue with Loki only! + IsArithmetic::value ? TEXT_TYPE_NUMBER : // + IsChronoDuration::value ? TEXT_TYPE_CHRONO : TEXT_TYPE_OTHER> {}; //###################################################################################### @@ -165,6 +182,20 @@ struct ConvertText } }; +template +struct ConvertText +{ + void writeText(const T& value, std::string& output) const + { + output = numberTo(value.count()); + } + bool readText(const std::string& input, T& value) const + { + value = T(stringTo(input)); + return true; + } +}; + //partial specialization: handle conversion for all string-like types! template struct ConvertText 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 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: 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(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_); -- cgit