From 75c07011b7c4d06acd7b45dabdcd60ab9d80f385 Mon Sep 17 00:00:00 2001 From: Daniel Wilhelm Date: Fri, 18 Apr 2014 17:29:28 +0200 Subject: 5.23 --- Application.cpp | 619 --- Application.h | 34 - BUILD/Changelog.txt | 1331 ------ BUILD/FreeFileSync.chm | Bin 416035 -> 0 bytes BUILD/Help/FreeFileSync.hhc | 92 - BUILD/Help/FreeFileSync.hhp | 31 - BUILD/Help/html/Command line.html | 129 - BUILD/Help/html/Comparison Settings.html | 130 - BUILD/Help/html/Daylight Saving Time.html | 74 - BUILD/Help/html/Exclude Items.html | 147 - BUILD/Help/html/Expert settings.html | 89 - BUILD/Help/html/External Applications.html | 64 - BUILD/Help/html/FreeFileSync.html | 82 - BUILD/Help/html/Links.html | 35 - BUILD/Help/html/Macros.html | 133 - BUILD/Help/html/RealtimeSync.html | 147 - BUILD/Help/html/Run as Service.html | 60 - BUILD/Help/html/Schedule a Batch Job.html | 120 - BUILD/Help/html/Synchronize with FTP.html | 77 - BUILD/Help/html/Variable Drive Letters.html | 70 - BUILD/Help/html/Versioning.html | 94 - BUILD/Help/html/Volume Shadow Copy.html | 67 - BUILD/Help/img/CmpSettings.png | Bin 16851 -> 0 bytes BUILD/Help/img/CompareButton.png | Bin 4438 -> 0 bytes BUILD/Help/img/FFS_logo.png | Bin 8711 -> 0 bytes BUILD/Help/img/MainDialog.png | Bin 69246 -> 0 bytes BUILD/Help/img/RTS_logo.png | Bin 6798 -> 0 bytes BUILD/Help/img/RealtimeSync.png | Bin 24081 -> 0 bytes BUILD/Help/img/ScheduleBatch.png | Bin 23027 -> 0 bytes BUILD/Help/img/SetupBatch.png | Bin 19598 -> 0 bytes BUILD/Help/img/SourceTarget.png | Bin 4649 -> 0 bytes BUILD/Help/img/SyncConfigButton.png | Bin 2530 -> 0 bytes BUILD/Help/img/SynchronizeButton.png | Bin 4943 -> 0 bytes BUILD/Help/img/VolumeName.png | Bin 6095 -> 0 bytes BUILD/Help/img/WatchUsbInsert.png | Bin 32665 -> 0 bytes BUILD/Help/img/create_shortcut.png | Bin 16019 -> 0 bytes BUILD/Help/img/schedule_realtimesync.png | Bin 29315 -> 0 bytes BUILD/Help/img/shortcut_properties.png | Bin 30148 -> 0 bytes BUILD/Help/img/ubuntuScheduler.png | Bin 53376 -> 0 bytes BUILD/Help/img/win7scheduler.png | Bin 28814 -> 0 bytes BUILD/HideConsole.vbs | 17 - BUILD/Languages/arabic.lng | 1557 ------- BUILD/Languages/chinese_simple.lng | 1499 ------- BUILD/Languages/chinese_traditional.lng | 1504 ------- BUILD/Languages/croatian.lng | 1527 ------- BUILD/Languages/czech.lng | 1527 ------- BUILD/Languages/danish.lng | 1516 ------- BUILD/Languages/dutch.lng | 1516 ------- BUILD/Languages/english_uk.lng | 1523 ------- BUILD/Languages/finnish.lng | 1510 ------- BUILD/Languages/french.lng | 1519 ------- BUILD/Languages/german.lng | 1523 ------- BUILD/Languages/greek.lng | 1515 ------- BUILD/Languages/hebrew.lng | 1518 ------- BUILD/Languages/hungarian.lng | 1521 ------- BUILD/Languages/italian.lng | 1519 ------- BUILD/Languages/japanese.lng | 1508 ------- BUILD/Languages/korean.lng | 1501 ------- BUILD/Languages/outdated/lithuanian.lng | 1526 ------- BUILD/Languages/outdated/norwegian.lng | 1506 ------- BUILD/Languages/polish.lng | 1524 ------- BUILD/Languages/portuguese.lng | 1517 ------- BUILD/Languages/portuguese_br.lng | 1523 ------- BUILD/Languages/romanian.lng | 1521 ------- BUILD/Languages/russian.lng | 1534 ------- BUILD/Languages/scottish_gaelic.lng | 1543 ------- BUILD/Languages/serbian.lng | 1530 ------- BUILD/Languages/slovenian.lng | 1539 ------- BUILD/Languages/spanish.lng | 1517 ------- BUILD/Languages/swedish.lng | 1519 ------- BUILD/Languages/turkish.lng | 1516 ------- BUILD/Languages/ukrainian.lng | 1527 ------- BUILD/Resources.zip | Bin 260187 -> 0 bytes BUILD/Sync_Complete.wav | Bin 460504 -> 0 bytes BUILD/styles.gtk_rc | 8 - FreeFileSync.vcxproj | 311 -- FreeFileSync/Build/Changelog.txt | 1352 ++++++ FreeFileSync/Build/Help/FreeFileSync.hhc | 92 + FreeFileSync/Build/Help/FreeFileSync.hhp | 31 + FreeFileSync/Build/Help/html/Command Line.html | 140 + .../Build/Help/html/Comparison Settings.html | 104 + .../Build/Help/html/Daylight Saving Time.html | 62 + FreeFileSync/Build/Help/html/Exclude Items.html | 136 + FreeFileSync/Build/Help/html/Expert Settings.html | 86 + .../Build/Help/html/External Applications.html | 60 + FreeFileSync/Build/Help/html/FreeFileSync.html | 58 + FreeFileSync/Build/Help/html/Links.html | 29 + FreeFileSync/Build/Help/html/Macros.html | 147 + FreeFileSync/Build/Help/html/RealtimeSync.html | 125 + FreeFileSync/Build/Help/html/Run as Service.html | 56 + .../Build/Help/html/Schedule a Batch Job.html | 90 + .../Build/Help/html/Synchronize with FTP.html | 56 + .../Build/Help/html/Variable Drive Letters.html | 59 + FreeFileSync/Build/Help/html/Versioning.html | 89 + .../Build/Help/html/Volume Shadow Copy.html | 60 + FreeFileSync/Build/Help/html/base.css | 49 + FreeFileSync/Build/Help/img/CmpSettings.png | Bin 0 -> 16851 bytes FreeFileSync/Build/Help/img/CompareButton.png | Bin 0 -> 4438 bytes FreeFileSync/Build/Help/img/FFS_logo.png | Bin 0 -> 8711 bytes FreeFileSync/Build/Help/img/MainDialog.png | Bin 0 -> 69246 bytes FreeFileSync/Build/Help/img/RTS_logo.png | Bin 0 -> 6798 bytes FreeFileSync/Build/Help/img/RealtimeSync.png | Bin 0 -> 24081 bytes FreeFileSync/Build/Help/img/ScheduleBatch.png | Bin 0 -> 23027 bytes FreeFileSync/Build/Help/img/SetupBatch.png | Bin 0 -> 19598 bytes FreeFileSync/Build/Help/img/SourceTarget.png | Bin 0 -> 4649 bytes FreeFileSync/Build/Help/img/SyncConfigButton.png | Bin 0 -> 2530 bytes FreeFileSync/Build/Help/img/SynchronizeButton.png | Bin 0 -> 4943 bytes FreeFileSync/Build/Help/img/VolumeName.png | Bin 0 -> 6095 bytes FreeFileSync/Build/Help/img/WatchUsbInsert.png | Bin 0 -> 32665 bytes FreeFileSync/Build/Help/img/create_shortcut.png | Bin 0 -> 16019 bytes .../Build/Help/img/schedule_realtimesync.png | Bin 0 -> 29315 bytes .../Build/Help/img/shortcut_properties.png | Bin 0 -> 30148 bytes FreeFileSync/Build/Help/img/ubuntuScheduler.png | Bin 0 -> 53376 bytes FreeFileSync/Build/Help/img/win7scheduler.png | Bin 0 -> 28814 bytes FreeFileSync/Build/Languages/arabic.lng | 1557 +++++++ FreeFileSync/Build/Languages/chinese_simple.lng | 1499 +++++++ .../Build/Languages/chinese_traditional.lng | 1504 +++++++ FreeFileSync/Build/Languages/croatian.lng | 1533 +++++++ FreeFileSync/Build/Languages/czech.lng | 1521 +++++++ FreeFileSync/Build/Languages/danish.lng | 1510 +++++++ FreeFileSync/Build/Languages/dutch.lng | 1510 +++++++ FreeFileSync/Build/Languages/english_uk.lng | 1526 +++++++ FreeFileSync/Build/Languages/finnish.lng | 1513 +++++++ FreeFileSync/Build/Languages/french.lng | 1513 +++++++ FreeFileSync/Build/Languages/german.lng | 1526 +++++++ FreeFileSync/Build/Languages/greek.lng | 1515 +++++++ FreeFileSync/Build/Languages/hebrew.lng | 1515 +++++++ FreeFileSync/Build/Languages/hungarian.lng | 1515 +++++++ FreeFileSync/Build/Languages/italian.lng | 1517 +++++++ FreeFileSync/Build/Languages/japanese.lng | 1502 +++++++ FreeFileSync/Build/Languages/korean.lng | 1504 +++++++ .../Build/Languages/outdated/lithuanian.lng | 1520 +++++++ .../Build/Languages/outdated/norwegian.lng | 1500 +++++++ FreeFileSync/Build/Languages/outdated/turkish.lng | 1510 +++++++ FreeFileSync/Build/Languages/polish.lng | 1524 +++++++ FreeFileSync/Build/Languages/portuguese.lng | 1517 +++++++ FreeFileSync/Build/Languages/portuguese_br.lng | 1517 +++++++ FreeFileSync/Build/Languages/romanian.lng | 1524 +++++++ FreeFileSync/Build/Languages/russian.lng | 1537 +++++++ FreeFileSync/Build/Languages/scottish_gaelic.lng | 1543 +++++++ FreeFileSync/Build/Languages/serbian.lng | 1530 +++++++ FreeFileSync/Build/Languages/slovenian.lng | 1539 +++++++ FreeFileSync/Build/Languages/spanish.lng | 1517 +++++++ FreeFileSync/Build/Languages/swedish.lng | 1513 +++++++ FreeFileSync/Build/Languages/ukrainian.lng | 1527 +++++++ FreeFileSync/Build/Resources.zip | Bin 0 -> 260256 bytes FreeFileSync/Build/Sync_Complete.wav | Bin 0 -> 460504 bytes FreeFileSync/Build/styles.gtk_rc | 8 + FreeFileSync/Source/FreeFileSync.vcxproj | 282 ++ FreeFileSync/Source/LICENSE | 621 +++ FreeFileSync/Source/Makefile | 172 + FreeFileSync/Source/RealtimeSync/Makefile | 101 + FreeFileSync/Source/RealtimeSync/RealtimeSync.ico | Bin 0 -> 111386 bytes .../Source/RealtimeSync/RealtimeSync.vcxproj | 250 ++ FreeFileSync/Source/RealtimeSync/app_icon.h | 40 + FreeFileSync/Source/RealtimeSync/application.cpp | 186 + FreeFileSync/Source/RealtimeSync/application.h | 26 + FreeFileSync/Source/RealtimeSync/gui_generated.cpp | 283 ++ FreeFileSync/Source/RealtimeSync/gui_generated.h | 117 + FreeFileSync/Source/RealtimeSync/main_dlg.cpp | 532 +++ FreeFileSync/Source/RealtimeSync/main_dlg.h | 82 + FreeFileSync/Source/RealtimeSync/monitor.cpp | 277 ++ FreeFileSync/Source/RealtimeSync/monitor.h | 38 + FreeFileSync/Source/RealtimeSync/tray_menu.cpp | 342 ++ FreeFileSync/Source/RealtimeSync/tray_menu.h | 23 + FreeFileSync/Source/RealtimeSync/xml_ffs.cpp | 77 + FreeFileSync/Source/RealtimeSync/xml_ffs.h | 23 + FreeFileSync/Source/RealtimeSync/xml_proc.cpp | 75 + FreeFileSync/Source/RealtimeSync/xml_proc.h | 30 + FreeFileSync/Source/algorithm.cpp | 1427 ++++++ FreeFileSync/Source/algorithm.h | 68 + FreeFileSync/Source/application.cpp | 627 +++ FreeFileSync/Source/application.h | 34 + FreeFileSync/Source/comparison.cpp | 907 ++++ FreeFileSync/Source/comparison.h | 59 + .../IFileDialog_Vista/IFileDialog_Vista.vcxproj | 165 + .../Source/dll/IFileDialog_Vista/ifile_dialog.cpp | 108 + .../Source/dll/IFileDialog_Vista/ifile_dialog.h | 67 + .../Source/dll/ShadowCopy/Shadow_Windows7.vcxproj | 169 + FreeFileSync/Source/dll/ShadowCopy/shadow.cpp | 199 + FreeFileSync/Source/dll/ShadowCopy/shadow.h | 94 + .../Source/dll/Taskbar_Seven/Taskbar_Seven.vcxproj | 167 + FreeFileSync/Source/dll/Taskbar_Seven/taskbar.cpp | 113 + FreeFileSync/Source/dll/Taskbar_Seven/taskbar.h | 74 + .../Source/dll/Thumbnail/Thumbnail.vcxproj | 165 + FreeFileSync/Source/dll/Thumbnail/thumbnail.cpp | 486 +++ FreeFileSync/Source/dll/Thumbnail/thumbnail.h | 85 + FreeFileSync/Source/file_hierarchy.cpp | 409 ++ FreeFileSync/Source/file_hierarchy.h | 1112 +++++ FreeFileSync/Source/lib/Batch.ico | Bin 0 -> 103260 bytes FreeFileSync/Source/lib/FreeFileSync.ico | Bin 0 -> 114124 bytes FreeFileSync/Source/lib/SyncDB.ico | Bin 0 -> 111496 bytes FreeFileSync/Source/lib/binary.cpp | 134 + FreeFileSync/Source/lib/binary.h | 25 + FreeFileSync/Source/lib/cmp_filetime.h | 58 + FreeFileSync/Source/lib/db_file.cpp | 821 ++++ FreeFileSync/Source/lib/db_file.h | 102 + FreeFileSync/Source/lib/dir_exist_async.h | 78 + FreeFileSync/Source/lib/dir_lock.cpp | 688 +++ FreeFileSync/Source/lib/dir_lock.h | 45 + FreeFileSync/Source/lib/error_log.h | 43 + FreeFileSync/Source/lib/ffs_paths.cpp | 144 + FreeFileSync/Source/lib/ffs_paths.h | 25 + FreeFileSync/Source/lib/generate_logfile.h | 181 + FreeFileSync/Source/lib/hard_filter.cpp | 404 ++ FreeFileSync/Source/lib/hard_filter.h | 270 ++ FreeFileSync/Source/lib/help_provider.h | 98 + FreeFileSync/Source/lib/icon_buffer.cpp | 758 ++++ FreeFileSync/Source/lib/icon_buffer.h | 52 + FreeFileSync/Source/lib/localization.cpp | 485 +++ FreeFileSync/Source/lib/localization.h | 46 + FreeFileSync/Source/lib/lock_holder.h | 56 + FreeFileSync/Source/lib/norm_filter.h | 85 + FreeFileSync/Source/lib/osx_file_icon.h | 36 + FreeFileSync/Source/lib/osx_file_icon.mm | 179 + FreeFileSync/Source/lib/parallel_scan.cpp | 588 +++ FreeFileSync/Source/lib/parallel_scan.h | 76 + FreeFileSync/Source/lib/parse_lng.h | 706 +++ FreeFileSync/Source/lib/parse_plural.h | 478 ++ FreeFileSync/Source/lib/perf_check.cpp | 261 ++ FreeFileSync/Source/lib/perf_check.h | 46 + FreeFileSync/Source/lib/process_xml.cpp | 1575 +++++++ FreeFileSync/Source/lib/process_xml.h | 298 ++ FreeFileSync/Source/lib/resolve_path.cpp | 708 +++ FreeFileSync/Source/lib/resolve_path.h | 36 + FreeFileSync/Source/lib/return_codes.h | 30 + FreeFileSync/Source/lib/shadow.cpp | 131 + FreeFileSync/Source/lib/shadow.h | 36 + FreeFileSync/Source/lib/soft_filter.h | 112 + FreeFileSync/Source/lib/status_handler.cpp | 29 + FreeFileSync/Source/lib/status_handler.h | 144 + FreeFileSync/Source/lib/status_handler_impl.h | 37 + FreeFileSync/Source/lib/versioning.cpp | 439 ++ FreeFileSync/Source/lib/versioning.h | 89 + FreeFileSync/Source/lib/xml_base.cpp | 108 + FreeFileSync/Source/lib/xml_base.h | 42 + FreeFileSync/Source/process_callback.h | 76 + FreeFileSync/Source/structures.cpp | 516 +++ FreeFileSync/Source/structures.h | 406 ++ FreeFileSync/Source/synchronization.cpp | 2519 +++++++++++ FreeFileSync/Source/synchronization.h | 159 + FreeFileSync/Source/ui/app_icon.h | 40 + FreeFileSync/Source/ui/batch_config.cpp | 177 + FreeFileSync/Source/ui/batch_config.h | 22 + FreeFileSync/Source/ui/batch_status_handler.cpp | 476 ++ FreeFileSync/Source/ui/batch_status_handler.h | 77 + FreeFileSync/Source/ui/check_version.cpp | 318 ++ FreeFileSync/Source/ui/check_version.h | 20 + FreeFileSync/Source/ui/column_attr.h | 111 + FreeFileSync/Source/ui/custom_grid.cpp | 1835 ++++++++ FreeFileSync/Source/ui/custom_grid.h | 81 + FreeFileSync/Source/ui/dir_name.cpp | 274 ++ FreeFileSync/Source/ui/dir_name.h | 64 + FreeFileSync/Source/ui/exec_finished_box.cpp | 279 ++ FreeFileSync/Source/ui/exec_finished_box.h | 60 + FreeFileSync/Source/ui/folder_history_box.cpp | 137 + FreeFileSync/Source/ui/folder_history_box.h | 97 + FreeFileSync/Source/ui/folder_history_types.h | 25 + FreeFileSync/Source/ui/folder_pair.h | 221 + FreeFileSync/Source/ui/grid_view.cpp | 548 +++ FreeFileSync/Source/ui/grid_view.h | 202 + FreeFileSync/Source/ui/gui_generated.cpp | 3499 +++++++++++++++ FreeFileSync/Source/ui/gui_generated.h | 880 ++++ FreeFileSync/Source/ui/gui_status_handler.cpp | 477 ++ FreeFileSync/Source/ui/gui_status_handler.h | 88 + FreeFileSync/Source/ui/main_dlg.cpp | 4570 ++++++++++++++++++++ FreeFileSync/Source/ui/main_dlg.h | 314 ++ FreeFileSync/Source/ui/osx_dock.h | 17 + FreeFileSync/Source/ui/osx_dock.mm | 24 + FreeFileSync/Source/ui/progress_indicator.cpp | 2147 +++++++++ FreeFileSync/Source/ui/progress_indicator.h | 92 + FreeFileSync/Source/ui/search.cpp | 99 + FreeFileSync/Source/ui/search.h | 18 + FreeFileSync/Source/ui/small_dlgs.cpp | 1044 +++++ FreeFileSync/Source/ui/small_dlgs.h | 52 + FreeFileSync/Source/ui/sorting.h | 193 + FreeFileSync/Source/ui/switch_to_gui.h | 40 + FreeFileSync/Source/ui/sync_cfg.cpp | 640 +++ FreeFileSync/Source/ui/sync_cfg.h | 41 + FreeFileSync/Source/ui/taskbar.cpp | 176 + FreeFileSync/Source/ui/taskbar.h | 51 + FreeFileSync/Source/ui/tray_icon.cpp | 236 + FreeFileSync/Source/ui/tray_icon.h | 45 + FreeFileSync/Source/ui/tree_view.cpp | 1339 ++++++ FreeFileSync/Source/ui/tree_view.h | 188 + FreeFileSync/Source/ui/triple_splitter.cpp | 241 ++ FreeFileSync/Source/ui/triple_splitter.h | 89 + .../Source/ui/wx_form_build_hide_warnings.h | 22 + FreeFileSync/Source/version/version.h | 9 + Makefile | 173 - RealtimeSync/RealtimeSync.ico | Bin 111386 -> 0 bytes RealtimeSync/RealtimeSync.vcxproj | 264 -- RealtimeSync/app_icon.h | 40 - RealtimeSync/application.cpp | 172 - RealtimeSync/application.h | 25 - RealtimeSync/gui_generated.cpp | 283 -- RealtimeSync/gui_generated.h | 117 - RealtimeSync/main_dlg.cpp | 534 --- RealtimeSync/main_dlg.h | 80 - RealtimeSync/makefile | 101 - RealtimeSync/monitor.cpp | 281 -- RealtimeSync/monitor.h | 38 - RealtimeSync/tray_menu.cpp | 342 -- RealtimeSync/tray_menu.h | 23 - RealtimeSync/xml_ffs.cpp | 79 - RealtimeSync/xml_ffs.h | 23 - RealtimeSync/xml_proc.cpp | 75 - RealtimeSync/xml_proc.h | 30 - algorithm.cpp | 1426 ------ algorithm.h | 68 - comparison.cpp | 895 ---- comparison.h | 59 - file_hierarchy.cpp | 409 -- file_hierarchy.h | 1112 ----- lib/Batch.ico | Bin 103260 -> 0 bytes lib/FreeFileSync.ico | Bin 114124 -> 0 bytes lib/ShadowCopy/Shadow_Windows7.vcxproj | 240 - lib/ShadowCopy/shadow.cpp | 199 - lib/ShadowCopy/shadow.h | 94 - lib/SyncDB.ico | Bin 111496 -> 0 bytes lib/Thumbnail/Thumbnail.vcxproj | 236 - lib/Thumbnail/thumbnail.cpp | 486 --- lib/Thumbnail/thumbnail.h | 85 - lib/binary.cpp | 134 - lib/binary.h | 25 - lib/cmp_filetime.h | 57 - lib/db_file.cpp | 821 ---- lib/db_file.h | 102 - lib/dir_exist_async.h | 78 - lib/dir_lock.cpp | 685 --- lib/dir_lock.h | 45 - lib/error_log.h | 43 - lib/ffs_paths.cpp | 144 - lib/ffs_paths.h | 25 - lib/generate_logfile.h | 181 - lib/hard_filter.cpp | 404 -- lib/hard_filter.h | 270 -- lib/help_provider.h | 98 - lib/icon_buffer.cpp | 674 --- lib/icon_buffer.h | 52 - lib/localization.cpp | 485 --- lib/localization.h | 46 - lib/lock_holder.h | 57 - lib/norm_filter.h | 85 - lib/osx_file_icon.h | 36 - lib/osx_file_icon.mm | 179 - lib/parallel_scan.cpp | 588 --- lib/parallel_scan.h | 76 - lib/parse_lng.h | 706 --- lib/parse_plural.h | 478 -- lib/perf_check.cpp | 262 -- lib/perf_check.h | 45 - lib/process_xml.cpp | 1577 ------- lib/process_xml.h | 298 -- lib/resolve_path.cpp | 708 --- lib/resolve_path.h | 36 - lib/return_codes.h | 30 - lib/shadow.cpp | 131 - lib/shadow.h | 36 - lib/soft_filter.h | 112 - lib/status_handler.cpp | 29 - lib/status_handler.h | 147 - lib/status_handler_impl.h | 37 - lib/versioning.cpp | 439 -- lib/versioning.h | 89 - lib/xml_base.cpp | 108 - lib/xml_base.h | 42 - process_callback.h | 74 - structures.cpp | 516 --- structures.h | 406 -- synchronization.cpp | 2536 ----------- synchronization.h | 159 - ui/IFileDialog_Vista/IFileDialog_Vista.vcxproj | 244 -- ui/IFileDialog_Vista/ifile_dialog.cpp | 108 - ui/IFileDialog_Vista/ifile_dialog.h | 67 - ui/Taskbar_Seven/Taskbar_Seven.vcxproj | 236 - ui/Taskbar_Seven/taskbar.cpp | 113 - ui/Taskbar_Seven/taskbar.h | 74 - ui/app_icon.h | 40 - ui/batch_config.cpp | 177 - ui/batch_config.h | 22 - ui/batch_status_handler.cpp | 477 -- ui/batch_status_handler.h | 76 - ui/check_version.cpp | 318 -- ui/check_version.h | 20 - ui/column_attr.h | 111 - ui/custom_grid.cpp | 1800 -------- ui/custom_grid.h | 82 - ui/dir_name.cpp | 274 -- ui/dir_name.h | 64 - ui/exec_finished_box.cpp | 279 -- ui/exec_finished_box.h | 60 - ui/folder_history_box.cpp | 137 - ui/folder_history_box.h | 97 - ui/folder_history_types.h | 25 - ui/folder_pair.h | 221 - ui/grid_view.cpp | 548 --- ui/grid_view.h | 202 - ui/gui_generated.cpp | 3505 --------------- ui/gui_generated.h | 880 ---- ui/gui_status_handler.cpp | 479 -- ui/gui_status_handler.h | 86 - ui/main_dlg.cpp | 4547 ------------------- ui/main_dlg.h | 314 -- ui/osx_dock.h | 17 - ui/osx_dock.mm | 24 - ui/progress_indicator.cpp | 2004 --------- ui/progress_indicator.h | 92 - ui/search.cpp | 100 - ui/search.h | 18 - ui/small_dlgs.cpp | 1049 ----- ui/small_dlgs.h | 52 - ui/sorting.h | 193 - ui/switch_to_gui.h | 40 - ui/sync_cfg.cpp | 640 --- ui/sync_cfg.h | 41 - ui/taskbar.cpp | 176 - ui/taskbar.h | 51 - ui/tray_icon.cpp | 236 - ui/tray_icon.h | 45 - ui/tree_view.cpp | 1334 ------ ui/tree_view.h | 186 - ui/triple_splitter.cpp | 241 -- ui/triple_splitter.h | 89 - ui/wx_form_build_hide_warnings.h | 22 - version/version.h | 9 - wx+/font_size.h | 2 +- wx+/graph.cpp | 10 +- wx+/grid.cpp | 767 ++-- wx+/grid.h | 137 +- wx+/popup_dlg.cpp | 8 +- wx+/popup_dlg_generated.cpp | 146 +- wx+/popup_dlg_generated.h | 56 +- zen/FindFilePlus/FindFilePlus.vcxproj | 131 +- zen/IFileOperation/FileOperation.vcxproj | 179 + zen/IFileOperation/FileOperation_Vista.vcxproj | 250 -- zen/debug_minidump.h | 4 +- zen/dir_watcher.cpp | 12 +- zen/file_handling.cpp | 225 +- zen/file_handling.h | 4 +- zen/file_id.cpp | 65 - zen/file_id.h | 22 - zen/file_traverser.cpp | 2 +- zen/fixed_list.h | 46 +- zen/format_unit.cpp | 7 +- zen/int64.h | 2 +- zen/recycler.cpp | 6 +- zenXml/Changelog.txt | 79 + zenxml/LICENSE_1_0.txt | 23 + zenxml/bind.h | 390 -- zenxml/cvrt_struc.h | 211 - zenxml/cvrt_text.h | 222 - zenxml/doc/annotated.html | 120 + zenxml/doc/bc_s.png | Bin 0 -> 705 bytes zenxml/doc/bdwn.png | Bin 0 -> 147 bytes zenxml/doc/bind_8h_source.html | 319 ++ zenxml/doc/classes.html | 120 + zenxml/doc/classzen_1_1_xml_doc-members.html | 128 + zenxml/doc/classzen_1_1_xml_doc.html | 353 ++ zenxml/doc/classzen_1_1_xml_element-members.html | 133 + zenxml/doc/classzen_1_1_xml_element.html | 552 +++ zenxml/doc/classzen_1_1_xml_in-members.html | 129 + zenxml/doc/classzen_1_1_xml_in.html | 446 ++ zenxml/doc/classzen_1_1_xml_out-members.html | 124 + zenxml/doc/classzen_1_1_xml_out.html | 317 ++ zenxml/doc/closed.png | Bin 0 -> 126 bytes zenxml/doc/cvrt__struc_8h_source.html | 305 ++ zenxml/doc/cvrt__text_8h_source.html | 276 ++ zenxml/doc/dom_8h_source.html | 346 ++ zenxml/doc/doxygen.css | 1012 +++++ zenxml/doc/doxygen.png | Bin 0 -> 3942 bytes zenxml/doc/error_8h_source.html | 126 + zenxml/doc/files.html | 118 + zenxml/doc/functions.html | 272 ++ zenxml/doc/functions_func.html | 253 ++ zenxml/doc/functions_vars.html | 125 + zenxml/doc/hierarchy.html | 122 + zenxml/doc/index.html | 679 +++ zenxml/doc/io_8h_source.html | 213 + zenxml/doc/jquery.js | 64 + zenxml/doc/namespacemembers.html | 143 + zenxml/doc/namespacemembers_func.html | 143 + zenxml/doc/namespaces.html | 112 + zenxml/doc/namespacezen.html | 613 +++ zenxml/doc/nav_f.png | Bin 0 -> 159 bytes zenxml/doc/nav_h.png | Bin 0 -> 97 bytes zenxml/doc/open.png | Bin 0 -> 118 bytes zenxml/doc/parser_8h_source.html | 687 +++ zenxml/doc/search/all_61.html | 25 + zenxml/doc/search/all_61.js | 5 + zenxml/doc/search/all_63.html | 25 + zenxml/doc/search/all_63.js | 4 + zenxml/doc/search/all_65.html | 25 + zenxml/doc/search/all_65.js | 4 + zenxml/doc/search/all_67.html | 25 + zenxml/doc/search/all_67.js | 13 + zenxml/doc/search/all_6c.html | 25 + zenxml/doc/search/all_6c.js | 6 + zenxml/doc/search/all_6e.html | 25 + zenxml/doc/search/all_6e.js | 4 + zenxml/doc/search/all_6f.html | 25 + zenxml/doc/search/all_6f.js | 6 + zenxml/doc/search/all_70.html | 25 + zenxml/doc/search/all_70.js | 5 + zenxml/doc/search/all_72.html | 25 + zenxml/doc/search/all_72.js | 9 + zenxml/doc/search/all_73.html | 25 + zenxml/doc/search/all_73.js | 11 + zenxml/doc/search/all_77.html | 25 + zenxml/doc/search/all_77.js | 5 + zenxml/doc/search/all_78.html | 25 + zenxml/doc/search/all_78.js | 13 + zenxml/doc/search/all_7a.html | 25 + zenxml/doc/search/all_7a.js | 4 + zenxml/doc/search/classes_78.html | 25 + zenxml/doc/search/classes_78.js | 10 + zenxml/doc/search/close.png | Bin 0 -> 273 bytes zenxml/doc/search/functions_61.html | 25 + zenxml/doc/search/functions_61.js | 5 + zenxml/doc/search/functions_65.html | 25 + zenxml/doc/search/functions_65.js | 4 + zenxml/doc/search/functions_67.html | 25 + zenxml/doc/search/functions_67.js | 13 + zenxml/doc/search/functions_6c.html | 25 + zenxml/doc/search/functions_6c.js | 5 + zenxml/doc/search/functions_6e.html | 25 + zenxml/doc/search/functions_6e.js | 4 + zenxml/doc/search/functions_6f.html | 25 + zenxml/doc/search/functions_6f.js | 6 + zenxml/doc/search/functions_70.html | 25 + zenxml/doc/search/functions_70.js | 5 + zenxml/doc/search/functions_72.html | 25 + zenxml/doc/search/functions_72.js | 8 + zenxml/doc/search/functions_73.html | 25 + zenxml/doc/search/functions_73.js | 11 + zenxml/doc/search/functions_77.html | 25 + zenxml/doc/search/functions_77.js | 5 + zenxml/doc/search/functions_78.html | 25 + zenxml/doc/search/functions_78.js | 6 + zenxml/doc/search/mag_sel.png | Bin 0 -> 563 bytes zenxml/doc/search/namespaces_7a.html | 25 + zenxml/doc/search/namespaces_7a.js | 4 + zenxml/doc/search/nomatches.html | 12 + zenxml/doc/search/search.css | 238 + zenxml/doc/search/search.js | 803 ++++ zenxml/doc/search/search_l.png | Bin 0 -> 604 bytes zenxml/doc/search/search_m.png | Bin 0 -> 158 bytes zenxml/doc/search/search_r.png | Bin 0 -> 612 bytes zenxml/doc/search/variables_63.html | 25 + zenxml/doc/search/variables_63.js | 4 + zenxml/doc/search/variables_6c.html | 25 + zenxml/doc/search/variables_6c.js | 4 + zenxml/doc/search/variables_72.html | 25 + zenxml/doc/search/variables_72.js | 4 + zenxml/doc/structzen_1_1_xml_error-members.html | 118 + zenxml/doc/structzen_1_1_xml_error.html | 136 + zenxml/doc/structzen_1_1_xml_error.png | Bin 0 -> 668 bytes .../doc/structzen_1_1_xml_file_error-members.html | 119 + zenxml/doc/structzen_1_1_xml_file_error.html | 144 + zenxml/doc/structzen_1_1_xml_file_error.png | Bin 0 -> 447 bytes .../structzen_1_1_xml_parsing_error-members.html | 120 + zenxml/doc/structzen_1_1_xml_parsing_error.html | 147 + zenxml/doc/structzen_1_1_xml_parsing_error.png | Bin 0 -> 500 bytes zenxml/doc/tab_a.png | Bin 0 -> 140 bytes zenxml/doc/tab_b.png | Bin 0 -> 178 bytes zenxml/doc/tab_h.png | Bin 0 -> 192 bytes zenxml/doc/tab_s.png | Bin 0 -> 189 bytes zenxml/doc/tabs.css | 59 + zenxml/doc/xml_8h_source.html | 122 + zenxml/dom.h | 335 -- zenxml/error.h | 19 - zenxml/io.h | 125 - zenxml/parser.h | 618 --- zenxml/summary.dox | 684 --- zenxml/unit_test.cpp | 95 - zenxml/xml.h | 15 - zenxml/zenxml/bind.h | 390 ++ zenxml/zenxml/cvrt_struc.h | 211 + zenxml/zenxml/cvrt_text.h | 222 + zenxml/zenxml/dom.h | 335 ++ zenxml/zenxml/error.h | 19 + zenxml/zenxml/io.h | 125 + zenxml/zenxml/parser.h | 618 +++ zenxml/zenxml/unit_test.cpp | 95 + zenxml/zenxml/xml.h | 15 + 585 files changed, 110511 insertions(+), 99776 deletions(-) delete mode 100644 Application.cpp delete mode 100644 Application.h delete mode 100644 BUILD/Changelog.txt delete mode 100644 BUILD/FreeFileSync.chm delete mode 100644 BUILD/Help/FreeFileSync.hhc delete mode 100644 BUILD/Help/FreeFileSync.hhp delete mode 100644 BUILD/Help/html/Command line.html delete mode 100644 BUILD/Help/html/Comparison Settings.html delete mode 100644 BUILD/Help/html/Daylight Saving Time.html delete mode 100644 BUILD/Help/html/Exclude Items.html delete mode 100644 BUILD/Help/html/Expert settings.html delete mode 100644 BUILD/Help/html/External Applications.html delete mode 100644 BUILD/Help/html/FreeFileSync.html delete mode 100644 BUILD/Help/html/Links.html delete mode 100644 BUILD/Help/html/Macros.html delete mode 100644 BUILD/Help/html/RealtimeSync.html delete mode 100644 BUILD/Help/html/Run as Service.html delete mode 100644 BUILD/Help/html/Schedule a Batch Job.html delete mode 100644 BUILD/Help/html/Synchronize with FTP.html delete mode 100644 BUILD/Help/html/Variable Drive Letters.html delete mode 100644 BUILD/Help/html/Versioning.html delete mode 100644 BUILD/Help/html/Volume Shadow Copy.html delete mode 100644 BUILD/Help/img/CmpSettings.png delete mode 100644 BUILD/Help/img/CompareButton.png delete mode 100644 BUILD/Help/img/FFS_logo.png delete mode 100644 BUILD/Help/img/MainDialog.png delete mode 100644 BUILD/Help/img/RTS_logo.png delete mode 100644 BUILD/Help/img/RealtimeSync.png delete mode 100644 BUILD/Help/img/ScheduleBatch.png delete mode 100644 BUILD/Help/img/SetupBatch.png delete mode 100644 BUILD/Help/img/SourceTarget.png delete mode 100644 BUILD/Help/img/SyncConfigButton.png delete mode 100644 BUILD/Help/img/SynchronizeButton.png delete mode 100644 BUILD/Help/img/VolumeName.png delete mode 100644 BUILD/Help/img/WatchUsbInsert.png delete mode 100644 BUILD/Help/img/create_shortcut.png delete mode 100644 BUILD/Help/img/schedule_realtimesync.png delete mode 100644 BUILD/Help/img/shortcut_properties.png delete mode 100644 BUILD/Help/img/ubuntuScheduler.png delete mode 100644 BUILD/Help/img/win7scheduler.png delete mode 100644 BUILD/HideConsole.vbs delete mode 100644 BUILD/Languages/arabic.lng delete mode 100644 BUILD/Languages/chinese_simple.lng delete mode 100644 BUILD/Languages/chinese_traditional.lng delete mode 100644 BUILD/Languages/croatian.lng delete mode 100644 BUILD/Languages/czech.lng delete mode 100644 BUILD/Languages/danish.lng delete mode 100644 BUILD/Languages/dutch.lng delete mode 100644 BUILD/Languages/english_uk.lng delete mode 100644 BUILD/Languages/finnish.lng delete mode 100644 BUILD/Languages/french.lng delete mode 100644 BUILD/Languages/german.lng delete mode 100644 BUILD/Languages/greek.lng delete mode 100644 BUILD/Languages/hebrew.lng delete mode 100644 BUILD/Languages/hungarian.lng delete mode 100644 BUILD/Languages/italian.lng delete mode 100644 BUILD/Languages/japanese.lng delete mode 100644 BUILD/Languages/korean.lng delete mode 100644 BUILD/Languages/outdated/lithuanian.lng delete mode 100644 BUILD/Languages/outdated/norwegian.lng delete mode 100644 BUILD/Languages/polish.lng delete mode 100644 BUILD/Languages/portuguese.lng delete mode 100644 BUILD/Languages/portuguese_br.lng delete mode 100644 BUILD/Languages/romanian.lng delete mode 100644 BUILD/Languages/russian.lng delete mode 100644 BUILD/Languages/scottish_gaelic.lng delete mode 100644 BUILD/Languages/serbian.lng delete mode 100644 BUILD/Languages/slovenian.lng delete mode 100644 BUILD/Languages/spanish.lng delete mode 100644 BUILD/Languages/swedish.lng delete mode 100644 BUILD/Languages/turkish.lng delete mode 100644 BUILD/Languages/ukrainian.lng delete mode 100644 BUILD/Resources.zip delete mode 100644 BUILD/Sync_Complete.wav delete mode 100644 BUILD/styles.gtk_rc delete mode 100644 FreeFileSync.vcxproj create mode 100644 FreeFileSync/Build/Changelog.txt create mode 100644 FreeFileSync/Build/Help/FreeFileSync.hhc create mode 100644 FreeFileSync/Build/Help/FreeFileSync.hhp create mode 100644 FreeFileSync/Build/Help/html/Command Line.html create mode 100644 FreeFileSync/Build/Help/html/Comparison Settings.html create mode 100644 FreeFileSync/Build/Help/html/Daylight Saving Time.html create mode 100644 FreeFileSync/Build/Help/html/Exclude Items.html create mode 100644 FreeFileSync/Build/Help/html/Expert Settings.html create mode 100644 FreeFileSync/Build/Help/html/External Applications.html create mode 100644 FreeFileSync/Build/Help/html/FreeFileSync.html create mode 100644 FreeFileSync/Build/Help/html/Links.html create mode 100644 FreeFileSync/Build/Help/html/Macros.html create mode 100644 FreeFileSync/Build/Help/html/RealtimeSync.html create mode 100644 FreeFileSync/Build/Help/html/Run as Service.html create mode 100644 FreeFileSync/Build/Help/html/Schedule a Batch Job.html create mode 100644 FreeFileSync/Build/Help/html/Synchronize with FTP.html create mode 100644 FreeFileSync/Build/Help/html/Variable Drive Letters.html create mode 100644 FreeFileSync/Build/Help/html/Versioning.html create mode 100644 FreeFileSync/Build/Help/html/Volume Shadow Copy.html create mode 100644 FreeFileSync/Build/Help/html/base.css create mode 100644 FreeFileSync/Build/Help/img/CmpSettings.png create mode 100644 FreeFileSync/Build/Help/img/CompareButton.png create mode 100644 FreeFileSync/Build/Help/img/FFS_logo.png create mode 100644 FreeFileSync/Build/Help/img/MainDialog.png create mode 100644 FreeFileSync/Build/Help/img/RTS_logo.png create mode 100644 FreeFileSync/Build/Help/img/RealtimeSync.png create mode 100644 FreeFileSync/Build/Help/img/ScheduleBatch.png create mode 100644 FreeFileSync/Build/Help/img/SetupBatch.png create mode 100644 FreeFileSync/Build/Help/img/SourceTarget.png create mode 100644 FreeFileSync/Build/Help/img/SyncConfigButton.png create mode 100644 FreeFileSync/Build/Help/img/SynchronizeButton.png create mode 100644 FreeFileSync/Build/Help/img/VolumeName.png create mode 100644 FreeFileSync/Build/Help/img/WatchUsbInsert.png create mode 100644 FreeFileSync/Build/Help/img/create_shortcut.png create mode 100644 FreeFileSync/Build/Help/img/schedule_realtimesync.png create mode 100644 FreeFileSync/Build/Help/img/shortcut_properties.png create mode 100644 FreeFileSync/Build/Help/img/ubuntuScheduler.png create mode 100644 FreeFileSync/Build/Help/img/win7scheduler.png create mode 100644 FreeFileSync/Build/Languages/arabic.lng create mode 100644 FreeFileSync/Build/Languages/chinese_simple.lng create mode 100644 FreeFileSync/Build/Languages/chinese_traditional.lng create mode 100644 FreeFileSync/Build/Languages/croatian.lng create mode 100644 FreeFileSync/Build/Languages/czech.lng create mode 100644 FreeFileSync/Build/Languages/danish.lng create mode 100644 FreeFileSync/Build/Languages/dutch.lng create mode 100644 FreeFileSync/Build/Languages/english_uk.lng create mode 100644 FreeFileSync/Build/Languages/finnish.lng create mode 100644 FreeFileSync/Build/Languages/french.lng create mode 100644 FreeFileSync/Build/Languages/german.lng create mode 100644 FreeFileSync/Build/Languages/greek.lng create mode 100644 FreeFileSync/Build/Languages/hebrew.lng create mode 100644 FreeFileSync/Build/Languages/hungarian.lng create mode 100644 FreeFileSync/Build/Languages/italian.lng create mode 100644 FreeFileSync/Build/Languages/japanese.lng create mode 100644 FreeFileSync/Build/Languages/korean.lng create mode 100644 FreeFileSync/Build/Languages/outdated/lithuanian.lng create mode 100644 FreeFileSync/Build/Languages/outdated/norwegian.lng create mode 100644 FreeFileSync/Build/Languages/outdated/turkish.lng create mode 100644 FreeFileSync/Build/Languages/polish.lng create mode 100644 FreeFileSync/Build/Languages/portuguese.lng create mode 100644 FreeFileSync/Build/Languages/portuguese_br.lng create mode 100644 FreeFileSync/Build/Languages/romanian.lng create mode 100644 FreeFileSync/Build/Languages/russian.lng create mode 100644 FreeFileSync/Build/Languages/scottish_gaelic.lng create mode 100644 FreeFileSync/Build/Languages/serbian.lng create mode 100644 FreeFileSync/Build/Languages/slovenian.lng create mode 100644 FreeFileSync/Build/Languages/spanish.lng create mode 100644 FreeFileSync/Build/Languages/swedish.lng create mode 100644 FreeFileSync/Build/Languages/ukrainian.lng create mode 100644 FreeFileSync/Build/Resources.zip create mode 100644 FreeFileSync/Build/Sync_Complete.wav create mode 100644 FreeFileSync/Build/styles.gtk_rc create mode 100644 FreeFileSync/Source/FreeFileSync.vcxproj create mode 100644 FreeFileSync/Source/LICENSE create mode 100644 FreeFileSync/Source/Makefile create mode 100644 FreeFileSync/Source/RealtimeSync/Makefile create mode 100644 FreeFileSync/Source/RealtimeSync/RealtimeSync.ico create mode 100644 FreeFileSync/Source/RealtimeSync/RealtimeSync.vcxproj create mode 100644 FreeFileSync/Source/RealtimeSync/app_icon.h create mode 100644 FreeFileSync/Source/RealtimeSync/application.cpp create mode 100644 FreeFileSync/Source/RealtimeSync/application.h create mode 100644 FreeFileSync/Source/RealtimeSync/gui_generated.cpp create mode 100644 FreeFileSync/Source/RealtimeSync/gui_generated.h create mode 100644 FreeFileSync/Source/RealtimeSync/main_dlg.cpp create mode 100644 FreeFileSync/Source/RealtimeSync/main_dlg.h create mode 100644 FreeFileSync/Source/RealtimeSync/monitor.cpp create mode 100644 FreeFileSync/Source/RealtimeSync/monitor.h create mode 100644 FreeFileSync/Source/RealtimeSync/tray_menu.cpp create mode 100644 FreeFileSync/Source/RealtimeSync/tray_menu.h create mode 100644 FreeFileSync/Source/RealtimeSync/xml_ffs.cpp create mode 100644 FreeFileSync/Source/RealtimeSync/xml_ffs.h create mode 100644 FreeFileSync/Source/RealtimeSync/xml_proc.cpp create mode 100644 FreeFileSync/Source/RealtimeSync/xml_proc.h create mode 100644 FreeFileSync/Source/algorithm.cpp create mode 100644 FreeFileSync/Source/algorithm.h create mode 100644 FreeFileSync/Source/application.cpp create mode 100644 FreeFileSync/Source/application.h create mode 100644 FreeFileSync/Source/comparison.cpp create mode 100644 FreeFileSync/Source/comparison.h create mode 100644 FreeFileSync/Source/dll/IFileDialog_Vista/IFileDialog_Vista.vcxproj create mode 100644 FreeFileSync/Source/dll/IFileDialog_Vista/ifile_dialog.cpp create mode 100644 FreeFileSync/Source/dll/IFileDialog_Vista/ifile_dialog.h create mode 100644 FreeFileSync/Source/dll/ShadowCopy/Shadow_Windows7.vcxproj create mode 100644 FreeFileSync/Source/dll/ShadowCopy/shadow.cpp create mode 100644 FreeFileSync/Source/dll/ShadowCopy/shadow.h create mode 100644 FreeFileSync/Source/dll/Taskbar_Seven/Taskbar_Seven.vcxproj create mode 100644 FreeFileSync/Source/dll/Taskbar_Seven/taskbar.cpp create mode 100644 FreeFileSync/Source/dll/Taskbar_Seven/taskbar.h create mode 100644 FreeFileSync/Source/dll/Thumbnail/Thumbnail.vcxproj create mode 100644 FreeFileSync/Source/dll/Thumbnail/thumbnail.cpp create mode 100644 FreeFileSync/Source/dll/Thumbnail/thumbnail.h create mode 100644 FreeFileSync/Source/file_hierarchy.cpp create mode 100644 FreeFileSync/Source/file_hierarchy.h create mode 100644 FreeFileSync/Source/lib/Batch.ico create mode 100644 FreeFileSync/Source/lib/FreeFileSync.ico create mode 100644 FreeFileSync/Source/lib/SyncDB.ico create mode 100644 FreeFileSync/Source/lib/binary.cpp create mode 100644 FreeFileSync/Source/lib/binary.h create mode 100644 FreeFileSync/Source/lib/cmp_filetime.h create mode 100644 FreeFileSync/Source/lib/db_file.cpp create mode 100644 FreeFileSync/Source/lib/db_file.h create mode 100644 FreeFileSync/Source/lib/dir_exist_async.h create mode 100644 FreeFileSync/Source/lib/dir_lock.cpp create mode 100644 FreeFileSync/Source/lib/dir_lock.h create mode 100644 FreeFileSync/Source/lib/error_log.h create mode 100644 FreeFileSync/Source/lib/ffs_paths.cpp create mode 100644 FreeFileSync/Source/lib/ffs_paths.h create mode 100644 FreeFileSync/Source/lib/generate_logfile.h create mode 100644 FreeFileSync/Source/lib/hard_filter.cpp create mode 100644 FreeFileSync/Source/lib/hard_filter.h create mode 100644 FreeFileSync/Source/lib/help_provider.h create mode 100644 FreeFileSync/Source/lib/icon_buffer.cpp create mode 100644 FreeFileSync/Source/lib/icon_buffer.h create mode 100644 FreeFileSync/Source/lib/localization.cpp create mode 100644 FreeFileSync/Source/lib/localization.h create mode 100644 FreeFileSync/Source/lib/lock_holder.h create mode 100644 FreeFileSync/Source/lib/norm_filter.h create mode 100644 FreeFileSync/Source/lib/osx_file_icon.h create mode 100644 FreeFileSync/Source/lib/osx_file_icon.mm create mode 100644 FreeFileSync/Source/lib/parallel_scan.cpp create mode 100644 FreeFileSync/Source/lib/parallel_scan.h create mode 100644 FreeFileSync/Source/lib/parse_lng.h create mode 100644 FreeFileSync/Source/lib/parse_plural.h create mode 100644 FreeFileSync/Source/lib/perf_check.cpp create mode 100644 FreeFileSync/Source/lib/perf_check.h create mode 100644 FreeFileSync/Source/lib/process_xml.cpp create mode 100644 FreeFileSync/Source/lib/process_xml.h create mode 100644 FreeFileSync/Source/lib/resolve_path.cpp create mode 100644 FreeFileSync/Source/lib/resolve_path.h create mode 100644 FreeFileSync/Source/lib/return_codes.h create mode 100644 FreeFileSync/Source/lib/shadow.cpp create mode 100644 FreeFileSync/Source/lib/shadow.h create mode 100644 FreeFileSync/Source/lib/soft_filter.h create mode 100644 FreeFileSync/Source/lib/status_handler.cpp create mode 100644 FreeFileSync/Source/lib/status_handler.h create mode 100644 FreeFileSync/Source/lib/status_handler_impl.h create mode 100644 FreeFileSync/Source/lib/versioning.cpp create mode 100644 FreeFileSync/Source/lib/versioning.h create mode 100644 FreeFileSync/Source/lib/xml_base.cpp create mode 100644 FreeFileSync/Source/lib/xml_base.h create mode 100644 FreeFileSync/Source/process_callback.h create mode 100644 FreeFileSync/Source/structures.cpp create mode 100644 FreeFileSync/Source/structures.h create mode 100644 FreeFileSync/Source/synchronization.cpp create mode 100644 FreeFileSync/Source/synchronization.h create mode 100644 FreeFileSync/Source/ui/app_icon.h create mode 100644 FreeFileSync/Source/ui/batch_config.cpp create mode 100644 FreeFileSync/Source/ui/batch_config.h create mode 100644 FreeFileSync/Source/ui/batch_status_handler.cpp create mode 100644 FreeFileSync/Source/ui/batch_status_handler.h create mode 100644 FreeFileSync/Source/ui/check_version.cpp create mode 100644 FreeFileSync/Source/ui/check_version.h create mode 100644 FreeFileSync/Source/ui/column_attr.h create mode 100644 FreeFileSync/Source/ui/custom_grid.cpp create mode 100644 FreeFileSync/Source/ui/custom_grid.h create mode 100644 FreeFileSync/Source/ui/dir_name.cpp create mode 100644 FreeFileSync/Source/ui/dir_name.h create mode 100644 FreeFileSync/Source/ui/exec_finished_box.cpp create mode 100644 FreeFileSync/Source/ui/exec_finished_box.h create mode 100644 FreeFileSync/Source/ui/folder_history_box.cpp create mode 100644 FreeFileSync/Source/ui/folder_history_box.h create mode 100644 FreeFileSync/Source/ui/folder_history_types.h create mode 100644 FreeFileSync/Source/ui/folder_pair.h create mode 100644 FreeFileSync/Source/ui/grid_view.cpp create mode 100644 FreeFileSync/Source/ui/grid_view.h create mode 100644 FreeFileSync/Source/ui/gui_generated.cpp create mode 100644 FreeFileSync/Source/ui/gui_generated.h create mode 100644 FreeFileSync/Source/ui/gui_status_handler.cpp create mode 100644 FreeFileSync/Source/ui/gui_status_handler.h create mode 100644 FreeFileSync/Source/ui/main_dlg.cpp create mode 100644 FreeFileSync/Source/ui/main_dlg.h create mode 100644 FreeFileSync/Source/ui/osx_dock.h create mode 100644 FreeFileSync/Source/ui/osx_dock.mm create mode 100644 FreeFileSync/Source/ui/progress_indicator.cpp create mode 100644 FreeFileSync/Source/ui/progress_indicator.h create mode 100644 FreeFileSync/Source/ui/search.cpp create mode 100644 FreeFileSync/Source/ui/search.h create mode 100644 FreeFileSync/Source/ui/small_dlgs.cpp create mode 100644 FreeFileSync/Source/ui/small_dlgs.h create mode 100644 FreeFileSync/Source/ui/sorting.h create mode 100644 FreeFileSync/Source/ui/switch_to_gui.h create mode 100644 FreeFileSync/Source/ui/sync_cfg.cpp create mode 100644 FreeFileSync/Source/ui/sync_cfg.h create mode 100644 FreeFileSync/Source/ui/taskbar.cpp create mode 100644 FreeFileSync/Source/ui/taskbar.h create mode 100644 FreeFileSync/Source/ui/tray_icon.cpp create mode 100644 FreeFileSync/Source/ui/tray_icon.h create mode 100644 FreeFileSync/Source/ui/tree_view.cpp create mode 100644 FreeFileSync/Source/ui/tree_view.h create mode 100644 FreeFileSync/Source/ui/triple_splitter.cpp create mode 100644 FreeFileSync/Source/ui/triple_splitter.h create mode 100644 FreeFileSync/Source/ui/wx_form_build_hide_warnings.h create mode 100644 FreeFileSync/Source/version/version.h delete mode 100644 Makefile delete mode 100644 RealtimeSync/RealtimeSync.ico delete mode 100644 RealtimeSync/RealtimeSync.vcxproj delete mode 100644 RealtimeSync/app_icon.h delete mode 100644 RealtimeSync/application.cpp delete mode 100644 RealtimeSync/application.h delete mode 100644 RealtimeSync/gui_generated.cpp delete mode 100644 RealtimeSync/gui_generated.h delete mode 100644 RealtimeSync/main_dlg.cpp delete mode 100644 RealtimeSync/main_dlg.h delete mode 100644 RealtimeSync/makefile delete mode 100644 RealtimeSync/monitor.cpp delete mode 100644 RealtimeSync/monitor.h delete mode 100644 RealtimeSync/tray_menu.cpp delete mode 100644 RealtimeSync/tray_menu.h delete mode 100644 RealtimeSync/xml_ffs.cpp delete mode 100644 RealtimeSync/xml_ffs.h delete mode 100644 RealtimeSync/xml_proc.cpp delete mode 100644 RealtimeSync/xml_proc.h delete mode 100644 algorithm.cpp delete mode 100644 algorithm.h delete mode 100644 comparison.cpp delete mode 100644 comparison.h delete mode 100644 file_hierarchy.cpp delete mode 100644 file_hierarchy.h delete mode 100644 lib/Batch.ico delete mode 100644 lib/FreeFileSync.ico delete mode 100644 lib/ShadowCopy/Shadow_Windows7.vcxproj delete mode 100644 lib/ShadowCopy/shadow.cpp delete mode 100644 lib/ShadowCopy/shadow.h delete mode 100644 lib/SyncDB.ico delete mode 100644 lib/Thumbnail/Thumbnail.vcxproj delete mode 100644 lib/Thumbnail/thumbnail.cpp delete mode 100644 lib/Thumbnail/thumbnail.h delete mode 100644 lib/binary.cpp delete mode 100644 lib/binary.h delete mode 100644 lib/cmp_filetime.h delete mode 100644 lib/db_file.cpp delete mode 100644 lib/db_file.h delete mode 100644 lib/dir_exist_async.h delete mode 100644 lib/dir_lock.cpp delete mode 100644 lib/dir_lock.h delete mode 100644 lib/error_log.h delete mode 100644 lib/ffs_paths.cpp delete mode 100644 lib/ffs_paths.h delete mode 100644 lib/generate_logfile.h delete mode 100644 lib/hard_filter.cpp delete mode 100644 lib/hard_filter.h delete mode 100644 lib/help_provider.h delete mode 100644 lib/icon_buffer.cpp delete mode 100644 lib/icon_buffer.h delete mode 100644 lib/localization.cpp delete mode 100644 lib/localization.h delete mode 100644 lib/lock_holder.h delete mode 100644 lib/norm_filter.h delete mode 100644 lib/osx_file_icon.h delete mode 100644 lib/osx_file_icon.mm delete mode 100644 lib/parallel_scan.cpp delete mode 100644 lib/parallel_scan.h delete mode 100644 lib/parse_lng.h delete mode 100644 lib/parse_plural.h delete mode 100644 lib/perf_check.cpp delete mode 100644 lib/perf_check.h delete mode 100644 lib/process_xml.cpp delete mode 100644 lib/process_xml.h delete mode 100644 lib/resolve_path.cpp delete mode 100644 lib/resolve_path.h delete mode 100644 lib/return_codes.h delete mode 100644 lib/shadow.cpp delete mode 100644 lib/shadow.h delete mode 100644 lib/soft_filter.h delete mode 100644 lib/status_handler.cpp delete mode 100644 lib/status_handler.h delete mode 100644 lib/status_handler_impl.h delete mode 100644 lib/versioning.cpp delete mode 100644 lib/versioning.h delete mode 100644 lib/xml_base.cpp delete mode 100644 lib/xml_base.h delete mode 100644 process_callback.h delete mode 100644 structures.cpp delete mode 100644 structures.h delete mode 100644 synchronization.cpp delete mode 100644 synchronization.h delete mode 100644 ui/IFileDialog_Vista/IFileDialog_Vista.vcxproj delete mode 100644 ui/IFileDialog_Vista/ifile_dialog.cpp delete mode 100644 ui/IFileDialog_Vista/ifile_dialog.h delete mode 100644 ui/Taskbar_Seven/Taskbar_Seven.vcxproj delete mode 100644 ui/Taskbar_Seven/taskbar.cpp delete mode 100644 ui/Taskbar_Seven/taskbar.h delete mode 100644 ui/app_icon.h delete mode 100644 ui/batch_config.cpp delete mode 100644 ui/batch_config.h delete mode 100644 ui/batch_status_handler.cpp delete mode 100644 ui/batch_status_handler.h delete mode 100644 ui/check_version.cpp delete mode 100644 ui/check_version.h delete mode 100644 ui/column_attr.h delete mode 100644 ui/custom_grid.cpp delete mode 100644 ui/custom_grid.h delete mode 100644 ui/dir_name.cpp delete mode 100644 ui/dir_name.h delete mode 100644 ui/exec_finished_box.cpp delete mode 100644 ui/exec_finished_box.h delete mode 100644 ui/folder_history_box.cpp delete mode 100644 ui/folder_history_box.h delete mode 100644 ui/folder_history_types.h delete mode 100644 ui/folder_pair.h delete mode 100644 ui/grid_view.cpp delete mode 100644 ui/grid_view.h delete mode 100644 ui/gui_generated.cpp delete mode 100644 ui/gui_generated.h delete mode 100644 ui/gui_status_handler.cpp delete mode 100644 ui/gui_status_handler.h delete mode 100644 ui/main_dlg.cpp delete mode 100644 ui/main_dlg.h delete mode 100644 ui/osx_dock.h delete mode 100644 ui/osx_dock.mm delete mode 100644 ui/progress_indicator.cpp delete mode 100644 ui/progress_indicator.h delete mode 100644 ui/search.cpp delete mode 100644 ui/search.h delete mode 100644 ui/small_dlgs.cpp delete mode 100644 ui/small_dlgs.h delete mode 100644 ui/sorting.h delete mode 100644 ui/switch_to_gui.h delete mode 100644 ui/sync_cfg.cpp delete mode 100644 ui/sync_cfg.h delete mode 100644 ui/taskbar.cpp delete mode 100644 ui/taskbar.h delete mode 100644 ui/tray_icon.cpp delete mode 100644 ui/tray_icon.h delete mode 100644 ui/tree_view.cpp delete mode 100644 ui/tree_view.h delete mode 100644 ui/triple_splitter.cpp delete mode 100644 ui/triple_splitter.h delete mode 100644 ui/wx_form_build_hide_warnings.h delete mode 100644 version/version.h create mode 100644 zen/IFileOperation/FileOperation.vcxproj delete mode 100644 zen/IFileOperation/FileOperation_Vista.vcxproj delete mode 100644 zen/file_id.cpp delete mode 100644 zen/file_id.h create mode 100644 zenXml/Changelog.txt create mode 100644 zenxml/LICENSE_1_0.txt delete mode 100644 zenxml/bind.h delete mode 100644 zenxml/cvrt_struc.h delete mode 100644 zenxml/cvrt_text.h create mode 100644 zenxml/doc/annotated.html create mode 100644 zenxml/doc/bc_s.png create mode 100644 zenxml/doc/bdwn.png create mode 100644 zenxml/doc/bind_8h_source.html create mode 100644 zenxml/doc/classes.html create mode 100644 zenxml/doc/classzen_1_1_xml_doc-members.html create mode 100644 zenxml/doc/classzen_1_1_xml_doc.html create mode 100644 zenxml/doc/classzen_1_1_xml_element-members.html create mode 100644 zenxml/doc/classzen_1_1_xml_element.html create mode 100644 zenxml/doc/classzen_1_1_xml_in-members.html create mode 100644 zenxml/doc/classzen_1_1_xml_in.html create mode 100644 zenxml/doc/classzen_1_1_xml_out-members.html create mode 100644 zenxml/doc/classzen_1_1_xml_out.html create mode 100644 zenxml/doc/closed.png create mode 100644 zenxml/doc/cvrt__struc_8h_source.html create mode 100644 zenxml/doc/cvrt__text_8h_source.html create mode 100644 zenxml/doc/dom_8h_source.html create mode 100644 zenxml/doc/doxygen.css create mode 100644 zenxml/doc/doxygen.png create mode 100644 zenxml/doc/error_8h_source.html create mode 100644 zenxml/doc/files.html create mode 100644 zenxml/doc/functions.html create mode 100644 zenxml/doc/functions_func.html create mode 100644 zenxml/doc/functions_vars.html create mode 100644 zenxml/doc/hierarchy.html create mode 100644 zenxml/doc/index.html create mode 100644 zenxml/doc/io_8h_source.html create mode 100644 zenxml/doc/jquery.js create mode 100644 zenxml/doc/namespacemembers.html create mode 100644 zenxml/doc/namespacemembers_func.html create mode 100644 zenxml/doc/namespaces.html create mode 100644 zenxml/doc/namespacezen.html create mode 100644 zenxml/doc/nav_f.png create mode 100644 zenxml/doc/nav_h.png create mode 100644 zenxml/doc/open.png create mode 100644 zenxml/doc/parser_8h_source.html create mode 100644 zenxml/doc/search/all_61.html create mode 100644 zenxml/doc/search/all_61.js create mode 100644 zenxml/doc/search/all_63.html create mode 100644 zenxml/doc/search/all_63.js create mode 100644 zenxml/doc/search/all_65.html create mode 100644 zenxml/doc/search/all_65.js create mode 100644 zenxml/doc/search/all_67.html create mode 100644 zenxml/doc/search/all_67.js create mode 100644 zenxml/doc/search/all_6c.html create mode 100644 zenxml/doc/search/all_6c.js create mode 100644 zenxml/doc/search/all_6e.html create mode 100644 zenxml/doc/search/all_6e.js create mode 100644 zenxml/doc/search/all_6f.html create mode 100644 zenxml/doc/search/all_6f.js create mode 100644 zenxml/doc/search/all_70.html create mode 100644 zenxml/doc/search/all_70.js create mode 100644 zenxml/doc/search/all_72.html create mode 100644 zenxml/doc/search/all_72.js create mode 100644 zenxml/doc/search/all_73.html create mode 100644 zenxml/doc/search/all_73.js create mode 100644 zenxml/doc/search/all_77.html create mode 100644 zenxml/doc/search/all_77.js create mode 100644 zenxml/doc/search/all_78.html create mode 100644 zenxml/doc/search/all_78.js create mode 100644 zenxml/doc/search/all_7a.html create mode 100644 zenxml/doc/search/all_7a.js create mode 100644 zenxml/doc/search/classes_78.html create mode 100644 zenxml/doc/search/classes_78.js create mode 100644 zenxml/doc/search/close.png create mode 100644 zenxml/doc/search/functions_61.html create mode 100644 zenxml/doc/search/functions_61.js create mode 100644 zenxml/doc/search/functions_65.html create mode 100644 zenxml/doc/search/functions_65.js create mode 100644 zenxml/doc/search/functions_67.html create mode 100644 zenxml/doc/search/functions_67.js create mode 100644 zenxml/doc/search/functions_6c.html create mode 100644 zenxml/doc/search/functions_6c.js create mode 100644 zenxml/doc/search/functions_6e.html create mode 100644 zenxml/doc/search/functions_6e.js create mode 100644 zenxml/doc/search/functions_6f.html create mode 100644 zenxml/doc/search/functions_6f.js create mode 100644 zenxml/doc/search/functions_70.html create mode 100644 zenxml/doc/search/functions_70.js create mode 100644 zenxml/doc/search/functions_72.html create mode 100644 zenxml/doc/search/functions_72.js create mode 100644 zenxml/doc/search/functions_73.html create mode 100644 zenxml/doc/search/functions_73.js create mode 100644 zenxml/doc/search/functions_77.html create mode 100644 zenxml/doc/search/functions_77.js create mode 100644 zenxml/doc/search/functions_78.html create mode 100644 zenxml/doc/search/functions_78.js create mode 100644 zenxml/doc/search/mag_sel.png create mode 100644 zenxml/doc/search/namespaces_7a.html create mode 100644 zenxml/doc/search/namespaces_7a.js create mode 100644 zenxml/doc/search/nomatches.html create mode 100644 zenxml/doc/search/search.css create mode 100644 zenxml/doc/search/search.js create mode 100644 zenxml/doc/search/search_l.png create mode 100644 zenxml/doc/search/search_m.png create mode 100644 zenxml/doc/search/search_r.png create mode 100644 zenxml/doc/search/variables_63.html create mode 100644 zenxml/doc/search/variables_63.js create mode 100644 zenxml/doc/search/variables_6c.html create mode 100644 zenxml/doc/search/variables_6c.js create mode 100644 zenxml/doc/search/variables_72.html create mode 100644 zenxml/doc/search/variables_72.js create mode 100644 zenxml/doc/structzen_1_1_xml_error-members.html create mode 100644 zenxml/doc/structzen_1_1_xml_error.html create mode 100644 zenxml/doc/structzen_1_1_xml_error.png create mode 100644 zenxml/doc/structzen_1_1_xml_file_error-members.html create mode 100644 zenxml/doc/structzen_1_1_xml_file_error.html create mode 100644 zenxml/doc/structzen_1_1_xml_file_error.png create mode 100644 zenxml/doc/structzen_1_1_xml_parsing_error-members.html create mode 100644 zenxml/doc/structzen_1_1_xml_parsing_error.html create mode 100644 zenxml/doc/structzen_1_1_xml_parsing_error.png create mode 100644 zenxml/doc/tab_a.png create mode 100644 zenxml/doc/tab_b.png create mode 100644 zenxml/doc/tab_h.png create mode 100644 zenxml/doc/tab_s.png create mode 100644 zenxml/doc/tabs.css create mode 100644 zenxml/doc/xml_8h_source.html delete mode 100644 zenxml/dom.h delete mode 100644 zenxml/error.h delete mode 100644 zenxml/io.h delete mode 100644 zenxml/parser.h delete mode 100644 zenxml/summary.dox delete mode 100644 zenxml/unit_test.cpp delete mode 100644 zenxml/xml.h create mode 100644 zenxml/zenxml/bind.h create mode 100644 zenxml/zenxml/cvrt_struc.h create mode 100644 zenxml/zenxml/cvrt_text.h create mode 100644 zenxml/zenxml/dom.h create mode 100644 zenxml/zenxml/error.h create mode 100644 zenxml/zenxml/io.h create mode 100644 zenxml/zenxml/parser.h create mode 100644 zenxml/zenxml/unit_test.cpp create mode 100644 zenxml/zenxml/xml.h diff --git a/Application.cpp b/Application.cpp deleted file mode 100644 index f84614f7..00000000 --- a/Application.cpp +++ /dev/null @@ -1,619 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "application.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "comparison.h" -#include "algorithm.h" -#include "synchronization.h" -#include "ui/batch_status_handler.h" -#include "ui/check_version.h" -#include "ui/main_dlg.h" -#include "ui/switch_to_gui.h" -#include "lib/process_xml.h" -#include "lib/error_log.h" - -#ifdef ZEN_WIN -#include - -#elif defined ZEN_LINUX -#include -#endif - -using namespace zen; -using namespace xmlAccess; - - -IMPLEMENT_APP(Application) - -namespace -{ -/* -boost::thread::id mainThreadId = boost::this_thread::get_id(); - -void onTerminationRequested() -{ -std::wstring msg = boost::this_thread::get_id() == mainThreadId ? - L"Termination requested in main thread!\n\n" : - L"Termination requested in worker thread!\n\n"; -msg += L"Please file a bug report at: http://sourceforge.net/projects/freefilesync"; - -wxSafeShowMessage(_("An exception occurred"), msg); -std::abort(); -} -*/ - -#ifdef _MSC_VER -void crtInvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved) { assert(false); } -#endif - - -std::vector getCommandlineArgs(const wxApp& app) -{ - std::vector args; -#ifdef ZEN_WIN - //we do the job ourselves! both wxWidgets and ::CommandLineToArgvW() parse "C:\" "D:\" as single line C:\" D:\" - //-> "solution": we just don't support protected quotation mark! - Zstring cmdLine = ::GetCommandLine(); //only way to get a unicode commandline - while (endsWith(cmdLine, L' ')) //may end with space - cmdLine.resize(cmdLine.size() - 1); - - auto iterStart = cmdLine.end(); //end() means: no token - for (auto it = cmdLine.begin(); it != cmdLine.end(); ++it) - if (*it == L' ') //space commits token - { - if (iterStart != cmdLine.end()) - { - args.push_back(Zstring(iterStart, it)); - iterStart = cmdLine.end(); //expect consecutive blanks! - } - } - else - { - //start new token - if (iterStart == cmdLine.end()) - iterStart = it; - - if (*it == L'\"') - { - it = std::find(it + 1, cmdLine.end(), L'\"'); - if (it == cmdLine.end()) - break; - } - } - if (iterStart != cmdLine.end()) - args.push_back(Zstring(iterStart, cmdLine.end())); - - if (!args.empty()) - args.erase(args.begin()); //remove first argument which is exe path by convention: http://blogs.msdn.com/b/oldnewthing/archive/2006/05/15/597984.aspx - - std::for_each(args.begin(), args.end(), - [](Zstring& str) - { - if (str.size() >= 2 && startsWith(str, L'\"') && endsWith(str, L'\"')) - str = Zstring(str.c_str() + 1, str.size() - 2); - }); - -#else - for (int i = 1; i < app.argc; ++i) //wxWidgets screws up once again making "argv implicitly convertible to a wxChar**" in 2.9.3, - args.push_back(toZ(wxString(app.argv[i]))); //so we are forced to use this pitiful excuse for a range construction!! -#endif - return args; -} - -const wxEventType EVENT_ENTER_EVENT_LOOP = wxNewEventType(); -} - -//################################################################################################################## - -bool Application::OnInit() -{ - //-> this seems rather useless: - //std::set_terminate(onTerminationRequested); //unlike wxWidgets uncaught exception handling, this works for all worker threads - -#ifdef ZEN_WIN -#ifdef _MSC_VER - _set_invalid_parameter_handler(crtInvalidParameterHandler); //see comment in -#endif - //Quote: "Best practice is that all applications call the process-wide ::SetErrorMode() function with a parameter of - //SEM_FAILCRITICALERRORS at startup. This is to prevent error mode dialogs from hanging the application." - ::SetErrorMode(SEM_FAILCRITICALERRORS); - -#elif defined ZEN_LINUX - ::gtk_init(nullptr, nullptr); - ::gtk_rc_parse((getResourceDir() + "styles.gtk_rc").c_str()); //remove inner border from bitmap buttons -#endif - -#ifdef ZEN_WIN - wxToolTip::SetMaxWidth(-1); //disable tooltip wrapping -> Windows only -#endif - //Windows User Experience Interaction Guidelines: tool tips should have 5s timeout, info tips no timeout => compromise: - wxToolTip::SetAutoPop(7000); //http://msdn.microsoft.com/en-us/library/windows/desktop/aa511495.aspx - - SetAppName(L"FreeFileSync"); //if not set, the default is the executable's name! - - initResourceImages(getResourceDir() + Zstr("Resources.zip")); - - Connect(wxEVT_QUERY_END_SESSION, wxEventHandler(Application::onQueryEndSession), nullptr, this); - Connect(wxEVT_END_SESSION, wxEventHandler(Application::onQueryEndSession), nullptr, this); - - //do not call wxApp::OnInit() to avoid using wxWidgets command line parser - - //Note: app start is deferred: batch mode requires the wxApp eventhandler to be established for UI update events. This is not the case at the time of OnInit()! - Connect(EVENT_ENTER_EVENT_LOOP, wxEventHandler(Application::onEnterEventLoop), nullptr, this); - wxCommandEvent scrollEvent(EVENT_ENTER_EVENT_LOOP); - AddPendingEvent(scrollEvent); - - return true; //true: continue processing; false: exit immediately. -} - - -int Application::OnExit() -{ - releaseWxLocale(); - return wxApp::OnExit(); -} - - -void Application::onEnterEventLoop(wxEvent& event) -{ - Disconnect(EVENT_ENTER_EVENT_LOOP, wxEventHandler(Application::onEnterEventLoop), nullptr, this); - - //determine FFS mode of operation - std::vector commandArgs = getCommandlineArgs(*this); - launch(commandArgs); -} - -#ifdef ZEN_MAC -/* -wxWidgets initialization sequence on OS X is a mess: ----------------------------------------------------- -1. double click FFS app bundle or execute from command line without arguments - OnInit() - OnRun() - onEnterEventLoop() - MacNewFile() - -2. double-click .ffs_gui file - OnInit() - OnRun() - onEnterEventLoop() - MacOpenFiles() - -3. start from command line with .ffs_gui file as first argument - OnInit() - OnRun() - MacOpenFiles() -> WTF!? - onEnterEventLoop() - MacNewFile() -> yes, wxWidgets screws up once again: http://trac.wxwidgets.org/ticket/14558 - -=> solution: map Apple events to regular command line via launcher -*/ -#endif - - -int Application::OnRun() -{ - auto processException = [](const std::wstring& msg) - { - //it's not always possible to display a message box, e.g. corrupted stack, however low-level file output works! - logError(utfCvrtTo(msg)); - wxSafeShowMessage(L"FreeFileSync - " + _("An exception occurred"), msg); - }; - - try - { - wxApp::OnRun(); - } - catch (const std::exception& e) //catch all STL exceptions - { - processException(utfCvrtTo(e.what())); - return FFS_RC_EXCEPTION; - } - catch (...) //catch the rest - { - processException(L"Unknown error."); - return FFS_RC_EXCEPTION; - } - - return returnCode; -} - - -void Application::onQueryEndSession(wxEvent& event) -{ - if (auto mainWin = dynamic_cast(GetTopWindow())) - mainWin->onQueryEndSession(); - OnExit(); //wxWidgets screws up again: http://trac.wxwidgets.org/ticket/3069 - //wxEntryCleanup(); -> gives popup "dll init failed" on XP - std::exit(returnCode); //Windows will terminate anyway: destruct global objects -} - - -void runGuiMode(); -void runGuiMode(const XmlGuiConfig& guiCfg, const std::vector& referenceFiles); -void runBatchMode(const XmlBatchConfig& batchCfg, const Zstring& referenceFile, FfsReturnCode& returnCode); -void showSyntaxHelp(); - - -void Application::launch(const std::vector& commandArgs) -{ - //wxWidgets app exit handling is weird... we want the app to exit only if the logical main window is closed - wxTheApp->SetExitOnFrameDelete(false); //avoid popup-windows from becoming temporary top windows leading to program exit after closure - ZEN_ON_SCOPE_EXIT(if (!mainWindowWasSet()) wxTheApp->ExitMainLoop();); //quit application, if no main window was set (batch silent mode) - - try - { - //tentatively set program language to OS default until GlobalSettings.xml is read later - setLanguage(xmlAccess::XmlGlobalSettings().programLanguage); //throw FileError - } - catch (const FileError&) { assert(false); } //no messagebox: consider batch job! - - auto notifyError = [&](const std::wstring& msg, const std::wstring& title) - { - showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setTitle(title).setDetailInstructions(msg)); - raiseReturnCode(returnCode, FFS_RC_ABORTED); - }; - - //parse command line arguments - std::vector leftDirs; - std::vector rightDirs; - std::vector> configFiles; //XmlType: batch or GUI files only - { - const Zchar optionLeftDir [] = Zstr("-leftdir"); - const Zchar optionRightDir[] = Zstr("-rightdir"); - - auto syntaxHelpRequested = [](const Zstring& arg) - { - auto it = std::find_if(arg.begin(), arg.end(), [](Zchar c) { return c != Zchar('/') && c != Zchar('-'); }); - const Zstring argTmp(it, arg.end()); - return argTmp == Zstr("help") || - argTmp == Zstr("h") || - argTmp == Zstr("?"); - }; - - for (auto it = commandArgs.begin(); it != commandArgs.end(); ++it) - if (syntaxHelpRequested(*it)) - return showSyntaxHelp(); - else if (*it == optionLeftDir) - { - if (++it == commandArgs.end()) - { - notifyError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfCvrtTo(optionLeftDir)), _("Syntax error")); - return; - } - leftDirs.push_back(*it); - } - else if (*it == optionRightDir) - { - if (++it == commandArgs.end()) - { - notifyError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfCvrtTo(optionRightDir)), _("Syntax error")); - return; - } - rightDirs.push_back(*it); - } - else - { - Zstring filename = *it; - if (!fileExists(filename)) //...be a little tolerant - { - if (fileExists(filename + Zstr(".ffs_batch"))) - filename += Zstr(".ffs_batch"); - else if (fileExists(filename + Zstr(".ffs_gui"))) - filename += Zstr(".ffs_gui"); - else - { - notifyError(replaceCpy(_("Cannot open file %x."), L"%x", fmtFileName(filename)), std::wstring()); - return; - } - } - - switch (getXmlType(filename)) //throw() - { - case XML_TYPE_GLOBAL: - case XML_TYPE_OTHER: - notifyError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename)), std::wstring()); - return; - - case XML_TYPE_GUI: - configFiles.push_back(std::make_pair(filename, XML_TYPE_GUI)); - break; - case XML_TYPE_BATCH: - configFiles.push_back(std::make_pair(filename, XML_TYPE_BATCH)); - break; - } - } - } - - if (leftDirs.size() != rightDirs.size()) - { - notifyError(_("Unequal number of left and right directories specified."), _("Syntax error")); - return; - } - - auto hasNonDefaultConfig = [](const FolderPairEnh& fp) - { - return !(fp == FolderPairEnh(fp.leftDirectory, - fp.rightDirectory, - nullptr, nullptr, FilterConfig())); - }; - - auto replaceDirectories = [&](MainConfiguration& mainCfg) - { - if (!leftDirs.empty()) - { - //check if config at folder-pair level is present: this probably doesn't make sense when replacing/adding the user-specified directories - if (hasNonDefaultConfig(mainCfg.firstPair) || std::any_of(mainCfg.additionalPairs.begin(), mainCfg.additionalPairs.end(), hasNonDefaultConfig)) - { - notifyError(_("The config file must not contain settings at directory pair level when directories are set via command line."), _("Syntax error")); - return false; - } - - mainCfg.additionalPairs.clear(); - for (size_t i = 0; i < leftDirs.size(); ++i) - if (i == 0) - { - mainCfg.firstPair.leftDirectory = leftDirs [0]; - mainCfg.firstPair.rightDirectory = rightDirs[0]; - } - else - mainCfg.additionalPairs.push_back(FolderPairEnh(leftDirs [i], - rightDirs[i], - nullptr, nullptr, FilterConfig())); - } - return true; - }; - - //distinguish sync scenarios: - //--------------------------- - if (configFiles.empty()) - { - //gui mode: default startup - if (leftDirs.empty()) - runGuiMode(); - //gui mode: default config with given directories - else - { - XmlGuiConfig guiCfg; - guiCfg.mainCfg.syncCfg.directionCfg.var = DirectionConfig::MIRROR; - - if (!replaceDirectories(guiCfg.mainCfg)) return; - runGuiMode(guiCfg, std::vector()); - } - } - else if (configFiles.size() == 1) - { - const Zstring filename = configFiles[0].first; - - //batch mode - if (configFiles[0].second == XML_TYPE_BATCH) - { - XmlBatchConfig batchCfg; - try - { - readConfig(filename, batchCfg); - } - catch (const xmlAccess::FfsXmlError& e) - { - //batch mode: break on errors AND even warnings! - notifyError(e.toString(), std::wstring()); - return; - } - if (!replaceDirectories(batchCfg.mainCfg)) return; - runBatchMode(batchCfg, filename, returnCode); - } - //GUI mode: single config - else - { - XmlGuiConfig guiCfg; - try - { - readConfig(filename, guiCfg); - } - catch (const xmlAccess::FfsXmlError& e) - { - if (e.getSeverity() == FfsXmlError::WARNING) - showNotificationDialog(nullptr, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(e.toString())); - //what about simulating changed config on parsing errors???? - else - { - notifyError(e.toString(), std::wstring()); - return; - } - } - if (!replaceDirectories(guiCfg.mainCfg)) return; - //what about simulating changed config due to directory replacement? - //-> propably fine to not show as changed on GUI and not ask user to save on exit! - - runGuiMode(guiCfg, { filename }); //caveat: guiCfg and filename do not match if directories were set/replaced via command line! - } - } - //gui mode: merged configs - else - { - if (!leftDirs.empty()) - { - notifyError(_("Directories cannot be set for more than one configuration file."), _("Syntax error")); - return; - } - - std::vector filenames; - for (const auto& item : configFiles) - filenames.push_back(item.first); - - XmlGuiConfig guiCfg; //structure to receive gui settings with default values - try - { - readAnyConfig(filenames, guiCfg); //throw FfsXmlError - } - catch (const FfsXmlError& e) - { - if (e.getSeverity() == FfsXmlError::WARNING) - showNotificationDialog(nullptr, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(e.toString())); - //what about simulating changed config on parsing errors???? - else - { - notifyError(e.toString(), std::wstring()); - return; - } - } - runGuiMode(guiCfg, filenames); - } -} - - -void runGuiMode() { MainDialog::create(); } - - -void runGuiMode(const xmlAccess::XmlGuiConfig& guiCfg, - const std::vector& referenceFiles) -{ - MainDialog::create(guiCfg, referenceFiles, nullptr, true); //startComparison == true! -} - - -void showSyntaxHelp() -{ - showNotificationDialog(nullptr, DialogInfoType::INFO, PopupDialogCfg(). - setTitle(_("Command line")). - setDetailInstructions(_("Syntax:") + L"\n" + - L"FreeFileSync [" + _("config files") + L"]\n[-leftdir " + _("directory") + L"] [-rightdir " + _("directory") + L"]" + L"\n" + - L"\n" + - _("config files") + L"\n" + - _("Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.") + L"\n\n" - - L"-leftdir " + _("directory") + L" -rightdir " + _("directory") + L"\n" + - _("Any number of alternative directories for at most one config file."))); -} - - -void runBatchMode(const XmlBatchConfig& batchCfg, const Zstring& referenceFile, FfsReturnCode& returnCode) -{ - auto notifyError = [&](const std::wstring& msg, FfsReturnCode rc) - { - if (batchCfg.handleError == ON_ERROR_POPUP) - showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(msg)); - else //"exit" or "ignore" - logError(utfCvrtTo(msg)); - - raiseReturnCode(returnCode, rc); - }; - - XmlGlobalSettings globalCfg; - try - { - if (fileExists(getGlobalConfigFile())) - readConfig(globalCfg); //throw FfsXmlError - //else: globalCfg already has default values - } - catch (const xmlAccess::FfsXmlError& e) - { - assert(false); - if (e.getSeverity() != FfsXmlError::WARNING) //ignore parsing errors: should be migration problems only *cross-fingers* - return notifyError(e.toString(), FFS_RC_ABORTED); //abort sync! - } - - try - { - setLanguage(globalCfg.programLanguage); //throw FileError - } - catch (const FileError& e) - { - notifyError(e.toString(), FFS_RC_FINISHED_WITH_WARNINGS); - //continue! - } - - //all settings have been read successfully... - - //regular check for program updates -> disabled for batch - //if (batchCfg.showProgress && manualProgramUpdateRequired()) - // checkForUpdatePeriodically(globalCfg.lastUpdateCheck); - - try //begin of synchronization process (all in one try-catch block) - { - - const TimeComp timeStamp = localTime(); - - const SwitchToGui switchBatchToGui(referenceFile, batchCfg, globalCfg); //prepare potential operational switch - - //class handling status updates and error messages - BatchStatusHandler statusHandler(batchCfg.showProgress, //throw BatchAbortProcess - extractJobName(referenceFile), - timeStamp, - batchCfg.logFileDirectory, - batchCfg.logfilesCountLimit, - globalCfg.lastSyncsLogFileSizeMax, - batchCfg.handleError, - globalCfg.automaticRetryCount, - globalCfg.automaticRetryDelay, - switchBatchToGui, - returnCode, - batchCfg.mainCfg.onCompletion, - globalCfg.gui.onCompletionHistory); - - const std::vector cmpConfig = extractCompareCfg(batchCfg.mainCfg); - - bool allowPwPrompt = false; - switch (batchCfg.handleError) - { - case ON_ERROR_POPUP: - allowPwPrompt = true; - break; - case ON_ERROR_IGNORE: - case ON_ERROR_STOP: - break; - } - - //batch mode: place directory locks on directories during both comparison AND synchronization - std::unique_ptr dirLocks; - - //COMPARE DIRECTORIES - FolderComparison folderCmp; - compare(globalCfg.fileTimeTolerance, - globalCfg.optDialogs, - allowPwPrompt, - globalCfg.runWithBackgroundPriority, - globalCfg.createLockFile, - dirLocks, - cmpConfig, - folderCmp, - statusHandler); - - //START SYNCHRONIZATION - const std::vector syncProcessCfg = extractSyncCfg(batchCfg.mainCfg); - if (syncProcessCfg.size() != folderCmp.size()) - throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo(__LINE__)); - - synchronize(timeStamp, - globalCfg.optDialogs, - globalCfg.verifyFileCopy, - globalCfg.copyLockedFiles, - globalCfg.copyFilePermissions, - globalCfg.failsafeFileCopy, - globalCfg.runWithBackgroundPriority, - syncProcessCfg, - folderCmp, - statusHandler); - } - catch (BatchAbortProcess&) {} //exit used by statusHandler - - try //save global settings to XML: e.g. ignored warnings - { - xmlAccess::writeConfig(globalCfg); //FfsXmlError - } - catch (const xmlAccess::FfsXmlError& e) - { - notifyError(e.toString(), FFS_RC_FINISHED_WITH_WARNINGS); - } -} diff --git a/Application.h b/Application.h deleted file mode 100644 index defc1e17..00000000 --- a/Application.h +++ /dev/null @@ -1,34 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef FREEFILESYNCAPP_H -#define FREEFILESYNCAPP_H - -#include -#include -#include -#include "lib/return_codes.h" - - -class Application : public wxApp -{ -public: - Application() : returnCode(zen::FFS_RC_SUCCESS) {} - -private: - virtual bool OnInit(); - virtual int OnExit(); - virtual int OnRun(); - virtual bool OnExceptionInMainLoop() { throw; } //just re-throw and avoid display of additional messagebox: it will be caught in OnRun() - - void onEnterEventLoop(wxEvent& event); - void onQueryEndSession(wxEvent& event); - void launch(const std::vector& commandArgs); - - zen::FfsReturnCode returnCode; -}; - -#endif // FREEFILESYNCAPP_H diff --git a/BUILD/Changelog.txt b/BUILD/Changelog.txt deleted file mode 100644 index 8f8006d6..00000000 --- a/BUILD/Changelog.txt +++ /dev/null @@ -1,1331 +0,0 @@ -FreeFileSync 5.22 [2013-10-01] ------------------------------- -New options for automatic retry after error -Improved compliance with Windows User Experience guidelines -Harmonized popup dialog layouts -Correctly show program menu when main dialog receives focus (OS X) -Revised configuration dialog layouts and designs -Fixed crash on startup for CPUs without SSE2 support (Windows XP) -Work around wxWidgets bug for sorted list boxes (Linux) -Updated and revised help file -Early parameter validation for filter and sync config dialogs -Fixed followed directory symlinks being incorrectly excluded -Automatically calculate best initial message box size -Progress graph and status icons support high contrast color schemes -Include directory child-elements when manually setting filter -Allow manual filter for short name on overview panel -Don't treat file drops on directory input fields as URI (Linux) -Updated translation files - - -FreeFileSync 5.21 [2013-09-02] ------------------------------- -Detect moved/renamed files in mirror and custom variants -New database format for two way variant: old database files are converted automatically -Support double-clicking ffs_gui/ffs_batch files (OS X) -Integrated search panel (Ctrl + F, F3) into main dialog -Merged variant names into top button labels -Hide dock icon while minimized to notification area (OS X) -New keyboard shortcuts: F5, F6, F7, F8, F9, F10 -Further reduced size of database files by 10% -Fixed Outlook *.ost files found missing on VSS snapshot volumes -Added include filter context menu option -Correctly scroll to search hits on different grid -Always remove .ffs_tmp files permanently -Fixed layout for buttons with text and graphics for RTL languages (Arabic, Hebrew) -Revised file filter parser: new syntax for excluding items in subdirectories -Improved configuration merge algorithm -Fixed crash when showing help due to wxWidgets 64-bit bug in help component (Windows 8) -Avoid progress dialog graph flicker during resize when too few samples are available -Progress status when deleting files not greyed out anymore -Increased time-out to 20 seconds when checking for directory existence -Exclude broken symlinks via filter before showing error message -Follow symlinks when checking file/directory existence (Linux) -Consistently set batch error codes during startup phase -Updated translation files - - -FreeFileSync 5.20 [2013-08-03] ------------------------------- -Fixed crash on startup due to wxWidgets 64-bit bug in font enumeration (Windows 8) - - -FreeFileSync 5.19 [2013-08-02] ------------------------------- -Redesigned progress dialog including new items graph -New command line syntax: set directory names of a .ffs_gui/.ffs_batch externally -Explicit button on progress dialog to minimize to systray -Fixed progress graph labels being truncated (Debian, Ubuntu, openSUSE) -Resolved main dialog z-order issues during sync (OS X) -Reduced progress dialog layout twitching -Further improved comparison speed by 10% -Use proper config file path in file picker dialog (OS X) -Never interrupt when updating a file with fail-safe file copy after target was deleted -Prevent crash when closing progress dialog while paused (OS X) -Support external command lines starting with whitespace (Windows) -Show warning before starting external applications for more than 10 items -Start external applications synchronously if needed to avoid running out of system resources -Don't show hidden progress dialog when showing an error message in silent batch mode (OS X) -Correctly show file names containing ampersand characters in progress dialog -Adapt size of results dialog to fit contents -Correctly execute file move before parent directory will be deleted -Show a blinking system tray icon on errors instead of a modal dialog in RealtimeSync -Added installation size for Windows' Add/Remove Programs - - -FreeFileSync 5.18 [2013-07-02] ------------------------------- -Work around boost 1.54 bug "The procedure entry point GetTickCount64 could not be located in the dynamic link library KERNEL32.dll" (Windows XP) - - -FreeFileSync 5.17 [2013-07-02] ------------------------------- -Consider target file when updating followed file symlinks -Support moving files to recycle bin contained in followed directory symlinks -Move instead of copy updated files into versioning directory -Reduced memory peak when loading large database files after comparison -Check recycle bin existence only once per base folder and only if deletions occur (Windows) -Revised and enhanced error messages -Show moved files in same category as updated files -More pessimistic calculation of required disk space reducing false positives -Implemented platform-specific standard button ordering (Linux, OS X) -Set configuration panel primary orientation to vertical -Added new checks and error message strings for translation file parser -Revised middle grid inactive color and duplicate equality symbol -Skip XML comments while parsing config files -Redesigned confirmation popup dialogs -Standard button spacing conforms to operating system conventions -Shrinked memory consumption of file hierarchy data structures -Don't show file deletion dialog if selection is empty -Fixed incorrect progress statistics if a file or directory is deleted externally after comparison -Focus grid cursor row after switching sides with keyboard direction keys -Improved localization process: find translation deltas more easily, better error reporting -Reset initiated grid selection when changing grid cursor -Improved sync progress dialog layout -Suppress dubious wxWidgets error message "locale 'es_AR' can not be set". (OS X) -Don't show busy cursor on synchronization results dialog -Log error message upon retry as type info only -Updated translation files - - -FreeFileSync 5.16 [2013-06-01] ------------------------------- -Integrated both category and sync action view into middle grid -Condensed folder pair display names on overview panel -Consider symlinks and junctions when copying locked files (Windows Vista) -Resolved failure to set directory lock within Windows XP as Virtual Box guest -Period resolves to working directory again -Fixed "DecodePointer could not be located in KERNEL32.dll" (Windows 2000) -Support closing progress dialog forcefully during sync (OS X) -Don't disable all child items if directory traversal fails for a single item only -Simplified deletion confirmation dialog (removed "delete on both sides") -Work around wxWidgets leaking memory on exit (OS X) -Avoid wxWidgets crash when deleting folder pair control (OS X) -Prevent wxWidgets corrupting stack when wxLocale is allocated statically (Linux) -Use GetUserDefaultLangID to determine installer default language -Avoid progress speed and remaining time jitter -Check existence only once for duplicate base directories -Detect invalid file symlinks pointing to directories (Windows) -Disable unsuitable buttons in pop up dialogs when checkbox is set -Copy folder attributes if source is a junction already on Windows XP instead of Vista -Mark failed UTF conversions with replacement character -Do not restore main dialog position outside visible screen area (multi monitor setup) -Support detection of moved files through symlinks -Reduced memory consumption when detecting moved files -Check for duplicate file ids when detecting renamed files -Redetermine volume id for followed directory symlinks -Removed "Compare_Complete.wav" -Don't accept file deletion confirmation in less than 50ms -Systematically resolved translation bugs -Added Serbian language - - -FreeFileSync 5.15 [2013-05-01] ------------------------------- -New menu option to activate/deactivate automatic update checking -Show status message while checking for program updates -Faster start up times through asynchronous config file checking -Automatically migrate configuration files to new format -New context menu options to copy and paste filter settings -Support file and folder names with trailing space or period characters -Do not show superfluous scroll bars for multiple folder pairs -Correctly show long file paths when moving to recycle bin failed (Windows Vista and later) -Status feedback before blocking while creating a Volume Shadow Copy -Do not show dummy texts while initializing progress dialog (OS X) -Allow to maximize filter dialog -New column for item count on overview panel -Allow CTRL + C to copy selection to clipboard on overview panel -Consider current view filter for file selection on overview panel -Workaround silent failure to set modification times on NTFS volumes (Linux) -Avoid main dialog flash when closing progress dialog (Linux) -Do not show middle grid tool tip when dragging outside visible area -Reduced file accesses when loading XML files -Simplified structure of GlobalSettings.xml -Allow to change default exclusion filter via GlobalSettings.xml: "DefaultExclusionFilter" -Split filter entries over multiple rows in ffs_gui/ffs_batch XML files -Resolved failed assert during start up (ReactOS) -Create directory locks after one-time existence check -Show warning when locking directory failed -Reset main dialog layout to fix top panel default height being too small -New help file topic "Expert Settings" -Updated translation files - - -FreeFileSync 5.14 [2013-03-31] ------------------------------- -Do not process child elements when parent directory creation fails -Start comparison after pressing Enter in directory input fields -Lead grid is determined via keyboard input instead of input focus change -Ignore empty directory entries in RealtimeSync -Restored mouse cursor "snap to default button" -Implemented file icon support for sync preview (OS X) -RealtimeSync exit via menu working again -Restore main dialog even if "close progress dialog" is selected -Show full path when failing to create directory on not existing target drive -Middle grid tool tip shown correctly again (Suse Linux/X11) -Prevent process hang when manually writing to directory history (Linux and OS X, wxWidgets 2.9.4) -Resolved crash after showing help dialog (OS X) -Properly handle non-ASCII characters for external commands (OS X) -Support UTF8 format restrictions on file systems like HFS (OS X) -Do not stretch small thumbnail icons (Linux) -Use 32x32 instead of 48x48 as medium icon size on Windows XP -Properly size non-jumbo icons in thumbnail view (Windows Vista and later) -Reduced GDI resources for file icon buffer (Windows) -Automatically check for updates weekly without showing pop up on first start -Restored program logo in systray progress indicator -Fit grid row label to match wide font sizes -Added macros %csidl_Downloads%, %csidl_PublicDownloads%, %csidl_QuickLaunch% (Windows Vista and later) - - -FreeFileSync 5.13 [2013-03-06] ------------------------------- -Prepared support for new build on Mac OS X -Time out for not existing directories after 10 seconds -Check directory existence in parallel -Inform about all missing directories via a single error message -Show remaining time considering relative error of 10% -Check for grid icon updates only when needed -Revised directory lock process detection -Implemented high resolution icons -Accessibility: fixed unreadable labels -More polished user interfaces -Fixed time stamp not being set on NFS/Samba shares (Linux) - - -FreeFileSync 5.12 [2013-02-04] ------------------------------- -Dynamic statistics adjustment during synchronization -Allow to save active view filter settings as default (context menu) -Stay responsive while checking recycle bin existence on slow disks -Reset option "Delete on both sides" upon each manual deletion -Added context menu to allow deletion of last used configurations -Support numpad add/subtract keys for overview tree -Revised external application integration -Call external applications for multiple selected items -Automatically schedule abandoned recycle bin temp directories (.ffs_tmp) for deletion -Binary comparison speed estimate considers errors and short-circuit evaluation -Use full time window of sync phase when calculating overall speed -Added Arabic language - - -FreeFileSync 5.11 [2013-01-06] ------------------------------- -New file versioning scheme: move to folder replacing existing files -Fixed high CPU consumption after longer syncs -Improved .ffs_batch configuration file handling -Allow to quick save .ffs_batch files on main dialog and program exit -Convert batch-exclusive settings when opening a .ffs_batch file on main dialog -Redesigned configuration dialog layout -Enhanced all file I/O error messages to show locking processes (Windows Vista and later) -Separator in CSV file now locale dependent -Avoid "Windows Error Code 2" for truly empty directories -Macro %month% resolves to decimal number -New macro %timestamp% -Revised sync progress graph -Fixed progress graph graphics glitch for RTL layout -Allow XML element values to contain non-escaped quotation marks -Updated help file -Updated translation files - - -FreeFileSync 5.10 [2012-12-03] ------------------------------- -Show synchronization log as a grid in results dialog -Improved grid scrolling performance (most noticeable on Linux) -Allow grid selection starting from outside of the grid -RealtimeSync: Support drag & drop on main dialog for *.ffs_real and *.ffs_batch files -Optimized memory consumption when generating log for millions of items -Optimized memory consumption when exporting to CSV file -Have grid row height match window default font size -Catch out of memory when copying huge lists into clipboard -Fixed failure to resume aborted sync after having FFS implicitly create target directory -Fixed horizontal mouse wheel scrolling direction for RTL languages (Hebrew) -RealtimeSync: Fixed drag and drop not working (Linux) -Set maximum size of LastSyncs.log in GlobalSettings.xml element -Show error when trying to copy a named pipe rather than hang (Linux) -Improved copy routine minimizing file accesses (Linux) -Copy file access permissions by default (Linux) -Fixed unexpected "File or Directory not existing" error during file copy (Linux) - - -FreeFileSync 5.9 [2012-11-03] ------------------------------ -Scroll grid under mouse cursor -Move files directly to recycle bin without parent "FFS 2012-05-15 131513" temporary folders -Offer $HOME directory alias in directory drop down list (Linux) -Support for tilde (~) character in input folder paths (Linux) -New environment variables for RealtimeSync: %change_action%, "%change_path% -Use Internet Explorer proxy settings for new version check (Windows) -Show proper error message after failed symlink creation -Start comparison upon double-clicking config list -New batch return code: "Synchronization completed with warnings" -Hide files that won't be copied by default if direction "none" is part of the rule set (e.g. update variant) -Remember save config and folder picker dialog positions separately -New sync completion sound -Fixed sync completion sound not playing (Ubuntu) - - -FreeFileSync 5.8 [2012-10-01] ------------------------------ -New icon theme -Dynamic save button and dialog title show unsaved configuration -Exclude all folders if file size or time span filters are active -Added macros %csidl_Nethood%, %csidl_Programs%, %csidl_Startup% -Fixed crash on failed CRT parameter validation (Windows) -Auto-updater handles moved web address for version check -Fixed configuration conversion error when deleting into versioning folder -Avoid modal error dialogs in batch mode unless error handling is set to "pop up" -Set return codes in batch mode even if modal dialogs are shown -Disabled UAC virtualization for 32-bit user-mode process -Descriptive error message when setting invalid dates on FAT volumes - - -FreeFileSync 5.7 [2012-09-04] ------------------------------ -Modern directory selection dialog (Windows Vista and later) -New file versioning scheme appending revision number to files -New sync option to limit number of versions per file -Revised configuration format for *.ffs_gui/*.ffs_batch files: old format will be supported for some time -Fixed crash on invalid file modification times -Fixed zlib error on empty database stream -GlobalSettings.xml: added "MaxSize" parameter to "ConfigHistory" -Fixed occasional crash on GTK 2 (Linux) -Always show "items processed" in log file -Simplified configuration dialogs -Fixed password prompt not always coming up when connecting to a network share -Support environment variables everywhere: +on completion; +external applications; +RTS command -Harmonized external application macros: %item_path%, %item_folder%, %item2_path%, %item2_folder% -Updated translation files - - -FreeFileSync 5.6 [2012-08-02] ------------------------------ -Resize left and right grids equally -Allow to move middle grid position via mouse -Automatically resize file name columns -Do not follow reparse points other than symlinks and mount points -Warn if Recycle Bin is not available during manual deletion -Fixed error when saving log file into volume root directory -Show files which differ in attributes only in the same category as "equal" files -Apply hidden attribute to lock file -Fixed potential "access denied" problem when updating the database file -Show errors when saving configuration files during exit (ignore for batch mode) -Mark begin of comparison phase in the log file -More detailed tool tip describing items that differ in attributes only -Added Scottish Gaelic translation - - -FreeFileSync 5.5 [2012-07-01] ------------------------------ -New database format for variant: old database files are converted automatically -Tuned performance for variant when saving database for millions of files: > 95% faster -Support partial database updates for variant respecting current filter -Reduced size of database files by 30% -Fine-tuned algorithm to avoid certain conflicts after changing comparison settings -Lower peak memory consumption when reading database participating in multiple sync jobs -Refined symlink categorization and variant handling -Always save log of last syncs to %appdata%\FreeFileSynce\LastSyncs.log (128 kB limit) -"Save" and "Save As" menu options -Properly show status message after save configuration -Avoid issues applying file modification time on certain NAS -Refined last-used configuration handling -Avoid race-condition: database file is only read if directory is existing -Protect against temporary network drop between comparison and synchronization -Rearranged statistics panel to save vertical space when vertically aligned -Removed limitation for number of conflicts shown in the warning message and log -Consider both global and local filter when estimating whether folder could contain matches -Updated translation files - - -FreeFileSync 5.4 [2012-06-01] ------------------------------ -Copy all NTFS extended attributes -Improved statistics panel -Improved main grid -Support context menu for files in overview tree -Process double-clicks outside main grid -Allow quoted paths ending with backslash in command line: "C:\" -Fully localized number formatting (Windows) -Fixed deletion dialog header being trimmed (Linux) -Fixed exclusion via context menu (Linux) -Preserve row label width after comparison (Linux) -Updated help file -New batch mode return codes, see help file -Prefix custom deletion directory with job name -Use the same time stamp for log file and versioning -Handle folder drag and drop outside main grid -Avoid name clash having multiple folder pairs delete into the same versioning folder -Exit FreeFileSync automatically while upgrading to new version -Accessibility: Support high contrast color schemes -Yet another UI design overhaul -Fixed "access denied" issue on OS X-hosted network shares -Support Citrix folder shares -Support Arch Linux (Chakra) -Updated translation files - - -FreeFileSync 5.3 [2012-05-02] ------------------------------ -Show which processes lock a file during synchronization (Windows Vista and later) -Use unbuffered copy to speed up copying large files (Windows Vista and later) -Preserve NTFS sparse files -Support referencing all logical volumes by name (including FreeOTFE virtual drives) -Fixed lag showing "Searching for directory" on comparison -New context menu filter option: exclude by short name -Use clicked-on row rather than anchor when determining action for shift-selection -Refresh grid after pressing "CTRL + A" -Add base folder pairs to CSV export -Show full path in tool tip if multiple folder pairs are used -Show child dialogs on same monitor as parent dialog on multiple monitor systems -Added statistics at beginning of batch log file -Fixed batch mode final speed statistic and reset graph after binary comparison -RealtimeSync: Automatically retry after 15 seconds if an error occurs -Show button images untrimmed (Linux) -Fixed problems with auto-closing progress dialog (Linux) -Fixed unresponsive progress dialog and systray icon (Linux) -New option in GlobalSettings.xml: "LockDirectoriesDuringSync" -Added Lithuanian translation -Added Norwegian translation -Updated translation files - - -FreeFileSync 5.2 [2012-04-01] ------------------------------ -Fixed runtime error "Error comparing strings! (LCMapString)" (Windows 2000, XP only) - - -FreeFileSync 5.1 [2012-03-31] ------------------------------ -New category for time span filter: last x days -Fixed "Error loading library function: GetVolumeInformationByHandleW" if NTFS permissions are copied -Fixed command line issues: allow config name without extension, allow multiple directories instead of a config file -Reenabled global shortcut F8 to toggle data shown in middle grid -Unified error handling on failure to create log directory -Do not close batch creation dialog after save -Tree view: compress and filter root nodes the same way as regular folder nodes -Fixed wrong tool tip being shown if directory name changes -Date range selector does not trim year field anymore -Show action "do nothing" on mouse-hover for conflicts in middle grid -Fixed "Windows Error Code 59: An unexpected network error occurred" -New filter pattern: *\* matches all files in sub directories of base directories -Fixed "*?" filter sub-sequence -Fixed "Cannot convert from the charset 'Unknown encoding (-1)'!" -Support Ctrl + A in filter dialog -Support large filter lists > 32 kByte -Allow to hide file icons -Avoid switching monitor when main dialog is maximized on multiple monitor systems -Improved huge XML file loading times by a factor of 3000, saving by a factor of 3 -Restore grid scroll position after repeated comparisons -Show log after sync when non-fatal errors occurred -Fixed crash in UTF8 conversion when processing a corrupted ffs_db file -Even more pedantic user interface fine-tuning -Compiles and runs on openSuse 12.1 -Fixed grid page-up/down keys scrolling twice (Linux, wxGTK 2.9.3) -Fixed unwanted grid scrolling when toggling middle column (Linux, wxGTK 2.9.3) -Fixed middle grid tool tip occasionally going blank (Linux) -Support single shift-click to check/set direction of multiple rows -Removed gtkmm dependency (Linux) -Installer remembers all settings for next installation (local installation only) -All executables digitally signed -Updated translation files - - -FreeFileSync 5.0 [2012-01-30] ------------------------------ -New grid control -New tree control -Revised Right to Left layout for Hebrew -Updated translation files - - -FreeFileSync 4.6 [2011-12-25] ------------------------------ -Execute user-defined command after synchronization -Option to automatically close synchronization progress dialog -Automatically adjust statistics during sync if changes happened after comparison -Fixed "DecodePointer could not be located in KERNEL32.dll" (Windows 2000) -Fixed "Windows Error Code 31: A device attached to the system is not functioning" -Mouse wheel will scroll list of folder pairs instead of toggle through directory history -No error message when scanning a single directory -Minimized disk accesses when deleting files -Less mouse-clicks required when overwriting configuration -Pause timers while showing error messages -Show error message for malformed external commands -Support detection of moved files over "subst" alias -New default font: Segoe UI (Windows Vista and later) -Save settings before forced exit due to shutdown or log off -Updated translation files - - -FreeFileSync 4.5 [2011-11-25] ------------------------------ -Fixed "Windows Error Code 50: The request is not supported" -Fixed "Windows Error Code 124: The system call level is not correct" -Fixed config load performance problem if network drive is not reachable -Support traversing truly empty directories (no ., ..) (Windows) - - -FreeFileSync 4.4 [2011-11-22] ------------------------------ -Fixed error copying files containing alternate data streams (Windows) - - -FreeFileSync 4.3 [2011-11-20] ------------------------------ -Detection of moved and renamed files -New database format for mode: a full sync is suggested before upgrading -Fixed overwrite symlink with regular file -Fixed synchronization result dialog GUI glitch (Windows XP) -Fixed macro %weekday% -RealtimeSync: Fixed support for manual volume unmount (Windows) -Added Croatian language -Updated translation files - - -FreeFileSync 4.2 [2011-11-02] ------------------------------ -Implemented workaround for compiler bug leading to uncaught exceptions (Windows 32 bit) -Shadow Copy Service: Native support for Windows7/Server 2008 -Fixed reference by volume name parsing issue -Rearranged synchronization progress dialog -More concise log message format -Fixed default file icon (Kubuntu) -Support for wxWidgets 2.9 series (Ubuntu/Kubuntu) -FAT 2 sec tolerance for files dated in the future -Honor DACL/SACL inheritance flags when copying NTFS permissions (Windows) -New option in GlobalSettings.xml: "RunWithBackgroundPriority" (Windows Vista and later) - - -FreeFileSync 4.1 [2011-10-09] ------------------------------ -Improved synchronization progress dialog -Show all available aliases in directory history list -Show password prompt when connecting to mapped network share -Removed busy cursor after program start up -RealtimeSync: atomically detect missing directories -Handle not existing reference by volume name as an invalid path -Improved start up responsiveness by checking dir/file existence asynchronously -Fixed loading incorrect directory name when using multiple folder pairs -Allow passing multiple configurations via command line -Allow passing multiple directory names via command line - - -FreeFileSync 4.0 [2011-09-25] ------------------------------ -Thumbnail list view -Option to specify comparison settings at folder pair level -Correctly update parent-child relationship when changing sync directions -Show history list for additional folder pairs -Switch between volume name and full path in directory history list -Perf: shrinked folder matching CPU time by over 70% -Show windows environment strings in directory history list -Show windows special folder IDs in directory history list -Fixed progress dialog going into background on heavy load -Support creating old 8.3 directories -Take over configuration name when creating new batch job -Remember batch-specific settings when loading a ffs_batch file from main dialog -Drag & drop ffs_batch files on main dialog to test and edit batch settings -Automatically resolve objects deleted externally after comparison -Date column context menu: manual time range selector -New categories for time span filter: today, this week, this month, this year -Respect both sides when sorting by relative path -Updated COM error message reporting resolving "Unknown error" -Smarter configuration merge algorithm -Correctly show existing folders on both sides when using include filter -Fixed network access using WebDrive -Update modification times during file copy to write current values to database -RealtimeSync: write name of changed file into environment variable "changed_file" -RealtimeSync: fixed network drop incorrectly being handled as a failure -Set default direction according to current configuration when deleting manually -Plenty of GUI improvements -Updated help file -Updated translation files - - -FreeFileSync 3.21 [2011-08-19] ------------------------------- -Fixed deleting to user-defined directory -Fixed crash when using include filter -New global option to disable transactional file copy - - -FreeFileSync 3.20 [2011-08-11] ------------------------------- -Scan multiple directories in parallel -Automatically resolve disconnected network maps -Fixed temporal hang when dropping large files on main dialog - mode: Fixed issue regarding directory names differing in case during first sync -Delete permanently if recycle bin is not available (Linux) -Keep FreeFileSync responsive when trying to access non-existent network folder -Support for Ubuntu Unity Launcher (Linux) -RealtimeSync: Failure notification if command line is invalid (Linux) - - -FreeFileSync 3.19 [2011-07-23] ------------------------------- -Exclude sub directories from synchronization which cannot be accessed during comparison -Warning if Recycle Bin is not available instead of deleting silently (Windows) -Adapted log message if missing recycler leads to permanent deletion (Windows) -Revert to per file recycle bin handling if creating temp recycler folder fails -Avoid orphaned deletion temp directories on network drives -Quick-select comparison and synchronization options via double-click -New right-click drop down menu on comparison and synchronization settings button -New database design: copying the database file does not lead to complications anymore -Full support for "retry" while comparing -Don't copy empty folders when filtering by time span -Allow loading/merging multiple configurations files via file open dialog -Allow loading/merging multiple configurations in last used config list -Fixed system shutdown interruption during batch mode -Allow saving log files in both silent and non-silent batch jobs -Reduced main dialog flicker when switching configurations -Database and lock files created by FreeFileSync do not trigger RealtimeSync anymore -Restrict maximum number of visible folder pairs to 6 (configurable via GlobalSettings.xml) -New macros: %day%, %hour%, %min%, %sec% - - -FreeFileSync 3.18 [2011-07-03] ------------------------------- -Launcher running synchronously and returning application error code -Fixed sort by file extension -Fixed drag and drop of SAMBA network folder -Render (all) invalid file dates correctly on GUI -Correct layout selection for RTL and LTR languages -Correct GUI status texts while waiting for directory lock -Properly set default directory when loading configuration -New XML framework: zen::Xml -Added Hebrew language -Added Danish language -Updated translation files - - -FreeFileSync 3.17 [2011-05-20] ------------------------------- -Filter files by size -Filter latest files by time span -Launcher automatically selecting 32/64 bit executable on start up -More detailed systray progress indicator -New database format for mode: a full sync is suggested before upgrading -Update database at individual file level (support for partial and aborted syncs) -New translation file format -Dynamically load existing translation files -Correct translation plural forms -Improved directory locking strategy -Restructured installation package -One button-click synchronization -Fixed CSV character encoding -Put CSV values in quotes if they contain semicolons -Explicit button and settings for "Custom" sync variant -> old configurations need to be migrated -Keyboard shortcuts also on middle grid -Minimize progress dialog by clicking on taskbar -Render invalid file dates correctly on GUI -Process user-defined commands via shell execution (FFS and RTS) -Allow base directory names having trailing white-space -Added Ukrainian language -Updated translation files - - -FreeFileSync 3.16 [2011-04-21] ------------------------------- -Fixed file copy issues on SAMBA shares -Small GUI fixes - - -FreeFileSync 3.15 [2011-04-19] ------------------------------- -Overwriting a file as fully transactional operation -Optimized synchronization speed (non-cached volumes, e.g. memory sticks in particular) -Volumes can be specified by name: []\ (use case: variable drive letters, RealtimeSync) -Copy NTFS compressed, encrypted and sparse file attributes -Copy NTFS compressed and encrypted directory attributes -Copy NTFS alternate data stream -Improved performance: CSV export, copy to clipboard, sync log display -Improved color theme support -Fixed crash on certain system text color settings -Fixed progress numbers for manual deletion -Allow aborting manual deletion via escape key -Use relative name for file tool tip -Automatically redirect arrow keys to main grid -More tolerant directory creation (operation not supported/wrong parameter) -More tolerant file move: ignore existing files (user-defined deletion directory) -Added macro %weekday% - - -FreeFileSync 3.14 [2011-03-20] ------------------------------- -New keyboard shortcuts: F5: compare F6: synchronize -Skip to next folder pair if fatal error occurred (instead of abort) -Reload last selected configuration on start up -Abort with error when copying to empty directory field -Full log information after comparison (including file transfer) -Check read access for source file before overwriting target -Fixed possible application crash after comparison -Fixed possible network freeze when comparing -Maximum number of log files can be specified -Don't condense white-space when loading XML configuration -RealtimeSync: Put executable name in quotes when parsing *.ffs_batch file -Large program icons - 256 x 256 -Handle daylight saving time(DST) on FAT network shares -Skip DST handling if drive does not support accurate file times -Many small GUI/usability fixes -Added Korean translation - - -FreeFileSync 3.13 [2011-01-16] ------------------------------- -Implemented Advanced User Interface to allow user specified layout customizations -Process case sensitive file/directory/symlink names -Synchronize name/attributes only avoiding full copy if appropriate -Prevent hibernation/sleep mode during comparison and synchronization (Windows) -New database format: single file for FreeFileSync 32 and 64 bit versions - - full sync suggested before migrating to v3.13 - - old sync.x64.ffs_db files may be deleted -Improved algorithm to calculate remaining time -Allow resizing window containing multiple folder pairs -Show folder short names in column file name -Correctly report message "nothing to sync" in batch mode -Removed libjpg-8 dependency (Linux) -Fixed loading correct maximized position on multi-screen desktop -RealtimeSync: Removed blank icons in ALT-TAB list during execution of command line -Show RealtimeSync job name as systray tool tip -Last used configurations as sorted list without size limitation -Remove redundant configuration when merging multiple ffs_gui/ffs_batch files -Warning if folder is modified that is part of multiple folder pairs -Aggregated warning messages for all folder pairs instead of one per pair -Added privilege to access restricted symlink content -Added Greek translation - - -FreeFileSync 3.12 [2010-11-28] ------------------------------- -Allow empty folder pairs without complaining -Automatically exclude database and lock files from all (sub-)directories (not only from base) -Resize grid columns on both sides in parallel -Fixed tool tip foreground text color (Linux) -Search via CTRL + F and F3 now as global hotkeys -Fully portable use of directory locking (Windows/Linux, 32/64 bit) -RealtimeSync: Treat missing network path the same as missing local path -Show current job name during synchronization (batch/gui) -Allow copying dereferenced (=followed) directory Symlinks over network share -Fail to copy Symlinks (=direct) over network share instead of silently creating empty folder (Windows XP) -Copy NTFS junctions as Symlinks (avoiding permission checks) -RealtimeSync: ignore request for device removal on network mapped drives -Support for copying SELinux security contexts -Fixed moving buttons in synchronization dialog -Allow deleting currently selected item from list of last used folders (not before wxWidgets 2.9.1) -Avoid losing focus after manually deleting a file -Preserve custom changes to sync directions after manually deleting a file -Handle empty tool tips correctly (Linux) -Updated translation files - - -FreeFileSync 3.11 [2010-09-20] ------------------------------- -Fixed migration issue: reasonable default value for number of folder pairs -Better message box background color - - -FreeFileSync 3.10 [2010-09-19] ------------------------------- -Automatically solve daylight saving time and time zone shift issues on FAT/FAT32 (finally) -Instantly resolve abandoned directory locks associated with local computer -Show expanded directory name as tool tip and label text (resolves macros and relative paths) -Do not copy relative file attributes for base target directories that are created implicitly -Move dialogs by clicking (almost) anywhere -RealtimeSync: ignore request for device removal on Samba shares -Added UTF-8 BOM for CSV export -Correctly handle window position on multi-screen desktop -Disabled warning "database not yet existing" -RealtimeSync: replaced delay by minimum idle time -Maximum number of folder pairs configurable via GlobalSettings.xml (XML node ) -Added tool tips to display long filenames on main grid -Keep application responsive when deleting large directories -Vista/Windows 7: harmonize modification times shown on main grid with Windows Explorer -Changed background color to avoid unreadable texts in combination with certain color themes -Toggle middle grid comparison result/sync preview with right mouse button click -Further GUI enhancements/polishment/standard conformance -Updated translation files - - -FreeFileSync 3.9 [2010-08-10] ------------------------------ -Advanced locking strategy to allow multiple processes synchronize the same directories (e.g. via network share) -Merge multiple *.ffs_batch, *.ffs_gui files or combinations of both via drag & drop -Copy file and folder permissions (requires admin rights): - - Windows: owner, group, DACL, SACL - - Linux: owner, group, permissions - - correctly handle Symbolic Links - - new option in global settings -Compare by content evaluates Symbolic Links -32-Bit build compiled with MinGW/GCC to preserve Windows 2000 compatibility -RealtimeSync: Handle requests for device removal (USB stick) while monitoring -Sort by file size: group symlinks before directories -Added macros %week%, %month%, %year% for creating time-stamped directories -Touch database file when changes occurred only -Moved settings "file time tolerance" and "verify copied files" to GlobalSettings.xml -Updated translation files - - -FreeFileSync 3.8 [2010-06-20] ------------------------------ -New options handling Symlinks: ignore/direct/follow => warning: new database format for mode -Fixed crash when starting sync for Windows XP SP2 -Prevent tool tip from stealing focus -Show associated file icons (Linux) -Run folder existence checks in separate thread (faster network share access) -Write mode database file even if both sides are already in sync -Don't raise status dialog to the top after synchronization -Embedded version information into executable (Windows) -Migrated compiler to Visual C++ 2010 (Windows) -Avoid losing manual changes when excluding via context menu -Adjusted auto-updater web-address -Updated translation files - - -FreeFileSync 3.7 [2010-05-16] ------------------------------ -RealtimeSync: Trigger command line only if all directories are existing -Allow for drag and drop of very large files -Batch modus: New "Switch" button opens GUI modus when warnings occur -Support copying old 8.3 filenames correctly -Handling of Symbolic Links configurable via GUI -Fine tuned calculation of remaining disk space for custom deletion directories -Save default config files only if actually changed -NSIS installer: Support for /D and /S switches -Fixed resource loading if installation folder is not working directory (Linux build) -Consolidated batch creation dialog - mode: Detect conflict when a directory shall be deleted while new sub-elements are to be copied -Automatically mark left behind temporary files (*.ffs_tmp) for deletion with next sync -New Project website: http://freefilesync.sourceforge.net -A lot of small GUI fixes -Updated translation files - - -FreeFileSync 3.6 [2010-03-31] ------------------------------ -Fixed occasional crash when starting FreeFileSync - - -FreeFileSync 3.5 [2010-03-27] ------------------------------ -Allow mode syncs between 32 bit, 64 bit, Windows and Linux builds -Show progess indicator in window title -Support for progess indicator in Windows 7 Superbar -Reduced progress indicator flicker -Prevent silent batch mode from taking keyboard focus -Improved error messages (loading/saving/copying files) -Improved environment variable tolerance: strip blanks and double-quotes -RealtimeSync: Fixed crash when double-clicking systray icon -Allow aborting all operations via Escape key -Added British English translation - - -FreeFileSync 3.4 [2010-03-04] ------------------------------ -Performance: Reduced Recycle Bin access time by 90% -Recycle Bin support for Linux -Performance: Reduced binary comparison sequential read time (by up to 75% for CD/DVD access) -Improved synchronization sequence to avoid disk space shortage: overwrite large files by small ones first -Fixed problems with file renaming on Samba share -New free text grid search via shortcuts CTRL + F and F3 -Show number of processed files at end of synchronization -New optional grid column: file extension -New comparison category icons -Fixed handling sync-config of first folder pair -Allow moving main dialog by dragging client area instead of title bar only -Enhanced help file: Run RealtimeSync as Service -Prefix log files with name of batch job -Fixed GUI right-to-left mirroring for locales Hebrew and Arabic -Portable version: save configuration in installation folder -Many small GUI enhancements -Updated translation files -New Linux .deb package: ppa:freefilesync/ffs - - -FreeFileSync 3.3 [2010-02-02] ------------------------------ -New installer package for portable/local/32/64-bit versions -Built-in support for very long filenames: apply \\?\-prefix automatically -New button for synchronization preview: show equal files -RealtimeSync: Respond to directory or volume arrival, e.g. USB stick insert -Start comparison automatically when double-clicking on *.ffs_gui files -Visual progress indicator for sys-tray icon -Fixed string comparison for 'ß' and 'ss' (all Windows versions) -Fixed general string comparison for Windows 2000 -Significantly faster file icon loading -Applied new IFileOperation interface for recycle bin (Windows >= Vista) -Patched mode to handle FAT32 2-second file time precision -Play optional sound after comparison: "Compare_Complete.wav" -Allow environment variables for log file-directory -Enhanced conflict reporting -Added Swedish translation -Updated translation files - - -FreeFileSync 3.2 [2009-12-13] ------------------------------ -Native Windows 64-Bit version (including Volume Shadow Copy Service) -Harmonized filter handling: global and local file filters -Unified handling of first folder pair: all pairs now semantically equal -Use environment variables within directory names (e.g. %USERNAME%) -New keyboard shortcuts to set sync-direction: ALT + -Allow copying to non-encrypted target directory -Fixed sort by filename -Fixed GDI resource leak when scrolling large grids -Fixed string comparison for 'ß' and 'ss' (Windows >= Vista) -Faster file icon loading -Remove elements in folder drop down list via DEL key -New integrated help file -Play optional sound after synchronization: "Sync_Complete.wav" -Several GUI/usability improvements -Created package for PortableApps.com -Added Finnish translation -Updated translation files - - -FreeFileSync 3.1 [2009-10-26] ------------------------------ -Support for multiple data sources in Automatic mode -Copy file and folder create/access/modification times when synchronizing -Progress dialog can be minimized to systray (Batch and GUI mode) -Allow switching between silent/non-silent batch mode interactively -Some GUI improvements - - -FreeFileSync 3.0 [2009-10-15] ------------------------------ -New synchronization mode: -Consolidated batch mode error handling -Fixed crash when comparing multiple pairs by content -Fixed calculation of remaining objects -Fixed swapping grids -Show scanned files when traversing with filter enabled -New default filter values -New macros %time%, %date% for creating time-stamped directories -Avoid corrupted data when program is terminated unexpectedly -Prevent deletion when source-directory (temporarily) is not accessible -Native Unicode support for Linux build -Added Romanian translation -Added Turkish translation -Updated translation files - - -FreeFileSync 2.3 [2009-09-27] ------------------------------ -New filter and sync configuration at folder pair level -Improved sorting: sort across multiple folder pairs - stable sorting in middle grid - consolidated sorting of sync-direction -Open external applications via context menu(customizable) -Removed performance penalty when using include filters -Improved filter syntax for strings beginning with wildcards -Default handling for conflict files now configurable -New option to show all hidden dialogs again -Fixed issue with macros %nameCo, %dirCo -New option in *.ffs_gui/ffs_batch files: Verify copied files -Use Windows Volume Shadow Copy for shared and locked files(new) -More detailed information in *.cvs export -Use current working directory to save global configuration (portable version) -Respect sub directories when manually changing sync-direction -Allow import of batch configuration into GUI mode -Some small GUI improvements -New shortcuts: SPACE: (de-)select rows; ENTER: start external application -Performance improvements: Reduced CPU time by 28%, (peak) memory consumption by 20% -Added Traditional Chinese translation -Updated translation files - - -FreeFileSync 2.2 [2009-08-16] ------------------------------ -New user-defined recycle bin directory -Possibility to create synchronization directories automatically (if not existing) -Support for relative directory names (e.g. \foo, ..\bar) respecting current working directory -New tool tip in middle grid showing detailed information (including conflicts) -Status feedback and new abort button for manual deletion -Options to add/remove folder pairs in batch dialog -Added tool tip showing progress for silent batchmode -New view filter buttons in synchronization preview -Revisioned handling of symbolic links (Linux/Windows) -GUI optimizations removing flicker -Possibility to create new folders via browse folder dialog -Open files with associated application by special command string -Improved warning/error handling -Auto-adjust columns automatically or manually with CTRL + '+' -New macros for double-click command line: %name, %dir, %nameCo, %dirCo -Fixed runtime error when multiple folder pairs are used -New tool 'RealtimeSync': Watch directories for changes and start synchronization automatically -Improved XML parsing, fault tolerance and concept revisioned -More detailed statistics before start of synchronization -Removed superfluous border for bitmap buttons (Linux only) -Added Czech translation -Updated translation files - - -FreeFileSync 2.1 [2009-07-03] ------------------------------ -Fixed bug that could cause FreeFileSync to crash after synchronization -Compiled with MS Visual C++ 2008 using static runtime library - - -FreeFileSync 2.0 [2009-06-30] ------------------------------ -Copy locked files using Windows Volume Shadow Copy -Load file icons asynchronously for maximum display performance -Handle include filter correctly when comparing -Display optional summary window before starting synchronization -Adjust sync direction properly when switching sides -Info about sync variant on main dialog -Issue a warning message for each conflict type when comparing -Save default configuration in user application path (Installer based version) -Limit main dialog minimum size -Update grid row labels while scrolling -Right-click selects cell before opening context menu -New context menu options to manually assign a sync-direction -Moved sync-preview switch into middle grid's context menu -Possibility to remove top folder pair -Fixed calculation of row total in sync preview -File icons configurable for each side -Many small GUI improvements -Compiled successfully with GCC 4.4.0 and MS Visual C++ 2008 -Added Russian translation -Updated translation files - - -FreeFileSync 1.19 [2009-06-01] ------------------------------- -New synchronization preview -Sync-direction can be adapted manually -New category type "conflict" -New check for unresolved conflicts -Improved overall GUI layout -New check for erroneous file modification dates -Optional pop up to notify on changed configuration -Files with invalid dates (e.g. year 30.000) do not result in a program abort anymore -Replaced column "full name" by "full path" to be combined with "filename" -Apply filtering WHILE comparing (if activated) and avoid traversing excluded directories -New filter paradigm: use relative instead of absolute names -New option "ignore DST +/- 1-hour" to correctly handle daylight saving changes -Sync preview statistics now on main dialog -Show only relevant synchronization options -File icon display configurable via grid column context menu -Updated translation files - - -FreeFileSync 1.18 [2009-05-10] ------------------------------- -Linux build officially released: all major problems solved! -New statistic: remaining time -New statistic: bytes per second -Automatically check for program updates every week -Finally got rid of scroll bar in middle grid for Linux build -Fixed issue with file icon display -Fixed overlapping grid cells -Alternate log file directory configurable via GUI -Added drag & drop support for batch job assembly -Simplyfied filter usage: - matches "\*" as well as "\" - - only distinct filter entries are considered -Platform dependent linebreaks in configuration *.xml files -"Significant difference check" runs at folder pair level -Sorting runs at folder pair level -New check for sufficient free disk space (considering recycle bin usage) -New optional grid column: directory -New sort by directory name -Reduced memory consumption by 10% -A lot of smaller improvements -Added Brazilian Portuguese translation -Updated translation files - - -FreeFileSync 1.17 [2009-04-05] ------------------------------- -Full support for Windows/Linux symbolic links: - - traverse, copy, delete symbolic links - - handle broken symbolic links - - new options in GlobalSettings.xml: TraverseDirectorySymlinks, CopyFileSymlinks -New menu option: "Check for new version" -Copy folder attributes and security settings when implicitly creating folders -Maximum file time difference now fully configurable -New history of last selected folders -Fixed "Year-2038-Problem" for time_t -Upgraded to wxWidgets 2.8.10 -Individual folder pairs can be selected for removal -Performance: Reduced CPU time by 9%, memory consumption by 36% -Support for cancellation when copying and comparing large files -Smooth progress indicators when copying and comparing large files -Support for Shift-PageUp/PageDown -Support for Home/End and Shift-Home/End -Alternative log file directory configurable via *.ffs_batch Xml -Show explorer file icons in grid (windows only) -Fixed compilation issues for Linux build -Fixed grid alignment issue in Linux build -Enhanced error messages for Linux build -Optimized traversing algorithm for Linux build -Fixed graphical misalignment with multiple folder pairs -Added Slovenian translation -Added Hungarian translation -Added Spanish translation -Updated translation files - - -FreeFileSync 1.16 [2009-03-13] ------------------------------- -Support for \\?\ path prefix for unrestricted path length (directory names > 255 characters) (windows only) -Copy files even if target folder does not exist -Fixed occasional error when switching languages -Added sys-tray icon for silent batch mode (pause, abort, about) -Support for numeric del-key -Avoid endless loops with Vista symbolic links (don't traverse into symbolic links - configurable) -New functionality for loading batch files (load button or drag & drop to main/batch window) -New options for batch file error handling: "pop up, ignore errors, exit with returncode < 0" -New option to reset all warning messages -Allow marking both sides of the main grid via CTRL + mouse-click -Allow manual deletion of files on both or one side only (respecting selections on both sides) -Special recycler option for manual deletion -New optional grid column: Full name -Fixed locale related issue when comparing. Big thanks to Persson Henric for providing support! -New check if more than 50% of files will be overwritten/deleted -Save memory by clearing old results before re-comparing -Usability improvements: - - name of config file in window title - - refresh view filters on configuration load - - default to ascending sort when changing column - - maximum length of config file history customizable through xml - - new "load configuration" button - - check/uncheck option for middle grid - - support for CTRL + A (select all) - - enhanced error messages (windows only) -Updated translation files - - -FreeFileSync 1.15 [2009-02-22] ------------------------------- -Fixed performance bottleneck in batch mode (non-silent) -Improved performance of comparison by another 10% -Configure column settings by right-click context menu -Remember column positions on main grid -Hide/Show individual columns -Added "sort by comparison result" -Sort file list by relative name after comparison (GUI mode only) -Removed Windows registry usage for portable version -Restored linebreaks in status texts for better readability -Revised German translation. Thanks to «Latino»! -Created custom button control to finally translate "compare" and "synchronize" -Allow manual setup of file manager integration (Windows and Linux) -Added Step-By-Step guide for manual compilation (Windows and Linux) -Added checkboxes to manually select/deselect rows -New option: Treat files with time deviation of less-equal 1 hour as equal (FAT/FAT32 drives only) -Added Polish translation -Added Portuguese translation -Added Italian translation -Updated translation files - - -FreeFileSync 1.14 [2009-02-01] ------------------------------- -Massive performance improvements: -- comprehensive analysis and optimization of comparison functionality -- new, fast directory traversing algorithm -- improved folder hierarchy compare algorithm -- lazy evaluation of formatted date strings -- new high-performance string class -=> reduction of CPU time by more than 90%! -Folder attributes are copied during synchronization -Sorting now case-insensitive (Windows-only) -Allow column positioning on main grid -Many small fixes -Added Chinese translation -Updated translation files - - -FreeFileSync 1.13 [2009-01-06] ------------------------------- -Automatically detect daylight saving time (DST) change for FAT/FAT32 drives -Added directory dependency check when synchronizing multiple folder pairs -New synchronization option: "update" -Reduced status screen flicker when comparing and synchronizing -Fixed bug when sorting by filename -Further GUI improvements -Updated translation files - - -FreeFileSync 1.12 [2008-12-23] ------------------------------- -Significantly improved speed of all sorting algorithms -Keep sorting sequence when adding or removing rows -'Sort by relative path' secondarily sorts by filename and respects folders -Allow adding multiple files/folders to exclude filter via context menu -Exclude full relative path instead of short filenames via context menu -Fixed possible memory leak when canceling compare -New option to manually adjust file modification times (To be used e.g. for FAT32 volumes on DST switch) -Handling of different types of configuration (GUI, batch, global) -Enhanced exception handling -Multiple GUI improvements -Added Dutch translation -Updated translation files - - -FreeFileSync 1.11 [2008-11-23] ------------------------------- -Support for multiple folder pairs -Optimized performance of multiple pairs to scan each folder just once -Enhanced batch file format -New context menu option to add files, file types or directories to exclude filter -Reworked file filter dialog -Updated translation files - - -FreeFileSync 1.10 [2008-11-09] ------------------------------- -Transformed configuration file format to XML -Exchanged batch files with shell links for full Unicode support (Windows-only) -Improved filter usage: ignore leading/trailing white-space, upper/lower-case (Windows-only) chars -Removed screen-flicker when clicking on compare: -Added elapsed time to compare status -Calculate height of middle grid independently of OS window layout -Multiple GUI improvements -Added Japanese translation -Updated translation files - - -FreeFileSync 1.9 [2008-10-26] ------------------------------ -Fixed wxWidgets multithreading issue that could cause synchronization to hang occasionally -Fixed issue with %1 parameter -Fixed issue with recycle bin usage in unicode mode -Added uninstaller -New installer option to associate *.ffs files with FreeFileSync -Transformed language files to Unicode (UTF-8) -Delete elements in configuration history list via DELETE key - - -FreeFileSync 1.8 [2008-10-19] ------------------------------ -Enhanced status bar information -Enhanced log file information -Enhanced progress information -Added Unicode support -Program now waits until work is completed when abort is triggered during synchronization -Added French translation -Updated German translation - - -FreeFileSync 1.7 [2008-10-12] ------------------------------ -Display only those view filter buttons that are actually needed -Compare by size and date: last write time may differ by up to 2 seconds (NTFS vs FAT32) -Fixed minor issue with trailing path separator when creating batch jobs -Fixed minor issue with window sizes not being remembered in some special situation -Further improved Unicode compliance -Updated German translation - - -FreeFileSync 1.6 [2008-10-05] ------------------------------ -Significantly improved speed of filtering files and view (< 10 ms for > 200.000 rows(!)) -Fixed minor grid mis-alignment under some special conditions -Enhanced status bar with centered texts -Flexible filter options depending on compare variant -Improved synchronization statistics -Fixed issue when trying to delete system folders -Usability improvements -Recycle Bin usage as command line parameter -New menu bar -Program language selectable from menu -UI-option to create sync jobs (batch files) for automated synchronization -Updated German translation - - -FreeFileSync 1.5 [2008-09-21] ------------------------------ -Improved speed of comparison by file content -Simplified and optimized calculation of accumulated file sizes -Added right-click context menu to main dialog -New installer for Windows -Improved usability of filtering and selecting rows -Solved possible issue with different file time precisions in multi-OS environments -Updated German translation - - -FreeFileSync 1.4 [2008-09-14] ------------------------------ -Implemented generic multithreading class to keep "compare by content" and "file synchronization" responsive -Added status bar when comparing files (with additional status information for "compare by content") -Some further speed optimizations -Added option to skip error messages and have them listed after synchronization -Restructured loading of configuration files -The result grid after synchronization now always consists of items that have not been synchronized (even if abort was pressed) -Added "remaining files" as sync-progress information -Updated German translation - - -FreeFileSync 1.3 [2008-09-07] ------------------------------ -Maintain and load different configurations by drag&drop, load-button or command line -New function to delete files (or move them to recycle bin) manually on the UI (without having to re-compare): - Deleting folders results in deletion of all dependent files, subfolders on UI grid (also no re-compare needed) - while catching error situations and allowing to resolve them -Improved manual filtering of rows: If folders are marked all dependent subfolders and files are marked as well -(keeping sort sequence when "hide filtered elements" is marked) -Comprehensive performance optimization of the two features above (manual filtering, deletion) for large grids (> 200,000 rows) -Improved usability: resizable borders, keyboard shortcuts, default buttons, dialog standard focus -Main window will remember restored position even if maximized -Updated sources to become more Linux and Unicode friendly -Updated German translation - - -FreeFileSync 1.2 [2008-08-31] ------------------------------ -New progress indicator and status information when synchronizing: - ->available for command line mode and UI mode: Status update and final error report -New progress information when comparing directories -Multithreading for copying of files to keep program responsive -Optimized all status dialogs and progress indicators for high performance: practically NO performance loss -Possibility to abort all performance critical operations (comparison, synchronization) at any time -New options in case of an error: "Continue, retry, abort" for UI and command line -New command line option "-skiperrors" to continue synchronization despite errors -Enhanced log file (-silent mode) to include all errors during compare and synchronization -Do not synchronize folders that have been deleted externally (but show an error message) -Manually filter out ranges from synchronization instead of just single rows -Some UI improvements -New option to use Recycle Bin when deleting or overwriting files -New synchronization sequence: first delete files, then copy files to avoid disc space shortages -Added different return values when used in command line mode to report success or failure -Updated German translation - - -FreeFileSync 1.1 [2008-08-24] ------------------------------ -Some further speed optimizations (sorting) -Written custom wxGrid class to avoid mapping of data to UI: huge performance increase (especially with formatted grids > 100000 items) -Filter files to include/exclude them from synchronization -Minor UI and text adaptions -Allow direct keyboard input for directory names -Added possibility to continue on error -Added indicator for sort direction -Simplified code concerning loading of UI resources -Prepared code to support unicode in some future version -Updated German translation - - -FreeFileSync 1.0 [2008-08-10] ------------------------------ -Initial release diff --git a/BUILD/FreeFileSync.chm b/BUILD/FreeFileSync.chm deleted file mode 100644 index 353494e5..00000000 Binary files a/BUILD/FreeFileSync.chm and /dev/null differ diff --git a/BUILD/Help/FreeFileSync.hhc b/BUILD/Help/FreeFileSync.hhc deleted file mode 100644 index 1902cb14..00000000 --- a/BUILD/Help/FreeFileSync.hhc +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - - - - - -
    -
  • - - - - -
      -
    • - - - -
    • - - - -
    • - - - -
    • - - - -
    • - - - -
    • - - - -
    • - - - -
    • - - - -
    • - - - -
    • - - - -
    • - - - -
    • - - - -
    • - - - -
    -
  • - - - - -
      -
    • - - - -
    • - - - -
    -
  • - - - - -
- diff --git a/BUILD/Help/FreeFileSync.hhp b/BUILD/Help/FreeFileSync.hhp deleted file mode 100644 index a9f19ebc..00000000 --- a/BUILD/Help/FreeFileSync.hhp +++ /dev/null @@ -1,31 +0,0 @@ -[OPTIONS] -Compatibility=1.1 or later -Compiled file=..\FreeFileSync.chm -Contents file=FreeFileSync.hhc -Default topic=html\FreeFileSync.html -Display compile progress=No -Full-text search=Yes -Language=0x409 Englisch (USA) -Title=FreeFileSync - Help - - -[FILES] -html\FreeFileSync.html -html\Versioning.html -html\Command Line.html -html\Expert Settings.html -html\Comparison Settings.html -html\Daylight Saving Time.html -html\Exclude Items.html -html\External Applications.html -html\Macros.html -html\Schedule a Batch Job.html -html\Synchronize with FTP.html -html\Variable Drive Letters.html -html\Volume Shadow Copy.html -html\RealtimeSync.html -html\Run as Service.html -html\Links.html - -[INFOTYPES] - diff --git a/BUILD/Help/html/Command line.html b/BUILD/Help/html/Command line.html deleted file mode 100644 index 56d6ce13..00000000 --- a/BUILD/Help/html/Command line.html +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - - - - - - - -

Command Line Usage

-

FreeFileSync -enables additional synchronization scenarios via a command line interface. To get a syntax overview, open the console, go to the directory where FreeFileSync is installed and type:

-

-

FreeFileSync - -h

-

-

-


-

-

1. -Run a FreeFileSync batch job

-

In order to start synchronization in batch mode, supply the path of a ffs_batch -configuration file as the first argument for FreeFileSync.exe:

-

-

FreeFileSync - "D:\Backup Projects.ffs_batch"

-

-

-

After -synchronization one of the following status codes is returned:

-

-

Return - Codes
0 - - Synchronization completed successfully
1 - Synchronization - completed with warnings
2 - Synchronization completed with - errors
3 - Synchronization was aborted
-

-

-

-

-You can evaluate these codes from a script (e.g. a cmd or bat file on Windows) -and check if synchronization completed successfully:

-

-

"C:\Program - Files\FreeFileSync\FreeFileSync.exe" "D:\Backup Projects.ffs_batch"
- if errorlevel 1 (
-   ::if return code is 1 or greater, something went wrong, add special treatment here
-   
echo Errors occurred during synchronization...
-   pause
- )
-

-

-

-

Instead -of displaying "An error occurred!" you can also send an -email notification (using a third party tool). -

-


-

-

-

- Attention
Make - sure your script is not blocked by a popup dialog. Consider the - following options when setting up a FreeFileSync batch job:
-

- -
  • - Disable - checkbox Show - progress dialog or have On completion - automatically close the results dialog after synchronization. -

    - -
  • - Set error handling to Stop or Ignore. -

    - -

    -

    -


    -

    -

    2. -Start a FreeFileSync GUI configuration

    -

    If you -pass a ffs_gui file, FreeFileSync will start in GUI mode and immediately start comparison (but only if all directories exist):

    -

    -

    FreeFileSync "D:\Manual Backup.ffs_gui"

    -

    -

    -


    -

    -

    3. -Customize an existing configuration

    -

    You can replace the directories of a given ffs_gui or ffs_batch configuration file by using the -leftdir -and -rightdir parameters:

    -

    -

    - FreeFileSync "D:\Manual Backup.ffs_gui" -leftdir C:\NewSource -rightdir D:\NewTarget

    -

    -

    -


    -

    -

    4. Merge multiple configurations

    -

    When -more than one configuration file is provided, FreeFileSync will merge -everything into a single configuration with multiple folder pairs and -start in GUI mode:

    -

    -

    - FreeFileSync "D:\Manual Backup.ffs_gui" "D:\Backup Projects.ffs_batch"

    -

    -

    -


    -

    - - \ No newline at end of file diff --git a/BUILD/Help/html/Comparison Settings.html b/BUILD/Help/html/Comparison Settings.html deleted file mode 100644 index 132cb15e..00000000 --- a/BUILD/Help/html/Comparison Settings.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - - - - - - - - -

    Comparison Settings

    -

    -

    I. Compare by File time and size

    -

    This -variant considers two files with the same name as equal when both -modification time and file size match. The following -categories are distinguished:

    -
      -
    1. file - exists on one side only

      -
        -
          -
        • left - only

          -
        • right - only

          -
        -
      -
    2. file - exists on both sides

      -
        -
      1. different - date

        -
          -
        • left - newer

          -
        • right - newer

          -
        -
      2. same - date

        -
          -
        • equal

          -
        • conflict - (same date, different size)

          -
        -
      -
    -


    -

    -

    II. Compare by File content

    -

    Two -files with the same name are marked as equal if and only if they have -the same content. This option is more useful for consistency checks -rather than backup operations since it is naturally slower. The file -modification time is not taken into account at all.

    -
      -
    1. file - exists on one side only

      -
        -
          -
        • left - only

          -
        • right - only

          -
        -
      -
    2. file - exists on both sides

      -
        -
          -
        • equal

          -
        • different - content

          -
        -
      -
    -


    -

    -

    Symbolic Link Handling

    -
    -

    FreeFileSync -offers three options to configure handling of symbolic links (also -called symlinks or soft links):

    -
      -
    1. Exclude: - Skip symbolic links while scanning - directories.
       

      -
    2. Direct: - Evaluate the symbolic link object - directly. Symbolic links will be shown as a separate entity on grid. - Links pointing to directories are not traversed and the link object - is copied directly during synchronization.
       

      -
    3. Follow: - Treat symbolic links like the object they are pointing to. Links - pointing to directories are traversed like ordinary directories and - the target of each link is copied during synchronization.

      -
    -


    -

    -

    -

    Note

    -
  • Under - Windows the symbolic link options apply to symbolic links, - volume mount points and NTFS junction points.

    -
  • - Copying symbolic links requires - FreeFileSync to be started with administrator rights.

    -

    -

    -


    -

    - - \ No newline at end of file diff --git a/BUILD/Help/html/Daylight Saving Time.html b/BUILD/Help/html/Daylight Saving Time.html deleted file mode 100644 index 8cc5601c..00000000 --- a/BUILD/Help/html/Daylight Saving Time.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - -

    Daylight Saving Time (Windows)

    -

    A -common problem synchronization software has to handle are +-1 hour -file time shifts after a Daylight Saving Time (DST) switch has -occurred. This can be observed for example when a FAT-formatted -volume is compared against an NTFS volume as is the case when synchronizing a local disk against a -USB memory stick. Files that previously appeared to be in sync are -now shown with an one hour modification time offset, although they -have not been modified by the user or the operating system.

    -

    The -reason for this behavior lies in the way NTFS and FAT drives -store file times: NTFS stores time in UTC format, while FAT uses -local time.

    -

    When -times of these two different formats are compared, one format -has to be converted into the other first. In either way Windows uses -the current DST status as well as the current time zone for -its calculations. Consequently the result of this comparison is -dependent from current system settings and in particular file times -that used to be the same can show up as different after a DST switch or when the time zone is changed.

    -


    -

    -

    For a -detailed discussion about this issue refer to: -http://www.codeproject.com/KB/datetime/dstbugs.aspx

    -


    -

    -

    Solution:

    -

    -FreeFileSync automatically handles this problem by adding the missing time information. Each file on a -FAT volume automatically gets additional meta data encoded in its -creation date that enables a correct file time calculation. This not -only solves all DST issues but also time shifts that occur due to -travel between different time zones.

    -


    -

    -

    -

    Note

    -
  • In - order for FreeFileSync to start handling DST and timezone - differences, an initial full synchronization is required. - Subsequent syncs will then never show a time difference again for unchanged files.

    -
  • - If a FAT volume is scanned the - first time by FreeFileSync this will take longer than usual since - additional meta data is written for each file.

    -

    -

    -


    -

    - - \ No newline at end of file diff --git a/BUILD/Help/html/Exclude Items.html b/BUILD/Help/html/Exclude Items.html deleted file mode 100644 index d14055fd..00000000 --- a/BUILD/Help/html/Exclude Items.html +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - - - - - - - - - - -

    Exclude Items

    -

    Files -and directories are only considered for synchronization if they pass -all filter rules. They have to match at least one entry in the -include list and none of the entries in the exclude list:

    - -

    -

  • Each - list item must be a file or directory path relative - to synchronization base directories.

    -
  • Multiple - items must be separated by ; or a new line.

    -
  • Wild - cards * and ? may be used: * means zero or more - characters while ? represents exactly one character.

    -

    -

    - -

    Example: -Exclude items for mirror-sync from C:\Source to D:\Target -

    - -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -

    Description

    -
    -

    Exclude

    -
    -

    Single file - C:\Source\file.txt

    -
    -

    \file.txt

    -
    -

    Single folder - C:\Source\SubFolder

    -
    -

    \SubFolder\

    -
    -

    All files (and folders) named thumbs.db - -

    -
    -

    *\thumbs.db

    -
    -

    All *.tmp files located in SubFolder only

    -
    -

    \SubFolder\*.tmp

    -
    -

    Files and folders containing temp somewhere in their path

    -
    -

    *temp*

    -
    -

    Multiple entries separated by semicolon

    -
    -

    *.tmp; *.doc; - *.bak

    -
    -

    Exclude all files and folders located in subdirectories of base directories

    -
    -

    \*\*

    -
    -

    Exclude only *.txt files located in subdirectories of base directories

    -
    -

    \*\*.txt

    -
    -


    -

    - -
    -

    -

    Note

    -
  • For - simple exclusions just right-click and exclude one item or a list - of items directly on main grid via context menu.

    -
  • - A filter phrase is compared against - both file and directory paths. If you want to consider directories - only, you can give a hint by appending a path separator (\).

    -

    -

    -


    -

    - - \ No newline at end of file diff --git a/BUILD/Help/html/Expert settings.html b/BUILD/Help/html/Expert settings.html deleted file mode 100644 index b0ffb459..00000000 --- a/BUILD/Help/html/Expert settings.html +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - - -

    Expert Settings

    -

    FreeFileSync -has a number of special purpose settings that can only be accessed -directly via the global configuration file GlobalSettings.xml. -To locate this file enter %appdata%\FreeFileSync in the Windows Explorer address bar or go to the FreeFileSync -installation folder if you are using the portable installation.

    - -

    -

    - - <?xml version="1.0" encoding="UTF-8"?>
    - <FreeFileSync XmlType="GLOBAL">
    -     <Shared>
    -         <FileTimeTolerance Seconds="2"/>
    -         <RunWithBackgroundPriority Enabled="false"/>
    -         <LockDirectoriesDuringSync Enabled="true"/>
    -         <VerifyCopiedFiles Enabled="false"/>
    -         <LastSyncsLogSizeMax Bytes="100000"/> -

    -

    -

    -


    -

    -

    FileTimeTolerance:
    By -default file modification times -are allowed to have a 2 second difference while still being -considered equal. This is required by FAT/FAT32 file systems which -store file times with a 2 second precision only.
    This setting -can also be used to simulate a "compare by file size", -ignoring last modification times: If tolerance is set to some high -value, e.g. 2000000000, then changed files will be detected as a -conflict (same date, different file size) and the -synchronization direction for conflicts can be set accordingly.

    -


    -

    -

    RunWithBackgroundPriority:
    While -synchronization is running, other applications which are accessing the same -data locations may experience a noticeable slowdown. Enable this -setting to lower FreeFileSync's resource consumption at the cost of a -significantly slower synchronization speed.

    -


    -

    -

    LockDirectoriesDuringSync:
    In -order to avoid race conditions of multiple FreeFileSync instances -writing to the same folder at the same time, accesses are serialized -by lock files (sync.ffs_lock). -This allows to operate FreeFileSync with an arbitrary number of users -in a network out of the box.

    -


    -

    -

    VerifyCopiedFiles:
    If -active, FreeFileSync will binary-compare source and target files after -copying and report verification errors. Note that this may double -file copy times and is no guarantee that data has not already been -corrupted prior to copying and corruption is not hidden by -deceptively reading valid data from various buffers in the -application and hardware stack.
    Does -the CopyFile function verify that the data reached its final -destination successfully?

    -


    -

    -

    LastSyncsLogSizeMax:
    The -progress logs of the most recent synchronizations (for both GUI and batch jobs) are collected automatically in the file LastSyncs.log. -The maximum size of this log file can be set here.

    - - \ No newline at end of file diff --git a/BUILD/Help/html/External Applications.html b/BUILD/Help/html/External Applications.html deleted file mode 100644 index e4ccdbaf..00000000 --- a/BUILD/Help/html/External Applications.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - -

    External Applications

    -

    -When you double-click on one of the rows on the main dialog, FreeFileSync opens the operating system's file browser by default. On Windows it calls explorer /select, "%item_path%", on Linux xdg-open "%item_folder%" and on OS X open -R "%item_path%". - -

    -

    You can customize this behavior and integrate other external applications into FreeFileSync: -Navigate to Menu -→ Tools → Global settings: Customize context menu -and add or replace a command. The first entry is executed when double-clicking a row on main grid or pressing ENTER while all other entries can be accessed via the -context menu shown after a right mouse click. The following macros -can be used:

    -

    -

    - - %item_path%     - full file or folder name
    - %item_folder%   - folder part only
    - %item2_path%    - Counterpart of %item_path% on the opposite grid
    - %item2_folder%  - Counterpart of %item_folder% on the opposite grid -

    -

    -

    -


    -

    -

    Examples:

    -
      -
    • Start - visual difference tool:
      "C:\Program - Files\WinMerge\WinMergeU.exe" "%item_path%" - "%item2_path%"
       

      -
    • Show file in Windows Explorer:
      explorer - /select, "%item_path%"
       

      -
    • Open file in associated application:
      cmd - /c start "" "%item_path%" or simply "%item_path%"
       

      -
    • Open console dialog:
      cmd /k cd /D "%item_folder%"
       

      -
    -

    -

    - Note
    You need to protect macros with quotation marks if they can resolve to a file path with space characters.

    -

    -

    - - \ No newline at end of file diff --git a/BUILD/Help/html/FreeFileSync.html b/BUILD/Help/html/FreeFileSync.html deleted file mode 100644 index 2812f282..00000000 --- a/BUILD/Help/html/FreeFileSync.html +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - -


    Folder -Comparison and Synchronization

    -

    Usage:

    -
      -
    1. Choose - left and right directories.

      -

       

      -
    2. Compare - them.

      -

       

      -
    3. Select - synchronization settings.

      -

       

      -
    4. Press - Synchronize - to begin synchronization.

      -
    -

    -
    -

    -


    -

    -

    Main -Dialog Overview

    -
      -

      -
    -


    -

    -
      -
    1. Start - comparison

      -
    2. Change - comparison settings

      -
    3. Change - synchronization settings

      -
    4. Start - synchronization

      -
    5. Tree - overview panel

      -
    6. Add - additional folder pairs

      -
    7. Select - left and right folders

      -
    8. Synchronization - preview -

      -
    9. Save/load - configuration

      -
    10. Include/exclude - specific files

      -
    11. Select - categories to show on grid

      -
    12. Synchronization - statistics

      -
    - - \ No newline at end of file diff --git a/BUILD/Help/html/Links.html b/BUILD/Help/html/Links.html deleted file mode 100644 index 38c46bf3..00000000 --- a/BUILD/Help/html/Links.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - -

    FreeFileSync -Links

    -

    -

    - Homepage:
    http://freefilesync.sourceforge.net

    -

    - Project on SourceForge: - feedback, suggestions and - bug-reports
    http://sourceforge.net/projects/freefilesync

    -

    - If you like FreeFileSync: consider - supporting the project by a donation
    Donate - via PayPal

    -

    -

    - - \ No newline at end of file diff --git a/BUILD/Help/html/Macros.html b/BUILD/Help/html/Macros.html deleted file mode 100644 index d9d34a65..00000000 --- a/BUILD/Help/html/Macros.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - - - - - - - - - - -

    Macros

    -

    All -directory names may contain macros that are expanded during -synchronization. Begin and end of each macro is marked by a % -character. Besides special macros -handling time and date, the operating -system's environment variables can -also be used.

    -


    -

    -

    Internal -macros:

    -

    -

    %date%       - e. g. 2012-12-22        format - [YYYY-MM-DD]
    %time%       - e. g. 123044            format - [hhmmss]
    %timestamp%  - e. g. 2012-12-22 - 123044 format [YYYY-MM-DD hhmmss]

    %year%       - e. g. 2012
    %month%      - e. g. 12
    %day%        - e. g. 22

    %hour%       - e. g. 12
    %min%        - e. g. 30
    %sec%        - e. g. 44

    %weekday%    - e. g. Monday day - of the week
    %week%       - e. g. 28     calendar - week

    -

    -

    -

    Environment -variables: (Windows)

    -

    -

    %AllUsersProfile%   e. - g. C:\ProgramData
    %AppData%           
    e. - g. C:\Users\<username>\AppData\Roaming
    %ComputerName%      
    e. - g. Zenju-PC
    %LocalAppData%      
    e. - g. C:\Users\<username>\AppData\Local
    %ProgramData%       
    e. - g. C:\ProgramData
    %ProgramFiles%      
    e. - g. C:\Program - Files
    %ProgramFiles(x86)% 
    e. g. C:\Program - Files (x86)
    %Public%            
    e. - g. C:\Users\Public
    %Temp%              
    e. - g. C:\Windows\Temp
    %UserName%          
    e. - g. Zenju
    %UserProfile%       
    e. - g. C:\Users\<username>
    %WinDir%            
    e. - g. C:\Windows

    -

    -

    -


    -

    -

    Special -folder locations (Windows)

    -

    -

    %csidl_Desktop%     e. - g. C:\Users\<username>\Desktop
    %csidl_Downloads%   
    e. - g. C:\Users\<username>\Downloads
    %csidl_Favorites%   
    e. - g. C:\Users\<username>\Favorites
    %csidl_MyDocuments% 
    e. - g. C:\Users\<username>\Documents
    %csidl_MyMusic%     
    e. - g. C:\Users\<username>\Music
    %csidl_MyPictures%  
    e. - g. C:\Users\<username>\Pictures
    %csidl_MyVideos%    
    e. - g. C:\Users\<username>\Videos
    %csidl_Nethood%     
    e. - g. C:\Users\<username>\AppData\Roaming\Microsoft\Windows\Network - Shortcuts
    %csidl_Programs%    
    e. g. - C:\Users\<username>\AppData\Roaming\Microsoft\Windows\Start - Menu\Programs
    %csidl_Quicklaunch% 
    e. g. - C:\Users\<username>\AppData\Roaming\Microsoft\Internet - Explorer\Quick Launch
    %csidl_Resources%   
    e. - g. C:\Windows\Resources
    %csidl_StartMenu%   
    e. - g. C:\Users\<username>\AppData\Roaming\Microsoft\Windows\Start - Menu
    %csidl_Startup%     
    e. g. - C:\Users\<username>\AppData\Roaming\Microsoft\Windows\Start - Menu\Programs\StartUp
    %csidl_Templates%   
    e. - g. C:\Users\<username>\AppData\Roaming\Microsoft\Windows\Templates

    -
    Note: Most -macros listed here also have a variant for public folders. -E.g. csidl_MyMusic → -csidl_PublicMusic -

    -


    -

    - -

    Hint:

    -

    You can add a great amount of flexibility to a ffs_batch configuration file - by creating new temporary environment variables in a bat or cmd file that are evaluated by FreeFileSync at runtime.

    -


    -

    -

    Example: -The FreeFileSync batch file C:\SyncJob.ffs_batch contains macro %MyVar% instead of an absolute target folder and is invoked by a cmd file: -

    -

    -

    set - MyVar=C:\Target
    "C:\Program files\FreeFileSync\FreeFileSync.exe" C:\SyncJob.ffs_batch
    - ::%MyVar% is resolved as C:\Target during synchronization
    -

    -

    -

    -


    -

    -

    -

    Note
    - Temporary environment variables created with the set command are only valid if the synchronization is started by calling the - FreeFileSync executable directly. Using start /wait creates a new program context without these temporal variables.

    -
    -

    - - \ No newline at end of file diff --git a/BUILD/Help/html/RealtimeSync.html b/BUILD/Help/html/RealtimeSync.html deleted file mode 100644 index e9eae9cf..00000000 --- a/BUILD/Help/html/RealtimeSync.html +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - - - - - - - - - - -


    Automated Synchronization

    -

    -The primary purpose of RealtimeSync is to execute a command line each time a directory becomes -available (e. g. insert of a USB-stick) or when it detects changes in one of the monitored directories. Usually this command line will trigger -a FreeFileSync batch job.

    -


    -

    -

    Example: -Real time synchronization - in -combination with FreeFileSync

    -

    Start RealtimeSync.exe located in FreeFileSync's installation directory and -enter all folders you want to monitor. Instead of doing this manually you can import a ffs_batch -file via Menu → Program → Open. This not only extracts all directories relevant for synchronization -but also sets up the command line to execute the ffs_batch file each time changes are detected. -Now press Start to begin monitoring.

    -
      -

      -
    -
    - - -

    -

    - Note

    -
  • The - command should not - block progress. If you call a FreeFileSync batch job, make sure it does not show any popup - windows. See notes in Command Line Usage.
     

    - -
  • - RealtimeSync will skip showing the main dialog and begin monitoring immediately if you pass a ffs_real configuration file or a FreeFileSync ffs_batch file - as first command line argument to RealtimeSync.exe. This helps you integrate RealtimeSync into your operating system's auto start:
    -       "C:\Program Files\FreeFileSync\RealtimeSync.exe" "D:\Backup Projects.ffs_real"
    -       "C:\Program Files\FreeFileSync\RealtimeSync.exe" "D:\Backup Projects.ffs_batch"
      -
    -

    - -
  • - RealtimeSync is not tied to starting FreeFileSync. It can also be used in other scenarios, like sending an email whenever a certain directory is modified.

    -
    -

    - -
    -
    -

    Example: -Automatic synchronization when a USB -stick is inserted

    -

    Save a -ffs_batch configuration in the USB stick's root directory, e.g. H:\, to let RealtimeSync call it when the stick is mounted. Configure RealtimeSync as follows:

    -
      -

      -

      -
    - -

    Whenever directory H:\Data becomes available, RealtimeSync executes the command line which starts the batch job located -on the stick. RealtimeSync will also trigger each time files are modified in H:\Data. -

    -
    -

    -

    - - Note
    - The full path of the last changed file and the action that triggered the - change notification (create, update or delete) are written - to the environment variables %change_path% and %change_action%. -
    -

    -
    -

    -
    -
    -

    Example: -Log names of changed files and directories (Windows)

    - -

    -

    - - Show which file or directory has triggered a change. Enter command - line:
    - - -     cmd /c echo %change_action% - "%change_path%" & pause

    -
    - - Write a list of all changes to a log file:
    - - -     cmd /c echo %change_action% "%change_path%" >> C:\log.txt - -

    -

    -

    - -

    -

    - - Note
    - When RealtimeSync executes a Windows batch file (bat or cmd) a black console window is shown. You can hide it using the Visual Basic script - HideConsole.vbs located in FreeFileSync's installation directory: -


    - - wscript "C:\Program files\FreeFileSync\HideConsole.vbs" C:\MyBatchFile.cmd -

    -

    -

    -


    -

    -

    Limitations:

    -
      -
    • If - multiple changes happen at the same time, only the name of the first - file is written to variable %changed_file%.

      - -
    • While - RealtimeSync is executing the command line, monitoring is inactive - and changes occurring during this time are lost. -

      -
    -


    -

    - - \ No newline at end of file diff --git a/BUILD/Help/html/Run as Service.html b/BUILD/Help/html/Run as Service.html deleted file mode 100644 index 39ee6ba6..00000000 --- a/BUILD/Help/html/Run as Service.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - -

    Run as Service (Windows)

    -

    - RealtimeSync is designed to run as a background process which does not need further - attention. Depending on your requirements there are a number of ways you can start it automatically. - Generally the goal is to execute a command line like:
    -
    - <FreeFileSync installation folder>\RealtimeSync.exe <path to *.ffs_real or *.ffs_batch file> -

    - -
    -

    Example:

    -
      -

      -

      - "C:\Program Files\FreeFileSync\RealtimeSync.exe" "D:\Backup Projects.ffs_real"

      -
      -

      -
    -

    -
      -
    1. - RealtimeSync should be monitoring only while a specific user is logged in: Create - a new shortcut, enter the command line from above as target and place it into the user's autostart folder.

      - -

      -

      -

       

      - -
    2. RealtimeSync should be monitoring while Windows is running irrespective of - currently logged in users: Create a new task in your operating systems's task scheduler and have it execute the command line above - when the system starts. See Schedule a Batch Job for an example how to add a task. Then change - the user which runs the task to SYSTEM - a special user account always running in the background.

      -

      -
    -


    -

    - - \ No newline at end of file diff --git a/BUILD/Help/html/Schedule a Batch Job.html b/BUILD/Help/html/Schedule a Batch Job.html deleted file mode 100644 index dbd3fc2a..00000000 --- a/BUILD/Help/html/Schedule a Batch Job.html +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - -

    Schedule a Batch Job

    -
      -
    1. Create - a new batch job via FreeFileSync's main dialog: Menu - → Program → Save as batch job...
       

      -
    2. If - the batch job shall run without user interaction or as part of an - unattended batch script, make sure that no popup dialog stops the - progress:
      Disable checkbox Show - progress dialog to avoid blocking while showing - the result after synchronization. Alternatively you can leave this option checked and select the On completion action Close progress dialog - located in synchronization settings. FreeFileSync will then show a progress dialog but close it automatically when it is finished.
      -
      - Note: Even if the - progress dialog is not shown at the beginning, you can make it - visible during synchronization by double-clicking the - FreeFileSync notification area icon.
      -
      - -

      -
      -

      - -
    3. In - order to prevent error or warning popup messages from stopping - progress, set Handle errors to either Ignore - or Stop. -

      -

      - -
    4. Setup - your operating system's scheduler

      -
    -
      -
        -
      1. Windows - 7 Task Scheduler:

        -
          -
        • -

          - Go to Start and run taskschd.msc. -

          - -
        • -

          - Create a new basic task and follow the wizard. -

          - -
        • -

          - Make Program/script point to the location of FreeFileSync.exe - and insert the ffs_batch file into Add arguments. -

          - -
        • Use quotation marks to protect spaces in path names, e.g. "D:\Backup Projects.ffs_batch"
          -
          -
          -

          -
        -
      -

      -

      Note
      Beginning - with Windows Vista the Program/script always needs point to an executable file like FreeFileSync.exe even - if ffs_batch file association is set. If a ffs_batch file is entered instead the task will return with - error code 0xC1, "%1 is not a valid Win32 application".

      -
        -

      -
    -
      -
        -
      1. Windows XP Scheduled Tasks:

        -
          -
        • Go - to Start → Control Panel → Scheduled Tasks and select Add Scheduled Task. -

          - -
        • Follow the wizard and select FreeFileSync.exe as program to run.

          - -
        • Fill the input field Run: - <FreeFileSync installation folder>\FreeFileSync.exe <job name>.ffs_batch
          -
          -
           

          -
        - -
      2. Ubuntu Linux Gnome-schedule:

        -
          -
        • - Install Gnome-schedule, if necessary: sudo apt-get install gnome-schedule

          - -
        • Go to System → Preferences → Scheduled tasks

          - -
        • Enter the command: - <FreeFileSync installation folder>/FreeFileSync <job name>.ffs_batch
          -
          -

          -
        -
      -
    -


    -

    - - \ No newline at end of file diff --git a/BUILD/Help/html/Synchronize with FTP.html b/BUILD/Help/html/Synchronize with FTP.html deleted file mode 100644 index cbca698d..00000000 --- a/BUILD/Help/html/Synchronize with FTP.html +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - - - - - -

    Synchronize -with FTP/WebDAV (Windows)

    -

    FreeFileSync -does not support accessing FTP volumes directly. But this -functionality can be integrated by mapping the FTP web space to a -drive letter:

    -


    -

    -

    Example: -Use the free utility NetDrive -(http://www.netdrive.net)

    -
      -
    • Add - a New Site and - specify site name, site URL, drive letter, account and password.

      -
    • Use - the newly created drive as if it were a normal hard disk.

      -
    -

    -

    - Note
    Most FTP drives set a - file's time stamp to the current time when synchronizing ignoring - the source file's time and date. As a workaround you can do a - Compare by File Size.

    -

    -

    -
    -

    Synchronize with -SFTP (Linux)

    -


    -

    -

    An SFTP -share can be easily mapped onto a local folder for use with -FreeFileSync:

    -

    -

  • - Install: -
    sudo apt-get install sshfs
    -  

    - -
  • - Mount SFTP share: -
    sshfs ssh-account@ssh-server:<path> mountpoint
    -  

    - -
  • Unmount: -
    fusermount -u mountpoint

    -

    -

    - - \ No newline at end of file diff --git a/BUILD/Help/html/Variable Drive Letters.html b/BUILD/Help/html/Variable Drive Letters.html deleted file mode 100644 index a9368041..00000000 --- a/BUILD/Help/html/Variable Drive Letters.html +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - -

    Variable Drive Letters

    -

    USB -memory sticks or external hard disks often get different -drive letters assigned when plugged into distinct computers. FreeFileSync -offers two solutions to handle this problem:

    -


    -

    -

    Option -1: Specify a folder path by using the volume name:

    -

    -

    - Use [ZENJU-USB]\folder instead of G:\folder where ZENJU-USB - is the volume name of the USB stick which is currently mounted in drive G:\. -

    -

    -

    -

    -

    - Note
    It - is not required to look up and enter the volume name manually. Just - select the corresponding entry in the drop down menu.
    - -

    -

    -

    -


    -

    -

    Option -2: Use a relative directory name:

    - - -

    -

  • - Use \folder instead of G:\folder -


    - -
  • - Save and copy synchronization settings to the USB stick: G:\Backup.ffs_gui -


    - -
  • - Start FreeFileSync by double-clicking on G:\Backup.ffs_gui
    -
    - The working directory is then automatically set to G:\ by the operating system so that the - relative path \folder will be resolved as G:\folder during synchronization. -
    -

    -

    -

    - - - \ No newline at end of file diff --git a/BUILD/Help/html/Versioning.html b/BUILD/Help/html/Versioning.html deleted file mode 100644 index 9b81b731..00000000 --- a/BUILD/Help/html/Versioning.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - - -

    File Versioning

    -

    When -you need to preserve files that have been deleted or overwritten it's -often sufficient to select Recycle bin in synchronization -settings. However this is only available for local drives and offers -little control on how to store and how long to keep the files. -FreeFileSync therefore has an additional option, Versioning.

    -


    -

    -

    1. -Keep all versions of old files

    -

    In -synchronization settings set deletion handling to Versioning -and naming convention to Time stamp. FreeFileSync will move -deleted files into the provided folder and add a time stamp to each -file name. The structure of the synchronized folders is preserved so -that old versions of a file can be conveniently accessed via a file -browser.

    -

    Example: -A file Folder\File.txt was updated three times and old versions were moved to folder C:\Revisions - -

    -

    -

    C:\Revisions\Folder\File.txt - 2012-12-12 - 111111.txt
    C:\Revisions\Folder\File.txt -
    2012-12-12 - 122222.txt
    C:\Revisions\Folder\File.txt -
    2012-12-12 133333.txt

    -

    -

    -


    -

    -

    2. -Save only the most recent version

    -

    Set -deletion handling to Versioning and naming convention to -Replace. Deleted files will be moved to the specified folder -without any decoration and will replace already existing older -versions.

    -


    -

    -

    3. -Save versions at certain intervals

    -

    With -naming convention Replace -it is possible to refine the -granularity of versions to keep by adding macros -to the versioning folder path. For example you can save deleted files -on a per sync session basis by adding the %timestamp% -macro:

    - -

    Example: -Using the dynamically generated folder name C:\Revisions\%timestamp% -

    - -

    -

    C:\Revisions\2012-12-12 - 111111\Folder\File.txt
    C:\Revisions\
    2012-12-12 - 122222\Folder\File.txt
    C:\Revisions\
    2012-12-12 - 133333\Folder\File.txt

    -

    -

    -

    This -allows for a simple manual undo by moving the deleted files from the -last synchronization session back to their original folders. Other -macros like %date% -or %weekday% -can be used to reduce the granularity -to days and weeks.

    - - \ No newline at end of file diff --git a/BUILD/Help/html/Volume Shadow Copy.html b/BUILD/Help/html/Volume Shadow Copy.html deleted file mode 100644 index c5e32e80..00000000 --- a/BUILD/Help/html/Volume Shadow Copy.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - -

    Volume -Shadow Copy Service (Windows only)

    -

    FreeFileSync -supports copying locked or shared files by creating a Volume Shadow -Copy of the source drive. This feature can be configured via Menu -→ Tools → Global settings: Copy locked files.

    -

    -

    Note

    -
  • The - volume snapshot created by the Volume Shadow Copy Service is used - when copying locked files only.

    -
  • - Accessing the Volume Shadow Copy - Service requires FreeFileSync to be started with administrator - rights.

    -

    -

    -

    Troubleshooting

    -

    If you -experience problems using the Volume Shadow Copy Service a renewal of -registration might help. Create and execute a cmd batch file and insert the following lines or enter directly via command line:

    -

    -

    - - cd /d %windir%\system32
    - Net stop vss
    - Net stop swprv
    - regsvr32 ole32.dll
    - regsvr32 oleaut32.dll
    - regsvr32 vss_ps.dll
    - Vssvc /register
    - regsvr32 /i swprv.dll
    - regsvr32 /i eventcls.dll
    - regsvr32 es.dll
    - regsvr32 stdprov.dll
    - regsvr32 vssui.dll
    - regsvr32 msxml.dll
    - regsvr32 msxml3.dll
    - regsvr32 msxml4.dll

    -

    -

    -

    Reference: -http://support.microsoft.com/kb/940032

    - - \ No newline at end of file diff --git a/BUILD/Help/img/CmpSettings.png b/BUILD/Help/img/CmpSettings.png deleted file mode 100644 index 16c30698..00000000 Binary files a/BUILD/Help/img/CmpSettings.png and /dev/null differ diff --git a/BUILD/Help/img/CompareButton.png b/BUILD/Help/img/CompareButton.png deleted file mode 100644 index 3fe37be1..00000000 Binary files a/BUILD/Help/img/CompareButton.png and /dev/null differ diff --git a/BUILD/Help/img/FFS_logo.png b/BUILD/Help/img/FFS_logo.png deleted file mode 100644 index 6a1a53e5..00000000 Binary files a/BUILD/Help/img/FFS_logo.png and /dev/null differ diff --git a/BUILD/Help/img/MainDialog.png b/BUILD/Help/img/MainDialog.png deleted file mode 100644 index 9f2f2566..00000000 Binary files a/BUILD/Help/img/MainDialog.png and /dev/null differ diff --git a/BUILD/Help/img/RTS_logo.png b/BUILD/Help/img/RTS_logo.png deleted file mode 100644 index 344da623..00000000 Binary files a/BUILD/Help/img/RTS_logo.png and /dev/null differ diff --git a/BUILD/Help/img/RealtimeSync.png b/BUILD/Help/img/RealtimeSync.png deleted file mode 100644 index 971baefd..00000000 Binary files a/BUILD/Help/img/RealtimeSync.png and /dev/null differ diff --git a/BUILD/Help/img/ScheduleBatch.png b/BUILD/Help/img/ScheduleBatch.png deleted file mode 100644 index 214a2870..00000000 Binary files a/BUILD/Help/img/ScheduleBatch.png and /dev/null differ diff --git a/BUILD/Help/img/SetupBatch.png b/BUILD/Help/img/SetupBatch.png deleted file mode 100644 index e1789917..00000000 Binary files a/BUILD/Help/img/SetupBatch.png and /dev/null differ diff --git a/BUILD/Help/img/SourceTarget.png b/BUILD/Help/img/SourceTarget.png deleted file mode 100644 index 5d8df5a4..00000000 Binary files a/BUILD/Help/img/SourceTarget.png and /dev/null differ diff --git a/BUILD/Help/img/SyncConfigButton.png b/BUILD/Help/img/SyncConfigButton.png deleted file mode 100644 index 53e4ecb1..00000000 Binary files a/BUILD/Help/img/SyncConfigButton.png and /dev/null differ diff --git a/BUILD/Help/img/SynchronizeButton.png b/BUILD/Help/img/SynchronizeButton.png deleted file mode 100644 index 0ab74cb8..00000000 Binary files a/BUILD/Help/img/SynchronizeButton.png and /dev/null differ diff --git a/BUILD/Help/img/VolumeName.png b/BUILD/Help/img/VolumeName.png deleted file mode 100644 index da39c9a5..00000000 Binary files a/BUILD/Help/img/VolumeName.png and /dev/null differ diff --git a/BUILD/Help/img/WatchUsbInsert.png b/BUILD/Help/img/WatchUsbInsert.png deleted file mode 100644 index 0e90278a..00000000 Binary files a/BUILD/Help/img/WatchUsbInsert.png and /dev/null differ diff --git a/BUILD/Help/img/create_shortcut.png b/BUILD/Help/img/create_shortcut.png deleted file mode 100644 index 7b0a5e2c..00000000 Binary files a/BUILD/Help/img/create_shortcut.png and /dev/null differ diff --git a/BUILD/Help/img/schedule_realtimesync.png b/BUILD/Help/img/schedule_realtimesync.png deleted file mode 100644 index ce52fd28..00000000 Binary files a/BUILD/Help/img/schedule_realtimesync.png and /dev/null differ diff --git a/BUILD/Help/img/shortcut_properties.png b/BUILD/Help/img/shortcut_properties.png deleted file mode 100644 index 77e9a773..00000000 Binary files a/BUILD/Help/img/shortcut_properties.png and /dev/null differ diff --git a/BUILD/Help/img/ubuntuScheduler.png b/BUILD/Help/img/ubuntuScheduler.png deleted file mode 100644 index 82bf329f..00000000 Binary files a/BUILD/Help/img/ubuntuScheduler.png and /dev/null differ diff --git a/BUILD/Help/img/win7scheduler.png b/BUILD/Help/img/win7scheduler.png deleted file mode 100644 index eabf331f..00000000 Binary files a/BUILD/Help/img/win7scheduler.png and /dev/null differ diff --git a/BUILD/HideConsole.vbs b/BUILD/HideConsole.vbs deleted file mode 100644 index 6a178410..00000000 --- a/BUILD/HideConsole.vbs +++ /dev/null @@ -1,17 +0,0 @@ -set argIn = WScript.Arguments -num = argIn.Count - -if num = 0 then - WScript.Echo "Call a Windows batch file (*.cmd, *.bat) without showing the console window" & VbCrLf & VbCrLf &_ - "Command line:" & VbCrLf & "WScript HideConsole.vbs MyBatchfile.cmd " - WScript.Quit 1 -end if - -argOut = "" -for i = 0 to num - 1 - argOut = argOut & """" & argIn.Item(i) & """ " -next - -set WshShell = WScript.CreateObject("WScript.Shell") - -WshShell.Run argOut, 0, True \ No newline at end of file diff --git a/BUILD/Languages/arabic.lng b/BUILD/Languages/arabic.lng deleted file mode 100644 index 9a59faea..00000000 --- a/BUILD/Languages/arabic.lng +++ /dev/null @@ -1,1557 +0,0 @@ -
    - العربية - MEinea - ar - flag_arabic.png - 6 - n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5 -
    - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -&Check - - -Retrying operation... - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Both sides have changed since last synchronization. -كلا الجانبين قد تغير منذ المزامنة الأخيرة. - -Cannot determine sync-direction: -لا يمكن تحديد اتجاه المزامنة: - -No change since last synchronization. -لم يطرأ أي تغيير منذ المزامنة الأخيرة. - -The database entry is not in sync considering current settings. -مدخلات قواعد البيانات غير متزامنة حسب إعدادات المزامنة الحالية. - -Setting default synchronization directions: Old files will be overwritten with newer files. -تحديد الاتجاهات الافتراضية للمزامنة: ستتم الكتابة فوق الملفات القديمة بالملفات الأحدث. - -Checking recycle bin availability for folder %x... -التحقق من توافر سلة المحذوفات من أجل المجلد %x... - -Moving file %x to the recycle bin -نقل الملف %x إلى سلة المهملات - -Moving folder %x to the recycle bin -نقل المجلد %x إلى سلة المهملات - -Moving symbolic link %x to the recycle bin -نقل الارتباط الرمزي %x إلى سلة المهملات - -Deleting file %x -حذف الملف %x - -Deleting folder %x -حذف المجلد %x - -Deleting symbolic link %x -حذف الارتباط الرمزي %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -سلة المهملات غير متوفرة للمجلدات التالية. سيتم حذف الملفات بشكل نهائي بدلاً عن ذلك: - -An exception occurred -حدث استثناء - -A directory path is expected after %x. -مسار متوقع بعد %x. - -Syntax error -خطأ في البنية - -Cannot open file %x. -تعذر فتح الملف %x. - -File %x does not contain a valid configuration. -لا يحتوي الملف %x تكويناً صحيحاً. - -Unequal number of left and right directories specified. -لم يتم تحديد عدد متساوي من المسارات على الطرفين - -The config file must not contain settings at directory pair level when directories are set via command line. -يجب أن يحتوي ملف الخيارات الخيارات على مستوى أزواج المسارات عند تحديد المسارات بواسطة سطر الأوامر - -Directories cannot be set for more than one configuration file. -لا يمكن اختيار المسارات لأكثر من ملف خيارات واحد - -Command line -سطر الأوامر - -Syntax: -بنية: - -config files -ملفات الخيارات - -directory -مسار - -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -أي عدد من ملفات خيارات FreeFileSyn أو\و _gui and/or .ffs_batch - -Any number of alternative directories for at most one config file. -أي عدد من المسارات البديلة لملف خيارات وحيد على الأكثر. - -A folder input field is empty. -حقل إدخال خاص بمجلد فارغ. - -The corresponding folder will be considered as empty. -سيتم اعتبار المجلد الموافق كمجلد فارغ - -Cannot find the following folders: -تعذر العثور على المجلدات التالية: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -بإمكانك إهمال هذا الخطأ لاعتبار كل مجلد على أنه مجلد فارغ. سيتم إنشاء المجلدات تلقائياً خلال المزامنة - -The following folders have dependent paths. Be careful when setting up synchronization rules: -للمجلدات التالية مسارات مستقلة عن بعضها. انتبه عند ضبط إعدادات المزامنة: - -File %x has an invalid date. -يحتوي الملف %x تاريخ غير صالح. - -Date: -التاريخ: - -Files %x have the same date but a different size. -الملفات %x لها نفس التاريخ ولكن حجم مختلف. - -Size: -الحجم: - -Items differ in attributes only -العناصر مختلفة في السمات فقط - -Resolving symbolic link %x -جاري حل المسار الرمزي %x - -Comparing content of files %x -مقارنة محتويات الملفات %x - -Generating file list... -إنشاء قائمة الملفات... - -Starting comparison -بدأ عملية المقارنة - -Calculating sync directions... -جاري حساب اتجاهات المزامنة... - -Out of memory. -نفدت الذاكرة. - -Item exists on left side only -العنصر موجود على الجانب الأيمن فقط - -Item exists on right side only -العنصر موجود في الجانب الأيسر فقط - -Left side is newer -الجانب الأيمن أحدث - -Right side is newer -الجانب الأيسر أحدث - -Items have different content -العناصر مختلفة بالمحتوى - -Both sides are equal -كلا الجانبين متماثلان - -Conflict/item cannot be categorized -الاختلاف\العنصر لا يمكن تصنيفه - -Copy new item to left -نسخ عنصر جديد إلى اليمين - -Copy new item to right -نسخ عنصر جديد إلى اليسار - -Delete left item -حذف العنصر الأيمن - -Delete right item -حذف العنصر الأيسر - -Move file on left -نقل ملف على اليمين - -Move file on right -نقل ملف على اليسار - -Overwrite left item -الكتابة فوق العنصر الأيمن - -Overwrite right item -الكتابة فوق العنصر الأيسر - -Do nothing -لا تفعل شيئا - -Update attributes on left -تحديث السمات على اليمين - -Update attributes on right -تحديث السمات على اليسار - -Database file %x is incompatible. -ملف قاعدة البيانات %x غير متوافق. - -Initial synchronization: -المزامنة الأولية: - -Database file %x does not yet exist. -ملف قاعدة البيانات %x غير موجود حتى الآن. - -Database file is corrupt: -ملف قاعدة البيانات معطوب: - -Cannot write file %x. -لا يمكن كتابة الملف %x. - -Cannot read file %x. -لا يمكن قراءة الملف %x. - -Database files do not share a common session. -ملفات قواعد البيانات لا تشترك في جلسة عمل مشتركة. - -Searching for folder %x... -البحث عن المجلد %x... - -Cannot read file attributes of %x. -لا يمكن قراءة سمات الملف %x. - -Cannot get process information. -لا يمكن الحصول على معلومات العملية. - -Waiting while directory is locked (%x)... -في انتظار تأمين قفل للمسار (%x)... - - -1 sec -%x sec - - -0 ثانية -1 ثانية واحدة -2 ثانيتين -%x ثواني -%x ثانية -%x ثانية - - -Creating file %x -إنشاء الملف %x - -Items processed: -معالجة العناصر: - -Items remaining: -العناصر المتبقية: - -Total time: -مجموع الوقت: - - -1 byte -%x bytes - - -0 byte -1 byte -2 bytes -%x bytes -%x bytes -%x bytes - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -حدث خطأ أثناء تحليل الملف %x، الصف %y، و العمود %z. - -Cannot set directory lock for %x. -تعذر إنشاء قفل للمسار %x. - -Scanning: -الفحص: - - -1 thread -%x threads - - -0 بند -1 بند واحد -2 بندان -%x بنود -%x بنداً -%x بند - - -Encoding extended time information: %x -ترميز المعلومات الموسعة للوقت: %x - -/sec -\ثانية - -%x items/sec -%x عنصر\الثانية - -Configuration file %x loaded partially only. -تم تحميل ملف التكوين %x بشكلٍ جزئي فقط. - -Show in Explorer -إظهار في المستكشف - -Open with default application -فتح باستخدام التطبيق الافتراضي - -Browse directory -تصفح المسار - -Cannot access the Volume Shadow Copy Service. -لا يمكن الوصول إلى خدمة النسخ الظلي الوسيط. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -الرجاء استخدام إصدار الـ 64-bit للبرنامج لإنشاء ملفات الظل الاحتياطية على هذا النظام. - -Cannot load file %x. -لا يمكن فتح الملف %x. - -Cannot determine volume name for %x. -تعذر تحديد اسم الوسط %x - -Volume name %x is not part of file path %y. -اسم الوسط %x ليس جزءا من مسار الملق %y. - -Stop requested: Waiting for current operation to finish... -طلب إحباط المهمة: في انتظار انتهاء المهمة الحالية... - -Unable to create timestamp for versioning: -فشل إنشاء طبعة زمنية من أجل عملية الوسم حسب الإصدار: - -Cannot read the following XML elements: -لا يمكن قراءة عناصر XML التالية: - -&Open... -&فتح... - -Save &as... -&حفظ باسم... - -&Quit -&إنهاء - -&Program -&البرنامج - -&View help -&إظهار المساعدة - -&About -&حول - -&Help -&تعليمات - -Usage: -الاستخدام: - -1. Select folders to watch. -1. حدد المجلدات للمتابعة. - -2. Enter a command line. -2. إدخال سطر أوامر. - -3. Press 'Start'. -3. اضغط على 'ابدأ'. - -To get started just import a .ffs_batch file. -للبدء قم باستيراد ملف .ffs_batch. - -Folders to watch: -المجلدات المراقبة - -Add folder -إضافة مجلد - -Remove folder -إزالة مجلد - -Browse -تصفح - -Select a folder -تحديد مجلد - -Idle time (in seconds): -وقت الخمول (بالثانية) - -Idle time between last detected change and execution of command -وقت الخمول بين آخر تغيير تم الكشف عنه وتنفيذ الأوامر - -Command line: -سطر الأوامر: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -يتم تشغيل الأمر إذا: --حدوث تغيير في الملفات أو المجلدات الفرعية --ظهور مجلدات جديدة (مثال: إدخال USB stick) - - -&Start -&ابدأ - -&Retry -&إعادة المحاولة - -Cancel -إلغاء الأمر - -About -حول - -Build: %x -بناء: %x - -All files -جميع الملفات - -Automated Synchronization -مزامنة تلقائية - -Directory monitoring active -مراقبة المسارات فعالة - -Waiting until all directories are available... -انتظر حتى توفر جميع المسارات... - -Error -خطأ - -&Restore -&استعادة - -&Show error -&إظهار الخطأ - -&Exit -&خروج - -Incorrect command line: -سطر أوامر خاطئ: - -File content -محتوى الملف - -File time and size -تاريخ الملف و حجمه - -Two way -بالاتجاهين - -Mirror -انعكاس - -Update -تحديث - -Custom -مخصص - -Multiple... -متعددة... - -Moving file %x to %y -نقل الملف %x إلى %y - -Moving folder %x to %y -نقل المجلد %x إلى %y - -Moving symbolic link %x to %y -نقل الارتباط الرمزي %x إلى %y - -Removing old versions... -إزالة الإصدارات القديمة... - -Creating symbolic link %x -إنشاء ارتباط رمزي %x - -Creating folder %x -إنشاء مجلد %x - -Overwriting file %x -الكتابة فوق الملف %x - -Overwriting symbolic link %x -الكتابة فوق الارتباط الرمزي %x - -Verifying file %x -التحقق من الملف %x - -Updating attributes of %x -تحديث سمات %x - -Cannot find %x. -تعذر العثور على %x. - -Target folder %x already existing. -المجلد الهدف %x موجود سابقاً. - -Target folder input field must not be empty. -يجب أن لا يكون حقل إدخال المجلد الهدف فارغاً. - -Please enter a target folder for versioning. -الرجاء تحديد مجلد هدف من أجل الوسم حسب الإصدار. - -Source folder %x not found. -لم يتم العثور على المجلد المصدر %x. - -The following items have unresolved conflicts and will not be synchronized: -العناصر التالية لم تحل اختلافاتها، و لن يتم مزامنتها: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -المجلدات التالية مختلفة بشكل كبير. تأكد من تقابل المجلدات بشكل صحيح من أجل المزامنة. - -Not enough free disk space available in: -المساحة الحرة المتوفرة على القرص غير كافية: - -Required: -مطلوب: - -Available: -متاح: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -سيتم تعديل مجلد الذي هو جزء من أزواج المجلدات المتعددة. الرجاء مراجعة إعدادات المزامنة. - -Synchronizing folder pair: -مزامنة زوج مجلدات: - -Generating database... -إنشاء قاعدة بيانات... - -Creating a Volume Shadow Copy for %x... -جاري إنشاء نسخة ظل وسيطة لـ %x... - -Data verification error: %x and %y have different content. -خطأ في التحقق من البيانات: يحتوي %x و %y بيانات مختلفة. - -job name -اسم المهمة - -Synchronization stopped -توقفت عملية المزامنة - -Synchronization completed with errors -انتهاء عملية المزامنة مع وجود أخطء - -Synchronization completed with warnings -انتهاء عملية المزامنة مع وجود تحذيرات - -Nothing to synchronize -لا يوجد شيء للمزامنة - -Synchronization completed successfully -تمت المزامنة بنجاح - -Saving log file %x... -حفظ ملف السجل %x... - -You can switch to FreeFileSync's main window to resolve this issue. -بإمكانك العودة إلى نافذة FreeFileSync الرئيسية لحل هذه المشكلة. - -&Don't show this warning again -&لا تظهر هذا التنبيه مرة ثانية - -&Ignore -&تجاهل - -&Switch -&تبديل - -Switching to FreeFileSync's main window -العودة إلى نافذة FreeFileSync الرئيسية - -Serious Error -خطأ فادح - -&Ignore subsequent errors -&تجاهلا الأخطاء المماثلة - -Check for Program Updates -تفقد وجود تحديثات للبرنامج - -A new version of FreeFileSync is available: -يتوفر إصدار جديد من FreeFileSync: - -Download now? -تنزيل الآن؟ - -&Download -&تنزيل - -FreeFileSync is up to date. -البرنامج هو الأحدث حتى الآن. - -Unable to connect to sourceforge.net. -تعذر الاتصال بـ sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -لم نستطع العثور على على رقم إصدار FreeFileSync على الشبكة. هل تريد التحقق يدوياً؟ - -Symlink -ارتباط-رمزي - -Folder -المجلد - -Full path -المسار الكامل - -Name -الاسم - -Relative path -المسار النسبي - -Base folder -المجلد الأساسي (القاعدي) - -Size -الحجم - -Date -تاريخ - -Extension -اللاحقة - -Category -الفئة - -Action -التصرف - -Drag && drop -سحب و إفلات - -Close progress dialog -إنهاء نافذة حوار تقدم العملية - -Standby -وضع الاستعداد - -Log off -تسجيل الخروج - -Shut down -إيقاف التشغيل - -Hibernate -السبات - -Alternate comparison settings -خيارات المفارنة البديلة - -Alternate synchronization settings -خيارات المزامنة البديلة - -Local filter -فلتر محلي - -Active -مفعل - -None -لا شيء - -Remove alternate settings -إزالة الإعدادات البديلة - -Clear filter settings -مسح إعدادات عامل الفلترة - -Copy -نسخ - -Paste -لصق - -Alternate Comparison Settings -خيارات المفارنة البديلة - -Alternate Synchronization Settings -خيارات المزامنة البديلة - -Local Filter -فلتر محلي - -&New -&جديد - -&Save -&حفظ - -Save as &batch job... -&حفظ كمهمة دفعية... - -1. &Compare -1-&مقارنة - -2. &Synchronize -2. &مزامنة - -&Global settings -&الإعدادات العامة - -&Language -&اللغة - -&Find... -&بحث... - -&Export file list... -&تصدير قائمة الملفات... - -&Tools -&أدوات - -&Check now -&تحقق الآن - -Check &automatically once a week -تحقق &أوتوماتيكياً بشكل أسبوعي - -&Check for new version -&التحقق من وجود نسخة جديدة - -Compare -قارن - -Synchronize -مزامنة - -Add folder pair -إضافة زوج مجلدات - -Remove folder pair -إزالة زوج مجلدات - -Swap sides -مبادلة الجانبين - -Close search bar -إغلاق شريط البحث - -Find: -بحث - -Match case -مطابقة حالة الأحرف (Match case) - -Save as batch job -حفظ كمهمة دفعية - -Hide excluded items -إخفاء العناصر المستبعدة - -Show filtered or temporarily excluded files -إظهار الملفات التي تم فلترتها أو استبعادها بشكل مؤقت - -Number of files and folders that will be created -عدد الملفات و المجلدات التي سيتم إنشاؤها - -Number of files that will be overwritten -عدد الملفات التي سيتم استبدالها - -Number of files and folders that will be deleted -عدد الملفات و المجلدات التي سيتم حذفها - -Total bytes to copy -إجمالي عدد الـ bytes التي سيتم نسخها - -Select a variant: -اختيار بديل: - -Identify equal files by comparing modification time and size. -التعرف على الملفات المتساوية عن طريق مقارنة التاريخ و الحجم - -Identify equal files by comparing the file content. -التعرف على الملفات المتساوية عن طريق مقارنة محتوى الملف - -Symbolic links: -ارتباطات رمزية: - -More information -المزيد من المعلومات - -OK -موافق - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -تحديد التغيرات و مواكبتها على الجانبين. عمليات الحذف, النقل و المشاكل المكتشفة بواسطة قواعد البيانات - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -إنشاء نسخة نظيرة للمجلد على اليسار بحيث يطابق المجلد على اليمين بعد المزامنة - -Copy new and updated files to the right folder. -نسخ الملفات المحدثة إلى المجلد المناسب - -Configure your own synchronization rules. -تحديد قواعد المزامنة الخاصة بك. - -Detect moved files -اكتشاف الملفات المنقولة - -Requires database files. Not supported by all file systems. -يتطلب ملفات قواعد بيانات. غير مدعوم من قبل جميع أنظمة الملفات. - -Delete files: -حذف الملفات: - -Permanent -الدائم - -Delete or overwrite files permanently -حذف أو الكتابة فوق الملفات بشكل دائم - -Recycle bin -سلة المهملات - -Back up deleted and overwritten files in the recycle bin -نسخ احتياطي للملفات المحذوفة و المستبدلة في سلة المهملات - -Versioning -الوسم برقم الإصدار - -Move files to a user-defined folder -نقل الملفات إلى المجلد المحدد من قبل المستخدم - -Naming convention: -اصطلاح التسمية: - -Show examples -إظهار أمثلة - -Handle errors: -التعامل مع الأخطاء: - -Ignore -تجاهل - -Hide all error and warning messages -إخفاء جميع رسائل الأخطاء و التحذير - -Pop-up -إطار منبثق - -Show pop-up on errors or warnings -إظهار إطارات منبثقة عند حصول أخطاء أو تحذيرات - -On completion: -عند الانتهاء: - -Start synchronization now? -بدأ المزامنة الآن؟ - -Variant: -بديل - -Statistics -إحصائيات - -&Don't show this dialog again -&لا تظهر نافذة الحوار هذه مرة ثانية - -Items found: -العناصر التي تم العثور عليها: - -Speed: -السرعة: - -Time remaining: -الوقت المتبقي: - -Time elapsed: -الوقت المنقضي: - -Synchronizing... -مزامنة... - -Minimize to notification area -تصغير إلى منطقة التنبيهات - -Close -إغلاق - -&Pause -&إيقاف مؤقت - -Stop -توقف - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -إنشاء ملف دفعي من أجل عمليات المزامنة غير المحضورة. للبدأ, انقر نقراً مزدوجاً على الملف أو المهمة المجدولة في منظم المهام: %x - -Stop synchronization at first error -إيقاف المزامنة عند أول خطأ - -Show progress dialog -إظهار نافذة حوار تقدم العملية - -Save log: -حفظ السجل - -Limit: -حدود - -Limit maximum number of log files -تقييد الحد الأقصى لعدد ملفات السجل - -How can I schedule a batch job? -كيف يمكنني جدولة مهمة دفعية؟ - -&Recycle bin -&سلة المهملات - -Delete on both sides -حذف على كلا الجانبين - -Delete on both sides even if the file is selected on one side only -حذف الملف في كلا الجانبين حتى إذا كان الملف محدداً على جانب واحد فقط - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -اختيار قوانين فلترة لاستثناء ملفات معينة من المزامنة. أدخل مسارات الملفات منسوبة إلى زوج المجلدات المقابل. - -Include: -إدخال: - -Exclude: -استثناء: - -Time span: -المجال الزمني: - -File size: -حجم الملف: - -Minimum: -أصغري - -Maximum: -أعظمي - -&Clear -&إزالة - -The following settings are used for all synchronization jobs. -هذه الإعدادات مستخدمة لجميع مهمات المزامنة - -Fail-safe file copy -نسخ ملفات آمن من الفشل - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -قم بالنسخ إلى ملف مؤقت (*.ffs_tmp) قبل استبدال الملف الهدف. -هذه العملية تضمن حالة مستقرة حتى في حال حدوث خطأ فادح. - - -(recommended) -(موصى به) - -Copy locked files -نسخ الملفات المقفلة - -Copy shared or locked files using the Volume Shadow Copy Service. -نسخ الملفات المشتركة مع مستخدمين آخرين أو المقفولة باستخدام خدمة نسخ الظل الوسيط - -(requires administrator rights) -(بحاجة امتيازات المدير) - -Copy file access permissions -نسخ أذونات الوصول إلى الملف - -Transfer file and folder permissions. -نقل أذونات الملفات و المجلدات. - -Automatic retry on error: -إعادة المحاولة بشكل تلقائي عند حصول خطأ: - -Retry count: -تعداد محاولات الإعادة: - -Delay (in seconds): -التأخير (بالثواني): - -Customize context menu: -تخصيص القائمة المحلية - -Description -الوصف - -Restore hidden windows -استعادة نوافذ الحوار المخفية - -&Default -&الافتراضي - -Source code written in C++ using: -الرماز المصدري مكتوب بلغة C++ باستخدام: - -If you like FreeFileSync -إذا أعجبك FreeFileSync - -Donate with PayPal -تبرع باستخدام PayPal - -Feedback and suggestions are welcome -التعليقات و الاقتراحات موضع ترحيب - -Homepage -الصفحة الرئيسية - -Email -البريد الإلكتروني - -Published under the GNU General Public License -نشر تحت رخصة GNU General Public License - -Many thanks for localization: -شكرا جزيلا للترجمة: - -Save as Batch Job -حفظ كمهمة دفعية - -Delete Items -حذف العناصر - -Global Settings -الخيارات العامة - -Select Time Span -اختيار المطال الزمني - -Folder Pairs -أزواج المجلدات - -Find -بحث - -Overview -نظرة عامة - -Configuration -التكوين - -Main Bar -الشريط الرئيسي - -Filter Files -فلترة الملفات - -Select View -اخيار طريقة العرض - -Open... -فتح... - -Save -حفظ - -Compare both sides -مقارنة بين كلا الجانبين - -Comparison settings -إعدادات المقارنة - -Synchronization settings -إعدادات المزامنة - -Start synchronization -بدء المزامنة - -Confirm -تأكيد - -&Execute -&تنفيذ - - -1 directory -%x directories - - -0 مسار -1 مسار واحد -2 مساران -%x مسارات -%x مساراً -%x مسار - - - -1 file -%x files - - -0 ملف -1 ملف واحد -2 ملفان -%x ملفات -%x ملفاً -%x ملف - - - -%y of 1 row in view -%y of %x rows in view - - -الظاهر %y من أصل 0 سطر -الظاهر %y من أصل 1 (سطر وحيد) -الظاهر %y من أصل 2 (سطرين) -الظاهر %y من أصل %x سطور -الظاهر %y من أصل %x سطراً -الظاهر %y من أصل %x سطر - - -Set direction: -تحديد الاتجاه: - -multiple selection -تحديد متعدد - -Include via filter: -تضمن عن طريق فلتر - -Exclude via filter: -استبعاد باستخدام عامل الفلترة: - -Exclude temporarily -استبعاد مؤقتاً - -Include temporarily -شمول مؤقتاً - -Delete -حذف - -Include all -شمول الكل - -Exclude all -استبعاد الكل - -Show icons: -إظهار الأيقونات: - -Small -صغيرة - -Medium -متوسطة - -Large -كبيرة - -Select time span... -حدد المجال الزمني... - -Default view -طريقة العرض الافتراضية - -Show "%x" -إظهار "%x" - -Last session -مصدر الجلسة - -Folder Comparison and Synchronization -مقارنة و مزامنة المجلد - -Configuration saved -تم حفظ التكوين - -FreeFileSync batch -دفعة FreeFileSync - -Do you want to save changes to %x? -هل تريد حفظ التغييرات إلى %x؟ - -Never save &changes -لا تقم بحفظ &التغيرات - -Do&n't save -&لا تحفظ - -Filter -عامل الفلترة - -Show files that exist on left side only -إظهار الملفات الموجودة في الجانب الأيمن فقط - -Show files that exist on right side only -إظهار الملفات الموجودة في الجانب الأيسر فقط - -Show files that are newer on left -إظهار الملفات الأحدث في اليمين - -Show files that are newer on right -إظهار الملفات الأحدث في اليسار - -Show files that are equal -إظهار الملفات المتماثلة على الطرفين - -Show files that are different -إظهار الملفات المختلفة على الطرفين - -Show conflicts -إظهار الاختلافات - -Show files that will be created on the left side -إظهار الملفات التي سيتم إنشاؤها في الجانب الأيمن - -Show files that will be created on the right side -إظهار الملفات التي سيتم إنشاؤها في الجانب الأيسر - -Show files that will be deleted on the left side -إظهار الملفات التي سيتم حذفها من من الجانب الأيمن - -Show files that will be deleted on the right side -إظهار الملفات التي سيتم حذفها من الجانب الأيسر - -Show files that will be overwritten on left side -إظهار الملفات التي سيتم الكتابة فوقها في الجانب الأيمن - -Show files that will be overwritten on right side -إظهار الملفات التي سيتم الكتابة فوقها في الجانب الأيسر - -Show files that won't be copied -إظهار الملفات التي لن يتم نسخها - -Set as default -تحديد كوضع افتراضي - -All folders are in sync -جميع المجلدات متزامنة - -Synchronization Settings -خيارات المزامنة - -Comparison Settings -خيارات المقارنة - -Cannot find %x -لا يمكن العثور على %x - -Comma-separated values -قيم مفصولة بواسطة فواصل - -File list exported -تم تصدير قائمة الملفات - -Searching for program updates... -جاري البحث عن تحديثات للبرنامج... - -Scanning... -جاري الفحص... - -Comparing content... -مقارنة المحتوى... - -Info -معلومات - -Warning -تحذير - -Paused -تم الإيقاف مؤقتاً - -Initializing... -التجهيز للبدأ... - -Stopped -توقف - -Completed -تم الإنتهاء - -&Continue -&متابعة - -Log -السجل - -Today -اليوم - -This week -هذا الأسبوع - -This month -هذا الشهر - -This year -هذه السنة - -Last x days -الأيام x الماضية - -Byte -Byte - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -هل تريد حقاً نقل 0 ملف إلى سلة المهملات ؟ -هل تريد حقاً نقل هذا الملف الوحيد 1 إلى سلة المهملات ؟ -هل تريد حقاً نقل هذين الملفبن 2 إلى سلة المهملات ؟ -هل تريد حقاً نقل %x ملفات إلى سلة المهملات ؟ -هل تريد حقاً نقل %x ملفاً إلى سلة المهملات ؟ -هل تريد حقاً نقل %x ملف إلى سلة المهملات ؟ - - -Move -نقل - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -هل تريد حقاً حذف 0 ملف التالي ؟ -هل تريد حقاً حذف الملف 1 التالي ؟ -هل تريد حقاً حذف الملفين 2 التاليين ؟ -هل تريد حقاً حذف الـ %x ملفات التالية ؟ -هل تريد حقاً حذف الـ %x ملفاً التالية ؟ -هل تريد حقاً حذف الـ %x ملف التالية ؟ - - -Exclude -استبعاد - -Direct -مباشر - -Follow -اتبع - -Copy NTFS permissions -نسخ أذونات NTFS - -Integrate external applications into context menu. The following macros are available: -دمج تطبيقات خارجية في قائمة السياق. تتوفر وحدات الماكرو التالية: - -- full file or folder name -- اسم الملف أو المجلد كاملاً - -- folder part only -- جزء مجلد فقط - -- Other side's counterpart to %item_path% -- النظير للجانب الآخر لـ %item_path% - -- Other side's counterpart to %item_folder% -- االنظير للجانب الآخر لـ %item_folder% - -Restore all hidden windows and warnings? -استعادة جميع نوافذ الحوار و التحذيرات المخفية؟ - -Leave as unresolved conflict -ترك كاختلافات من دون حل - -Replace -استبدال - -Move files and replace if existing -نقل الملفات و استبدال الموجودة بها إن وجدت - -Time stamp -البصمة الزمنية - -Append a timestamp to each file name -إلحاق طابع زمني اسم كل ملف - -File -ملف - -YYYY-MM-DD hhmmss -YYYY-MM-DD hhmmss - -Files -ملفات - -Items -عناصر - -Percentage -النسبة المئوية - -Cannot monitor directory %x. -لا يمكن مراقبة المسار %x. - -Conversion error: -خطأ في تحويل: - -Cannot delete file %x. -لا يمكن حذف الملف %x. - -The file is locked by another process: -الملف مقفول من قبل عملية أخرى: - -Cannot move file %x to %y. -لا يمكن نقل الملف %x إلى %y. - -Cannot delete directory %x. -لا يمكن حذف المسار %x. - -Cannot write file attributes of %x. -لا يمكن كتابة سمات الملف %x. - -Cannot write modification time of %x. -لا يمكن كتابة وقت تعديل %x. - -Cannot read security context of %x. -لا يمكن قراءة سياق الأمان %x. - -Cannot write security context of %x. -لا يمكن كتابة سياق الأمان %x. - -Cannot read permissions of %x. -لا يمكن قراءة أذونات %x. - -Cannot write permissions of %x. -لا يمكن كتابة أذونات %x. - -Cannot create directory %x. -لا يمكن إنشاء المسار %x. - -Cannot create symbolic link %x. -تعذر إنشاء رابط رمزي %x - -Cannot find system function %x. -لا يمكن العثور على وظيفة نظام %x. - -Cannot copy file %x to %y. -لا يمكن نسخ الملف %x إلى %y. - -Type of item %x is not supported: -نوع العنصر %x غير مدعوم: - -Cannot resolve symbolic link %x. -لا يمكن حل الارتباط الرمزي %x. - -Cannot open directory %x. -لا يمكن فتح المسار %x. - -Cannot enumerate directory %x. -لا يمكن تعداد المسار %x. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -0 دقيقة -1 دقيقة واحدة -2 دقيقتان -%x دقائق -%x دقيقة -%x دقيقة - - - -1 hour -%x hours - - -0 ساعة -1 ساعة واحدة -2 ساعتين -%x ساعات -%x ساعة -%x ساعة - - - -1 day -%x days - - -0 يوم -1 يوم واحد -2 يومان -%x أيام -%x يوماً -%x يوم - - -Unable to register to receive system messages. -تعذر التسجيل لاستقبال رسائل النظام. - -Cannot set privilege %x. -لا يمكن تعيين امتيازات %x. - -Unable to suspend system sleep mode. -تعذر تعليق وضع النوم للنظام. - -Cannot change process I/O priorities. -تعذر تغيير أولويات I/O للعملية - -Unable to move %x to the recycle bin. -تعذر نقل %x إلى سلة المهملات. - -Cannot determine final path for %x. -تعذر تحديد المسار النهائي لـ %x. - -Error Code %x: -خطأ رقم %x: - diff --git a/BUILD/Languages/chinese_simple.lng b/BUILD/Languages/chinese_simple.lng deleted file mode 100644 index 8b5a89d6..00000000 --- a/BUILD/Languages/chinese_simple.lng +++ /dev/null @@ -1,1499 +0,0 @@ -
    - 简体中文 - CyberCowBoy - zh_CN - flag_china.png - 1 - 0 -
    - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -&Check - - -Retrying operation... - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Both sides have changed since last synchronization. -在最后的同步之后两边均已改变. - -Cannot determine sync-direction: -不能检测同步方向: - -No change since last synchronization. -自从最后一次同步以来没有变动. - -The database entry is not in sync considering current settings. -考虑到当前设置,数据库入口不同步. - -Setting default synchronization directions: Old files will be overwritten with newer files. -设置默认的同步方向:旧文件会被新文件覆盖. - -Checking recycle bin availability for folder %x... -正在为文件夹 %x 检测回收站可用性... - -Moving file %x to the recycle bin -移动文件 %x 到回收站 - -Moving folder %x to the recycle bin -移动文件夹 %x 到回收站 - -Moving symbolic link %x to the recycle bin -移动符号连接 %x 到回收站 - -Deleting file %x -正删除文件 %x - -Deleting folder %x -正删除文件夹 %x - -Deleting symbolic link %x -正在删除符号连接 %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -对于如下的文件夹回收站不可用. 文件将被永久性删除: - -An exception occurred -发生异常 - -A directory path is expected after %x. -在 %x 之后预期是一个目录路径. - -Syntax error -语法错误 - -Cannot open file %x. -无法打开文件 %x. - -File %x does not contain a valid configuration. -文件 %x 并未包含合法的配置. - -Unequal number of left and right directories specified. -左边和右边指定了数量不相等的目录. - -The config file must not contain settings at directory pair level when directories are set via command line. -当目录通过命令行设定时配置文件必须不包含目录对级别的设置. - -Directories cannot be set for more than one configuration file. -无法为多于一个配置文件设置目录. - -Command line -命令行 - -Syntax: -语法: - -config files -配置文件 - -directory -目录 - -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -任意数量的 FreeFileSync .ffs_gui 和/或 .ffs_batch 配置文件. - -Any number of alternative directories for at most one config file. -任意数量的替代目录中至多有一个配置文件. - -A folder input field is empty. -有一个文件夹输入框为空. - -The corresponding folder will be considered as empty. -相应的文件夹将被视为空. - -Cannot find the following folders: -无法找到如下文件夹: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -你可忽略此错误而将每个文件夹视为空. 这些文件夹将会在同步过程中被自动创建. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -如下的文件夹有路径依赖性. 请在设置同步规则时特别小心: - -File %x has an invalid date. -文件 %x 的日期非法. - -Date: -日期: - -Files %x have the same date but a different size. -文件 %x 日期相同但大小不同. - -Size: -大小: - -Items differ in attributes only -项目仅是文件属性不同 - -Resolving symbolic link %x -正在解决符号连接 %x - -Comparing content of files %x -正在比较文件 %x 的内容 - -Generating file list... -生成文件列表... - -Starting comparison -正在开始比较 - -Calculating sync directions... -正在计算同步方向... - -Out of memory. -内存不足. - -Item exists on left side only -项目仅存在于左侧 - -Item exists on right side only -项目仅存在于右侧 - -Left side is newer -左侧较新 - -Right side is newer -右侧较新 - -Items have different content -项目有不同的内容 - -Both sides are equal -两侧相等 - -Conflict/item cannot be categorized -冲突/项目未能被分类 - -Copy new item to left -复制新项目到左侧 - -Copy new item to right -复制新项目到右侧 - -Delete left item -删除左侧项目 - -Delete right item -删除右侧项目 - -Move file on left -移动左侧的文件 - -Move file on right -移动右侧的文件 - -Overwrite left item -覆盖左侧的项目 - -Overwrite right item -覆盖右侧的项目 - -Do nothing -保持不动 - -Update attributes on left -更新左侧的文件属性 - -Update attributes on right -更新右侧的文件属性 - -Database file %x is incompatible. -数据库文件 %x 不兼容. - -Initial synchronization: -初始化同步: - -Database file %x does not yet exist. -数据库文件 %x 还未存在. - -Database file is corrupt: -数据库文件已损坏: - -Cannot write file %x. -无法写入文件 %x . - -Cannot read file %x. -无法读取文件 %x . - -Database files do not share a common session. -数据库文件并未共享一个公共会话. - -Searching for folder %x... -正在搜索文件夹 %x... - -Cannot read file attributes of %x. -无法读取 %x 的文件属性. - -Cannot get process information. -无法取得进程信息. - -Waiting while directory is locked (%x)... -由于目录已锁定而正在等待(%x)... - - -1 sec -%x sec - - -%x 秒 - - -Creating file %x -正在创建文件 %x - -Items processed: -已处理的项目: - -Items remaining: -剩余的项目: - -Total time: -总共时间: - - -1 byte -%x bytes - - -%x 字节 - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -当分析文件 %x , 行 %y, 列 %z 时出错. - -Cannot set directory lock for %x. -无法为 %x 设置目录锁定. - -Scanning: -扫描中: - - -1 thread -%x threads - - -%x 线程 - - -Encoding extended time information: %x -正在编码扩展时间信息:%x - -/sec -/秒 - -%x items/sec -%x 个项目/秒 - -Configuration file %x loaded partially only. -配置文件 %x 只是部分载入. - -Show in Explorer -在Explorer中显示 - -Open with default application -用默认应用软件打开 - -Browse directory -浏览目录 - -Cannot access the Volume Shadow Copy Service. -无法访问卷影复制服务. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -请使用 FreeFileSync 64位版本来在这个系统上创建卷影复制. - -Cannot load file %x. -不能载入文件 %x. - -Cannot determine volume name for %x. -无法为 %x 确定卷名. - -Volume name %x is not part of file path %y. -卷名 %x 不是文件路径 %y 的一部分. - -Stop requested: Waiting for current operation to finish... -停止要求: 等待当前操作完成... - -Unable to create timestamp for versioning: -无法为历史版本创建时间戳: - -Cannot read the following XML elements: -无法读取如下XML元素: - -&Open... -打开(&O)... - -Save &as... -另存为(&A)... - -&Quit -退出(&Q) - -&Program -程序(&P) - -&View help -查看帮助(&V) - -&About -关于(&A) - -&Help -帮助(&H) - -Usage: -用法: - -1. Select folders to watch. -1. 选择要监视的文件夹. - -2. Enter a command line. -2. 输入一个命令行. - -3. Press 'Start'. -3. 点击'开始'. - -To get started just import a .ffs_batch file. -要开始只需导入一个 .ffs_batch文件. - -Folders to watch: -要监视的文件夹: - -Add folder -添加文件夹 - -Remove folder -移除文件夹 - -Browse -浏览 - -Select a folder -选择一个文件夹 - -Idle time (in seconds): -空闲时间(秒): - -Idle time between last detected change and execution of command -最后检测到改变和命令执行之间的空闲时间 - -Command line: -命令行: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -命令将被触发如果: -- 文件或子文件夹改变 -- 新文件夹到达(例如U盘插入) - - -&Start -开始(&S) - -About -关于 - -Build: %x -Build: %x - -All files -所有文件 - -Automated Synchronization -自动同步 - -Directory monitoring active -目录监视激活 - -Waiting until all directories are available... -正在等待直到所有目录都可用... - -Error -错误 - -&Restore -恢复(&R) - -&Show error -显示错误(&S) - -&Exit -退出(&E) - -Incorrect command line: -不正确的命令行: - -&Retry -重试(&R) - -File content -文件内容 - -File time and size -文件时间和大小 - -Two way -双向 - -Mirror -镜像 - -Update -更新 - -Custom -自定义 - -Multiple... -并联... - -Moving file %x to %y -正在移动文件 %x 到 %y - -Moving folder %x to %y -正在移动文件夹 %x 到 %y - -Moving symbolic link %x to %y -正在移动符号连接 %x 到 %y - -Removing old versions... -移除旧版本 - -Creating symbolic link %x -正在创建符号连接 %x - -Creating folder %x -正创建文件夹 %x - -Overwriting file %x -正在覆盖文件 %x - -Overwriting symbolic link %x -正在覆盖符号连接 %x - -Verifying file %x -校验文件 %x - -Updating attributes of %x -更新 %x 的属性 - -Cannot find %x. -无法找到 %x. - -Target folder %x already existing. -目标文件夹 %x 已经存在. - -Target folder input field must not be empty. -目标文件夹输入框必须不为空. - -Please enter a target folder for versioning. -请输入一个用于保存历史版本的目标文件夹. - -Source folder %x not found. -无法找到源文件夹 %x. - -The following items have unresolved conflicts and will not be synchronized: -如下项目有无法解决的冲突并将不会被同步: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -如下的文件夹有显著的不同. 请确认你为同步配对了正确的文件夹. - -Not enough free disk space available in: -没有足够的可用磁盘空间用于: - -Required: -必需: - -Available: -可用: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -一个将被修改的文件夹是多个文件夹对的一部分. 请重新检视同步设置. - -Synchronizing folder pair: -正在同步成对的文件夹: - -Generating database... -正在生成数据库... - -Creating a Volume Shadow Copy for %x... -为 %x 创建一个卷影副本... - -Data verification error: %x and %y have different content. -数据校验错误: %x 和 %y 有不同的内容. - -job name -作业名称 - -Synchronization stopped -同步已停止 - -Synchronization completed with errors -同步已完成但有错误 - -Synchronization completed with warnings -同步已完成但有警告 - -Nothing to synchronize -没有什么可同步 - -Synchronization completed successfully -同步成功完成 - -Saving log file %x... -正在保存日志文件 %x... - -You can switch to FreeFileSync's main window to resolve this issue. -你可以切换至 FreeFileSync 的主窗口来解决这个问题. - -&Don't show this warning again -不再显示这个警告(&D) - -&Ignore -忽略(&I) - -&Switch -切换(&S) - -Switching to FreeFileSync's main window -切换至 FreeFileSync 的主窗口 - -&Ignore subsequent errors -忽略后续的错误(&I) - -Serious Error -严重错误 - -Check for Program Updates -检查程序的更新 - -A new version of FreeFileSync is available: -已经有新版本的FreeFileSync可用: - -Download now? -立即下载? - -&Download -下载(&D) - -FreeFileSync is up to date. -FreeFileSync 已是最新. - -Unable to connect to sourceforge.net. -无法链接到 Sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -无法在线找到当前FreeFileSync版本号. 你要手动检查吗? - -Symlink -符号连接 - -Folder -文件夹 - -Full path -完整路径 - -Name -文件名 - -Relative path -相对路径 - -Base folder -主文件夹 - -Size -大小 - -Date -日期 - -Extension -扩展名 - -Category -分类 - -Action -动作 - -Drag && drop -拖放 - -Close progress dialog -关闭进度对话框 - -Standby -待机 - -Log off -注销 - -Shut down -关机 - -Hibernate -休眠 - -Alternate comparison settings -备用比较设置 - -Alternate synchronization settings -备用同步设置 - -Local filter -本地过滤器 - -Active -生效 - -None - - -Remove alternate settings -移除替换设置 - -Clear filter settings -清除过滤器设置 - -Copy -复制 - -Paste -粘贴 - -Alternate Comparison Settings -备用比较设置 - -Alternate Synchronization Settings -备用同步设置 - -Local Filter -本地过滤器 - -&New -新建(&N) - -&Save -保存(&S) - -Save as &batch job... -另存为批处理作业(&b) - -1. &Compare -1. 比较(&C) - -2. &Synchronize -2. 同步(&S) - -&Global settings -全局设置(&G) - -&Language -切换语言(&L) - -&Find... -查找...(&F) - -&Export file list... -导出文件列表(&E)... - -&Tools -工具(&T) - -&Check now -现在检查(&C) - -Check &automatically once a week -每周自动检查一次(&A) - -&Check for new version -检查新版本(&C) - -Compare -比较 - -Cancel -取消 - -Synchronize -同步 - -Add folder pair -添加成对文件夹 - -Remove folder pair -移除文件夹对 - -Swap sides -两侧互换 - -Close search bar -关闭搜索条 - -Find: -查找: - -Match case -匹配大小写 - -Save as batch job -另存为批处理作业 - -Hide excluded items -隐藏排除项目 - -Show filtered or temporarily excluded files -显示已被过滤或被临时排除的文件 - -Number of files and folders that will be created -将被创建的文件和文件夹数 - -Number of files that will be overwritten -将被覆盖的文件数 - -Number of files and folders that will be deleted -将被删除的文件和文件夹数 - -Total bytes to copy -要复制的总字节数 - -Select a variant: -选择一个变化: - -Identify equal files by comparing modification time and size. -以比较修改时间和文件大小来识别相同文件. - -Identify equal files by comparing the file content. -以比较文件内容来识别相同的文件. - -Symbolic links: -符号连接: - -More information -更多信息 - -OK -确定 - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -识别和传播两侧的变化. 删除, 移动和冲突会使用一个数据库来自动检测. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -创建左侧文件夹的镜像备份, 在同步之后右侧文件夹完全精确匹配. - -Copy new and updated files to the right folder. -复制新的和已更新的文件到右侧文件夹. - -Configure your own synchronization rules. -配置你自己的同步规则. - -Detect moved files -检测被移动的文件 - -Requires database files. Not supported by all file systems. -需要数据库文件. 不是所有文件系统都支持. - -Delete files: -删除文件: - -Permanent -永久 - -Delete or overwrite files permanently -永久性删除或覆盖文件 - -Recycle bin -回收站 - -Back up deleted and overwritten files in the recycle bin -备份被删除和被覆盖的文件到回收站 - -Versioning -保留历史版本 - -Move files to a user-defined folder -移动文件到一个用户定义的文件夹 - -Naming convention: -命名规则: - -Show examples -显示范例 - -Handle errors: -处理错误: - -Ignore -忽略 - -Hide all error and warning messages -隐藏所有错误与警告信息 - -Pop-up -弹出框 - -Show pop-up on errors or warnings -在错误或警告时显示弹出对话框 - -On completion: -在完成时: - -Start synchronization now? -现在开始同步吗? - -Variant: -变化: - -Statistics -统计 - -&Don't show this dialog again -不要再显示这个对话框(&D) - -Items found: -已找到的项目: - -Speed: -速度: - -Time remaining: -剩余时间: - -Time elapsed: -已用时间: - -Synchronizing... -同步中... - -Minimize to notification area -最小化到托盘 - -Close -关闭 - -&Pause -暂停(&P) - -Stop -停止 - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -创建一个用于无人值守同步的批处理文件. 要开始, 双击这个文件或任务计划器中的计划调度: %x - -Stop synchronization at first error -在遇到第一个错误时停止同步 - -Show progress dialog -显示进度对话框 - -Save log: -保存日志: - -Limit: -限制: - -Limit maximum number of log files -限制日志文件的量大个数 - -How can I schedule a batch job? -我如何计划一个批处理作业? - -&Recycle bin -回收站(&R) - -Delete on both sides -从两侧删除 - -Delete on both sides even if the file is selected on one side only -从两侧删除(即使仅在一侧选择文件) - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -选择过滤器规则以将某些文件从同步中排除. 输入与相关的文件夹对相对的与文件路径. - -Include: -包括: - -Exclude: -排除: - -Time span: -时间跨度: - -File size: -文件大小: - -Minimum: -最小: - -Maximum: -最大: - -&Clear -清除(&C) - -The following settings are used for all synchronization jobs. -如下的设置使用于所有同步作业. - -Fail-safe file copy -无风险的文件复制 - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - -在覆盖目标之前先复制到临时文件(*.ffs_tmp). 这样即使在发生严重错误的情况下也可以保证有一致的状态. - -(recommended) -(推荐) - -Copy locked files -复制被锁定的文件 - -Copy shared or locked files using the Volume Shadow Copy Service. -使用卷影复制服务来复制已共享或已锁定的文件. - -(requires administrator rights) -(需要管理员权限) - -Copy file access permissions -复制文件存取权限 - -Transfer file and folder permissions. -传输文件及文件夹的权限. - -Automatic retry on error: -在出错时自动重试: - -Retry count: -重试计数: - -Delay (in seconds): -延时 (秒): - -Customize context menu: -自定义右键菜单: - -Description -描述 - -Restore hidden windows -恢复隐藏窗口 - -&Default -默认(&D) - -Source code written in C++ using: -源代码用如下C++工具写成: - -If you like FreeFileSync -如果你喜欢 FreeFileSync - -Donate with PayPal -通过PayPal捐赠 - -Feedback and suggestions are welcome -欢迎反馈意见和提出建议 - -Homepage -主页 - -Email -邮箱 - -Published under the GNU General Public License -在GNU通用公共许可下发布 - -Many thanks for localization: -非常感谢以下本地化翻译者: - -Save as Batch Job -另存为批处理作业 - -Delete Items -删除项目 - -Global Settings -全局设置 - -Select Time Span -选择时间跨度 - -Folder Pairs -文件夹对 - -Find -查找 - -Overview -摘要 - -Configuration -配置 - -Main Bar -主工具栏 - -Filter Files -过滤器文件 - -Select View -选择视图 - -Open... -打开... - -Save -保存 - -Compare both sides -比较两侧 - -Comparison settings -比较设置 - -Synchronization settings -同步设置 - -Start synchronization -开始同步 - -Confirm -确认 - -&Execute -排除(&E) - - -1 directory -%x directories - - -%x 目录 - - - -1 file -%x files - - -%x 文件 - - - -%y of 1 row in view -%y of %x rows in view - - -%x 中的 %y 行在视图中 - - -Set direction: -设置方向: - -multiple selection -多选 - -Include via filter: -通过过滤器包括: - -Exclude via filter: -通过过滤器排除: - -Exclude temporarily -暂时排除 - -Include temporarily -暂时包括 - -Delete -删除 - -Include all -包括所有 - -Exclude all -排除所有 - -Show icons: -显示图标: - -Small - - -Medium - - -Large - - -Select time span... -选择时间跨度... - -Default view -默认视图 - -Show "%x" -显示 "%x" - -Last session -最后会话 - -Folder Comparison and Synchronization -文件夹比较与同步 - -Configuration saved -配置已保存 - -FreeFileSync batch -FreeFileSync批处理文件 - -Do you want to save changes to %x? -是否要保存修改到 %x ? - -Never save &changes -不再保存更改(&C) - -Do&n't save -不保存(&N) - -Filter -过滤器 - -Show files that exist on left side only -显示仅存在左侧的文件 - -Show files that exist on right side only -显示仅存在右侧的文件 - -Show files that are newer on left -显示左侧较新的文件 - -Show files that are newer on right -显示右侧较新的文件 - -Show files that are equal -显示相同的文件 - -Show files that are different -显示不同的文件 - -Show conflicts -显示冲突 - -Show files that will be created on the left side -显示将在左侧被建立的文件 - -Show files that will be created on the right side -显示将在右侧被建立的文件 - -Show files that will be deleted on the left side -显示将在左侧被删除的文件 - -Show files that will be deleted on the right side -显示将在右侧被删除的文件 - -Show files that will be overwritten on left side -显示将在左侧被覆盖的文件 - -Show files that will be overwritten on right side -显示将在右侧被覆盖的文件 - -Show files that won't be copied -显示将不被复制的文件 - -Set as default -设置为默认值 - -All folders are in sync -所有文件夹都是同步的 - -Synchronization Settings -同步设置 - -Comparison Settings -比较设置 - -Cannot find %x -找不到 %x - -Comma-separated values -逗号分割的数值(CSV) - -File list exported -文件清单已经导出 - -Searching for program updates... -正在搜索程序的更新... - -Scanning... -正扫描... - -Comparing content... -正在比较文件内容... - -Info -信息 - -Warning -警告 - -Paused -已暂停 - -Initializing... -正在初始化... - -Stopped -已停止 - -Completed -完成 - -&Continue -继续(&C) - -Log -日志 - -Today -今天 - -This week -本周 - -This month -本月 - -This year -今年 - -Last x days -最后 x 天 - -Byte -字节 - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -你是否要将如下 %x 个项目移动到回收站? - - -Move -移动 - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -是否真的要删除如下 %x 个项目? - - -Exclude -排除 - -Direct -直接 - -Follow -跟随 - -Copy NTFS permissions -复制NTFS权限 - -Integrate external applications into context menu. The following macros are available: -集成外部应用程序到右键菜单. 如下宏可用: - -- full file or folder name -- 完整的文件或文件夹名 - -- folder part only -- 只对文件夹部分 - -- Other side's counterpart to %item_path% -- 另一边的对应项到 %item_path% - -- Other side's counterpart to %item_folder% -- 另一边的对应项到 %item_folder% - -Restore all hidden windows and warnings? -要恢复所有隐藏的窗口和警告吗? - -Leave as unresolved conflict -遗留为未解决的冲突 - -Replace -替换 - -Move files and replace if existing -移动文件, 若文件已存在则替换 - -Time stamp -时间戳 - -Append a timestamp to each file name -附加时间戳到每一个文件名 - -File -文件 - -YYYY-MM-DD hhmmss -YYYY-MM-DD hhmmss - -Files -文件 - -Items -项目 - -Percentage -百分比 - -Cannot monitor directory %x. -无法监视目录 %x. - -Conversion error: -转换错误: - -Cannot delete file %x. -无法删除文件 %x. - -The file is locked by another process: -此文件被另一进程锁定: - -Cannot move file %x to %y. -无法移动文件 %x 到 %y. - -Cannot delete directory %x. -无法删除目录 %x. - -Cannot write file attributes of %x. -无法写入 %x 的文件属性. - -Cannot write modification time of %x. -无法写入 %x 的最后修改时间. - -Cannot read security context of %x. -无法读取 %x 的安全上下文. - -Cannot write security context of %x. -无法写入 %x 的安全上下文. - -Cannot read permissions of %x. -无法读取 %x 的权限. - -Cannot write permissions of %x. -无法写入 %x 的权限. - -Cannot create directory %x. -无法创建目录 %x. - -Cannot create symbolic link %x. -无法创建符号连接 %x. - -Cannot find system function %x. -无法找到系统功能 %x. - -Cannot copy file %x to %y. -无法复制文件 %x 到 %y. - -Type of item %x is not supported: -%x 的类型不被支持: - -Cannot resolve symbolic link %x. -无法解决符号连接 %x. - -Cannot open directory %x. -无法打开目录 %x. - -Cannot enumerate directory %x. -无法列举目录 %x. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -%x 分 - - - -1 hour -%x hours - - -%x 小时 - - - -1 day -%x days - - -%x 天 - - -Unable to register to receive system messages. -无法注册以接收系统信息. - -Cannot set privilege %x. -无法设置 %x 的特权. - -Unable to suspend system sleep mode. -无法路上系统睡眠模式. - -Cannot change process I/O priorities. -无法更改进程的I/O优先级. - -Unable to move %x to the recycle bin. -无法将 %x 移动到回收站. - -Cannot determine final path for %x. -无法确定 %x 的最终路径. - -Error Code %x: -错误代码 %x: - diff --git a/BUILD/Languages/chinese_traditional.lng b/BUILD/Languages/chinese_traditional.lng deleted file mode 100644 index 36cbd8ad..00000000 --- a/BUILD/Languages/chinese_traditional.lng +++ /dev/null @@ -1,1504 +0,0 @@ -
    - 正體中文 - Carlos Chen - Chinese (Traditional) - flag_taiwan.png - 1 - 0 -
    - -&Check - - -Retrying operation... - - -Both sides have changed since last synchronization. -自上次同步後,兩邊均已變更過。 - -Cannot determine sync-direction: -無法確定同步方向: - -No change since last synchronization. -自上次同步以來都沒有變更。 - -The database entry is not in sync considering current settings. -資料庫條目在同步時,不會考慮到目前的設定。 - -Setting default synchronization directions: Old files will be overwritten with newer files. -設定預設同步方向:舊檔案會被較新的檔案覆蓋。 - -Checking recycle bin availability for folder %x... -正在檢查資源回收筒的可用性資料夾 %x... - -Moving file %x to the recycle bin -正在移動檔案 %x 到資源回收筒 - -Moving folder %x to the recycle bin -正在移動資料夾 %x 到資源回收筒 - -Moving symbolic link %x to the recycle bin -正在移動符號連結 %x 到資源回收筒 - -Deleting file %x -正在刪除檔案 %x - -Deleting folder %x -正在刪除資料夾 %x - -Deleting symbolic link %x -正在刪除符號連結 %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -資源回收筒不可用於以下資料夾。檔案將被永久刪除替代: - -An exception occurred -發生異常 - -A directory path is expected after %x. -%x 後預期的目錄路徑。 - -Syntax error -語法錯誤 - -Cannot open file %x. -無法開啟檔案 %x。 - -File %x does not contain a valid configuration. -檔案 %x 不包含一個有效的配置。 - -Unequal number of left and right directories specified. -左邊和右邊指定的目錄數不相等。 - -The config file must not contain settings at directory pair level when directories are set via command line. -目錄設定透過命令列時,該設定檔必須不包含配對目錄級別的設定。 - -Directories cannot be set for more than one configuration file. -目錄無法設定多個配置檔。 - -Command line -命令列 - -Syntax: -語法: - -config files -配置檔案 - -directory -目錄 - -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -FreeFileSync .ffs_gui和/或.ffs_batch配置檔案的任意數量。 - -Any number of alternative directories for at most one config file. -備用目錄的任意數量中最多有一個配置文件。 - -A folder input field is empty. -資料夾輸入欄位是空的。 - -The corresponding folder will be considered as empty. -對應的資料夾將視為空的。 - -Cannot find the following folders: -找不到下列資料夾: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -您可以忽略每個資料夾視為空的錯誤。資料夾會在同步過程中自動新建。 - -The following folders have dependent paths. Be careful when setting up synchronization rules: -下列資料夾有相依路徑。請小心設定同步規則: - -File %x has an invalid date. -檔案 %x 的日期無效。 - -Date: -日期: - -Files %x have the same date but a different size. -檔案 %x 日期相同但大小不同。 - -Size: -大小: - -Items differ in attributes only -只有項目的屬性不同 - -Resolving symbolic link %x -正在解析符號連結 %x - -Comparing content of files %x -正在比對檔案内容 %x - -Generating file list... -正在產生檔案清單... - -Starting comparison -開始比對 - -Calculating sync directions... -正在計算同步方向... - -Out of memory. -記憶體不足。 - -Item exists on left side only -只存在於左邊的項目 - -Item exists on right side only -只存在於右邊的項目 - -Left side is newer -左邊較新 - -Right side is newer -右邊較新 - -Items have different content -項目具有不同內容 - -Both sides are equal -兩邊都相同 - -Conflict/item cannot be categorized -衝突/項目不能被分類 - -Copy new item to left -複製新的項目到左邊 - -Copy new item to right -複製新的項目到右邊 - -Delete left item -刪除左邊項目 - -Delete right item -刪除右邊項目 - -Move file on left -移動左邊的檔案 - -Move file on right -移動右邊的檔案 - -Overwrite left item -覆蓋左邊項目 - -Overwrite right item -覆蓋右邊項目 - -Do nothing -維持原狀 - -Update attributes on left -更新左邊的屬性 - -Update attributes on right -更新右邊的屬性 - -Database file %x is incompatible. -資料庫檔案 %x 是不相容的。 - -Initial synchronization: -初始化同步: - -Database file %x does not yet exist. -資料庫檔案 %x 並不存在。 - -Database file is corrupt: -資料庫檔案已損毀: - -Cannot write file %x. -無法寫入檔案 %x。 - -Cannot read file %x. -無法讀取檔案 %x。 - -Database files do not share a common session. -資料庫檔案不會共享一個共同的連線。 - -Searching for folder %x... -正在搜尋資料夾 %x... - -Cannot read file attributes of %x. -無法讀取 %x 的檔案屬性。 - -Cannot get process information. -無法得到進程資訊。 - -Waiting while directory is locked (%x)... -等待同時目錄被鎖定(%x)... - - -1 sec -%x sec - - -%x 秒 - - -Creating file %x -正在新建檔案 %x - -Items processed: -已處理項目: - -Items remaining: -剩餘項目: - -Total time: -全部時間: - - -1 byte -%x bytes - - -%x 個位元組 - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -解析 %x 檔案,第 %y 列,第 %z 行出現錯誤。 - -Cannot set directory lock for %x. -無法對 %x 設定目錄鎖。 - -Scanning: -正在掃瞄: - - -1 thread -%x threads - - -%x 個執行緒 - - -Encoding extended time information: %x -編碼延長時間資訊:%x - -/sec -/秒 - -%x items/sec -%x 個項目/秒 - -Configuration file %x loaded partially only. -只載入設定檔 %x 的一部份。 - -Show in Explorer -在資源管理器中顯示 - -Open with default application -使用預設的應用程式開啟 - -Browse directory -瀏覽目錄 - -Cannot access the Volume Shadow Copy Service. -無法讀取卷影複製服務。 - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -請在此系統上使用FreeFileSync 64位元版本來新建卷影副本。 - -Cannot load file %x. -無法載入檔案 %x。 - -Cannot determine volume name for %x. -無法確定卷名為 %x。 - -Volume name %x is not part of file path %y. -卷名 %x 不是檔案路徑 %y 的一部分。 - -Stop requested: Waiting for current operation to finish... -停止要求:等待目前的操作結束... - -Unable to create timestamp for versioning: -無法新建版本控制的時間戳記: - -Cannot read the following XML elements: -無法讀取下列XML元素: - -&Open... -開啟(&O)... - -Save &as... -另存新檔(&A)... - -&Quit -離開(&Q) - -&Program -程式(&P) - -&View help -檢視說明(&V) - -&About -關於(&A) - -&Help -說明(&H) - -Usage: -使用量: - -1. Select folders to watch. -1. 選擇要監看的資料夾。 - -2. Enter a command line. -2. 輸入命令列。 - -3. Press 'Start'. -3. 按下 '開始'。 - -To get started just import a .ffs_batch file. -開始只是導入一個.ffs_batch檔。 - -Folders to watch: -要監看的資料夾: - -Add folder -新增資料夾 - -Remove folder -移除資料夾 - -Browse -瀏覽 - -Select a folder -選擇一個資料夾 - -Idle time (in seconds): -閒置時間(以秒為單位) - -Idle time between last detected change and execution of command -最後檢測到變更和執行命令之間的閒置時間 - -Command line: -命令列: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -假如要觸發該命令: -- 檔案或子資料夾變更 -- 新資料夾到來(例如插入USB隨身碟) - - -&Start -開始(&S) - -About -關於 - -Build: %x -建立:%x - -All files -所有檔案 - -Automated Synchronization -自動化同步 - -Directory monitoring active -目錄監測啟動 - -Waiting until all directories are available... -等到所有目錄可用... - -Error -錯誤 - -&Restore -還原(&R) - -&Show error -顯示錯誤(&S) - -&Exit -結束(&E) - -Incorrect command line: -不正確的命令列: - -&Retry -重試(&R) - -File content -檔案内容 - -File time and size -檔案大小和日期 - -Two way -雙向 - -Mirror -鏡像 - -Update -更新 - -Custom -自訂 - -Multiple... -多個... - -Moving file %x to %y -正在移動檔案 %x 到 %y - -Moving folder %x to %y -正在移動資料夾 %x 到 %y - -Moving symbolic link %x to %y -正在移動符號連結 %x 到 %y - -Removing old versions... -正在刪除舊版本... - -Creating symbolic link %x -正在新建符號連結 %x - -Creating folder %x -正在新建資料夾 %x - -Overwriting file %x -正在覆蓋檔案 %x - -Overwriting symbolic link %x -正在覆蓋符號連結 %x - -Verifying file %x -正在驗證檔案 %x - -Updating attributes of %x -正在更新 %x 個屬性 - -Cannot find %x. -找不到 %x。 - -Target folder %x already existing. -目標資料夾 %x 已存在。 - -Target folder input field must not be empty. -目標資料夾輸入欄位不能為空。 - -Please enter a target folder for versioning. -請輸入版本控制的目的資料夾。 - -Source folder %x not found. -來源資料夾 %x 找不到。 - -The following items have unresolved conflicts and will not be synchronized: -下列項目有未解決的衝突,將不會同步: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -下列資料夾明顯不同。請確定要進行同步的資料夾匹配正確。 - -Not enough free disk space available in: -沒有足夠的可用空間: - -Required: -必要: - -Available: -可用: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -一個將被修改的資料夾,是多個配對資料夾的一部分。請檢查同步設定。 - -Synchronizing folder pair: -同步配對資料夾: - -Generating database... -正在產生資料庫... - -Creating a Volume Shadow Copy for %x... -正在新建卷影複製為 %x... - -Data verification error: %x and %y have different content. -資料驗證錯誤:%x 和 %y 內容不同! - -job name -作業名稱 - -Synchronization stopped -同步已停止。 - -Synchronization completed with errors -同步完成但有錯誤 - -Synchronization completed with warnings -同步已完成,但出現警告 - -Nothing to synchronize -沒有什麼東西可同步 - -Synchronization completed successfully -同步已成功完成 - -Saving log file %x... -正在儲存日誌檔 %x... - -You can switch to FreeFileSync's main window to resolve this issue. -您可以切換到FreeFileSync的主視窗中來解決此問題。 - -&Don't show this warning again -不要再顯示此警告(&D) - -&Ignore -忽略(&I) - -&Switch -切換(&S) - -Switching to FreeFileSync's main window -切換到FreeFileSync的主視窗 - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - -在 %x 秒自動重試... - - -&Ignore subsequent errors -忽略後續的錯誤(&I) - -Serious Error -嚴重錯誤 - -Check for Program Updates -檢查程式更新 - -A new version of FreeFileSync is available: -FreeFileSync有新版本可用: - -Download now? -要立即下載嗎? - -&Download -下載(&D) - -FreeFileSync is up to date. -FreeFileSync已經是最新版本。 - -Unable to connect to sourceforge.net. -無法連接到sourceforge.net。 - -Cannot find current FreeFileSync version number online. Do you want to check manually? -找不到目前線上FreeFileSync版號!是否要手動檢查? - -Symlink -符號連結 - -Folder -資料夾 - -Full path -完整路徑 - -Name -名稱 - -Relative path -相對路徑 - -Base folder -底層資料夾 - -Size -大小 - -Date -日期 - -Extension -擴展 - -Category -分類 - -Action -動作 - -Drag && drop -拖放 - -Close progress dialog -關閉進度對話框 - -Standby -待機 - -Log off -登出 - -Shut down -關機 - -Hibernate -休眠 - -Alternate comparison settings -備用比對設定 - -Alternate synchronization settings -備用同步設定 - -Local filter -本機篩選器 - -Active -啟用 - -None - - -Remove alternate settings -移除備用設定 - -Clear filter settings -清除篩選器設定 - -Copy -複製 - -Paste -貼上 - -Alternate Comparison Settings -備用比對設定 - -Alternate Synchronization Settings -備用同步設定 - -Local Filter -本機篩選器 - -&New -新增(&N) - -&Save -儲存(&S) - -Save as &batch job... -另存為批次處理作業(&b) - -1. &Compare -1. 比對(&C) - -2. &Synchronize -2. 同步(&S) - -&Global settings -整體設定(&G) - -&Language -語言(&L) - -&Find... -尋找(&F) - -&Export file list... -匯出檔案清單(&E)... - -&Tools -工具(&T) - -&Check now -現在檢查(&C) - -Check &automatically once a week -一週自動檢查一次(&a) - -&Check for new version -檢查新版本(&C) - -Compare -比對 - -Cancel -取消 - -Synchronize -同步 - -Add folder pair -新增配對資料夾 - -Remove folder pair -移除配對資料夾 - -Swap sides -兩邊交換 - -Close search bar -關閉搜尋欄位 - -Find: -尋找: - -Match case -區分大小寫 - -Save as batch job -另存為批次處理作業 - -Hide excluded items -隱藏排除項目 - -Show filtered or temporarily excluded files -顯示已篩選或暫時排除的檔案 - -Number of files and folders that will be created -將被新建的檔案和資料夾數量 - -Number of files that will be overwritten -一些檔案和目錄會被覆蓋 - -Number of files and folders that will be deleted -將被刪除的檔案和資料夾數量 - -Total bytes to copy -要複製的位元組總數 - -Select a variant: -選擇一個變數: - -Identify equal files by comparing modification time and size. -由修改時間和檔案大小比對來判斷相同檔案。 - -Identify equal files by comparing the file content. -由比對檔案內容來判斷相同檔案。 - -Symbolic links: -符號連結: - -More information -詳細資訊 - -OK -確定 - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -識別和傳播兩邊的變更。自動檢測刪除、移動和衝突使用的資料庫。 - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -新建左邊資料夾的鏡像備份,同步後完整匹配右邊資料夾。 - -Copy new and updated files to the right folder. -將新的和已更新的檔案複製到右邊資料夾。 - -Configure your own synchronization rules. -配置你自己的同步規則。 - -Detect moved files -檢測被移動的檔案 - -Requires database files. Not supported by all file systems. -需要資料庫檔案。不支援所有檔案系統。 - -Delete files: -刪除檔案: - -Permanent -常駐 - -Delete or overwrite files permanently -永久刪除或覆蓋檔案 - -Recycle bin -資源回收筒 - -Back up deleted and overwritten files in the recycle bin -在資源回收筒中刪除備份和覆蓋檔案 - -Versioning -版本控制 - -Move files to a user-defined folder -將檔案移動到一個使用者定義的資料夾 - -Naming convention: -命名慣例: - -Show examples -顯示範例 - -Handle errors: -錯誤處理: - -Ignore -忽略 - -Hide all error and warning messages -隱藏所有錯誤和警告訊息 - -Pop-up -彈出視窗 - -Show pop-up on errors or warnings -在彈出視窗上顯示錯誤或警告訊息 - -On completion: -完成後: - -Start synchronization now? -現在開始同步? - -Variant: -變數: - -Statistics -統計 - -&Don't show this dialog again -不要再顯示此對話框(&D) - -Items found: -尋找項目: - -Speed: -速度: - -Time remaining: -剩餘時間: - -Time elapsed: -經過時間: - -Synchronizing... -正在同步... - -Minimize to notification area -最小化到通知區域 - -Close -關閉 - -&Pause -暫停(&P) - -Stop -停止 - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -新建一個批次處理檔,用於無人值守同步。開始,點兩下此檔案,或安排在任務規劃中:%x - -Stop synchronization at first error -在第一個錯誤出現停止同步 - -Show progress dialog -顯示進度對話框 - -Save log: -儲存日誌 - -Limit: -限制: - -Limit maximum number of log files -限制日誌檔的最大數量 - -How can I schedule a batch job? -如何安排批次處理作業? - -&Recycle bin -資源回收筒(&R) - -Delete on both sides -兩邊都刪除 - -Delete on both sides even if the file is selected on one side only -即使只在一邊中選好檔案,還是會刪除兩邊檔案。 - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -選擇從同步中排除某些檔案的篩選器規則。輸入相對於其對應的配對資料夾路徑。 - -Include: -包括: - -Exclude: -排除: - -Time span: -時間間隔: - -File size: -檔案大小: - -Minimum: -最小: - -Maximum: -最大: - -&Clear -清除(&C) - -The following settings are used for all synchronization jobs. -以下設定用於所有同步作業。 - -Fail-safe file copy -故障保護檔案複製 - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -覆蓋目標之前先複製到一個臨時檔案(*.ffs_tmp)。 -即使發生嚴重錯誤時,還能確保一致的狀態。 - - -(recommended) -(建議) - -Copy locked files -複製被鎖定的檔案 - -Copy shared or locked files using the Volume Shadow Copy Service. -共用的副本或鎖定的檔案使用卷影複製服務 - -(requires administrator rights) -(需要管理員權限) - -Copy file access permissions -複製檔案系統權限 - -Transfer file and folder permissions. -傳輸檔案和資料夾的權限。 - -Automatic retry on error: -錯誤時自動重試: - -Retry count: -重試次數: - -Delay (in seconds): -延遲(以秒為單位): - -Customize context menu: -自訂內容功能表: - -Description -描述 - -Restore hidden windows -還原隱藏視窗 - -&Default -預設(&D) - -Source code written in C++ using: -使用C++編寫的原始碼 - -If you like FreeFileSync -如果你喜歡FreeFileSync - -Donate with PayPal -使用PayPal捐款 - -Feedback and suggestions are welcome -歡迎反饋意見和建議 - -Homepage -首頁 - -Email -信箱 - -Published under the GNU General Public License -在GNU通用公共許可證下發佈 - -Many thanks for localization: -非常感謝本地化語系翻譯工作人員: - -Save as Batch Job -另存為批次處理作業 - -Delete Items -刪除項目 - -Global Settings -整體設定 - -Select Time Span -選擇時間間隔 - -Folder Pairs -配對資料夾 - -Find -尋找 - -Overview -摘要 - -Configuration -配置 - -Main Bar -主欄位 - -Filter Files -篩選檔案 - -Select View -選擇檢視 - -Open... -開啟... - -Save -儲存 - -Compare both sides -比對兩邊 - -Comparison settings -比對設定 - -Synchronization settings -同步設定 - -Start synchronization -開始同步 - -Confirm -確認 - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - -你真的要執行 %x 項目的命令 %y 嗎? - - -&Execute -執行(&E) - - -1 directory -%x directories - - -%x 個目錄 - - - -1 file -%x files - - -%x 個檔案 - - - -%y of 1 row in view -%y of %x rows in view - - -視圖中的第 %x 列中的第 %y 列 - - -Set direction: -設定方向: - -multiple selection -多重選擇 - -Include via filter: -透過篩選器包括: - -Exclude via filter: -透過篩選器排除: - -Exclude temporarily -暫時排除 - -Include temporarily -暫時包括 - -Delete -刪除 - -Include all -包括所有 - -Exclude all -排除所有 - -Show icons: -顯示圖示: - -Small - - -Medium - - -Large - - -Select time span... -選擇時間間隔... - -Default view -預設檢視 - -Show "%x" -顯示 "%x" - -Last session -最後連線 - -Folder Comparison and Synchronization -資料夾比對和同步 - -Configuration saved -配置已儲存 - -FreeFileSync batch -FreeFileSync批次處理 - -Do you want to save changes to %x? -是否要儲存變更到 %x? - -Never save &changes -從不保存更改(&c) - -Do&n't save -不儲存(&N) - -Filter -篩選器 - -Show files that exist on left side only -顯示只存在於左邊的檔案 - -Show files that exist on right side only -顯示只存在於右邊的檔案 - -Show files that are newer on left -顯示左邊較新的檔案 - -Show files that are newer on right -顯示右邊較新的檔案 - -Show files that are equal -顯示相同的檔案 - -Show files that are different -顯示不同的檔案 - -Show conflicts -顯示衝突 - -Show files that will be created on the left side -顯示左邊將被新建的檔案 - -Show files that will be created on the right side -顯示右邊將被新建的檔案 - -Show files that will be deleted on the left side -顯示左邊將被刪除的檔案 - -Show files that will be deleted on the right side -顯示右邊將被刪除的檔案 - -Show files that will be overwritten on left side -顯示左邊將被覆蓋的檔案 - -Show files that will be overwritten on right side -顯示右邊將被覆蓋的檔案 - -Show files that won't be copied -顯示將不會被複製的檔案 - -Set as default -設為預設值 - -All folders are in sync -所有資料夾都是同步的 - -Synchronization Settings -同步設定 - -Comparison Settings -比對設定 - -Cannot find %x -找不到 %x - -Comma-separated values -以逗號分隔值 - -File list exported -檔案清單已匯出 - -Searching for program updates... -正在搜尋程式更新... - -Scanning... -正在掃瞄... - -Comparing content... -正在比對内容... - -Info -訊息 - -Warning -警告 - -Paused -已暫停 - -Initializing... -正在初始化... - -Stopped -已停止 - -Completed -已完成 - -&Continue -繼續(&C) - -Log -日誌 - -Today -今日 - -This week -本週 - -This month -本月 - -This year -今年 - -Last x days -最後 x 天 - -Byte -位元組 - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -你真的要將以下項目 %x 移動到資源回收筒嗎? - - -Move -移動 - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -您真的要將下列 %x 項目刪除嗎? - - -Exclude -排除 - -Direct -直接 - -Follow -遵從 - -Copy NTFS permissions -複製NTFS權限 - -Integrate external applications into context menu. The following macros are available: -整合內容功能表中的外部應用程式。可以使用下面的巨集: - -- full file or folder name -- 完整檔案或資料夾名稱 - -- folder part only -- 只有資料夾一部分 - -- Other side's counterpart to %item_path% -- 另一邊對應到 %item_path% - -- Other side's counterpart to %item_folder% -- 另一邊對應到 %item_folder% - -Restore all hidden windows and warnings? -還原所有隱藏的視窗和警告? - -Leave as unresolved conflict -保留給未解決的衝突 - -Replace -取代 - -Move files and replace if existing -如果存在,移動檔案並取代 - -Time stamp -時間戳記 - -Append a timestamp to each file name -將時間戳記附加到每個檔案名稱上 - -File -檔案 - -YYYY-MM-DD hhmmss -YYYY-MM-DD hhmmss - -Files -檔案 - -Items -項目 - -Percentage -百分比 - -Cannot monitor directory %x. -無法監測目錄 %x。 - -Conversion error: -轉換錯誤: - -Cannot delete file %x. -無法刪除目錄 %x。 - -The file is locked by another process: -檔案被另一個進程鎖定: - -Cannot move file %x to %y. -無法移動檔案 %x 到 %y。 - -Cannot delete directory %x. -無法刪除目錄 %x。 - -Cannot write file attributes of %x. -無法寫入 %x 的檔案屬性。 - -Cannot write modification time of %x. -無法寫入 %x 的修改時間。 - -Cannot read security context of %x. -無法讀取 %x 的安全內容。 - -Cannot write security context of %x. -無法寫入 %x 的安全內容。 - -Cannot read permissions of %x. -無法讀取 %x 的權限。 - -Cannot write permissions of %x. -無法寫入 %x 的權限。 - -Cannot create directory %x. -無法新建目錄 %x。 - -Cannot create symbolic link %x. -無法新建符號連結 %x。 - -Cannot find system function %x. -找不到系統函數 %x。 - -Cannot copy file %x to %y. -無法複製檔案 %x 到 %y。 - -Type of item %x is not supported: -項目類型 %x 不被支援: - -Cannot resolve symbolic link %x. -無法解析符號連結 %x。 - -Cannot open directory %x. -無法開啟目錄 %x。 - -Cannot enumerate directory %x. -無法枚舉目錄 %x。 - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -%x 分 - - - -1 hour -%x hours - - -%x 小時 - - - -1 day -%x days - - -%x 天 - - -Unable to register to receive system messages. -無法登錄接收系統訊息。 - -Cannot set privilege %x. -無法設定權限 %x。 - -Unable to suspend system sleep mode. -無法中止系統睡眠模式。 - -Cannot change process I/O priorities. -無法更改I/O處理優先順序。 - -Unable to move %x to the recycle bin. -無法將 %x 移動到資源回收筒。 - -Cannot determine final path for %x. -無法確定最後路徑為 %x。 - -Error Code %x: -錯誤代碼 %x: - diff --git a/BUILD/Languages/croatian.lng b/BUILD/Languages/croatian.lng deleted file mode 100644 index 3bf1410c..00000000 --- a/BUILD/Languages/croatian.lng +++ /dev/null @@ -1,1527 +0,0 @@ -
    - Hrvatski - Miroslav Vranić - hr_HR - flag_croatia.png - 3 - n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2 -
    - -Unable to suspend system sleep mode. - - -Unable to register to receive system messages. - - -Restore all hidden windows and warnings? - - -Move - - -Stopped - - -Serious Error - - -Comparison Settings - - -Synchronization Settings - - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -Select View - - -Filter Files - - -Main Bar - - -Folder Pairs - - -Select Time Span - - -Global Settings - - -Delete Items - - -Save as Batch Job - - -Restore hidden windows - - -Customize context menu: - - -Delay (in seconds): - - -Retry count: - - -Automatic retry on error: - - -Transfer file and folder permissions. - - -(requires administrator rights) - - -Copy shared or locked files using the Volume Shadow Copy Service. - - -(recommended) - - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - - -The following settings are used for all synchronization jobs. - - -Maximum: - - -Minimum: - - -File size: - - -Time span: - - -Exclude: - - -Include: - - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. - - -&Recycle bin - - -How can I schedule a batch job? - - -Limit: - - -Save log: - - -Stop synchronization at first error - - -Stop - - -Variant: - - -Start synchronization now? - - -On completion: - - -Handle errors: - - -Show examples - - -Delete files: - - -Copy new and updated files to the right folder. - - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. - - -More information - - -Symbolic links: - - -Identify equal files by comparing the file content. - - -Identify equal files by comparing modification time and size. - - -Select a variant: - - -Find: - - -Close search bar - - -&Check for new version - - -&Find... - - -Local Filter - - -Alternate Synchronization Settings - - -Alternate Comparison Settings - - -None - - -Active - - -Local filter - - -Alternate synchronization settings - - -Alternate comparison settings - - -Check for Program Updates - - -Retrying operation - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Switching to FreeFileSync's main window - - -You can switch to FreeFileSync's main window to resolve this issue. - - -Synchronization stopped - - -Please enter a target folder for versioning. - - -Incorrect command line: - - -Automated Synchronization - - -&Start - - -Command line: - - -Idle time (in seconds): - - -Folders to watch: - - -&View help - - -Unable to create timestamp for versioning: - - -Stop requested: Waiting for current operation to finish... - - -%x items/sec - - -Both sides have changed since last synchronization. -Obje su strane promjenjene od posljednje sinkronizacije. - -Cannot determine sync-direction: -Ne mogu odrediti smijer sinkronizacije. - -No change since last synchronization. -Nema promjena od zadnje sinkronizacije. - -The database entry is not in sync considering current settings. -Unos podataka nije u sinkronizaciji uzevši u obzir trenutne postavke. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Postavljam zadani sinkronizacijski smijer: Stare datoteke će biti prepisane novim datotekama. - -Checking recycle bin availability for folder %x... -Provjeravam dosupnost koša za smeće za mapu %x... - -Moving file %x to the recycle bin -Premještam datoteku %x u koš - -Moving folder %x to the recycle bin -Premještam datoteku %x u koš - -Moving symbolic link %x to the recycle bin -Premještam simobličnu opveznicu %x u koš - -Deleting file %x -Brisanje datoteke %x - -Deleting folder %x -Brisanje mape %x - -Deleting symbolic link %x -Brisanje simboličnih poveznica %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -Koš nije dosuutpan za sljedeće mape. Zbog toga će datoteke biti trajno obrisane: - -An exception occurred -Dogodilo se izuzeće - -A directory path is expected after %x. -Putanja direktorija se očekuje nakon %x. - -Syntax error -Greška sintakse - -Cannot open file %x. -Ne mogu otvoriti datoteku %x. - -Error -Greška - -File %x does not contain a valid configuration. -Datoteka %x ne sadrži valjanu konfiguraciju - -Unequal number of left and right directories specified. -Neravnomjeran broj lijevih i desnih direkotrija specificiran. - -The config file must not contain settings at directory pair level when directories are set via command line. -Config datoteka ne smije sadržavati postavke na direktorij par razini ukoliko su direktoriji upisani preko naredbene linije. - -Warning -Oprez - -Directories cannot be set for more than one configuration file. -Direkotrij nemože biti postavljen za više od jedne konfigracijske datoteke. - -Syntax: -Sintaksa: - -config files -config datoteke - -directory -direktorij - -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 directories for at most one config file. -Bilo koji broj alternativnih mapa za najviše jedanu config datoteku. - -Command line -Naredbena linija - -A folder input field is empty. -Polje za odabir foldera je prazno. - -The corresponding folder will be considered as empty. -Odgovarajuća mapa će se smatrati prazna. - -Cannot find the following folders: -Ne mogu pronaći slijedeće mape: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -Možete ignorirati ovu pogrešku te uzeti u obzir svaku mapu praznom. Mape se tada stvaraju automatski tokom sinkronizacije. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -Sljedeće mape imaju zavisnu putanju. Budite pažljivi prilikom postavljanja pravila za sinkronizaciju: - -File %x has an invalid date. -Datoteka %x ima nevaljan datum. - -Date: -Datum: - -Files %x have the same date but a different size. -Datoteke %x imaju isti datum ali drugačiju veličinu. - -Size: -Veličina: - -Items differ in attributes only -Stavke se razlikuju samo u atributima - -Resolving symbolic link %x -Rješavam simboličku vezu %x - -Comparing content of files %x -Uspoređujem sadržaj datoteka %x - -Generating file list... -Generiram listu datoteka... - -Starting comparison -Počinjem usporedbu - -Calculating sync directions... -Izračunavam smjerove sinkronizacije... - -Out of memory. -Nedostatak memorije. - -Item exists on left side only -Stavka postoji samo na lijevoj strani - -Item exists on right side only -Stavka postoji samo na desnoj strani - -Left side is newer -Lijeva strana je novija - -Right side is newer -Desna strana je novija - -Items have different content -Stavke imaju različit sadržaj - -Both sides are equal -Obje strane su jednake - -Conflict/item cannot be categorized -Sukob/stavka ne može biti razvrstana - -Copy new item to left -Kopiraj novu stavku lijevo - -Copy new item to right -Kopiraj novu stavku desno - -Delete left item -Izbriši lijevu stavku - -Delete right item -Izbriši desnu stavku - -Move file on left -Premjesti datoteku lijevo - -Move file on right -Premjest datoteku desno - -Overwrite left item -Prepiši lijevu stavku - -Overwrite right item -Prepiši desnu stavku - -Do nothing -Ne radi ništa - -Update attributes on left -Osvježi atribute lijevo - -Update attributes on right -Osvježi atribute desno - -Database file %x is incompatible. -Datoteka baze %x je nekompatibilna - -Initial synchronization: -Početna sinkronizacija: - -Database file %x does not yet exist. -Datoteka baze %x još ne postoji - -Database file is corrupt: -Baza je oštećena: - -Cannot write file %x. -Ne mogu zapisati datoteku %x. - -Cannot read file %x. -Ne mogu čitati datoteku %x. - -Database files do not share a common session. -Datoteke baze ne dijele zajednički protokol. - -Searching for folder %x... -Tražim mapu %x... - -Cannot read file attributes of %x. -Ne mogu pročitati osobine od %x. - -Cannot get process information. -Ne mogu dobit informacije o procesu - -Waiting while directory is locked (%x)... -Čekam dok se direktorij zaključava (%x)... - - -1 sec -%x sec - - -%x sek -%x sek -%x sek - - -Creating file %x -Izrađujem datoteku %x - -Items processed: -Obrađeni elementi: - -Items remaining: -Preostale stavke: - -Total time: -Ukupno vrijeme: - - -1 byte -%x bytes - - -%x bajt -%x bajta -%x bajtova - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Greška u analizi datoteke %x, red %y, stupac %z. - -Cannot set directory lock for %x. -Ne mogu zaključiti mapu %x. - -Scanning: -Pretražujem: - - -1 thread -%x threads - - -%x nit -%x niti -%x niti - - -Encoding extended time information: %x -:Spremam informacije o vremenu %x - -/sec -/sek - -Configuration file %x loaded partially only. -Datoteka postavki %x učitana samo djelomično - -Show in Explorer -Prikaži u Exploreru - -Open with default application -Otvori s zadanom aplikacijom - -Browse directory -Odaberi direktorij - -Cannot access the Volume Shadow Copy Service. -Ne mogu pristupiti Voulme Shadow Copy servisu. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Molimo koristite FreeFileSync 64-bitnu verziju za izradu shadow kopija na ovom sustavu - -Cannot load file %x. -Ne mogu učitati datoteku %x. - -Cannot determine volume name for %x. -Ne mogu ustanoviti ime volumen for %x. - -Volume name %x is not part of file path %y. -Ime voumena %x nije dio puta datotke %y. - -Cannot read the following XML elements: -Ne mogu čitati slijedeće XML elemente - -&Open... -&Otvori... - -Save &as... -Spremiti &kao... - -&Quit -&Izlaz - -&Program -&Program - -&About -&O programu - -&Help -&Pomoć - -Usage: -Uporaba: - -1. Select folders to watch. -1. Odaberite mape za nadzirati - -2. Enter a command line. -2. Unesite naredbu. - -3. Press 'Start'. -3. Pretisnite 'Start'. - -To get started just import a .ffs_batch file. -Za započeti uvezite .ffs slijednu datoteku. - -Add folder -Dodaj mapu - -Remove folder -Ukloni mapu - -Browse -Odaberi - -Select a folder -Izaberite mapu - -Idle time between last detected change and execution of command -Vrijeme čekanja između zadnje prepoznate promjene i izvršenja naredbe - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -Naredba će biti pokrenuta ako se: -- datoteke ili podmape promijene -- nove mape pojave (npr. umetanje USB sticka) - - -&Retry -&Ponovi - -Cancel -Odustani - -Build: %x -Inačnica: %x - -About -O programu - -All files -Sve datoteke - -Directory monitoring active -Nadzor direktorija je aktivan - -Waiting until all directories are available... -Čekanje na dostupnost svih direktorija... - -&Restore -&Vrati - -&Show error -&Prikaži grešku - -&Exit -&Izlaz - -File content -Sadržaj datoteke - -File time and size -Vrijeme i veličina datoteke - -Two way -Dvosmjerno - -Mirror -Zrcalno - -Update -Ažuriraj - -Custom -Uobičajeno - -Multiple... -Mnogostruko... - -Moving file %x to %y -Premještam datoteku %x u %y - -Moving folder %x to %y -Premještam mapu %x u %y - -Moving symbolic link %x to %y -Premještam simboličnu poveznicu %x u %y - -Removing old versions... -Uklanjam starije verzije... - -Creating symbolic link %x -Izrađujem simboličnu poveznicu %x - -Creating folder %x -Izrađujem mapu %x - -Overwriting file %x -Prepisujem datoteku %x - -Overwriting symbolic link %x -Prepisujem simboličnu poveznicu %x - -Verifying file %x -Provjeravam datoteku %x - -Updating attributes of %x -Obnavljam atribute od %x - -Cannot find %x. -Ne mogu pronaći %x. - -Target folder %x already existing. -Odredišna mapa %x već postoji. - -Target folder input field must not be empty. -Odredišna mapa ne može biti prazna. - -Source folder %x not found. -Izvorna mapa %x nije pronađena. - -The following items have unresolved conflicts and will not be synchronized: -Slijedeće stavke imaju nedefiniranih sukoba te neće biti sinkronizirane: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -Navedene mape se značajno razlikuju. Prvojerite dali uspoređujete ispravne mape za sinkronizaciju. - -Not enough free disk space available in: -Nedovoljno prostora na disku: - -Required: -Potrebno: - -Available: -Dostupno: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Mapa će biti uređena koja je dio više pari mapa. Molimo provjerite sinkronizacijske postavke. - -Synchronizing folder pair: -Par sinkronizirane mape: - -Generating database... -Izrađujem bazu podataka... - -Creating a Volume Shadow Copy for %x... -Lreiram Volume Shadow Copy za %x... - -Data verification error: %x and %y have different content. -Greška pri provjeri podataka: %x i %y imaju različit sadržaj. - -job name -Ime zadatka - -Synchronization completed with errors -Sinkronizacija završena s greškama - -Synchronization completed with warnings -Sinkronizacija završena s upozorenjima - -Nothing to synchronize -Ništa za sinkronizirati - -Synchronization completed successfully -Sinkronizacija uspješno dovršena - -Saving log file %x... -Spremam izješće %x... - -A new version of FreeFileSync is available: -Nova verzija FreeFileSync je dostupna: - -Download now? -Preuzeti sada? - -&Download -&Preuzmi - -FreeFileSync is up to date. -FreeFileSync je ažuriran. - -Information -Informacija - -Unable to connect to sourceforge.net. -Ne mogu se povezati na sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Ne mogu pronaći trenutnu verziju FreeFileSync-a online. Dali želite ručno potražiti? - -Symlink -Poveznica simbola - -Folder -Mapa - -Full path -Puna putanja - -Name -Naziv - -Relative path -Relativna Putanja - -Base folder -Izvorna mapa - -Size -Veličina - -Date -Datum - -Extension -Ekstenzija - -Category -Kategorija - -Action -Radnja - -Drag && drop -Povuci && ispusti - -Close progress dialog -Zatvori prozor zadatka - -Standby -Spavaj - -Log off -Odlogiraj se - -Shut down -Isključi računalo - -Hibernate -Hibernacija - -Remove alternate settings -Ukloni alternativne postavke - -Clear filter settings -Počisti postavke filtera - -Copy -Kopiraj - -Paste -Zalijepi - -&New -&Novo - -&Save -&Spremi - -Save as &batch job... -Spremi kao &slijedni zadatak... - -1. &Compare -1. &Usporedi - -2. &Synchronize -2. &Sinkroniziraj - -&Global settings -&Globalne postavke - -&Language -&Jezik - -&Export file list... -&Izvoz liste datoteka... - -&Tools -&Alati - -&Check now -&Provjeri sada - -Check &automatically once a week -Provjeri &automatski jednom tjedno - -Compare -Usporedi - -Synchronize -Sinkroniziraj - -Add folder pair -Dodaj mapu - -Remove folder pair -Odstrani mapu - -Swap sides -Zamjeni strane - -Match case -Poklopi se - -Save as batch job -Spremi kao slijedni zadatak - -Hide excluded items -Sakrij isključene stavke - -Show filtered or temporarily excluded files -Prikaži filtrirane ili privremeno izdvojene datoteke - -Number of files and folders that will be created -Broj datoteka i foldera koji će se stvoriti - -Number of files that will be overwritten -Broj datoteka koje će biti prepisane - -Number of files and folders that will be deleted -Broj datoteka i mapa koje će se izbrisati - -Total bytes to copy -Ukupno bajta za kopirati - -OK -U redu - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Pronađi i izvrši izmjene na obje strane. Izbrisano, premješteno te sukobe se otkrije automatski pomoću baze. - -Configure your own synchronization rules. -Konfigurirajte vaša vlastita sinkronizacijska pravila. - -Detect moved files -Proađene preseljene datoteke - -Requires database files. Not supported by all file systems. -Zahtjevaju se datoteke baze. Nisu podržai svi datotečni sustavi. - -Permanent -Trajno - -Delete or overwrite files permanently -Trajno izbriši ili prepiši datoteke - -Recycle bin -Koš - -Back up deleted and overwritten files in the recycle bin -Spremi obrisae ili prepisane datoteke u koš - -Versioning -Označi - -Move files to a user-defined folder -Premjesti datoteke u mapu određenu od korsnika - -Naming convention: -Pravilo imena: - -Ignore -Ignoriraj - -Hide all error and warning messages -Sakrij sve greške i upozorenja - -Pop-up -Pop-up - -Show pop-up on errors or warnings -Prikaži skočni prozor pri greškama i upozorenjima - -Statistics -Statistika - -&Don't show this dialog again -&Nemoj više prikazivati ovaj dijaloški prozor - -Items found: -Pronađene stavke: - -Speed: -Brzina: - -Time remaining: -Preostalo vremena: - -Time elapsed: -Proteklo vremena: - -Synchronizing... -Sinkroniziram... - -Minimize to notification area -Spusti u notifikacije - -Close -Zatvori - -&Pause -&Pauziraj - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Stvaranje batch datoteka za sinkronizaciju bez nadzora. Za početak, dvaput kliknite na ovu datoteku ili planirajte u planeru zadataka: %x - -Show progress dialog -Prikaži napredak - -Limit maximum number of log files -Ograniči maksimalan broj izvješća - -Delete on both sides -Izbriši na obje strane - -Delete on both sides even if the file is selected on one side only -Izbriši na obje strane iako je označena datoteka samo na jednoj strani - -&Clear -&Brisati - -Fail-safe file copy -Kopiranje zaštićeno od grešaka - -Copy locked files -Kopiraj zaključane datoteke - -Copy file access permissions -Kopiraj datotečna dopuštenja - -Description -Opis - -&Default -&Zadano - -Source code written in C++ using: -Izvorni kod napisan u C++ uz korištenje: - -If you like FreeFileSync -Ako volite FreeFileSync - -Donate with PayPal -Doniraj s PayPal - -Feedback and suggestions are welcome -Povratne informacije i prijedlozi su dobrodošli - -Homepage -Početna stranica - -Email -Email - -Published under the GNU General Public License -Objavljeno pod licencom GNU General Public - -Many thanks for localization: -Velike zahvale idu: - -Find -Pronađi - -Overview -Pregled - -Configuration -Postavke - -Open... -Otvori... - -Save -Spremi - -Compare both sides -Usporedi obje strane - -Comparison settings -Postavke usporedbe - -Synchronization settings -Postavke sinkronizacije - -Start synchronization -Započni sinkronizaciju - -Confirm -Potvrdi - -&Execute -&Izvrši - - -1 directory -%x directories - - -%x direktorij -%x direktorija -%x direktorija - - - -1 file -%x files - - -%x datoteka -%x datoteke -%x datoteka - - - -%y of 1 row in view -%y of %x rows in view - - -%y od %x reda u prikazu -%y od %x reda u prikazu -%y od %x reda u prikazu - - -Set direction: -Odaberi smijer: - -multiple selection -višestruki odabir - -Include via filter: -Pridruži preko filtera: - -Exclude via filter: -Isključi preko filtra: - -Exclude temporarily -Izuzmi privremeno - -Include temporarily -Trenutno uključi - -Delete -Izbriši - -Include all -Uključi sve - -Exclude all -Izdvoji sve - -Show icons: -Prikaži ikone: - -Small -Malo - -Medium -Srednje - -Large -Veliko - -Select time span... -Izaberite vremenski raspon... - -Default view -Standardni prikaz - -Show "%x" -Prikaži "%x" - -Last session -Zadnja sesija - -Folder Comparison and Synchronization -Usporedba i sinkronizacija mapa - -Configuration saved -Postavke spremljene - -FreeFileSync batch -FreeFileSync slijedni zadatak - -Do you want to save changes to %x? -Da li želite spremiti izmjene za %x? - -Do&n't save -&Nemoj spremiti - -Never save &changes -Nikad ne spremaj &promjene - -Filter -Filtriranje - -Show files that exist on left side only -Prikaži datoteke koje postoje samo na lijevoj strani - -Show files that exist on right side only -Prikaži datoteke koje postoje samo na desnoj strani - -Show files that are newer on left -Prikaži datoteke koje su novije lijevo - -Show files that are newer on right -Prikaži datoteke koje su novije desno - -Show files that are equal -Prikaži jednake datoteke - -Show files that are different -Prikaži datoteke koje su različite - -Show conflicts -Prikaži spore - -Show files that will be created on the left side -Prikaži datoteke koje će biti napravljene na lijevoj strani - -Show files that will be created on the right side -Prikaži datoteke koje će biti napravljene na desnoj strani - -Show files that will be deleted on the left side -Prikaži datoteke koje će biti izbrisane na lijevoj strani - -Show files that will be deleted on the right side -Prikaži datoteke koje će biti izbrisane na desnoj strani - -Show files that will be overwritten on left side -Prikaži datoteke koje će biti prepisane na lijevoj strani - -Show files that will be overwritten on right side -Prikaži datoteke koje će biti prepisane na desnoj strani - -Show files that won't be copied -Prikaži datoteke koje neće biti kopirane - -Set as default -Postavi kao zadano - -All folders are in sync -Sve mape su u sinkronizaciji - -Cannot find %x -Nemogu pronaći %x - -Comma-separated values -Zarezom odvojene vrijednosti - -File list exported -Datotečna lista izvezena - -Searching for program updates... -Pretražujem ažuriranja programa... - -&Ignore subsequent errors -&Zanemari naknadne pogreške - -&Ignore -&Ignoriraj - -&Don't show this warning again -&Ne prikazuj više ovo upozorenje. - -&Switch -&Zamjeni - -&Yes -&Da - -&No -&Ne - -Scanning... -Pregledajem... - -Comparing content... -Uspoređujem sadržaj... - -Info -Info - -Paused -Pauzirano - -Initializing... -Učitavam - -Completed -Završeno - -&Continue -&Nastavi - -Log -Dnevnik - -Today -Danas - -This week -Ovaj tjedan - -This month -Ovaj mjesec - -This year -Ove godine - -Last x days -Zadnjih x dana - -Byte -Bajt - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Dali stvarno želite premjestiti sljedeću %x stavku u koš? -Dali stvarno želite premjestiti sljedeće %x stavke u koš? -Dali stvarno želite premjestiti sljedećih %x stavki u koš? - - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Dali stvarno želite obrisati sljedeću %x stavku? -Dali stvarno želite obristi sljedeće %x stavke? -Dali stvarno želite obristi sljedećih %x stavki? - - -Exclude -Isključi - -Direct -Neposredno - -Follow -Slijedi - -Copy NTFS permissions -Kopiraj NTFS dopuštenja - -Integrate external applications into context menu. The following macros are available: -Integriraj vanjske aplikacije u kontekstni meni. Sljedeći makroi su dostupni: - -- full file or folder name -- cijelo ime datoteke ili mape - -- folder part only -- samo mapa - -- Other side's counterpart to %item_path% -- Duplikat s druge strane u %item_path% - -- Other side's counterpart to %item_folder% -- Duplikat s druge strane u %item_folder% - -Leave as unresolved conflict -Ostavi kao neriješeni sukob - -Replace -Zamjeni - -Move files and replace if existing -Premjesti datoteke i zamjeni ako već postoji - -Time stamp -Vremenska oznaka - -Append a timestamp to each file name -Dodaj vremensku oznaku svakom imenu datoteke - -File -Datoteka - -YYYY-MM-DD hhmmss -GGGG-MM-DD hhmmss - -Files -Datoteke - -Items -Stavke - -Percentage -Postotak - -Cannot monitor directory %x. -Ne mogu posmatrati direktorij %x. - -Conversion error: -Greška pri pretvorbi: - -Cannot delete file %x. -Ne mogu izbrisati datoteku %x. - -The file is locked by another process: -Datoteka je blokirana drugim procesom: - -Cannot move file %x to %y. -Ne mogu premjestiti datoteku %x u %y. - -Cannot delete directory %x. -Ne mogu izbrisati mapu %x. - -Cannot write file attributes of %x. -Ne mogu zapisati svojstva od %x. - -Cannot write modification time of %x. -Ne mogu zapisati vrijeme izmjene %x. - -Cannot read security context of %x. -Ne mogu čitati zaštićeni sadržaj %x. - -Cannot write security context of %x. -Ne mogu zapisati zaštićeni sadržaj %x. - -Cannot read permissions of %x. -Ne mogu čitati dopuštenja od %x. - -Cannot write permissions of %x. -Ne mogu zapisati dopuštenja za %x. - -Cannot create directory %x. -Ne mogu izraditi mapu %x. - -Cannot create symbolic link %x. -Ne mogu stvoriti simboličku vezu %x. - -Cannot find system function %x. -Ne mogu pronaći sistemsku funkciju %x. - -Cannot copy file %x to %y. -Ne mogu kopirati datoteku %x na %y. - -Type of item %x is not supported: -Tip stavke %x koji nije podržan: - -Cannot resolve symbolic link %x. -Ne mogu odrediti poveznicu %x. - -Cannot open directory %x. -Ne mogu otvoriti mapu %x. - -Cannot enumerate directory %x. -Ne mogu izlistati mapu %x. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -%x min -%x min -%x min - - - -1 hour -%x hours - - -%x sat -%x sata -%x sati - - - -1 day -%x days - - -%x dan -%x dana -%x dana - - -Cannot set privilege %x. -Ne mogu postaviti prava za %x. - -Cannot change process I/O priorities. -Ne može se promjeniti proces I/O prioriteta - -Unable to move %x to the recycle bin. -Nije moguće prebaciti %x u koš za smeće. - -Cannot determine final path for %x. -Ne mogu odrediti završnu putanju za %x. - -Error Code %x: -Pogreška broj %x: - diff --git a/BUILD/Languages/czech.lng b/BUILD/Languages/czech.lng deleted file mode 100644 index 1e49b62e..00000000 --- a/BUILD/Languages/czech.lng +++ /dev/null @@ -1,1527 +0,0 @@ -
    - Čeština - ViCi - cs_CZ - flag_czech_republic.png - 3 - n==1 ? 0 : n>=2 && n<=4 ? 1 : 2 -
    - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -Retrying operation - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Both sides have changed since last synchronization. -Došlo ke změně obou stran od poslední synchronizace. - -Cannot determine sync-direction: -Nelze určit směr synchronizace: - -No change since last synchronization. -Žádné změny od poslední synchronizace. - -The database entry is not in sync considering current settings. -Databázové položky nejsou podle aktuální konfigurace synchronní. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Nastaven výchozí způsob synchronizace: Staré soubory budou nahrazeny novými. - -Checking recycle bin availability for folder %x... -Kontrola Koše na složku %x ... - -Moving file %x to the recycle bin -Přesouvání souboru %x do Koše - -Moving folder %x to the recycle bin -Přesouvání adresáře %x do Koše - -Moving symbolic link %x to the recycle bin -Přesouvání symbolického odkazu %x do Koše - -Deleting file %x -Mazání souboru %x - -Deleting folder %x -Mazání adresáře %x - -Deleting symbolic link %x -Mazání symbolického odkazu %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -Nelze použít Koš pro následující složky. Soubory tak budou odstraněny permanentně: - -An exception occurred -Vyskytla se chyba - -A directory path is expected after %x. -Očekávána adresářová cesta po %x. - -Syntax error -Chyba syntaxe - -Cannot open file %x. -Nelze otevřít soubor %x. - -Error -Chyba - -File %x does not contain a valid configuration. -Soubor %x neobsahuje platnou konfiguraci. - -Unequal number of left and right directories specified. -Zjištěn nestejný počet levých a pravých adresářů. - -The config file must not contain settings at directory pair level when directories are set via command line. -Konfigurační soubor nesmí obsahovat nastavení na úrovni adresářových párů pokud je zadán přes příkazovou řádku. - -Warning -Varování - -Directories cannot be set for more than one configuration file. -Adresáře nemohou obsahovat více než jeden konfigurační soubor. - -Syntax: -Syntaxe: - -config files -konfigurační soubory - -directory -adresář - -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 directories for at most one config file. -Libovolný počet alternativních adresářů na alespoň jednu konfiguraci. - -Command line -Příkazová řádka - -A folder input field is empty. -Není zadána vstupní složka. - -The corresponding folder will be considered as empty. -Odpovídající složka bude považována za prázdnou. - -Cannot find the following folders: -Nelze najít následující složky: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -Tuto chybu můžete přeskočit a považovat neexistující složku za prázdnou. Složka pak bude vytvořena při synchronizaci automaticky. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -Následující složky mají navzájem závislé cesty! Buďte opatrní s definicí synchronizačních pravidel: - -File %x has an invalid date. -Soubor %x má chybné datum. - -Date: -Čas: - -Files %x have the same date but a different size. -Soubory %x mají stejné datum a čas ale rozdílnou velikost. - -Size: -Velikost: - -Items differ in attributes only -Položky se liší pouze ve vlastnostech - -Resolving symbolic link %x -Hledání odkazu symbolického zástupce %x - -Comparing content of files %x -Porovnávání obsahu souborů %x - -Generating file list... -Vytváření seznamu souborů... - -Starting comparison -Začátek porovnávání - -Calculating sync directions... -Příprava adresářů synchronizace... - -Out of memory. -Nedostatek pracovní paměti. - -Item exists on left side only -Položky existující pouze vlevo - -Item exists on right side only -Položky existující pouze vpravo - -Left side is newer -Vlevo je novější - -Right side is newer -Vpravo je novější - -Items have different content -Obsah položek je rozdílný - -Both sides are equal -Obě strany jsou shodné - -Conflict/item cannot be categorized -Konflikty/položky které nelze zařadit - -Copy new item to left -Kopírovat novou položku doleva - -Copy new item to right -Kopírovat novou položku doprava - -Delete left item -Smazat položku zleva - -Delete right item -Smazat položku zprava - -Move file on left -Přesunout soubor nalevo - -Move file on right -Přesunout soubor napravo - -Overwrite left item -Přepsat levou položku tou zprava - -Overwrite right item -Přepsat pravou položku tou zleva - -Do nothing -Nic nedělat - -Update attributes on left -Nastavit vlastnosti vlevo - -Update attributes on right -Nastavit vlastnosti vpravo - -Database file %x is incompatible. -Chybný formát databáze %x. - -Initial synchronization: -Prvotní synchronizace: - -Database file %x does not yet exist. -Databázový soubor %x neexistuje. - -Database file is corrupt: -Databáze je poškozená: - -Cannot write file %x. -Nelze zapsat soubor %x. - -Cannot read file %x. -Nelze číst soubor %x. - -Database files do not share a common session. -Databázové soubory nejsou navzájem komplementární. - -Searching for folder %x... -Otevírání složky %x - -Cannot read file attributes of %x. -Nelze číst atributy souboru %x. - -Cannot get process information. -Nelze získat informace procesu. - -Waiting while directory is locked (%x)... -Čekání na uzamčení adresáře (%x) - - -1 sec -%x sec - - -1 sekunda -%x sekundy -%x sekund - - -Creating file %x -Vytváření souboru %x - -Items processed: -Zpracováno položek: - -Items remaining: -Zbývá položek: - -Total time: -Celkový čas: - - -1 byte -%x bytes - - -%x B -%x B -%x B - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Chyba zpracování souboru %x: na řádku %y ve sloupci %z - -Cannot set directory lock for %x. -Nelze nastavit zámek adresáře %x. - -Scanning: -Zpracováváno: - - -1 thread -%x threads - - -1 proces -%x procesy -%x procesů - - -Encoding extended time information: %x -Zpracování rozšířené informace o čase: %x - -/sec -/s - -%x items/sec -%x položek/s - -Configuration file %x loaded partially only. -Konfigurace ze souboru %x byla načtena jen částečně. - -Show in Explorer -Zobrazit v Průzkumníkovi - -Open with default application -Otevřít výchozí aplikací - -Browse directory -Procházet adresář - -Cannot access the Volume Shadow Copy Service. -Nepodařil se přístup ke službě Stínové kopie. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Prosím použijte FreeFileSync 64-bitovou verzi pro použití služby Stínové kopie. - -Cannot load file %x. -Nelze načíst soubor %x. - -Cannot determine volume name for %x. ->Nelze zjistit jméno jednotky souboru %x. - -Volume name %x is not part of file path %y. -Název disku %x není součástí cesty souboru %y. - -Stop requested: Waiting for current operation to finish... -Zastavování: Čekání na dokončení právě probíhající operace... - -Unable to create timestamp for versioning: -Nelze vytvořit časové značky verzování: - -Cannot read the following XML elements: -Nelze načíst následující XML elementy: - -&Open... -&Otevřít... - -Save &as... -Uložit &jako... - -&Quit -U&končit - -&Program -&Nástroje - -&View help -&Nápověda (pouze anglicky) - -&About -O &Programu - -&Help -&Pomoc - -Usage: -Použití: - -1. Select folders to watch. -1. Vyberte složku ke sledování. - -2. Enter a command line. -2. Zadejte příkazovou řádku. - -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 - -Folders to watch: -Sledovaná složka: - -Add folder -Přidat adresář - -Remove folder -Odstranit adresář - -Browse -Procházet - -Select a folder -Vyberte adresář - -Idle time (in seconds): -Prodleva (v sekundách): - -Idle time between last detected change and execution of command -Prodleva mezi zjištěním poslední změny a spuštěním příkazu - -Command line: -Příkazová řádka: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -Příkaz je spuštěn když: -- dojde ke změně v souboru nebo ve složce -- je zjištěna nová složka (např. vložením USB disku) - - -&Start -&Start - -&Retry -&Opakovat - -Cancel -Zrušit - -Build: %x -Verze: %x - -About -O programu - -All files -Všechny soubory - -Automated Synchronization -Automatická synchronizace - -Directory monitoring active -Sledování adresářů je aktivní - -Waiting until all directories are available... -Čekání na nedostupné adresáře... - -&Restore -&Obnovit - -&Show error -&Zobrazit chybu - -&Exit -&Konec - -Incorrect command line: -Neplatný příkaz: - -File content -Podle obsahu souboru - -File time and size -Podle velikosti a data souboru - -Two way -Databáze - -Mirror -Zrcadlení - -Update -Aktualizace - -Custom -Vlastní - -Multiple... -Různé... - -Moving file %x to %y -Přesouvání souboru %x do %y - -Moving folder %x to %y -Přesouvání adresáře %x do %y - -Moving symbolic link %x to %y -Přesouvání symbolického odkazu %x do %y - -Removing old versions... -Odstraňování starých verzí... - -Creating symbolic link %x -Vytváření symbolického odkazu %x - -Creating folder %x -Vytváření adresáře %x - -Overwriting file %x -Přepisování souboru %x - -Overwriting symbolic link %x -Přepisování symbolického odkazu %x - -Verifying file %x -Kontrola souboru %x - -Updating attributes of %x -Aktualizace atributů souboru %x - -Cannot find %x. -Nelze najít %x. - -Target folder %x already existing. -Cílová složka %x již existuje. - -Target folder input field must not be empty. -Cílová složka nesmí být prázdná. - -Please enter a target folder for versioning. -Prosím zadejte cílovou složku pro verzování. - -Source folder %x not found. -Zdrojovou složku %x nelze najít. - -The following items have unresolved conflicts and will not be synchronized: -Následující položky jsou nevyřešené konflikty a nebudou synchronizovány: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -Nsledující složky se významně liší. Zkontrolujte zda porovnáváte správnou dvojici složek pro synchronizaci. - -Not enough free disk space available in: -Nedostatek místa na disku: - -Required: -Požadováno - -Available: -K dispozici - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Bude změněna složka, která je součástí adresářového páru vícenásobného porovnání. Prosím zkontrolujte si nastavení synchronizace. - -Synchronizing folder pair: -Zpracovávání páru složek: - -Generating database... -Vytváření databáze... - -Creating a Volume Shadow Copy for %x... -Vytváření Stínové kopie pro %x... - -Data verification error: %x and %y have different content. -Chyba porovnání dat: %x má jiný obsah než %y - -job name -název úlohy - -Synchronization stopped -Synchronizace zastavena - -Synchronization completed with errors -Synchronizace dokončena s chybami - -Synchronization completed with warnings -Synchronizace dokončena s varováními - -Nothing to synchronize -Není co synchronizovat - -Synchronization completed successfully -Synchronizace dokončena úspěšně - -Saving log file %x... -Ukládání žurnálu %x... - -You can switch to FreeFileSync's main window to resolve this issue. -K odstranění tohoto problému se přepněte do hlavního okna FreeFileSync. - -Switching to FreeFileSync's main window -Přepínání do hlavního okna FreeFileSync - -A new version of FreeFileSync is available: -Je dostupná novější verze FreeFileSync: - -Download now? -Stáhnout nyní? - -Check for Program Updates -Hledání aktualizací programu - -&Download -&Stahování - -FreeFileSync is up to date. -FreeFileSync je aktuální. - -Information -Informace - -Unable to connect to sourceforge.net. -Není možné se připojit k sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Současná verze FreeFileSync nebyla nalezena online! Chcete verzi zkontrolovat ručně? - -Symlink -Symlink - -Folder -Složka - -Full path -Plná cesta - -Name -Jméno - -Relative path -Relativní cesta - -Base folder -Složka - -Size -Velikost - -Date -Čas - -Extension -Přípona - -Category -Kategorie - -Action -Akce - -Drag && drop -Přetáhni sem && pusť - -Close progress dialog -Zavřít průběh zpracování - -Standby -Přepnout do úsporného režimu - -Log off -Odhlásit uživatele - -Shut down -Vypnout počítač - -Hibernate -Přepnout do režimu spánku - -Alternate comparison settings -Jiné nastavení porovnání - -Alternate synchronization settings -Jiné nastavení synchronizace - -Local filter -Místní filtr - -Active -Zapnutý - -None -Žádný - -Remove alternate settings -Zrušit jiné nastavení - -Clear filter settings -Zrušit nastavení filtru - -Copy -Kopírovat - -Paste -Vložit - -Alternate Comparison Settings -Jiné nastavení porovnání - -Alternate Synchronization Settings -Jiné nastavení synchronizace - -Local Filter -Místní filtr - -&New -&Nový - -&Save -&Uložit - -Save as &batch job... -Uložit jako &dávku - -1. &Compare -1. &Porovnat - -2. &Synchronize -2. &Synchronizovat - -&Global settings -&Nastavení programu - -&Language -&Jazyk - -&Find... -&Najít... - -&Export file list... -&Exportovat seznam souborů... - -&Tools -&Upřesnit - -&Check now -Zkontrolovat &nyní - -Check &automatically once a week -Kontrolovat &automaticky jednou týdně - -&Check for new version -&Zkontrolovat aktualizace - -Compare -Porovnání - -Synchronize -Synchronizace - -Add folder pair -Přidat adresář pro porovnání - -Remove folder pair -Odstranit dvojici adresářů - -Swap sides -Změna stran - -Close search bar -Zavřít hledání - -Find: -Najít: - -Match case -Rozlišovat malá a velká písmena - -Save as batch job -Uložit jako dávku - -Hide excluded items -Skrýt vynechané položky - -Show filtered or temporarily excluded files -Zobrazit filtrované nebo dočasně vynechané soubory - -Number of files and folders that will be created -Počet souborů a složek k vytvoření - -Number of files that will be overwritten -Počet souborů a adresářů k přepsání - -Number of files and folders that will be deleted -Počet souborů a složek ke smazání - -Total bytes to copy -Celkový objem dat - -Select a variant: -Výběr varianty: - -Identify equal files by comparing modification time and size. -Rozpoznat shodné soubory porovnáním jejich data změny a velikosti. - -Identify equal files by comparing the file content. -Rozpoznat shodné soubory porovnáním jejich obsahu. - -Symbolic links: -Symbolickéh odkazy: - -More information -Více informací - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Rozpoznat a provést změny na obou stranách. Odstraněné, přesunuté nebo přejmenované soubory a konflikty budou detekovány automaticky pomocí databáze. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Vytvořit zrcadlovou kopii levé složky tak, aby po synchronizci pravá složka přesně odpovídala levé. - -Copy new and updated files to the right folder. -Kopírovat nové a aktualizované soubory do pravé složky. - -Configure your own synchronization rules. -Nastavení vlastních pravidel synchronizace. - -Detect moved files -Detekce přesunutých souborů - -Requires database files. Not supported by all file systems. -Je nutný soubor databáze. Toto není dostupné na všech systémech. - -Delete files: -Mazání souborů: - -Permanent -Trvale - -Delete or overwrite files permanently -Smazat nebo přepsat soubory trvale - -Recycle bin -Koš - -Back up deleted and overwritten files in the recycle bin -Použít Koš při mazání nebo přepisu souborů - -Versioning -Verzování - -Move files to a user-defined folder -Přesunout soubory do uživatelského adresáře - -Naming convention: -Pojmenování: - -Show examples -Ukázat příklady - -Handle errors: -Zpracování chyb: - -Ignore -Přeskočit - -Hide all error and warning messages -Skrýt všechny chyby a varování - -Pop-up -Hlášení - -Show pop-up on errors or warnings -Zobrazit hlášení při chybě nebo varování - -On completion: -Po dokončení: - -Start synchronization now? -Začít synchronizovat nyní? - -Variant: -Varianta: - -Statistics -Statistika - -&Don't show this dialog again -Tento dialog již &nezobrazovat - -Items found: -Nalezeno položek: - -Speed: -Rychlost: - -Time remaining: -Zbývající čas: - -Time elapsed: -Uplynulý čas: - -Synchronizing... -Synchronizuji... - -Minimize to notification area -Minimalizovat do oznamovací oblasti - -Close -Zavřít - -&Pause -&Pauza - -Stop -Ukončit - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Vytvoří dávkový souboru pro automatickou synchronizaci. Ke spuštění dávky jednoduše poklikejte na vytvořený soubor nebo využijte plánovač úloh vašeho systému: %x - -Stop synchronization at first error -Ukončit synchronizaci při první chybě - -Show progress dialog -Zobrazit průběh zpracování - -Save log: -Uložit žurnál: - -Limit: -Omezení: - -Limit maximum number of log files -Omezení maximálního počtu žurnálů - -How can I schedule a batch job? -Jak nastavit spouštění dávky? - -&Recycle bin -&Koš - -Delete on both sides -Smazat z obou stran - -Delete on both sides even if the file is selected on one side only -Smazat na obou stranách i když je soubor vybrán pouze na jedné z nich - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Nastavte pravidla filtru pro vynechání některých souborů ze synchronizace. Zadávajte cesty relativně k odpovídající složce. - -Include: -Zahrnout: - -Exclude: -Vynechat: - -Time span: -Časové rozmezí: - -File size: -Velikost soubor: - -Minimum: -Od: - -Maximum: -Do: - -&Clear -&Smazat - -The following settings are used for all synchronization jobs. -Toto nastavení je platné pro všechny synchronizační úlohy. - -Fail-safe file copy -Bezpečné kopírování souborů - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - -Kopírovat nejprve do pomocného souboru (*.ffs_tmp) před přepisem cíle. Tento postup zajišťuje bezpečné chování i v případě závažné chyby. - -(recommended) -(doporučeno) - -Copy locked files -Kopírovat zamčené soubory - -Copy shared or locked files using the Volume Shadow Copy Service. -Kopírovat sdílené nebo zamčené soubory pomocí služby Stínové kopie - -(requires administrator rights) -(vyžaduje administrátorská oprávnění) - -Copy file access permissions -Kopírovat přístupová oprávnění k souborům - -Transfer file and folder permissions. -Přenést přístupová oprávnění souborů a složek - -Automatic retry on error: -Automaticky opakovat při chybě: - -Retry count: -Počet opakování: - -Delay (in seconds): -Prodleva (v sekundách): - -Customize context menu: -Přizpůsobit kontextovou nabídku: - -Description -Popis - -Restore hidden windows -Obnovit skryté dialogy - -&Default -&Předdefinované - -Source code written in C++ using: -Zdrojový kód byl napsán kompletně v C++ pomocí: - -If you like FreeFileSync -Pokud se Vám FreeFileSync líbí - -Donate with PayPal -Přispět pomocí PayPal - -Feedback and suggestions are welcome -Komentáře a náměty jsou vždy vítány - -Homepage -Navštivte - -Email -Napište - -Published under the GNU General Public License -Vydáno pod GNU General Public License (GPL) - -Many thanks for localization: -Poděkování za překlad FreeFileSync: - -Save as Batch Job -Uložit jako dávku - -Delete Items -Smazané položky - -Global Settings -Nastavení programu - -Select Time Span -Časové rozmezí - -Folder Pairs -Adresářové páry - -Find -Najít - -Overview -Přehled - -Configuration -Konfigurace - -Main Bar -Hlavní lišta - -Filter Files -Filtr souborů - -Select View -Zobrazení - -Open... -Otevřít... - -Save -Uložit - -Compare both sides -Porovnat obě strany - -Comparison settings -Nastavení porovnání - -Synchronization settings -Nastavení synchronizace - -Start synchronization -Start synchronizace - -Confirm -Potvrdit - -&Execute -&Spustit - - -1 directory -%x directories - - -1 adresář -%x adresáře -%x adresářů - - - -1 file -%x files - - -1 soubor -%x soubory -%x souborů - - - -%y of 1 row in view -%y of %x rows in view - - -%y z 1 řádku -%y z %x řádků -%y z %x řádků - - -Set direction: -Nastavit adresář: - -multiple selection -vícenásobný výběr - -Include via filter: -Zahrnout pomocí filtru: - -Exclude via filter: -Vynechat pomocí filtru: - -Exclude temporarily -Vynechat dočasně - -Include temporarily -Přidat dočasně - -Delete -Smazat - -Include all -Zahrnout vše - -Exclude all -Vynechat vše - -Show icons: -Ikony: - -Small -Malé - -Medium -Střední - -Large -Velké - -Select time span... -Zadejte časové rozmezí... - -Default view -Výchozí zobrazení - -Show "%x" -Zobrazit "%x" - -Last session -Poslední sezení - -Folder Comparison and Synchronization -porovnání a synchronizace složek - -Configuration saved -Konfigurace uložena - -FreeFileSync batch -FreeFileSync dávka - -Do you want to save changes to %x? -Uložit změny do %x? - -Do&n't save -&Neukládat - -Never save &changes -Nikdy &neukládat změny - -Filter -Filtr - -Show files that exist on left side only -Zobrazit soubory existující pouze vlevo - -Show files that exist on right side only -Zobrazit soubory existující pouze vpravo - -Show files that are newer on left -Zobrazit soubory novější vlevo - -Show files that are newer on right -Zobrazit soubory novější vpravo - -Show files that are equal -Zobrazit shodné soubory - -Show files that are different -Zobrazit rozdílené soubory - -Show conflicts -Zobrazit konflikty - -Show files that will be created on the left side -Zobrazit soubory, které budou vlevo vytvořeny - -Show files that will be created on the right side -Zobrazit soubory, které budou vpravo vytvořeny - -Show files that will be deleted on the left side -Zobrazit soubory, které budou vlevo smazány - -Show files that will be deleted on the right side -Zobrazit soubory, které budou vpravo smazány - -Show files that will be overwritten on left side -Zobrazit soubory, které budou vlevo přepsány - -Show files that will be overwritten on right side -Zobrazit soubory, které budou vpravo přepsány - -Show files that won't be copied -Zobrazit soubory, které nebudou kopírovány - -Set as default -Nastavit jako výchozí - -All folders are in sync -Všechny složky jsou synchronizovány - -Synchronization Settings -Nastavení synchronizace - -Comparison Settings -Nastavení porovnání - -Cannot find %x -Nelze najít %x - -Comma-separated values -Text oddělený čárkami - -File list exported -Seznam souborů exportován - -Searching for program updates... -Hledání aktualizací programu ... - -&Ignore subsequent errors -Pře&skočit další chyby - -&Ignore -&Pokračovat - -Serious Error -Závažná chyba - -&Don't show this warning again -&Nezobrazovat již příště toto varování - -&Switch -&Přepnout - -&Yes -&Ano - -&No -&Ne - -Scanning... -Zpracovávání... - -Comparing content... -Porovnávání obsahu... - -Info -Info - -Paused -Pauza - -Initializing... -Inicializace... - -Stopped -Zastaveno - -Completed -Hotovo - -&Continue -&Pokračovat - -Log -Záznam zpracování - -Today -Dnes - -This week -Tento týden - -This month -Tento měsíc - -This year -Tento rok - -Last x days -dní zpětně - -Byte -Byte - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Opravdu chcete přesunout následující položku do Koše? -Opravdu chcete přesunout následující %x položky do Koše? -Opravdu chcete přesunout následujících %x položek do Koše? - - -Move -Přesunout - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Opravdu chcete smazat následující položku? -Opravdu chcete smazat následující %x položky? -Opravdu chcete smazat následujících %x položek? - - -Exclude -Vynechat - -Direct -Zachovat - -Follow -Použít cíl - -Copy NTFS permissions -Kopírovat oprávnění NTFS - -Integrate external applications into context menu. The following macros are available: -Integrace externí aplikace do kontextového menu. K dispozici jsou následující makra: - -- full file or folder name -- celá cesta nebo jméno souboru - -- folder part only -- pouze cesta - -- Other side's counterpart to %item_path% -- celá cesta nebo jméno z opačného panelu pro %item_path% - -- Other side's counterpart to %item_folder% -- pouze cesta z opačného panelu pro %item_folder% - -Restore all hidden windows and warnings? -Obnovit všechny skryté dialogy a varování? - -Leave as unresolved conflict -Ponechat jako nevyřešený konflikt - -Replace -Nahradit - -Move files and replace if existing -Přesunout a nahradit pokud soubory existují - -Time stamp -Časová značka - -Append a timestamp to each file name -Přidat časovou značku ke jménu souboru - -File -Soubor - -YYYY-MM-DD hhmmss -RRRR-MM-DD hhmmss - -Files -Soubory - -Items -Položky - -Percentage -Procentní podíl - -Cannot monitor directory %x. -Nelze nastavit monitorování adresáře %x. - -Conversion error: -Chyba konverze: - -Cannot delete file %x. -Nelze smazat soubor %x. - -The file is locked by another process: -Soubor je uzamčen jiným procesem: - -Cannot move file %x to %y. -Nelze přesunout soubor %x do %y. - -Cannot delete directory %x. -Nelze smazat adresář %x. - -Cannot write file attributes of %x. -Nelze zapsat atributy souboru %x. - -Cannot write modification time of %x. -Nelze nastavit atribut času změny pro %x. - -Cannot read security context of %x. -Nelze číst přístupová práva pro %x. - -Cannot write security context of %x. -Nelze zapsat přístupová práva pro %x. - -Cannot read permissions of %x. -Nelze číst oprávnění pro %x. - -Cannot write permissions of %x. -Nelze zapsat oprávnění pro %x. - -Cannot create directory %x. -Nelze vytvořit adresář %x. - -Cannot create symbolic link %x. -Nelze vytvořit zástupce %x. - -Cannot find system function %x. -Nelze najít systémovou funkci %x. - -Cannot copy file %x to %y. -Nelze kopírovat soubor %x do %y. - -Type of item %x is not supported: -Typ položky %x není podporován: - -Cannot resolve symbolic link %x. -Nelze najít odkaz zástupce %x. - -Cannot open directory %x. -Nelze otevřít adresář %x. - -Cannot enumerate directory %x. -Nelze procházet adresář %x. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -1 minuta -%x minuty -%x minut - - - -1 hour -%x hours - - -1 hodina -%x hodiny -%x hodin - - - -1 day -%x days - - -1 den -%x dny -%x dnů - - -Unable to register to receive system messages. -Nepodařilo se zaregistrovat k odběru systémových zpráv. - -Cannot set privilege %x. -Nelze nastavit práva pro %x. - -Unable to suspend system sleep mode. -Nelze pozastavit Režim spánku. - -Cannot change process I/O priorities. -Nelze nastavit priority procesu. - -Unable to move %x to the recycle bin. -Není možné přesunout %x do Koše. - -Cannot determine final path for %x. -Nelze určit výslednou cestu pro %x. - -Error Code %x: -Chybový kód %x - diff --git a/BUILD/Languages/danish.lng b/BUILD/Languages/danish.lng deleted file mode 100644 index 423b649c..00000000 --- a/BUILD/Languages/danish.lng +++ /dev/null @@ -1,1516 +0,0 @@ -
    - Dansk - Regmos - da_DK - flag_denmark.png - 2 - n == 1 ? 0 : 1 -
    - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -Retrying operation - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Both sides have changed since last synchronization. -Begge sider ændret siden sidste synkronisering. - -Cannot determine sync-direction: -Kan ikke bestemme retning: - -No change since last synchronization. -Ingen ændringer siden sidste synkronisering. - -The database entry is not in sync considering current settings. -Databaseemnet er ikke i synk med de aktuelle indstillinger. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Sætter standardretning: Gamle filer overskrives med nyere. - -Checking recycle bin availability for folder %x... -Tjekker papirkurvs tilgængelighed for mappen %x... - -Moving file %x to the recycle bin -Flytter filen %x til papirkurv - -Moving folder %x to the recycle bin -Flytter mappen %x til papirkurv - -Moving symbolic link %x to the recycle bin -Flytter symbolsk link %x til papirkurv - -Deleting file %x -Sletter filen %x - -Deleting folder %x -Sletter mappen %x - -Deleting symbolic link %x -Sletter symlink %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -Papirkurven kan ikke bruges til følgende mapper. Filerne slettes permanent: - -An exception occurred -Undtagelse opstod - -A directory path is expected after %x. -Der forventes en mappestil efter %x. - -Syntax error -Syntaksfejl - -Cannot open file %x. -Filen %x kan ikke åbnes. - -Error -Fejl - -File %x does not contain a valid configuration. -Filen %x indeholder ikke gyldige indstillinger. - -Unequal number of left and right directories specified. -Der er angivet et ulige antal højre og venstre mapper. - -The config file must not contain settings at directory pair level when directories are set via command line. -Indstillingsfilen må ikke indeholde indstillinger på mappepar niveau når mapper er sat via kommando. - -Warning -Advarsel - -Directories cannot be set for more than one configuration file. -Mapper kan ikke sættes til mere end en indstillingsfil. - -Syntax: -Syntaks: - -config files -indstillingsfiler - -directory -mappe - -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 directories for at most one config file. -Vilkårligt antal alternative mappe til højst en indstillingsfil - -Command line -Kommando - -A folder input field is empty. -Der er ikke valgt nogen mapper. - -The corresponding folder will be considered as empty. -Den tilsvarende mappe betragtes som tom. - -Cannot find the following folders: -Kan ikke finde følgende mapper: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -Ignorer denne fejl for at betragte hver mappe som tom. Mapperne bliver så automatisk oprettet ved synkronisering. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -Følgende mapper har afhængige stier. Opsæt synkroniseringsreglerne med omhu: - -File %x has an invalid date. -Filen %x har en ugyldig dato. - -Date: -Dato: - -Files %x have the same date but a different size. -Filerne %x har den samme dato men forskellig størrelse. - -Size: -Størrelse: - -Items differ in attributes only -Enhederne har kun attributter til forskel - -Resolving symbolic link %x -Løser symbolsk link %x - -Comparing content of files %x -Sammenligner indhold af filer %x - -Generating file list... -Opretter filliste... - -Starting comparison -Starter analyse - -Calculating sync directions... -Forbereder synkronisering... - -Out of memory. -Ikke nok hukommelse. - -Item exists on left side only -Emnet findes kun på venstre side - -Item exists on right side only -Emnet findes kun på højre side - -Left side is newer -Venstre er nyest - -Right side is newer -Højre er nyest - -Items have different content -Emnerne har forskelligt indhold - -Both sides are equal -Begge sider er ens - -Conflict/item cannot be categorized -Konflikt/ukendt emne - -Copy new item to left -Kopier nyt emne mod venstre - -Copy new item to right -Kopier nyt emne mod højre - -Delete left item -Slet emne til venstre - -Delete right item -Slet emne til højre - -Move file on left -Flyt filen til venstre - -Move file on right -flyt filen til højre - -Overwrite left item -Overskriv venstre emne - -Overwrite right item -Overskriv højre emne - -Do nothing -Gør intet - -Update attributes on left -Opdater attributter mod venstre - -Update attributes on right -Opdater attributter mod højre - -Database file %x is incompatible. -Databasefilen %x er inkompatibel. - -Initial synchronization: -Forbereder synkronisering: - -Database file %x does not yet exist. -Databasefilen %x findes ikke. - -Database file is corrupt: -Databasefilen er ødelagt: - -Cannot write file %x. -Kan ikke oprette filen %x. - -Cannot read file %x. -Kan ikke læse filen %x. - -Database files do not share a common session. -Databasefiler kan ikke dele handling. - -Searching for folder %x... -Søger efter mappen %x... - -Cannot read file attributes of %x. -Kan ikke læse filattributterne på %x. - -Cannot get process information. -Kan ikke hente procesinformation. - -Waiting while directory is locked (%x)... -Venter mens mappe låses (%x)... - - -1 sec -%x sec - - -1 sek -%x sek - - -Creating file %x -Opretter filen %x - -Items processed: -Emner behandlet: - -Items remaining: -Emner tilbage: - -Total time: -Samlet tid: - - -1 byte -%x bytes - - -1 byte -%x byte - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Behandlingsfejl i filen %x, række %y, kolonne %z. - -Cannot set directory lock for %x. -Kan ikke låse mappen %x. - -Scanning: -Skanner: - - -1 thread -%x threads - - -1 tråd -%x tråde - - -Encoding extended time information: %x -Opretter udvidet tidsinformation: %x - -/sec -/sek - -%x items/sec -%x emner/sek - -Configuration file %x loaded partially only. -Indstillingsfilen %x er kun delvist indlæst. - -Show in Explorer -Åben filplacering - -Open with default application -Åben med standardprogram - -Browse directory -Gennemse mappe - -Cannot access the Volume Shadow Copy Service. -VSS tjenesten er ikke tilgængelig - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Brug FreeFileSync 64-bit til at lave VSS kopier på dette system. - -Cannot load file %x. -Kan ikke indlæse filen %x. - -Cannot determine volume name for %x. -Kan ikke bestemme volumennavn for %x. - -Volume name %x is not part of file path %y. -Volumennavnet %x er ikke del af filstien %y. - -Stop requested: Waiting for current operation to finish... -Afbrydelse: Venter på aktuel opgave afsluttes... - -Unable to create timestamp for versioning: -Kunne ikke oprette versioneringstid: - -Cannot read the following XML elements: -Kan ikke læse følgende XML emner: - -&Open... -&Åben... - -Save &as... -Gem &som... - -&Quit -&Afslut - -&Program -&Filer - -&View help -&Åben hjælp - -&About -&Om - -&Help -&Hjælp - -Usage: -Gør sådan: - -1. Select folders to watch. -1. Vælg mapper til jobbet. - -2. Enter a command line. -2. Angiv en kommando. - -3. Press 'Start'. -3. Klik 'Start'. - -To get started just import a .ffs_batch file. -Importer en .ffs_batchfil (Filer > Åben...) for at komme igang. - -Folders to watch: -Jobbets mapper: - -Add folder -Tilføj mappe - -Remove folder -Fjern mappe - -Browse -Gennemse - -Select a folder -Vælg en mappe - -Idle time (in seconds): -Efter PC tomgang i (sek) - -Idle time between last detected change and execution of command -Tid imellem sidst fundne ændring og udførsel - -Command line: -Kommando: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -Kommandoen udføres hvis: -- filer eller undermapper ændres -- nye mapper findes (f.eks USB nøgle) - - -&Start -&Start - -&Retry -&Prøv igen - -Cancel -Annuller - -Build: %x -Udgivet: %x - -About -Om - -All files -Alle filer - -Automated Synchronization -Automatisk synkronisering - -Directory monitoring active -Mappeovervågning aktiv - -Waiting until all directories are available... -Venter til alle mapper er tilgængelige... - -&Restore -&Vis vindue - -&Show error -Vi&s fejl - -&Exit -&Luk - -Incorrect command line: -Ugyldig kommando: - -File content -Indhold - -File time and size -Størrelse og tid - -Two way -Tovejs - -Mirror -Spejling - -Update -Opdater - -Custom -Tilpasset - -Multiple... -Flere... - -Moving file %x to %y -Flytter filen %x til %y - -Moving folder %x to %y -Flytter mappen %x til %y - -Moving symbolic link %x to %y -Flytter symlinket %x til %y - -Removing old versions... -Fjerner gamle udgaver... - -Creating symbolic link %x -Opretter symlinket %x - -Creating folder %x -Opretter mappen %x - -Overwriting file %x -Overskriver filen %x - -Overwriting symbolic link %x -Overskriver symlinket %x - -Verifying file %x -Verificerer filen %x - -Updating attributes of %x -Opdaterer attributter for %x - -Cannot find %x. -Kan ikke finde %x. - -Target folder %x already existing. -Destinationsmappen %x findes allerede. - -Target folder input field must not be empty. -Destinationsmappen skal angives. - -Please enter a target folder for versioning. -Angiv mappe til versionering. - -Source folder %x not found. -Kildemappen %x blev ikke fundet. - -The following items have unresolved conflicts and will not be synchronized: -Følgende emner har uløste konflikter og synkroniseres ikke: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -Følgende mapper har markante forskelle. Kontroller at du synkroniserer de rigtige mapper - -Not enough free disk space available in: -Ikke nok ledig diskplads på: - -Required: -Krævet: - -Available: -Tilgængeligt: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Der ændres en mappe der tilhører flere mappepar. Kontroller dine synkroniseringsindstillinger. - -Synchronizing folder pair: -Synkroniserer mappepar: - -Generating database... -Opretter database... - -Creating a Volume Shadow Copy for %x... -Oprette VSS kopi for %x... - -Data verification error: %x and %y have different content. -Godkendelsesfejl: %x og %y har forskelligt indhold - -job name -Jobnavn - -Synchronization stopped -Synkronisering afbrudt - -Synchronization completed with errors -Synkronisering gennemført med fejl - -Synchronization completed with warnings -Synkronisering gennemført med advarsel - -Nothing to synchronize -Alt er synkroniseret - -Synchronization completed successfully -Synkronisering gennemført - -Saving log file %x... -Gemmer rapport %x... - -You can switch to FreeFileSync's main window to resolve this issue. -Skift til FreeFileSyncs hovedvindue for at løse problemet. - -Switching to FreeFileSync's main window -Skifter til hovedvinduet - -A new version of FreeFileSync is available: -Opdatering tilgængelig: - -Download now? -Download nu? - -Check for Program Updates -Søg efter opdatering - -&Download -&Download - -FreeFileSync is up to date. -FreeFileSync er opdateret. - -Information -Information - -Unable to connect to sourceforge.net. -Kan ikke kontakte sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Kunne ikke finde FreeFileSync's versionsnummer online. Vil du kontrollere manuelt? - -Symlink -Symlink - -Folder -Mappe - -Full path -Fuld sti - -Name -Navn - -Relative path -Relativ sti - -Base folder -Hovedmappe - -Size -Størrelse - -Date -Dato - -Extension -Filtype - -Category -Status - -Action -Handling - -Drag && drop -Træk emner hertil - -Close progress dialog -Luk dialogen - -Standby -Standby - -Log off -Log af - -Shut down -Luk ned - -Hibernate -Dvale - -Alternate comparison settings -Tilpas analyse - -Alternate synchronization settings -Tilpas synkronisering - -Local filter -Lokalt filter - -Active -Aktiv - -None -Ingen - -Remove alternate settings -Fjern tilpassede indstillinger - -Clear filter settings -Nulstil filter - -Copy -Kopiér - -Paste -Indsæt - -Alternate Comparison Settings -Tilpas analyse - -Alternate Synchronization Settings -Tilpas synkronisering - -Local Filter -Lokalt filter - -&New -&Ny - -&Save -&Gem - -Save as &batch job... -Gem som &batchfil - -1. &Compare -1. &Analyser - -2. &Synchronize -2. &Synkroniser - -&Global settings -&Programindstillinger - -&Language -&Sprog - -&Find... -S&øg - -&Export file list... -&Eksporter filliste... - -&Tools -&Værktøj - -&Check now -Søg &nu - -Check &automatically once a week -Søg &automatisk en gang om ugen - -&Check for new version -Søg &efter opdatering - -Compare -Analysér - -Synchronize -Synkronisér - -Add folder pair -Tilføj mappepar - -Remove folder pair -Fjern mappepar - -Swap sides -Byt side - -Close search bar -Luk søgelinie - -Find: -Søg: - -Match case -Versalfølsom (a/A) - -Save as batch job -Gem som batchfil - -Hide excluded items -Skjul ekskluderede emner - -Show filtered or temporarily excluded files -Vis filtrerede eller midlertidigt ekskluderede filer - -Number of files and folders that will be created -Antal filer og mapper der oprettes - -Number of files that will be overwritten -Antal filer der overskrives - -Number of files and folders that will be deleted -Antal filer og mapper der slettes - -Total bytes to copy -Antal bytes der kopieres - -Select a variant: -Vælg metode: - -Identify equal files by comparing modification time and size. -Genkend ens filer efter filtid og størrelse. - -Identify equal files by comparing the file content. -Genkend ens filer efter indhold. - -Symbolic links: -Symbolske link: - -More information -Mere information - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Find og udbred ændringer på begge sider. Sletninger, omdøbninger og konflikter findes automatisk i en database. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Spejl venstre mappe. Efter synkronisering er højre mappe en kopi af venstre. - -Copy new and updated files to the right folder. -Kopier nye og opdaterede filer til højre mappe. - -Configure your own synchronization rules. -Opret dine egne synkroniseringsregler. - -Detect moved files -Genkend flyttede filer - -Requires database files. Not supported by all file systems. -Kræver databasefiler. Ikke understøttet i alle filsystemer - -Delete files: -Filsletning: - -Permanent -Permanent - -Delete or overwrite files permanently -Slet eller overskriv filer permanent - -Recycle bin -Papirkurv - -Back up deleted and overwritten files in the recycle bin -Backup slettede og overskrevne filer i papirkurven - -Versioning -Versionering - -Move files to a user-defined folder -Flyt filer til brugerdefineret mappe - -Naming convention: -Navneregler: - -Show examples -Vis eksempler - -Handle errors: -Fejlhåndtering: - -Ignore -Ignorer - -Hide all error and warning messages -Skjul fejlbeskeder og advarsler - -Pop-up -Besked - -Show pop-up on errors or warnings -Vis fejlbeskeder og advarsler - -On completion: -Ved gennemført: - -Start synchronization now? -Synkroniser nu? - -Variant: -Jobtype: - -Statistics -Statistik - -&Don't show this dialog again -&Vis ikke igen - -Items found: -Emner fundet: - -Speed: -Hastighed: - -Time remaining: -Resterende tid: - -Time elapsed: -Brugt tid: - -Synchronizing... -Synkroniserer... - -Minimize to notification area -Minimér til uret - -Close -Luk - -&Pause -&Pause - -Stop -Stop - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Opret batchfil til automatisk synkronisering. Start ved at dobbeltklikke på filen eller planlæg via opgavestyring: %x - -Stop synchronization at first error -Stop synkronisering ved første fejl - -Show progress dialog -Vis fremskridt - -Save log: -Gem log: - -Limit: -Højst: - -Limit maximum number of log files -Begræns antal rapporter - -How can I schedule a batch job? -Hvordan oprettes en batchfil? - -&Recycle bin -&Papirkurv - -Delete on both sides -Slet på begge sider - -Delete on both sides even if the file is selected on one side only -Slet på begge sider selvom selvom filen kun er valgt på en side - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Angiv filterregler til ekskludering af bestemte filer fra synkroniseringen. Filstier skal relatere til kildemapperne. - -Include: -Inkludér: - -Exclude: -Ekskludér: - -Time span: -Interval: - -File size: -Filstørrelse: - -Minimum: -Minimum: - -Maximum: -Maksimum: - -&Clear -&Ryd - -The following settings are used for all synchronization jobs. -Disse indstillinger gælder alle synkroniseringer. - -Fail-safe file copy -Sikker filkopiering - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - -Kopiér til midlertidig fil (*.ffs_tmp) før overskrivning. Sikrer processen ved alvorlige fejl. - -(recommended) -(anbefalet) - -Copy locked files -Kopier låste filer - -Copy shared or locked files using the Volume Shadow Copy Service. -Kopiér delte eller låste filer med VSS kopiering. - -(requires administrator rights) -(administrator) - -Copy file access permissions -Kopier adgangstilladelser - -Transfer file and folder permissions. -Overfør fil og mappetilladelser. - -Automatic retry on error: -Antal forsøg ved fejl: - -Retry count: -Antal forsøg: - -Delay (in seconds): -Forsinkelse (sek): - -Customize context menu: -Tilpas kontekstmenu: - -Description -Beskrivelse - -Restore hidden windows -Gendan skjulte vinduer - -&Default -S&tandard - -Source code written in C++ using: -Kildekoden er skrevet i C++ med hjælp fra: - -If you like FreeFileSync -Er du glad for FreeFileSync - -Donate with PayPal -Donér med PayPal - -Feedback and suggestions are welcome -Kritik og forslag er meget velkomne - -Homepage -Hjemmeside - -Email -Email - -Published under the GNU General Public License -Udgivet under GNU General Public Licence - -Many thanks for localization: -Tak for oversættelse: - -Save as Batch Job -Gem som batchfil - -Delete Items -Slet emner - -Global Settings -Programindstillinger - -Select Time Span -Vælg tidsinterval - -Folder Pairs -Mappepar - -Find -Søg - -Overview -Oversigt - -Configuration -Indstilling - -Main Bar -Hovedlinie - -Filter Files -Filtrer filer - -Select View -Tilpas visning - -Open... -Åben... - -Save -Gem - -Compare both sides -Analyser begge sider - -Comparison settings -Analyseindstillinger - -Synchronization settings -Synkroniseringsindstillinger - -Start synchronization -Start synkronisering - -Confirm -Bekræft - -&Execute -&Udfør - - -1 directory -%x directories - - -1 mappe -%x mapper - - - -1 file -%x files - - -1 fil -%x filer - - - -%y of 1 row in view -%y of %x rows in view - - -%y af 1 række i visning -%y af %x rækker i visning - - -Set direction: -Retning: - -multiple selection -vælg flere - -Include via filter: -Inkludér via filter: - -Exclude via filter: -Ekskluder m. filter: - -Exclude temporarily -Ekskluder midlertidigt - -Include temporarily -Inkluder midlertidigt - -Delete -Slet - -Include all -Vælg alle - -Exclude all -Fravælg alle - -Show icons: -Vis ikoner: - -Small -Små - -Medium -Medium - -Large -Store - -Select time span... -Vælg tidsinterval... - -Default view -Standardvisning - -Show "%x" -Vis "%x" - -Last session -Sidste opgave - -Folder Comparison and Synchronization -Mappeanalyse og synkronisering - -Configuration saved -Indstillinger gemt - -FreeFileSync batch -FreeFileSync batchfil - -Do you want to save changes to %x? -Vil du gemme ændringer i %x? - -Do&n't save -&Gem ikke - -Never save &changes -Gem &aldrig ændringer - -Filter -Filter - -Show files that exist on left side only -Vis filer der kun findes på venstre side - -Show files that exist on right side only -Vis filer der kun findes på højre side - -Show files that are newer on left -Vis nyere filer på venstre side - -Show files that are newer on right -Vis nyere filer på højre side - -Show files that are equal -Vis ens filer - -Show files that are different -Vis uens filer - -Show conflicts -Vis konflikter - -Show files that will be created on the left side -Vis filer der oprettes på venstre side - -Show files that will be created on the right side -Vis filer der oprettes på højre side - -Show files that will be deleted on the left side -Vis filer der slettes på venstre side - -Show files that will be deleted on the right side -Vis filer der slettes på højre side - -Show files that will be overwritten on left side -Vis filer der overskrives på venstre side - -Show files that will be overwritten on right side -Vis filer der overskrives på højre side - -Show files that won't be copied -Vis filer der ikke kopieres - -Set as default -Sæt som standard - -All folders are in sync -Alle mapper er synkrone - -Synchronization Settings -Synkroniseringsindstillinger - -Comparison Settings -Analyseindstillinger - -Cannot find %x -Kan ikke finde %x - -Comma-separated values -Kommaopdelte værdier - -File list exported -Fillisten blev eksporteret - -Searching for program updates... -Søger efter opdatering... - -&Ignore subsequent errors -&Ignorer efterfølgende fejl - -&Ignore -&Ignorer - -Serious Error -Kritisk fejl - -&Don't show this warning again -&Vis ikke igen - -&Switch -&Skift - -&Yes -&Ja - -&No -&Nej - -Scanning... -Skanner... - -Comparing content... -Analyserer indhold... - -Info -Info - -Paused -Pauset - -Initializing... -Forbereder... - -Stopped -Afbrudt - -Completed -Gennemført - -&Continue -&Fortsæt - -Log -Log - -Today -Idag - -This week -Denne uge - -This month -Denne måned - -This year -Dette år - -Last x days -Sidste x dage - -Byte -Byte - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Vil du flytte følgende emne til papirkurven? -Vil du flytte følgende %x emner til papirkurven? - - -Move -Flyt - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Vil du slette følgende emne? -Vil du slette følgende %x emner? - - -Exclude -Ekskludér - -Direct -Direkte - -Follow -Følg - -Copy NTFS permissions -Kopier NTFS tilladelser - -Integrate external applications into context menu. The following macros are available: -Integrer eksterne programmer i kontekstmenu. Brug følgende macroer: - -- full file or folder name -Komplet fil- eller mappenavn - -- folder part only -Kun mappedelen - -- Other side's counterpart to %item_path% -Modsat sides pendant til %item_path% - -- Other side's counterpart to %item_folder% -Modsat sides pendant til %item_folder% - -Restore all hidden windows and warnings? -Gendan skjulte vinduer og advarsler? - -Leave as unresolved conflict -Efterlad som uløst konflikt - -Replace -Erstat - -Move files and replace if existing -Flyt fil og erstat eventuelt eksisterende - -Time stamp -Tidsstempel - -Append a timestamp to each file name -Føj tidsstempel til hvert filnavn - -File -Fil - -YYYY-MM-DD hhmmss -YYYY-MM-DD hhmmss - -Files -Filer - -Items -Emner - -Percentage -Procent - -Cannot monitor directory %x. -Kan ikke overvåge mappen %x. - -Conversion error: -Konverteringsfejl: - -Cannot delete file %x. -Kan ikke slette filen %x. - -The file is locked by another process: -Filen er låst af en anden process: - -Cannot move file %x to %y. -Kan ikke flytte filen %x til %y. - -Cannot delete directory %x. -Kan ikke slette mappen %x. - -Cannot write file attributes of %x. -Kan ikke skrive filattributter til %x. - -Cannot write modification time of %x. -Kan ikke opdatere tidsændring på %x. - -Cannot read security context of %x. -Kan ikke læse sikkerhedsindstillinger på %x. - -Cannot write security context of %x. -Kan ikke skrive sikkerhedsindstillinger til %x. - -Cannot read permissions of %x. -Kan ikke læse tilladelserne på %x. - -Cannot write permissions of %x. -Kan ikke skrive tilladelserne til %x. - -Cannot create directory %x. -Kan ikke oprette mappen %x. - -Cannot create symbolic link %x. -Kan ikke oprette symbolsk link %x. - -Cannot find system function %x. -Kan ikke finde systemfunktionen %x. - -Cannot copy file %x to %y. -Kan ikke kopiere filen %x til %y. - -Type of item %x is not supported: -Filtypen %x understøttes ikke: - -Cannot resolve symbolic link %x. -Kan ikke følge symlinket %x. - -Cannot open directory %x. -Kan ikke åbne mappen %x. - -Cannot enumerate directory %x. -Kan ikke optælle mappen %x. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -1 min -%x min - - - -1 hour -%x hours - - -1 time -%x timer - - - -1 day -%x days - - -1 dag -%x dage - - -Unable to register to receive system messages. -Kunne ikke registrere modtagelse af systembeskeder. - -Cannot set privilege %x. -Kan ikke sætte %x privilegier. - -Unable to suspend system sleep mode. -Kunne ikke ophæve standby. - -Cannot change process I/O priorities. -Kan ikke ændre I/O prioriteter. - -Unable to move %x to the recycle bin. -Kunne ikke flytte %x til papirkurv. - -Cannot determine final path for %x. -Kan ikke bestemme endelig sti for %x - -Error Code %x: -Fejlkode %x: - diff --git a/BUILD/Languages/dutch.lng b/BUILD/Languages/dutch.lng deleted file mode 100644 index cb0656b5..00000000 --- a/BUILD/Languages/dutch.lng +++ /dev/null @@ -1,1516 +0,0 @@ -
    - Nederlands - Edwin Dierssen - nl_NL - flag_holland.png - 2 - n == 1 ? 0 : 1 -
    - -Unable to suspend system sleep mode. - - -Unable to register to receive system messages. - - -Restore all hidden windows and warnings? - - -Move - - -Stopped - - -Serious Error - - -Comparison Settings - - -Synchronization Settings - - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -Select View - - -Filter Files - - -Main Bar - - -Folder Pairs - - -Select Time Span - - -Global Settings - - -Delete Items - - -Save as Batch Job - - -Restore hidden windows - - -Customize context menu: - - -Delay (in seconds): - - -Retry count: - - -Automatic retry on error: - - -Transfer file and folder permissions. - - -(requires administrator rights) - - -Copy shared or locked files using the Volume Shadow Copy Service. - - -(recommended) - - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - - -The following settings are used for all synchronization jobs. - - -Maximum: - - -Minimum: - - -File size: - - -Time span: - - -Exclude: - - -Include: - - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. - - -&Recycle bin - - -How can I schedule a batch job? - - -Limit: - - -Save log: - - -Stop synchronization at first error - - -Stop - - -Variant: - - -Start synchronization now? - - -On completion: - - -Handle errors: - - -Show examples - - -Delete files: - - -Copy new and updated files to the right folder. - - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. - - -More information - - -Symbolic links: - - -Identify equal files by comparing the file content. - - -Identify equal files by comparing modification time and size. - - -Select a variant: - - -Find: - - -Close search bar - - -&Check for new version - - -&Find... - - -Local Filter - - -Alternate Synchronization Settings - - -Alternate Comparison Settings - - -None - - -Active - - -Local filter - - -Alternate synchronization settings - - -Alternate comparison settings - - -Check for Program Updates - - -Retrying operation - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Switching to FreeFileSync's main window - - -You can switch to FreeFileSync's main window to resolve this issue. - - -Synchronization stopped - - -Please enter a target folder for versioning. - - -Incorrect command line: - - -Automated Synchronization - - -&Start - - -Command line: - - -Idle time (in seconds): - - -Folders to watch: - - -&View help - - -Unable to create timestamp for versioning: - - -Stop requested: Waiting for current operation to finish... - - -%x items/sec - - -Both sides have changed since last synchronization. -Beide zijdes zijn veranderd sinds de laatste synchronisatie. - -Cannot determine sync-direction: -Kan de synchronisatie-richting niet bepalen: - -No change since last synchronization. -Geen veranderingen sinds de laatste synchronisatie. - -The database entry is not in sync considering current settings. -De database is niet gesynchroniseerd volgens de huidige instellingen. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Stel standaard synchronisatie richtingen in: Oude bestanden worden door nieuwere bestanden overschreven. - -Checking recycle bin availability for folder %x... -Prullebak beschikbaarheid voor map %x te controleren... - -Moving file %x to the recycle bin -Bezig met verplaatsen van bestand %x naar de prullenbak - -Moving folder %x to the recycle bin -Bezig met verplaatsen van map %x naar de prullenbak - -Moving symbolic link %x to the recycle bin -Bezig met verplaatsen van snelkoppeling %x naar de prullenbak - -Deleting file %x -Verwijderen van bestand %x - -Deleting folder %x -Verwijderen van map %x - -Deleting symbolic link %x -Verwijderen van snelkoppeling %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -De prullenbak is niet beschikbaar voor de volgende mappen. Bestanden zullen permanent verwijderd worden: - -An exception occurred -Er heeft een uitzondering plaatsgevonden - -A directory path is expected after %x. -Een bestandslocatie pad is verwacht na %x. - -Syntax error -Syntax fout - -Cannot open file %x. -Kan bestand %x niet openen. - -Error -Fout - -File %x does not contain a valid configuration. -Bestand %x bevat geen valide configuratie. - -Unequal number of left and right directories specified. -Oneven nummer van linker en rechter bestandslocaties gespecificeerd. - -The config file must not contain settings at directory pair level when directories are set via command line. -Het configuratiebestand mag geen instellingen bevatten voor een bestandslocatie level als bestandslocaties in de command line ingesteld zijn. - -Warning -Waarschuwing - -Directories cannot be set for more than one configuration file. -Bestandslocaties kunnen niet voor meer dan een configuratiebestand gemaakt worden. - -Syntax: -Syntax: - -config files -configuratiebestanden - -directory -bestandslocatie - -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Elk aantal FreeFileSync .ffs_gui en/of .ffs_batch configuratiebestanden. - -Any number of alternative directories for at most one config file. -Elk aantal alternatieve bestandslocaties voor een configuratiebestand. - -Command line -Opdrachtregel - -A folder input field is empty. -Een map invoerveld is leeg. - -The corresponding folder will be considered as empty. -De overeenkomstige map zal als leeg worden beschouwd. - -Cannot find the following folders: -Kan de volgende mappen niet vinden: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -U kunt deze foutmelding negeren om elke map als leeg te markeren. De mappen worden dan automatisch aangemaakt tijdens de synchronisatie. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -De volgende mappen hebben afhankelijke paden. Wees voorzichtig bij het opzetten van synchronisatieregels: - -File %x has an invalid date. -Bestand %x heeft een ongeldige datum. - -Date: -Datum: - -Files %x have the same date but a different size. -Bestanden %x hebben dezelfde datums maar een afwijkende grootte. - -Size: -Grootte: - -Items differ in attributes only -Items verschillen alleen in attributen - -Resolving symbolic link %x -Bezig met vinden van snelkoppeling %x - -Comparing content of files %x -De inhoud van %x bestanden wordt vergeleken - -Generating file list... -Genereren van bestandslijst... - -Starting comparison -Vergelijking starten - -Calculating sync directions... -Synchronisatie richtingen calculeren... - -Out of memory. -Onvoldoende geheugen. - -Item exists on left side only -Item bestaat alleen aan de linkerkant - -Item exists on right side only -Item bestaat alleen aan de rechterkant - -Left side is newer -De linkerkant is nieuwer - -Right side is newer -De rechterkant is nieuwer - -Items have different content -De items hebben een andere inhoud - -Both sides are equal -Beide kanten zijn gelijk - -Conflict/item cannot be categorized -Conflict/item kan niet worden gecategoriseerd - -Copy new item to left -Kopieër nieuw item naar de linkerkant - -Copy new item to right -Kopieër nieuw item naar de rechterkant - -Delete left item -Verwijder linker item - -Delete right item -Verwijder rechter item - -Move file on left -Verplaats bestand aan de linkerkant - -Move file on right -Verplaats bestand aan de rechterkant - -Overwrite left item -Overschrijf linker item - -Overwrite right item -Overschrijf rechter item - -Do nothing -Geen actie ondernemen - -Update attributes on left -Update attributen aan de linkerkant - -Update attributes on right -Update attributen aan de rechterkant - -Database file %x is incompatible. -Databasebestand %x is niet compatibel. - -Initial synchronization: -Initiële synchronisatie: - -Database file %x does not yet exist. -Databasebestand %x bestaat nog niet. - -Database file is corrupt: -Databasebestand is corrupt: - -Cannot write file %x. -Kan bestand %x niet schrijven. - -Cannot read file %x. -Kan bestand %x niet vinden. - -Database files do not share a common session. -Databasebestanden delen geen gezamelijke sessie. - -Searching for folder %x... -Bezig met zoeken naar map %x... - -Cannot read file attributes of %x. -Kan bestandskenmerken van %x niet uitlezen. - -Cannot get process information. -Kan geen procesinformatie verkrijgen. - -Waiting while directory is locked (%x)... -Wachten totdat map is vergrendeld (%x)... - - -1 sec -%x sec - - -1 sec -%x sec - - -Creating file %x -Bestand %x wordt aangemaakt - -Items processed: -Onderdelen verwerkt: - -Items remaining: -Onderdelen te gaan: - -Total time: -Totale tijd: - - -1 byte -%x bytes - - -1 byte -%x bytes - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Fout bij het parsen van bestand %x, rij %y, kolom %z. - -Cannot set directory lock for %x. -Kan locatie %x niet op slot zetten. - -Scanning: -Doorzoekt: - - -1 thread -%x threads - - -1 thread -%x threads - - -Encoding extended time information: %x -Coderen uitgebreide tijdinformatie: %x - -/sec -/sec - -Configuration file %x loaded partially only. -Configuratiebestand %x alleen deels geladen. - -Show in Explorer -Toon in Verkenner - -Open with default application -Open met standaardapplicatie - -Browse directory -Verken map - -Cannot access the Volume Shadow Copy Service. -Kan de Volume Shadow Copy Service niet benaderen. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Gebruik alstublieft de FreeFileSync 64-bit versie om schaduwkopieën te maken op dit systeem. - -Cannot load file %x. -Kan bestand %x niet laden. - -Cannot determine volume name for %x. -Kan volumenaam voor %x niet bepalen. - -Volume name %x is not part of file path %y. -Schijfnaam %x is geen deel van bestandspad %y. - -Cannot read the following XML elements: -Kan de volgende XML elementen niet lezen: - -&Open... -&Open... - -Save &as... -Ops&laan als... - -&Quit -&Afsluiten - -&Program -&Programma - -&About -&Over - -&Help -&Help - -Usage: -Gebruik: - -1. Select folders to watch. -1. Selecteer mappen om te bekijken. - -2. Enter a command line. -2. Geef een opdrachtregel in. - -3. Press 'Start'. -3. Klik op 'Start'. - -To get started just import a .ffs_batch file. -Importeer een .ffs_batch bestand om te beginnen. - -Add folder -Map toevoegen - -Remove folder -Verwijder map - -Browse -Verkennen - -Select a folder -Selecteer een map - -Idle time between last detected change and execution of command -Tijd tussen de laatste gedetecteerde verandering en de uitvoering van het commando - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -De opdracht word geactiveerd als: -- bestanden of subfolders veranderen -- nieuwe folders worden gevonden (bijvoorbeeld bij invoeging van USB stick) - - -&Retry -&Opnieuw proberen - -Cancel -Annuleren - -Build: %x -Build: %x - -About -Informatie - -All files -Alle bestanden - -Directory monitoring active -Bestandslocatie controle actief - -Waiting until all directories are available... -Wachten tot alle bestandslocaties beschikbaar zijn... - -&Restore -&Herstellen - -&Show error -Laat &fouten zien - -&Exit -&Afsluiten - -File content -Bestandsinhoud - -File time and size -Bestandstijd- en grootte - -Two way -Twee kanten op - -Mirror -Spiegelen - -Update -Bijwerken - -Custom -Aangepast - -Multiple... -Meerdere... - -Moving file %x to %y -Bezig met verplaatsen van bestand %x naar %y - -Moving folder %x to %y -Bezig met verplaatsen van map %x naar %y - -Moving symbolic link %x to %y -Bezig met verplaatsen van snelkoppeling %x naar %y - -Removing old versions... -Bezig met verwijderen van oude versies... - -Creating symbolic link %x -Snelkoppeling %x wordt aangemaakt - -Creating folder %x -Map %x wordt aangemaakt - -Overwriting file %x -Bezig met overschrijven van bestand %x - -Overwriting symbolic link %x -Bezig met overschrijven van snelkoppeling %x - -Verifying file %x -Verifieert bestand %x - -Updating attributes of %x -Attributen bijwerken van %x - -Cannot find %x. -Kan %x niet vinden. - -Target folder %x already existing. -Doelmap %x bestaat al. - -Target folder input field must not be empty. -Doelmap mag niet leeg zijn. - -Source folder %x not found. -Bronmap %x niet gevonden. - -The following items have unresolved conflicts and will not be synchronized: -De volgende items hebben onopgeloste conflicten en zullen niet worden gesynchroniseerd: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -De volgende mappen zijn heel verschillend. Wees er zeker van dat dit de goede mappen zijn voor synchronisatie. - -Not enough free disk space available in: -Niet genoeg vrije schijfruimte beschikbaar op: - -Required: -Vereist: - -Available: -Beschikbaar: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Een map die onderdeel is van meerdere map paren word aangepast. Kijk alstublieft uw synchronisatie instellingen na. - -Synchronizing folder pair: -Bezig met synchroniseren van folder paar: - -Generating database... -Genereren van database... - -Creating a Volume Shadow Copy for %x... -Aanmaken van een Volume Shadow Copy voor %x... - -Data verification error: %x and %y have different content. -Data verificatie fout: %x en %y hebben een verschillende inhoud. - -job name -taaknaam - -Synchronization completed with errors -Synchronisatie is met fouten afgerond - -Synchronization completed with warnings -Synchronisatie afgerond met waarschuwingen - -Nothing to synchronize -Niets om te synchroniseren - -Synchronization completed successfully -Synchronisatie succesvol - -Saving log file %x... -Opslaan van logbestand %x... - -A new version of FreeFileSync is available: -Er is een nieuwe versie van FreeFileSync beschikbaar: - -Download now? -Nu downloaden? - -&Download -&Download - -FreeFileSync is up to date. -U gebruikt de nieuwste versie van FreeFileSync. - -Information -Informatie - -Unable to connect to sourceforge.net. -Kan geen verbinding maken met sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Kan huidige FreeFileSync versienummer niet online vinden. Wilt u handmatig controleren? - -Symlink -Symlink - -Folder -Map - -Full path -Volledig pad - -Name -Naam - -Relative path -Relatief pad - -Base folder -Hoofdmap - -Size -Grootte - -Date -Datum - -Extension -Extensie - -Category -Categorie - -Action -Actie - -Drag && drop -Drag en drop - -Close progress dialog -Sluit voortgangsvenster - -Standby -Stand-by - -Log off -Afmelden - -Shut down -Afsluiten - -Hibernate -Slaapstand - -Remove alternate settings -Verwijder alternatieve instellingen - -Clear filter settings -Verwijder filterinstellingen - -Copy -Kopiëren - -Paste -Plakken - -&New -&Nieuw - -&Save -O&pslaan - -Save as &batch job... -Opslaan als &batch opdracht... - -1. &Compare -1. &Vergelijk - -2. &Synchronize -2. &Synchroniseer - -&Global settings -&Algemene instellingen - -&Language -&Taal - -&Export file list... -&Exporteer bestandslijst... - -&Tools -&Gereedschap - -&Check now -&Controleer nu - -Check &automatically once a week -Controleer &automatisch eens per week - -Compare -Vergelijk - -Synchronize -Synchroniseer - -Add folder pair -Voeg gekoppelde mappen toe - -Remove folder pair -Verwijder gekoppelde mappen - -Swap sides -Wissel zijdes - -Match case -Hoofdlettergevoelig - -Save as batch job -Opslaan als batch opdracht - -Hide excluded items -Verberg uitgesloten bestanden - -Show filtered or temporarily excluded files -Laat gefilterde of tijdelijk uitgesloten bestanden zien - -Number of files and folders that will be created -Aantal bestanden en mappen die zullen worden aangemaakt - -Number of files that will be overwritten -Aantal bestanden die overschreven zullen worden - -Number of files and folders that will be deleted -Aantal bestanden en mappen die verwijderd zullen worden - -Total bytes to copy -Aantal bytes om te kopiëren - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Identificeer en pas veranderingen toe aan beide kanten. Verwijderingen, verplaatsingen en conflicten worden automatisch gevonden met behulp van een database. - -Configure your own synchronization rules. -Configureer uw eigen synchronisatieregels. - -Detect moved files -Detecteer verplaatste bestanden - -Requires database files. Not supported by all file systems. -Heeft database bestanden nodig. Wordt niet ondersteund door alle bestandssystemen - -Permanent -Permanent - -Delete or overwrite files permanently -Bestanden definitief verwijderen of overschrijven - -Recycle bin -Prullenbak - -Back up deleted and overwritten files in the recycle bin -Maak een backup van verwijderde en overschreven bestanden in de prullenbak - -Versioning -Versiebeheer - -Move files to a user-defined folder -Verplaats bestanden naar een gebruikers gedefineerde map - -Naming convention: -Naamgevingsconventie - -Ignore -Negeer - -Hide all error and warning messages -Verberg alle fout- en waarschuwingsberichten - -Pop-up -Pop-up - -Show pop-up on errors or warnings -Laat pop-up zien bij foutmeldingen of waarschuwingen - -Statistics -Statistieken - -&Don't show this dialog again -Laat deze dialoog &niet meer zien - -Items found: -Onderdelen gevonden: - -Speed: -Snelheid: - -Time remaining: -Resterende tijd: - -Time elapsed: -Verstreken tijd: - -Synchronizing... -Synchroniseert... - -Minimize to notification area -Minimaliseer naar systeemvak - -Close -Sluiten - -&Pause -&Pauze - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Maak een batchbestand voor onbeheerde synchronisatie. Om te starten dubbelklikt u dit bestand of rooster in een taakplanner: %x - -Show progress dialog -Toon voortgangsdialoogvenster - -Limit maximum number of log files -Limiteer maximaal aantal log bestanden - -Delete on both sides -Verwijder aan beide zijdes - -Delete on both sides even if the file is selected on one side only -Verwijder aan beide zijdes ook al is het bestand maar aan één zijde geselecteerd - -&Clear -&Leegmaken - -Fail-safe file copy -Fail-safe bestandskopie - -Copy locked files -Kopiëer vergrendelde bestanden - -Copy file access permissions -Kopiëer toegangsrechten van bestand. - -Description -Omschrijving - -&Default -&Standaard - -Source code written in C++ using: -Broncode geschreven in C++ met behulp van: - -If you like FreeFileSync -Indien FreeFileSync u bevalt - -Donate with PayPal -Doneer met PayPal - -Feedback and suggestions are welcome -Feedback en suggesties zijn welkom - -Homepage -Homepage - -Email -E-mail - -Published under the GNU General Public License -Gepubliceerd onder de GNU General Public License - -Many thanks for localization: -Veel dank voor de vertalingen: - -Find -Vind - -Overview -Overzicht - -Configuration -Configuratie - -Open... -Open... - -Save -Opslaan - -Compare both sides -Vergelijk beide zijdes - -Comparison settings -Vergelijksinstellingen - -Synchronization settings -Synchronisatieinstellingen - -Start synchronization -Start synchronisatie - -Confirm -Bevestigen - -&Execute -&Uitvoeren - - -1 directory -%x directories - - -1 map -%x mappen - - - -1 file -%x files - - -1 bestand -%x bestanden - - - -%y of 1 row in view -%y of %x rows in view - - -%y van 1 rij in weergave -%y van %x rijen in weergave - - -Set direction: -Stel richting in: - -multiple selection -meervoudige selectie - -Include via filter: -Invoegen via filter: - -Exclude via filter: -Sluit via filter uit: - -Exclude temporarily -Tijdelijk uitsluiten - -Include temporarily -Tijdelijk opnemen - -Delete -Verwijderen - -Include all -Alles opnemen - -Exclude all -Alles uitsluiten - -Show icons: -Laat pictogrammen zien: - -Small -Klein - -Medium -Middel - -Large -Groot - -Select time span... -Selecteer tijdsspanne... - -Default view -Standaard weergave - -Show "%x" -Toon "%x" - -Last session -Laatste sessie - -Folder Comparison and Synchronization -Mappen vergelijken en synchroniseren - -Configuration saved -Configuratie opgeslagen - -FreeFileSync batch -FreeFileSync taak - -Do you want to save changes to %x? -Wilt u de wijzigingen in %x opslaan? - -Do&n't save -&Niet opslaan - -Never save &changes -Sla &veranderingen nooit op - -Filter -Filter - -Show files that exist on left side only -Toon bestanden die alleen aan de linkerzijde bestaan - -Show files that exist on right side only -Toon bestanden die alleen aan de rechterzijde bestaan - -Show files that are newer on left -Toon bestanden die aan de linkerzijde nieuwer zijn - -Show files that are newer on right -Toon bestanden die aan de rechterzijde nieuwer zijn - -Show files that are equal -Toon bestanden die gelijk zijn - -Show files that are different -Toon bestanden die verschillend zijn - -Show conflicts -Toon conflicten - -Show files that will be created on the left side -Toon bestanden die aan de linkerzijde aangemaakt zullen worden - -Show files that will be created on the right side -Toon bestanden die aan de rechterzijde aangemaakt zullen worden - -Show files that will be deleted on the left side -Toon bestanden die van de linkerzijde verwijderd zullen worden - -Show files that will be deleted on the right side -Toon bestanden die van de rechterzijde verwijderd zullen worden - -Show files that will be overwritten on left side -Toon bestanden die aan de linkerzijde overschreven zullen worden - -Show files that will be overwritten on right side -Toon bestanden die aan de rechterzijde overschreven zullen worden - -Show files that won't be copied -Toon bestanden die niet gekopiëerd zullen worden - -Set as default -Instellen als standaard - -All folders are in sync -Alle mappen zijn gesynchroniseerd - -Cannot find %x -Kan %x niet vinden - -Comma-separated values -Door komma gescheiden waarden - -File list exported -Bestandslijst geëxporteerd - -Searching for program updates... -Bezig met zoeken naar programma updates... - -&Ignore subsequent errors -&Negeer volgende foutmeldingen - -&Ignore -&Negeren - -&Don't show this warning again -Laat deze &waarschuwing niet meer zien - -&Switch -&Omschakelen - -&Yes -&Ja - -&No -&Nee - -Scanning... -Doorzoekt... - -Comparing content... -Inhoud vergelijken... - -Info -Info - -Paused -Gepauzeerd - -Initializing... -Initialiseren... - -Completed -Voltooid - -&Continue -&Doorgaan - -Log -Log - -Today -Vandaag - -This week -Deze week - -This month -Deze maand - -This year -Dit jaar - -Last x days -Laatste x dagen - -Byte -Byte - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Wilt u dit item echt naar de prullenbak verplaatsen? -Wilt u echt de volgende %x items naar de prullenbak verplaatsen? - - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Wilt u het volgende item echt verwijderen? -Wilt u echt de volgende %x items verwijderen? - - -Exclude -Uitsluiten - -Direct -Direct - -Follow -Volgen - -Copy NTFS permissions -Kopiëer NTFS permissies - -Integrate external applications into context menu. The following macros are available: -Integreer externe applicaties in het context menu. De volgende macros zijn beschikbaar: - -- full file or folder name -- volledige bestands- of mapnaam - -- folder part only -- alleen het map gedeelte - -- Other side's counterpart to %item_path% -- Tegenhanger van de andere kant naar %item_path% - -- Other side's counterpart to %item_folder% -- Tegenhanger van de andere kant naar %item_folder% - -Leave as unresolved conflict -Beschouw als onopgelost conflict - -Replace -Vervangen - -Move files and replace if existing -Verplaats bestanden en overschrijf bestaande bestanden - -Time stamp -Tijdstempel - -Append a timestamp to each file name -Voeg een timestamp aan elke bestandsnaam toe - -File -Bestand - -YYYY-MM-DD hhmmss -JJJJ-MM-DD hhmmss - -Files -Bestanden - -Items -Items - -Percentage -Percentage - -Cannot monitor directory %x. -Kan locatie %x niet controleren. - -Conversion error: -Converteerfout: - -Cannot delete file %x. -Kan bestand %x niet verwijderen. - -The file is locked by another process: -Het bestand is in gebruik door een ander proces: - -Cannot move file %x to %y. -Kan bestand %x niet verplaatsen naar %y. - -Cannot delete directory %x. -Kan bestand %x niet verwijderen. - -Cannot write file attributes of %x. -Kan bestandskenmerken van %x niet schrijven. - -Cannot write modification time of %x. -Kan wijzigingstijd van %x niet toevoegen. - -Cannot read security context of %x. -Kan de beveiligingscontext van %x niet lezen. - -Cannot write security context of %x. -Kan de beveiligingscontext van %x niet schrijven. - -Cannot read permissions of %x. -Kan de bevoegdheden van %x niet lezen. - -Cannot write permissions of %x. -Kan de bevoegdheden van %x niet schrijven. - -Cannot create directory %x. -Kan map %x niet aanmaken. - -Cannot create symbolic link %x. -Kan snelkoppeling %x niet aanmaken. - -Cannot find system function %x. -Kan systeemfunctie %x niet vinden. - -Cannot copy file %x to %y. -Kan bestand %x niet kopiëren naar %y. - -Type of item %x is not supported: -Type van bestand %x is niet ondersteund: - -Cannot resolve symbolic link %x. -Kan snelkoppeling %x niet vinden. - -Cannot open directory %x. -Kan map %x niet openen. - -Cannot enumerate directory %x. -Kan map %x niet opsommen. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -1 min -%x min - - - -1 hour -%x hours - - -1 uur -%x uren - - - -1 day -%x days - - -1 dag -%x dagen - - -Cannot set privilege %x. -Kan privilege %x niet instellen. - -Cannot change process I/O priorities. -Kan de I/O prioriteiten niet aanpassen. - -Unable to move %x to the recycle bin. -Kan %x niet verplaatsen naar de prullenbak. - -Cannot determine final path for %x. -Kan pad voor %x niet vaststellen. - -Error Code %x: -Foutcode %x: - diff --git a/BUILD/Languages/english_uk.lng b/BUILD/Languages/english_uk.lng deleted file mode 100644 index 71e8c67e..00000000 --- a/BUILD/Languages/english_uk.lng +++ /dev/null @@ -1,1523 +0,0 @@ -
    - English (UK) - Robert Readman - en_GB - flag_england.png - 2 - n == 1 ? 0 : 1 -
    - -Both sides have changed since last synchronization. -Both sides have changed since last synchronisation. - -Cannot determine sync-direction: -Cannot determine sync-direction: - -No change since last synchronization. -No change since last synchronisation. - -The database entry is not in sync considering current settings. -The database entry is not in sync considering current settings. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Setting default synchronisation directions: Old files will be overwritten with newer files. - -Checking recycle bin availability for folder %x... -Checking recycle bin availability for folder %x... - -Moving file %x to the recycle bin -Moving file %x to the recycle bin - -Moving folder %x to the recycle bin -Moving folder %x to the recycle bin - -Moving symbolic link %x to the recycle bin -Moving symbolic link %x to the recycle bin - -Deleting file %x -Deleting file %x - -Deleting folder %x -Deleting folder %x - -Deleting symbolic link %x -Deleting symbolic link %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -The recycle bin is not available for the following folders. Files will be deleted permanently instead: - -An exception occurred -An exception occurred - -A directory path is expected after %x. -A directory path is expected after %x. - -Syntax error -Syntax error - -Cannot open file %x. -Cannot open file %x. - -Error -Error - -File %x does not contain a valid configuration. -File %x does not contain a valid configuration. - -Unequal number of left and right directories specified. -Unequal number of left and right directories specified. - -The config file must not contain settings at directory pair level when directories are set via command line. -The config file must not contain settings at directory pair level when directories are set via command line. - -Warning -Warning - -Directories cannot be set for more than one configuration file. -Directories cannot be set for more than one configuration file. - -Syntax: -Syntax: - -config files -config files - -directory -directory - -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 directories for at most one config file. -Any number of alternative directories for at most one config file. - -Command line -Command line - -A folder input field is empty. -A folder input field is empty. - -The corresponding folder will be considered as empty. -The corresponding folder will be considered as empty. - -Cannot find the following folders: -Cannot find the following folders: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronisation. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -The following folders have dependent paths. Be careful when setting up synchronisation rules: - -File %x has an invalid date. -File %x has an invalid date. - -Date: -Date: - -Files %x have the same date but a different size. -Files %x have the same date but a different size. - -Size: -Size: - -Items differ in attributes only -Items differ in attributes only - -Resolving symbolic link %x -Resolving symbolic link %x - -Comparing content of files %x -Comparing content of files %x - -Generating file list... -Generating file list... - -Starting comparison -Starting comparison - -Calculating sync directions... -Calculating sync directions... - -Out of memory. -Out of memory. - -Item exists on left side only -Item exists on left side only - -Item exists on right side only -Item exists on right side only - -Left side is newer -Left side is newer - -Right side is newer -Right side is newer - -Items have different content -Items have different content - -Both sides are equal -Both sides are equal - -Conflict/item cannot be categorized -Conflict/item cannot be categorised - -Copy new item to left -Copy new item to left - -Copy new item to right -Copy new item to right - -Delete left item -Delete left item - -Delete right item -Delete right item - -Move file on left -Move file on left - -Move file on right -Move file on right - -Overwrite left item -Overwrite left item - -Overwrite right item -Overwrite right item - -Do nothing -Do nothing - -Update attributes on left -Update attributes on left - -Update attributes on right -Update attributes on right - -Database file %x is incompatible. -Database file %x is incompatible. - -Initial synchronization: -Initial synchronisation: - -Database file %x does not yet exist. -Database file %x does not yet exist. - -Database file is corrupt: -Database file is corrupt: - -Cannot write file %x. -Cannot write file %x. - -Cannot read file %x. -Cannot read file %x. - -Database files do not share a common session. -Database files do not share a common session. - -Searching for folder %x... -Searching for folder %x... - -Cannot read file attributes of %x. -Cannot read file attributes of %x. - -Cannot get process information. -Cannot get process information. - -Waiting while directory is locked (%x)... -Waiting while directory is locked (%x)... - - -1 sec -%x sec - - -1 sec -%x sec - - -Creating file %x -Creating file %x - -Items processed: -Elements processed: - -Items remaining: -Elements remaining: - -Total time: -Total time: - - -1 byte -%x bytes - - -1 byte -%x bytes - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Error parsing file %x, row %y, column %z. - -Cannot set directory lock for %x. -Cannot set directory lock for %x. - -Scanning: -Scanning: - - -1 thread -%x threads - - -1 thread -%x threads - - -Encoding extended time information: %x -Encoding extended time information: %x - -/sec -/sec - -%x items/sec -%x items/sec - -Configuration file %x loaded partially only. -Configuration file %x loaded partially only. - -Show in Explorer -Show in Explorer - -Open with default application -Open with default application - -Browse directory -Browse directory - -Cannot access the Volume Shadow Copy Service. -Cannot access the Volume Shadow Copy Service. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Please use FreeFileSync 64-bit version to create shadow copies on this system. - -Cannot load file %x. -Cannot load file %x. - -Cannot determine volume name for %x. -Cannot determine volume name for %x. - -Volume name %x is not part of file path %y. -Volume name %x is not part of file path %y. - -Stop requested: Waiting for current operation to finish... -Stop requested: Waiting for current operation to finish... - -Unable to create timestamp for versioning: -Unable to create timestamp for versioning: - -Cannot read the following XML elements: -Cannot read the following XML elements: - -&Open... -&Open... - -Save &as... -Save &as... - -&Quit -&Quit - -&Program -&Program - -&View help -&View help - -&About -&About - -&Help -&Help - -Usage: -Usage: - -1. Select folders to watch. -1. Select folders to watch. - -2. Enter a command line. -2. Enter a command line. - -3. Press 'Start'. -3. Press 'Start'. - -To get started just import a .ffs_batch file. -To get started just import a .ffs_batch file. - -Folders to watch: -Folders to watch: - -Add folder -Add folder - -Remove folder -Remove folder - -Browse -Browse - -Select a folder -Select a folder - -Idle time (in seconds): -Idle time (in seconds): - -Idle time between last detected change and execution of command -Idle time between last detected change and execution of command - -Command line: -Command line: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -&Start -&Start - -&Retry -&Retry - -Cancel -Cancel - -Build: %x -Build: %x - -About -About - -All files -All files - -Automated Synchronization -Automated Synchronisation - -Directory monitoring active -Directory monitoring active - -Waiting until all directories are available... -Waiting until all directories are available... - -&Restore -&Restore - -&Show error -&Show error - -&Exit -&Exit - -Incorrect command line: -Incorrect command line: - -File content -File content - -File time and size -File time and size - -Two way -Two way - -Mirror -Mirror - -Update -Update - -Custom -Custom - -Multiple... -Multiple... - -Moving file %x to %y -Moving file %x to %y - -Moving folder %x to %y -Moving folder %x to %y - -Moving symbolic link %x to %y -Moving symbolic link %x to %y - -Removing old versions... -Removing old versions... - -Creating symbolic link %x -Creating symbolic link %x - -Creating folder %x -Creating folder %x - -Overwriting file %x -Overwriting file %x - -Overwriting symbolic link %x -Overwriting symbolic link %x - -Verifying file %x -Verifying file %x - -Updating attributes of %x -Updating attributes of %x - -Cannot find %x. -Cannot find %x. - -Target folder %x already existing. -Target folder %x already existing. - -Target folder input field must not be empty. -Target folder input field must not be empty. - -Please enter a target folder for versioning. -Please enter a target folder for versioning. - -Source folder %x not found. -Source folder %x not found. - -The following items have unresolved conflicts and will not be synchronized: -The following items have unresolved conflicts and will not be synchronised: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -The following folders are significantly different. Make sure you are matching the correct folders for synchronisation. - -Not enough free disk space available in: -Not enough free disk space available in: - -Required: -Required: - -Available: -Available: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -A folder will be modified which is part of multiple folder pairs. Please review synchronisation settings. - -Synchronizing folder pair: -Synchronising folder pair: - -Generating database... -Generating database... - -Creating a Volume Shadow Copy for %x... -Creating a Volume Shadow Copy for %x... - -Data verification error: %x and %y have different content. -Data verification error: %x and %y have different content. - -job name -job name - -Synchronization stopped -Synchronisation stopped - -Synchronization completed with errors -Synchronisation completed with errors - -Synchronization completed with warnings -Synchronization completed with warnings - -Nothing to synchronize -Nothing to synchronise - -Synchronization completed successfully -Synchronisation completed successfully - -Saving log file %x... -Saving log file %x... - -You can switch to FreeFileSync's main window to resolve this issue. -You can switch to FreeFileSync's main window to resolve this issue. - -Switching to FreeFileSync's main window -Switching to FreeFileSync's main window - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - -Retrying operation -Retrying operation - -A new version of FreeFileSync is available: -A new version of FreeFileSync is available: - -Download now? -Download now? - -Check for Program Updates -Check for Program Updates - -&Download -&Download - -FreeFileSync is up to date. -FreeFileSync is up to date. - -Information -Information - -Unable to connect to sourceforge.net. -Unable to connect to sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Cannot find current FreeFileSync version number online. Do you want to check manually? - -Symlink -Symlink - -Folder -Folder - -Full path -Full path - -Name -Name - -Relative path -Relative path - -Base folder -Base folder - -Size -Size - -Date -Date - -Extension -Extension - -Category -Category - -Action -Action - -Drag && drop -Drag && drop - -Close progress dialog -Close progress dialogue - -Standby -Standby - -Log off -Log off - -Shut down -Shut down - -Hibernate -Hibernate - -Alternate comparison settings -Alternate comparison settings - -Alternate synchronization settings -Alternate synchronisation settings - -Local filter -Local filter - -Active -Active - -None -None - -Remove alternate settings -Remove alternate settings - -Clear filter settings -Clear filter settings - -Copy -Copy - -Paste -Paste - -Alternate Comparison Settings -Alternate Comparison Settings - -Alternate Synchronization Settings -Alternate Synchronisation Settings - -Local Filter -Local Filter - -&New -&New - -&Save -&Save - -Save as &batch job... -Save as &batch job... - -1. &Compare -1. &Compare - -2. &Synchronize -2. &Synchronise - -&Global settings -&Global settings - -&Language -&Language - -&Find... -&Find... - -&Export file list... -&Export file list... - -&Tools -&Tools - -&Check now -&Check now - -Check &automatically once a week -Check &automatically once a week - -&Check for new version -&Check for new version - -Compare -Compare - -Synchronize -Synchronise - -Add folder pair -Add folder pair - -Remove folder pair -Remove folder pair - -Swap sides -Swap sides - -Close search bar -Close search bar - -Find: -Find: - -Match case -Match case - -Save as batch job -Save as batch job - -Hide excluded items -Hide excluded items - -Show filtered or temporarily excluded files -Show filtered or temporarily excluded files - -Number of files and folders that will be created -Number of files and folders that will be created - -Number of files that will be overwritten -Number of files that will be overwritten - -Number of files and folders that will be deleted -Number of files and folders that will be deleted - -Total bytes to copy -Total bytes to copy - -Select a variant: -Select a variant: - -Identify equal files by comparing modification time and size. -Identify equal files by comparing modification time and size. - -Identify equal files by comparing the file content. -Identify equal files by comparing the file content. - -Symbolic links: -Symbolic links: - -More information -More information - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Create a mirror backup of the left folder which exactly matches the right folder after synchronisation. - -Copy new and updated files to the right folder. -Copy new and updated files to the right folder. - -Configure your own synchronization rules. -Configure your own synchronisation rules. - -Detect moved files -Detect moved files - -Requires database files. Not supported by all file systems. -Requires database files. Not supported by all file systems. - -Delete files: -Delete files: - -Permanent -Permanent - -Delete or overwrite files permanently -Delete or overwrite files permanently - -Recycle bin -Recycle bin - -Back up deleted and overwritten files in the recycle bin -Back up deleted and overwritten files in the recycle bin - -Versioning -Versioning - -Move files to a user-defined folder -Move files to a user-defined folder - -Naming convention: -Naming convention: - -Show examples -Show examples - -Handle errors: -Handle errors: - -Ignore -Ignore - -Hide all error and warning messages -Hide all error and warning messages - -Pop-up -Pop-up - -Show pop-up on errors or warnings -Show pop-up on errors or warnings - -On completion: -On completion: - -Start synchronization now? -Start synchronisation now? - -Variant: -Variant: - -Statistics -Statistics - -&Don't show this dialog again -&Don't show this dialogue again - -Items found: -Elements found: - -Speed: -Speed: - -Time remaining: -Time remaining: - -Time elapsed: -Time elapsed: - -Synchronizing... -Synchronising... - -Minimize to notification area -Minimize to notification area - -Close -Close - -&Pause -&Pause - -Stop -Stop - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Create a batch file for unattended synchronisation. To start, double-click this file or schedule in a task planner: %x - -Stop synchronization at first error -Stop synchronisation at first error - -Show progress dialog -Show progress dialogue - -Save log: -Save log: - -Limit: -Limit: - -Limit maximum number of log files -Limit maximum number of log files - -How can I schedule a batch job? -How can I schedule a batch job? - -&Recycle bin -&Recycle bin - -Delete on both sides -Delete on both sides - -Delete on both sides even if the file is selected on one side only -Delete on both sides even if the file is selected on one side only - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Select filter rules to exclude certain files from synchronisation. Enter file paths relative to their corresponding folder pair. - -Include: -Include: - -Exclude: -Exclude: - -Time span: -Time span: - -File size: -File size: - -Minimum: -Minimum: - -Maximum: -Maximum: - -&Clear -&Clear - -The following settings are used for all synchronization jobs. -The following settings are used for all synchronisation jobs. - -Fail-safe file copy -Fail-safe file copy - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -(recommended) -(recommended) - -Copy locked files -Copy locked files - -Copy shared or locked files using the Volume Shadow Copy Service. -Copy shared or locked files using the Volume Shadow Copy Service. - -(requires administrator rights) -(requires administrator rights) - -Copy file access permissions -Copy file access permissions - -Transfer file and folder permissions. -Transfer file and folder permissions. - -Automatic retry on error: -Automatic retry on error: - -Retry count: -Retry count: - -Delay (in seconds): -Delay (in seconds): - -Customize context menu: -Customise context menu: - -Description -Description - -Restore hidden windows -Restore hidden windows - -&Default -&Default - -Source code written in C++ using: -Source code written in C++ using: - -If you like FreeFileSync -If you like FreeFileSync - -Donate with PayPal -Donate with PayPal - -Feedback and suggestions are welcome -Feedback and suggestions are welcome - -Homepage -Homepage - -Email -E-mail - -Published under the GNU General Public License -Published under the GNU General Public Licence - -Many thanks for localization: -Many thanks for localisation: - -Save as Batch Job -Save as Batch Job - -Delete Items -Delete Items - -Global Settings -Global Settings - -Select Time Span -Select Time Span - -Folder Pairs -Folder Pairs - -Find -Find - -Overview -Overview - -Configuration -Configuration - -Main Bar -Main Bar - -Filter Files -Filter Files - -Select View -Select View - -Open... -Open... - -Save -Save - -Compare both sides -Compare both sides - -Comparison settings -Comparison settings - -Synchronization settings -Synchronisation settings - -Start synchronization -Start synchronisation - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - -Confirm -Confirm - -&Execute -&Execute - - -1 directory -%x directories - - -1 directory -%x directories - - - -1 file -%x files - - -1 file -%x files - - - -%y of 1 row in view -%y of %x rows in view - - -%y of 1 row in view -%y of %x rows in view - - -Set direction: -Set direction: - -multiple selection -multiple selection - -Include via filter: -Include via filter: - -Exclude via filter: -Exclude via filter: - -Exclude temporarily -Exclude temporarily - -Include temporarily -Include temporarily - -Delete -Delete - -Include all -Include all - -Exclude all -Exclude all - -Show icons: -Show icons: - -Small -Small - -Medium -Medium - -Large -Large - -Select time span... -Select time span... - -Default view -Default view - -Show "%x" -Show "%x" - -Last session -Last session - -Folder Comparison and Synchronization -Folder Comparison and Synchronisation - -Configuration saved -Configuration saved - -FreeFileSync batch -FreeFileSync batch - -Do you want to save changes to %x? -Do you want to save changes to %x? - -Do&n't save -Do&n't save - -Never save &changes -Never save &changes - -Filter -Filter - -Show files that exist on left side only -Show files that exist on left side only - -Show files that exist on right side only -Show files that exist on right side only - -Show files that are newer on left -Show files that are newer on left - -Show files that are newer on right -Show files that are newer on right - -Show files that are equal -Show files that are equal - -Show files that are different -Show files that are different - -Show conflicts -Show conflicts - -Show files that will be created on the left side -Show files that will be created on the left side - -Show files that will be created on the right side -Show files that will be created on the right side - -Show files that will be deleted on the left side -Show files that will be deleted on the left side - -Show files that will be deleted on the right side -Show files that will be deleted on the right side - -Show files that will be overwritten on left side -Show files that will be overwritten on left side - -Show files that will be overwritten on right side -Show files that will be overwritten on right side - -Show files that won't be copied -Show files that won't be copied - -Set as default -Set as default - -All folders are in sync -All folders are in sync - -Synchronization Settings -Synchronisation Settings - -Comparison Settings -Comparison Settings - -Cannot find %x -Cannot find %x - -Comma-separated values -Comma-separated values - -File list exported -File list exported - -Searching for program updates... -Searching for program updates... - -&Ignore subsequent errors -&Ignore subsequent errors - -&Ignore -&Ignore - -Serious Error -Serious Error - -&Don't show this warning again -&Don't show this warning again - -&Switch -&Switch - -&Yes -&Yes - -&No -&No - -Scanning... -Scanning... - -Comparing content... -Comparing content... - -Info -Info - -Paused -Paused - -Initializing... -Initialising... - -Stopped -Stopped - -Completed -Completed - -&Continue -&Continue - -Log -Log - -Today -Today - -This week -This week - -This month -This month - -This year -This year - -Last x days -Last x days - -Byte -Byte - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Move -Move - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Exclude -Exclude - -Direct -Direct - -Follow -Follow - -Copy NTFS permissions -Copy NTFS permissions - -Integrate external applications into context menu. The following macros are available: -Integrate external applications into context menu. The following macros are available: - -- full file or folder name -- full file or folder name - -- folder part only -- folder part only - -- Other side's counterpart to %item_path% -- Other side's counterpart to %item_path% - -- Other side's counterpart to %item_folder% -- Other side's counterpart to %item_folder% - -Restore all hidden windows and warnings? -Restore all hidden windows and warnings? - -Leave as unresolved conflict -Leave as unresolved conflict - -Replace -Replace - -Move files and replace if existing -Move files and replace if existing - -Time stamp -Time stamp - -Append a timestamp to each file name -Append a timestamp to each file name - -File -File - -YYYY-MM-DD hhmmss -YYYY-MM-DD hhmmss - -Files -Files - -Items -Items - -Percentage -Percentage - -Cannot monitor directory %x. -Cannot monitor directory %x. - -Conversion error: -Conversion error: - -Cannot delete file %x. -Cannot delete file %x. - -The file is locked by another process: -The file is locked by another process: - -Cannot move file %x to %y. -Cannot move file %x to %y. - -Cannot delete directory %x. -Cannot delete directory %x. - -Cannot write file attributes of %x. -Cannot write file attributes of %x. - -Cannot write modification time of %x. -Cannot write modification time of %x. - -Cannot read security context of %x. -Cannot read security context of %x. - -Cannot write security context of %x. -Cannot write security context of %x. - -Cannot read permissions of %x. -Cannot read permissions of %x. - -Cannot write permissions of %x. -Cannot write permissions of %x. - -Cannot create directory %x. -Cannot create directory %x. - -Cannot create symbolic link %x. -Cannot create symbolic link %x. - -Cannot find system function %x. -Cannot find system function %x. - -Cannot copy file %x to %y. -Cannot copy file %x to %y. - -Type of item %x is not supported: -Type of item %x is not supported: - -Cannot resolve symbolic link %x. -Cannot resolve symbolic link %x. - -Cannot open directory %x. -Cannot open directory %x. - -Cannot enumerate directory %x. -Cannot enumerate directory %x. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -1 min -%x min - - - -1 hour -%x hours - - -1 hour -%x hours - - - -1 day -%x days - - -1 day -%x days - - -Unable to register to receive system messages. -Unable to register to receive system messages. - -Cannot set privilege %x. -Cannot set privilege %x. - -Unable to suspend system sleep mode. -Unable to suspend system sleep mode. - -Cannot change process I/O priorities. -Cannot change process I/O priorities. - -Unable to move %x to the recycle bin. -Unable to move %x to the recycle bin. - -Cannot determine final path for %x. -Cannot determine final path for %x. - -Error Code %x: -Error Code %x: - diff --git a/BUILD/Languages/finnish.lng b/BUILD/Languages/finnish.lng deleted file mode 100644 index 45b3b9fc..00000000 --- a/BUILD/Languages/finnish.lng +++ /dev/null @@ -1,1510 +0,0 @@ -
    - Suomi - Nalle Juslén - fi_FI - flag_finland.png - 2 - n == 1 ? 0 : 1 -
    - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -&Check - - -Retrying operation... - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Both sides have changed since last synchronization. -Molemmat puolet muuttuneet edellisestä täsmäyksestä. - -Cannot determine sync-direction: -Tuntematon suunta täsmäykselle: - -No change since last synchronization. -Ei muutoksia edellisen täsmäyksen jälkeen. - -The database entry is not in sync considering current settings. -Tietokanta ei vastaa nykyisiä asetuksia. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Aseta oletussuunta täsmäykselle: Vanhat tiedostot korvataan uudemilla tiedostoilla. - -Checking recycle bin availability for folder %x... -Tarkistetaan Roskakorin käyttö hakemistolle %x... - -Moving file %x to the recycle bin -Siirrä tiedosto %x Roskakoriin - -Moving folder %x to the recycle bin -Siirrä hakemisto %x Roskakoriin - -Moving symbolic link %x to the recycle bin -Siirrä linkki %x Roskakoriin - -Deleting file %x -Poista tiedosto %x - -Deleting folder %x -Poista hakemisto %x - -Deleting symbolic link %x -Pistetaan pikakuvake %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -Roskakori puuttuu näiltä hakemistoilta. Tiedostot poistetaan ehdotta: - -An exception occurred -Virhe havaittu - -A directory path is expected after %x. -Tarvitaan hakemistopolku %x jälkeen. - -Syntax error -Komento virhe - -Cannot open file %x. -Tiedosto %x ei aukea. - -File %x does not contain a valid configuration. -Tiedosto %x ei sisällä kelvollista kokoonpanoa. - -Unequal number of left and right directories specified. -Hakemisto määrät ei täsmää (oikea<>vasen). - -The config file must not contain settings at directory pair level when directories are set via command line. -Määrittelyssä on oltava viittaukset hakemistopareista, jos hakemistot kutsutaam komentoriviltä. - -Directories cannot be set for more than one configuration file. -Hakemisto määriteltävä vain yhteen määrittelyyn. - -Command line -Komentokehote - -Syntax: -Syntaksi: - -config files -määrittelytiedosto - -directory -hakemisto - -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Vapaa määrä FreeFileSync .ffs_gui ja/tai .ffs_batch määrittelytiedostoja. - -Any number of alternative directories for at most one config file. -Vapaa määrä eri hakemistoja yhdessä määrittelytiedostossa. - -A folder input field is empty. -Hakemiston syöte on tyhjä. - -The corresponding folder will be considered as empty. -Hakemisto tulkitaan tyhjäksi. - -Cannot find the following folders: -Hakemistot ei löydy: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -Voit ohita virhe olettamalla hakemistot tyhjiksi. Hakemistot luodaan täsmäytyksen aikana. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -Hakemistolla on polkumäärittely. Luo täsmäytyksen vertailusäännöt huolella: - -File %x has an invalid date. -Tiedostolla %x on virheellinen päiväys. - -Date: -Pvm: - -Files %x have the same date but a different size. -Tiedostoilla %x on sama päiväys mutta eri koko. - -Size: -Koko: - -Items differ in attributes only -Kohteiden eroaa vain määritteissä - -Resolving symbolic link %x -Tulkitaan pikakuvike %x - -Comparing content of files %x -Vertaa tiedostojen %x tietosisältöä - -Generating file list... -Luodaan tiedostolista... - -Starting comparison -Vertailu alkaa - -Calculating sync directions... -Lasketaa täsmäytyksen suuntaa... - -Out of memory. -Muisti loppui. - -Item exists on left side only -Kohde löytyy vain vasemmalta - -Item exists on right side only -Kohde löytyy vain oikealta - -Left side is newer -Uudempi vasemmalla - -Right side is newer -Uudempi oikealla - -Items have different content -Kohteilla eri sisältö - -Both sides are equal -Puolet ovat identtiset - -Conflict/item cannot be categorized -Ristiriita, ei määriteltävissä - -Copy new item to left -Monista uusi vasemmalle - -Copy new item to right -Monista uusi oikealle - -Delete left item -Poista vasen - -Delete right item -Poista oikea - -Move file on left -Siirä vasen tiedosto - -Move file on right -Siirrä oikea tiedosto - -Overwrite left item -Korvaa vasen - -Overwrite right item -Korvaa oikea - -Do nothing -Älä tee mitään - -Update attributes on left -Päivitä oikeudet vasemmalla - -Update attributes on right -Päivitä oikeudet oikealla - -Database file %x is incompatible. -Tietokanta %x vierasta muotoa. - -Initial synchronization: -Ensi täsmäytys: - -Database file %x does not yet exist. -Tietokanta %x on vielä luomatta. - -Database file is corrupt: -Tietokanta viottunut: - -Cannot write file %x. -Kirjoittaminen ei onnistu %x. - -Cannot read file %x. -Lukeminen ei onnistu %x. - -Database files do not share a common session. -Tietokannan tiedostot eri sessioista. - -Searching for folder %x... -Etsitään hakemistoa %x... - -Cannot read file attributes of %x. -Tiedoston %x määritteitä ei voitu lukea. - -Cannot get process information. -Prosessin tietoja ei saada. - -Waiting while directory is locked (%x)... -Odotan hakemiston lukitusta (%x)... - - -1 sec -%x sec - - -1 s -%x s - - -Creating file %x -Luodaan tiedosto %x - -Items processed: -Osia käsitelty: - -Items remaining: -Osia jäljellä: - -Total time: -Kokonaisaika: - - -1 byte -%x bytes - - -1 tavu -%x tavua - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Virhe jäsennys tiedo %x, rivi %y, sarake %z. - -Cannot set directory lock for %x. -Hakemiston %x lukitus ei onnistu. - -Scanning: -Skannaus: - - -1 thread -%x threads - - -1 säije -%x säijettä - - -Encoding extended time information: %x -Tulkitaan laajennettua aikatietoa: %x - -/sec -/s - -%x items/sec -%x olioo/s - -Configuration file %x loaded partially only. -Kokoonpanotiedosto %x ladattu vain osittain. - -Show in Explorer -Näytä Explorerissa - -Open with default application -Avaa oletussovelluksessa - -Browse directory -Selaa hakemistoa - -Cannot access the Volume Shadow Copy Service. -Volume Shadow Copy palvelu ei vastaa. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Tilannevedoksia varten käytä tässä järjestelmässä FreeFileSync 64-bittinen versio. - -Cannot load file %x. -Tiedostoa %x ei voida ladata. - -Cannot determine volume name for %x. -Nimen %x tunnistus ei onnistu. - -Volume name %x is not part of file path %y. -Nimi %x ei esiinny tiedostopolussa %y. - -Stop requested: Waiting for current operation to finish... -Keskeytyspyyntö: Odotetaan tehtävän valmistumista... - -Unable to create timestamp for versioning: -Versiohallinan aikaleimaa ei voida luoda: - -Cannot read the following XML elements: -XML elementit lukukelvottimia: - -&Open... -&Avaa... - -Save &as... -Tallenna n&imellä... - -&Quit -&Lopeta - -&Program -&Ohjelma - -&View help -&Näytä ohje - -&About -&Ohje - -&Help -&Ohje - -Usage: -Käyttö: - -1. Select folders to watch. -1. Valitse seurattavat hakemistot. - -2. Enter a command line. -2. Anna komentokehote - -3. Press 'Start'. -3. Paina 'Käynnistä'. - -To get started just import a .ffs_batch file. -Aloita lataamalla joku .ffs_batch -tiedosto. - -Folders to watch: -Seurattavat hakemistot: - -Add folder -Lisää hakemisto - -Remove folder -Poista hakemisto - -Browse -Selaa - -Select a folder -Valitse hakemisto - -Idle time (in seconds): -Jouten (s) - -Idle time between last detected change and execution of command -Joutoaika edellisen muutoksen ja käskyn suorittamisen välillä - -Command line: -Komentokehoite - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -Käsky suoritetaan jos: -- tiedosto tai alihakemisto muuttu -- uusi hakemisto ilmestyy (esim. USB tikku) - - -&Start -&Käynnistä - -About -Ohje - -Build: %x -Versio: %x - -All files -Kaikki tiedostot - -Automated Synchronization -Automaattinen täsmäytys - -Directory monitoring active -Hakemistovalvonta päällä - -Waiting until all directories are available... -Odota kunnes kaikki hakemistot on saatavilla... - -Error -Virhe - -&Restore -&Palauta - -&Show error -&Näytä virheet - -&Exit -&Poistu - -Incorrect command line: -Virheellinen komento: - -&Retry -&Uudestaan - -File content -Tiedoston sisältö - -File time and size -Tiedoston aika ja koko - -Two way -Molemmat - -Mirror -Peilaava - -Update -Päivittävä - -Custom -Oma määritelmä - -Multiple... -Moninkertainen... - -Moving file %x to %y -Siirrä tiedosto %x -> %y - -Moving folder %x to %y -Siirrä hakemisto %x -> %y - -Moving symbolic link %x to %y -Siirrä pikakuvike %x -> %y - -Removing old versions... -Vanhat poistetaa... - -Creating symbolic link %x -Luodaan pikakuvake %x - -Creating folder %x -LuoDAAN hakemisto %x - -Overwriting file %x -Korvaa tiedosto %x - -Overwriting symbolic link %x -Korvaa pikakuvake %x - -Verifying file %x -Tarkistan tiedostoa %x - -Updating attributes of %x -Päivitän %x:n ominaisuudet - -Cannot find %x. -%x ei löydy. - -Target folder %x already existing. -Kohdehakemisto %x on olemassa. - -Target folder input field must not be empty. -Kohde hakemiston kenttä on annettava. - -Please enter a target folder for versioning. -Anna versioinnin kohdehakemisto. - -Source folder %x not found. -Lähdehakemisto %x ei löydy. - -The following items have unresolved conflicts and will not be synchronized: -Näissä kohteissa on selvittämättömiä ristiriitoja, niitä ei täsmäytetä: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -Hakemistoissa on oleelliset erot. Varmista että hakemistot on oikein valittu. - -Not enough free disk space available in: -Vapaa levytila ei riitä: - -Required: -Vaadittu: - -Available: -Saatavilla: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Moniosaisen hakemistoparin hakemistoa muutetaan. Tarkista täsmäytyksen asetuksia. - -Synchronizing folder pair: -Täsmäytetään hakemistoparia: - -Generating database... -Tietokanta luodaan... - -Creating a Volume Shadow Copy for %x... -Luo %x:lle Volume Shadow Copy ... - -Data verification error: %x and %y have different content. -Tiedon verifiointie virhe: %x ja %y sisältö on erilainen. - -job name -työnimi - -Synchronization stopped -Täsmäytys on pysäytetty - -Synchronization completed with errors -Täsmäytys päättyi virheisiin - -Synchronization completed with warnings -Täsmäytys päättyi varoituksin - -Nothing to synchronize -Ei mitään täsmäytettävää - -Synchronization completed successfully -Täsmäytys päättyi onnistuneesti - -Saving log file %x... -Lokitiedosto: tallennetaan %x... - -You can switch to FreeFileSync's main window to resolve this issue. -Siirry FreeFileSync -pääikkunaan korjataksesi virheen. - -&Don't show this warning again -&Älä enää näytä tätä varoitusta - -&Ignore -&Unohda - -&Switch -&Vaihda - -Switching to FreeFileSync's main window -Vaihdä FreeFileSync -pääikkunaan - -&Ignore subsequent errors -&Hylkää toistuvat virheet - -Serious Error -Vakava virhe - -Check for Program Updates -Etsi ohjelmaan päivitystä - -A new version of FreeFileSync is available: -FreeFileSync:n uusi verio on saatavilla: - -Download now? -Lataa nyt? - -&Download -&Lataa - -FreeFileSync is up to date. -FreeFileSync on ajan tasalla. - -Unable to connect to sourceforge.net. -Yhteys sourceforge.net:n ei onnistu. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Nykyinen FreeFileSync versio ei löydy verkosta, etsitäänkö manuaalisesti? - -Symlink -Pikakuvake - -Folder -Hakemisto - -Full path -Koko polku - -Name -Nimi - -Relative path -Suhteellinen polku - -Base folder -Juurihakemisto - -Size -Koko - -Date -Päiväys - -Extension -Laajennus - -Category -Luokka - -Action -Suorita - -Drag && drop -Vedä ja pudota - -Close progress dialog -Sulje etenämän dialoogi - -Standby -Lepotila - -Log off -Kirjaudu ulos - -Shut down -Sulje - -Hibernate -Horrostila - -Alternate comparison settings -Vaihtoehtoinen asetus vertailulle - -Alternate synchronization settings -Vaihtoehtoinen asetus täsmäytykselle - -Local filter -Paikallinen suodin - -Active -Aktiivinen - -None -Ei yhtään - -Remove alternate settings -Poista muut asetukset - -Clear filter settings -Nollaa suodin - -Copy -Monista - -Paste -Liitä - -Alternate Comparison Settings -Vaihtoehtoinen asetus vertailulle - -Alternate Synchronization Settings -Vaihtoehtoinen asetus täsmäytykselle - -Local Filter -Paikallinen suodin - -&New -&Uusi - -&Save -&Tallenna - -Save as &batch job... -Tallenna &eräajona... - -1. &Compare -1. &Vertaa - -2. &Synchronize -2. &Täsmäytä - -&Global settings -&Yleiset asetukset - -&Language -&Kieli - -&Find... -&Etsi... - -&Export file list... -&Vie tiedostojoukko... - -&Tools -&Asetukset - -&Check now -&Tarkista nyt - -Check &automatically once a week -Tarkista &viikoittain - -&Check for new version -&Hae uusi versio - -Compare -Vertaile - -Cancel -Lopeta - -Synchronize -Täsmäytä - -Add folder pair -Lisää hakemistopari - -Remove folder pair -Poista hakemistopari - -Swap sides -Puolten vaihto - -Close search bar -Sulje hakukenttä - -Find: -Etsi - -Match case -Täsmää kirjainkoko - -Save as batch job -Tallenna eräajona - -Hide excluded items -Piilota ohitettavat - -Show filtered or temporarily excluded files -Näytä suodatetut/nyt pois suljetut tiedostot - -Number of files and folders that will be created -Luotavien tiedostojen ja hakemistojen määrä - -Number of files that will be overwritten -Korvattavien tiedostojen ja hakemistojen määrä - -Number of files and folders that will be deleted -Poistettavien tiedostojen ja hakemistojen määrä - -Total bytes to copy -Kopioitava määrä tavuja - -Select a variant: -Valitse vaihtoehto: - -Identify equal files by comparing modification time and size. -Tunnista tiedostojen vastaavuus ajan ja koon mukaan. - -Identify equal files by comparing the file content. -Tunnista tiedostojen vastaavuus sisällöstä. - -Symbolic links: -Pikakuvike: - -More information -Lisää tietoa - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Löydä ja suorita muutokset molemmilla puolilla. Poistot, siirrot ja eroavuudet tunnistetaan tietokannan avulla. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Tee tarkka kopio vasemmasta oikealle. - -Copy new and updated files to the right folder. -Monista muuttuneet ja uudet oikealle. - -Configure your own synchronization rules. -Määritä omat täsmäytyssäännöt. - -Detect moved files -Tunnista siirretyt tiedostot - -Requires database files. Not supported by all file systems. -Vaatii tietokantaa. Ei toimi kaikissa käyttöjärjestelmissä. - -Delete files: -Poista tiedosto: - -Permanent -Pysyvä - -Delete or overwrite files permanently -Poista tai korvaa tiedostoja pysyvästi - -&Recycle bin -&Roskakori - -Back up deleted and overwritten files in the recycle bin -Tallenna poistetut/ylikirjoitetut tiedostot Roskakoriin - -Versioning -Versiointi - -Move files to a user-defined folder -Siirrä tiedostot määrättyyn hakemistoon - -Naming convention: -Nimeämis käytäntö: - -Show examples -Näytä esimerkki - -Handle errors: -Hoida virheet: - -Ignore -Ohita - -Hide all error and warning messages -Piilota kaikki virhe- ja varoitusviestit - -Pop-up -Pop-up - -Show pop-up on errors or warnings -Näytä Pop-Up (virheet/varoitukset) - -On completion: -Kun valmis: - -Start synchronization now? -Kännistetäänkö täsmäyts? - -Variant: -Vaihtoehto - -Statistics -Tilastot - -&Don't show this dialog again -&Älä näytä valitaa uudestaan - -Items found: -Osia löytyi: - -Speed: -Nopeus: - -Time remaining: -Aikaa jäljellä: - -Time elapsed: -Aikaa kulunut: - -Synchronizing... -Täsmäytetään... - -Minimize to notification area -Pienennä huomiokenttään - -Close -Sulje - -&Pause -&Keskeytä - -Stop -Keskeytä - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Luo eräajo-tiedosto automaattiseen täsmäytykseen. Käynnista tuplaklickillä tai ajasta: %x - -Stop synchronization at first error -Keskeytä täsmäytys ensimmäiseen virheeseen - -Show progress dialog -Näytä etenemä ikkuna - -Save log: -Tallenna loki: - -Limit: -Raja: - -Limit maximum number of log files -Lokeja enintäin - -How can I schedule a batch job? -Miten ajastan eräajon? - -Delete on both sides -Poista molemmilta puolilta - -Delete on both sides even if the file is selected on one side only -Poista molemmin puolin, vaikka tiedosto on valittu vain toisella puolella - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Ohitettavien tiedostojen ehdot täsmäytysessä. Anna hakemistopariin nähden suhteellinen osoite. - -Include: -Sisällytä: - -Exclude: -Sulje pois: - -Time span: -Aikajakso: - -File size: -Tiedosto koko: - -Minimum: -Vähintäin: - -Maximum: -Enintäin: - -&Clear -&Pyyhi - -The following settings are used for all synchronization jobs. -Nämä asetukset käytetään kaikkiin täsmäytyksiin. - -Fail-safe file copy -Varmennettu tiedostokopiointi - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -Kopioi väliaikaistiedostoon (*.ffs_tmp) ennen kohteen ylikirjoitusta. -Tällä varmistetaa eheys vaikka vakava virhe tapahtuisi. - - -(recommended) -(suositus) - -Copy locked files -Kopioi lukitut tiedostot - -Copy shared or locked files using the Volume Shadow Copy Service. -Jaettujen tai lukittujen tiedostojen kopiointi käyttäen Volume Shadow Copy Service - -(requires administrator rights) -(edellyttää pääkäyttäjän oikeuksia) - -Copy file access permissions -Kopioi tiedoston käyttöoikeuksia - -Transfer file and folder permissions. -Tiedosto ja hakemisto määreet siirretään. - -Automatic retry on error: -Automaattinen uusintayritys virheestä: - -Retry count: -Uusintayrityksiä: - -Delay (in seconds): -Viive (s): - -Customize context menu: -Yksilöi valikko: - -Description -Seloste - -Restore hidden windows -Palauta piiloitettu ikkuna - -&Default -&Vakio - -Source code written in C++ using: -Koodikieli on C++ käyttäen: - -If you like FreeFileSync -Jos pidät FreeFileSync:tä - -Donate with PayPal -Lahjoita PayPal:lla - -Feedback and suggestions are welcome -Palaute ja uudet ideat ovat tervetulleita - -Homepage -Kotisivu - -Email -S-posti - -Published under the GNU General Public License -Julkaistu lisenssillä GNU General Public License - -Many thanks for localization: -Paljon kiitoksia kääntäjille: - -Save as Batch Job -Tallenna eräajona - -Delete Items -Poista kohteet - -Global Settings -Yleinen asetus - -Select Time Span -Valitse aikajakso - -Folder Pairs -Hakemisto parit - -Find -Etsi - -Overview -Yleiskatsaus - -Configuration -Asetukset - -Main Bar -Pääpalkki - -Filter Files -Suodata tiedostot - -Select View -Valitse näkymä - -Open... -Avaa... - -Save -Tallenna - -Compare both sides -Vertaile molemmat puolet - -Comparison settings -Vertailun asetukset - -Synchronization settings -Täsmäytyksen asetukset - -Start synchronization -Käynnistä täsmäytys - -Confirm -Vahvista - -&Execute -&Suorita - - -1 directory -%x directories - - -1 hakemisto -%x hakemistoja - - - -1 file -%x files - - -1 tiedosto -%x tiedostoja - - - -%y of 1 row in view -%y of %x rows in view - - -%y rivi 1:stä näytetään -%y riviä %x:stä näytetään - - -Set direction: -Aseta suunta: - -multiple selection -monivalinta - -Include via filter: -Lisää suotimella: - -Exclude via filter: -Sulje pois suotimella: - -Exclude temporarily -Sulje pois, tilapäisesti - -Include temporarily -Sisällytä, tilapäisesti - -Delete -Poista - -Include all -Valitse kaikki - -Exclude all -Hylkää kaikki - -Show icons: -Näytä kuvakkeet: - -Small -Pieni - -Medium -Keskikoko - -Large -Iso - -Select time span... -Valitse aikajana... - -Default view -Oletusnäkymä - -Show "%x" -Näytä "%x" - -Last session -Edellinen istunto - -Folder Comparison and Synchronization -Hakemistojen vertailu ja täsmäytys - -Configuration saved -Asetukset tallennettu - -FreeFileSync batch -FreeFileSync eräajo - -Do you want to save changes to %x? -Haluatko tallentaa muutokset: %x? - -Never save &changes -Älä tallenna &muutoksia - -Do&n't save -Äl&ä tallenna - -Filter -Suodin - -Show files that exist on left side only -Näytä vain vasemmalla esiintyvät tiedostot - -Show files that exist on right side only -Näytä vain oikealla esiintyvät tiedostot - -Show files that are newer on left -Näytä vasemmalla olevat uudemmat tiedostot - -Show files that are newer on right -Näytä uudemmat tiedostot oikealla - -Show files that are equal -Näytä yhteneväiset tiedostot - -Show files that are different -Näytä erilaiset tiedostot - -Show conflicts -Näytä ristiriidat - -Show files that will be created on the left side -Näytä vasemmalle luotavat tiedostot - -Show files that will be created on the right side -Näytä oikealle luotavat tiedostot - -Show files that will be deleted on the left side -Näytä vasemmalta poistettavat tiedostot - -Show files that will be deleted on the right side -Näytä oikealta poistettavat tiedostot - -Show files that will be overwritten on left side -Näytä vasemmalla korvattavat tiedostot - -Show files that will be overwritten on right side -Näytä oikealla korvattavat tiedostot - -Show files that won't be copied -Näytä kopioimatta jäävät tiedostot - -Set as default -Aseta oletukseksi - -All folders are in sync -Kaikki hakemistot täsmäytetty - -Synchronization Settings -Täsmäytyksen asetukset - -Comparison Settings -Vertailun asetukset - -Cannot find %x -En löydä %x - -Comma-separated values -Pilkulla erotetut arvot - -File list exported -Tiedostolista viety - -Searching for program updates... -Ohjelmapäivytys haetaa... - -Scanning... -Tiedostoja haetaan... - -Comparing content... -Sisällön vertailu... - -Info -Info - -Warning -Varoitus - -Paused -Pysäytetty - -Initializing... -Alustus... - -Stopped -Keskeytys - -Completed -Valmis - -&Continue -&Jatka - -Log -Loki - -Today -Tänään - -This week -Tällä viikolla - -This month -Tässä kuussa - -This year -Tänä vuonna - -Last x days -Viimeiset x päivää - -Byte -tavua - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Haluatko varmasti siirtää kohde Roskakoriin? -Haluatko varmasti siirtää nämä %x kohteet Roskakoriin? - - -Move -Siirrä - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Haluatko todella poistta tämä? -Haluatko todella poistaa nämä %x kohdetta? - - -Exclude -Sulje pois - -Direct -Suoraan - -Follow -Seuraa - -Copy NTFS permissions -Monista NTFS oikeudet - -Integrate external applications into context menu. The following macros are available: -Liitä ulkoinen sovellus viitekehysvalikkoon. Seuraavat makrot ovat valittavissa: - -- full file or folder name -- tiedoston/hakemiston koko nimi - -- folder part only -- vain hakemisto-osa - -- Other side's counterpart to %item_path% -- Toisen puolen osa liittyy %item_path% - -- Other side's counterpart to %item_folder% -- Toisen puolen osa liittyy %item_folder% - -Restore all hidden windows and warnings? -Palauta kaikki piiloitetut ikkunat ja varoitukset? - -Leave as unresolved conflict -Jätä ratkaisemattomana virheenä - -Replace -Korvaa - -Move files and replace if existing -Siirrä tiedostot ja korvaa olemassaolevat - -Time stamp -Aikaleima - -Append a timestamp to each file name -Lisää aikaleima tiedostoihin - -File -Tiedosto - -YYYY-MM-DD hhmmss -VVVV-KK-PP ttmmss - -Files -Tiedostot - -Items -Kappaletta - -Percentage -Prosenttia - -Cannot monitor directory %x. -Hakemistoa %x ei voida tarkkaila. - -Conversion error: -Muunnosvirhe: - -Cannot delete file %x. -Ei voi poistaa tiedostoa %x. - -The file is locked by another process: -Tiedosto on toisen prosessin lukitsema: - -Cannot move file %x to %y. -Tiedostoa %x ei voida siirtää kohtaan %y. - -Cannot delete directory %x. -Hakemistoa %x ei voida poistaa. - -Cannot write file attributes of %x. -Tiedoston %x ominaisuuksia ei voitu tallentaa. - -Cannot write modification time of %x. -Tiedoston %x muutosaikaa ei voida kirjoittaa. - -Cannot read security context of %x. -Ei voi lukea %x -suojauskontekstia. - -Cannot write security context of %x. -Ei voi tallentaa %x -suojauskontekstia. - -Cannot read permissions of %x. -Ei voi lukea %x -oikeuksia. - -Cannot write permissions of %x. -Ei voi tallentaa %x -oikeuksia. - -Cannot create directory %x. -Hakemistoa %x ei voitu luoda. - -Cannot create symbolic link %x. -Pikakuvikkeen luonti epäonnistui %x. - -Cannot find system function %x. -Järjestelmäfunktio ei löydy, %x - -Cannot copy file %x to %y. -Tiedostoa %x ei voida kopioida kohtaan %y. - -Type of item %x is not supported: -Kohteen %x tyyppiä ei tueta: - -Cannot resolve symbolic link %x. -Tämä linkki on virheellinen %x. - -Cannot open directory %x. -Hakemistoa %x ei voida avata. - -Cannot enumerate directory %x. -Hakemistoa %x ei voitu luetella. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -1 min -%x min - - - -1 hour -%x hours - - -1 tunti -%x tuntia - - - -1 day -%x days - - -1 päivä -%x päivää - - -Unable to register to receive system messages. -Virheviestien vastaanotto ei onnistu. - -Cannot set privilege %x. -Oikeuksia %x ei voitu asettaa. - -Unable to suspend system sleep mode. -Lepotilan katkaisu ei onnistu. - -Cannot change process I/O priorities. -Prosessin I/O -pririteetin muutos ei onnistu. - -Unable to move %x to the recycle bin. -%x siirto Roskakoriin epäonnistui. - -Cannot determine final path for %x. -Polkua ei voida määrittää, %x. - -Error Code %x: -Virhe %x: - diff --git a/BUILD/Languages/french.lng b/BUILD/Languages/french.lng deleted file mode 100644 index d040b728..00000000 --- a/BUILD/Languages/french.lng +++ /dev/null @@ -1,1519 +0,0 @@ -
    - Français - Jean-François Hartmann - fr_FR - flag_france.png - 2 - n <= 1 ? 0 : 1 -
    - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -Retrying operation... - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Both sides have changed since last synchronization. -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 : - -No change since last synchronization. -Aucun changement depuis la dernière synchronisation. - -The database entry is not in sync considering current settings. -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. - -Checking recycle bin availability for folder %x... -Contrôle de la disponibilité de la Corbeille pour le dossier %x ... - -Moving file %x to the recycle bin -Déplacement du fichier %x vers la Corbeille< - -Moving folder %x to the recycle bin -Déplacement du dossier %x vers la Corbeille< - -Moving symbolic link %x to the recycle bin -Déplacement du lien symbolique %x vers la Corbeille - -Deleting file %x -Suppression du fichier %x - -Deleting folder %x -Suppression du dossier %x - -Deleting symbolic link %x -Suppression du lien symbolique %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -La Corbeille n'est pas disponible por les dossiers suivants. Les fichiers seront détruits définitivement : - -An exception occurred -Une erreur s'est produite - -A directory path is expected after %x. -Un chemin est attendu après %x. - -Syntax error -Erreur de syntaxe - -Cannot open file %x. -Impossible d'ouvrir le fichier %x. - -Error -Erreur - -File %x does not contain a valid configuration. -Le fichier %x ne contient pas une configuration valide. - -Unequal number of left and right directories specified. -Le nombre de répertoires à gauche et à droite n'est pas identique. - -The config file must not contain settings at directory pair level when directories are set via command line. -Le fichier de configuration ne peut pas contenir de paramètres au niveau de paires de répertoires quand ceux-ci sont définis par une ligne de commande. - -Warning -Attention - -Directories cannot be set for more than one configuration file. -Les répertoires ne peuvent pas être définis pour plus d'un fichier de configuration. - -Syntax: -Syntaxe : - -config files -fichiers de configuration - -directory -répertoire - -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 directories for at most one config file. -N'importe quel nombre de répertoires alternatifs pour au plus un fichier de configuration. - -Command line -Ligne de commande - -A folder input field is empty. -Une entrée dossier est vide. - -The corresponding folder will be considered as empty. -Le dossier correspondant sera considéré comme vide. - -Cannot find the following folders: -Impossible de trouver les dossiers suivants : - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -Vous pouvez ignorer cette erreur en considérant chaque dossier comme vide. Les dossiers seront automatiquement créés pendant la synchronisation. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -Les fichiers suivants ont des chemins interdépendants. Attention à la mise à jour des règles de synchronisation : - -File %x has an invalid date. -Le fichier %x a une date invalide. - -Date: -Date : - -Files %x have the same date but a different size. -Les fichiers %x ont la même date mais une taille différente. - -Size: -Taille : - -Items differ in attributes only -Seuls les attributs des éléments diffèrent - -Resolving symbolic link %x -Résolution du lien symbolique %x - -Comparing content of files %x -Comparaison du contenu des fichiers %x - -Generating file list... -Génération de la liste des fichiers... - -Starting comparison -Comparaison en cours - -Calculating sync directions... -Evaluation du sens des synchronisations ... - -Out of memory. -Mémoire insuffisante. - -Item exists on left side only -Cet élément existe seulement à gauche - -Item exists on right side only -Cet élément existe seulement à droite - -Left side is newer -L'élément de gauche est plus récent - -Right side is newer -L'élément de droite est plus récent - -Items have different content -Les élement ont un contenu différent - -Both sides are equal -Les deux cotés sont identiques - -Conflict/item cannot be categorized -Conflit/élément impossible à classer - -Copy new item to left -Copie du nouvel élément à gauche - -Copy new item to right -Copie du nouvel élément à droite - -Delete left item -Suppression de l'élément de gauche - -Delete right item -Suppression de l'élément de droite - -Move file on left -Déplacer les fichiers à gauche - -Move file on right -Déplacer les fichiers à droite - -Overwrite left item -Remplacement de l'élément de gauche - -Overwrite right item -Remplacement de l'élément de droite - -Do nothing -Ne rien faire - -Update attributes on left -Mise à jour des attributs à gauche - -Update attributes on right -Mise à jour des attributs à droite - -Database file %x is incompatible. -La base de données %x n'est pas compatible. - -Initial synchronization: -Première synchronisation : - -Database file %x does not yet exist. -La base de données %x n'existe plus. - -Database file is corrupt: -Le fichier Base de données est endommagé : - -Cannot write file %x. -Impossible d'écrire le fichier %x. - -Cannot read file %x. -Impossible de lire le fichier %x. - -Database files do not share a common session. -Les fichiers de la base de données ne font pas partie de la même session. - -Searching for folder %x... -Recherche du dossier %x... - -Cannot read file attributes of %x. -Impossible de lire les attributs du fichier %x. - -Cannot get process information. -Impossible d'obtenir les informations du traitement. - -Waiting while directory is locked (%x)... -En attente tant que le répertoire est verrouillé (%x)... - - -1 sec -%x sec - - -%x sec -%x sec - - -Creating file %x -Création du fichier %x - -Items processed: -Élements traités : - -Items remaining: -Élements restants : - -Total time: -Durée totale : - - -1 byte -%x bytes - - -%x octet -%x octets - - -%x MB -%x Mo - -%x KB -%x Ko - -%x GB -%x Go - -Error parsing file %x, row %y, column %z. -Erreur lors de l'analyse du fichier %x, ligne %y, colonne %z. - -Cannot set directory lock for %x. -Impossible de verrouiller le dossier %x - -Scanning: -Lecture en cours : - - -1 thread -%x threads - - -%x tâche -%x tâches - - -Encoding extended time information: %x -Codage de l'heure au format étendu : %x - -/sec -/sec - -%x items/sec -%x éléments/sec - -Configuration file %x loaded partially only. -Le fichier de configuration %x a été chargé seulement partiellement. - -Show in Explorer -Montrer dans l'explorateur - -Open with default application -Ouvrir avec l'application par défaut - -Browse directory -Parcourir le répertoire - -Cannot access the Volume Shadow Copy Service. -Impossible d'accéder au service Volume Shadow Copy. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Utilisez la version FreeFileSync 64-bit pour créer des "shadow copies" sur ce système. - -Cannot load file %x. -Impossible de charger le fichier %x. - -Cannot determine volume name for %x. -Impossible de déterminer le nom du volume de %x. - -Volume name %x is not part of file path %y. -Le nom de volume %x ne fait pas partie du chemin %y. - -Stop requested: Waiting for current operation to finish... -Arrêt demandé : En attente de la fin de l'opération en cours... - -Unable to create timestamp for versioning: -Impossible de créer l'horodatage des versions : - -Cannot read the following XML elements: -Impossible de lire les données XML suivantes : - -&Open... -&Ouvrir... - -Save &as... -S&auvegarder sous... - -&Quit -&Quitter - -&Program -&Programme - -&View help -&Afficher l'aide - -&About -A &Propos de - -&Help -&Aide - -Usage: -Utilisation : - -1. Select folders to watch. -1. Choisir les dossiers à examiner. - -2. Enter a command line. -2. Entrez une ligne de commande. - -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. - -Folders to watch: -Dossiers à surveiller : - -Add folder -Ajout d'un dossier - -Remove folder -Supprimer le dossier - -Browse -Parcourir - -Select a folder -Choisissez un dossier - -Idle time (in seconds): -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 : - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -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) - - -&Start -&Démarrer - -&Retry -&Réessayer - -Cancel -Annuler - -Build: %x -Généré : %x - -About -A propos de - -All files -Tous les fichiers - -Automated Synchronization -Synchronisation Automatique - -Directory monitoring active -Gestion des répertoires active - -Waiting until all directories are available... -En attente de la disponibilité de tous les répertoires ... - -&Restore -&Restaurer - -&Show error -&Afficher l'erreur - -&Exit -&Quitter - -Incorrect command line: -Ligne de commande incorrecte : - -File content -Contenu du fichier - -File time and size -Date et taille du fichier - -Two way -Deux sens - -Mirror -Miroir - -Update -Mise à Jour - -Custom -Personnaliser - -Multiple... -Multiple... - -Moving file %x to %y -Déplacement du fichier %x vers %y - -Moving folder %x to %y -Déplacement du dossier %x vers %y - -Moving symbolic link %x to %y -Déplacement du lien symbolique %x vers %y - -Removing old versions... -Suppression des anciennes versions... - -Creating symbolic link %x -Création du lien symbolique %x - -Creating folder %x -Création du dossier %x - -Overwriting file %x -Remplacement du fichier %x - -Overwriting symbolic link %x -Remplacement du lien symbolique %x - -Verifying file %x -Contrôle du fichier %x - -Updating attributes of %x -Mise à jour des attributs de %x - -Cannot find %x. -Impossible de trouver %x - -Target folder %x already existing. -Le dossier destination %x existe déjà. - -Target folder input field must not be empty. -L'entrée dossier de destination ne doit pas être vide. - -Please enter a target folder for versioning. -Veuillez entrer le dossier destinataire pour la gestion des versions. - -Source folder %x not found. -Dossier source %x non trouvé. - -The following items have unresolved conflicts and will not be synchronized: -Les éléments suivants sont en conflit non résolu et ne seront pas synchronisés : - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -Les dossiers suivants sont significativement différents. Assurez-vous que les dossiers correspondent avant de lancer la synchronisation. - -Not enough free disk space available in: -Espace disque insuffisant sur : - -Required: -Requis : - -Available: -Disponible : - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Un dossier faisant partie de paires multiples sera modifié. Veuillez vérifier les paramètres de synchronisation. - -Synchronizing folder pair: -Synchronisation de la paire de dossiers - -Generating database... -Génération de la base de données... - -Creating a Volume Shadow Copy for %x... -Création d'un Volume Shadow Copy pour %x ... - -Data verification error: %x and %y have different content. -Erreur lors de la vérification des données : %x et %y ont des contenus différents. - -job name -nom du job - -Synchronization stopped -Synchronisation arrêtée - -Synchronization completed with errors -Synchronisation terminée avec des erreurs - -Synchronization completed with warnings -Synchronisation terminée avec avertissements - -Nothing to synchronize -Rien à synchroniser - -Synchronization completed successfully -Synchronisation terminée sans erreurs - -Saving log file %x... -Enregistrement du fichier log %x... - -You can switch to FreeFileSync's main window to resolve this issue. -Vous pouvez basculer vers la fenêtre principale de FreeFileSync pour résoudre ce problème. - -Switching to FreeFileSync's main window -Basculer vers la fenêtre principale de FreeFileSync - -A new version of FreeFileSync is available: -Une nouvelle version de FreeFileSync est disponible - -Download now? -Télécharger maintenant ? - -Check for Program Updates -Recherche des Mises à Jour - -&Download -&Télécharger - -FreeFileSync is up to date. -FreeFileSync est à jour. - -Information -Information - -Unable to connect to sourceforge.net. -Impossible de se connecter à sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Impossible de trouver en ligne une nouvelle version de FreeFileSync.Voulez-vous le faire manuellement ? - -Symlink -Lien symbolique - -Folder -Dossier - -Full path -Chemin complet - -Name -Nom - -Relative path -Chemin relatif - -Base folder -Dossier de base - -Size -Taille - -Date -Date - -Extension -Extension - -Category -Catégorie - -Action -Action - -Drag && drop -Glisser && Déposer - -Close progress dialog -Fermer la fenêtre de progression - -Standby -Pause - -Log off -Quitter - -Shut down -Arrêter - -Hibernate -Figer - -Alternate comparison settings -configuration distincte de la comparaison - -Alternate synchronization settings -configuration distincte de la synchronisation - -Local filter -Filtre local - -Active -Actif - -None -Aucun - -Remove alternate settings -Supprimer les paramètres de rechange - -Clear filter settings -Effacer la configuration du filtrage - -Copy -Copier - -Paste -Coller - -Alternate Comparison Settings -Configuration Distincte de la Comparaison - -Alternate Synchronization Settings -Configuration Distincte de la Synchronisation - -Local Filter -Filtre Local - -&New -&Nouveau - -&Save -&Sauvegarder - -Save as &batch job... -Enregistrer en temps que job &batch... - -1. &Compare -1. &Comparer - -2. &Synchronize -2. &Synchroniser - -&Global settings -&Paramètres généraux - -&Language -&Langue - -&Find... -&Rechercher... - -&Export file list... -&Exportation de la liste des fichiers... - -&Tools -Ou&tils - -&Check now -&Contrôler maintenant - -Check &automatically once a week -Contrôler &automatiquement une fois par semaine - -&Check for new version -&Vérifier les nouvelles versions - -Compare -Comparer - -Synchronize -Synchroniser - -Add folder pair -Ajout d'un couple de dossiers - -Remove folder pair -Supprimer le couple de dossiers - -Swap sides -Permuter les côtés - -Close search bar -Fermer la barre de recherche - -Find: -Rechercher : - -Match case -Respecter la casse - -Save as batch job -Enrgistrer en temps que fichier batch - -Hide excluded items -Cacher les éléments exclus - -Show filtered or temporarily excluded files -Afficher les fichiers filtrés ou temporairement exclus - -Number of files and folders that will be created -Nombre de fichiers et de dossiers qui seront créés - -Number of files that will be overwritten -Nombre de fichiers qui seront remplacés - -Number of files and folders that will be deleted -Nombre de fichiers et de dossiers qui seront supprimés - -Total bytes to copy -Nombre total d'octets à copier - -Select a variant: -Choisir une variante : - -Identify equal files by comparing modification time and size. -Reconnaître les fichiers identiques à l'aide de leur taille et de leur date. - -Identify equal files by comparing the file content. -Reconnaître les fichiers identiques à l'aide de leur contenu. - -Symbolic links: -Liens symboliques - -More information -Plus d'informations - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Identifier et propager les modifications des deux côtés. Suppressions, déplacements et conflits sont détectés automatiquement en utilisant une base de données. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Créer une copie miroir du dossier de gauche qui correspondra exactement à celui de droite après la synchronisation. - -Copy new and updated files to the right folder. -Copier les fichiers nouveaux ou mis à jour dans le dossier de droite. - -Configure your own synchronization rules. -Paramétrage de vos règles de synchronisation. - -Detect moved files -Détection des fichiers déplacés - -Requires database files. Not supported by all file systems. -Exige les fichiers de la base de données. Non supporté par tous les systèmes de fichiers. - -Delete files: -Supprimer les fichiers : - -Permanent -Permanent - -Delete or overwrite files permanently -Supprimer ou écraser les fichiers définitivement - -Recycle bin -Corbeille - -Back up deleted and overwritten files in the recycle bin -Sauvegarder les fichier détruits ou écrasés dans la Corbeille - -Versioning -Gestion des versions - -Move files to a user-defined folder -Déplacer les fichiers vers un dossier utilisateur - -Naming convention: -Convention de nommage : - -Show examples -Afficher les exemples - -Handle errors: -Gestion des erreurs : - -Ignore -Ignorer - -Hide all error and warning messages -Masquer tous les messages d'erreurs et les avertissements - -Pop-up -Pop-up - -Show pop-up on errors or warnings -Montrer les messages d'avertissement ou d'erreur - -On completion: -A la fin : - -Start synchronization now? -Démarrer la synchronisation maintenant ? - -Variant: -Variante : - -Statistics -Statistiques - -&Don't show this dialog again -&Ne plus afficher cette boîte de dialogue - -Items found: -Élements trouvés : - -Speed: -Vitesse : - -Time remaining: -Temps restant : - -Time elapsed: -Temps écoulé : - -Synchronizing... -Synchronisation en cours... - -Minimize to notification area -Réduction à la zone de notification - -Close -Fermer - -&Pause -&Pause - -Stop -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 - -Stop synchronization at first error -Arrêter la synchronisation à la première erreur - -Show progress dialog -Montrer la fenêtre de progression - -Save log: -Sauvegarder le fichier log : - -Limit: -Mimite : - -Limit maximum number of log files -Nombre maximal de fichiers log - -How can I schedule a batch job? -Comment plannifier un job batch ? - -&Recycle bin -&Corbeille - -Delete on both sides -Suppression des deux côtés - -Delete on both sides even if the file is selected on one side only -Suppression des deux côtés même si le fichier n'est sélectionné que d'un seul côté - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Sélectionnez les règles de filtrage pour exclure certains fichiers de la synchronisation. Entrez les chemins des fichiers par rapport à leur paire de dossiers. - -Include: -Inclure : - -Exclude: -Exclure : - -Time span: -Intervalle de temps : - -File size: -Taille du fichier : - -Minimum: -Minimum : - -Maximum: -Maximum : - -&Clear -&Effacer - -The following settings are used for all synchronization jobs. -Les paramètres suivants sont utilisés lors de toutes les synchronisations. - -Fail-safe file copy -Copie de fichiers sécurisé - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -Copie vers un fichier temporaire (*.ffs_tmp) avant le remplacement de la cible. -Cela garantit la cohérence du système de fichiers en cas d'erreur grave. - - -(recommended) -(recommandé) - -Copy locked files -Copier les fichiers verrouillés - -Copy shared or locked files using the Volume Shadow Copy Service. -Copie les fichiers partagés ou verrouillés à l'aide du Service Volume Shadow Copy. - -(requires administrator rights) -(nécessite les droits administrateur) - -Copy file access permissions -Copie des droits d'accès aux fichiers - -Transfer file and folder permissions. -Transfert des autorisations des fichiers et dossiers. - -Automatic retry on error: -Nouvelle tentative automatique en cas d'erreur : - -Retry count: -Nombre de tentatives : - -Delay (in seconds): -Délai (en secondes) : - -Customize context menu: -Personnaliser le menu contextuel : - -Description -Description - -Restore hidden windows -Restaurer les fenêtres masquées - -&Default -&Défaut - -Source code written in C++ using: -Code source écrit en C++ utilisant : - -If you like FreeFileSync -Si vous aimez FreeFileSync - -Donate with PayPal -Faites un don avec PayPal - -Feedback and suggestions are welcome -Vos commentaires et vos suggestions sont les bienvenus - -Homepage -Accueil - -Email -Email - -Published under the GNU General Public License -Publié sous licence GNU General Public License - -Many thanks for localization: -Un grand merci pour la traduction à : - -Save as Batch Job -Sauvegarder comme Job Batch - -Delete Items -Supprimer les Éléments - -Global Settings -Configuration Générale - -Select Time Span -Sélection de l'Intervalle de Temps - -Folder Pairs -Paires de Dossiers - -Find -Rechercher - -Overview -Présentation - -Configuration -Configuration - -Main Bar -Barre Principale - -Filter Files -Filtrage des Fichiers - -Select View -Sélection de l'Affichage - -Open... -Ouvrir ... - -Save -Sauvegarder - -Compare both sides -Comparer les deux listes - -Comparison settings -Paramètres de comparaison - -Synchronization settings -Paramétrage de la synchronisation - -Start synchronization -Démarrer la synchronisation - -Confirm -Confirmer - -&Execute -&Exécuter - - -1 directory -%x directories - - -%x répertoire -%x répertoires - - - -1 file -%x files - - -%x fichier -%x fichiers - - - -%y of 1 row in view -%y of %x rows in view - - -vue de %y ligne sur %x -vue de %y lignes sur %x - - -Set direction: -Choix de la direction : - -multiple selection -sélection multiple - -Include via filter: -Inclure via le filtre : - -Exclude via filter: -Exclure à l'aide du filtre : - -Exclude temporarily -Exclure temporairement - -Include temporarily -Inclure temporairement - -Delete -Supprimer - -Include all -Inclure tout - -Exclude all -Exclure tout - -Show icons: -Afficher les icônes : - -Small -Petit - -Medium -Moyen - -Large -Grand - -Select time span... -Choisir un intervalle de temps... - -Default view -Vue par défaut - -Show "%x" -Afficher "%x" - -Last session -Dernière session - -Folder Comparison and Synchronization -Comparaison de dossiers et Synchronisation - -Configuration saved -Configuration enregistrée - -FreeFileSync batch -FreeFileSync batch - -Do you want to save changes to %x? -Voulez-vous enregistrer les modifications dans %x ? - -Do&n't save -&Ne pas Sauvegarder - -Never save &changes -Ne jamais sauvegarder les &modifications - -Filter -Filtre - -Show files that exist on left side only -Afficher les fichiers existant seulement à gauche - -Show files that exist on right side only -Afficher les fichiers existant seulement à droite - -Show files that are newer on left -Afficher les fichiers de gauche plus récents que ceux de droite - -Show files that are newer on right -Afficher les fichiers de droite plus récents que ceux de gauche - -Show files that are equal -Afficher les fichiers identiques - -Show files that are different -Afficher les fichiers différents - -Show conflicts -Afficher les conflits - -Show files that will be created on the left side -Afficher les fichiers qui seront créés à gauche - -Show files that will be created on the right side -Afficher les fichiers qui seront créés à droite - -Show files that will be deleted on the left side -Afficher les fichiers qui seront supprimés à gauche - -Show files that will be deleted on the right side -Afficher les fichiers qui seront supprimés à droite - -Show files that will be overwritten on left side -Afficher les fichiers qui seront écrasés à gauche - -Show files that will be overwritten on right side -Afficher les fichiers qui seront écrasés à droite - -Show files that won't be copied -Afficher les fichiers qui ne seront pas copiés - -Set as default -Définir par défaut - -All folders are in sync -Tous les dossiers sont synchronisés - -Synchronization Settings -Configuration de la Synchronisation - -Comparison Settings -Configuration de la Comparaison - -Cannot find %x -Impossible de trouver %x - -Comma-separated values -Valeurs séparées par une virgule - -File list exported -Liste des fichiers exportée - -Searching for program updates... -Recherche de mises à jour ... - -&Ignore subsequent errors -&Ignorer les erreurs suivantes - -&Ignore -&Ignorer - -Serious Error -Erreur Grave - -&Don't show this warning again -&Ne plus afficher cet avertissement - -&Switch -&Changer - -&Yes -&Oui - -&No -&Non - -Scanning... -Lecture en cours... - -Comparing content... -Comparaison du contenu... - -Info -Info - -Paused -En pause - -Initializing... -Initialisation... - -Stopped -Arrêté - -Completed -Terminé - -&Continue -&Continuer - -Log -Log - -Today -Aujourd'hui - -This week -Cette semaine - -This month -Ce mois - -This year -Cette année - -Last x days -Derniers x jours - -Byte -Octet - -KB -Ko - -MB -Mo - - -Do you really want to move the following item to the recycle bin? -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 ? - - -Move -Déplacer - - -Do you really want to delete the following item? -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 ? - - -Exclude -Exclure - -Direct -Direct - -Follow -Suivre - -Copy NTFS permissions -Copie des droits NTFS - -Integrate external applications into context menu. The following macros are available: -Inclure les applications externes dans le menu contextuel. Les macros suivantes sont disponibles : - -- full file or folder name -- nom du fichier ou du dossier complet - -- folder part only -- Partie dossier seulement - -- Other side's counterpart to %item_path% -- Duplication vers %item_path% - -- Other side's counterpart to %item_folder% -- Duplication vers %item_folder% - -Restore all hidden windows and warnings? -Reataurer toutes les fenêtres masquées et les avertissements ? - -Leave as unresolved conflict -Abandonner en tant que conflit non résolu - -Replace -Remplacer - -Move files and replace if existing -Déplacer les fichiers et remplacer ceux existant - -Time stamp -Horodatage - -Append a timestamp to each file name -Ajouter un horodatage à chaque fichier - -File -Fichier - -YYYY-MM-DD hhmmss -AAAA-MM-JJ hhmmss - -Files -Fichiers - -Items -Éléments - -Percentage -Pourcentage - -Cannot monitor directory %x. -Impossible de gérer le répertoire %x. - -Conversion error: -Erreur de conversion : - -Cannot delete file %x. -Impossible de supprimer le fichier %x. - -The file is locked by another process: -Le fichier est verrouillé par un autre process : - -Cannot move file %x to %y. -Impossible de déplacer le fichier %x vers %y. - -Cannot delete directory %x. -Impossible de supprimer le répertoire %x. - -Cannot write file attributes of %x. -Impossible d'écrire les attributs de fichier de %x. - -Cannot write modification time of %x. -Impossible d'écrire la date de modification de %x. - -Cannot read security context of %x. -Impossible de lire les paramètres de sécurité de %x. - -Cannot write security context of %x. -Impossible d'écrire les paramètres de sécurité de %x. - -Cannot read permissions of %x. -Impossible de lire les permissions de %x. - -Cannot write permissions of %x. -Impossible d'écrire les permissions de %x. - -Cannot create directory %x. -Impossible de créer le répertoire %x. - -Cannot create symbolic link %x. -Impossible de créer le lien symbolique %x. - -Cannot find system function %x. -Impossible de trouver la fonction système %x. - -Cannot copy file %x to %y. -Impossible de copier le fichier %x vers %y. - -Type of item %x is not supported: -Le type de l'élément %x n'est pas accepté : - -Cannot resolve symbolic link %x. -Impossible de résoudre le lien symbolique %x. - -Cannot open directory %x. -Impossible d'ouvrir le répertoire %x. - -Cannot enumerate directory %x. -Impossible de parcourir le répertoire %x. - -%x TB -%x To - -%x PB -%x Po - - -1 min -%x min - - -%x min -%x min - - - -1 hour -%x hours - - -%x heure -%x heures - - - -1 day -%x days - - -%x jour -%x jours - - -Unable to register to receive system messages. -Impossible d'enregistrer la réception des messages système. - -Cannot set privilege %x. -Impossible de fixer le privilège %x. - -Unable to suspend system sleep mode. -Impossible de suspendre le mode veille du système. - -Cannot change process I/O priorities. -Impossible de modifier les priorités E/S des tâches - -Unable to move %x to the recycle bin. -Impossible de déplacer %x dans la Corbeille. - -Cannot determine final path for %x. -Impossible de déterminer le chemin pour %x. - -Error Code %x: -Code erreur %x : - diff --git a/BUILD/Languages/german.lng b/BUILD/Languages/german.lng deleted file mode 100644 index ca271765..00000000 --- a/BUILD/Languages/german.lng +++ /dev/null @@ -1,1523 +0,0 @@ -
    - Deutsch - Zenju - de_DE - flag_germany.png - 2 - n == 1 ? 0 : 1 -
    - -&Check -&Prüfen - -Syntax error -Syntaxfehler - -Both sides have changed since last synchronization. -Beide Seiten wurden seit der letzten Synchronisation verändert. - -Cannot determine sync-direction: -Die Synchronisationsrichtung kann nicht bestimmt werden: - -No change since last synchronization. -Keine Änderungen seit der letzten Synchronisation. - -The database entry is not in sync considering current settings. -Der Datenbankeintrag ist nicht synchron gemäß den aktuellen Einstellungen. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Setze Standardwerte für Synchronisationsrichtungen: Alte Dateien werden durch neuere überschrieben. - -Checking recycle bin availability for folder %x... -Prüfe Verfügbarkeit des Papierkorbs für Ordner %x... - -Moving file %x to the recycle bin -Verschiebe Datei %x in den Papierkorb - -Moving folder %x to the recycle bin -Verschiebe Ordner %x in den Papierkorb - -Moving symbolic link %x to the recycle bin -Verschiebe Symbolischen Link %x in den Papierkorb - -Deleting file %x -Lösche Datei %x - -Deleting folder %x -Lösche Ordner %x - -Deleting symbolic link %x -Lösche Symbolischen Link %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -Der Papierkorb ist für die folgenden Ordner nicht verfügbar. Die Dateien werden stattdessen permanent gelöscht: - -An exception occurred -Eine Ausnahme ist aufgetreten - -A directory path is expected after %x. -Ein Verzeichnispfad wird nach %x erwartet. - -Cannot open file %x. -Die Datei %x kann nicht geöffnet werden. - -File %x does not contain a valid configuration. -Die Datei %x enthält keine gültige Konfiguration. - -Unequal number of left and right directories specified. -Ungleiche Anzahl linker und rechter Verzeichnisse angegeben. - -The config file must not contain settings at directory pair level when directories are set via command line. -Die Konfigurationsdatei darf keine Einstellungen auf Verzeichnispaarebene enthalten, wenn Verzeichnisse über die Befehlszeile gesetzt werden. - -Directories cannot be set for more than one configuration file. -Verzeichnisse können nicht für mehr als eine Konfigurationsdatei gesetzt werden. - -Command line -Kommandozeile - -Syntax: -Syntax: - -config files -Konfigurationsdateien - -directory -Verzeichnis - -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 directories for at most one config file. -Beliebige Anzahl von alternativen Verzeichnissen für höchstens eine Konfigurationsdatei. - -A folder input field is empty. -Ein Ordnereingabefeld ist leer. - -The corresponding folder will be considered as empty. -Der entsprechende Ordner wird als leer angesehen. - -Cannot find the following folders: -Die folgenden Ordner wurden nicht gefunden: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -Dieser Fehler kann ignoriert werden, um die Ordner als leer anzusehen und beim Synchronisieren automatisch zu erstellen. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -Die folgenden Ordner haben abhängige Pfade. Achtung beim Festlegen der Synchronisationsregeln: - -File %x has an invalid date. -Die Datei %x hat ein ungültiges Datum. - -Date: -Datum: - -Files %x have the same date but a different size. -Die Dateien %x haben dasselbe Datum, aber unterschiedliche Größen. - -Size: -Größe: - -Items differ in attributes only -Die Elemente unterscheiden sich nur in Attributen - -Resolving symbolic link %x -Folge dem Symbolischen Link %x - -Comparing content of files %x -Vergleiche Inhalt der Dateien %x - -Generating file list... -Erzeuge Dateiliste... - -Starting comparison -Starte Vergleich - -Calculating sync directions... -Berechne Synchronisationsrichtungen... - -Out of memory. -Nicht genügend Arbeitsspeicher. - -Item exists on left side only -Element existiert nur links - -Item exists on right side only -Element existiert nur rechts - -Left side is newer -Linke Seite ist neuer - -Right side is newer -Rechte Seite ist neuer - -Items have different content -Elemente haben unterschiedlichen Inhalt - -Both sides are equal -Beide Seiten sind gleich - -Conflict/item cannot be categorized -Konflikt/Element, das nicht eingeordnet werden kann - -Copy new item to left -Kopiere neues Element nach links - -Copy new item to right -Kopiere neues Element nach rechts - -Delete left item -Lösche linkes Element - -Delete right item -Lösche rechtes Element - -Move file on left -Verschiebe linke Datei - -Move file on right -Verschiebe rechte Datei - -Overwrite left item -Überschreibe linkes Element - -Overwrite right item -Überschreibe rechtes Element - -Do nothing -Nichts tun - -Update attributes on left -Aktualisiere Attribute des linken Elements - -Update attributes on right -Aktualisiere Attribute des rechten Elements - -Database file %x is incompatible. -Die Datenbankdatei %x ist nicht kompatibel. - -Initial synchronization: -Erstmalige Synchronisation: - -Database file %x does not yet exist. -Die Datenbankdatei %x existiert noch nicht. - -Database file is corrupt: -Die Datenbankdatei ist beschädigt: - -Cannot write file %x. -Die Datei %x kann nicht geschrieben werden. - -Cannot read file %x. -Die Datei %x kann nicht gelesen werden. - -Database files do not share a common session. -Die Datenbankdateien teilen keine gemeinsame Sitzung. - -Searching for folder %x... -Suche Ordner %x... - -Cannot read file attributes of %x. -Die Dateiattribute von %x können nicht gelesen werden. - -Cannot get process information. -Prozessinformationen können nicht gelesen werden. - -Waiting while directory is locked (%x)... -Warte während Verzeichnis gesperrt ist (%x)... - - -1 sec -%x sec - - -1 Sek. -%x Sek. - - -Creating file %x -Erstelle Datei %x - -Items processed: -Verarbeitete Elemente: - -Items remaining: -Verbleibende Elemente: - -Total time: -Gesamtzeit: - - -1 byte -%x bytes - - -1 Byte -%x Bytes - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Fehler beim Auswerten der Datei %x, Zeile %y, Spalte %z. - -Cannot set directory lock for %x. -Die Verzeichnissperre für %x kann nicht gesetzt werden. - -Scanning: -Suche Dateien: - - -1 thread -%x threads - - -1 Thread -%x Threads - - -Encoding extended time information: %x -Speichere erweiterte Zeitinformation: %x - -/sec -/sek - -%x items/sec -%x Elemente/sek - -Configuration file %x loaded partially only. -Die Konfigurationsdatei %x wurde nur teilweise geladen. - -Show in Explorer -Im Explorer anzeigen - -Open with default application -Mit Standardanwendung öffnen - -Browse directory -Verzeichnis öffnen - -Cannot access the Volume Shadow Copy Service. -Auf den Volumenschattenkopiedienst kann nicht zugegriffen werden. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Bitte benutzen Sie die FreeFileSync 64-Bit Version, um Schattenkopien auf diesem System zu erstellen. - -Cannot load file %x. -Die Datei %x kann nicht geladen werden. - -Cannot determine volume name for %x. -Der Laufwerksname für %x kann nicht bestimmt werden. - -Volume name %x is not part of file path %y. -Laufwerksname %x ist kein Teil des Dateipfades %y. - -Stop requested: Waiting for current operation to finish... -Stop wurde eingeleitet: Warte bis die aktuelle Operation beendet ist... - -Unable to create timestamp for versioning: -Der Zeitstempel für die Versionierung kann nicht erstellt werden: - -Cannot read the following XML elements: -Die folgenden XML-Elemente können nicht gelesen werden: - -&Open... -Ö&ffnen... - -Save &as... -Speichern &unter... - -&Quit -&Beenden - -&Program -&Programm - -&View help -&Hilfe anzeigen - -&About -&Über - -&Help -&Hilfe - -Usage: -Verwendung: - -1. Select folders to watch. -1. Zu überwachende Ordner wählen. - -2. Enter a command line. -2. Eine Befehlszeile angeben. - -3. Press 'Start'. -3. 'Start' drücken. - -To get started just import a .ffs_batch file. -Zum Schnelleinstieg einfach eine .ffs_batch Datei importieren. - -Folders to watch: -Zu überwachende Ordner: - -Add folder -Ordner hinzufügen - -Remove folder -Ordner entfernen - -Browse -Auswählen - -Select a folder -Ordner auswählen - -Idle time (in seconds): -Ruhezeit (in Sekunden): - -Idle time between last detected change and execution of command -Ruhezeit zwischen der letzten erkannten Änderung und dem Aufruf der Befehlszeile - -Command line: -Befehlszeile: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -Die Befehlszeile wird ausgelöst, wenn: -- Dateien oder Unterordner sich ändern -- neue Ordner erscheinen (z.B. Anschluss eines USB Sticks) - - -&Start -&Start - -&Retry -&Wiederholen - -Cancel -Abbrechen - -About -Über - -Build: %x -Build: %x - -All files -Alle Dateien - -Automated Synchronization -Automatisierte Synchronisation - -Directory monitoring active -Verzeichnisüberwachung ist aktiv - -Waiting until all directories are available... -Warte bis alle Verzeichnisse verfügbar sind... - -Error -Fehler - -&Restore -&Wiederherstellen - -&Show error -&Zeige Fehler - -&Exit -&Beenden - -Incorrect command line: -Ungültige Befehlszeile: - -File content -Dateiinhalt - -File time and size -Datum und Größe - -Two way -Zwei Wege - -Mirror -Spiegeln - -Update -Aktualisieren - -Custom -Eigene - -Multiple... -Verschiedene... - -Moving file %x to %y -Verschiebe Datei %x nach %y - -Moving folder %x to %y -Verschiebe Ordner %x nach %y - -Moving symbolic link %x to %y -Verschiebe Symbolischen Link %x nach %y - -Removing old versions... -Entferne alte Versionen... - -Creating symbolic link %x -Erstelle Symbolischen Link %x - -Creating folder %x -Erstelle Ordner %x - -Overwriting file %x -Überschreibe Datei %x - -Overwriting symbolic link %x -Überschreibe Symbolischen Link %x - -Verifying file %x -Verifiziere Datei %x - -Updating attributes of %x -Aktualisiere Attribute von %x - -Cannot find %x. -%x wurde nicht gefunden. - -Target folder %x already existing. -Der Zielordner %x existiert bereits. - -Target folder input field must not be empty. -Das Eingabefeld für den Zielordner darf nicht leer sein. - -Please enter a target folder for versioning. -Bitte geben Sie einen Zielorder für die Versionierung ein. - -Source folder %x not found. -Der Quellordner %x wurde nicht gefunden. - -The following items have unresolved conflicts and will not be synchronized: -Die folgenden Elemente haben ungelöste Konflikte und werden nicht synchronisiert werden: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -Die folgenden Ordner unterscheiden sich erheblich. Stellen Sie sicher, dass die richtigen Ordner für die Synchronisation ausgewählt sind. - -Not enough free disk space available in: -Nicht genügend freier Speicher verfügbar unter: - -Required: -Benötigt: - -Available: -Verfügbar: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Ein Ordner wird verändert werden, der Teil mehrerer Ordnerpaare ist. Bitte überprüfen Sie die Synchronisationseinstellungen. - -Synchronizing folder pair: -Synchronisiere Ordnerpaar: - -Generating database... -Erzeuge Synchronisationsdatenbank... - -Creating a Volume Shadow Copy for %x... -Erstelle eine Volumenschattenkopie für %x... - -Data verification error: %x and %y have different content. -Verifizierungsfehler: %x und %y haben unterschiedlichen Inhalt. - -job name -Auftragsname - -Synchronization stopped -Synchronisation gestoppt - -Synchronization completed with errors -Synchronisation mit Fehlern abgeschlossen - -Synchronization completed with warnings -Synchronisation mit Warnungen abgeschlossen - -Nothing to synchronize -Es gibt nichts zu synchronisieren - -Synchronization completed successfully -Synchronisation erfolgreich abgeschlossen - -Saving log file %x... -Speichere Logdatei %x... - -You can switch to FreeFileSync's main window to resolve this issue. -Sie können auf FreeFileSyncs Hauptfenster wechseln, um das Problem zu beheben. - -Switching to FreeFileSync's main window -Wechsle auf FreeFileSyncs Hauptfenster - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - -Automatische Wiederholung in 1 Sekunde... -Automatische Wiederholung in %x Sekunden... - - -Retrying operation... -Wiederhole Operation... - -Check for Program Updates -Suche nach neuer Programmversion - -A new version of FreeFileSync is available: -Eine neue Version von FreeFileSync ist verfügbar: - -Download now? -Jetzt herunterladen? - -&Download -&Download - -FreeFileSync is up to date. -FreeFileSync ist auf dem neuesten Stand. - -Unable to connect to sourceforge.net. -Es kann keine Verbindung zu Sourceforge.net aufgebaut werden. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Die aktuelle FreeFileSync Versionsnummer wurde online nicht gefunden. Möchten Sie manuell prüfen? - -Symlink -Symlink - -Folder -Ordner - -Full path -Absoluter Pfad - -Name -Name - -Relative path -Relativer Pfad - -Base folder -Basisordner - -Size -Größe - -Date -Datum - -Extension -Erweiterung - -Category -Kategorie - -Action -Aktion - -Drag && drop -Drag && Drop - -Close progress dialog -Schließe Fortschrittsdialog - -Standby -Energie sparen - -Log off -Abmelden - -Shut down -Herunterfahren - -Hibernate -Ruhezustand - -Alternate comparison settings -Alternative Vergleichseinstellungen - -Alternate synchronization settings -Alternative Synchronisationseinstellungen - -Local filter -Lokaler Filter - -Active -Aktiv - -None -Ohne - -Remove alternate settings -Alternative Einstellungen entfernen - -Clear filter settings -Filtereinstellungen löschen - -Copy -Kopieren - -Paste -Einfügen - -Alternate Comparison Settings -Alternative Vergleichseinstellungen - -Alternate Synchronization Settings -Alternative Synchronisationseinstellungen - -Local Filter -Lokaler Filter - -&New -&Neu - -&Save -&Speichern - -Save as &batch job... -Speichern als Batch-&Auftrag... - -1. &Compare -1. &Vergleichen - -2. &Synchronize -2. &Synchronisieren - -&Global settings -&Globale Einstellungen - -&Language -&Sprache - -&Find... -S&uchen... - -&Export file list... -Dateiliste e&xportieren... - -&Tools -E&xtras - -&Check now -&Jetzt prüfen - -Check &automatically once a week -&Automatisch wöchentlich prüfen - -&Check for new version -&Auf neue Version prüfen - -Compare -Vergleichen - -Synchronize -Synchronisieren - -Add folder pair -Ordnerpaar hinzufügen - -Remove folder pair -Ordnerpaar entfernen - -Swap sides -Seiten vertauschen - -Close search bar -Suchleiste schließen - -Find: -Suchen: - -Match case -Groß-/Kleinschreibung - -Save as batch job -Als Batch-Auftrag speichern - -Hide excluded items -Ausgeschlossene Elemente verstecken - -Show filtered or temporarily excluded files -Gefilterte oder temporär ausgeschlossene Dateien zeigen - -Number of files and folders that will be created -Anzahl der zu erstellenden Dateien und Ordner - -Number of files that will be overwritten -Anzahl der zu überschreibenden Dateien - -Number of files and folders that will be deleted -Anzahl der zu löschenden Dateien und Ordner - -Total bytes to copy -Gesamtmenge der zu kopierenden Daten - -Select a variant: -Variante wählen: - -Identify equal files by comparing modification time and size. -Erkenne gleiche Dateien anhand der Änderungszeit und Größe. - -Identify equal files by comparing the file content. -Erkenne gleiche Dateien durch Vergleich des Dateiinhaltes. - -Symbolic links: -Symbolische Links: - -More information -Mehr Information - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Identifiziere und propagiere Änderungen auf beiden Seiten. Löschungen, Verschiebungen und Konflikte werden automatisch mit Hilfe einer Datenbank erkannt. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Eine Spiegelkopie des linken Ordners erstellen, die dem rechten Ordner genau entspricht. - -Copy new and updated files to the right folder. -Neue und aktualisierte Dateien in den rechten Ordner kopieren. - -Configure your own synchronization rules. -Eigene Synchronisationsregeln definieren. - -Detect moved files -Verschobene Dateien erkennen - -Requires database files. Not supported by all file systems. -Benötigt Datenbankdateien. Wird nicht von allen Dateisystemen unterstützt. - -Delete files: -Dateien löschen: - -Permanent -Permanent - -Delete or overwrite files permanently -Dateien endgültig löschen oder überschreiben - -Recycle bin -Papierkorb - -Back up deleted and overwritten files in the recycle bin -Gelöschte und überschriebene Dateien im Papierkorb sichern - -Versioning -Versionierung - -Move files to a user-defined folder -Dateien in einen benutzerdefinierten Ordner verschieben - -Naming convention: -Namenskonvention: - -Show examples -Zeige Beispiele - -Handle errors: -Fehlerbehandlung: - -Ignore -Ignorieren - -Hide all error and warning messages -Alle Fehler- und Warnmeldungen unterdrücken - -Pop-up -Nachfragen - -Show pop-up on errors or warnings -Ein Auswahlfenster bei Fehlern oder Warnungen anzeigen - -On completion: -Nach Abschluss: - -Start synchronization now? -Synchronisation jetzt starten? - -Variant: -Variante: - -Statistics -Statistiken - -&Don't show this dialog again -&Diesen Dialog nicht mehr anzeigen - -Items found: -Gefundene Elemente: - -Speed: -Geschwindigkeit: - -Time remaining: -Verbleibende Zeit: - -Time elapsed: -Vergangene Zeit: - -Synchronizing... -Synchronisiere... - -Minimize to notification area -In das Benachrichtigungsfeld minimieren - -Close -Schließen - -&Pause -&Pause - -Stop -Stop - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Erstellt eine Batchdatei für die unbeaufsichtigte Synchronisation. Zum Starten die Datei doppelklicken oder in einen Taskplaner eintragen: %x - -Stop synchronization at first error -Synchronisation beim ersten Fehler anhalten - -Show progress dialog -Fortschrittsdialog zeigen - -Save log: -Log speichern: - -Limit: -Limit: - -Limit maximum number of log files -Maximale Anzahl der Logdateien begrenzen - -How can I schedule a batch job? -Wie können Batch-Aufträge in den Taskplaner eingetragen werden? - -&Recycle bin -&Papierkorb - -Delete on both sides -Auf beiden Seiten löschen - -Delete on both sides even if the file is selected on one side only -Lösche auf beiden Seiten, auch wenn die Datei nur auf einer Seite markiert ist - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Wählen Sie Filterregeln aus, um einzelne Dateien von der Synchronisation auszuschließen. Geben Sie Dateipfade relativ zu ihrem zugehörigen Ordnerpaar an. - -Include: -Einschließen: - -Exclude: -Ausschließen: - -Time span: -Zeitspanne: - -File size: -Dateigröße: - -Minimum: -Minimum: - -Maximum: -Maximum: - -&Clear -&Löschen - -The following settings are used for all synchronization jobs. -Die folgenden Einstellungen werden für alle Synchronisationsaufgaben verwendet. - -Fail-safe file copy -Dateien ausfallsicher kopieren - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -Kopiere in eine temporäre Datei (*.ffs_tmp) bevor das Ziel überschrieben wird. -Dadurch wird ein konsistenter Datenstand auch im schweren Fehlerfall garantiert. - - -(recommended) -(Empfohlen) - -Copy locked files -Gesperrte Dateien kopieren - -Copy shared or locked files using the Volume Shadow Copy Service. -Kopiere gesperrte Dateien mit Hilfe des Volumenschattenkopie-Dienstes. - -(requires administrator rights) -(Benötigt Administratorrechte) - -Copy file access permissions -Dateizugriffsberechtigungen kopieren - -Transfer file and folder permissions. -Übertrage Datei- und Ordnerberechtigungen. - -Automatic retry on error: -Automatische Wiederholung bei Fehlern: - -Retry count: -Wiederholungen: - -Delay (in seconds): -Verzögerung (in Sekunden): - -Customize context menu: -Kontextmenü anpassen: - -Description -Beschreibung - -Restore hidden windows -Versteckte Fenster wiederherstellen - -&Default -&Standard - -Source code written in C++ using: -Der Quellcode wurde in C++ geschrieben mit: - -If you like FreeFileSync -Wenn Sie FreeFileSync mögen - -Donate with PayPal -Mit PayPal spenden - -Feedback and suggestions are welcome -Feedback und Vorschläge sind willkommen - -Homepage -Homepage - -Email -Email - -Published under the GNU General Public License -Veröffentlicht unter der Allgemeinen Öffentlichen GNU-Lizenz - -Many thanks for localization: -Vielen Dank für die Lokalisation: - -Save as Batch Job -Als Batch-Auftrag speichern - -Delete Items -Elemente löschen - -Global Settings -Globale Einstellungen - -Select Time Span -Zeitspanne auswählen - -Serious Error -Schwerer Fehler - -Folder Pairs -Ordnerpaare - -Find -Suchen - -Overview -Übersicht - -Configuration -Konfiguration - -Main Bar -Hauptleiste - -Filter Files -Dateien filtern - -Select View -Ansicht auswählen - -Open... -Öffnen... - -Save -Speichern - -Compare both sides -Beide Seiten vergleichen - -Comparison settings -Vergleichseinstellungen - -Synchronization settings -Synchronisationseinstellungen - -Start synchronization -Synchronisation starten - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - -Soll der Befehl %y wirklich für ein Element ausgeführt werden? -Soll der Befehl %y wirklich für %x Elemente ausgeführt werden? - - -Confirm -Bestätigen - -&Execute -&Ausführen - - -1 directory -%x directories - - -1 Verzeichnis -%x Verzeichnisse - - - -1 file -%x files - - -1 Datei -%x Dateien - - - -%y of 1 row in view -%y of %x rows in view - - -Zeige %y von 1 Zeile -Zeige %y von %x Zeilen - - -Set direction: -Setze Richtung: - -multiple selection -Mehrfachauswahl - -Include via filter: -Über Filter einschließen: - -Exclude via filter: -Über Filter ausschließen: - -Exclude temporarily -Temporär ausschließen - -Include temporarily -Temporär einschließen - -Delete -Löschen - -Include all -Alle einschließen - -Exclude all -Alle ausschließen - -Show icons: -Symbole anzeigen: - -Small -Klein - -Medium -Mittel - -Large -Groß - -Select time span... -Zeitspanne auswählen... - -Default view -Standardansicht - -Show "%x" -Zeige "%x" - -Last session -Letzte Sitzung - -Folder Comparison and Synchronization -Ordnervergleich und Synchronisation - -Configuration saved -Konfiguration gespeichert - -FreeFileSync batch -FreeFileSync Batch - -Do you want to save changes to %x? -Möchten Sie die Änderungen an %x speichern? - -Do&n't save -&Nicht speichern - -Never save &changes -Änderungen nie &speichern - -Filter -Filter - -Show files that exist on left side only -Nur links existierende Dateien anzeigen - -Show files that exist on right side only -Nur rechts existierende Dateien anzeigen - -Show files that are newer on left -Auf beiden Seiten existierende Dateien anzeigen; linke Datei ist neuer - -Show files that are newer on right -Auf beiden Seiten existierende Dateien anzeigen; rechte Datei ist neuer - -Show files that are equal -Gleiche Dateien anzeigen - -Show files that are different -Ungleiche Dateien anzeigen - -Show conflicts -Konflikte zeigen - -Show files that will be created on the left side -Dateien die links erstellt werden anzeigen - -Show files that will be created on the right side -Dateien die rechts erstellt werden anzeigen - -Show files that will be deleted on the left side -Dateien die links gelöscht werden anzeigen - -Show files that will be deleted on the right side -Dateien die rechts gelöscht werden anzeigen - -Show files that will be overwritten on left side -Dateien die links überschrieben werden anzeigen - -Show files that will be overwritten on right side -Dateien die rechts überschrieben werden anzeigen - -Show files that won't be copied -Dateien die nicht kopiert werden anzeigen - -Set as default -Als Standard festlegen - -All folders are in sync -Alle Ordner sind synchron - -Synchronization Settings -Synchronisationseinstellungen - -Comparison Settings -Vergleichseinstellungen - -Cannot find %x -%x wurde nicht gefunden. - -Comma-separated values -Kommagetrennte Werte - -File list exported -Dateiliste exportiert - -Searching for program updates... -Suche nach aktualisierten Programmversionen... - -Scanning... -Suche Dateien... - -Comparing content... -Vergleiche Dateiinhalt... - -Info -Info - -Warning -Warnung - -Paused -Angehalten - -Initializing... -Initialisiere... - -Stopped -Angehalten - -Completed -Fertig - -&Continue -&Fortfahren - -Log -Protokoll - -Today -Heute - -This week -Diese Woche - -This month -Dieser Monat - -This year -Dieses Jahr - -Last x days -Letzte x Tage - -Byte -Byte - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Soll das folgende Element wirklich in den Papierkorb verschoben werden? -Sollen die folgenden %x Elemente wirklich in den Papierkorb verschoben werden? - - -Move -Verschieben - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Soll das folgende Element wirklich gelöscht werden? -Sollen die folgenden %x Elemente wirklich gelöscht werden? - - -Exclude -Ausschließen - -Direct -Direkt - -Follow -Folgen - -Copy NTFS permissions -NTFS Berechtigungen kopieren - -Integrate external applications into context menu. The following macros are available: -Integriert externe Anwendungen in das Kontextmenü. Die folgenden Makros stehen zur Verfügung: - -- full file or folder name -- kompletter Datei oder Ordnername - -- folder part only -- nur Ordneranteil - -- Other side's counterpart to %item_path% -- Entsprechung der anderen Seite zu %item_path% - -- Other side's counterpart to %item_folder% -- Entsprechung der anderen Seite zu %item_folder% - -Restore all hidden windows and warnings? -Sollen alle versteckten Fenster und Warnungen wiederhergestellt werden? - -Leave as unresolved conflict -Als unbehandelten Konflikt belassen - -Replace -Ersetzen - -Move files and replace if existing -Dateien verschieben und vorhandene ersetzen - -Time stamp -Zeitstempel - -Append a timestamp to each file name -Einen Zeitstempel an jeden Dateinamen anhängen - -File -Datei - -YYYY-MM-DD hhmmss -JJJJ-MM-TT hhmmss - -Files -Dateien - -Items -Elemente - -Percentage -Prozent - -&Ignore subsequent errors -&Nachfolgende Fehler ignorieren - -&Ignore -&Ignorieren - -&Don't show this warning again -&Diese Warnung nicht mehr anzeigen - -&Switch -&Wechseln - -&Yes -&Ja - -&No -&Nein - -Cannot monitor directory %x. -Das Verzeichnis %x kann nicht überwacht werden. - -Conversion error: -Fehler bei Konvertierung: - -Cannot delete file %x. -Die Datei %x kann nicht gelöscht werden. - -The file is locked by another process: -Die Datei wird von einem anderen Prozess gesperrt: - -Cannot move file %x to %y. -Die Datei %x kann nicht nach %y verschoben werden. - -Cannot delete directory %x. -Das Verzeichnis %x kann nicht gelöscht werden. - -Cannot write file attributes of %x. -Die Dateiattribute von %x können nicht geschrieben werden. - -Cannot write modification time of %x. -Die Änderungszeit von %x kann nicht geschrieben werden. - -Cannot read security context of %x. -Der Sicherheitskontext von %x kann nicht gelesen werden. - -Cannot write security context of %x. -Der Sicherheitskontext von %x kann nicht geschrieben werden. - -Cannot read permissions of %x. -Die Berechtigungen von %x können nicht gelesen werden. - -Cannot write permissions of %x. -Die Berechtigungen von %x können nicht geschrieben werden. - -Cannot create directory %x. -Das Verzeichnis %x kann nicht erstellt werden. - -Cannot create symbolic link %x. -Der Symbolische Link %x kann nicht erstellt werden. - -Cannot find system function %x. -Die Systemfunktion %x wurde nicht gefunden. - -Cannot copy file %x to %y. -Die Datei %x kann nicht nach %y kopiert werden. - -Type of item %x is not supported: -Der Typ des Elements %x wird nicht unterstützt: - -Cannot resolve symbolic link %x. -Der Symbolische Link %x kann nicht aufgelöst werden. - -Cannot open directory %x. -Das Verzeichnis %x kann nicht geöffnet werden. - -Cannot enumerate directory %x. -Das Verzeichnis %x kann nicht gelistet werden. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -1 Min. -%x Min. - - - -1 hour -%x hours - - -1 Stunde -%x Stunden - - - -1 day -%x days - - -1 Tag -%x Tage - - -Unable to register to receive system messages. -Die Registrierung zum Empfang von Systemmeldungen ist fehlgeschlagen. - -Cannot set privilege %x. -Das Privileg %x kann nicht gesetzt werden. - -Unable to suspend system sleep mode. -Der Schlafmodus des Betriebssystems kann nicht ausgesetzt werden. - -Cannot change process I/O priorities. -Die Eingabe/Ausgabe Prioritäten für den Prozess kann nicht geändert werden. - -Unable to move %x to the recycle bin. -%x kann nicht in den Papierkorb verschoben werden. - -Cannot determine final path for %x. -Der endgültige Pfad für %x kann nicht ermittelt werden. - -Error Code %x: -Fehlercode %x: - diff --git a/BUILD/Languages/greek.lng b/BUILD/Languages/greek.lng deleted file mode 100644 index 4b74807f..00000000 --- a/BUILD/Languages/greek.lng +++ /dev/null @@ -1,1515 +0,0 @@ -
    - Eλληνικά - Γιώργος Δ. Γιαγλής - el_GR - flag_greece.png - 2 - n == 1 ? 0 : 1 -
    - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -&Check - - -Retrying operation... - - -Both sides have changed since last synchronization. -Και οι δυο πλευρές έχουν αλλάξει από τον τελευταίο συγχρονισμό. - -Cannot determine sync-direction: -Δεν μπορεί να προσδιοριστεί η κατεύθυνση συγχρονισμού: - -No change since last synchronization. -Καμία αλλαγή από τον προηγούμενο συγχρονισμό. - -The database entry is not in sync considering current settings. -Με βάση τις τρέχουσες ρυθμίσεις η βάση δεδομένων δεν είναι συγχρονισμένη. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Ρύθμιση προεπιλεγμένης κατεύθυνσης συγχρονισμού: Τα νεότερα αρχεία θα αντικαταστήσουν τα παλιότερα. - -Checking recycle bin availability for folder %x... -Έλεγχος της διαθεσιμότητας του κάδου ανακύκλωσης για τον υποκατάλογο %x... - -Moving file %x to the recycle bin -Μεταφορά του αρχείου %x στον κάδο ανακύκλωσης - -Moving folder %x to the recycle bin -Μεταφορά του υποκαταλόγου %x στον κάδο ανακύκλωσης - -Moving symbolic link %x to the recycle bin -Μεταφορά του συμβολικού δεσμού %x στον κάδο ανακύκλωσης - -Deleting file %x -Διαγραφή του αρχείου %x - -Deleting folder %x -Διαγραφή του υποκαταλόγου %x - -Deleting symbolic link %x -Διαγραφή του συμβολικού δεσμού %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -Ο κάδος ανακύκλωσης δεν είναι διαθέσιμος για τους ακόλουθους υποκαταλόγους. Τα αρχεία θα διαγραφούν μόνιμα: - -An exception occurred -Παρουσιάστηκε σφάλμα - -A directory path is expected after %x. -Αναμένεται μια διαδρομή υποκαταλόγου μετά το %x. - -Syntax error -Σφάλμα σύνταξης - -Cannot open file %x. -Δεν είναι δυνατό το άνοιγμα του αρχείου %x. - -File %x does not contain a valid configuration. -Το αρχείο %x δεν περιέχει μια έγκυρη διάταξη. - -Unequal number of left and right directories specified. -Έχει οριστεί άνισος αριθμός υποκαταλόγων δεξιά από ό,τι αριστερά. - -The config file must not contain settings at directory pair level when directories are set via command line. -Το αρχείο διάταξης δεν πρέπει να περιλαμβάνει ρυθμίσεις στο επίπεδο του ζεύγους των υποκαταλόγων, όταν οι υποκατάλογοι ορίζονται μέσω της γραμμής εργασιών. - -Directories cannot be set for more than one configuration file. -Δεν μπορούν να οριστούν υποκατάλογοι για περισσότερα από ένα αρχεία διάταξης. - -Command line -Γραμμή εντολών - -Syntax: -Σύνταξη: - -config files -αρχεία διάταξης - -directory -υποκατάλογος - -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Οποιοσδήποτε αριθμός αρχείων διάταξης .ffs_gui ή/και .ffs_batch του FreeFileSync - -Any number of alternative directories for at most one config file. -Οποιοσδήποτε αριθμός εναλλακτικών υποκαταλόγων για το πολύ ένα αρχείο διάταξης. - -A folder input field is empty. -Ένα πεδίο εισαγωγής υποκαταλόγων είναι άδειο. - -The corresponding folder will be considered as empty. -Ο αντίστοιχος υποκατάλογος θα θεωρηθεί κενός. - -Cannot find the following folders: -Οι ακόλουθοι υποκατάλογοι δεν ήταν δυνατό να βρεθούν: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -Μπορείτε να αγνοήσετε αυτό το σφάλμα, για να αντιμετωπίσετε κάθε υποκατάλογο ως κενό. Οι υποκατάλογοι θα δημιουργηθούν αυτόματα κατά τη διάρκεια του συγχρονισμού. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -Οι παρακάτω υποκατάλογοι δεν έχουν σταθερή διαδρομή. Προσοχή όταν ορίσετε κανόνες συγχρονισμού: - -File %x has an invalid date. -Το αρχείο %x δεν έχει έγκυρη ημερομηνία. - -Date: -Ημερομηνία: - -Files %x have the same date but a different size. -Τα αρχεία %x έχουν την ίδια ημερομηνία αλλά διαφορετικό μέγεθος. - -Size: -Μέγεθος: - -Items differ in attributes only -Τα στοιχεία διαφέρουν μόνο ως προς τα χαρακτηριστικά τους - -Resolving symbolic link %x -Επίλυση του συμβολικού δεσμού %x - -Comparing content of files %x -Σύγκριση του περιεχομένου των αρχείων %x - -Generating file list... -Δημιουργία καταλόγου αρχείων... - -Starting comparison -Έναρξη της σύγκρισης - -Calculating sync directions... -Υπολογισμός των κατευθύνσεων συγχρονισμού... - -Out of memory. -Ανεπαρκής μνήμη. - -Item exists on left side only -Το αντικείμενο υπάρχει μόνο στην αριστερή πλευρά - -Item exists on right side only -Το αντικείμενο υπάρχει μόνο στη δεξιά πλευρά - -Left side is newer -Το αντικείμενο στην αριστερή πλευρά είναι πιο πρόσφατο - -Right side is newer -Το αντικείμενο στη δεξιά πλευρά είναι πιο πρόσφατο - -Items have different content -Τα αντικείμενα έχουν διαφορετικό περιεχόμενο - -Both sides are equal -Οι δυο πλευρές είναι ίδιες - -Conflict/item cannot be categorized -Διένεξη/το αντικείμενο δεν μπορεί να κατηγοριοποιηθεί - -Copy new item to left -Αντιγραφή του νέου στοιχείου στα αριστερά - -Copy new item to right -Αντιγραφή του νέου στοιχείου στα δεξιά - -Delete left item -Διαγραφή του στοιχείου στα αριστερά - -Delete right item -Διαγραφή του στοιχείου στα δεξιά - -Move file on left -Μεταφορά του αρχείου που βρίσκεται αριστερά - -Move file on right -Μεταφορά του αρχείου που βρίσκεται δεξιά - -Overwrite left item -Αντικατάσταση του στοιχείου που βρίσκεται στα αριστερά - -Overwrite right item -Αντικατάσταση του στοιχείου που βρίσκεται στα δεξιά - -Do nothing -Καμία ενέργεια - -Update attributes on left -Ενημέρωση των στοιχείων στα αριστερά - -Update attributes on right -Ενημέρωση των στοιχείων στα δεξιά - -Database file %x is incompatible. -Το αρχείο βάσης δεδομένων %x δεν είναι συμβατό - -Initial synchronization: -Αρχικός συγχρονισμός: - -Database file %x does not yet exist. -Το αρχείο βάσης δεδομένων %x δεν υπάρχει ακόμα. - -Database file is corrupt: -Το αρχείο βάσης δεδομένων είναι αλλοιωμένο: - -Cannot write file %x. -Δεν μπορεί να γίνει εγγραφή του αρχείου %x. - -Cannot read file %x. -Δεν μπορεί να γίνει ανάγνωση του αρχείου %x. - -Database files do not share a common session. -Τα αρχεία βάσης δεδομένων δεν έχουν χρησιμοποιηθεί από κοινού σε συγχρονισμό. - -Searching for folder %x... -Αναζήτηση του υποκαταλόγου %x... - -Cannot read file attributes of %x. -Δεν μπορεί να γίνει ανάγνωση των χαρακτηριστικών του αρχείου %x. - -Cannot get process information. -Δεν μπορούν να ληφθούν πληροφορίες για τις τρέχουσες διαδικασίες. - -Waiting while directory is locked (%x)... -Αναμονή μέχρι να κλειδωθεί ο υποκατάλογος (%x)... - - -1 sec -%x sec - - -1 δ/λεπτο -%x δ/λεπτα - - -Creating file %x -Δημιουργία του αρχείου %x - -Items processed: -Επεξεργάστηκαν στοιχεία: - -Items remaining: -Περισσεύουν στοιχεία: - -Total time: -Συνολική διάρκεια: - - -1 byte -%x bytes - - -1 byte -%x bytes - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Σφάλμα κατά την ανάλυση του αρχείου %x, γραμμή %y, στήλη %z. - -Cannot set directory lock for %x. -Ο υποκατάλογος για το %x δεν μπορεί να κλειδωθεί. - -Scanning: -Ανίχνευση: - - -1 thread -%x threads - - -1 νήμα -%x νήματα - - -Encoding extended time information: %x -Κωδικοποίηση εκτεταμένων πληροφοριών για την ώρα: %x - -/sec -/δευτερόλεπτο - -%x items/sec -%x στοιχεία - -Configuration file %x loaded partially only. -Το αρχείο διάταξης %x έχει φορτωθεί μόνο κατά ένα μέρος. - -Show in Explorer -Εμφάνιση στην Εξερεύνηση - -Open with default application -Άνοιγμα με την προεπιλεγμένη εφαρμογή - -Browse directory -Αναζήτηση υποκαταλόγου - -Cannot access the Volume Shadow Copy Service. -Δεν είναι δυνατή η πρόσβαση στην Υπηρεσία Σκιώδους Αντίγραφου Τόμου. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Χρησιμοποιήστε την 64-μπιτη έκδοση του FreeFileSync για να δημιουργήσετε σκιώδη αντίγραφα σε αυτόν τον υπολογιστή. - -Cannot load file %x. -Το αρχείο %x δεν ήταν δυνατόν να φορτωθεί. - -Cannot determine volume name for %x. -Δεν μπορεί να προσδιοριστεί το όνομα τόμου για το %x. - -Volume name %x is not part of file path %y. -Το όνομα τόμου %x δεν είναι μέρος της διαδρομής %y. - -Stop requested: Waiting for current operation to finish... -Αίτημα διακοπής: Αναμονή για την ολοκλήρωση της τρέχουσας εργασίας... - -Unable to create timestamp for versioning: -Αδυναμία δημιουργίας χρονικής σήμανσης για διατήρηση εκδόσεων: - -Cannot read the following XML elements: -Δεν ήταν δυνατό να αναγνωσθούν τα ακόλουθα στοιχεία XML: - -&Open... -Ά&νοιγμα... - -Save &as... -Αποθήκευση &ως... - -&Quit -Έ&ξοδος - -&Program -&Πρόγραμμα - -&View help -&Προβολή βοήθειας - -&About -&Σχετικά - -&Help -&Βοήθεια - -Usage: -Χρήση: - -1. Select folders to watch. -1. Επιλέξτε τους υποκαταλόγους που θα παρακολουθούνται. - -2. Enter a command line. -2. Εισάγετε μια γραμμή εντολών. - -3. Press 'Start'. -3. Πατήστε το 'Έναρξη'. - -To get started just import a .ffs_batch file. -Για να ξεκινήσετε μπορείτε απλά να εισάγετε ένα αρχείο .ffs_batch. - -Folders to watch: -Υποκατάλογοι για παρακολούθηση: - -Add folder -Προσθήκη υποκαταλόγου - -Remove folder -Διαγραφή του υποκαταλόγου - -Browse -Αναζήτηση - -Select a folder -Επιλογή υποκαταλόγου - -Idle time (in seconds): -Λανθάνων χρόνος (σε δευτερόλεπτα): - -Idle time between last detected change and execution of command -Λανθάνων χρόνος μεταξύ τελευταίας αλλαγής που ανιχνεύθηκε και εκτέλεσης της εντολής - -Command line: -Γραμμή εντολών - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -Αυτή η εντολή πραγματοποιείται εάν: --αρχεία ή υποκτάλογοι αλλάξουν --παρουσιαστούν νέοι υποκατάλογοι (π.χ. εισαχθεί ένα φλασάκι USB) - - -&Start -Έ&ναρξη - -About -Σχετικά με το... - -Build: %x -Δημιουργήθηκε : %x - -All files -Όλα τα αρχεία - -Automated Synchronization -Αυτοματοποιημένος Συγχρονισμός - -Directory monitoring active -Η παρακολούθηση των υποκαταλόγων είναι ενεργή - -Waiting until all directories are available... -Αναμονή μέχρι να είναι διαθέσιμοι όλοι οι υποκατάλογοι... - -Error -Σφάλματα - -&Restore -&Επαναφορά - -&Show error -&Εμφάνιση σφάλματος - -&Exit -Έ&ξοδος - -Incorrect command line: -Εσφαλμένη γραμμή εντολών: - -&Retry -&Επανάληψη - -File content -Περιεχόμενο αρχείων - -File time and size -Ημερομηνία και μέγεθος αρχείων - -Two way -Διπλής κατεύθυνσης - -Mirror -Κατοπτρισμός - -Update -Ενημέρωση - -Custom -Εξατομίκευση - -Multiple... -Πολλαπλές ρυθμίσεις... - -Moving file %x to %y -Μεταφορά του αρχείου %x στο %y - -Moving folder %x to %y -Μεταφορά του υποκαταλόγου %x στο %y - -Moving symbolic link %x to %y -Μεταφορά του συμβολικού δεσμού %x στο %y - -Removing old versions... -Διαγραφή παλιών εκδόσεων... - -Creating symbolic link %x -Δημιουργία του συμβολικού δεσμού %x - -Creating folder %x -Δημιουργία του υποκαταλόγου %x - -Overwriting file %x -Αντικατάσταση του αρχείου %x - -Overwriting symbolic link %x -Αντικατάσταση του συμβολικού δεσμού %x - -Verifying file %x -Επικύρωση του αρχείου %x - -Updating attributes of %x -Ενημέρωση των χαρακτηριστικών αρχείου του %x - -Cannot find %x. -Το %x δεν μπορεί να βρεθεί. - -Target folder %x already existing. -Ο υποκατάλογος-στόχος %x υπάρχει ήδη. - -Target folder input field must not be empty. -Το πεδίο εισαγωγής του υποκαταλόγου-στόχου πρέπει να μην είναι κενό. - -Please enter a target folder for versioning. -Παρακαλώ εισάγετε έναν υποκατάλογο για αποθήκευση εκδόσεων. - -Source folder %x not found. -Ο υποκατάλογος %x δε βρέθηκε. - -The following items have unresolved conflicts and will not be synchronized: -Τα ακόλουθα στοιχεία έχουν ανεπίλυτες διενέξεις και δε θα συγχρονιστούν: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -Οι ακόλουθοι υποκατάλογοι είναι σημαντικά διαφορετικοί. Σιγουρευτείτε ότι έχετε επιλέξει τους κατάλληλους υποκαταλόγους για συγχρονισμό. - -Not enough free disk space available in: -Δεν υπάρχει αρκετός διαθέσιμος χώρος στο δίσκο: - -Required: -Απαιτείται: - -Available: -Διαθέσιμος: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Θα τροποποιηθεί ένας υποκατάλογος που είναι μέρος από πολλαπλά ζεύγη υποκαταλόγων. Παρακαλούμε επανελέγξτε τις ρυθμίσεις συγχρονισμού. - -Synchronizing folder pair: -Συγχρονίζεται το ζευγάρι υποκαταλόγων: - -Generating database... -Δημιουργία βάσης δεδομένων... - -Creating a Volume Shadow Copy for %x... -Δημιουργία Σκιώδους Αντίγραφου Τόμου για το %x... - -Data verification error: %x and %y have different content. -Σφάλμα επαλήθευσης δεδομένων: το %x και το %y έχουν διαφορετικό περιεχόμενο. - -job name -όνομα ενέργειας - -Synchronization stopped -Ο συγχρονισμός διακόπηκε - -Synchronization completed with errors -Ο συγχρονισμός ολοκληρώθηκε με σφάλματα - -Synchronization completed with warnings -Ο συγχρονισμός ολοκληρώθηκε με προειδοποιήσεις - -Nothing to synchronize -Δεν υπάρχει τίποτα προς συγχρονισμό - -Synchronization completed successfully -Ο συγχρονισμός ολοκληρώθηκε επιτυχώς - -Saving log file %x... -Αποθήκευση του αρχείου καταγραφής %x... - -You can switch to FreeFileSync's main window to resolve this issue. -Μπορείτε να επιστρέψετε στο κύριο παράθυρο του FreeFileSync για να επιλύσετε αυτό το θέμα. - -&Don't show this warning again -&Να μην εμφανιστεί ξανά αυτή η προειδοποίηση - -&Ignore -&Παράβλεψη - -&Switch -&Εναλλαγή - -Switching to FreeFileSync's main window -Επιστροφή στο κύριο παράθυρο του FreeFileSync - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - -Αυτόματη επανάληψη σε 1 δευτερόλεπτο... -Αυτόματη επανάληψη σε %x δευτερόλεπτα... - - -&Ignore subsequent errors -&Αγνόηση επόμενων σφαλμάτων - -Serious Error -Σοβαρό Σφάλμα - -Check for Program Updates -Έλεγχος για Ενημερώσεις του Προγράμματος - -A new version of FreeFileSync is available: -Μια νέα έκδοση του FreeFileSync είναι διαθέσιμη: - -Download now? -Λήψη τώρα; - -&Download -&Λήψη - -FreeFileSync is up to date. -Το FreeFileSync είναι ενημερωμένο. - -Unable to connect to sourceforge.net. -Δεν είναι δυνατή η σύνδεση με το sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Ο αριθμός της τρέχουσας έκδοσης του FreeFileSync δεν βρέθηκε στο δίκτυο. Θέλετε να το ελέγξετε εσείς; - -Symlink -Συμβολικός δεσμός - -Folder -Υποκατάλογος - -Full path -Πλήρης διαδρομή - -Name -Όνομα - -Relative path -Σχετική διαδρομή - -Base folder -Βασικός υποκατάλογος - -Size -Μέγεθος - -Date -Ημερομηνία - -Extension -Επέκταση - -Category -Κατηγορία - -Action -Ενέργεια - -Drag && drop -Μεταφορά && Απόθεση - -Close progress dialog -Κλείσιμο του παράθυρου αναφοράς - -Standby -Αναστολή λειτουργίας - -Log off -Αποσύνδεση - -Shut down -Τερματισμός λειτουργίας - -Hibernate -Αδρανοποίηση - -Alternate comparison settings -Διαφοροποιημένες ρυθμίσεις σύγκρισης - -Alternate synchronization settings -Διαφοροποιημένες ρυθμίσεις συγχρονισμού - -Local filter -Τοπικό φίλτρο - -Active -Ενεργό - -None -Καθόλου - -Remove alternate settings -Διαγραφή των διαφοροποιημένων ρυθμίσεων - -Clear filter settings -Διαγραφή όλων των ρυθμίσεων φίλτρου - -Copy -Αντιγραφή - -Paste -Επικόλληση - -Alternate Comparison Settings -Διαφοροποιημένες Ρυθμίσεις Σύγκρισης - -Alternate Synchronization Settings -Διαφοροποιημένες Ρυθμίσεις Συγχρονισμού - -Local Filter -Τοπικό Φίλτρο - -&New -&Δημιουργία - -&Save -&Αποθήκευση - -Save as &batch job... -Αποθήκευση ως δέσ&μη ενεργειών... - -1. &Compare -1. &Σύγκριση - -2. &Synchronize -2. Συ&γχρονισμός - -&Global settings -Γενικές &ρυθμίσεις - -&Language -&Γλώσσα - -&Find... -&Εύρεση - -&Export file list... -Ε&ξαγωγή καταλόγου αρχείων... - -&Tools -&Εργαλεία - -&Check now -Έλεγχος &τώρα - -Check &automatically once a week -&Aυτόματος έλεγχος μια φορά την εβδομάδα - -&Check for new version -Έλεγχος για &νέα έκδοση - -Compare -Σύγκριση - -Cancel -Άκυρο - -Synchronize -Συγχρονισμός - -Add folder pair -Προσθήκη ζεύγους υποκαταλόγων - -Remove folder pair -Διαγραφή του ζεύγους υποκαταλόγων - -Swap sides -Ανταλλαγή πλευρών - -Close search bar -Κλείσιμο γραμμής εύρεσης - -Find: -Εύρεση: - -Match case -Αντιστοίχιση πεζών-κεφαλαίων - -Save as batch job -Αποθήκευση ως δέσμη ενεργειών - -Hide excluded items -Απόκρυψη στοιχείων που εξαιρέθηκαν - -Show filtered or temporarily excluded files -Εμφάνιση φιλτραρισμένων ή προσωρινά εξαιρεθέντων αρχείων - -Number of files and folders that will be created -Αριθμός αρχείων και υποκαταλόγων που θα δημιουργηθούν - -Number of files that will be overwritten -Αριθμός αρχείων και υποκαταλόγων που θα αντικατασταθούν - -Number of files and folders that will be deleted -Αριθμός αρχείων και υποκαταλόγων που θα διαγραφούν - -Total bytes to copy -Συνολικός αριθμός bytes προς αντιγραφή - -Select a variant: -Επιλογή μεθόδου: - -Identify equal files by comparing modification time and size. -Αναγνώριση των ταυτόσημων αρχείων με σύγκριση του χρόνου τροποποίησης και του μεγέθους. - -Identify equal files by comparing the file content. -Αναγνώριση των ταυτόσημων αρχείων με σύγκριση του περιεχομένου τους. - -Symbolic links: -Συμβολικοί δεσμοί: - -More information -Περισσότερες πληροφορίες - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Αναγνώριση και εφαρμογή αλλαγών και στις δυο πλευρές. Οι διαγραφές, οι μεταφορές και οι διενέξεις αναγνωρίζονται αυτόματα με τη βοήθεια μιας βάσης δεδομένων. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Δημιουργία ενός κατοπτρικού αντιγράφου του υποκαταλόγου αριστερά, ώστε μετά το συγχρονισμό ο υποκατάλογος δεξιά να είναι ένα ακριβές αντίγραφό του. - -Copy new and updated files to the right folder. -Αντιγραφή των νέων και των τροποποιημένων αρχείων στον υποκατάλογο δεξιά. - -Configure your own synchronization rules. -Ορίστε τους δικούς σας κανόνες συγχρονισμού. - -Detect moved files -Ανίχνευση των αρχείων που μεταφέρθηκαν - -Requires database files. Not supported by all file systems. -Απαιτεί αρχεία βάσης δεδομένων. Δεν υποστηρίζεται από όλα τα συστήματα αρχείων. - -Delete files: -Διαγραφή αρχείων: - -Permanent -Μόνιμα - -Delete or overwrite files permanently -Μόνιμη διαγραφή ή αντικατάσταση των αρχείων - -Recycle bin -Κάδος ανακύκλωσης - -Back up deleted and overwritten files in the recycle bin -Μεταφορά όλων των διαγραμμένων και αντικατεστημένων αρχείων στον κάδο ανακύκλωσης. - -Versioning -Διατήρηση παλιών εκδόσεων - -Move files to a user-defined folder -Μεταφορά αρχείων σε έναν υποκατάλογο που ορίζεται από το χρήστη - -Naming convention: -Τρόπος ονομασίας: - -Show examples -Εμφάνιση παραδειγμάτων - -Handle errors: -Χειρισμός σφαλμάτων: - -Ignore -Παράβλεψη - -Hide all error and warning messages -Απόκρυψη όλων των σφαλμάτων και προειδοποιήσεων - -Pop-up -Αναδυόμενο μήνυμα - -Show pop-up on errors or warnings -Εμφάνιση αναδυόμενου παράθυρου σε σφάλματα ή προειδοποιήσεις - -On completion: -Μετά την ολοκλήρωση: - -Start synchronization now? -Να ξεκινήσει ο συγχρονισμός τώρα; - -Variant: -Μέθοδος: - -Statistics -Στατιστικά - -&Don't show this dialog again -&Να μην εμφανιστεί ξανά αυτό το μήνυμα - -Items found: -Βρέθηκαν στοιχεία: - -Speed: -Ταχύτητα: - -Time remaining: -Απομένει χρόνος: - -Time elapsed: -Πέρασε χρόνος: - -Synchronizing... -Γίνεται συγχρονισμός... - -Minimize to notification area -Ελαχιστοποίηση στην περιοχή ειδοποιήσεων - -Close -Κλείσιμο - -&Pause -&Παύση - -Stop -Διακοπή - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Δημιουργία ενός αρχείου δέσμης για αυτόματο συγχρονισμό. Για να ξεκινήσετε, κάντε διπλό κλικ σε αυτό το αρχείο ή ενσωματώστε το σε ένα χρονοδιάγραμμα εργασιών: %x - -Stop synchronization at first error -Διακοπή του συγχρονισμού με το πρώτο σφάλμα - -Show progress dialog -Εμφάνιση της αναφοράς προόδου - -Save log: -Αποθήκευση αρχείου καταγραφής: - -Limit: -Όριο: - -Limit maximum number of log files -Μέγιστος αριθμός αρχείων καταγραφής - -How can I schedule a batch job? -Πώς μπορώ να προγραμματίσω μια εργασία με δέσμη ενεργειών; - -&Recycle bin -&Κάδος ανακύκλωσης - -Delete on both sides -Διαγραφή και στις δυο πλευρές - -Delete on both sides even if the file is selected on one side only -Διαγραφή και στις δυο πλευρές, ακόμα κι αν το αρχείο έχει επιλεχθεί μόνο στη μια πλευρά - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Επιλογή κανόνων φιλτραρίσματος, ώστε να αποκλειστούν ορισμένα αρχεία από το συγχρονισμό. Εισάγετε τις διαδρομές των αρχείων σχετικά με το αντίστοιχο ζεύγος υποκαταλόγων. - -Include: -Συμπερίληψη: - -Exclude: -Αποκλεισμός: - -Time span: -Χρονικό εύρος: - -File size: -Μέγεθος αρχείου: - -Minimum: -Ελάχιστο: - -Maximum: -Μέγιστο - -&Clear -&Καθαρισμός - -The following settings are used for all synchronization jobs. -Οι ακόλουθες ρυθμίσεις χρησιμοποιούνται για όλες τις εργασίες συγχρονισμού. - -Fail-safe file copy -Ασφαλής αντιγραφή αρχείων - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -Αντιγραφή σε ένα προσωρινό αρχείο (*.ffs_tmp) πριν την αντικατάσταση του προορισμού. -Αυτό εγγυάται μια συνεπή κατάσταση ακόμα και στην περίπτωση σοβαρού σφάλματος. - - -(recommended) -(προτεινόμενο) - -Copy locked files -Αντιγραφή κλειδωμένων αρχείων - -Copy shared or locked files using the Volume Shadow Copy Service. -Αντιγραφή των κοινών ή κλειδωμένων αρχείων με τη χρήση της Υπηρεσίας Σκιωδών Αντιγράφων Τόμου. - -(requires administrator rights) -(απαιτεί δικαιώματα διαχειριστή) - -Copy file access permissions -Αντιγραφή των αδειών προσπέλασης των αρχείων - -Transfer file and folder permissions. -Μεταφορά των αδειών προσπέλασης αρχείων και υποκαταλόγων. - -Automatic retry on error: -Αυτόματη επανάληψη σε περίπτωση σφάλματος: - -Retry count: -Αριθμός προσπαθειών: - -Delay (in seconds): -Καθυστέρηση (σε δευτερόλεπτα): - -Customize context menu: -Προσαρμογή μενού επιλογών: - -Description -Περιγραφή - -Restore hidden windows -Επαναφορά των κρυμμένων διαλόγων - -&Default -&Προεπιλογή - -Source code written in C++ using: -Ο πηγαίος κώδικας γράφτηκε σε C++ χρησιμοποιώντας τα: - -If you like FreeFileSync -Αν σας αρέσει το FreeFileSync - -Donate with PayPal -Κάντε μια δωρεά μέσω PayPal - -Feedback and suggestions are welcome -Τα σχόλια και οι προτάσεις σας είναι ευπρόσδεκτα - -Homepage -Ιστοσελίδα - -Email -Email - -Published under the GNU General Public License -Διανέμεται υπό την Γενική Άδεια Δημόσιας Χρήσης GNU - -Many thanks for localization: -Πολλές ευχαριστίες για τις μεταφράσεις: - -Save as Batch Job -Αποθήκευση ως Δέσμη Εργασιών - -Delete Items -Διαγραφή Στοιχείων - -Global Settings -Γενικές Ρυθμίσεις - -Select Time Span -Επιλογή Χρονικού Εύρους - -Folder Pairs -Ζεύγη Υποκαταλόγων - -Find -Αναζήτηση - -Overview -Σύνοψη - -Configuration -Διάταξη - -Main Bar -Κύρια Γραμμή - -Filter Files -Φιλτράρισμα Αρχείων - -Select View -Επιλογή Εμφάνισης - -Open... -Άνοιγμα... - -Save -Αποθήκευση - -Compare both sides -Σύγκριση των δύο πλευρών - -Comparison settings -Ρυθμίσεις σύγκρισης - -Synchronization settings -Ρυθμίσεις συγχρονισμού - -Start synchronization -Έναρξη του συγχρονισμού - -Confirm -Επιβεβαίωση - -&Execute -&Εκτέλεση - - -1 directory -%x directories - - -1 υποκατάλογος -%x υποκατάλογοι - - - -1 file -%x files - - -1 αρχείο -%x αρχεία - - - -%y of 1 row in view -%y of %x rows in view - - -%y από τη 1 φανερή γραμμή -%y από τις %x φανερές γραμμές - - -Set direction: -Επιλογή κατεύθυνσης: - -multiple selection -πολλαπλή επιλογή - -Include via filter: -Συμπερίληψη μέσω του φίλτρου: - -Exclude via filter: -Εξαίρεση με βάση το φίλτρο: - -Exclude temporarily -Προσωρινή εξαίρεση - -Include temporarily -Προσωρινή συμπερίληψη - -Delete -Διαγραφή - -Include all -Συμπερίληψη όλων - -Exclude all -Εξαίρεση όλων - -Show icons: -Εμφάνιση εικονιδίων: - -Small -Μικρό - -Medium -Μεσαίο - -Large -Μεγάλο - -Select time span... -Επιλέξτε το χρονικό εύρος... - -Default view -Προεπιλεγμένη εμφάνιση - -Show "%x" -Εμφάνιση της γραμμής "%x" - -Last session -Τελευταία χρήση - -Folder Comparison and Synchronization -Σύγκριση υποκαταλόγων και Συγχρονισμός - -Configuration saved -Η διάταξη αποθηκεύτηκε - -FreeFileSync batch -Δέσμη ενεργειών του FreeFileSync - -Do you want to save changes to %x? -Θέλετε να αποθηκεύσετε τις αλλαγές στο %x; - -Never save &changes -Να &μην αποθηκεύονται οι αλλαγές - -Do&n't save -Να &μην αποθηκευθούν - -Filter -Φίλτρο - -Show files that exist on left side only -Εμφάνιση των αρχείων που υπάρχουν μόνο στα αριστερά - -Show files that exist on right side only -Εμφάνιση των αρχείων που υπάρχουν μόνο στα δεξιά - -Show files that are newer on left -Εμφάνιση των αρχείων που είναι πιο πρόσφατα στα αριστερά - -Show files that are newer on right -Εμφάνιση των αρχείων που είναι πιο πρόσφατα στα δεξιά - -Show files that are equal -Εμφάνιση των αρχείων που είναι ίδια - -Show files that are different -Εμφάνιση των αρχείων που είναι διαφορετικά - -Show conflicts -Εμφάνιση διενέξεων - -Show files that will be created on the left side -Εμφάνιση των αρχείων που θα δημιουργηθούν στα αριστερά - -Show files that will be created on the right side -Εμφάνιση των αρχείων που θα δημιουργηθούν στα δεξιά - -Show files that will be deleted on the left side -Εμφάνιση των αρχείων που θα διαγραφούν στα αριστερά - -Show files that will be deleted on the right side -Εμφάνιση των αρχείων που θα διαγραφούν στα δεξιά - -Show files that will be overwritten on left side -Εμφάνιση των αρχείων που θα αντικατασταθούν στα αριστερά - -Show files that will be overwritten on right side -Εμφάνιση των αρχείων που θα αντικατασταθούν στα αριστερά - -Show files that won't be copied -Εμφάνιση των αρχείων που δε θα αντιγραφούν - -Set as default -Ορισμός ως προεπιλογής - -All folders are in sync -Όλοι οι υποκατάλογοι είναι συγχρονισμένοι - -Synchronization Settings -Ρυθμίσεις συγχρονισμού - -Comparison Settings -Ρυθμίσεις Σύγκρισης - -Cannot find %x -Δεν μπορεί να βρεθεί το %x - -Comma-separated values -Αρχείο χωρισμένο με κόμματα - -File list exported -Ο κατάλογος των αρχείων έχει εξαχθεί - -Searching for program updates... -Αναζήτηση καινούριας έκδοσης... - -Scanning... -Ανίχνευση... - -Comparing content... -Σύγκριση του περιεχομένου... - -Info -Πληροφορίες - -Warning -Προειδοποίηση - -Paused -Σε παύση - -Initializing... -Αρχικοποίηση... - -Stopped -Διεκόπη - -Completed -Ολοκληρώθηκε - -&Continue -&Συνέχεια - -Log -Καταγραφή - -Today -Σήμερα - -This week -Αυτήν την εβδομάδα - -This month -Αυτόν το μήνα - -This year -Αυτό το έτος - -Last x days -Τελευταίες x ημέρες - -Byte -Byte - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Είστε σίγουροι ότι θέλετε να μετακινήσετε το ακόλουθο αντικείμενο στον κάδο ανακύκλωσης; -Είστε σίγουροι ότι θέλετε να μετακινήσετε τα ακόλουθα %x αντικείμενα στον κάδο ανακύκλωσης; - - -Move -Μεταφορά - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Είσαι σίγουρος ότι θέλεις να διαγραφεί το ακόλουθο αντικείμενο; -Είσαι σίγουρος ότι θέλεις να διαγραφούν τα ακόλουθα %x αντικείμενα; - - -Exclude -Εξαίρεση - -Direct -Ως δεσμοί - -Follow -Ως περιεχόμενο - -Copy NTFS permissions -Αντιγραφή αδειών προσπέλασης NTFS - -Integrate external applications into context menu. The following macros are available: -Ένταξη εξωτερικών εφαρμογών στο μενού περιβάλλοντος. Οι ακόλουθες μακροεντολές είναι διαθέσιμες: - -- full file or folder name -- πλήρες όνομα αρχείου ή υποκαταλόγου - -- folder part only -- μέρος μόνο του υποκαταλόγου - -- Other side's counterpart to %item_path% -- Το αντίστοιχο του %item_path% της άλλης πλευράς - -- Other side's counterpart to %item_folder% -- Το αντίστοιχο του %item_folder% της άλλης πλευράς - -Restore all hidden windows and warnings? -Να γίνει επαναφορά όλων των κρυμμένων μηνυμάτων και προειδοποιήσεων; - -Leave as unresolved conflict -Παράβλεψη ως ανεπίλυτη διένεξη - -Replace -Αντικατάσταση προηγούμενης - -Move files and replace if existing -Μετακίνηση αρχείων και αντικατάσταση - -Time stamp -Χρονική σήμανση - -Append a timestamp to each file name -Προσάρτηση χρονικής σήμανσης σε κάθε όνομα αρχείου - -File -Αρχείο - -YYYY-MM-DD hhmmss -ΕΕΕΕ-ΜΜ-ΗΗ ωωλλδδ - -Files -Αρχεία - -Items -Στοιχεία - -Percentage -Ποσοστό - -Cannot monitor directory %x. -Δεν είναι δυνατή η παρακολύθηση του υποκαταλόγου %x. - -Conversion error: -Σφάλμα μετατροπής: - -Cannot delete file %x. -Δεν μπορεί να διαγραφεί το αρχείο %x. - -The file is locked by another process: -Το αρχείο είναι κλειδωμένο από μια άλλη διαδικασία: - -Cannot move file %x to %y. -Δεν μπορεί το αρχείο %x να μεταφερθεί στο %y. - -Cannot delete directory %x. -Δεν μπορεί να διαγραφεί ο υποκατάλογος %x. - -Cannot write file attributes of %x. -Δεν μπορεί να γίνει εγγραφή των χαρακτηριστικών αρχείου του %x. - -Cannot write modification time of %x. -Δεν μπορεί να γίνει εγγραφή της ώρας τροποποίησης του %x. - -Cannot read security context of %x. -Δεν μπορεί να αναγνωσθεί το περιβάλλον ασφαλείας του %x. - -Cannot write security context of %x. -Δεν μπορεί να γίνει εγγραφή του περιβάλλοντος ασφαλείας του %x. - -Cannot read permissions of %x. -Δεν μπορούν να αναγνωσθούν οι άδειες προσπέλασης του %x. - -Cannot write permissions of %x. -Δεν μπορεί να γίνει εγγραφή των αδειών προσπέλασης του %x. - -Cannot create directory %x. -Δεν μπορεί να δημιουργηθεί ο υποκατάλογος %x. - -Cannot create symbolic link %x. -Ο συμβολικός δεσμός %x δεν μπορεί να δημιουργηθεί. - -Cannot find system function %x. -Δεν ανευρίσκεται η λειτουργία συστήματος %x. - -Cannot copy file %x to %y. -Δεν μπορεί να αντιγραφεί το αρχείο %x στο %y. - -Type of item %x is not supported: -Ο τύπος του στοιχείου %x δεν υποστηρίζεται: - -Cannot resolve symbolic link %x. -Ο συμβολικός δεσμός %x δεν μπορεί να επιλυθεί - -Cannot open directory %x. -Δεν είναι δυνατό το άνοιγμα του υποκαταλόγου %x. - -Cannot enumerate directory %x. -Δεν είναι δυνατή η καταλογογράφηση του υποκαταλόγου %x. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -1 λεπτό -%x λεπτά - - - -1 hour -%x hours - - -1 ώρα -%x ώρες - - - -1 day -%x days - - -1 μέρα -%x μέρες - - -Unable to register to receive system messages. -Δεν μπορεί να καταχωρηθεί η λήψη μηνυμάτων συστήματος. - -Cannot set privilege %x. -Τα δικαιώματα %x δεν μπορούν να οριστούν. - -Unable to suspend system sleep mode. -Δεν μπορεί να διακοπεί η αναστολή του συστήματος. - -Cannot change process I/O priorities. -Δεν μπορούν να αλλάξουν οι προτεραιότητες I/O της διεργασίας. - -Unable to move %x to the recycle bin. -Δεν ήταν δυνατή η μεταφορά του %x στον κάδο ανακύκλωσης. - -Cannot determine final path for %x. -Δεν μπορεί να προσδιοριστεί η τελική διαδρομή για το %x. - -Error Code %x: -Κωδικός Σφάλματος %x - diff --git a/BUILD/Languages/hebrew.lng b/BUILD/Languages/hebrew.lng deleted file mode 100644 index 210efe49..00000000 --- a/BUILD/Languages/hebrew.lng +++ /dev/null @@ -1,1518 +0,0 @@ -
    - עברית - nitnit - he_IL - flag_israel.png - 2 - n == 1 ? 0 : 1 -
    - -Retrying operation... - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Both sides have changed since last synchronization. -שני הצדדים שונו מאז הסנכרון האחרון. - -Cannot determine sync-direction: -לא מזהה כוון סנכרון: - -No change since last synchronization. -אין שינוי מאז הסנכרון האחרון. - -The database entry is not in sync considering current settings. -בסיס הנתונים אינו מסונכרן בהתאם להגדרות הנוכחיות. - -Setting default synchronization directions: Old files will be overwritten with newer files. -בחר ברירת מחדל של סנכרון: קבצים ישנים ידרסו ע"י קבצים חדשים יותר. - -Checking recycle bin availability for folder %x... -בודק זמינות סל מחזור עבור מחיצה %x... - -Moving file %x to the recycle bin -מעביר קובץ %x לסל המיחזור - -Moving folder %x to the recycle bin -מעביר תיקייה %x לסל המיחזור - -Moving symbolic link %x to the recycle bin -מעביר קישור סימבולי %x לסל המיחזור - -Deleting file %x -מוחק קובץ %x - -Deleting folder %x -מוחק מחיצה %x - -Deleting symbolic link %x -מוחק קישור סימבולי %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -סל המיחזור אינו זמין עבור התיקיות הבאות. הקבצים יימחקו לחלוטין: - -An exception occurred -אירוע חריג - -A directory path is expected after %x. -נתיב מחיצה נדרש אחרי %x. - -Syntax error -שגיאת תחביר - -Cannot open file %x. -לא יכול לפתוח קובץ %x. - -File %x does not contain a valid configuration. -קובץ %x אינו כולל תצורה תקינה. - -Unequal number of left and right directories specified. -מספר בלתי שווה של מחיצות ימין ושמאל צוין. - -The config file must not contain settings at directory pair level when directories are set via command line. -קבצי הקונפיגורציה אינם יכולים לכלול הגדרות של זוגות מחיצות כאשר מחיצות מוגדרות באמצעות שורת הפקודה - -Directories cannot be set for more than one configuration file. -מחיצות אינו יכולות להיות מוגדרות עבור יותר מקובץ קונפיגורציה אחד. - -Syntax: -תחביר: - -config files -קבצי קונפיגורציה - -directory -מחיצה - -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -כל כמות של קבצי FreeFileSync .ffs_gui ו\או קבצי קונפיגורציה .ffs_batch. - -Any number of alternative directories for at most one config file. -כל כמות של מחיצות אלטרנטיביות עבור לפחות קובץ קונפיגורציה אחד. - -Command line -שורת פקודות - -A folder input field is empty. -שדה קלט תיקייה ריק. - -The corresponding folder will be considered as empty. -המחיצה המתאימה תחשב כריקה. - -Cannot find the following folders: -לא יכול למצוא את המחיצות הבאות: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -אפשר להתעלם משגיאה זו ולהחשיב כל תיקיה כריקה. התיקיות יווצרו אוטומטית בזמן הסינכרון. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -מסלול המחיצות הבאות מותנה. שים לב כאשר חוקי הסינכרון מוגדרים: - -File %x has an invalid date. -קובץ %x מכיל תאריך שגוי. - -Date: -תאריך: - -Files %x have the same date but a different size. -קובץ %x בעל תאריך זהה אך גודל שונה. - -Size: -גודל: - -Items differ in attributes only -פריטים שונים בתכונות בלבד - -Resolving symbolic link %x -פותר קישור סימבולי %x - -Comparing content of files %x -השווה תכולה של קבצים %x - -Generating file list... -מייצר רשימת קבצים... - -Starting comparison -מתחיל השוואה - -Calculating sync directions... -מחשב כיווני סנכרון... - -Out of memory. -תם הזכרון. - -Item exists on left side only -הפריט קיים בצד ימין בלבד - -Item exists on right side only -הפריט קיים בצד שמאל בלבד - -Left side is newer -צד ימין חדש יותר - -Right side is newer -צד שמאל חדש יותר - -Items have different content -הפריטים הם בעלי תוכן שונה - -Both sides are equal -שני הצדדים שווים - -Conflict/item cannot be categorized -סתירה/פריט אינו ניתן לסיווג - -Copy new item to left -העתק פריט חדש לצד ימין - -Copy new item to right -העתק פריט חדש לצד שמאל - -Delete left item -מחק פריט בצד ימין - -Delete right item -מחק פריט בצד שמאל - -Move file on left -העבר קובץ בצד ימין - -Move file on right -העבר קובץ בצד שמאל - -Overwrite left item -רשום על גבי פריט בצד ימין - -Overwrite right item -רשום על גבי פריט בצד שמאל - -Do nothing -אל תעשה כלום - -Update attributes on left -עדכן תכונות בצד ימין - -Update attributes on right -עדכן תכונות בצד שמאל - -Database file %x is incompatible. -קובץ מסד נתונים %x אינו במבנה מתאים. - -Initial synchronization: -סנכרון ראשוני: - -Database file %x does not yet exist. -קובץ מסד נתונים %x אינו קיים עדיין. - -Database file is corrupt: -קובץ מסד נתונים משובש: - -Cannot write file %x. -לא יכול לכתוב קובץ %x. - -Cannot read file %x. -לא יכול לקרוא קובץ %x. - -Database files do not share a common session. -קבצי הנתונים אינם כוללים מופע פעילות משותף. - -Searching for folder %x... -מחפש את תיקייה %x... - -Cannot read file attributes of %x. -לא יכול לקרוא תכונות של קובץ %x. - -Cannot get process information. -לא יכול לקבל את נתוני התהליך. - -Waiting while directory is locked (%x)... -ממתין כאשר מחיצה נעולה (%x)... - - -1 sec -%x sec - - -1 שנייה -%x שניות - - -Creating file %x -יוצר קובץ %x - -Items processed: -אלמנטים עובדו: - -Items remaining: -אלמנתים נותרו: - -Total time: -זמן כולל: - - -1 byte -%x bytes - - -בית 1 -%x בתים - - -%x MB -%x מגה בייט - -%x KB -%x קילו בייט - -%x GB -%x גיגה בייט - -Error parsing file %x, row %y, column %z. -שגיאה בפענוח קובץ %x, שורה %y, טור %z. - -Cannot set directory lock for %x. -לא ניתן לנעול מחיצה עבור %x. - -Scanning: -סורק: - - -1 thread -%x threads - - -תהליך 1 -%x תהליכים - - -Encoding extended time information: %x -מקודד אינפורמצית זמן מורחבת: %x - -/sec -/שנ - -%x items/sec -%x פריטים לשניה - -Configuration file %x loaded partially only. -קובץ תצורה %x נטען חלקית בלבד. - -Show in Explorer -הראה בסייר הקבצים - -Open with default application -פתח באמצאות האפליקציה המתאימה - -Browse directory -עיין במחיצה - -Cannot access the Volume Shadow Copy Service. -לא ניתן לגשת אל שרות Volume Shadow Copy - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -אנא השתמש בגירסת 64-bit של FreeFileSync על מנת ליצר shadow copies במערכת הפעלה זו. - -Cannot load file %x. -לא יכול לטעון קובץ %x. - -Cannot determine volume name for %x. -לא יכול לקבוע שם אמצעי אחסון %x. - -Volume name %x is not part of file path %y. -כרך בשם %x אינו חלק של נתיב קובץ %y. - -Stop requested: Waiting for current operation to finish... -עצירה התבקשה: מחכה שפעולה נוכחית תסתיים... - -Unable to create timestamp for versioning: -לא ניתן ליצור תג זמן לגרסאות: - -Cannot read the following XML elements: -לא יכול לקרוא את שמות צמתי XML: - -&Open... -&פתח... - -Save &as... -שמור &בשם... - -&Quit -&יציאה - -&Program -&תוכנה - -&View help -&הראה עזרה - -&About -&אודות - -&Help -&עזרה - -Usage: -שימוש: - -1. Select folders to watch. -1. בחר תיקיות לצפייה. - -2. Enter a command line. -2. הקש שורת פקודות. - -3. Press 'Start'. -3. לחץ 'הפעל'. - -To get started just import a .ffs_batch file. -בכדי להתחיל יבא קובץ .ffs_batch - -Folders to watch: -תיקיות לצפייה: - -Add folder -הוסף מחיצה - -Remove folder -הסר מחיצה - -Browse -עיין - -Select a folder -בחר מחיצה - -Idle time (in seconds): -זמן המתנה (בשניות): - -Idle time between last detected change and execution of command -זמן המתנה בין שינויי מאובחן אחרון לבין ביצוע של פקודה - -Command line: -שורת פקודה: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -הפקודה מופעלת כאשר: -- קבצים או תת-תיקיות משתנים -- תיקיות חדשות מופיעות (לדוגמה התקן USB מוכנס) - - -&Start -&התחל - -&Retry -&נסה שנית - -Cancel -בטל - -Build: %x -מבנה: %x - -About -אודות - -All files -כל הקבצים - -Automated Synchronization -סנכרון אוטומטי - -Directory monitoring active -ניטור מחיצות פעיל - -Waiting until all directories are available... -מחכה עד שכל המחיצות יהיו זמינות... - -Error -שגיאה - -&Restore -&טען מחדש - -&Show error -&הראה שגיאה - -&Exit -&יציאה - -Incorrect command line: -שורת פקודה לא תקינה: - -File content -תכולת הקובץ - -File time and size -זמן וגודל קובץ - -Two way -דו כווני - -Mirror -מראה - -Update -שדרג - -Custom -מותאם - -Multiple... -הכפל... - -Moving file %x to %y -מעביר קובץ %x אל %y - -Moving folder %x to %y -מעביר מחיצה %x אל %y - -Moving symbolic link %x to %y -מעביר קישור סימבולי %x אל %y - -Removing old versions... -מסיר גרסאות ישנות... - -Creating symbolic link %x -יוצר קישור סימבולי %x - -Creating folder %x -יוצר מחיצה %x - -Overwriting file %x -דורס קובץ %x - -Overwriting symbolic link %x -דורס קישור סימבולי %x - -Verifying file %x -מאמת קובץ %x - -Updating attributes of %x -מעדכן תכונות של %x - -Cannot find %x. -לא מוצא %x. - -Target folder %x already existing. -תיקיית מטרה %x כבר קיימת. - -Target folder input field must not be empty. -קלט תיקיית מטרה אינה יכול להיות ריק. - -Please enter a target folder for versioning. -בבקשה הזן תיקיית יעד עבור גרסאות. - -Source folder %x not found. -תיקיית מקור %x לא נמצאת. - -The following items have unresolved conflicts and will not be synchronized: -לפריטים הבאים יש קונפליקטים בלתי פתורים והם לא יסונכרנו: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -התיקיות הבאות שונות באופן מהותי. ודא את התאמת התיקיות לסינכרון. - -Not enough free disk space available in: -אין מספיק מקום דיסק פנוי ב: - -Required: -נדרש: - -Available: -זמין: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -התיקייה שתשתנה היא חלק מריבוי זוגות תיקיות. אנא סקור מחדש הגדרות סינכרון. - -Synchronizing folder pair: -מסנכרן זוג תיקיות: - -Generating database... -מייצר מסד נתונים... - -Creating a Volume Shadow Copy for %x... -מייצר Volume Shadow Copy עבור %x... - -Data verification error: %x and %y have different content. -שגיאת אימות נתונים. ל %x ו %y יש תוכן שונה. - -job name -שם משימה - -Synchronization stopped -סנכרון הופסק - -Synchronization completed with errors -סנכרון הושלם עם שגיאות - -Synchronization completed with warnings -סנכרון הסתיים עם אזהרות - -Nothing to synchronize -אין מה לסנכרן - -Synchronization completed successfully -סנכרון הסתיים בהצלחה - -Saving log file %x... -שומר קובץ יומן %x... - -You can switch to FreeFileSync's main window to resolve this issue. -ניתן לעבור לחלון הראשי של FreeFileSybc כדי לפתור את הסוגיה הזו. - -Switching to FreeFileSync's main window -מעבר אל החלון הראשי של FreeFileSybc - -A new version of FreeFileSync is available: -גירסה חדשה של FreeFileSync זמינה: - -Download now? -הורד עכשיו? - -Check for Program Updates -בדוק קיום עדכוני תוכנה - -&Download -&הורד - -FreeFileSync is up to date. -FreeFileSync מעודכן לגירסה האחרונה. - -Unable to connect to sourceforge.net. -אין תקשורת ל sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -לא מוצא מספר גירסה עדכנית של FreeFileSync באופן מכוון. האם אתה רוצה לבדוק באופן ידני? - -Symlink -קשור סימבולי - -Folder -תיקייה - -Full path -נתיב מלא - -Name -שם - -Relative path -נתיב יחסי - -Base folder -תיקיית בסיס - -Size -גודל - -Date -תארין - -Extension -סיומת - -Category -קטגוריה - -Action -פעולה - -Drag && drop -גרור &והשלך - -Close progress dialog -סגור שיח התקדמות - -Standby -עבור למצב המתנה - -Log off -התנתק כמשתמש - -Shut down -כבה מחשב - -Hibernate -עבור למצב שינה - -Alternate comparison settings -הגדרות השוואה חלופיות - -Alternate synchronization settings -הגדרות סנכרון חלופיות - -Local filter -מסנן מקומי - -Active -פעיל - -None -אין - -Remove alternate settings -הסר הגדרות תצורה חליפיות - -Clear filter settings -נקה בחירת מסנן - -Copy -העתק - -Paste -הדבק - -Alternate Comparison Settings -הגדרות השוואה חלופיות - -Alternate Synchronization Settings -הגדרות סנכרון חלופיות - -Local Filter -מסנן מקומי - -&New -&חדש - -&Save -&שמור - -Save as &batch job... -שמור כעבודת &אצווה - -1. &Compare -1. &השווה - -2. &Synchronize -2. &סנכרן - -&Global settings -&משתנים גלובליים - -&Language -&שפה - -&Find... -&מצא - -&Export file list... -&יצא רשימת קבצים... - -&Tools -&כלים - -&Check now -&בדוק עכשיו - -Check &automatically once a week -בדוק &אוטומטית אחת לשבוע - -&Check for new version -&בדוק קיום גירסה חדשה - -Compare -השוואה - -Synchronize -סנכרן - -Add folder pair -הוסף זוג מחיצות - -Remove folder pair -הסר זוג מחיצות - -Swap sides -החלף צדדים - -Close search bar -סגור סרגל חיפוש - -Find: -מצא: - -Match case -התאם רישיות - -Save as batch job -שמור כעבודת אצווה - -Hide excluded items -הסתר פריטים שלט נכללו - -Show filtered or temporarily excluded files -הראה קבצים שסוננו או לא נכללו זמנית - -Number of files and folders that will be created -מספר הקבצים והתיקיות שייוצרו - -Number of files that will be overwritten -מספר הקבצים העומדים להיכתב מחדש - -Number of files and folders that will be deleted -מספר הקבצים והתיקיות שימחקו - -Total bytes to copy -סה"כ בתים להעתיק - -Select a variant: -בחר גירסה: - -Identify equal files by comparing modification time and size. -זהה קבצים זהים באמצעות השוואת תג הזמן והגודל. - -Identify equal files by comparing the file content. -זהה קבצים זהים באמצעות השוואת תוכן. - -Symbolic links: -קישורים סימבוליים: - -More information -מידע נוסף - -OK -אשר - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -זהה והפץ שינויים בשני הצדדים. מחיקות העברות וסתירות מתגלים באופן אוטומטי באמצעות מסד נתונים. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -צור גבוי מראה של התיקייה השמאלית אשר יהיה זהה לתיקייה הימנית לאחר הסנכרון. - -Copy new and updated files to the right folder. -העתק קבצים חדשים ומעודכנים לתיקייה הימנית. - -Configure your own synchronization rules. -סדר את כללי הסנכרון שלך. - -Detect moved files -גלה קבצים מועברים - -Requires database files. Not supported by all file systems. -דורש קבצי בסיס נתונים. לא נתמך ע"י כל מערכות הקבצים. - -Delete files: -מחק קבצים: - -Permanent -קבוע - -Delete or overwrite files permanently -מחק או דרוס קבצים לצמיתות - -Recycle bin -סל מיחזור - -Back up deleted and overwritten files in the recycle bin -גבה קבצים שנמחקו או שנדרסו בסל המיחזור - -Versioning -עדכון גרסאות - -Move files to a user-defined folder -העבר קבצים לתיקייה המוגדרת ע"י המפעיל - -Naming convention: -מוסכמות לקביעת שמות: - -Show examples -הראה דוגמאות - -Handle errors: -טפל בשגיאות: - -Ignore -התעלם - -Hide all error and warning messages -הסתר את כל הודעות ההזהרה והשגיאה - -Pop-up -מוקפץ - -Show pop-up on errors or warnings -הראה חלונות מוקפצים עבור שגיאות או אזהרות - -On completion: -עם הסיום: - -Start synchronization now? -האם להתחיל סנכרון עכשיו? - -Variant: -גרסה אחרת: - -Statistics -סטטיסטיקה - -&Don't show this dialog again -&אל תראה דושיח זה שנית - -Items found: -אלמנטים נמצאו: - -Speed: -מהירות: - -Time remaining: -זמן שנשאר: - -Time elapsed: -זמן שעבר: - -Synchronizing... -מסנכרן... - -Minimize to notification area -הקטן לאיזור ההתרעות - -Close -סגור - -&Pause -&עצור - -Stop -עצור - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -צור קובץ אצווה לסינכרון ללא התערבות מפעיל. כדי להפעיל, הקלק הקלקה כפולה על הקובץ או תזמן במתכנן המשימות: %x - -Stop synchronization at first error -עצור סנכרון עם הופעת שגיאה ראשונה - -Show progress dialog -הראה שיח התקדמות - -Save log: -שמור יומן: - -Limit: -מגבלה: - -Limit maximum number of log files -הגבל מספר מכסימלי של קבצי יומן - -How can I schedule a batch job? -כיצד לתזמן משימת אצווה? - -&Recycle bin -&סל מחזור - -Delete on both sides -מחק בשני הצדדים - -Delete on both sides even if the file is selected on one side only -מחק בשני הצדדים אף אם הקובץ נבחר בצד אחד בלבד - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -בחר כללי סינון כדי לא לכלול קבצים מסוימים בסנכרון. הזן את נתיבי הקבצים ביחס לזוג התיקיות בהתאמה. - -Include: -כלול: - -Exclude: -לא לכלול: - -Time span: -טווח זמן: - -File size: -גודל קובץ: - -Minimum: -מינימום: - -Maximum: -מקסימום: - -&Clear -&נקה - -The following settings are used for all synchronization jobs. -ההגדרות הבאות משמשות לכל משימות הסנכרון. - -Fail-safe file copy -כשלון באבטחת העתקת קובץ - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -העתק לקובץ זמני (*.ffs_tmp) לפני כתיבה מחדש על היעד. -זה מבטיח מצב עקבי גם במקרה של שגיאה חמורה. - - -(recommended) -(מומלץ) - -Copy locked files -העתק קבצים נעולים - -Copy shared or locked files using the Volume Shadow Copy Service. -העתק קבצים בשיתוף או בנעילה באמצעות שרות Volume Shadow Copy. - -(requires administrator rights) -(נדרשות זכויות מנהל) - -Copy file access permissions -העתק הרשאות גישה של הקובץ - -Transfer file and folder permissions. -הרשאות העברת קובץ ומחיצה. - -Automatic retry on error: -נסיון חוזר אוטומטי במקרה שגיאה: - -Retry count: -מונה נסיונות חוזרים: - -Delay (in seconds): -השהייה (בשניות): - -Customize context menu: -התאמה אישית של תפריט הקשר: - -Description -תאור - -Restore hidden windows -שחזר חלונות ניסתרים - -&Default -&ברירת מחדל - -Source code written in C++ using: -קוד מקור נכתב ב- C++ באמצעות: - -If you like FreeFileSync -במידה ו-FreeFileSync מוצאת חן בעינכם - -Donate with PayPal -תרום עם פייפל - -Feedback and suggestions are welcome -משוב והצעות יתקבלו בברכה - -Homepage -אתר-הבית: - -Email -דוא"ל: - -Published under the GNU General Public License -מפורסם במסגרת GNU General Public License - -Many thanks for localization: -תודות עבור תרגום שפות: - -Save as Batch Job -שמור כמשימת אצווה - -Delete Items -מחק פריטים - -Global Settings -הגדרות גלובליות - -Select Time Span -בחר טווח זמן - -Folder Pairs -זוגות תיקיות - -Find -חפש - -Overview -מבט כללי - -Configuration -תצורה - -Main Bar -סרגל ראשי - -Filter Files -סנן קבצים - -Select View -בחר מבט - -Open... -פתח... - -Save -שמור - -Compare both sides -השווה בין שני הצדדים - -Comparison settings -הגדרות השוואה - -Synchronization settings -הגדרות סנכרון - -Start synchronization -התחל סנכרון - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - -האם ברצונך לבצע את הפקודה %y עבור פריט אחד? -האם ברצונל לבצע את הפקודה %y עבור %x פריטים? - - -Confirm -אשר - -&Execute -&בצע - - -1 directory -%x directories - - -1 מחיצה -%x מחיצות - - - -1 file -%x files - - -1 קובץ -%x קבצים - - - -%y of 1 row in view -%y of %x rows in view - - -%y של שורה 1 במראה -%y של %x שורות במראה - - -Set direction: -בחר כוון: - -multiple selection -בחירה מרובה - -Include via filter: -כלול באמצעות מסנן: - -Exclude via filter: -אל תכלול בעזרת סנן: - -Exclude temporarily -אל תכלול זמנית - -Include temporarily -כלול זמנית - -Delete -מחק - -Include all -הכלל הכל - -Exclude all -הוצא מן הכלל הכל - -Show icons: -הצג סמלים: - -Small -קטן - -Medium -בינוני - -Large -גדול - -Select time span... -בחר תחום זמן... - -Default view -תצוגה בתצורת ברירת מחדל - -Show "%x" -הראה "%x" - -Last session -פעילות אחרונה - -Folder Comparison and Synchronization -סנכרון קבצים ומחיצות - -Configuration saved -תצורה נשמרה - -FreeFileSync batch -אצוות FreeFileSync - -Do you want to save changes to %x? -האם לשמור שינויים אל %x? - -Do&n't save -אל &תשמור - -Never save &changes -לא לשמור שינויים ל&עולם - -Filter -מסנן - -Show files that exist on left side only -הראה קבצים הנמצאים אך ורק בצד ימין - -Show files that exist on right side only -הראה קבצים הנמצאים אך ורק בצד שמאל - -Show files that are newer on left -הראה קבצים חדשים יותר בצד ימין - -Show files that are newer on right -הראה קבצים חדשים יותר בצד שמאל - -Show files that are equal -הראה קבצים שווים - -Show files that are different -הראה קבצים שונים - -Show conflicts -הראה קונפליקטים - -Show files that will be created on the left side -הראה קבצים שיווצרו בצד ימין - -Show files that will be created on the right side -הראה קבצים שיווצרו בצד שמאל - -Show files that will be deleted on the left side -הראה קבצים שימחקו בצד ימין - -Show files that will be deleted on the right side -הראה קבצים שימחקו בצד שמאל - -Show files that will be overwritten on left side -הראה קבצים שידרסו בצד ימין - -Show files that will be overwritten on right side -הראה קבצים שידרסו בצד שמאל - -Show files that won't be copied -הראה קבצים שלא יועתקו - -Set as default -הגדר כברירת מחדל - -All folders are in sync -כל התיקיות מסונכרנות - -Synchronization Settings -הגדרות סנכרון - -Comparison Settings -הגדרות השוואה - -Cannot find %x -לא מוצא %x - -Comma-separated values -ערכים מופרדים באמצעות פסיק - -File list exported -רשימת קבצים יוצאה - -Searching for program updates... -מחפש עידכוני תוכנה... - -Scanning... -סורק... - -Comparing content... -משווה תכולה... - -Info -מידע - -Warning -אזהרה - -Serious Error -שגיאה חמורה - -Paused -עצור - -Initializing... -מאתחל ... - -Stopped -נעצר - -Completed -הושלם - -&Continue -&המשך - -Log -יומן - -Today -היום - -This week -בשבוע הנוכחי - -This month -בחודש הנוכחי - -This year -בשנה הנוכחית - -Last x days -x ימים אחרונים - -Byte -בייט - -KB -קילו-בייט - -MB -מגה-בייט - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -האם באמת ברצונך להעביר את הפריט הבא לסל המחזור? -האם באמת ברצונך להעביר את הפריטים הבאים %x לסל המחזור? - - -Move -העבר - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -האם ברצונך למחוק את הפריט הבא? -האם ברצונך למחוק את הפריטים %x הבאים? - - -Exclude -אל תכלול - -Direct -כוון - -Follow -עקוב - -Copy NTFS permissions -העתק הרשאות NTFS - -Integrate external applications into context menu. The following macros are available: -הטמע תוכנות חיצוניות. תפריטי המקרו הבאים זמינים: - -- full file or folder name -- שם מלא של קובץ או תיקייה - -- folder part only -- תיקייה בלבד - -- Other side's counterpart to %item_path% -- הצד השני מקביל ל %item_path% - -- Other side's counterpart to %item_folder% -הצד השני המקביל ל %item_folder% - -Restore all hidden windows and warnings? -האם לשחזר את כל החלונות וההודעות הנסתרות? - -Leave as unresolved conflict -השאר כקונפליקט לא מטופל - -Replace -החלף - -Move files and replace if existing -העבר קבצים והחלף במדה וקיימים - -Time stamp -תג זמן - -Append a timestamp to each file name -הצמד תג זמן לכל שם קובץ - -File -קובץ - -YYYY-MM-DD hhmmss -YYYY-MM-DD hhmmss - -Files -קבצים - -Items -פריטים - -Percentage -אחוז - -&Ignore subsequent errors -&התעלם מאזהרות נוספות - -&Ignore -&התעלם - -&Don't show this warning again -&לא להראות אזהרה זו שוב - -&Switch -&החלפה - -&Yes -&כן - -&No -&לא - -Cannot monitor directory %x. -לא יכול לנטר מחיצה %x. - -Conversion error: -שגיאה בהסבה: - -Cannot delete file %x. -לא יכול למחוק קובץ %x. - -The file is locked by another process: -הקובץ נעול ע"י תהליך: - -Cannot move file %x to %y. -לא יכול להעביר קובץ %x אל %y. - -Cannot delete directory %x. -לא יכול למחוק מחיצה %x. - -Cannot write file attributes of %x. -לא יכול לכתוב תכונות קובץ של %x. - -Cannot write modification time of %x. -לא יכול לרשום זמן שינוי של %x. - -Cannot read security context of %x. -לא יכול לקרוא הקשר בטיחות של %x. - -Cannot write security context of %x. -לא יכול לכתוב הקשר בטיחות של %x. - -Cannot read permissions of %x. -לא יכול לקרוא הרשאות של %x. - -Cannot write permissions of %x. -לא יכול לכתוב הרשאות של %x. - -Cannot create directory %x. -לא יכול ליצור מחיצה %x. - -Cannot create symbolic link %x. -לא יכול ליצור קישור סימבולי %x. - -Cannot find system function %x. -לא יכול למצוא פונקצית מערכת %x. - -Cannot copy file %x to %y. -לא יכול להעתיק קובץ %x אל %y. - -Type of item %x is not supported: -סוג של פריט %x אינו נתמך: - -Cannot resolve symbolic link %x. -לא יכול לפענח את הקישור הסימבולי %x. - -Cannot open directory %x. -לא יכול לפתוח מחיצה %x. - -Cannot enumerate directory %x. -לא יכול למספר מחיצה %x. - -%x TB -%x טרה בייט - -%x PB -%x פטה בייט - - -1 min -%x min - - -1 דקה -%x דקות - - - -1 hour -%x hours - - -1 שעה -%x שעות - - - -1 day -%x days - - -1 יום -%x ימים - - -Unable to register to receive system messages. -לא ניתן להרשם לקבלת הודעות מערכת. - -Cannot set privilege %x. -לא יכול להגדיר זבות %x. - -Unable to suspend system sleep mode. -לא ניתן להשעות מצב שינה של המערכת. - -Cannot change process I/O priorities. -לא יכול לשנות קדימויות של תהליך קלט פלט. - -Unable to move %x to the recycle bin. -לא יכול להעביר את %x לסל המחזור - -Cannot determine final path for %x. -לא יכול לקבוע את המסלול הסופי ל %x. - -Error Code %x: -קוד שגיאה %x: - diff --git a/BUILD/Languages/hungarian.lng b/BUILD/Languages/hungarian.lng deleted file mode 100644 index f7057811..00000000 --- a/BUILD/Languages/hungarian.lng +++ /dev/null @@ -1,1521 +0,0 @@ -
    - Magyar - szittner - hu_HU - flag_hungary.png - 2 - n == 1 ? 0 : 1 -
    - -&Check - - -Retrying operation... - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Both sides have changed since last synchronization. -Mindkét oldal megváltozott az utolsó szinkronizálás óta. - -Cannot determine sync-direction: -Nem meghatározható a szinkronizálás iránya: - -No change since last synchronization. -Az utolsó szinkronizálás óta nem történt változás. - -The database entry is not in sync considering current settings. -Az adatbázis-bejegyzés nincs összhangban figyelembe véve a jelenlegi beállításokat. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Alapértelmezett szinkronizálási irányok beállítása: a régebbi fájlokat írja felül az újabbakkal. - -Checking recycle bin availability for folder %x... -A Lomtár elérhetőségének ellenőrzése %x könyvtár vonatkozásában... - -Moving file %x to the recycle bin -%x fájlt a Lomtárba mozgatja - -Moving folder %x to the recycle bin -%x könyvtárat a Lomtárba mozgatja - -Moving symbolic link %x to the recycle bin -%x szimbolikus linket a Lomtárba mozgatja - -Deleting file %x -%x fájl törlése - -Deleting folder %x -%x könyvtár törlése - -Deleting symbolic link %x -%x szimbolikus hivatkozás törlése - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -A Lomtár nem elérhető a következő könyvtárak esetében. A fájlok ezért véglegesen törlődnek: - -An exception occurred -Kivétel lépett fel - -A directory path is expected after %x. -%x után egy könyvtár elérési útvonalának kell következnie. - -Syntax error -Szintaktikai hiba - -Cannot open file %x. -%x fájl nem nyitható. - -File %x does not contain a valid configuration. -%x fájl nem tartalmaz érvényes beállításokat. - -Unequal number of left and right directories specified. -Eltérő számú bal- és jobboldali könyvtárat határozott meg. - -The config file must not contain settings at directory pair level when directories are set via command line. -A konfigurációs fájl nem tartalmazhat beállításokat könyvár-pár szinten, ha a könyvtárakat parancssorban állítjuk be. - -Directories cannot be set for more than one configuration file. -A könyvtárakat nem lehet egynél több konfigurációs fájlra beállítani - -Command line -Parancssor - -Syntax: -Szintaxis: - -config files -konfigurációs fájlok - -directory -könyvtár - -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 fájl készíthető a FreeFileSync-hez - -Any number of alternative directories for at most one config file. -Tetszőleges számú alternatív könyvtár legfeljebb egy konfigurációs fájlhoz - -A folder input field is empty. -A könyvtár beviteli mező üres. - -The corresponding folder will be considered as empty. -A kapcsolódó könyvtárat üresként kezeli. - -Cannot find the following folders: -A következő könyvtárak nem találhatóak: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -Figyelmen kívül hagyhatod ezt a hibát, ezzel üresnek tekintve minden egyes könyvtárat. A könytárak ekkor automatikusan létrejönnek a szinkronizálás folyamán. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -A következő könyvtárak egymástól függő útvonalat tartalmaznak. Légy óvatos a szinkronizálás szabályainak meghatározásánál! - -File %x has an invalid date. -%x fájl dátuma érvénytelen. - -Date: -Dátum: - -Files %x have the same date but a different size. -A(z) %x fájlok dátuma megegyezik, de a méretük nem. - -Size: -Méret: - -Items differ in attributes only -Az elemek csak attribútumaikban különböznek - -Resolving symbolic link %x -A %x szimbolikus hivatkozás feloldása - -Comparing content of files %x -%x fájlok tartalmának összehasonlítása - -Generating file list... -Fájllista generálása... - -Starting comparison -Kezdi az összehasonlítást - -Calculating sync directions... -Szinkronizálási irányok számítása... - -Out of memory. -Memória túlcsordulás. - -Item exists on left side only -Az elem csak a bal oldalon létezik - -Item exists on right side only -Az elem csak a jobb oldalon létezik - -Left side is newer -A bal oldal újabb - -Right side is newer -A jobb oldal újabb - -Items have different content -Az elemek tartalma különböző - -Both sides are equal -Mindkét oldal azonos - -Conflict/item cannot be categorized -Az ütközés vagy elem nem kategorizálható - -Copy new item to left -Új elem másolása a bal oldalra - -Copy new item to right -Új elem másolása a jobb oldalra - -Delete left item -Bal oldali elem törlése - -Delete right item -Jobb oldali elem törlése - -Move file on left -Bal oldali fájl mozgatása - -Move file on right -Jobb oldali fájl mozgatása - -Overwrite left item -Bal oldali elem fölülírása - -Overwrite right item -Jobb oldali elem fölülírása - -Do nothing -Nincs mit csinálni - -Update attributes on left -Attribútumok frissítése a bal oldalon - -Update attributes on right -Attribútumok frissítése a jobb oldalon - -Database file %x is incompatible. -%x adatbázisfájl inkompatibilis: . - -Initial synchronization: -Kezdeti szinkronizálás: - -Database file %x does not yet exist. -%x adatbázisfájl még nem létezik. - -Database file is corrupt: -Sérült adatbázisfájl: - -Cannot write file %x. -A következő fájl írása nem sikerült: %x. - -Cannot read file %x. -A következő fájl olvasása nem sikerült: %x. - -Database files do not share a common session. -Az adatbázisfájloknak nincs közös munkafolyamatuk. - -Searching for folder %x... -%x könyvtár keresése... - -Cannot read file attributes of %x. -%x fájl attribútumainak olvasása nem sikerült. - -Cannot get process information. -Nem sikerült lekérdezni processz-információkat. - -Waiting while directory is locked (%x)... -Várakozás a könyvtár zárolásának a feloldására (%x)... - - -1 sec -%x sec - - -1 másodperc -%x másodperc - - -Creating file %x -%x fájl létrehozása - -Items processed: -Feldolgozott elemek száma: - -Items remaining: -Hátralévő elemek száma: - -Total time: -Összes időszükségelet: - - -1 byte -%x bytes - - -1 bájt -%x bájt - - -%x MB -%x MB - -%x KB -%x kB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Hiba történt a feldolgozás közben: %x fájl, %y sor, %z oszlop. - -Cannot set directory lock for %x. -Nem sikerült zárolni a(z) %x könyvtárat. - -Scanning: -Vizsgálat: - - -1 thread -%x threads - - -1 szál -%x szál - - -Encoding extended time information: %x -Kibővített időinformációk kódolása: %x - -/sec -/másodperc - -%x items/sec -%x elem/másodperc - -Configuration file %x loaded partially only. -%x konfigurációs fájl csak részlegesen töltődött be: . - -Show in Explorer -Mutassa az Intézőben - -Open with default application -Nyissa meg az alapértelmezett alkalmazással - -Browse directory -Tallózza a könyvtárat - -Cannot access the Volume Shadow Copy Service. -Nem elérhető a Kötet Árnyék-másolat szolgáltatás. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Kérjük, használd a FreeFileSync 64 bites verzióját az árnyékmásolatok készítéséhez ezen a rendszeren. - -Cannot load file %x. -%x fájl betöltése nem sikerült. - -Cannot determine volume name for %x. -Nem lehet meghatározni a kötet-nevet a(z) %x számára - -Volume name %x is not part of file path %y. -%x kötet név nem része a(z) %y fájl elérési útvonalnak - -Stop requested: Waiting for current operation to finish... -Megállás szükséges: várakozás a jelenlegi művelet befejezésére... - -Unable to create timestamp for versioning: -Nem sikerült időbélyeget generálni a verziókövetéshez - -Cannot read the following XML elements: -Nem sikerült a következő XML elemek olvasása: - -&Open... -&Megnyitás... - -Save &as... -Mentés m&ásként... - -&Quit -&Kilépés - -&Program -&Program - -&View help -&Nézd meg a súgót - -&About -&A programról - -&Help -&Súgó - -Usage: -Használat: - -1. Select folders to watch. -1. Válaszd ki a figyelendő könyvtárat. - -2. Enter a command line. -2. Add meg a parancssort. - -3. Press 'Start'. -3. Nyomd meg a Start gombot. - -To get started just import a .ffs_batch file. -Az induláshoz importálj egy .ffs_batch fájlt. - -Folders to watch: -Figyelendő könyvtárak: - -Add folder -Könyvtárat hozzáad - -Remove folder -Könyvtárat eltávolít - -Browse -Tallóz - -Select a folder -Könyvtárat kiválaszt - -Idle time (in seconds): -Üresjárat időtartama (másodperc): - -Idle time between last detected change and execution of command -Üresjárat időtartama az utolsó változás észlelése és a parancs végrehajtása között - -Command line: -Parancssor: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -A parancs végrehajtódik, ha: -- fájlok vagy alkönyvtárak megváltoznak -- új könyvtárak jelennek meg (pl. USB-kulcs csatlakoztatása) - - -&Start -&Start - -&Retry -&Ismét - -Cancel -Mégsem - -About -A programról - -Build: %x -Build: %x - -All files -Összes fájl - -Automated Synchronization -Automatizált szinkronizálás - -Directory monitoring active -A könyvtár figyelés aktív - -Waiting until all directories are available... -Várakozik, amíg az összes könyvtár elérhetővé válik... - -Error -Hiba - -&Restore -&Visszaállít - -&Show error -&Mutassa a hibákat - -&Exit -&Kilép - -Incorrect command line: -Hibás parancssor: - -File content -Fájl tartalma - -File time and size -Fájl dátuma és mérete - -Two way -Kétirányú - -Mirror -Tükröz - -Update -Frissít - -Custom -Egyedi - -Multiple... -Sokszoroz - -Moving file %x to %y -%x fájl mozgatása ide: %y - -Moving folder %x to %y -%x könyvtár mozgatása ide: %y - -Moving symbolic link %x to %y -%x szimbolikus hivatkozás mozgatása ide: %y - -Removing old versions... -Régi verziók eltávolítása... - -Creating symbolic link %x -%x szimbolikus hivatkozás létrehozása - -Creating folder %x -%x könyvtár létrehozása - -Overwriting file %x -%x fájl felülírása - -Overwriting symbolic link %x -%x szimbolikus hivatkozás felülírása - -Verifying file %x -%x fájl ellenőrzése - -Updating attributes of %x -%x attribútumainak frissítése - -Cannot find %x. -%x nem található. - -Target folder %x already existing. -%x célkönyvtár már létezik. - -Target folder input field must not be empty. -A célkönyvtárat meghatározó beviteli mező nem lehet üres. - -Please enter a target folder for versioning. -Adja meg a célkönyvtárat a verziókövetéshez. - -Source folder %x not found. -%x forráskönyvtár nem található . - -The following items have unresolved conflicts and will not be synchronized: -A következő elemek feloldatlan ütközést tartalmaznak, így nem lesznek szinkronizálva: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -A következő könyvtárak lényegileg különböznek. Győződjön meg róla, hogy a megfelelő könyvtárakat illesztette-e össze szinkronizáláshoz. - -Not enough free disk space available in: -Nincs elég szabad lemezterület: - -Required: -Szükséges: - -Available: -Szabad: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Egy olyan könyvtár kerül módosításra, amely több könyvtár-pár része. Kérjük, ellenőrizze a szinkronizálási beállításokat. - -Synchronizing folder pair: -A szinkronizált könyvtár-pár: - -Generating database... -Adatbázis generálása... - -Creating a Volume Shadow Copy for %x... -Készít egy árnyékmásolat-kötetet %x számára - -Data verification error: %x and %y have different content. -Adat-ellenőrzési hiba: %x és %y tartalma különböző. - -job name -feladat neve - -Synchronization stopped -Szinkronizálás leállítva - -Synchronization completed with errors -A szinkronizálás befejeződött, de történtek hibák - -Synchronization completed with warnings -A szinkronizálás befejeződött, de figyelmeztetésekkel - -Nothing to synchronize -Nincs mit szinkronizálni - -Synchronization completed successfully -A szinkronizálás sikeresen befejeződött - -Saving log file %x... -Naplófájl mentése a következő fájlba: %x. - -You can switch to FreeFileSync's main window to resolve this issue. -E jelenség megoldásához kapcsoljon át a FreeFileSync fő ablakába. - -Switching to FreeFileSync's main window -Átkapcsolás a FreeFileSync fő ablakába - -Check for Program Updates -Program-frissítések ellenőrzése - -A new version of FreeFileSync is available: -Elérhető a FreeFileSync új verziója: - -Download now? -Letöltsem most? - -&Download -&Letölt - -FreeFileSync is up to date. -A FreeFileSync naprakész. - -Unable to connect to sourceforge.net. -Nem sikerült a csatlakozás a sourceforge.net-hez. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Nem lehet online megtalálni a jelenlegi FreeFileSync verziószámot. Meg akarod keresni manuálisan? - -Symlink -Symlink - -Folder -Könyvtár - -Full path -Teljes elérési útvonal - -Name -Név - -Relative path -Relatív útvonal - -Base folder -Alapkönyvtár - -Size -Méret - -Date -Dátum - -Extension -Kiterjesztés - -Category -Kategória - -Action -Művelet - -Drag && drop -Húzd && Ejtsd - -Close progress dialog -Zárja be a folyamatjelző párbeszédablakot - -Standby -Készenléti állapot - -Log off -Kijelentkezik - -Shut down -Leállítja a gépet - -Hibernate -Hibernál - -Alternate comparison settings -Megváltoztatja az összehasonlítási beállításokat - -Alternate synchronization settings -Megváltoztatja a szinkronizálási beállításokat - -Local filter -Helyi szűrő - -Active -Aktív - -None -Egyik sem - -Remove alternate settings -Törölje a módosított beállításokat - -Clear filter settings -Törölje a szűrőbeállításokat - -Copy -Másol - -Paste -Beilleszt - -Alternate Comparison Settings -Megváltoztatja az összehasonlítási beállításokat - -Alternate Synchronization Settings -Megváltoztatja a szinkronizálási beállításokat - -Local Filter -Helyi szűrő - -&New -&Új - -&Save -&Ment - -Save as &batch job... -Mentse &kötegelt feladatként... - -1. &Compare -1. &Összehasonlít - -2. &Synchronize -2. &Szinkronizál - -&Global settings -&Globális beállítások - -&Language -&Nyelv - -&Find... -Ke&res... - -&Export file list... -&Exportálja a fájllistát... - -&Tools -Es&zközök - -&Check now -&Ellenőrizze most - -Check &automatically once a week -&Automatikus ellenőrzés hetente - -&Check for new version -&Új verzió keresése - -Compare -Összehasonlít - -Synchronize -Szinkronizál - -Add folder pair -Adjon hozzá könyvtár-párt - -Remove folder pair -Távolítsa el a könyvtár-párt - -Swap sides -Cserélje fel az oldalakat - -Close search bar -Zárja be a keresési sávot - -Find: -Keres: - -Match case -Kis-/nagybetű megkülönböztetése - -Save as batch job -Mentse kötegelt feladatként - -Hide excluded items -Rejtse el a kizárt elemeket - -Show filtered or temporarily excluded files -Mutassa a szűrt vagy ideiglenesen kizárt fájlokat - -Number of files and folders that will be created -A létrehozandó fájlok és könyvtárak száma - -Number of files that will be overwritten -A felülírandó fájlok száma - -Number of files and folders that will be deleted -A törlendő fájlok és könyvtárak száma - -Total bytes to copy -Összesen másolandó bájtok - -Select a variant: -Válasszon egy változatot: - -Identify equal files by comparing modification time and size. -Módosítási idő és méret összehasonlítása alapján azonosítsa az azonos fájlokat. - -Identify equal files by comparing the file content. -Tartalom összehasonlítása alapján azonosítsa az azonos fájlokat. - -Symbolic links: -Szimbolikus linkek: - -More information -További információ - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Változások keresése és bemutatása mindkét oldalon. A törléseket, mozgatásokat és ütközéseket automatikusan ismeri fel egy adatbázis segítségével. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Készítse el a bal oldali könyvtár egy tükör-másolatát, amely pontosan megegyezik a jobb oldali könyvtárral a szinkronizálás után. - -Copy new and updated files to the right folder. -Másolja az új és módosult fájlokat a jobb oldali könyvtárba. - -Configure your own synchronization rules. -Állítsd be a saját szinkronizálási szabályaidat - -Detect moved files -Érzékelje a mozgatott fájlokat - -Requires database files. Not supported by all file systems. -Adatbázis fájlok szükségesek. Nem minden fájlrendszer támogatja. - -Delete files: -Törölje a következő fájlokat: - -Permanent -Állandó - -Delete or overwrite files permanently -Törölje vagy írja felül véglegesen a fájlokat - -Recycle bin -Lomtár - -Back up deleted and overwritten files in the recycle bin -Mentse a törölt és felülírt fájlokat a Lomtárba - -Versioning -Verziókövetés - -Move files to a user-defined folder -Mozgassa a fájlokat egy a felhasználó által meghatározott könyvtárba - -Naming convention: -Elnevezési konvenció: - -Show examples -Mutasson példákat - -Handle errors: -A következő hibák kezelése: - -Ignore -Figyelmen kívül hagy - -Hide all error and warning messages -Rejtse el az összes hibaüzenetet és figyelmeztetést - -Pop-up -Párbeszédablak - -Show pop-up on errors or warnings -Hiba vagy figyelmeztetés esetén mutasson párbeszédablakot - -On completion: -Végrehajtás alatt: - -Start synchronization now? -Megkezdje a szinkronizálást? - -Variant: -Variáns: - -Statistics -Statisztika - -&Don't show this dialog again -&Ne mutassa ismételten ezt a párbeszédablakot - -Items found: -Talált elemek száma: - -Speed: -Sebesség: - -Time remaining: -Hátralévő idő: - -Time elapsed: -Eltelt idő: - -Synchronizing... -Szinkronizálás folyamatban... - -Minimize to notification area -Minimalizálja a figyelmeztetési területre - -Close -Bezár - -&Pause -&Várakozás - -Stop -Leállít - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Készítsen egy parancsfájlt a felügyelet nélküli szinkronizáláshoz. Az indításhoz kattintson kétszer erre a fájlra vagy ütemezze a feladat-tervezőben: %x - -Stop synchronization at first error -Állítsa le a szinkronizálást az első hibánál - -Show progress dialog -Mutassa a folyamatjelző párbeszédablakot - -Save log: -Mentse a következő napló-fájlt: - -Limit: -Határérték: - -Limit maximum number of log files -Naplófájlok maximális számának korlátja - -How can I schedule a batch job? -Hogyan tudok kötegelt feldolgozást ütemezni? - -&Recycle bin -Lomtá&r - -Delete on both sides -Törlés mindkét oldalon - -Delete on both sides even if the file is selected on one side only -Törlés mindkét oldalon, még akkor is, ha a fájl csak az egyik oldalon lett kijelölve - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Meghatározott fájlok szinkronizálásból történő kizárásához válasszon szűrőt. Az útvonalakat a megfelelő könyvtár-párjaikhoz viszonyítva adja meg. - -Include: -Bevonva: - -Exclude: -Kizárva: - -Time span: -Idősáv (időtartam): - -File size: -Fájl méret: - -Minimum: -Minimum: - -Maximum: -Maximum: - -&Clear -&Töröl - -The following settings are used for all synchronization jobs. -A következő beállítások minden szinkronizálási feladatnál használja. - -Fail-safe file copy -Hibamentes fájlmásolás - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -Másolja először egy ideiglenes fájlba (*.ffs_tmp) mielőtt felülírja a célállományt. -Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. - - -(recommended) -(ajánlott) - -Copy locked files -Másolja a zárolt fájlokat - -Copy shared or locked files using the Volume Shadow Copy Service. -Megosztott vagy zárolt fájlok másolása a Kötet-árnyékmásolat szolgáltatás használatával. - -(requires administrator rights) -(rendszergazdai jogosultság szükséges) - -Copy file access permissions -Másolja a fájl hozzáférési jogosultságokat - -Transfer file and folder permissions. -Vigye át a fájl és könyvtár jogosultságokat. - -Automatic retry on error: -Automatikus visszatérés a következő hibánál: - -Retry count: -Visszatérések száma: - -Delay (in seconds): -Késedelem (másodpercben): - -Customize context menu: -Környezeti menü testreszabása: - -Description -Leírás - -Restore hidden windows -Állítsa vissza a rejtett ablakokat - -&Default -&Alapértelmezett - -Source code written in C++ using: -A programot C++-ban fejlesztették a következők felhasználásával: - -If you like FreeFileSync -Ha szereted a FreeFileSync-et - -Donate with PayPal -Támogasd a PayPal segítségével - -Feedback and suggestions are welcome -Várjuk a visszajelzéseket és az ötleteket - -Homepage -Honlap - -Email -E-mail - -Published under the GNU General Public License -Közzétéve a GNU General Public License alatt - -Many thanks for localization: -Köszönet a lokalizációért: - -Save as Batch Job -Mentse kötegelt feladatként - -Delete Items -Törölje az elemeket - -Global Settings -Globális beállítások - -Select Time Span -Válassza ki az idősávot (időtartamot) - -Serious Error -Komoly hiba - -Folder Pairs -Könyvtár-párok - -Find -Keres - -Overview -Áttekintés - -Configuration -Beállítás - -Main Bar -Fő sáv - -Filter Files -Szűrje a fájlokat - -Select View -Válasszon nézetet - -Open... -Megnyit... - -Save -Ment - -Compare both sides -Vesse össze mindkét oldalt - -Comparison settings -Összehasonlítási beállítások - -Synchronization settings -Szinkronizálási beállítások - -Start synchronization -Indítja a szinkronizálást - -Confirm -Jóváhagy - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - -Tényleg a végre akarja hajtani a(z) %y parancsot a következő elemen? -Tényleg a végre akarja hajtani a(z) %y parancsot a következő %x elemen? - - -&Don't show this warning again -&Ne mutassa ismételten ezt a figyelmeztetést - -&Execute -Vé&grehajt - - -1 directory -%x directories - - -1 könyvtárat -%x könyvtárat - - - -1 file -%x files - - -1 fájlt -%x fájlt - - - -%y of 1 row in view -%y of %x rows in view - - -1 sorból %y sor látható -%x sorból %y látható - - -Set direction: -Állítsa be az irányt: - -multiple selection -többszörös kijelölés - -Include via filter: -A szűrőt figyelembe véve adja hozzá - -Exclude via filter: -Zárja ki szűrő segítségével: - -Exclude temporarily -Zárja ki ideiglenesen - -Include temporarily -Csatolja ideiglenesen - -Delete -Töröl - -Include all -Csatolja az összest - -Exclude all -Zárja ki az összest - -Show icons: -Mutassa az ikonokat: - -Small -Kicsi - -Medium -Közepes - -Large -Nagy - -Select time span... -Időintervallum kiválasztása... - -Default view -Alapértelmezett nézet - -Show "%x" -"%x" mutatása - -Last session -Utolsó munkamenet - -Folder Comparison and Synchronization -Könyvtár összehasonlítás és szinkronizálás - -Configuration saved -Beállítások elmentve - -FreeFileSync batch -FreeFileSync kötegelt fájl - -Do you want to save changes to %x? -Akarod menteni a(z) %x változtatásait? - -Do&n't save -&ne mentse - -Never save &changes -N&e mentse a változásokat - -Filter -Szűrő - -Show files that exist on left side only -Mutassa a csak a bal oldalon létező fájlokat - -Show files that exist on right side only -Mutassa a csak a jobb oldalon létező fájlokat - -Show files that are newer on left -Mutassa a fájlokat, amelyek a bal oldalon frissebbek - -Show files that are newer on right -Mutassa a fájlokat, amelyek a jobb oldalon frissebbek - -Show files that are equal -Mutassa az egyező fájlokat - -Show files that are different -Mutassa az eltérő fájlokat - -Show conflicts -Mutassa az ütközéseket - -Show files that will be created on the left side -Mutassa a bal oldalon létrehozandó fájlokat - -Show files that will be created on the right side -Mutassa a jobb oldalon létrehozandó fájlokat - -Show files that will be deleted on the left side -Mutassa a bal oldalon törlendő fájlokat - -Show files that will be deleted on the right side -Mutassa a jobb oldalon törlendő fájlokat - -Show files that will be overwritten on left side -Mutassa a bal oldalon felülírandó fájlokat - -Show files that will be overwritten on right side -Mutassa a jobb oldalon felülírandó fájlokat - -Show files that won't be copied -Mutassa a nem másolandó fájlokat - -Set as default -Beállítás alapértelmezettként - -All folders are in sync -Minden könyvtár szinkronban - -Synchronization Settings -Szinkronizálási beállítások - -Comparison Settings -Összehasonlítási beállítások - -Cannot find %x -Nem található: %x - -Comma-separated values -Vesszővel elválasztott értékek (CSV) - -File list exported -A fájllista exportálása befejeződött - -Searching for program updates... -Programfrissítés keresése... - -Scanning... -Vizsgálat folyamatban... - -Comparing content... -Tartalom összehasonlítása... - -Info -Információ - -Warning -Figyelmeztetés - -Paused -Megállítva - -Initializing... -Inicializálás... - -Stopped -Leállítva - -Completed -Befejeződött - -&Continue -&Folytat - -Log -Napló - -Today -Mai - -This week -E heti - -This month -E havi - -This year -Ez évi - -Last x days -Utolsó x nap - -Byte -Bájt - -KB -kB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Tényleg a Lomtárba akarja áthelyezni a következő elemet? -Tényleg a Lomtárba akarja áthelyezni a következő %x elemet? - - -Move -Mozgat - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Valóban törölni akarod a következő elemet? -Valóban törölni akarod a következő %x elemet? - - -Exclude -Kizár - -Direct -Közvetlen - -Follow -Követ - -Copy NTFS permissions -Másolja az NTFS jogosultságokat - -Integrate external applications into context menu. The following macros are available: -Integráljon a helyi menübe külső alkalmazásokat. A következő makrók érhetők el: - -- full file or folder name -- teljes fájl- vagy könyvtárnév - -- folder part only -- csak a könyvtár része - -- Other side's counterpart to %item_path% -- A másik oldali megfelelőjét a következő fájlba: %item_path% - -- Other side's counterpart to %item_folder% -- A másik oldali megfelelőjét a következő könyvtárba: %item_folder% - -Restore all hidden windows and warnings? -Visszaállítsa az összes rejtett ablakot és figyelmeztetést? - -Leave as unresolved conflict -Hagyja feloldatlan ütközésként - -Replace -Felülír - -Move files and replace if existing -Mozgassa a fájlokat és írja felül amennyiben léteztek - -Time stamp -Időbélyeg - -Append a timestamp to each file name -Adjon időbélyeget minden fájlnévhez - -File -Fájl - -YYYY-MM-DD hhmmss -ÉÉÉÉ-HH-NN óóppmm - -Files -Fájlok - -Items -Elemek - -Percentage -Százalék - -&Ignore subsequent errors -F&igyelmen kívül hagyja az utána következő hibákat - -&Ignore -&Kihagy - -&Switch -&Váltás - -&Yes -&Igen - -&No -&Nem - -Cannot monitor directory %x. -Nem sikerült monitorozni a(z) %x könyvtárat. - -Conversion error: -Konverziós hiba: - -Cannot delete file %x. -Nem sikerült a(z) %x fájl törlése. - -The file is locked by another process: -A fájlt egy másik processz zárolta: - -Cannot move file %x to %y. -Nem sikerült %x fájl mozgatása %y fájlba. - -Cannot delete directory %x. -Nem sikerült a %x könyvtár törlése. - -Cannot write file attributes of %x. -Nem sikerült %x fájl attribútumainak írása. - -Cannot write modification time of %x. -Nem sikerült felírni az utolsó módosítás dátumát a következőnél: %x. - -Cannot read security context of %x. -Nem sikerült a biztonsági környezet olvasása a következőnél: %x. - -Cannot write security context of %x. -Nem sikerült a biztonsági környezet írása a következőnél: %x. - -Cannot read permissions of %x. -Nem sikerült a jogosultságok olvasása a következőnél: %x. - -Cannot write permissions of %x. -Nem sikerült a jogosultságok írása a következőnél: %x. - -Cannot create directory %x. -Nem sikerült a következő könyvtár létrehozása: %x. - -Cannot create symbolic link %x. -Nem lehet létrehozni a(z) %x szimbolikus hivatkozást - -Cannot find system function %x. -Nem található a következő rendszerfunkció: %x. - -Cannot copy file %x to %y. -%x fájl másolása a(z) %y fájlba nem sikerült. - -Type of item %x is not supported: -A(z) %x elem típusa nem támogatott: - -Cannot resolve symbolic link %x. -Nem sikerült a következő szimbolikus hivatkozás feloldása: %x. - -Cannot open directory %x. -A %x könyvtár megnyitása nem sikerült. - -Cannot enumerate directory %x. -Nem sikerült besorolni a(z) %x könyvtárat. . - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -1 perc -%x perc - - - -1 hour -%x hours - - -1 óra -%x óra - - - -1 day -%x days - - -1 nap -%x nap - - -Unable to register to receive system messages. -Nem tud regisztrálni hogy megkapja a rendszerüzeneteket. - -Cannot set privilege %x. -A következő privilégium beállítása sikertelen: %x. - -Unable to suspend system sleep mode. -Nem tudta felfüggeszteni a rendszer alvó üzemmódját. - -Cannot change process I/O priorities. -Nem sikerült a folyamatok I/O prioritását megváltoztatni. - -Unable to move %x to the recycle bin. -%x nem helyezhető a Lomtárba - -Cannot determine final path for %x. -Nem lehet meghatározni a végső útvonalat a(z) %x számára. - -Error Code %x: -Hibakód %x: - diff --git a/BUILD/Languages/italian.lng b/BUILD/Languages/italian.lng deleted file mode 100644 index c18c6d30..00000000 --- a/BUILD/Languages/italian.lng +++ /dev/null @@ -1,1519 +0,0 @@ -
    - Italiano - Luciano Paravella - it_IT - flag_italy.png - 2 - n == 1 ? 0 : 1 -
    - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -Retrying operation - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Both sides have changed since last synchronization. -Entrambi i lati sono cambiati dall'ultima sincronizzazione. - -Cannot determine sync-direction: -Impossibile determinare direzione di sincronia: - -No change since last synchronization. -Nessun cambiamento dall'ultima sincronizzazione. - -The database entry is not in sync considering current settings. -La voce di database non è in sincronia con le impostazioni correnti. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Imposta le direzioni di sincronizzazione predefinite: i vecchi file saranno sovrascritti dai nuovi. - -Checking recycle bin availability for folder %x... -Controllo di disponibilità Cestino per la cartella %x... - -Moving file %x to the recycle bin -Spostamento file %x nel cestino - -Moving folder %x to the recycle bin -Spostamento delle cartelle %x nel cestino - -Moving symbolic link %x to the recycle bin -Spostamento collegamento %x nel cestino - -Deleting file %x -Eliminazione file %x - -Deleting folder %x -Eliminazione cartella %x - -Deleting symbolic link %x -Eliminazione collegamento %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -Il cestino non è disponibile per le seguenti cartelle. I file verranno invece eliminati in modo permanente: - -An exception occurred -Si è verificata una eccezione - -A directory path is expected after %x. -Un percorso di directory è previsto dopo %x. - -Syntax error -Errore di sintassi - -Cannot open file %x. -Impossibile aprire il file %x. - -Error -Errore - -File %x does not contain a valid configuration. -Il file %x non contiene una configurazione valida. - -Unequal number of left and right directories specified. -Numero disuguale di directory sinistra e destra specificate. - -The config file must not contain settings at directory pair level when directories are set via command line. -Il file di configurazione non deve contenere le impostazioni a livello di coppia di directory quando le directory sono impostati tramite linea di comando. - -Warning -Attenzione - -Directories cannot be set for more than one configuration file. -Directory non possono essere impostate per più di un file di configurazione. - -Syntax: -Sintassi: - -config files -file di configurazione - -directory -directory - -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 directories for at most one config file. -Qualsiasi numero di directory alternativa per al massimo un file di configurazione. - -Command line -Linea di comando - -A folder input field is empty. -Un campo di input cartella è vuoto. - -The corresponding folder will be considered as empty. -La cartella corrispondente sarà considerata come vuota. - -Cannot find the following folders: -Impossibile trovare le seguenti cartelle: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -È possibile ignorare questo errore che considera ogni cartella come vuota. Le cartelle poi verranno create automaticamente durante la sincronizzazione. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -Le seguenti cartelle hanno percorsi dipendenti. Fare attenzione quando si imposta le regole di sincronizzazione: - -File %x has an invalid date. -Il file %x ha una data non valida. - -Date: -Data: - -Files %x have the same date but a different size. -I file %x hanno la stessa data ma dimensione diversa. - -Size: -Dimensione: - -Items differ in attributes only -Gli oggetti differiscono solo negli attributi - -Resolving symbolic link %x -Risoluzione collegamento %x - -Comparing content of files %x -Confronto contenuto del file %x - -Generating file list... -Generazione elenco file... - -Starting comparison -Inizio confronto - -Calculating sync directions... -Calcolo della direzione di sincronizzazione... - -Out of memory. -Memoria insufficiente. - -Item exists on left side only -L'oggetto esiste solo sul lato sinistro - -Item exists on right side only -L'oggetto esiste solo sul lato destro - -Left side is newer -Il più recente è sul lato sinistro - -Right side is newer -Il più recente è sul lato destro - -Items have different content -Gli oggetti hanno contenuto differente - -Both sides are equal -Entrambi i lati sono uguali - -Conflict/item cannot be categorized -Conflitto/oggetto non categorizzabile - -Copy new item to left -Copia nuovo oggetto a sinistra - -Copy new item to right -Copia nuovo oggetto a destra - -Delete left item -Elimina oggetto di sinistra - -Delete right item -Elimina oggetto di destra - -Move file on left -Sposta il file a sinistra - -Move file on right -Sposta il file a destra - -Overwrite left item -Sovrascrivi oggetto di sinistra - -Overwrite right item -Sovrascrivi oggetto di destra - -Do nothing -Non fare nulla - -Update attributes on left -Aggiorna attributi a sinistra - -Update attributes on right -Aggiorna attributi a destra - -Database file %x is incompatible. -Il file del database %x non è compatibile. - -Initial synchronization: -Prima sincronizzazione: - -Database file %x does not yet exist. -Il file del database %x non è ancora stato creato. - -Database file is corrupt: -Il file del database è corrotto: - -Cannot write file %x. -Impossibile scrivere il file %x. - -Cannot read file %x. -Impossibile leggere il file %x. - -Database files do not share a common session. -I file del database non condividono una sessione comune. - -Searching for folder %x... -Ricerca della cartella %x... - -Cannot read file attributes of %x. -Impossibile leggere gli attributi del file %x. - -Cannot get process information. -Impossibile ottenere informazioni sul processo. - -Waiting while directory is locked (%x)... -Attendi mentre la cartella è bloccata (%x)... - - -1 sec -%x sec - - -1 sec -%x sec - - -Creating file %x -Creazione file %x - -Items processed: -Oggetti processati: - -Items remaining: -Oggetti rimanenti: - -Total time: -Tempo totale: - - -1 byte -%x bytes - - -1 byte -%x byte - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Errore nel parsing del file %x, riga %y, colonna %z. - -Cannot set directory lock for %x. -Impossibile impostare blocco cartella per %x. - -Scanning: -Scansione di: - - -1 thread -%x threads - - -1 thread -%x thread - - -Encoding extended time information: %x -Codifica estesa informazioni orario: %x - -/sec -/sec - -%x items/sec -%x elementi/sec - -Configuration file %x loaded partially only. -File di configurazione %x caricato solo parzialmente. - -Show in Explorer -Mostra in Esplora Risorse - -Open with default application -Apri con applicazione predefinita - -Browse directory -Sfoglia cartelle - -Cannot access the Volume Shadow Copy Service. -Impossibile accedere al Volume Shadow Copy Service. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -E' necessario utilizzare FreeFileSync versione 64-bit per creare copie shadow su questo sistema. - -Cannot load file %x. -Impossibile caricare il file %x. - -Cannot determine volume name for %x. -Impossibile determinare nome del volume per %x. - -Volume name %x is not part of file path %y. -Nome volume %x non fa parte del percorso del file %y. - -Stop requested: Waiting for current operation to finish... -Bloccata richiesta: Aspettare la fine dell'operazione in corso ... - -Unable to create timestamp for versioning: -Impossibile creare l'ora per la versione: - -Cannot read the following XML elements: -Impossibile leggere i seguenti elementi XML: - -&Open... -&Apri... - -Save &as... -Salva &come... - -&Quit -&Esci - -&Program -&Programma - -&View help -&Vedere aiuto - -&About -&Informazioni - -&Help -&Aiuto - -Usage: -Uso: - -1. Select folders to watch. -1. Seleziona cartelle da controllare. - -2. Enter a command line. -2. Inserisci linea di comando. - -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 - -Folders to watch: -Cartelle da guardare: - -Add folder -Aggiungi cartella - -Remove folder -Rimuovi cartella - -Browse -Sfoglia - -Select a folder -Seleziona una cartella - -Idle time (in seconds): -Tempo di inattività (in secondi): - -Idle time between last detected change and execution of command -Tempo di attesa tra ultimo cambiamento rilevato ed l'esecuzione del comando - -Command line: -Riga di comando: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -Il comando è attivato se: -- file o sottocartelle cambiano -- nuove cartelle vengono aggiunte (es. inserimento di memoria USB) - - -&Start -&Start - -&Retry -&Riprova - -Cancel -Annulla - -Build: %x -Versione: %x - -About -Info su - -All files -Tutti i file - -Automated Synchronization -Sincronizzazione automatizzata - -Directory monitoring active -Directory di monitoraggio attiva - -Waiting until all directories are available... -Aspettare fino a quando sono disponibili tutte le directory... - -&Restore -&Ripristina - -&Show error -&Mostra errore - -&Exit -&Esci - -Incorrect command line: -Linea di comando non corretta: - -File content -Contenuto del file - -File time and size -Ora e dimensione file - -Two way -Due vie - -Mirror -Specchio - -Update -Aggiorna - -Custom -Personalizza - -Multiple... -Multiplo... - -Moving file %x to %y -Spostamento file %x in %y - -Moving folder %x to %y -Spostamento cartella %x in %y - -Moving symbolic link %x to %y -Spostamento collegamento %x in %y - -Removing old versions... -Rimozione di vecchie versioni... - -Creating symbolic link %x -Creazione collegamento %x - -Creating folder %x -Creazione cartella %x - -Overwriting file %x -Sovrascrittura file %x - -Overwriting symbolic link %x -Sovrascrittura collegamento %x - -Verifying file %x -Verifica file %x - -Updating attributes of %x -Aggiornamento attributi di %x - -Cannot find %x. -Impossibile trovare %x. - -Target folder %x already existing. -La cartella di destinazione %x è già esistente. - -Target folder input field must not be empty. -Il campo per la cartella di destinazione non può essere vuoto. - -Please enter a target folder for versioning. -Inserisci una cartella di destinazione per il controllo delle versioni. - -Source folder %x not found. -Cartella sorgente %x non trovata. - -The following items have unresolved conflicts and will not be synchronized: -I seguenti oggetti hanno conflitti irrisolti e non saranno sincronizzati: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -Le seguenti cartelle sono significativamente differenti. Assicurarsi che si sta confrontando le cartelle corrette per la sincronizzazione. - -Not enough free disk space available in: -Spazio libero su disco insufficiente in: - -Required: -Richiesto: - -Available: -Disponibile: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Verrà modificata una cartella che è parte di molte coppie di cartelle. Controlla le impostazioni di sincronizzazione. - -Synchronizing folder pair: -Sincronizzazione della coppia di cartelle: - -Generating database... -Generazione database... - -Creating a Volume Shadow Copy for %x... -Creazione di un Volume Shadow Copy per %x... - -Data verification error: %x and %y have different content. -Dati di verifica errore: %x e %y hanno un contenuto diverso. - -job name -nome del lavoro - -Synchronization stopped -Sincronizzazione fermata - -Synchronization completed with errors -Sincronizzazione completata con errori - -Synchronization completed with warnings -Sincronizzazione completata con avvisi - -Nothing to synchronize -Non c'è nulla da sincronizzare - -Synchronization completed successfully -Sincronizzazione completata con successo - -Saving log file %x... -Salvataggio file di log %x... - -You can switch to FreeFileSync's main window to resolve this issue. -È possibile passare alla finestra principale di FreeFileSync per risolvere questo problema. - -Switching to FreeFileSync's main window -Passaggio alla finestra principale di FreeFileSync - -A new version of FreeFileSync is available: -E' disponibile una nuova versione di FreeFileSync: - -Download now? -Scaricarla ora? - -Check for Program Updates -Controlla Aggiornamenti del Programma - -&Download -&Scaricare - -FreeFileSync is up to date. -FreeFileSync è aggiornato. - -Information -Informazioni - -Unable to connect to sourceforge.net. -Impossibile collegarsi a sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Non riesci a trovare l'attuale numero di versione di FreeFileSync on-line . Vuoi controllare manualmente? - -Symlink -Symlink - -Folder -Cartella - -Full path -Percorso completo - -Name -Nome - -Relative path -Percorso relativo - -Base folder -Cartella di partenza - -Size -Dimensione - -Date -Data - -Extension -Estensione - -Category -Categoria - -Action -Azioni - -Drag && drop -Trascina - -Close progress dialog -Chiudi stato di avanzamento - -Standby -Sospendi - -Log off -Termina sessione - -Shut down -Arresta - -Hibernate -Iberna - -Alternate comparison settings -Impostazioni di Confronto Alternativi - -Alternate synchronization settings -Impostazioni di Sincronizzazione Alternativi - -Local filter -Filtro Locale - -Active -Attivo - -None -Nessuno - -Remove alternate settings -Rimuovi impostazioni alternative - -Clear filter settings -Azzera impostazioni filtro - -Copy -Copia - -Paste -Incolla - -Alternate Comparison Settings -Impostazioni di Confronto Alternativi - -Alternate Synchronization Settings -Impostazioni di Sincronizzazione Alternativi - -Local Filter -Filtro Locale - -&New -&Nuovo - -&Save -&Salva - -Save as &batch job... -Salva come &processo batch... - -1. &Compare -1. &Compara - -2. &Synchronize -2. &Sincronizza - -&Global settings -&Preferenze - -&Language -&Lingua - -&Find... -&Trova ... - -&Export file list... -&Esporta l'elenco dei file... - -&Tools -&Strumenti - -&Check now -&Controlla ora - -Check &automatically once a week -Controlla &automaticamente una volta a settimana - -&Check for new version -&Controlla nuova versione - -Compare -Compara - -Synchronize -Sincronizza - -Add folder pair -Aggiungi una coppia di cartelle - -Remove folder pair -Elimina la coppia di cartelle - -Swap sides -Inverti i lati - -Close search bar -Chiudi barra di ricerca - -Find: -Trova: - -Match case -Corrispondenza - -Save as batch job -Salva come processo batch - -Hide excluded items -Nascondi oggetti esclusi - -Show filtered or temporarily excluded files -Mostra file filtrati o temporaneamente esclusi - -Number of files and folders that will be created -Numero di file e cartelle che verranno creati - -Number of files that will be overwritten -Numero di file che verranno sovrascritti - -Number of files and folders that will be deleted -Numero di file e cartelle che verranno eliminati - -Total bytes to copy -Bytes totali da copiare - -Select a variant: -Scegli una variante: - -Identify equal files by comparing modification time and size. -Identificare i file uguali confrontando data di modifica e dimensione. - -Identify equal files by comparing the file content. -Identificare i file uguali confrontando il contenuto del file. - -Symbolic links: -Link simbolici: - -More information -Maggiori informazioni - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Identifica e propaga cambiamenti su entrambi i lati. Cancellazioni, spostamenti e conflitti sono identificati automaticamente usando un database. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Creare un backup specchio della cartella di sinistra, che corrisponde esattamente alla cartella giusta dopo la sincronizzazione. - -Copy new and updated files to the right folder. -Copiare i file nuovi e aggiornati nella cartella giusta. - -Configure your own synchronization rules. -Configura le tue regole di sincronizzazione. - -Detect moved files -Rileva file spostati - -Requires database files. Not supported by all file systems. -Richiede il file di database. Non è supportato da tutti i sistemi di file. - -Delete files: -Eliminare i file: - -Permanent -Permanente - -Delete or overwrite files permanently -Elimina o sovrascrivi i file definitivamente - -Recycle bin -Cestino - -Back up deleted and overwritten files in the recycle bin -Eseguire il backup di file sovrascritti e cancellati nel cestino - -Versioning -Versione - -Move files to a user-defined folder -Spostare i file in una cartella definita dall'utente - -Naming convention: -Modalità di rinomina: - -Show examples -Mostra esempi - -Handle errors: -Gestire gli errori: - -Ignore -Ignora - -Hide all error and warning messages -Nascondi tutti gli errori e i messaggi d'avviso - -Pop-up -Pop-up - -Show pop-up on errors or warnings -Mostra popup di errore o avviso - -On completion: -Al termine: - -Start synchronization now? -Avviare la sincronizzazione ora? - -Variant: -Variante: - -Statistics -Statistiche - -&Don't show this dialog again -&Non mostrare più questo avviso - -Items found: -Oggetti trovati: - -Speed: -Velocita': - -Time remaining: -Tempo rimanente: - -Time elapsed: -Tempo trascorso: - -Synchronizing... -Sincronizzazione... - -Minimize to notification area -Ridurre al minimo l'area di notifica - -Close -Chiudi - -&Pause -&Pausa - -Stop -Arresto - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Creare un file batch per la sincronizzazione automatica. Per iniziare, fare doppio clic su questo file o programmarlo in un pianificatore di compiti: %x - -Stop synchronization at first error -Interrompere la sincronizzazione al primo errore - -Show progress dialog -Mostra stato di avanzamento - -Save log: -Salva log: - -Limit: -Limite: - -Limit maximum number of log files -Limita il numero massimo di file log - -How can I schedule a batch job? -Come posso programmare un lavoro batch? - -&Recycle bin -&Cestino - -Delete on both sides -Elimina su entrambi i lati - -Delete on both sides even if the file is selected on one side only -Elimina su entrambi i lati anche se il file è selezionato su un solo lato. - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Selezionare le regole del filtro per escludere alcuni file dalla sincronizzazione. Immettere i percorsi dei file relativi alla loro corrispondente coppia di cartelle. - -Include: -Includi: - -Exclude: -Escludi: - -Time span: -Intervallo di tempo: - -File size: -Dimensione del file: - -Minimum: -Minimo: - -Maximum: -Massimo: - -&Clear -&Pulisci - -The following settings are used for all synchronization jobs. -Le seguenti impostazioni vengono utilizzate per tutti i processi di sincronizzazione. - -Fail-safe file copy -Copia file di fail-safe - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -Copiare in un file temporaneo ( *.ffs_tmp ) prima di sovrascrivere il bersaglio. -Questo garantisce uno stato costante anche in caso di un errore grave. - - -(recommended) -(consigliato) - -Copy locked files -Copia file bloccati - -Copy shared or locked files using the Volume Shadow Copy Service. -Copiare file condivisi o bloccati con il Volume Shadow Copy Service. - -(requires administrator rights) -(richiede i diritti di amministratore) - -Copy file access permissions -Copia permessi di accesso file - -Transfer file and folder permissions. -Trasferimento file e autorizzazioni delle cartelle. - -Automatic retry on error: -Tentativo Automaticoin caso di errore: - -Retry count: -Riprova conta: - -Delay (in seconds): -Ritardo (in secondi): - -Customize context menu: -Personalizzare menu contestuale: - -Description -Descrizione - -Restore hidden windows -Ripristina le finestre nascoste - -&Default -&Predefinito - -Source code written in C++ using: -Codice sorgente scritto in C++ utilizzando: - -If you like FreeFileSync -Se ti piace FreeFileSync - -Donate with PayPal -Fai una donazione con PayPal - -Feedback and suggestions are welcome -Ogni commento o suggerimento è ben accetto - -Homepage -Homepage - -Email -Email - -Published under the GNU General Public License -Pubblicato sotto licenza GNU General Public - -Many thanks for localization: -Ringraziamenti per la localizzazione: - -Save as Batch Job -Salva come Processo Batch - -Delete Items -Elimina Elementi - -Global Settings -Impostazioni Globali - -Select Time Span -Selezionare Intervallo di Tempo - -Folder Pairs -Coppie di Cartelle - -Find -Trova - -Overview -Anteprima - -Configuration -Configurazione - -Main Bar -Barra Principale - -Filter Files -Filtrare i file - -Select View -Selezionare Visualizza - -Open... -Apri... - -Save -Salva - -Compare both sides -Confronta entrambi i lati - -Comparison settings -Impostazioni di comparazione - -Synchronization settings -Parametri di sincronizzazione - -Start synchronization -Avvia sincronizzazione - -Confirm -Confermare - -&Execute -&Esecuzione - - -1 directory -%x directories - - -1 cartella -%x cartelle - - - -1 file -%x files - - -1 file -%x file - - - -%y of 1 row in view -%y of %x rows in view - - -%y di 1 riga in vista -%y di %x righe in vista - - -Set direction: -Imposta direzione: - -multiple selection -selezione multipla - -Include via filter: -Includi tramite filtro: - -Exclude via filter: -Escludi tramite filtro: - -Exclude temporarily -Escludi temporaneamente - -Include temporarily -Includi temporaneamente - -Delete -Elimina - -Include all -Includi tutto - -Exclude all -Escludi tutto - -Show icons: -Mostra icone: - -Small -Piccole - -Medium -Medie - -Large -Grandi - -Select time span... -Seleziona intervallo di tempo... - -Default view -Vista normale - -Show "%x" -Mostra "%x" - -Last session -Ultima sessione - -Folder Comparison and Synchronization -Comparazione di cartelle e sincronizzazione - -Configuration saved -Configurazione salvata - -FreeFileSync batch -FreeFileSync batch - -Do you want to save changes to %x? -Vuoi salvare le modifiche a %x? - -Do&n't save -No&n salvare - -Never save &changes -Non salvare mai &modifiche - -Filter -Filtro - -Show files that exist on left side only -Mostra file esistenti solo a sinistra - -Show files that exist on right side only -Mostra file esistenti solo a destra - -Show files that are newer on left -Mostra i file che sono più recenti sulla sinistra - -Show files that are newer on right -Mostra i file che sono più recenti sulla destra - -Show files that are equal -Mostra file identici - -Show files that are different -Mostra file differenti - -Show conflicts -Mostra conflitti - -Show files that will be created on the left side -Mostra file che verranno creati sul lato sinistro - -Show files that will be created on the right side -Mostra file che verranno creati sul lato destro - -Show files that will be deleted on the left side -Mostra file che verranno cancellati sul lato sinistro - -Show files that will be deleted on the right side -Mostra file che verranno cancellati sul lato destro - -Show files that will be overwritten on left side -Mostra file che verranno sovrascritti sul lato sinistro - -Show files that will be overwritten on right side -Mostra file che verranno sovrascritti sul lato destro - -Show files that won't be copied -Mostra file che non saranno copiati - -Set as default -Imposta come predefinito - -All folders are in sync -Tutte le cartelle sono sincronizzate - -Synchronization Settings -Impostazioni di Sincronizzazione - -Comparison Settings -Impostazioni di Confronto - -Cannot find %x -Impossibile trovare %x - -Comma-separated values -Valori separati da virgole - -File list exported -Elenco file esportato - -Searching for program updates... -Ricerca di aggiornamenti al programma... - -&Ignore subsequent errors -&Ignora errori successivi - -&Ignore -&Ignora - -Serious Error -Errore Grave - -&Don't show this warning again -&Non mostrare più questo avviso - -&Switch -&Passa - -&Yes -&Si - -&No -&No - -Scanning... -Scansione... - -Comparing content... -Comparazione contenuto... - -Info -Info - -Paused -In pausa - -Initializing... -Inizializzazione... - -Stopped -Arrestato - -Completed -Completato - -&Continue -&Continuare - -Log -Log - -Today -Oggi - -This week -Questa settimana - -This month -Questo mese - -This year -Quest'anno - -Last x days -Ultimi x giorni - -Byte -Byte - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Vuoi davvero spostare il seguente oggetto nel cestino? -Vuoi davvero spostare i seguenti %x oggetti nel cestino? - - -Move -Spostare - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Vuoi veramente eliminare il seguente oggetto? -Vuoi veramente eliminare i seguenti %x oggetti? - - -Exclude -Escludi - -Direct -Diretto - -Follow -Segui - -Copy NTFS permissions -Copia permessi NTFS - -Integrate external applications into context menu. The following macros are available: -Integra applicazioni esterne nel menu contestuale. Sono disponibili le seguenti macro: - -- full file or folder name -- nome completo file o cartella - -- folder part only -- solo porzione cartella - -- Other side's counterpart to %item_path% -- Omologo dell'altro lato a %item_path% - -- Other side's counterpart to %item_folder% -- Omologo dell'altro lato a %item_folder% - -Restore all hidden windows and warnings? -Ripristina tutte le finestre nascoste e gli avvisi? - -Leave as unresolved conflict -Lascia come conflitti irrisolti - -Replace -Sostituisci - -Move files and replace if existing -Sposta file e sostituisci se esistenti - -Time stamp -Data e ora - -Append a timestamp to each file name -Accoda data e ora ad ogni nome di file - -File -File - -YYYY-MM-DD hhmmss -AAAA-MM-GG hhmmss - -Files -File - -Items -Oggetti - -Percentage -Percentuale - -Cannot monitor directory %x. -Impossibile monitorare la cartella %x. - -Conversion error: -Errore di conversione: - -Cannot delete file %x. -Impossibile eliminare il file %x. - -The file is locked by another process: -Il file è bloccato da un altro processo: - -Cannot move file %x to %y. -Impossibile spostare il file %x in %y. - -Cannot delete directory %x. -Impossibile eliminare la cartella %x. - -Cannot write file attributes of %x. -Impossibile scrivere attributi file di %x. - -Cannot write modification time of %x. -Impossibile scrivere data e ora di modifica di %x. - -Cannot read security context of %x. -Impossibile leggere il contesto di protezione di %x. - -Cannot write security context of %x. -Impossibile scrivere il contesto di protezione di %x. - -Cannot read permissions of %x. -Impossibile leggere i permessi di %x. - -Cannot write permissions of %x. -Impossibile scrivere i permessi di %x. - -Cannot create directory %x. -Impossibile creare cartella %x. - -Cannot create symbolic link %x. -Impossibile creare il collegamento %x. - -Cannot find system function %x. -Impossibile trovare la funzione di sistema %x. - -Cannot copy file %x to %y. -Impossibile copiare file %x in %y. - -Type of item %x is not supported: -Il tipo di oggetto %x non è supportato: - -Cannot resolve symbolic link %x. -Impossibile risolvere collegamento %x. - -Cannot open directory %x. -Impossibile aprire cartella %x. - -Cannot enumerate directory %x. -Impossibile numerare cartella %x. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -1 min -%x min - - - -1 hour -%x hours - - -1 ora -%x ore - - - -1 day -%x days - - -1 giorno -%x giorni - - -Unable to register to receive system messages. -Impossibile registrarsi per ricevere i messaggi di sistema. - -Cannot set privilege %x. -Impossibile impostare privilegi %x. - -Unable to suspend system sleep mode. -Impossibile sospendere la modalità di sonno del sistema. - -Cannot change process I/O priorities. -Impossibile modificare le priorità I/O del processo. - -Unable to move %x to the recycle bin. -Impossibile spostare %x nel cestino. - -Cannot determine final path for %x. -Impossibile determinare il percorso finale per %x. - -Error Code %x: -Errore Codice %x: - diff --git a/BUILD/Languages/japanese.lng b/BUILD/Languages/japanese.lng deleted file mode 100644 index d47a75fc..00000000 --- a/BUILD/Languages/japanese.lng +++ /dev/null @@ -1,1508 +0,0 @@ -
    - 日本語 - Tilt - ja_JP - flag_japan.png - 1 - 0 -
    - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -Retrying operation - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Both sides have changed since last synchronization. -前回最後の同期処理以降、両側とも変更があります. - -Cannot determine sync-direction: -同期方向が決定されていません: - -No change since last synchronization. -前回の同期以降、変更はありません. - -The database entry is not in sync considering current settings. -現在の設定により、このデータベースエントリの同期は行われていません. - -Setting default synchronization directions: Old files will be overwritten with newer files. -同期方向のデフォルト設定: 古いファイルに新しいファイルを上書き - -Checking recycle bin availability for folder %x... -フォルダ %x をゴミ箱として使用できるかを確認中... - -Moving file %x to the recycle bin -ファイル %x をゴミ箱に移動中 - -Moving folder %x to the recycle bin -フォルダ %x をゴミ箱に移動中 - -Moving symbolic link %x to the recycle bin -シンボリックリンク %x をゴミ箱に移動中 - -Deleting file %x -ファイル %x を削除中 - -Deleting folder %x -フォルダ %x を削除中 - -Deleting symbolic link %x -シンボリックリンク %x を削除中 - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -以下のフォルダではゴミ箱を利用できません。代替手段としてファイルの完全削除を利用することが出来ます: - -An exception occurred -例外が発生しました - -A directory path is expected after %x. -%x の後にはディレクトリ・パスが必要です - -Syntax error -構文エラー - -Cannot open file %x. -ファイル %x を開けません - -Error -エラー - -File %x does not contain a valid configuration. -ファイル %x には有効な構成が含まれていません. - -Unequal number of left and right directories specified. -誤った数の左右ディレクトリが指定されています. - -The config file must not contain settings at directory pair level when directories are set via command line. -コマンドライン経由でディレクトリが設定されている時は、構成設定にペア・レベルのディレクトリは含められません. - -Warning -警告 - -Directories cannot be set for more than one configuration file. -構成設定ファイルにひとつ以上のディレクトリは設定できません. - -Syntax: -構文: - -config files -構成設定ファイル - -directory -ディレクトリ - -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -任意の数の FreeFileSync 構成設定ファイル(.ffs_gui および/または .ffs_batch). - -Any number of alternative directories for at most one config file. -ひとつの構成設定ファイルに対して任意の数の代替ディレクトリ. - -Command line -コマンドライン - -A folder input field is empty. -フォルダ入力欄が空白です. - -The corresponding folder will be considered as empty. -対応するフォルダは空であるとみなされます - -Cannot find the following folders: -以下のフォルダがみつかりません: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -各フォルダを空とみなす場合はこのエラーを無視することができます。このフォルダは、同期処理を実行する際自動的に作成されるものです。 - -The following folders have dependent paths. Be careful when setting up synchronization rules: -以下のフォルダには、依存関係にあるパスが存在します。同期規則を設定する時は慎重に行ってください: - -File %x has an invalid date. -ファイル %x の日付は無効なものです. - -Date: -日時: - -Files %x have the same date but a different size. -ファイル %x は、同じ時間ですがサイズが異なっています. - -Size: -サイズ: - -Items differ in attributes only -属性のみ異なる項目 - -Resolving symbolic link %x -シンボリックリンク %x を解決中 - -Comparing content of files %x -ファイル %x の内容を比較中 - -Generating file list... -ファイル一覧を作成中... - -Starting comparison -比較処理を開始中 - -Calculating sync directions... -同期方向を計算しています... - -Out of memory. -メモリが足りません. - -Item exists on left side only -左側のみに存在する項目 - -Item exists on right side only -右側のみに存在する項目 - -Left side is newer -左側がより新しい - -Right side is newer -右側がより新しい - -Items have different content -内容が異なる項目 - -Both sides are equal -両側とも等しい - -Conflict/item cannot be categorized -競合/分類化できない項目 - -Copy new item to left -新しい項目を左にコピー - -Copy new item to right -新しい項目を右にコピー - -Delete left item -左の項目を削除 - -Delete right item -右の項目を削除 - -Move file on left -ファイルを左に移動 - -Move file on right -ファイルを右に移動 - -Overwrite left item -左の項目に上書き - -Overwrite right item -右の項目に上書き - -Do nothing -何もしない - -Update attributes on left -左の属性を更新 - -Update attributes on right -右の属性を更新 - -Database file %x is incompatible. -データベース %x とは互換性がありません. - -Initial synchronization: -同期処理の初期化: - -Database file %x does not yet exist. -データベース %x は存在しません. - -Database file is corrupt: -破損しているデータベース: - -Cannot write file %x. -ファイル %x に書き込めません. - -Cannot read file %x. -ファイル %x を読み込めません. - -Database files do not share a common session. -データベースは一般セッションで共有できません. - -Searching for folder %x... -フォルダ %x を検索中... - -Cannot read file attributes of %x. -%x のファイル属性を読み込めません. - -Cannot get process information. -プロセス情報を取得できません. - -Waiting while directory is locked (%x)... -待機時間中ディレクトリはロックされます(%x)... - - -1 sec -%x sec - - -%x 秒. - - -Creating file %x -ファイル %x を作成中 - -Items processed: -処理された要素: - -Items remaining: -残りの要素: - -Total time: -合計時間: - - -1 byte -%x bytes - - -%x バイト - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -ファイル %x の構文解析エラー, 行 %y, 列 %z. - -Cannot set directory lock for %x. -%x のディレクトリロックができません. - -Scanning: -スキャン: - - -1 thread -%x threads - - -%x スレッド - - -Encoding extended time information: %x -拡張された時間情報のエンコーディング: %x - -/sec -/秒 - -%x items/sec -%x 項目/秒 - -Configuration file %x loaded partially only. -構成ファイル %x は部分的のみ読み込まれます. - -Show in Explorer -エクスプローラで表示 - -Open with default application -既定アプリケーションで開く - -Browse directory -ディレクトリを参照 - -Cannot access the Volume Shadow Copy Service. -ボリュームシャドウコピーサービスにアクセス出来ません. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -FreeFileSync 64-bit 版を使用してこのシステムにシャドウコピーを作成してください. - -Cannot load file %x. -ファイル %x を読み込めません. - -Cannot determine volume name for %x. -%x のボリューム名が決定されていません - -Volume name %x is not part of file path %y. -ボリューム名 %x は、ファイルパス %y の一部ではありません. - -Stop requested: Waiting for current operation to finish... -停止の要求: 現在の操作が完了するのを待機しています... - -Unable to create timestamp for versioning: -バージョン付けのタイムスタンプを作成出来ません: - -Cannot read the following XML elements: -次の XML要素を読み込めません: - -&Open... -開く(&O)... - -Save &as... -別名保存(&A)... - -&Quit -終了(&Q) - -&Program -プログラム(&P) - -&View help -ヘルプを表示(&V) - -&About -情報(&A) - -&Help -ヘルプ(&H) - -Usage: -使用方法: - -1. Select folders to watch. -1. 監視するフォルダを選択 - -2. Enter a command line. -2. コマンドラインを入力 - -3. Press 'Start'. -3. 'スタート'をクリック - -To get started just import a .ffs_batch file. -一括ファイル(.ffs)からインポートして開始 - -Folders to watch: -監視するフォルダ: - -Add folder -フォルダを追加 - -Remove folder -フォルダ除去 - -Browse -参照 - -Select a folder -フォルダを選択 - -Idle time (in seconds): -待機時間(秒で指定): - -Idle time between last detected change and execution of command -最後にコマンドを実行してから、次に変更を検出するまでの待機時間 - -Command line: -コマンドライン: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -このコマンドのトリガ条件: -- ファイルまたはサブフォルダの変更 -- 新規フォルダの追加 (例. USB の挿入) - - -&Start -開始(&S) - -&Retry -再試行(&R) - -Cancel -キャンセル - -Build: %x -ビルド: %x - -About -情報 - -All files -すべてのファイル - -Automated Synchronization -自動同期 - -Directory monitoring active -ディレクトリの監視アクティブ - -Waiting until all directories are available... -全ディレクトリが利用可能になるまで待機中... - -&Restore -修復(&R) - -&Show error -エラー表示(&S) - -&Exit -終了(&E) - -Incorrect command line: -不正なコマンドライン: - -File content -ファイルの内容 - -File time and size -ファイル時刻とサイズ - -Two way -両方向 - -Mirror -ミラー - -Update -更新 - -Custom -カスタム - -Multiple... -複数処理... - -Moving file %x to %y -ファイル %x を %y に移動中 - -Moving folder %x to %y -フォルダ %x を %y に移動中 - -Moving symbolic link %x to %y -シンボリックリンク %x を %y に移動中 - -Removing old versions... -旧バージョンを除去中... - -Creating symbolic link %x -シンボリックリンク %x を作成中 - -Creating folder %x -フォルダ %x を作成中 - -Overwriting file %x -ファイル %x を上書き中 - -Overwriting symbolic link %x -シンボリックリンク %x を上書き中 - -Verifying file %x -ファイル %x の検証中 - -Updating attributes of %x -%x の属性を更新 - -Cannot find %x. -%x が見つかりません - -Target folder %x already existing. -対象フォルダ %x は既に存在します. - -Target folder input field must not be empty. -対象フォルダ入力欄が空白になっています. - -Please enter a target folder for versioning. -バージョン付けをする対象フォルダを入力 - -Source folder %x not found. -ソースフォルダ %x が見つかりません - -The following items have unresolved conflicts and will not be synchronized: -以下の項目は、未解決の競合が存在するため同期処理を実行できませんでした: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -以下のフォルダには大量の差異が存在します。同期フォルダの双方が正確にセットされているかどうかをもう一度慎重にご確認ください. - -Not enough free disk space available in: -利用可能なディスク空き容量が足りません: - -Required: -必須: - -Available: -利用可能: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -複数フォルダペアの一部であるフォルダが変更されています. 同期設定を確認してください. - -Synchronizing folder pair: -フォルダペアを同期処理: - -Generating database... -データベースを作成中... - -Creating a Volume Shadow Copy for %x... -ボリュームシャドウコピーを作成中 %x... - -Data verification error: %x and %y have different content. -データ検証エラー: %x と %y には異なる内容が含まれています. - -job name -ジョブ名 - -Synchronization stopped -同期処理を停止 - -Synchronization completed with errors -同期処理はエラーで終了しています - -Synchronization completed with warnings -同期処理は警告で終了しています - -Nothing to synchronize -同期対象がありません - -Synchronization completed successfully -同期処理はすべてが正常に完了しました - -Saving log file %x... -ログファイル %x を保存中... - -You can switch to FreeFileSync's main window to resolve this issue. -FreeFileSync のメインウィンドウを切り替えることでこの問題を解決 - -Switching to FreeFileSync's main window -FreeFileSync メインウィンドウの切り替え - -A new version of FreeFileSync is available: -FreeFileSync の新しいバージョンが利用できます: - -Download now? -ダウンロードしますか? - -Check for Program Updates -プログラムのアップデートを確認 - -&Download -ダウンロード(&D) - -FreeFileSync is up to date. -FreeFileSync は最新です. - -Information -インフォメーション - -Unable to connect to sourceforge.net. -Sourceforge.net に接続できません. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -現在の FreeFileSync バージョン番号を確認できませんでした、手動で確認しますか? - -Symlink -シンボリックリンク - -Folder -フォルダ - -Full path -フルパス - -Name -名前 - -Relative path -相対パス - -Base folder -基準フォルダ - -Size -サイズ - -Date -日付 - -Extension -拡張子 - -Category -カテゴリ - -Action -操作 - -Drag && drop -ドラッグ && ドロップ - -Close progress dialog -進捗ダイアログを閉じる - -Standby -スタンバイ - -Log off -ログオフ - -Shut down -シャットダウン - -Hibernate -休止状態 - -Alternate comparison settings -代替比較設定 - -Alternate synchronization settings -代替同期設定 - -Local filter -ローカル フィルター - -Active -アクティブ - -None -なし - -Remove alternate settings -代替設定を除去 - -Clear filter settings -フィルター設定をクリア - -Copy -コピー - -Paste -貼り付け - -Alternate Comparison Settings -代替比較設定 - -Alternate Synchronization Settings -代替同期設定 - -Local Filter -ローカル フィルター - -&New -新規(&N) - -&Save -保存(&S) - -Save as &batch job... -一括ジョブで保存(&B)... - -1. &Compare -1. 比較(&C) - -2. &Synchronize -2. 同期処理(&S) - -&Global settings -全般的な設定(&G) - -&Language -使用言語(&L) - -&Find... -検索(&F)... - -&Export file list... -ファイル一覧をエクスポート(&E)... - -&Tools -ツール(&T) - -&Check now -今すぐ確認(&C) - -Check &automatically once a week -週に一回、自動的に確認する(&A) - -&Check for new version -新バージョンの確認(&C) - -Compare -比較 - -Synchronize -同期処理 - -Add folder pair -フォルダのペアを追加 - -Remove folder pair -フォルダペアを除去 - -Swap sides -パネルを入れ替え - -Close search bar -検索バーを閉じる - -Find: -検索: - -Match case -文字種を区別 - -Save as batch job -一括ジョブで保存 - -Hide excluded items -除外対象項目を隠す - -Show filtered or temporarily excluded files -フィルター済、または一時除外ファイルを表示 - -Number of files and folders that will be created -作成されたファイル、およびフォルダの数 - -Number of files that will be overwritten -上書きされたファイル数 - -Number of files and folders that will be deleted -削除されたファイル、およびフォルダの数 - -Total bytes to copy -コピーする合計バイト - -Select a variant: -メソッドを選択: - -Identify equal files by comparing modification time and size. -更新時刻とサイズを比較して、同一ファイルを識別します - -Identify equal files by comparing the file content. -ファイルの内容を比較して、同一ファイルを識別します - -Symbolic links: -シンボリック リンク: - -More information -更に詳細な情報 - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -変更箇所を識別して両側に変更を反映します。データベースを使用することで、削除、移動および競合などが自動的に検出されます。 - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -同意処理後、右側フォルダと厳密に合致する左側フォルダのミラーバックアップを作成 - -Copy new and updated files to the right folder. -新しい、または更新されたファイルを右フォルダにコピー - -Configure your own synchronization rules. -あなたの設定した同期規則を使用します。 - -Detect moved files -移動済みのファイルを検出する - -Requires database files. Not supported by all file systems. -データベースファイルが必要です(すべてのシステムには未対応). - -Delete files: -ファイルの削除: - -Permanent -完全消去 - -Delete or overwrite files permanently -ファイルを上書き、または完全に削除 - -Recycle bin -ゴミ箱 - -Back up deleted and overwritten files in the recycle bin -ファイルを削除/上書きする時、ゴミ箱にバックアップをとる - -Versioning -バージョン付け - -Move files to a user-defined folder -ユーザ定義のフォルダにファイルを移動 - -Naming convention: -命名規則: - -Show examples -入力例を表示 - -Handle errors: -エラーの取り扱い: - -Ignore -無視 - -Hide all error and warning messages -すべてのエラーと警告メッセージを非表示 - -Pop-up -ポップアップ - -Show pop-up on errors or warnings -エラーと警告をポップアップで表示 - -On completion: -完了時の動作: - -Start synchronization now? -今すぐ同期を開始しますか? - -Variant: -メソッド: - -Statistics -統計 - -&Don't show this dialog again -次回以降から表示しない(&D) - -Items found: -見つかった要素: - -Speed: -速度: - -Time remaining: -残り時間: - -Time elapsed: -経過時間 - -Synchronizing... -同期処理中... - -Minimize to notification area -通知領域に最小化 - -Close -閉じる - -&Pause -一時停止(&P) - -Stop -停止 - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -無人で同期を行う為のバッチファイルを作成 - このファイルか、タスクプランナーからスケジュールをダブルクリックすることで開始: %x - -Stop synchronization at first error -最初のエラーで同期処理を停止 - -Show progress dialog -進捗ダイアログを表示 - -Save log: -ログ保存: - -Limit: -制限: - -Limit maximum number of log files -ログファイルの最大数を制限 - -How can I schedule a batch job? -一括ジョブ スケジュールの作成方法 - -&Recycle bin -ゴミ箱(&R) - -Delete on both sides -両方を削除 - -Delete on both sides even if the file is selected on one side only -片側のペインのみ選択されている場合でも両方を削除する - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -同期処理から特定のファイルを除外するフィルター規則を選択します。対応するそれらフォルダ ペアからの相対ファイルパスを入力. - -Include: -含める: - -Exclude: -除外: - -Time span: -タイムスパン: - -File size: -ファイルサイズ: - -Minimum: -最小: - -Maximum: -最大: - -&Clear -クリア(&C) - -The following settings are used for all synchronization jobs. -以下の設定は、すべての同期ジョブで使用されます - -Fail-safe file copy -安全なファイルコピーを実施 - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -対象を上書きする前に一時ファイル (*.ffs_tmp) にコピーします. -これにより、エラーが発生した場合も一貫性ある状態が保証されます. - - -(recommended) -(推奨) - -Copy locked files -ロックされたファイルをコピーする - -Copy shared or locked files using the Volume Shadow Copy Service. -共有、ロックされたファイルをボリュームシャドウコピーサービスを使用してコピー - -(requires administrator rights) -(管理者権限が必要) - -Copy file access permissions -ファイルのアクセスパーミッションをコピーする - -Transfer file and folder permissions. -ファイルとフォルダのパーミッションを転送します - -Automatic retry on error: -エラー時の自動再試行: - -Retry count: -再試行回数: - -Delay (in seconds): -遅延 (秒で指定): - -Customize context menu: -コンテキストメニューのカスタマイズ: - -Description -説明 - -Restore hidden windows -非表示ウィンドウを復帰 - -&Default -デフォルト(&D) - -Source code written in C++ using: -ソースコードは C++ で書かれています - -If you like FreeFileSync -FreeFileSync を気に入ってくれた方へ - -Donate with PayPal -PayPal から寄付 - -Feedback and suggestions are welcome -フィードバック、提案などはこちらから - -Homepage -ホームページ - -Email -E-メール - -Published under the GNU General Public License -GNU 一般共有使用許諾に基づき公開されています - -Many thanks for localization: -ローカライズのご協力に感謝します: - -Save as Batch Job -一括ジョブを保存 - -Delete Items -項目の削除 - -Global Settings -全般的な設定 - -Select Time Span -タイムスパンを選択 - -Folder Pairs -フォルダ ペア - -Find -検索 - -Overview -概要 - -Configuration -構成設定 - -Main Bar -メインバー - -Filter Files -ファイルフィルター - -Select View -表示を選択 - -Open... -開く... - -Save -保存 - -Compare both sides -両方を比較 - -Comparison settings -比較設定 - -Synchronization settings -同期処理設定 - -Start synchronization -同期の開始 - -Confirm -確認 - -&Execute -実行(&E) - - -1 directory -%x directories - - -%x ディレクトリ - - - -1 file -%x files - - -%x 個のファイル - - - -%y of 1 row in view -%y of %x rows in view - - -%y / %x 行(表示内) - - -Set direction: -方向の設定: - -multiple selection -複数選択 - -Include via filter: -フィルター経由で含める: - -Exclude via filter: -フィルターを経由して除外 - -Exclude temporarily -一時的に除外 - -Include temporarily -一時的に含める - -Delete -削除 - -Include all -すべて含める - -Exclude all -すべて除外 - -Show icons: -アイコン表示: - -Small - - -Medium - - -Large - - -Select time span... -タイムスパンを選択... - -Default view -デフォルト表示 - -Show "%x" -"%x" で表示 - -Last session -最後のセッション - -Folder Comparison and Synchronization -フォルダの比較と同期 - -Configuration saved -構成設定は保存されました - -FreeFileSync batch -FreeFileSync 一括 - -Do you want to save changes to %x? -本当に %x の変更を保存しますか? - -Do&n't save -保存しない(&N) - -Never save &changes -変更の保存をしない(&C) - -Filter -フィルター - -Show files that exist on left side only -左側のみに存在するファイルを表示 - -Show files that exist on right side only -右側のみに存在するファイルを表示 - -Show files that are newer on left -左側の新しいファイルを表示 - -Show files that are newer on right -右側の新しいファイルを表示 - -Show files that are equal -同じ内容のファイルを表示 - -Show files that are different -差異のあるファイルを表示 - -Show conflicts -不一致を表示 - -Show files that will be created on the left side -左側で作成されたファイルを表示 - -Show files that will be created on the right side -右側で作成されたファイルを表示 - -Show files that will be deleted on the left side -左側で削除されたファイルを表示 - -Show files that will be deleted on the right side -右側で削除されたファイルを表示 - -Show files that will be overwritten on left side -左側で上書きされたファイルを表示 - -Show files that will be overwritten on right side -右側で上書きされたファイルを表示 - -Show files that won't be copied -コピーされなかったファイルを表示 - -Set as default -デフォルトにセット - -All folders are in sync -すべてのフォルダを同期 - -Synchronization Settings -同期の設定 - -Comparison Settings -比較の設定 - -Cannot find %x -%x は見つかりません - -Comma-separated values -カンマ区切りの値 - -File list exported -ファイル一覧のエクスポートが完了 - -Searching for program updates... -アップデートを検索しています... - -&Ignore subsequent errors -以降のエラーは無視する(&I) - -&Ignore -無視(&I) - -Serious Error -重大なエラー - -&Don't show this warning again -次回からこの警告を表示しない(&D) - -&Switch -切り替え(&S) - -&Yes -はい(&Y) - -&No -いいえ(&N) - -Scanning... -スキャン中... - -Comparing content... -内容を比較中... - -Info -情報 - -Paused -一時停止中 - -Initializing... -初期化中... - -Stopped -停止 - -Completed -完了しました! - -&Continue -続行(&C) - -Log -ログ - -Today -今日 - -This week -この週 - -This month -この月 - -This year -この年 - -Last x days -最後の x 日 - -Byte -バイト - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -本当に以下 %x 個の項目をゴミ箱に移動しますか? - - -Move -移動 - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -本当に以下の %x 個の項目を削除しますか - - -Exclude -除外 - -Direct -ダイレクト - -Follow -考慮する - -Copy NTFS permissions -NTFS パーミッションをコピーする - -Integrate external applications into context menu. The following macros are available: -外部のアプリケーションをコンテキストメニューに統合、以下のマクロが利用できます: - -- full file or folder name -- ファイル、フォルダの完全な名前 - -- folder part only -- フォルダ部分のみ - -- Other side's counterpart to %item_path% -- %item_path% の反対側の対象 - -- Other side's counterpart to %item_folder% -- %item_folder% の反対側の対象 - -Restore all hidden windows and warnings? -すべての非表示ウィンドウと警告を元に戻しますか? - -Leave as unresolved conflict -未解決の競合はそのまま残す - -Replace -置換 - -Move files and replace if existing -ファイルを移動、存在する場合は上書き - -Time stamp -タイムスタンプ - -Append a timestamp to each file name -各ファイル名にタイムスタンプを追加 - -File -ファイル - -YYYY-MM-DD hhmmss -YYYY-MM-DD hhmmss - -Files -ファイル - -Items -項目 - -Percentage -パーセント - -Cannot monitor directory %x. -ディレクトリ %x を監視できません. - -Conversion error: -変換エラー: - -Cannot delete file %x. -ファイル %x を削除出来ません. - -The file is locked by another process: -次のファイルは別のプロセスで使用中です: - -Cannot move file %x to %y. -ファイル %x を %y に移動できません. - -Cannot delete directory %x. -ディレクトリ %x を削除できません. - -Cannot write file attributes of %x. -%x のファイル属性を書き込めません. - -Cannot write modification time of %x. -%x の更新時刻を書き込めませんでした. - -Cannot read security context of %x. -%x のセキュリティコンテキストを読み込めません. - -Cannot write security context of %x. ->%x のセキュリティコンテキストを書き込めません. - -Cannot read permissions of %x. ->%x のパーミッションを読み込めません. - -Cannot write permissions of %x. -%x のパーミッションを書き込めません. - -Cannot create directory %x. -ディレクトリ %x を作成出来ません. - -Cannot create symbolic link %x. -シンボリックリンク %x を作成出来ません - -Cannot find system function %x. -システム関数 %x がみつかりません. - -Cannot copy file %x to %y. -%x から %y にコピーできません. - -Type of item %x is not supported: -項目 %x には対応していません: - -Cannot resolve symbolic link %x. -シンボリックリンク %x を解決できません. - -Cannot open directory %x. -ディレクトリ %x を開けません. - -Cannot enumerate directory %x. -ディレクトリ %x を列挙できません - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -%x 分. - - - -1 hour -%x hours - - -%x 時間 - - - -1 day -%x days - - -%x 日 - - -Unable to register to receive system messages. -システム受信メッセージに登録できません - -Cannot set privilege %x. -%x の特権をセットできません. - -Unable to suspend system sleep mode. -システム スリープモードを中断できません - -Cannot change process I/O priorities. -プロセスの I/O 優先度を変更できません - -Unable to move %x to the recycle bin. -%x をゴミ箱に移動できません. - -Cannot determine final path for %x. -%x の最終的なパスを決定できません. - -Error Code %x: -エラーコード %x: - diff --git a/BUILD/Languages/korean.lng b/BUILD/Languages/korean.lng deleted file mode 100644 index 0caaf5a4..00000000 --- a/BUILD/Languages/korean.lng +++ /dev/null @@ -1,1501 +0,0 @@ -
    - 한국어 - Simon Park - ko_KR - flag_south_korea.png - 1 - 0 -
    - -&Check - - -Retrying operation... - - -Both sides have changed since last synchronization. -마지막 동기화 작업 이후, 양측 모두 변경 되었습니다. - -Cannot determine sync-direction: -동기화 방향을 결정할 수 없습니다: - -No change since last synchronization. -마지막 동기화 이후 변경사항 없음. - -The database entry is not in sync considering current settings. -데이터베이스 항목이 현재 설정을 고려하여 동기화 되지 않았습니다. - -Setting default synchronization directions: Old files will be overwritten with newer files. -기본값 동기화 방향 설정: 이전 파일들은 보다 최신 파일들로 덮어 쓰여집니다. - -Checking recycle bin availability for folder %x... -폴더 %x을(를) 위한 휴지통 가용성 여부 확인 중... - -Moving file %x to the recycle bin -파일 %x을(를) 휴지통으로 이동 중 - -Moving folder %x to the recycle bin -폴더 %x을(를) 휴지통으로 이동 중 - -Moving symbolic link %x to the recycle bin -심볼릭 링크 %x을(를) 휴지통으로 이동 중 - -Deleting file %x -파일 %x 삭제 중 - -Deleting folder %x -폴더 %x 삭제 중 - -Deleting symbolic link %x -심볼릭 링크 %x 삭제 중 - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -다음 폴더들을 위해 휴지통을 사용할 수 없습니다. 대신 파일들을 영구 삭제합니다: - -An exception occurred -예외 발생 - -A directory path is expected after %x. -%x 이후 일반적으로 디렉토리 경로가 하나 나옵니다. - -Syntax error -구문 오류 - -Cannot open file %x. -파일 %x을(를) 열 수 없습니다. - -File %x does not contain a valid configuration. -파일 %x 의 구성이 유효하지 않습니다. - -Unequal number of left and right directories specified. -지정된 좌측 및 우측 디렉터리 개수가 같지 않음. - -The config file must not contain settings at directory pair level when directories are set via command line. -명령줄을 통해 설정된 디렉토리의 경우, 설정 파일은 디렉토리 페어 상의 설정을 포함할 수 없습니다. - -Directories cannot be set for more than one configuration file. -디렉토리는 하나 이상의 설정 파일을 위해 설정될 수 없습니다. - -Command line -명령줄(커맨드라인) - -Syntax: -구문(Syntax): - -config files -설정 파일 - -directory -디렉토리 - -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -FreeFileSync .ffs_gui 또는 .ffs_batch 설정파일 개수 - -Any number of alternative directories for at most one config file. -최대 한개 설정파일에 대한 대체 디렉토리 개수 - -A folder input field is empty. -폴더 입력 필드 하나가 비어 있습니다. - -The corresponding folder will be considered as empty. -해당 폴더를 비어 있는 상태로 간주합니다. - -Cannot find the following folders: -다음 폴더를 찾을 수 없습니다: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -각 폴더를 비어있는 상태로 간주할 경우 이 오류는 무시될 수 있습니다. 폴더들은 동기화가 진행되는 동안 자동 생성됩니다. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -다음 폴더들에는 종속 경로가 있습니다. 동기화 규칙을 설정 시 주의하시기 바랍니다: - -File %x has an invalid date. -파일 %x 의 날짜가 유효하지 않습니다. - -Date: -날짜: - -Files %x have the same date but a different size. -파일 %x 의 날짜는 같으나, 크기가 다릅니다. - -Size: -크기: - -Items differ in attributes only -항목들이 속성에서만 차이가 있습니다. - -Resolving symbolic link %x -심볼릭 링크 %x 해결 - -Comparing content of files %x -파일 %x 내용 별 비교 중 - -Generating file list... -파일 리스트 생성 중... - -Starting comparison -비교 시작 - -Calculating sync directions... -동기화 방향을 계산 중... - -Out of memory. -메모리 부족. - -Item exists on left side only -항목이 좌측에만 존재합니다. - -Item exists on right side only -항목이 우측에만 존재합니다. - -Left side is newer -좌측이 최신입니다. - -Right side is newer -우측이 최신입니다. - -Items have different content -항목 내용이 서로 다릅니다. - -Both sides are equal -양측이 같음 - -Conflict/item cannot be categorized -충돌 / 항목을 분류할 수 없음 - -Copy new item to left -좌측에 새 항목 복사 - -Copy new item to right -우측에 새 항목 복사 - -Delete left item -좌측 항목 삭제 - -Delete right item -우측 항목 삭제 - -Move file on left -좌측 파일 이동 - -Move file on right -우측 파일 이동 - -Overwrite left item -좌측 항목 덮어쓰기 - -Overwrite right item -우측 항목 덮어쓰기 - -Do nothing -아무 것도 하지 않음 - -Update attributes on left -좌측 속성 업데이트 - -Update attributes on right -우측 속성 업데이트 - -Database file %x is incompatible. -데이터베이스 파일 %x 은(는) 호환 불가능합니다. - -Initial synchronization: -초기 동기화: - -Database file %x does not yet exist. -데이터베이스 파일 %x 은(는) 아직 존재하지 않습니다. - -Database file is corrupt: -데이터베이스 파일 손상: - -Cannot write file %x. -파일 %x을(를) 쓸 수 없습니다. - -Cannot read file %x. -파일 %x을(를) 읽을 수 없습니다. - -Database files do not share a common session. -데이터베이스 파일이 일반/공동 세션을 공유하지 않습니다. - -Searching for folder %x... -폴더 %x 검색 중... - -Cannot read file attributes of %x. -%x의 파일 속성을 읽을 수 없습니다. - -Cannot get process information. -프로세스 정보를 얻을 수 없습니다. - -Waiting while directory is locked (%x)... -디렉토리 잠금 대기 중 (%x)... - - -1 sec -%x sec - - -%x초 - - -Creating file %x -파일 %x 생성 중 - -Items processed: -처리된 항목: - -Items remaining: -남은 항목: - -Total time: -전체 시간: - - -1 byte -%x bytes - - -%x 바이트 - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -분석 오류 - 파일: %x; 행: %y; 열: %z - -Cannot set directory lock for %x. -%x 에 대한 디렉토리 잠금을 설정할 수 없습니다. - -Scanning: -스캔 중: - - -1 thread -%x threads - - -%x 스레드 - - -Encoding extended time information: %x -인코딩 확장 시간 정보: %x - -/sec -/초 - -%x items/sec -%x 항목 - -Configuration file %x loaded partially only. -구성 파일 %x이(가) 부분적으로만 로드 되었음. - -Show in Explorer -탐색기에 표시 - -Open with default application -기본값 응용 프로그램으로 열기 - -Browse directory -디렉토리 찾아보기 - -Cannot access the Volume Shadow Copy Service. -Volume Shadow Copy Service에 접근할 수 없습니다. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -본 운영체제에서의 Shadow Copy 생성은 FreeFileSync 64-비트 버전을 사용하세요. - -Cannot load file %x. -파일 %x을(를) 로드할 수 없습니다. - -Cannot determine volume name for %x. -%x 에 대한 볼륨 이름을 결정할 수 없습니다. - -Volume name %x is not part of file path %y. -볼륨 이름 %x은(는) 파일 경로 %y의 일부가 아닙니다. - -Stop requested: Waiting for current operation to finish... -사용자에 의한 작업 중단: 현재 작업 종료 대기 중... - -Unable to create timestamp for versioning: -버저닝을 위한 타임 스탬프 생성 불가: - -Cannot read the following XML elements: -다음 XML 요소를 읽을 수 없습니다: - -&Open... -열기(&O) - -Save &as... -다른 이름으로 저장(&a) - -&Quit -종료(&Q) - -&Program -프로그램(&P) - -&View help -도움말 보기(&V) - -&About -상세 정보(&A) - -&Help -도움말(&H) - -Usage: -사용: - -1. Select folders to watch. -1. 열어 볼 폴더를 선택하세요. - -2. Enter a command line. -2. 명령줄을 입력하세요. - -3. Press 'Start'. -3. '시작'을 누르세요. - -To get started just import a .ffs_batch file. -시작하려면 .ffs_batch file (일괄 파일)을 가져오십시오. - -Folders to watch: -열어 볼 폴더: - -Add folder -폴더 추가 - -Remove folder -폴더 제거 - -Browse -찾아보기 - -Select a folder -폴더 선택 - -Idle time (in seconds): -유휴 시간 (초 단위) - -Idle time between last detected change and execution of command -마지막으로 감지된 변화와 명령 실행 간의 유휴 시간 - -Command line: -명령줄(커맨드라인): - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -명령은 다음과 같은 경우에 실행됩니다: -- 파일이나 하위 폴더 변경 시 -- 새 폴더가 생겼을 시 (예: USB 스틱 삽입) - - -&Start -시작(&S) - -&Retry -다시 시도(&R) - -Cancel -취소 - -About -상세 정보 - -Build: %x -빌드: %x - -All files -모든 파일 - -Automated Synchronization -자동 동기화 - -Directory monitoring active -디렉토리 모니터링 활성화 - -Waiting until all directories are available... -모든 디렉토리가 사용 가능할 때까지 대기 중... - -Error -오류 - -&Restore -복원(&R) - -&Show error -오류 표시(&S) - -&Exit -나가기(&E) - -Incorrect command line: -부정확한 명령줄: - -File content -파일 내용 - -File time and size -파일 시간 및 크기 - -Two way -양방/양면 (Two Way) - -Mirror -미러 - -Update -업데이트 - -Custom -개인 설정 - -Multiple... -다중처리 (멀티플) 작업... - -Moving file %x to %y -파일 %x을(를) %y(으)로 이동 중 - -Moving folder %x to %y -폴더 %x을(를) %y(으)로 이동 중 - -Moving symbolic link %x to %y -심볼릭 링크 %x을(를) %y(으)로 이동 중 - -Removing old versions... -구 버전 삭제 중... - -Creating symbolic link %x -심볼릭 링크 %x 생성 중 - -Creating folder %x -폴더 %x 생성 중 - -Overwriting file %x -파일 %x 덮어쓰는 중 - -Overwriting symbolic link %x -심볼릭 링크 %x 덮어쓰는 중 - -Verifying file %x -파일 %x 확인 중 - -Updating attributes of %x -%x 속성 업데이트 중 - -Cannot find %x. -%x을(를) 찾을 수 없습니다. - -Target folder %x already existing. -대상 폴더 %x이(가) 이미 존재함. - -Target folder input field must not be empty. -대상 폴더 입력 필드가 비어 있어서는 안 됩니다. - -Please enter a target folder for versioning. -버저닝을 위한 대상 폴더를 입력하세요. - -Source folder %x not found. -소스 폴더 %x을(를) 찾을 수 없음. - -The following items have unresolved conflicts and will not be synchronized: -아래의 항목들은 해결치 못 한 충돌로 인해 동기화할 수 없습니다: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -다음 폴더들은 차이가 상당합니다. 동기화를 위해 올바른 폴더들이 매치되었는지 확인해 보십시오. - -Not enough free disk space available in: -사용 가능한 디스크 여유 공간이 부족합니다: - -Required: -필요 공간(크기): - -Available: -여유 공간(크기): - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -다중 폴더 페어의 일부인 폴더가 변경됩니다. 동기화 설정을 재검토하세요. - -Synchronizing folder pair: -폴더 페어(짝) 동기화 진행 중 - -Generating database... -데이터베이스 생성 중... - -Creating a Volume Shadow Copy for %x... -%x을(를) 위한 Volume Shadow Copy 생성 중... - -Data verification error: %x and %y have different content. -데이터 확인 오류: %x 및 %y 의 내용이 서로 다릅니다. - -job name -작업 이름 - -Synchronization stopped -동기화 중단 - -Synchronization completed with errors -동기화가 완료되긴 했으나, 오류가 있습니다 - -Synchronization completed with warnings -경고 메세지와 함께 동기화 완료 - -Nothing to synchronize -동기화 할 항목이 없습니다 - -Synchronization completed successfully -동기화가 성공적으로 완료 됐습니다 - -Saving log file %x... -로그 파일 %x 저장 중... - -You can switch to FreeFileSync's main window to resolve this issue. -이 문제는 FreeFileSync 기본 창으로 전환해서 해결 가능합니다. - -&Don't show this warning again -이 경고를 다시 표시하지 않음(&D) - -&Ignore -무시(&I) - -&Switch -전환(&S) - -Switching to FreeFileSync's main window -FreeFileSync 기본 창으로 전환 중 - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - -%x초 이내에 자동 재시도... - - -Serious Error -심각한 오류 - -&Ignore subsequent errors -이후 오류 무시(&I) - -Check for Program Updates -프로그램 업데이트 확인 - -A new version of FreeFileSync is available: -새로운 버전의 FreeFileSync가 나왔습니다: - -Download now? -지금 다운로드 하시겠습니까? - -&Download -다운로드(&D) - -FreeFileSync is up to date. -FreeFileSync는 현재 최신버전 상태입니다. - -Unable to connect to sourceforge.net. -Sourceforge.net에 접속할 수 없습니다. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -현재 사용 중인 FreeFileSync 버전 번호를 온라인에서 찾을 수 없습니다. 수동으로 확인해 보시겠습니까? - -Symlink -심링크 - -Folder -폴더 - -Full path -전체 경로 - -Name -이름 - -Relative path -대상 경로 - -Base folder -기본 폴더 - -Size -크기 - -Date -날짜 - -Extension -확장자 - -Category -카테고리 - -Action -실행 - -Drag && drop -드래그 앤 드랍(&&) [마우스로 파일 끌어다 놓기] - -Close progress dialog -진행 표시 창 닫기 - -Standby -대기 - -Log off -로그오프 - -Shut down -종료 - -Hibernate -최대절전모드 - -Alternate comparison settings -비교 설정 변경 - -Alternate synchronization settings -동기화 설정 변경 - -Local filter -로컬 필터 - -Active -활성화 - -None -아무것도 안 함 - -Remove alternate settings -대체설정 제거 - -Clear filter settings -필터 설정 지우기 - -Copy -복사 - -Paste -붙여넣기 - -Alternate Comparison Settings -비교 설정 변경 - -Alternate Synchronization Settings -동기화 설정 변경 - -Local Filter -로컬 필터 - -&New -신규 작업(&N) - -&Save -저장(&S) - -Save as &batch job... -일괄 작업으로 저장(&b) - -1. &Compare -1. 비교(&C) - -2. &Synchronize -2. 동기화(&S) - -&Global settings -전체 설정(&G) - -&Language -언어 선택(&L) - -&Find... -찾기(&F) - -&Export file list... -파일 리스트 내보내기(&E) - -&Tools -도구(&T) - -&Check now -지금 확인(&C) - -Check &automatically once a week -자동으로 주당 1회 확인(&a) - -&Check for new version -신규 버전 확인(&C) - -Compare -비 교 - -Synchronize -동 기 화 - -Add folder pair -폴더 페어(짝) 추가 - -Remove folder pair -폴더 페어(짝) 제거 - -Swap sides -양측 위치 바꾸기 - -Close search bar -검색 창 닫기 - -Find: -찾기: - -Match case -대문자/소문자 구분 - -Save as batch job -일괄 작업으로 저장 - -Hide excluded items -제외 항목 숨기기 - -Show filtered or temporarily excluded files -필터링 또는 일시적으로 제외된 파일 보이기 - -Number of files and folders that will be created -생성될 파일 및 폴더 개수 - -Number of files that will be overwritten -덮어 씌어질 파일 개수 - -Number of files and folders that will be deleted -삭제될 파일 및 폴더 개수 - -Total bytes to copy -복사할 전체 바이트 크기 - -Select a variant: -베어리언트 선택: - -Identify equal files by comparing modification time and size. -수정 시간 및 크기를 비교하여 동일한 파일을 식별 - -Identify equal files by comparing the file content. -파일 내용을 비교하여 동일한 파일을 식별 - -Symbolic links: -심볼링 링크: - -More information -상세 정보 - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -양측 변경사항을 확인하고 전달. 삭제, 이동 및 충돌은 데이터베이스를 통해 자동으로 감지됩니다. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -좌측 폴더 백업 미러 생성: 동기화 이후 좌측 및 우측 폴더는 완전히 똑같이 매치 됩니다. - -Copy new and updated files to the right folder. -신규 또는 업데이트 된 파일을 우측 폴더로 복사 - -Configure your own synchronization rules. -개인 동기화 규칙 설정 - -Detect moved files -이동 파일 탐지 - -Requires database files. Not supported by all file systems. -데이터베이스 파일이 필요합니다. 모든 파일 시스템에서 지원되지는 않습니다. - -Delete files: -파일 삭제: - -Permanent -영구 - -Delete or overwrite files permanently -파일 영구 삭제 또는 덮어쓰기 - -&Recycle bin -휴지통(&R) - -Back up deleted and overwritten files in the recycle bin -휴지통에 삭제되고 덮어 씌어진 파일 백업 - -Versioning -버저닝 - -Move files to a user-defined folder -사용자 정의 폴더로 파일 이동 - -Naming convention: -이름 지정: - -Show examples -예 보이기 - -Handle errors: -오류 발생시: - -Ignore -무시 - -Hide all error and warning messages -모든 오류/경고 메세지 숨기기 - -Pop-up -팝업 - -Show pop-up on errors or warnings -오류 또는 경고에 대한 팝업 보이기 - -On completion: -완료 시: - -Start synchronization now? -지금 동기화를 시작하시겠습니까? - -Variant: -베어리언트: - -Statistics -통 계 - -&Don't show this dialog again -이 대화 창을 다시 표시 안 함(&D) - -Items found: -발견된 항목: - -Speed: -속도: - -Time remaining: -남은 시간: - -Time elapsed: -경과 시간: - -Synchronizing... -동기화 작업 중... - -Minimize to notification area -알림 영역으로 최소화 - -Close -닫기 - -&Pause -일시정지(&P) - -Stop -중지 - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -직접 지켜보지 않는 자동 동기화의 경우, 배치 파일을 만듭니다. 시작하려면 파일을 더블 클릭하거나 작업 플래너에서 일정을 만듭니다: %x - -Stop synchronization at first error -첫 오류 발생 시 동기화 중지 - -Show progress dialog -진행 표시 창 보기 - -Save log: -로그 저장: - -Limit: -제한: - -Limit maximum number of log files -로그 파일의 최대 개수 제한 - -How can I schedule a batch job? -일괄 작업 예약 방법은? - -Delete on both sides -양측 모두 삭제 - -Delete on both sides even if the file is selected on one side only -어느 한쪽의 파일만 선택하더라도 양측 모두 삭제 - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -동기화에서 특정 파일을 제외시키는 필터 규칙을 선택합니다. 해당 폴더 페어(짝)에 대한 상대 파일 경로를 입력하세요. - -Include: -포함: - -Exclude: -제외: - -Time span: -시간 간격: - -File size: -파일 크기: - -Minimum: -최소: - -Maximum: -최대: - -&Clear -지우기(&C) - -The following settings are used for all synchronization jobs. -다음 설정은 모든 동기화 작업에 사용됩니다. - -Fail-safe file copy -실패 - 안전 파일 복사 - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -대상을 덮어쓰기 전에 임시파일(*.ffs_tmp)로 복사. -이는 심각한 오류 발생시에도 일관된 상태 유지를 보장합니다. - - -(recommended) -(권장) - -Copy locked files -락 걸린 파일 복사 - -Copy shared or locked files using the Volume Shadow Copy Service. -Volume Shadow Copy Service를 사용하여 공유 또는 잠긴 파일 복사 - -(requires administrator rights) -(관리자 권한 필요) - -Copy file access permissions -파일 접근 권한 복사 - -Transfer file and folder permissions. -파일 및 폴더 권한 전송 - -Automatic retry on error: -오류 발생 시 자동 재시도: - -Retry count: -재시도 횟수: - -Delay (in seconds): -지연 (초 단위): - -Customize context menu: -컨텍스트 메뉴 커스터마이즈 (사용자 정의): - -Description -설명 - -Restore hidden windows -숨겨진 창 복원 - -&Default -기본 설정/값(&D) - -Source code written in C++ using: -소스코드는 C++ 언어로 아래 툴을 사용하여 작성되었습니다: - -If you like FreeFileSync -FreeFileSync를 위한 기부 - -Donate with PayPal -PayPal로 기부하기 - -Feedback and suggestions are welcome -모든 의견 및 건의/제안을 환영합니다 - -Homepage -홈페이지 - -Email -이메일 - -Published under the GNU General Public License -GNU 일반 공용 라이센스에 의한 출시 - -Many thanks for localization: -현지화 작업에 깊은 감사 드립니다: - -Save as Batch Job -일괄 작업으로 저장 - -Delete Items -항목 삭제 - -Global Settings -전체 설정 - -Select Time Span -시간간격(타임스팬) 선택 - -Folder Pairs -폴더 페어(짝) - -Find -검색 - -Overview -개요 - -Configuration -구성 설정 - -Main Bar -메인 바 - -Filter Files -파일 필터 - -Select View -보기 선택 - -Open... -열기 - -Save -저장 - -Compare both sides -양측 비교 - -Comparison settings -비교 설정 - -Synchronization settings -동기화 설정 - -Start synchronization -동기화 시작 - -Confirm -확인 - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - -정말로 %x 항목을 위해 %y 명령을 실행하기를 원하십니까? - - -&Execute -실행(&E) - - -1 directory -%x directories - - -%x개 디렉토리 - - - -1 file -%x files - - -%x개 파일 - - - -%y of 1 row in view -%y of %x rows in view - - -보기에 나타난 %x 행의 %y - - -Set direction: -방향 설정: - -multiple selection -복수 선택 - -Include via filter: -필터를 통해 다음을 포함: - -Exclude via filter: -필터를 통해 다음을 제외: - -Exclude temporarily -임시 제외 - -Include temporarily -임시 포함 - -Delete -삭제 - -Include all -모두 포함 - -Exclude all -모두 제외 - -Show icons: -아이콘 표시: - -Small -작게 - -Medium -중간 - -Large -크게 - -Select time span... -시간간격(타임스팬) 선택... - -Default view -기본 보기 - -Show "%x" -"%x" 표시 - -Last session -마지막 세션 - -Folder Comparison and Synchronization -폴더 비교 및 동기화 - -Configuration saved -설정 저장 완료 - -FreeFileSync batch -FreeFileSync 일괄처리(배치) - -Do you want to save changes to %x? -%x의 변경사항을 저장하시겠습니까? - -Never save &changes -변경사항을 절대 저장하기 않기(&c) - -Do&n't save -저장하기 않기(&n) - -Filter -필터 - -Show files that exist on left side only -좌측에만 존재하는 파일 표시 - -Show files that exist on right side only -우측에만 존재하는 파일 표시 - -Show files that are newer on left -좌측이 보다 최신인 파일 표시 - -Show files that are newer on right -우측이 보다 최신인 파일 표시 - -Show files that are equal -내용이 같은 파일 표시 - -Show files that are different -내용이 다른 파일 표시 - -Show conflicts -충돌 표시 - -Show files that will be created on the left side -좌측에 생성될 파일 표시 - -Show files that will be created on the right side -우측에 생성될 파일 표시 - -Show files that will be deleted on the left side -좌측에서 삭제될 파일 표시 - -Show files that will be deleted on the right side -우측에서 삭제될 파일 표시 - -Show files that will be overwritten on left side -좌측에 덮어쓰여질 파일 표시 - -Show files that will be overwritten on right side -우측에 덮어쓰여질 파일 표시 - -Show files that won't be copied -복사되지 않을 파일 표시 - -Set as default -기본 값으로 설정 - -All folders are in sync -모든 폴더가 동기화 되었음 - -Synchronization Settings -동기화 설정 - -Comparison Settings -비교 설정 - -Cannot find %x -%x을(를) 찾을 수 없습니다. - -Comma-separated values -쉼표로 구분된 값 - -File list exported -파일 리스트 내보내기 완료 - -Searching for program updates... -프로그램 업데이트 검색 중... - -Scanning... -스캔 중... - -Comparing content... -내용 비교 중... - -Info -정보 - -Warning -경고 - -Paused -일시정지 중 - -Initializing... -초기화 작업 중... - -Stopped -중단 - -Completed -완료 - -&Continue -계속(&C) - -Log -로그 - -Today -오늘 - -This week -이번 주 - -This month -이번 달 - -This year -올해 - -Last x days -최근 x일 - -Byte -바이트 - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -다음 %x 항목을 정말로 휴지통으로 이동하기를 원하십니까? - - -Move -이동 - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -정말로 다음 %x 항목(들)을 삭제하시겠습니까? - - -Exclude -제외 - -Direct -다이렉트 - -Follow -팔로우 - -Copy NTFS permissions -NTFS 권한 복사 - -Integrate external applications into context menu. The following macros are available: -외부 응용 프로그램을 Context Menu에 통합. 다음 매크로가 사용 가능합니다: - -- full file or folder name -- 전체 파일 또는 폴더 이름 - -- folder part only -- 폴더 부분만 - -- Other side's counterpart to %item_path% -% 항목_경로 %의 반대편 대응부(카운터파트) - -- Other side's counterpart to %item_folder% -% 항목_폴더 %의 반대편 대응부(카운터파트) - -Restore all hidden windows and warnings? -모든 숨겨진 창 및 경고를 복원하시겠습니까? - -Leave as unresolved conflict -미해결 충돌로 놔두기 - -Replace -대체 - -Move files and replace if existing -파일 이동 및 기존 파일 존재 시에는 대체 - -Time stamp -타임 스탬프 - -Append a timestamp to each file name -각 파일 이름마다 타임스탬프 추가 - -File -파일 - -YYYY-MM-DD hhmmss -YYYY-MM-DD hhmmss - -Files -파일 - -Items -항목 - -Percentage -백분율(%) - -Cannot monitor directory %x. -디렉토리 %x을(를) 모니터링 할 수 없습니다. - -Conversion error: -변환 오류: - -Cannot delete file %x. -파일 %x을(를) 삭제할 수 없습니다. - -The file is locked by another process: -파일이 다른 프로세스에 의해 잠겨 있습니다: - -Cannot move file %x to %y. -파일 %x을(를) %y(으)로 이동할 수 없습니다. - -Cannot delete directory %x. -디렉토리 %x을(를) 삭제할 수 없습니다. - -Cannot write file attributes of %x. -%x의 파일 속성을 쓸 수 없습니다. - -Cannot write modification time of %x. -%x의 수정 시간을 쓸 수 없습니다. - -Cannot read security context of %x. -%x의 보안 컨텍스트를 읽을 수 없습니다. - -Cannot write security context of %x. -%x의 보안 컨텍스트를 쓸 수 없습니다. - -Cannot read permissions of %x. -%x의 권한을 읽을 수 없습니다. - -Cannot write permissions of %x. -%x의 권한을 쓸 수 없습니다. - -Cannot create directory %x. -디렉토리 %x을(를) 생성할 수 없습니다. - -Cannot create symbolic link %x. -심볼릭 링크 %x을(를) 생성할 수 없습니다. - -Cannot find system function %x. -시스템 함수 %x을(를) 찾을 수 없습니다. - -Cannot copy file %x to %y. -파일 %x을(를) %y(으)로 복사할 수 없습니다. - -Type of item %x is not supported: -항목 %x의 형식은 지원되지 않습니다: - -Cannot resolve symbolic link %x. -심볼릭 링크 %x을(를) 해결할 수 없습니다. - -Cannot open directory %x. -디렉토리 %x을(를) 열 수 없습니다. - -Cannot enumerate directory %x. -디렉토리 %x을(를) 열거할 수 없습니다. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -%x분 - - - -1 hour -%x hours - - -%x시간 - - - -1 day -%x days - - -%x일 - - -Unable to register to receive system messages. -시스템 메시지 수신을 위한 등록을 할 수 없습니다. - -Cannot set privilege %x. -권한 %x을(를) 설정할 수 없습니다. - -Unable to suspend system sleep mode. -시스템 절전모드 중지를 할 수 없습니다. - -Cannot change process I/O priorities. -프로세스 I/O 우선순위 변경을 할 수 없습니다. - -Unable to move %x to the recycle bin. -휴지통으로 %x을(를) 이동할 수 없습니다. - -Cannot determine final path for %x. -%x 에 대한 최종 경로를 결정할 수 없습니다. - -Error Code %x: -오류 코드 %x - diff --git a/BUILD/Languages/outdated/lithuanian.lng b/BUILD/Languages/outdated/lithuanian.lng deleted file mode 100644 index d503fa7e..00000000 --- a/BUILD/Languages/outdated/lithuanian.lng +++ /dev/null @@ -1,1526 +0,0 @@ -
    - Lietuvių - Tadas Norbutas - lt_LT - flag_lithuania.png - 4 - n==1 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : n%10==0 || (n%100>10 && n%100<20) ? 2 : 3 -
    - -Unable to suspend system sleep mode. - - -Unable to register to receive system messages. - - -Restore all hidden windows and warnings? - - -Move - - -&Continue - - -Stopped - - -&Don't show this warning again - - -Serious Error - - -&Ignore subsequent errors - - -Comparison Settings - - -Synchronization Settings - - -Never save &changes - - - -%y of 1 row in view -%y of %x rows in view - - - - -&Execute - - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -Select View - - -Filter Files - - -Main Bar - - -Folder Pairs - - -Select Time Span - - -Global Settings - - -Delete Items - - -Save as Batch Job - - -Restore hidden windows - - -Customize context menu: - - -Delay (in seconds): - - -Retry count: - - -Automatic retry on error: - - -Transfer file and folder permissions. - - -(requires administrator rights) - - -Copy shared or locked files using the Volume Shadow Copy Service. - - -(recommended) - - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - - -The following settings are used for all synchronization jobs. - - -Maximum: - - -Minimum: - - -File size: - - -Time span: - - -Exclude: - - -Include: - - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. - - -&Recycle bin - - -How can I schedule a batch job? - - -Limit: - - -Save log: - - -Stop synchronization at first error - - -Stop - - -&Don't show this dialog again - - -Variant: - - -Start synchronization now? - - -On completion: - - -Handle errors: - - -Show examples - - -Delete files: - - -Copy new and updated files to the right folder. - - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. - - -More information - - -Symbolic links: - - -Identify equal files by comparing the file content. - - -Identify equal files by comparing modification time and size. - - -Select a variant: - - -Find: - - -Close search bar - - -&Check for new version - - -&Tools - - -&Find... - - -Local Filter - - -Alternate Synchronization Settings - - -Alternate Comparison Settings - - -None - - -Active - - -Local filter - - -Alternate synchronization settings - - -Alternate comparison settings - - -&Download - - -Check for Program Updates - - -Retrying operation - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Switching to FreeFileSync's main window - - -You can switch to FreeFileSync's main window to resolve this issue. - - -Synchronization stopped - - -Please enter a target folder for versioning. - - -Incorrect command line: - - -&Show error - - -Automated Synchronization - - -&Start - - -Command line: - - -Idle time (in seconds): - - -Folders to watch: - - -&View help - - -Unable to create timestamp for versioning: - - -Stop requested: Waiting for current operation to finish... - - -%x items/sec - - - -1 thread -%x threads - - - - - -1 byte -%x bytes - - - - -Both sides have changed since last synchronization. -Abi pusės buvo pakeistos nuo paskutinio sinchronizavimo. - -Cannot determine sync-direction: -Nepavyksta nustatyti sinchronizavimo krypties: - -No change since last synchronization. -Nėra pakitimo nuo pakutinio sinchronizavimo. - -The database entry is not in sync considering current settings. -Duomenų bazės įrašas nesinchronizuotas remiantis šiais nustatymais. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Nustatomos numatytos sinchronizavimo kryptys: Seni failai bus perrašyti naujesniais failais. - -Checking recycle bin availability for folder %x... -Tikrinamas šiukšliadėžės prieinamumas aplankui %x... - -Moving file %x to the recycle bin -%x failas perkeliamas į šiukšliadėžę - -Moving folder %x to the recycle bin -%x katalogas perkeliamas į šiukšliadėžę - -Moving symbolic link %x to the recycle bin -Simbolinė nuoroda %x perkeliama į šiukšliadėžę - -Deleting file %x -Trinamas failas %x - -Deleting folder %x -Trinamas aplankas %x - -Deleting symbolic link %x -Tinama simbolinė nuoroda %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -Šiuklšlių dėžė šiems katalogams yra nepasiekiama. Failai bus negrįžtamai ištrinti: - -An exception occurred -Atsirado išimtis - -A directory path is expected after %x. -Reikalingas katalogo kelias po %x - -Syntax error -Sintaksės klaida - -Cannot open file %x. -%x failo nepavyko atidaryti. - -Error -Klaida - -File %x does not contain a valid configuration. -Failas %x neturi tinkamų nustatymų. - -Unequal number of left and right directories specified. -Nustatytas nevienodas skaičius katalogų kairėje ir dešinėje. - -The config file must not contain settings at directory pair level when directories are set via command line. -Config failas negali turėti katalogų poros lygio nustatymų kai katalogai nustatomi per komandinę eilutę. - -Warning -Perspėjimas - -Directories cannot be set for more than one configuration file. -Katalogai negali būti nustatyti daugiau nei viename konfigūracijos faile. - -Syntax: -Sintaksė: - -config files -config failai - -directory -katalogas - -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Keletas FreeFileSync .ffs_gui ir/arba .ffs_batch configūracijos failų - -Any number of alternative directories for at most one config file. -Daugiausia vienam config failui, n alternatyvių katalogų - -Command line -Komandinė eilutė - -A folder input field is empty. -Aplanko įvesties laukas yra tuščias. - -The corresponding folder will be considered as empty. -Atitinkamas aplankas bus laikomas tuščiu. - -Cannot find the following folders: -Nepavyksta rasti šių aplankų: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -Jei žinote, kad kiekvienas katalogas yra tuščias, šią klaidą galite ignoruoti. Katalogai bus automatiškai sukurti sinchronizacijos metu. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -Šie katalogai turi tarpusavyje susijusių kelių. Būkite atsargūs nustatydami sinchronizacijos taisykles: - -File %x has an invalid date. -Failas %x turi netinkamą datą. - -Date: -Data: - -Files %x have the same date but a different size. -Failai %x turi tokią pačią datą bet skirtingą dydį. - -Size: -Dydis: - -Items differ in attributes only -Elementai skiriasi tik atributais - -Resolving symbolic link %x -Ieškoma simbolinės nuorodos %x - -Comparing content of files %x -Sulyginamas failų turinys %x - -Generating file list... -Generuojamas failų sąrašas... - -Starting comparison -Pradedamas palyginimas - -Calculating sync directions... -Apskaičiuojamos sinchrinizacijos kryptys... - -Out of memory. -Trūksta atminties. - -Item exists on left side only -Elementas egzistuoja tik kairėje pusėje - -Item exists on right side only -Elementas egzistuoja tik dešinėje pusėje - -Left side is newer -Kairė pusė yra naujesnė - -Right side is newer -Dešinė pusė yra naujesnė - -Items have different content -Elementai turi skirtingą turinį - -Both sides are equal -Abi pusės yra lygios - -Conflict/item cannot be categorized -Konfliktas/elementas negali būti kategorizuojamas - -Copy new item to left -Kopijuoti naują elementą į kairę - -Copy new item to right -Kopijuoti naują elementą į dešinę - -Delete left item -Ištrinti kairįjį elementą - -Delete right item -Ištrinti dešinįjį elementą - -Move file on left -Perkelti failą į kairę - -Move file on right -Perkelti failą į dešinę - -Overwrite left item -Perrašyti kairįjį elementą - -Overwrite right item -Perrašyti dešinįjį elementą - -Do nothing -Nieko nedaryti - -Update attributes on left -Atnaujinti atributus kairėje - -Update attributes on right -Atnaujinti atributus dešinėje - -Database file %x is incompatible. -Duomenų bazė %x yra netinkama. - -Initial synchronization: -Pirminis sinchronizavimas: - -Database file %x does not yet exist. -Duomenų bazės failo %x dar nėra. - -Database file is corrupt: -Duomenų bazės failas sugadintas - -Cannot write file %x. -Nepavyksta įrašyti failo %x. - -Cannot read file %x. -Nepavyksta nuskaityti failo %x. - -Database files do not share a common session. -Duomenų bazės failai nesidalina bendros sesijos. - -Searching for folder %x... -Ieškoma aplanko %x... - -Cannot read file attributes of %x. -Nepavyko perskaityti failo %x atributų - -Cannot get process information. -Nepavyksta gauti eigos informacijos. - -Waiting while directory is locked (%x)... -Laikiama kol katalogas bus užrakintas (%x)... - - -1 sec -%x sec - - -1 sek -%x sek -%x sek -%x sek - - -Creating file %x -Kuriamas failas %x - -Items processed: -Elementų apdorota: - -Items remaining: -Likę elementai: - -Total time: -Visas laikas: - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Klaida trinant failą %x, eilė %y, stulpelis %z. - -Cannot set directory lock for %x. -Nepavyksta nustatyti katalogo užrakto %x. - -Scanning: -Skenuojama: - -Encoding extended time information: %x -Koduojama išplėstinė laiko informacija: %x - -/sec -/sek. - -Configuration file %x loaded partially only. -Nustatymų failas %x įkeltas tik dalinai. - -Show in Explorer -Rodyti naršyklėje - -Open with default application -Atverti su numatyta programa - -Browse directory -Naršyti katalogą - -Cannot access the Volume Shadow Copy Service. -Volume Shadow Copy paslauga nepasiekiama. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Prašome naudoti 64-bit FreeFileSync versiją, kad sukurti šėšėlines kopijas šioje sistemoje. - -Cannot load file %x. -Nepavyksta įkelti failo %x. - -Cannot determine volume name for %x. -Vietos vardo %x nustatyti nepavyko - -Volume name %x is not part of file path %y. -Vietos vardas %x nėra failo kelio %y dalis. - -Cannot read the following XML elements: -Nepavyksta perskaityti sekančių XML elementų: - -&Open... -&Atverti... - -Save &as... -Išsaugoti &kaip... - -&Quit -&Išeiti - -&Program -&Programa - -&About -&Apie - -&Help -&Pagalba - -Usage: -Naudojimas: - -1. Select folders to watch. -1. Pasirinkite stebimus aplankus. - -2. Enter a command line. -2. Įvesti komandinę eilutę. - -3. Press 'Start'. -3. Spauskite „Pradėti“'. - -To get started just import a .ffs_batch file. -Kad pradėti tiesiog importuokite .ffs_batch failą. - -Add folder -Pridėti aplanką - -Remove folder -Pašalinti aplanką - -Browse -Naršyti - -Select a folder -Pažymėti aplanką - -Idle time between last detected change and execution of command -Neveiklus laikas tarp paskutinio aptikto pokyčio ir komandos įvykdymo - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -Komanda inicijuojama jei: -- failai arba poaplankiai keičiasi -- atsiranda nauji aplankai (pvz.: įkišamas USB raktas) - - -&Retry -&Bandyti vėl - -Cancel -Atšaukti - -Build: %x -Versija: %x - -About -Apie - -All files -Visi failai - -Directory monitoring active -Katalogų stebėjimas yra aktyvus - -Waiting until all directories are available... -Laukiama kol katalogai bus prieinami... - -&Restore -&Atstatyti - -&Exit -&Išeiti - -File content -Failo turinį - -File time and size -Failo laiką ir dydį - -Two way -Dvipusis - -Mirror -Veidrodis - -Update -Atnaujinti - -Custom -Savitas - -Multiple... -Keletas... - -Moving file %x to %y -Perkeliamas failas %x į %y - -Moving folder %x to %y -Perkeliamas aplankas %x į %y - -Moving symbolic link %x to %y -Perkeliama simbolinė nuoroda %x į %y - -Removing old versions... -Šalinamos senos versijos... - -Creating symbolic link %x -Kuriama simbolinė nuoroda %x - -Creating folder %x -Kuriamas aplankas %x - -Overwriting file %x -Perrašomas failas %x - -Overwriting symbolic link %x -Perrašoma simbolinė nuoroda %x - -Verifying file %x -Tikrinamas failas %x - -Updating attributes of %x -Atnaujinami atributai %x - -Cannot find %x. -Nepavyksta rasti %x. - -Target folder %x already existing. -Tikslo aplankas %x jau yra. - -Target folder input field must not be empty. -Tikslo aplanko įvesties laukas negali būti tuščias. - -Source folder %x not found. -Šaltinio aplankas %x nerastas. - -The following items have unresolved conflicts and will not be synchronized: -Šie elementai turi neišspręstų konfliktų ir nebus sinchronizuoti: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -Šie katalogai yra labai skirtingi. Įsitikinkite, kad sinchronizacijai pasirinkote teisingus katalogus. - -Not enough free disk space available in: -Nepakanka laisvos disko vietos: - -Required: -Reikia: - -Available: -Pasiekiama: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Aplankas, kuris yra dalis keletos aplankų porų, bus pakeistas. Prašome peržiūrėti sinchronizavimo nustatymus. - -Synchronizing folder pair: -Sinchrinizuojama aplankų pora: - -Generating database... -Generuojama duomenų bazė... - -Creating a Volume Shadow Copy for %x... -%x kuriamas Volume Shadow Copy... - -Data verification error: %x and %y have different content. -Duomenų patikros klaida: %x ir %y turinys yra skirtingas. - -job name -Užduoties pavadinimas - -Synchronization completed with errors -Synchronizavimas baigtas su klaidomis - -Synchronization completed with warnings -Synchronizavimas baigtas su perspėjimais - -Nothing to synchronize -Nėra ko sinchronizuoti - -Synchronization completed successfully -Synchronizavimas sėkmingai baigtas - -Saving log file %x... -Saugmas žurnalo failas %x... - -A new version of FreeFileSync is available: -Yra nauja FreeFileSync versija: - -Download now? -Atsiųsti dabar? - -FreeFileSync is up to date. -FreeFileSync yra naujausia. - -Information -Informacija - -Unable to connect to sourceforge.net. -Nepavyksta prisijungti prie sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Dabartinės FreeFileSync versijos numeris internete nerastas. Ar norėtumėte patikrinti rankiniu būdu? - -Symlink -Simbolinė nuoroda - -Folder -Katalogas - -Full path -Pilnas kelias - -Name -Pavadinimas - -Relative path -Elemento kelias - -Base folder -Bazinis katalogas - -Size -Dydis - -Date -Data - -Extension -Plėtinys - -Category -Kategorija - -Action -Veiksmas - -Drag && drop -Vilkti ir numesti - -Close progress dialog -Uždaryti eigos langą - -Standby -Stabdyti į diską - -Log off -Atsijungti - -Shut down -Išjungti kompiuterį - -Hibernate -Stabdyti į operatyviąją atmintį - -Remove alternate settings -Pašalinti alternatyvius nustatymus - -Clear filter settings -Išvalyti filtro nustatymus - -Copy -Kopijuoti - -Paste -Įklijuoti - -&New -&Naujas - -&Save -&Išsaugoti - -Save as &batch job... -Išsaugoti kaip &paketinę užduotį... - -1. &Compare -1. &Sulyginti - -2. &Synchronize -2. &Sinchronizuoti - -&Global settings -&Bendri nustatymai - -&Language -&Kalba - -&Export file list... -&Eksportuoti failų sąrašą... - -&Check now -&Tikrinti dabar - -Check &automatically once a week -Tikrinti &automatiškai kartą per savaitę - -Compare -Sulyginti - -Synchronize -Sinchronizuoti - -Add folder pair -Pridėti aplankų porą - -Remove folder pair -Pašalinti aplankų porą - -Swap sides -Sukeisti puses - -Match case -Atitikti atveją - -Save as batch job -Išsaugoti kaip paketinį darbą - -Hide excluded items -Slėpti išskirtus elementus - -Show filtered or temporarily excluded files -Rodyti išfiltruotus ar laikinai išskirtus failus - -Number of files and folders that will be created -Failų ir aplankų, kurie bus sukurti, skaičius - -Number of files that will be overwritten -Skaičius failų, kurie bus perrašyti - -Number of files and folders that will be deleted -Failų ir aplankų, kurie bus ištrinti, skaičius - -Total bytes to copy -Viso baitų kopijuoti - -OK -Gerai - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Nustatyti ir skatinti pokyčius apbiejose pusėse. Trinimai, perkėlimai ir konfliktai yra aptinkami automatiškai naudojant duomenų bazę. - -Configure your own synchronization rules. -Nustatyti Jūsų pačių sinchronizavimo taisykles. - -Detect moved files -Rasti perkeltus failus - -Requires database files. Not supported by all file systems. -Reikalauja duomenų bazės failų. Visų failų sistemų nėra palaikoma. - -Permanent -Visiškai - -Delete or overwrite files permanently -Trinti ar perrašyti failus visam laikui - -Recycle bin -Šiukšlių dėžė - -Back up deleted and overwritten files in the recycle bin -Padaryti šiukšlių dėžėje esančių ištrintų ar perrašytų failų atsarginę kopiją - -Versioning -Versijavimas - -Move files to a user-defined folder -Perkelti failus į vartotojo nustatytą katalogą - -Naming convention: -Pavadinimų taisyklės: - -Ignore -Ignoruoti - -Hide all error and warning messages -Slėpti visus klaidų ir perspėjimų pranešimus - -Pop-up -Iššokti - -Show pop-up on errors or warnings -Rodyti pranešimą esant klaidoms ar perspėjimams - -Statistics -Statistika - -Items found: -Rasta elementų: - -Speed: -Greitis: - -Time remaining: -Likęs laikas: - -Time elapsed: -Praėjęs laikas: - -Synchronizing... -Sinchronizuojama... - -Minimize to notification area -Nuleisti į apačią dešinėje - -Close -Uždaryti - -&Pause -&Pauzė - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Sukurti paleidimo failą sinchronizacijai be priežiūros. Norint jį paleisti reikia paspausti and failo du kartus pele arba nustatyti su užduočių planuotoju: %x - -Show progress dialog -Rodyti eigos langą - -Limit maximum number of log files -Apriboti ataskaitų failų skaičių - -Delete on both sides -Ištrinti abiejose pusėse - -Delete on both sides even if the file is selected on one side only -Ištrinti abiejose pusėse net jei failas yra pažymėtas tik vienoje pusėje - -&Clear -&Išvalyti - -Fail-safe file copy -Apsauginė failo kopija - -Copy locked files -Kopijuoti užrakintus failus - -Copy file access permissions -Kopijuoti failo leidimus - -Description -Apibūdinimas - -&Default -&Numatyta - -Source code written in C++ using: -Šaltinio kodas parašytas su C++ naudojant: - -If you like FreeFileSync -Jei Jums patinka FreeFileSync - -Donate with PayPal -Paremkite per PayPal - -Feedback and suggestions are welcome -Nuomonė ir patarimai laukiami - -Homepage -Namų puslapis - -Email -El. paštas - -Published under the GNU General Public License -Platinama su GNU General Public licenzija - -Many thanks for localization: -Labai dėkojame už vertimą: - -Find -Rasti - -Overview -Apžvalga - -Configuration -Nustatymai - -Open... -Atverti... - -Save -Išsaugoti - -Compare both sides -Sulyginti abi puses - -Comparison settings -Sulyginimo nustatymai - -Synchronization settings -Sinchronizavimo nustatymai - -Start synchronization -Pradėti sinchronizavimą - -Confirm -Patvirtinti - - -1 directory -%x directories - - -1 katalogas -%x katalogai -%x katalogų -%x katalogas - - - -1 file -%x files - - -1 failas -%x failai -%x failų -%x failas - - -Set direction: -Nustatyti kryptį: - -multiple selection -keletos pažymėjimas - -Include via filter: -Įtraukti naudojant filtrą: - -Exclude via filter: -Neįtraukti per filtrą: - -Exclude temporarily -Neįtraukti laikinai - -Include temporarily -Įtraukti laikinai - -Delete -Trinti - -Include all -Įtraukti visus - -Exclude all -Neįtraukti visų - -Show icons: -Rodyti ženkliukus: - -Small -Maži - -Medium -Vidutiniai - -Large -Dideli - -Select time span... -Pasirinkti laiko tarpą... - -Default view -Numatytas rodmuo - -Show "%x" -Rodyti "%x" - -Last session -Paskutinė sesija - -Folder Comparison and Synchronization -Aplankų sulyginimas ir sinchronizavimas - -Configuration saved -Nustatymai išsaugoti - -FreeFileSync batch -FreeFileSync paketinė užduotis - -Do you want to save changes to %x? -Ar norite išsaugoti %x pakeitimus? - -Do&n't save -&Nesaugoti - -Filter -Filtras - -Show files that exist on left side only -Rodyti failus, kurie egzistuoja tik kairėje pusėje - -Show files that exist on right side only -Rodyti failus, kurie egzistuoja tik dešinėje pusėje - -Show files that are newer on left -Rodyti failus, kurie yra naujesni kairėje - -Show files that are newer on right -Rodyti failus, kurie yra naujesni dešinėje - -Show files that are equal -Rodyti failus, kurie yra lygūs - -Show files that are different -Rodyti failus, kurie yra skirtingi - -Show conflicts -Rodyti konfliktus - -Show files that will be created on the left side -Rodyti failus, kurie bus sukurti kairėje pusėje - -Show files that will be created on the right side -Rodyti failus, kurie bus sukurti dešinėje pusėje - -Show files that will be deleted on the left side -Rodyti failus, kurie bus ištrinti kairėje pusėje - -Show files that will be deleted on the right side -Rodyti failus, kurie bus ištrinti dešinėje pusėje - -Show files that will be overwritten on left side -Rodyti failus, kurie bus perrašyti kairėje pusėje - -Show files that will be overwritten on right side -Rodyti failus, kurie bus perrašyti dešinėje pusėje - -Show files that won't be copied -Rodyti failus, kurie ne bus kopijuojami - -Set as default -Nustatyti kaip numatytą - -All folders are in sync -Visi aplankai susinchronizuoti - -Cannot find %x -Nepavyksta rasti %x - -Comma-separated values -Kableliu atskirtos reikšmės - -File list exported -Failų sąrašas eksportuotas - -Searching for program updates... -Ieškoma programos atnaujinimų... - -&Ignore -&Ignoruoti - -&Switch -&Perjungti - -&Yes -&Taip - -&No -&Ne - -Scanning... -Skenuojama... - -Comparing content... -Sulyginamas turinys... - -Info -Informacija - -Paused -Pristabdyta - -Initializing... -Pradedama... - -Completed -Baigta - -Log -Archyvas - -Today -Šiandien - -This week -Ši savaitė - -This month -Šis mėnuo - -This year -Šie metai - -Last x days -Paskutinės x dienos - -Byte -Baitai - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Ar tikrai norite perkelti poziciją į šiukšklių dėžę? -Ar tikrai norite perkelti šias %x pozicijas į šiukšklių dėžę? -Ar tikrai norite perkelti šias %x pozicijų į šiukšklių dėžę? -Ar tikrai norite perkelti poziciją %x į šiukšklių dėžę? - - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Ar tikrai norite ištrinti šį elementą? -Ar tikrai norite ištrinti šiuos %x elementus? -Ar tikrai norite ištrinti šiuos %x elementų? -Ar tikrai norite ištrinti šį %x elementą? - - -Exclude -Neįtraukti - -Direct -Tiesiogiai - -Follow -Sekti - -Copy NTFS permissions -Kopijuoti NTFS leidimus - -Integrate external applications into context menu. The following macros are available: -Integruoti išorines programas į kontekstinį meniu. Sekantys makro galimi: - -- full file or folder name -- pilnas failo ar aplanko pavadinimas - -- folder part only -- tik aplanko dalis - -- Other side's counterpart to %item_path% -- Kitos pusės atitikmuo %item_path% - -- Other side's counterpart to %item_folder% -- Kitos pusės atitikmuo %item_folder% - -Leave as unresolved conflict -Palikti kaip neišpręstą konfliktą - -Replace -Pakeisti - -Move files and replace if existing -Perkelti failus ir pakeisti jei egzistuoja - -Time stamp -Laiko žymė - -Append a timestamp to each file name -Pridėti laiko žymę kiekvieno failo pavadinime - -File -Failas - -YYYY-MM-DD hhmmss -YYYY-MM-DD hhmmss - -Files -Failai - -Items -Pozicijos - -Percentage -Procentai - -Cannot monitor directory %x. -Nepavyko stebėti katalogo %x. - -Conversion error: -Konvertavimo klaida: - -Cannot delete file %x. -Nepavyksta ištrinti failo %x. - -The file is locked by another process: -Failas yra užrakintas kito procceso: - -Cannot move file %x to %y. -Nepavyksta perkelti failo %x į %y. - -Cannot delete directory %x. -Nepavyksta ištrinti katalogo %x. - -Cannot write file attributes of %x. -Nepavyksta įrašyti atributų failui %x. - -Cannot write modification time of %x. -Nepavyksta šrašyti pakeitimo datos %x. - -Cannot read security context of %x. -Nepavyksta perskaityti %x saugumo konteksto. - -Cannot write security context of %x. -Nepavyksta įrašyti %x saugumo konteksto. - -Cannot read permissions of %x. -Nepavyksta perskaityti %x leidimų. - -Cannot write permissions of %x. -Nepavyksta įrašyti leidimų %x. - -Cannot create directory %x. -Nepavyksta sukurti katalogo %x. - -Cannot create symbolic link %x. -Nepavyko sukurti simbolinės nuorodos %x - -Cannot find system function %x. -Nepavyksta rasti sistemos funkcijos %x. - -Cannot copy file %x to %y. -Nepavyksta nukopijuoti failo %x į %y. - -Type of item %x is not supported: -Element tipas %x nepalaikomas: - -Cannot resolve symbolic link %x. -Nepavyko rasti simbolinės nuorodos %x reikšmės - -Cannot open directory %x. -Nepavyksta atverti direktorijos %x. - -Cannot enumerate directory %x. -Nepavyksta sunumeruoti direktorijos %x. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -1 min -%x min -%x min -%x min - - - -1 hour -%x hours - - -1 valanda -%x valandos -%x valandų -%x valanda - - - -1 day -%x days - - -1 diena -%x dienos -%x dienų -%x diena - - -Cannot set privilege %x. -Nepavyksta nustatyti privilegijos %x. - -Cannot change process I/O priorities. -Nepavyksta pakeisti proceso I/O prioritetų. - -Unable to move %x to the recycle bin. -%x į šiukšlinę perkelti nepavyko - -Cannot determine final path for %x. -Galutinio %x kelio rasti nepavyko - -Error Code %x: -Klaidos kodas %x: - diff --git a/BUILD/Languages/outdated/norwegian.lng b/BUILD/Languages/outdated/norwegian.lng deleted file mode 100644 index e965718f..00000000 --- a/BUILD/Languages/outdated/norwegian.lng +++ /dev/null @@ -1,1506 +0,0 @@ -
    - Norsk - Bjørn Snoen - nb_NO - flag_norway.png - 2 - n == 1 ? 0 : 1 -
    - -Cannot determine final path for %x. - - -Unable to move %x to the recycle bin. - - -Unable to suspend system sleep mode. - - -Unable to register to receive system messages. - - -Cannot create symbolic link %x. - - -Items - - -Time stamp - - -Restore all hidden windows and warnings? - - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - - - -Move - - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - - - -Log - - -&Continue - - -Stopped - - -&Don't show this warning again - - -Serious Error - - -&Ignore subsequent errors - - -Searching for program updates... - - -Comma-separated values - - -Comparison Settings - - -Synchronization Settings - - -Never save &changes - - -Include via filter: - - - -%y of 1 row in view -%y of %x rows in view - - - - -&Execute - - -Confirm - - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -Select View - - -Filter Files - - -Main Bar - - -Folder Pairs - - -Select Time Span - - -Global Settings - - -Delete Items - - -Save as Batch Job - - -Restore hidden windows - - -Customize context menu: - - -Delay (in seconds): - - -Retry count: - - -Automatic retry on error: - - -Transfer file and folder permissions. - - -(requires administrator rights) - - -Copy shared or locked files using the Volume Shadow Copy Service. - - -(recommended) - - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - - -The following settings are used for all synchronization jobs. - - -&Clear - - -Maximum: - - -Minimum: - - -File size: - - -Time span: - - -Exclude: - - -Include: - - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. - - -&Recycle bin - - -How can I schedule a batch job? - - -Limit: - - -Save log: - - -Stop synchronization at first error - - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x - - -Stop - - -Minimize to notification area - - -&Don't show this dialog again - - -Variant: - - -Start synchronization now? - - -On completion: - - -Handle errors: - - -Show examples - - -Move files to a user-defined folder - - -Back up deleted and overwritten files in the recycle bin - - -Recycle bin - - -Delete files: - - -Requires database files. Not supported by all file systems. - - -Detect moved files - - -Copy new and updated files to the right folder. - - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. - - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. - - -More information - - -Symbolic links: - - -Identify equal files by comparing the file content. - - -Identify equal files by comparing modification time and size. - - -Select a variant: - - -Find: - - -Close search bar - - -&Check for new version - - -Check &automatically once a week - - -&Check now - - -&Tools - - -&Find... - - -Local Filter - - -Alternate Synchronization Settings - - -Alternate Comparison Settings - - -Paste - - -None - - -Active - - -Local filter - - -Alternate synchronization settings - - -Alternate comparison settings - - -Cannot find current FreeFileSync version number online. Do you want to check manually? - - -&Download - - -Check for Program Updates - - -Retrying operation - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Switching to FreeFileSync's main window - - -You can switch to FreeFileSync's main window to resolve this issue. - - -Synchronization stopped - - -job name - - -Data verification error: %x and %y have different content. - - -Creating a Volume Shadow Copy for %x... - - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. - - -Please enter a target folder for versioning. - - -Incorrect command line: - - -&Show error - - -Waiting until all directories are available... - - -Directory monitoring active - - -Automated Synchronization - - -&Start - - -Command line: - - -Idle time (in seconds): - - -Folders to watch: - - -&View help - - -Unable to create timestamp for versioning: - - -Stop requested: Waiting for current operation to finish... - - -Volume name %x is not part of file path %y. - - -Cannot determine volume name for %x. - - -Cannot access the Volume Shadow Copy Service. - - -%x items/sec - - - -1 thread -%x threads - - - - -Cannot set directory lock for %x. - - - -1 byte -%x bytes - - - - -Calculating sync directions... - - -Starting comparison - - -Resolving symbolic link %x - - -The following folders have dependent paths. Be careful when setting up synchronization rules: - - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. - - -The corresponding folder will be considered as empty. - - -Any number of alternative directories for at most one config file. - - -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. - - -directory - - -config files - - -Syntax: - - -Directories cannot be set for more than one configuration file. - - -The config file must not contain settings at directory pair level when directories are set via command line. - - -Unequal number of left and right directories specified. - - -Cannot open file %x. - - -Syntax error - - -A directory path is expected after %x. - - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: - - -Moving symbolic link %x to the recycle bin - - -Moving folder %x to the recycle bin - - -Moving file %x to the recycle bin - - -The database entry is not in sync considering current settings. - - -Both sides have changed since last synchronization. -Begge sider er endret siden siste synkronisering. - -Cannot determine sync-direction: -Kan ikke bestemme synkroniseringsretning: - -No change since last synchronization. -Ingen endringer siden siste synkronisering. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Stiller inn standard synkroniseringsretning: Gamle filer blir overskrevet med nyere filer. - -Checking recycle bin availability for folder %x... -Sjekker søppelbøtte for tilgjengelig mappe %x... - -Deleting file %x -Sletter fil %x - -Deleting folder %x -Sletter mappe %x - -Deleting symbolic link %x -Sletter symbolsk lenke %x - -An exception occurred -Et avvik har oppstått - -Error -Feil - -File %x does not contain a valid configuration. -Filen %x inneholder en ugyldig innstilling. - -Warning -Advarsel - -Command line -Kommandolinje - -A folder input field is empty. -Et mappefelt er tomt. - -Cannot find the following folders: -Kan ikke finne følgende mapper: - -File %x has an invalid date. -Filen %x har en ugyldig dato. - -Date: -Dato: - -Files %x have the same date but a different size. -Filer %x har den samme datoen, men forskjellig størrelse. - -Size: -Størrelse: - -Items differ in attributes only -Elementer har kun forskjellige attributter - -Comparing content of files %x -Sammenligner innhold til filer %x - -Generating file list... -Lager filliste... - -Out of memory. -For lite minne. - -Item exists on left side only -Element eksisterer kun på venstre side - -Item exists on right side only -Element eksisterer kun på høyre side - -Left side is newer -Venstre side er nyere - -Right side is newer -Høyre side er nyere - -Items have different content -Elementer har forskjellig innhold - -Both sides are equal -Begge sider er like - -Conflict/item cannot be categorized -Konflikt/element kan ikke kategoriseres - -Copy new item to left -Kopier nytt element til venstre - -Copy new item to right -Kopier nytt element til høyre - -Delete left item -Slett venstre element - -Delete right item -Slett høyre element - -Move file on left -Flytt venstre fil - -Move file on right -Flytt høyre fil - -Overwrite left item -Skriv over venstre element - -Overwrite right item -Skriv over høyre element - -Do nothing -Ikke gjør noe - -Update attributes on left -Oppdater attributter til venstre - -Update attributes on right -Oppdater attributter til høyre - -Database file %x is incompatible. -Databasefil %x er inkompatibel. - -Initial synchronization: -Førstegangs synkronisering: - -Database file %x does not yet exist. -Databasefil %x finnes ikke ennå. - -Database file is corrupt: -Databasefilen er korrupt - -Cannot write file %x. -Kan ikke skrive fil %x. - -Cannot read file %x. -Kan ikke lese fil %x. - -Database files do not share a common session. -Databasefiler deler ikke en felles synkroniseringsøkt. - -Searching for folder %x... -Søker etter mappen %x... - -Cannot read file attributes of %x. -Kan ikke lese filattributter fra %x. - -Cannot get process information. -Kan ikke hente prosessinformasjon. - -Waiting while directory is locked (%x)... -Venter mens mappe er låst (%x)... - - -1 sec -%x sec - - -1 sekund -%x sekunder - - -Creating file %x -Oppretter fil %x - -Items processed: -Elementer behandlet: - -Items remaining: -Gjenværende elementer: - -Total time: -Total tid: - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Feil ved analysering av fil %x, rekke %y, kolonne %z. - -Scanning: -Skanner: - -Encoding extended time information: %x -Koder utvided tidsinformasjon: %x - -/sec -/sekund - -Configuration file %x loaded partially only. -Innstillingsfilen %x bare delvis lastet. - -Show in Explorer -Vis i utforsker - -Open with default application -Åpne med standardprogram - -Browse directory -Utforsk mappe - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Bruk FreeFileSync 64-bit-versjon til å opprette skyggekopier på dette systemet. - -Cannot load file %x. -Kan ikke laste filen %x. - -Cannot read the following XML elements: -Kan ikke lese følgende XML-elementer: - -&Open... -&Åpne... - -Save &as... -Lagre &som... - -&Quit -&Avslutt - -&Program -&Program - -&About -&Om programmet - -&Help -&Hjelp - -Usage: -Bruk: - -1. Select folders to watch. -1. Velg mapper å overvåke. - -2. Enter a command line. -2. Skriv en kommandolinje. - -3. Press 'Start'. -3. Trykk 'Start'. - -To get started just import a .ffs_batch file. -Importer en .ffs_batch-fil for å komme i gang - -Add folder -Legg til mappe - -Remove folder -Fjern mappe - -Browse -Bla gjennom - -Select a folder -Velg en mappe - -Idle time between last detected change and execution of command -Inaktiv tid mellom forrige endring og utførelse av kommando - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -Kommandoen utløses hvis: -- filer eller mapper endres -- nye mapper legges til (f.eks. en USB-pinne kobles til) - - -&Retry -&Prøv igjen - -Cancel -Avbryt - -Build: %x -Build: %x - -About -Om - -All files -Alle filer - -&Restore -&Gjenopprett - -&Exit -&Avslutt - -File content -Filinnhold - -File time and size -Filtid og størrelse - -Two way -Toveis - -Mirror -Speile - -Update -Oppdatere - -Custom -Brukerdefinert - -Multiple... -Flere... - -Moving file %x to %y -Flytter fil %x til %y - -Moving folder %x to %y -Flytter mappe %x til %y - -Moving symbolic link %x to %y -Flytter symbolsk lenke %x til %y - -Removing old versions... -Fjerner gamle versjoner... - -Creating symbolic link %x -Oppretter symbolsk lenke %x - -Creating folder %x -Oppretter mappe %x - -Overwriting file %x -Overskriver fil %x - -Overwriting symbolic link %x -Overskriver symbolsk lenke %x - -Verifying file %x -Verifiserer fil %x - -Updating attributes of %x -Oppdaterer attributter til %x - -Cannot find %x. -Kan ikke finne %x. - -Target folder %x already existing. -Målmappe %x eksisterer allerede. - -Target folder input field must not be empty. -Feltet for målmappe kan ikke være tomt. - -Source folder %x not found. -Kildemappe %x finnes ikke. - -The following items have unresolved conflicts and will not be synchronized: -De følgende elementene har uoppklarte konflikter og vil ikke bli synkroniserte: - -Not enough free disk space available in: -Ikke nok ledig diskplass tilgjengelig på: - -Required: -Nødvendig: - -Available: -Tilgjengelig: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -En mappe vil endres som er en del av flere mappepar. Se over innstillingene for synkronisering. - -Synchronizing folder pair: -Synkroniserer mappepar: - -Generating database... -Oppretter database... - -Synchronization completed with errors -Synkronisering fullført med feil - -Synchronization completed with warnings -Synkronisering fullført med advarsler - -Nothing to synchronize -Ikke noe å synkronisere - -Synchronization completed successfully -Synkdonisering fullført - -Saving log file %x... -Lagrer loggfil %x... - -A new version of FreeFileSync is available: -En ny versjon av FreeFileSync er tilgjengelig - -Download now? -Laste ned nå? - -FreeFileSync is up to date. -FreeFileSync er oppdatert. - -Information -Informasjon - -Unable to connect to sourceforge.net. -Kan ikke koble til sourceforge.net. - -Symlink -Symbolsk lenke - -Folder -Mappe - -Full path -Full bane - -Name -Navn - -Relative path -Relativ bane - -Base folder -Grunnmappe - -Size -Størrelse - -Date -Dato - -Extension -Filendelse - -Category -Kategori - -Action -Handling - -Drag && drop -Dra && slipp - -Close progress dialog -Lukk status vindu - -Standby -Standby - -Log off -Logg av - -Shut down -Slå av maskinen - -Hibernate -Sett i dvalemodus - -Remove alternate settings -Fjern alternative innstillinger - -Clear filter settings -Fjern filterinnstillinger - -Copy -Kopier - -&New -&Ny - -&Save -&Lagre - -Save as &batch job... -Lagre som &sammensatt jobb... - -1. &Compare -1. Sammen&lign - -2. &Synchronize -2. &Synkroniser - -&Global settings -&Felles innstillinger - -&Language -&Språk - -&Export file list... -&Eksporter filliste... - -Compare -Sammenlign - -Synchronize -Synkroniser - -Add folder pair -Legg til mappepar - -Remove folder pair -Fjern mappepar - -Swap sides -Bytt sider - -Match case -Skill mellom store og små bokstaver - -Save as batch job -Lagre som sammensatt oppgave - -Hide excluded items -Skjul ekskluderte elementer - -Show filtered or temporarily excluded files -Vis filtrerte eller midlertidig ekskluderte filer - -Number of files and folders that will be created -Antall filer og mapper som vil opprettes - -Number of files that will be overwritten -Antall filer som blir overskrevet - -Number of files and folders that will be deleted -Antall filer og mapper som vil slettes - -Total bytes to copy -Mengde bytes å kopiere - -OK -OK - -Configure your own synchronization rules. -Still inn dine egne synkroniseringsregler. - -Permanent -Permanent - -Delete or overwrite files permanently -Slett eller overskriv filer permanent - -Versioning -Versjonshåndtering - -Naming convention: -Navnekonvensjon: - -Ignore -Ignorer - -Hide all error and warning messages -Skjul feilmeldinger og advarsler - -Pop-up -Pop-up - -Show pop-up on errors or warnings -Vis pop-upvindu ved feil eller advarsler - -Statistics -Statistikk - -Items found: -Elementer funnet: - -Speed: -Hastighet: - -Time remaining: -Gjenstående tid: - -Time elapsed: -Tid gått: - -Synchronizing... -Synkroniserer... - -Close -Lukk - -&Pause -&Pause - -Show progress dialog -Vis statuslinje - -Limit maximum number of log files -Begrens maks antall loggfiler - -Delete on both sides -Slett på begge sider - -Delete on both sides even if the file is selected on one side only -Slett på begge sider selv om filen bare er valgt på en side - -Fail-safe file copy -Trygg filkopi - -Copy locked files -Kopier låste filer - -Copy file access permissions -Kopier filadgangstillatelser - -Description -Beskrivelse - -&Default -&Standard - -Source code written in C++ using: -Kildekode skrevet i C++ med hjelp fra: - -If you like FreeFileSync -Hvis du liker FreeFileSync - -Donate with PayPal -Doner med PayPal - -Feedback and suggestions are welcome -Tilbakemelding og forslag er velkomne - -Homepage -Hjemmeside - -Email -E-post - -Published under the GNU General Public License -Utgitt under GNU General Public Licence - -Many thanks for localization: -Mange takk for lokalisering: - -Find -Søk - -Overview -Oversikt - -Configuration -Innstilling - -Open... -Åpne... - -Save -Lagre - -Compare both sides -Sammenlign begge sider - -Comparison settings -Innstillinger for sammenligning - -Synchronization settings -Innstillinger for synkronisering - -Start synchronization -Start synkronisering - - -1 directory -%x directories - - -1 mappe -%x mapper - - - -1 file -%x files - - -1 fil -%x filer - - -Set direction: -Still inn retningen: - -multiple selection -flervalg - -Exclude via filter: -Ekskluder via filter: - -Exclude temporarily -Ekskluder midlertidig - -Include temporarily -Inkluder midlertidig - -Delete -Slett - -Include all -Inkluder alle - -Exclude all -Ekskluder alle - -Show icons: -Vis ikoner: - -Small -Små - -Medium -Middels - -Large -Store - -Select time span... -Velg tidsområde... - -Default view -Standardvisning - -Show "%x" -Vis "%x" - -Last session -Siste økt - -Folder Comparison and Synchronization -Mappesammenligning og synkronisering - -Configuration saved -Innstilling lagret - -FreeFileSync batch -FreeFileSync batch - -Do you want to save changes to %x? -Vil du lagre endringene til %x? - -Do&n't save -&Ikke lagre - -Filter -Filter - -Show files that exist on left side only -Vis filer som bare finnes på venstre side - -Show files that exist on right side only -Vis filer som bare finnes på høyre side - -Show files that are newer on left -Vis filer som er nyere til venstre - -Show files that are newer on right -Vis filer som er nyere til høyre - -Show files that are equal -Vis filer som er like - -Show files that are different -Vis filer som er forskjellige - -Show conflicts -Vis konflikter - -Show files that will be created on the left side -Vis filer som blir opprettet på venstre side - -Show files that will be created on the right side -Vis filer som blir opprettet på høyre side - -Show files that will be deleted on the left side -Vis filer som blir slettet på venstre side - -Show files that will be deleted on the right side -Vis filer som blir slettet på høyre side - -Show files that will be overwritten on left side -Vis filer som blir overskrevet på venstre side - -Show files that will be overwritten on right side -Vis filer som blir overskrevet på høyre side - -Show files that won't be copied -Vis filer som ikke blir kopiert - -Set as default -Set som standard - -All folders are in sync -Alle mapper er synkroniserte - -Cannot find %x -Kan ikke finne %x - -File list exported -Filliste eksportert - -&Ignore -&Ignorer - -&Switch -&Skift - -&Yes -&Ja - -&No -&Nei - -Scanning... -Skanner... - -Comparing content... -Sammenligner innhold... - -Info -Info - -Paused -Pauset - -Initializing... -Initialiserer... - -Completed -Fullført - -Today -Idag - -This week -Denne uke - -This month -Denne måned - -This year -Dette år - -Last x days -Siste x dager - -Byte -Byte - -KB -KB - -MB -MB - -Exclude -Ekskluder - -Direct -Direkte - -Follow -Følg - -Copy NTFS permissions -Kopier NTFS-tillatelser - -Integrate external applications into context menu. The following macros are available: -Integrer eksterne programmer i høyreklikkmeny. Følgende makroer er tilgjengelige: - -- full file or folder name -- full fil- eller mappenavn - -- folder part only -- kun mapper - -- Other side's counterpart to %item_path% -- Den andre sidens motpart til %item_path% - -- Other side's counterpart to %item_folder% -- Den andre sidens motpart til %item_folder% - -Leave as unresolved conflict -Etterlat som uløste konflikter - -Replace -Erstatt - -Move files and replace if existing -Flytt filer og erstatt hvis eksisterer - -Append a timestamp to each file name -Legg til et tidsstempel til hvert filnavn - -File -Fil - -YYYY-MM-DD hhmmss -ÅÅÅÅ-MM-DD ttmmss - -Files -Filer - -Percentage -Prosent - -Cannot monitor directory %x. -Kan ikke overvåke mappen %x. - -Conversion error: -Konverteringsfeil: - -Cannot delete file %x. -Kan ikke slette filen %x. - -The file is locked by another process: -Filen er låst av en annen prosess: - -Cannot move file %x to %y. -Kan ikke flytte filen %x til %y. - -Cannot delete directory %x. -Kan ikke slette mappen %x. - -Cannot write file attributes of %x. -Kan ikke skrive filattributter til %x. - -Cannot write modification time of %x. -Kan ikke skrive endringstid til %x. - -Cannot read security context of %x. -Kan ikke lese sikkerhetskontekst til %x. - -Cannot write security context of %x. -Kan ikke skrive sikkerhetskontekst til %x. - -Cannot read permissions of %x. -Kan ikke lese tillatelser til %x. - -Cannot write permissions of %x. -Kan ikke skrive tillatelser til %x. - -Cannot create directory %x. -Kan ikke opprette mappen %x. - -Cannot find system function %x. -Kan ikke finne systemfunksjonen %x. - -Cannot copy file %x to %y. -Kan ikke kopiere filen %x til %y. - -Type of item %x is not supported: -Type element %x er ikke støttet: - -Cannot resolve symbolic link %x. -Kan ikke følge symbolsk lenke %x. - -Cannot open directory %x. -Kan ikke åpne mappe %x. - -Cannot enumerate directory %x. -Kan ikke gjennomgå mappe %x. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -1 min -%x min - - - -1 hour -%x hours - - -1 time -%x timer - - - -1 day -%x days - - -1 dag -%x dager - - -Cannot set privilege %x. -Kan ikke sette privilegie %x. - -Cannot change process I/O priorities. -Kan ikke endre I/O prioriet på prosess - -Error Code %x: -Feil kode %x: - diff --git a/BUILD/Languages/polish.lng b/BUILD/Languages/polish.lng deleted file mode 100644 index 32b40d31..00000000 --- a/BUILD/Languages/polish.lng +++ /dev/null @@ -1,1524 +0,0 @@ -
    - Polski - Wojciech Pietruszewski - pl_PL - flag_poland.png - 3 - n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2 -
    - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -&Check - - -Retrying operation... - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Both sides have changed since last synchronization. -Obie strony uległy zmianie od ostatniej synchronizacji. - -Cannot determine sync-direction: -Nie można określić kierunku synchronizacji: - -No change since last synchronization. -Brak zmian od ostatniej synchronizacji. - -The database entry is not in sync considering current settings. -Baza danych nie jest spójna z aktualnymi ustawieniami. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Ustawianie domyślnego kierunku synchronizacji: Stare pliki zostaną nadpisane nowszymi. - -Checking recycle bin availability for folder %x... -Sprawdzanie dostępności kosza dla katalogu %x... - -Moving file %x to the recycle bin -Przenoszenie pliku %x do kosza - -Moving folder %x to the recycle bin -Przenoszenie katalogu %x do kosza - -Moving symbolic link %x to the recycle bin -Przenoszenie dowiązania symbolicznego %x do kosza - -Deleting file %x -Usuwanie pliku %x - -Deleting folder %x -Usuwanie folderu %x - -Deleting symbolic link %x -Usuwanie dowiązania symbolicznego %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -Dla następujących katalogów kosz nie jest dostępny. Pliki zostaną usunięte bez możliwości ich przywrócenia: - -An exception occurred -Wystąpił wyjątek - -A directory path is expected after %x. -Za %x oczekiwana jest ścieżka katalogu. - -Syntax error -Błąd składni - -Cannot open file %x. -Nie można otworzyć pliku %x. - -File %x does not contain a valid configuration. -Plik %x nie zawiera prawidłowej konfiguracji - -Unequal number of left and right directories specified. -Wprowadzona liczba katalogów do synchronizacji jest nierówna. - -The config file must not contain settings at directory pair level when directories are set via command line. -Plik konfiguracyjny nie może zawierać informacji o synchronizowanych katalogach gdy nazwy ścieżek przekazywane są w linii poleceń. - -Directories cannot be set for more than one configuration file. -Dla więcej niż jednego pliku konfiguracyjnego, nie można ustawić wielu katalogów. - -Command line -Linia komend - -Syntax: -Składnia: - -config files -pliki konfiguracyjne - -directory -katalog - -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 directories for at most one config file. -Dowolna liczba katalogów w przypadku użycia jednego pliku konfiguracyjnego. - -A folder input field is empty. -Pole katalog źródłowy jest puste. - -The corresponding folder will be considered as empty. -Katalog będzie oznaczony jako pusty. - -Cannot find the following folders: -Nie można znaleźć następujących katalogów: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -Możesz zignorować ten błąd aby uznając katalogi jako puste. Katalog zostanie utworzony automatycznie podczas synchronizacji. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -Podane katalogi mają zależne ścieżki. Bądź ostrożny podczas ustawiania reguł synchronizacji: - -File %x has an invalid date. -Plik %x ma nieprawidłową datę. - -Date: -Data: - -Files %x have the same date but a different size. -Pliki %x mają tą samą datę lecz różne rozmiary. - -Size: -Rozmiar: - -Items differ in attributes only -Elementy różnią się wyłącznie atrybutami - -Resolving symbolic link %x -Rozwiązywanie dowiązania symbolicznego %x - -Comparing content of files %x -Porównywanie zawartości plików %x - -Generating file list... -Generowanie listy plików... - -Starting comparison -Kompresowanie - -Calculating sync directions... -Obliczanie kierunków synchronizacji... - -Out of memory. -Brak wolnej pamięci. - -Item exists on left side only -Element istnieje tylko po lewej stronie - -Item exists on right side only -Element istnieje tylko po prawej stronie - -Left side is newer -Lewa strona jest nowsza - -Right side is newer -Prawa strona jest nowsza - -Items have different content -Elementy różnią się zawartością - -Both sides are equal -Obie strony są równe - -Conflict/item cannot be categorized -Konflikt/element nie może zostać skategoryzowany - -Copy new item to left -Kopiuj nowy element na lewą stronę - -Copy new item to right -Kopiuj nowy element na prawą stronę - -Delete left item -Usuń lewy element - -Delete right item -Usuń prawy element - -Move file on left -Przenieś plik na lewą stronę - -Move file on right -Przenieś plik na prawą stronę - -Overwrite left item -Nadpisz lewy element - -Overwrite right item -Nadpisz prawy element - -Do nothing -Nie rób nic - -Update attributes on left -Aktualizuj atrybuty po lewej stronie - -Update attributes on right -Aktualizuj atrybuty po prawej stronie - -Database file %x is incompatible. -Plik bazy danych %x nie jest kompatybilny. - -Initial synchronization: -Wstępna synchronizacja: - -Database file %x does not yet exist. -Plik bazy danych %x nie istnieje. - -Database file is corrupt: -Plik bazy danych jest uszkodzony: - -Cannot write file %x. -Nie można zapisać pliku %x. - -Cannot read file %x. -Nie można odczytać pliku %x. - -Database files do not share a common session. -Pliki bazy danych nie współdzielą sesji. - -Searching for folder %x... -Wyszukiwanie katalogu %x... - -Cannot read file attributes of %x. -Nie można odczytać atrybutów pliku %x. - -Cannot get process information. -Nie można uzyskać informacji dla procesu. - -Waiting while directory is locked (%x)... -Blokada katalogu (%x), oczekiwanie... - - -1 sec -%x sec - - -1 sekunda -%x sekundy -%x sekund - - -Creating file %x -Tworzenie pliku %x - -Items processed: -Przetworzone elementy: - -Items remaining: -Pozostałe elementy: - -Total time: -Całkowity czas: - - -1 byte -%x bytes - - -1 bajt -%x bajty -%x bajtów - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Błąd podczas parsowania pliku %x, rząd %y, kolumna %z. - -Cannot set directory lock for %x. -Nie można utworzyć blokady dla katalogu %x. - -Scanning: -Skanowanie: - - -1 thread -%x threads - - -1 wątek -%x wątki -%x wątków - - -Encoding extended time information: %x -Odkodowywanie rozszerzonych informacji o czasie: %x - -/sec -/sekundę - -%x items/sec -%x elementów/sek - -Configuration file %x loaded partially only. -Plik konfiguracyjny %x został wczytany tylko częściowo. - -Show in Explorer -Wyświetl w Eksploratorze - -Open with default application -Otwórz za pomocą domyślnej aplikacji - -Browse directory -Przeglądaj katalog - -Cannot access the Volume Shadow Copy Service. -Nie można uzyskać dostępu do usługi Volume Shadow Copy. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Usługa shadow copy jest aktywna tylko w wersji 64 bitowej programu FreeFileSync. - -Cannot load file %x. -Nie można wczytać pliku %x. - -Cannot determine volume name for %x. -Nie można określić nazwy dysku dla %x. - -Volume name %x is not part of file path %y. -Nazwa zasobu %x ni jest częścią ścieżki %y. - -Stop requested: Waiting for current operation to finish... -Przerwanie: Oczekiwanie na zakończenie aktualnej operacji... - -Unable to create timestamp for versioning: -Nie można utowrzyć znacznika czasu dla wersjonowania: - -Cannot read the following XML elements: -Nie można odczytać elementu XML: - -&Open... -&Otwórz... - -Save &as... -&Zapisz jako... - -&Quit -Zam&knij - -&Program -&Program - -&View help -&Pomoc - -&About -O Program&ie - -&Help -Pomo&c - -Usage: -Użycie: - -1. Select folders to watch. -1. Określ obserwowane katalogi. - -2. Enter a command line. -2. Wprowadź komendę. - -3. Press 'Start'. -3. Wciśnij 'Start'. - -To get started just import a .ffs_batch file. -Aby rozpocząć po zaimportuj plik .ffs_batch. - -Folders to watch: -Katalogi do obserwowania: - -Add folder -Dodaj katalog - -Remove folder -Usuń katalog - -Browse -Przeglądaj - -Select a folder -Wybierz katalog - -Idle time (in seconds): -Czas bezczynności (w sekundach): - -Idle time between last detected change and execution of command -Czas pomiędzy ostatnią wykrytą zmianą, a uruchomieniem komendy - -Command line: -Linia komend: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -Komenda jest wykonywana gdy: -- pliki lub podkatalogi ulegną zmianie -- zostaną utworzony nowe katalogi (n.p włożenie pamięci USB) - - -&Start -&Rozpocznij - -About -O Programie - -Build: %x -Zbudowano: %x - -All files -Wszystkie pliki - -Automated Synchronization -Automatyczna synchronizacja - -Directory monitoring active -Monitorowanie katalogów aktywne - -Waiting until all directories are available... -Oczekiwanie na dostępność wszystkich katalogów... - -Error -Błąd - -&Restore -&Przywróć - -&Show error -Pokaż &błędy - -&Exit -&Wyjście - -Incorrect command line: -Niepoprawne polecenie: - -&Retry -&Powtórz - -File content -Zawartość pliku - -File time and size -Czas modyfikacji i rozmiar - -Two way -Obustronna - -Mirror -Lustrzana - -Update -Uaktualnij - -Custom -Własne - -Multiple... -Wiele... - -Moving file %x to %y -Przenoszenie pliku %x do %y - -Moving folder %x to %y -Przenoszenie katalogu %x do %y - -Moving symbolic link %x to %y -Przenoszenie dowiązania symbolicznego %x do %y - -Removing old versions... -Usuwanie starszych wersji... - -Creating symbolic link %x -Tworzenie dowiązania symbolicznego %x - -Creating folder %x -Tworzenie folderu %x - -Overwriting file %x -Nadpisywanie pliku %x - -Overwriting symbolic link %x -Nadpisywanie dowiązania symbolicznego %x - -Verifying file %x -Weryfikowanie pliku %x - -Updating attributes of %x -Aktualizowanie atrybutów %x - -Cannot find %x. -Nie można znaleźć %x. - -Target folder %x already existing. -Katalog docelowy %x już istnieje. - -Target folder input field must not be empty. -Pole katalog docelowy nie może być puste. - -Please enter a target folder for versioning. -Określ katalog do wersjonowania. - -Source folder %x not found. -Nie znaleziono katalogu docelowego %x. - -The following items have unresolved conflicts and will not be synchronized: -Te elementy znajdują się w konflikcie, którego nie można rozwiązać. Pliki nie zostaną zsynchronizowane: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -Poniższe katalogi znacznie się różnią. Upewnij się, że określone zostały prawiłowe katalogi. - -Not enough free disk space available in: -Brak wystarczającej przestrzeni dyskowej na: - -Required: -Wymagane: - -Available: -Dostępne: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Katalog zostanie zmodyfikowany w związku z synchronizacją wielu katalogów. Zweryfikuj ustawienia synchronizacji. - -Synchronizing folder pair: -Synchronizacja katalgów: - -Generating database... -Generowanie bazy danych... - -Creating a Volume Shadow Copy for %x... -Tworzenie Volume Shadow Copy dla %x... - -Data verification error: %x and %y have different content. -Nastąpił błąd weryfikacji: %x oraz %y różnią się zawartością. - -job name -nazwa zadania - -Synchronization stopped -Synchronizacja zatrzymana - -Synchronization completed with errors -Synchronizacja zakończona z błędami - -Synchronization completed with warnings -Synchronizacja zakończona z ostrzeżeniami - -Nothing to synchronize -Brak plików do synchronizacji - -Synchronization completed successfully -Synchronizacja zakończona pomyślnie - -Saving log file %x... -Zapisywanie pliku logów %x... - -You can switch to FreeFileSync's main window to resolve this issue. -Możesz przejść do głównego okna FreeFileSync abe rozwiązać ten problem. - -&Don't show this warning again -&Nie pokazuj ponownie tego ostrzeżenia - -&Ignore -&Ignoruj - -&Switch -&Zamień - -Switching to FreeFileSync's main window -Przejdź do głównego okna FreeFileSync. - -&Ignore subsequent errors -&Ignoruj kolejne błędy - -Serious Error -Poważny błąd - -Check for Program Updates -Sprawdź dostępne aktualizacje. - -A new version of FreeFileSync is available: -Dostępna jest nowa wersja FreeFileSync: - -Download now? -Pobrać teraz? - -&Download -&Pobierz - -FreeFileSync is up to date. -Posiadasz aktualną wersję FreeFileSync. - -Unable to connect to sourceforge.net. -Nie można się połączyć z sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Nie można znaleźć obecnej wersji FreeFileSync. Czy chcesz sprawdzić ręcznie? - -Symlink -Dowiązanie symboliczne - -Folder -Katalog - -Full path -Pełna ścieżka - -Name -Nazwa - -Relative path -Relatywna ścieżka - -Base folder -Katalog bazowy - -Size -Rozmiar - -Date -Data - -Extension -Rozszerzenie - -Category -Kategoria - -Action -Akcja - -Drag && drop -Drag && Drop - -Close progress dialog -Zamknij okno postępu - -Standby -Przejdź w stan uśpienia - -Log off -Wyloguj użytkownika - -Shut down -Wyłącz komputer - -Hibernate -Przejdź w stan hibernacji - -Alternate comparison settings -Alternatywne ustawienia porównywania - -Alternate synchronization settings -Alternatywne ustawienia synchronizacji - -Local filter -Filtr lokalny - -Active -Aktywny - -None -Żaden - -Remove alternate settings -Usuń alternatywne ustawienia - -Clear filter settings -Wyczyść ustawienia filtra - -Copy -Kopiuj - -Paste -Wklej - -Alternate Comparison Settings -Alternatywne ustawienia porównywania - -Alternate Synchronization Settings -Alternatywne ustawienia synchronizacji - -Local Filter -Filtr lokalny - -&New -&Nowy - -&Save -&Zapisz - -Save as &batch job... -Zapisz w trybie &wsadowym... - -1. &Compare -1. &Porównaj - -2. &Synchronize -2. &Synchronizuj - -&Global settings -&Ustawienia programu - -&Language -&Język - -&Find... -&Szukaj... - -&Export file list... -&Eksportuj listę plików... - -&Tools -&Narzędzia - -&Check now -Spra&wdź teraz - -Check &automatically once a week -Sprawdzaj &automatycznie raz w tygodniu - -&Check for new version -Sprawdź &nową wersję - -Compare -Porównaj - -Cancel -Anuluj - -Synchronize -Synchronizuj - -Add folder pair -Dodaj katalogi do porównania - -Remove folder pair -Usuń katalogi - -Swap sides -Zamień stronami - -Close search bar -Zamknij pasek wyszukiwania - -Find: -Szukaj: - -Match case -Uwzględnij wielkość liter - -Save as batch job -Zapisz w trybie wsadowym - -Hide excluded items -Ukryj wykluczone elementy - -Show filtered or temporarily excluded files -Pokaż pliki wykluczone tymczasowo lub pliki wykluczone tymczasowo - -Number of files and folders that will be created -Liczba plików i katalogów, które zostaną utworzone - -Number of files that will be overwritten -Liczba plików, które zostaną nadpisane - -Number of files and folders that will be deleted -Liczba plików i katalogów, które zostaną usunięte - -Total bytes to copy -Całkowity rozmiar do skopiowania - -Select a variant: -Określ wariant: - -Identify equal files by comparing modification time and size. -Określ różnice w plikach na podstawie czasu modyfikacji i rozmiaru. - -Identify equal files by comparing the file content. -Określ różnice w plikach na podstawie ich zawartości. - -Symbolic links: -Dowiązania symboliczne: - -More information -Więcej informacji - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Wyszukaj oraz zastosuj zmiany po obu stronach. Wszystkie operacje na plikach takie jak usunięcia, zmiany oraz konflikty wykrywane są automatycznie przy użyciu bazy danych. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Kopia lustrzana lewego katalogu. Po synchronizacji, zawartość prawego katalogu będzie będzie identyczna jak w lewym katalogu. - -Copy new and updated files to the right folder. -Kopiuj nowe i zmodyfikowane pliki do prawego katalogu. - -Configure your own synchronization rules. -Skonfiguruj swoje własne reguły synchronizacji. - -Detect moved files -Wykryj przeniesione pliki. - -Requires database files. Not supported by all file systems. -Wymaga plików bazy danych. Nie wspierane przez wszystkie systemy plików. - -Delete files: -Usuwanie plików: - -Permanent -Trwale - -Delete or overwrite files permanently -Usuń lub nadpisz pliki na stałe - -Recycle bin -Kosz - -Back up deleted and overwritten files in the recycle bin -Przechowuj pliki, które zostały usunięte lub nadpisane w koszu. - -Versioning -Wersjonowanie - -Move files to a user-defined folder -Przenieś pliki do katalogu zdefiniowanego przez użytkownika - -Naming convention: -Konwencja nazewnictwa: - -Show examples -Przykład - -Handle errors: -Obsługa błędów: - -Ignore -Ignoruj - -Hide all error and warning messages -Ukryj wszystkie informacje błędach i ostrzeżeniach - -Pop-up -Pop-up - -Show pop-up on errors or warnings -Pokazuj okna pop-up dla błędów i ostrzeżeń - -On completion: -Po zakończeniu: - -Start synchronization now? -Rozpocząć teraz synchronizację? - -Variant: -Wariant: - -Statistics -Statystyki - -&Don't show this dialog again -&Nie pokazuj więcej tego okna - -Items found: -Znalezione elementy: - -Speed: -Prędkość: - -Time remaining: -Do ukończenia: - -Time elapsed: -Szacowany czas: - -Synchronizing... -Synchronizuję... - -Minimize to notification area -Minimalizuj do obszaru powiadomiń - -Close -Zamknij - -&Pause -&Pauza - -Stop -Przerwij - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Utwórz plik wsadowy do zautomatyzowania procesu synchronizacji. Aby rozpocząć, klknij dwa razy plik lub zaplanuj zadanie w: %x - -Stop synchronization at first error -Przerwij synchronizację przy pierwszym błędzie - -Show progress dialog -Pokaż okno postępu - -Save log: -Zapisz logi: - -Limit: -Limit: - -Limit maximum number of log files -Określ maksymalną liczbę plików z logami - -How can I schedule a batch job? -Jak zaplanować zadanie w trybie wsadowym? - -&Recycle bin -&Kosz systemowy - -Delete on both sides -Usuń po obu stronach - -Delete on both sides even if the file is selected on one side only -Usuń po obu stronach nawet jeżeli plik zaznaczony jest tylko po jednej stronie - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Określ reguły filtrowania w celu wykluczenia niektórych plików z synchronizacji. Ścieżki plików muszą być relatywne do podanych par katalogów. - -Include: -Dołącz: - -Exclude: -Wyklucz: - -Time span: -Przedział czasu: - -File size: -Rozmiar pliku: - -Minimum: -Minimalny: - -Maximum: -Maksymalny: - -&Clear -W&yczyść - -The following settings are used for all synchronization jobs. -Następujące ustawienia stosowane są do wszystkich zadań synchronizacji. - -Fail-safe file copy -Bezpieczne kopiowanie - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -Aby zagwarantować spójność synchronizacji nawet podczas błędu, -program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadpisuje plik docelowy. - - -(recommended) -(rekomendowane) - -Copy locked files -Kopiuj zablokowane pliki - -Copy shared or locked files using the Volume Shadow Copy Service. -Kopiuj pliki współdzielone lub zablokowane używając usługi Volume Shadow Copy. - -(requires administrator rights) -(wymagane uprawnienia administratora) - -Copy file access permissions -Kopiuj uprawnienia plików - -Transfer file and folder permissions. -kopiuj uprawnienia plików i katalogów. - -Automatic retry on error: -Automatyczne ponowienie operacji podczas błędu: - -Retry count: -Liczba prób: - -Delay (in seconds): -Opóźnienie (w sekundach): - -Customize context menu: -Dostosuj menu kontekstowe: - -Description -Opis - -Restore hidden windows -Przywróć ukryte dialogi - -&Default -&Domyślne - -Source code written in C++ using: -Kod stworzony w C++ z wykorzystaniem: - -If you like FreeFileSync -Podoba Ci się FreeFileSync? - -Donate with PayPal -Wesprzyj z PayPal - -Feedback and suggestions are welcome -Wszelkie opinie i sugestie mile widziane - -Homepage -Strona domowa - -Email -Poczta - -Published under the GNU General Public License -Udostępnione na zasadach licencji GNU General Public License - -Many thanks for localization: -Podziękowania za tłumaczenia: - -Save as Batch Job -Zapisz w trybie wsadowym - -Delete Items -Usuń elementy - -Global Settings -Ustawienia ogólne - -Select Time Span -Określ - -Folder Pairs -Pary katalogów - -Find -Znajdź - -Overview -Przegląd - -Configuration -Konfiguracja - -Main Bar -Główny pasek - -Filter Files -Filtr - -Select View -Widok - -Open... -Otwórz... - -Save -Zapisz - -Compare both sides -Porównaj foldery - -Comparison settings -Ustawienia porównywania - -Synchronization settings -Ustawienia synchronizacji - -Start synchronization -Rozpocznij synchronizację - -Confirm -Potwierdź - -&Execute -&Wykonaj - - -1 directory -%x directories - - -1 katalog -%x katalogi -%x katalogów - - - -1 file -%x files - - -1 plik -%x pliki -%x plików - - - -%y of 1 row in view -%y of %x rows in view - - -Widok %y z 1 lini -Widok %y z %x lini -Widok %y z %x lini - - -Set direction: -Kierunek synchronizacji: - -multiple selection -zaznaczone elementy - -Include via filter: -Dodaj pliki: - -Exclude via filter: -Dodaj filtr: - -Exclude temporarily -Wyklucz tymczasowo - -Include temporarily -Dołącz tymczasowo - -Delete -Usuń - -Include all -Zaznacz wszystko - -Exclude all -Odznacz wszystko - -Show icons: -Pokaż ikony: - -Small -Mały - -Medium -Średni - -Large -Duży - -Select time span... -Określ przedział czasowy... - -Default view -Widok domyślny - -Show "%x" -Pokaż "%x" - -Last session -Ostatnia sesja - -Folder Comparison and Synchronization -Porównywanie i Synchronizacja folderów - -Configuration saved -Konfiguracja zapisana - -FreeFileSync batch -Plik wsadowy FreeFileSync - -Do you want to save changes to %x? -Czy chcesz zapisać zmiany w %x? - -Never save &changes -Nigdy nie zapisuj &zmian - -Do&n't save -&Nie zapisuj - -Filter -Filtr - -Show files that exist on left side only -Pokaż pliki istniejące tylko po lewej stronie - -Show files that exist on right side only -Pokaż pliki istniejące tylko po prawej stronie - -Show files that are newer on left -Pokaż pliki nowsze po lewej stronie - -Show files that are newer on right -Pokaż pliki nowsze po prawej stronie - -Show files that are equal -Pokaż pliki, które są równe - -Show files that are different -Pokaż pliki, które się różnią - -Show conflicts -Pokaż konflikty - -Show files that will be created on the left side -Pokaż pliki, które będą utworzone po lewej stronie - -Show files that will be created on the right side -Pokaż pliki, które będą utworzone po prawej stronie - -Show files that will be deleted on the left side -Pokaż pliki, które będą usunięte po lewej stronie - -Show files that will be deleted on the right side -Pokaż pliki, które będą usunięte po prawej stronie - -Show files that will be overwritten on left side -Pokaż pliki, które zostaną nadpisane po lewej stronie - -Show files that will be overwritten on right side -Pokaż pliki, które zostaną nadpisane po prawej stronie - -Show files that won't be copied -Pokaż pliki, które nie będą kopiowane - -Set as default -Zapisz jako domyślne - -All folders are in sync -Wszystkie katalogi są zsynchronizowane - -Synchronization Settings -Ustawienia synchronizacji - -Comparison Settings -Ustawienia porównywania - -Cannot find %x -Nie można znaleźć %x - -Comma-separated values -Wartości rozdzielone przecinkiem - -File list exported -Lista plików wyeksportowana - -Searching for program updates... -Wyszukiwanie aktualizacji... - -Scanning... -Skanowanie... - -Comparing content... -Porównywanie zawartości... - -Info -Info - -Warning -Ostrzeżenie - -Paused -Pauza - -Initializing... -Inicjalizacja... - -Stopped -Zatrzymana - -Completed -Zakończono - -&Continue -&Kontynuuj - -Log -Log - -Today -Dzisiaj - -This week -W tym tygodniu - -This month -W tym miesiącu - -This year -W tym roku - -Last x days -Ostatnie x dni - -Byte -Bajtów - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Czy na pewno chcesz przenieść ten element do kosza? -Czy na pewno chcesz przenieść te %x elementy do kosza? -Czy na pewno chcesz przenieść te %x elemntów do kosza? - - -Move -Przenieś - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Czy na pewno chcesz usunąć następujący element? -Czy na pewno chcesz usunąć %x następujące elementy? -Czy na pewno chcesz usunąć %x następujących elementów? - - -Exclude -Wyklucz - -Direct -Bezpośrednio - -Follow -Podążaj - -Copy NTFS permissions -Kopiuj uprawnienia NTFS - -Integrate external applications into context menu. The following macros are available: -Dołącz zewnętrzną aplikację do menu kontekstowego. Dostępne makra: - -- full file or folder name -- pełna nazwa pliku lub katalogu - -- folder part only -- tylko część katalogu - -- Other side's counterpart to %item_path% -- Odpowiednik %item_path% po drugiej stronie - -- Other side's counterpart to %item_folder% -- Odpowiednik %item_folder% po drugiej stronie - -Restore all hidden windows and warnings? -Przywrócić wszystkie ukryte dialogi? - -Leave as unresolved conflict -Zostaw jako nierozwiązany konflikt - -Replace -Zamień - -Move files and replace if existing -Przenieś pliki i nadpisz jeżeli już istnieją - -Time stamp -Znacznik czasu - -Append a timestamp to each file name -Dołącz znacznik czasu do nazwy każdego pliku - -File -Plik - -YYYY-MM-DD hhmmss -YYYY-MM-DD hhmmss - -Files -Pliki - -Items -Elementy - -Percentage -Procentowo - -Cannot monitor directory %x. -Nie można monitorować katalogu %x. - -Conversion error: -Błąd konwersji: - -Cannot delete file %x. -Nie można usunąć pliku %x. - -The file is locked by another process: -Plik jest zablokowany przez inny proces: - -Cannot move file %x to %y. -Nie można przenieść pliku %x do %y. - -Cannot delete directory %x. -Nie można usunąć katalogu %x. - -Cannot write file attributes of %x. -Nie można zapisać atrybutów %x. - -Cannot write modification time of %x. -Nie można zapisać czasu modyfikacji %x. - -Cannot read security context of %x. -Nie można odczytać ustawień bezpieczeństwa %x. - -Cannot write security context of %x. -Nie można zapisać ustawień bezpieczeństwa %x. - -Cannot read permissions of %x. -Nie można odczytać uprawnień %x. - -Cannot write permissions of %x. -Nie można zapisać uprawnień %x. - -Cannot create directory %x. -Nie można utworzyć katalogu %x. - -Cannot create symbolic link %x. -Nie można utworzyć dowiązania symbolicznego dla %x. - -Cannot find system function %x. -Nie można odnaleźć funkcji systemowej %x. - -Cannot copy file %x to %y. -Nie można skopiować pliku %x do %y. - -Type of item %x is not supported: -Element typu %x nie jest wspierany: - -Cannot resolve symbolic link %x. -Nie można określić położenia dowiązania symbolicznego %x. - -Cannot open directory %x. -Nie można otworzyć katalogu %x. - -Cannot enumerate directory %x. -Nie można wyliczyć katalogu %x. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -1 minuta -%x minuty -%x minut - - - -1 hour -%x hours - - -1 godzina -%x godziny -%x godzin - - - -1 day -%x days - - -1 dzień -%x dni -%x dni - - -Unable to register to receive system messages. -Błąd podczas rejestrowania do odbioru komunikatów systemowych. - -Cannot set privilege %x. -Nie można ustawić uprawnień %x. - -Unable to suspend system sleep mode. -Nie można przerwać trybu uśpienia. - -Cannot change process I/O priorities. -Nie można zmienić priorytetu I/O procesu. - -Unable to move %x to the recycle bin. -Nie można przenieść %x do kosza. - -Cannot determine final path for %x. -Nie można określić ostatecznej ścieżki dla %x. - -Error Code %x: -Kod błędu %x: - diff --git a/BUILD/Languages/portuguese.lng b/BUILD/Languages/portuguese.lng deleted file mode 100644 index 66105d0a..00000000 --- a/BUILD/Languages/portuguese.lng +++ /dev/null @@ -1,1517 +0,0 @@ -
    - Português - Carlos Balseiro - pt_PT - flag_portugal.png - 2 - n == 1 ? 0 : 1 -
    - -&Check - - -Retrying operation... - - -Both sides have changed since last synchronization. -Ambos os lados tiveram alterações desde a última sincronização. - -Cannot determine sync-direction: -Não é possível saber a direção de sincronização: - -No change since last synchronization. -Não há alterações desde a última sincronização. - -The database entry is not in sync considering current settings. -A base de dados não está sincronizada com as definições correntes. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Escolher direcção de sincronização por defeito: Os ficheiros antigos serão substituídos pelos novos. - -Checking recycle bin availability for folder %x... -A verificar a disponibilidade da reciclagem para a pasta %x... - -Moving file %x to the recycle bin -Mover ficheiro %x para a reciclagem - -Moving folder %x to the recycle bin -Mover pasta %x para a reciclagem - -Moving symbolic link %x to the recycle bin -Mover link simbólico %x para a reciclagem - -Deleting file %x -Eliminar ficheiro %x - -Deleting folder %x -Eliminar pasta %x - -Deleting symbolic link %x -Eliminar link simbólico %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -A reciclagem não está disponível para as seguintes pastas. Os ficheiros serão, em alternativa, eliminados permanentemente: - -An exception occurred -Ocorreu uma excepção - -A directory path is expected after %x. -É esperado um caminho de directório após %x. - -Syntax error -Erro de sintaxe - -Cannot open file %x. -Não é possível abrir o ficheiro %x. - -File %x does not contain a valid configuration. -Ficheiro %x não tem uma configuração válida. - -Unequal number of left and right directories specified. -Número desigual de directórios à esquerda e à direita. - -The config file must not contain settings at directory pair level when directories are set via command line. -O ficheiro de configuração não deve conter configurações ao nível de par-directório quando os directórios são definidos pela linha de comandos. - -Directories cannot be set for more than one configuration file. -Os directórios não podem ser definidos em mais do que um ficheiro de configuração. - -Command line -Linha de comandos - -Syntax: -Sintaxe: - -config files -ficheiros de configuração - -directory -directório - -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 directories for at most one config file. -Qualquer número de directórios alternativos para apenas um ficheiro de configuração. - -A folder input field is empty. -Um dos campos de directório para comparar está vazio. - -The corresponding folder will be considered as empty. -A pasta correspondente será considerada como vazia. - -Cannot find the following folders: -Não é possível encontrar as seguintes pastas: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -Pode ignorar este erro para considerar cada pasta como vazia. As pastas necessárias serão criadas automaticamente durante a sincronização. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -As seguintes pastas têm caminhos dependentes. Tenha atenção ao definir as regras de sincronizaçção: - -File %x has an invalid date. -Ficheiro %x tem data inválida. - -Date: -Data: - -Files %x have the same date but a different size. -Os ficheiros %x têm a mesma data, mas tamanho diferente. - -Size: -Tamanho: - -Items differ in attributes only -Itens diferem apenas nos atributos - -Resolving symbolic link %x -Resolver link simbólico %x - -Comparing content of files %x -A comparar o conteúdo do ficheiro %x - -Generating file list... -A gerar lista ficheiros... - -Starting comparison -A iniciar a comparação - -Calculating sync directions... -A calcular a direcção de sincronização... - -Out of memory. -Sem memória disponível. - -Item exists on left side only -Item existe apenas à esquerda - -Item exists on right side only -Item existe apenas à direita - -Left side is newer -Esquerda é mais recente - -Right side is newer -Direita é mais recente - -Items have different content -Itens tem conteúdo diferente - -Both sides are equal -Ambos os lados iguais - -Conflict/item cannot be categorized -Conflito/item não pode ser categorizado - -Copy new item to left -Copiar novo item para a esquerda - -Copy new item to right -Copiar novo item para a direita - -Delete left item -Apagar item esquerdo - -Delete right item -Apagar item direito - -Move file on left -Mover ficheiro à esquerda - -Move file on right -Mover ficheiro à direita - -Overwrite left item -Substituir item da esquerda - -Overwrite right item -Substituir item da direita - -Do nothing -Não fazer nada - -Update attributes on left -Actualizar atributos à esquerda - -Update attributes on right -Actualizar atributos à direita - -Database file %x is incompatible. -Base de dados %x não é compatível. - -Initial synchronization: -Sincronização inicial: - -Database file %x does not yet exist. -Base de dados %x não existe. - -Database file is corrupt: -Ficheiro de base de dados está corrompido: - -Cannot write file %x. -Não é possível escrever o ficheiro %x. - -Cannot read file %x. -Não é possível ler o ficheiro %x. - -Database files do not share a common session. -As bases de dados são de sessões diferentes. - -Searching for folder %x... -À procura da pasta %x... - -Cannot read file attributes of %x. -Não é possível ler os atributos do ficheiro %x. - -Cannot get process information. -Não é possível obter informação sobre o processo. - -Waiting while directory is locked (%x)... -Aguardar enquanto o directório é bloqueado (%x)... - - -1 sec -%x sec - - -1 seg -%x segs - - -Creating file %x -Criar ficheiro %x - -Items processed: -Elementos processados: - -Items remaining: -Elementos restantes: - -Total time: -Tempo total: - - -1 byte -%x bytes - - -1 byte -%x bytes - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Erro ao analisar ficheiro %x, linha %y, coluna %z. - -Cannot set directory lock for %x. -Não é possível colocar o bloqueio de directório para %x. - -Scanning: -A pesquisar: - - -1 thread -%x threads - - -1 thread -%x threads - - -Encoding extended time information: %x -A codificar dados temporais extendidos: %x - -/sec -/seg - -%x items/sec -%x itens/seg - -Configuration file %x loaded partially only. -Ficheiro de configuração %x carregado parcialmente. - -Show in Explorer -Mostrar no Explorer - -Open with default application -Abrir com a aplicação associada - -Browse directory -Procurar directório - -Cannot access the Volume Shadow Copy Service. -Não é possível aceder ao serviço Volume Shadow Copy. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Utilize a versão 64-bit do FreeFileSync para criar cópias sombra neste sistema. - -Cannot load file %x. -Não é possível carregar o ficheiro %x. - -Cannot determine volume name for %x. -Não é possível determinar o nome do volume para %x. - -Volume name %x is not part of file path %y. -Nome de volume %x não faz parte do caminho do ficheiro %y. - -Stop requested: Waiting for current operation to finish... -Paragem solicitada: À espera que termine a operação actual... - -Unable to create timestamp for versioning: -Não é possível criar timestamp para controle de versões: - -Cannot read the following XML elements: -Não é possível ler os elementos XML: - -&Open... -&Abrir... - -Save &as... -Guardar &como... - -&Quit -&Sair - -&Program -&Programa - -&View help -&Ver ajuda - -&About -So&bre - -&Help -A&juda - -Usage: -Uso: - -1. Select folders to watch. -1. Selecionar pastas para observar. - -2. Enter a command line. -2. Inserir a linha de comando. - -3. Press 'Start'. -3. Pressionar 'Iniciar'. - -To get started just import a .ffs_batch file. -Para começar basta importar um ficheiro .ffs_batch. - -Folders to watch: -Pastas a verificar: - -Add folder -Adicionar pasta - -Remove folder -Remover pasta(s) - -Browse -Procurar - -Select a folder -Selecione uma pasta - -Idle time (in seconds): -Tempo de espera (em segundos): - -Idle time between last detected change and execution of command -Tempo de espera entre a última alteração detetada e a execução do comando - -Command line: -Linha de comandos: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -O comando é executado se: -- ficheiros ou subpastas forem alterados -- forem detetadas novas pastas (p.e. pen USB) - - -&Start -&Início - -About -Sobre - -Build: %x -Build: %x - -All files -Todos os ficheiros - -Automated Synchronization -Sincronização Automática - -Directory monitoring active -Monitorização de directório activa - -Waiting until all directories are available... -A aguardar que todos os directórios fiquem disponíveis... - -Error -Erro - -&Restore -&Restaurar - -&Show error -Mostrar &erro - -&Exit -&Sair - -Incorrect command line: -Linha de comandos incorrecta: - -&Retry -&Tentar de Novo - -File content -Conteúdo do ficheiro - -File time and size -Data e tamanho do ficheiro - -Two way -Duas vias - -Mirror -Espelhar - -Update -Actualizar - -Custom -Personalizado - -Multiple... -Multiplo... - -Moving file %x to %y -Mover ficheiro %x para %y - -Moving folder %x to %y -Mover pasta %x para %y - -Moving symbolic link %x to %y -Mover link simbólico %x para %y - -Removing old versions... -A remover versões antigas... - -Creating symbolic link %x -Criar link simbólico %x - -Creating folder %x -Criar pasta %x - -Overwriting file %x -Substituir ficheiro %x - -Overwriting symbolic link %x -Substituir link simbólico %x - -Verifying file %x -A verificar ficheiro %x - -Updating attributes of %x -Actualizar atributos de %x - -Cannot find %x. -Não é possível encontrar %x. - -Target folder %x already existing. -Directório de destino %x já existe. - -Target folder input field must not be empty. -Campo de directório de destino não deve estar vazio. - -Please enter a target folder for versioning. -Introduza uma pasta de destino para o controlo de versões. - -Source folder %x not found. -Directório %x não encontrado. - -The following items have unresolved conflicts and will not be synchronized: -Os seguintes itens têm conflitos não resolvidos, e não serão sincronizados: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -As seguintes pastas são significativamente diferentes. Verifique se está a usar as pastas correctas na sincronização. - -Not enough free disk space available in: -Não há espaço livre suficiente em: - -Required: -Requirido: - -Available: -Disponível: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Uma pasta que faz parte de vários pares vai ser modificada. Por favor, reveja os parametros de sincronização. - -Synchronizing folder pair: -Sincronizar o par de pastas: - -Generating database... -A gerar base de dados... - -Creating a Volume Shadow Copy for %x... -A criar Volume Shadow Copy para %x... - -Data verification error: %x and %y have different content. -Erro de verificação de dados: %x e %y têm conteúdo diferente. - -job name -nome da tarefa - -Synchronization stopped -Sincronização parada - -Synchronization completed with errors -Sincronização completa com erros - -Synchronization completed with warnings -Sincronização completa com avisos - -Nothing to synchronize -Nada a sincronizar - -Synchronization completed successfully -Sincronização completa com sucesso - -Saving log file %x... -A guardar ficheiro log %x... - -You can switch to FreeFileSync's main window to resolve this issue. -Pode mudar para a janela principal do FreeFileSync para resovler este problema. - -&Don't show this warning again -&Não mostrar este aviso novamente - -&Ignore -&Ignorar - -&Switch -&Trocar - -Switching to FreeFileSync's main window -A mudar para a janela principal do FreeFileSync - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - -Repetir automaticamente dentro de 1 segundo... -Repetir automaticamente dentro de %x segundos... - - -&Ignore subsequent errors -&Ignorar erros subsequentes - -Serious Error -Erro Grave - -Check for Program Updates -Procurar actualizações do programa - -A new version of FreeFileSync is available: -Uma nova versão do FreeFileSync está disponível: - -Download now? -Fazer download agora? - -&Download -&Download - -FreeFileSync is up to date. -FreeFileSync está actualizado. - -Unable to connect to sourceforge.net. -Não é possível ligar a sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Não é possível encontrar a última versão do FreeFileSync online. Deseja verificar manualmente? - -Symlink -Link Simbólico - -Folder -Pasta - -Full path -Caminho completo - -Name -Nome - -Relative path -Caminho - -Base folder -Pasta principal - -Size -Tamanho - -Date -Data - -Extension -Extensão - -Category -Categoria - -Action -Ação - -Drag && drop -Arrastar && Largar - -Close progress dialog -Fechar diálogo de progresso - -Standby -Standby - -Log off -Terminar sessão - -Shut down -Desligar - -Hibernate -Hibernar - -Alternate comparison settings -Definições de comparação alternativas - -Alternate synchronization settings -Definições de sincronização alternativas - -Local filter -Filtro local - -Active -Activo - -None -Nenhum - -Remove alternate settings -Remover opções alternativas - -Clear filter settings -Limpar opções do filtro - -Copy -Copiar - -Paste -Colar - -Alternate Comparison Settings -Definições de comparação alternativas - -Alternate Synchronization Settings -Definições de sincronização alternativas - -Local Filter -Filtro local - -&New -&Novo - -&Save -G&uardar - -Save as &batch job... -Guardar como &batch... - -1. &Compare -1. &Comparar - -2. &Synchronize -2. &Sincronizar - -&Global settings -&Opções - -&Language -&Língua - -&Find... -&Procurar... - -&Export file list... -&Exportar lista de ficheiros... - -&Tools -&Ferramentas - -&Check now -&Verificar agora - -Check &automatically once a week -Verificar &automaticamente uma vez por semana - -&Check for new version -&Procurar por actualizações - -Compare -Comparar - -Cancel -Cancelar - -Synchronize -Sincronizar - -Add folder pair -Adicionar um par de pastas - -Remove folder pair -Remover o par de pastas - -Swap sides -Trocar lados - -Close search bar -Fechar barra de pesquisa - -Find: -Procurar: - -Match case -Correspondência - -Save as batch job -Guardar como batch - -Hide excluded items -Esconder itens excluídos - -Show filtered or temporarily excluded files -Mostrar ficheiros filtrados ou temporariamente excluidos - -Number of files and folders that will be created -Número de ficheiros e pastas a ser criados - -Number of files that will be overwritten -Número de ficheiros substituidos - -Number of files and folders that will be deleted -Número de ficheiros e pastas a ser eliminados - -Total bytes to copy -Total em bytes a copiar - -Select a variant: -Seleccionar uma variante: - -Identify equal files by comparing modification time and size. -Identificar ficheiros iguais ao comparar hora de modificação e tamanho. - -Identify equal files by comparing the file content. -Identificar ficheiros iguais ao comparar o conteúdo. - -Symbolic links: -Links simbólicos: - -More information -Mais informação - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Identificar e propagar mudanças nos dois lados. Eliminações, cópias e conflitos são detectados automaticamente usando base de dados. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Criar uma cópia exacta da pasta à esquerda, que vai corresponder à pasta da direita após a sincronização. - -Copy new and updated files to the right folder. -Copiar ficheiros novos e actualizados para a pasta à direita. - -Configure your own synchronization rules. -Configure as suas regras de sincronização. - -Detect moved files -Detectar ficheiros movidos - -Requires database files. Not supported by all file systems. -Requer base de dados. Não é suportado por todos os sistemas de ficheiros. - -Delete files: -Eliminar ficheiros: - -Permanent -Permanente - -Delete or overwrite files permanently -Apagar ou substituir ficheiros permanentemente - -Recycle bin -Reciclagem - -Back up deleted and overwritten files in the recycle bin -Fazer backup dos ficheiros apagados e substituidos na reciclagem - -Versioning -Manter versões anteriores - -Move files to a user-defined folder -Mover ficheiros para uma pasta definida pelo utilizador - -Naming convention: -Convenção de nomes: - -Show examples -Mostrar exemplos - -Handle errors: -Tratar erros: - -Ignore -Ignorar - -Hide all error and warning messages -Ocultar todas as mensagens de erro/aviso - -Pop-up -Pop-up - -Show pop-up on errors or warnings -Mostrar popup em caso de erros ou avisos - -On completion: -Ao completar: - -Start synchronization now? -Iniciar sincronização agora? - -Variant: -Variante: - -Statistics -Estatísticas - -&Don't show this dialog again -&Não mostrar este diálogo novamente - -Items found: -Elementos encontrados: - -Speed: -Velocidade: - -Time remaining: -Tempo restante: - -Time elapsed: -Tempo decorrido - -Synchronizing... -A sincronizar... - -Minimize to notification area -Minimizar para a área de notificação - -Close -Fechar - -&Pause -&Pausa - -Stop -Parar - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Criar ficheiro batch para sincronização automática. Para iniciar, fazer duplo clique no ficheiro ou acrescentar ao planeador de tarefas: %x - -Stop synchronization at first error -Para sincronização ao primeiro erro - -Show progress dialog -Mostrar diálogo de progresso - -Save log: -Guardar registo: - -Limit: -Limitar: - -Limit maximum number of log files -Limitar o número máximo de ficheiros log - -How can I schedule a batch job? -Como posso agendar um trabalho batch? - -&Recycle bin -&Reciclagem - -Delete on both sides -Eliminar em ambos os lados - -Delete on both sides even if the file is selected on one side only -Eliminar em ambos os lados mesmo se o ficheiro só está seleccionado num lado - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Seleccionar regras de filtro para excluir certos ficheiros da sincronização. Insira os caminhos relativos ao par de directórios correspondente. - -Include: -Incluir: - -Exclude: -Excluir: - -Time span: -Intervalo de tempo: - -File size: -Tamanho ficheiro: - -Minimum: -Mínimo: - -Maximum: -Máximo - -&Clear -&Limpar - -The following settings are used for all synchronization jobs. -As seguintes definições são usadas para todas as sincronizações. - -Fail-safe file copy -Cópia à prova de falhas - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -Copiar para um ficheiro temporário (*.ffs_tmp) antes de substituir. -Isto garante um estado consistente mesmo em caso de falha grave. - - -(recommended) -(recomendado) - -Copy locked files -Copiar ficheiros bloqueados - -Copy shared or locked files using the Volume Shadow Copy Service. -Copiar ficheiros partilhados ou bloqueados usando o serviço Volume Shadow Copy. - -(requires administrator rights) -(requer permissões de administrador) - -Copy file access permissions -Copiar permissões de acesso do ficheiro - -Transfer file and folder permissions. -Transferir permissões de pasta e ficheiro. - -Automatic retry on error: -Retentar automaticamente em caso de erro: - -Retry count: -Nº de tentativas: - -Delay (in seconds): -Atraso (em segundos): - -Customize context menu: -Personalizar menu de contexto: - -Description -Descrição - -Restore hidden windows -Restaurar janelas escondidas - -&Default -&Config. Iniciais - -Source code written in C++ using: -Código fonte escrito em C++ utilizando: - -If you like FreeFileSync -Se gosta do FreeFileSync - -Donate with PayPal -Doar usando PayPal - -Feedback and suggestions are welcome -Comentários e sugestões são apreciados - -Homepage -Site - -Email -Email - -Published under the GNU General Public License -Publicado sobre GNU General Public License - -Many thanks for localization: -Muito obrigado pela localização: - -Save as Batch Job -Guardar como Ficheiro Batch - -Delete Items -Eliminar Itens - -Global Settings -Opções Globais - -Select Time Span -Seleccionar Intervalo de Tempo - -Folder Pairs -Par de Pastas - -Find -Procurar - -Overview -Vista - -Configuration -Configuração - -Main Bar -Barra principal - -Filter Files -Filtrar Ficheiros - -Select View -Seleccionar Vista - -Open... -Abrir... - -Save -Guardar - -Compare both sides -Comparar listas - -Comparison settings -Opções de comparação - -Synchronization settings -Parametros de sincronização - -Start synchronization -Iniciar a sincronização - -Confirm -Confirmar - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - -Deseja mesmo executar o comando %y para um item? -Deseja mesmo executar o comando %y para %x itens? - - -&Execute -&Executar - - -1 directory -%x directories - - -1 directório -%x directórios - - - -1 file -%x files - - -1 ficheiro -%x ficheiros - - - -%y of 1 row in view -%y of %x rows in view - - -%y de 1 linha em vista -%y de %x linhas em vista - - -Set direction: -Escolher direcção: - -multiple selection -seleção múltipla - -Include via filter: -Incluir via filtro: - -Exclude via filter: -Excluir por filtro: - -Exclude temporarily -Excluir temporariamente - -Include temporarily -Incluir temporariamente - -Delete -Eliminar - -Include all -Incluir tudo - -Exclude all -Excluir tudo - -Show icons: -Mostrar ícones: - -Small -Pequeno - -Medium -Médio - -Large -Grande - -Select time span... -Selecione intervalo de tempo... - -Default view -Vista normal - -Show "%x" -Mostrar "%x" - -Last session -Última Sessão - -Folder Comparison and Synchronization -Comparação e Sincronização de Pastas - -Configuration saved -Configuração guardada - -FreeFileSync batch -FreeFileSync batch - -Do you want to save changes to %x? -Deseja guardar as alterações a %x? - -Never save &changes -Nunca guardar &alterações - -Do&n't save -Não g&uardar - -Filter -Filtro - -Show files that exist on left side only -Mostrar ficheiros existentes somente à esquerda - -Show files that exist on right side only -Mostrar ficheiros existentes somente à direita - -Show files that are newer on left -Mostrar ficheiros mais recentes à esquerda - -Show files that are newer on right -Mostrar ficheiros mais recentes à direita - -Show files that are equal -Mostrar ficheiros iguais - -Show files that are different -Mostrar ficheiros diferentes - -Show conflicts -Mostrar conflitos - -Show files that will be created on the left side -Mostrar ficheiros a ser criados à esquerda - -Show files that will be created on the right side -Mostrar ficheiros a ser criados à direita - -Show files that will be deleted on the left side -Mostrar ficheiros a ser apagados à esquerda - -Show files that will be deleted on the right side -Mostrar ficheiros a ser apagados à direita - -Show files that will be overwritten on left side -Mostrar ficheiros a ser substituidos do lado esquerdo - -Show files that will be overwritten on right side -Mostrar ficheiros a ser substituidos do lado direito - -Show files that won't be copied -Mostrar ficheiros que não serão copiados - -Set as default -Definir como padrão - -All folders are in sync -Todas as pastas estão sincronizadas - -Synchronization Settings -Definições de Sincronização - -Comparison Settings -Definições de Comparação - -Cannot find %x -Não é possível descobrir %x - -Comma-separated values -Valores separados por virgula - -File list exported -Lista dos ficheiros exportada - -Searching for program updates... -A procurar actualizações do programa... - -Scanning... -A pesquisar... - -Comparing content... -A comparar... - -Info -Info - -Warning -Atenção - -Paused -Em pausa - -Initializing... -A iniciar... - -Stopped -Parado - -Completed -Terminado - -&Continue -&Continuar - -Log -Registo - -Today -Hoje - -This week -Esta semana - -This month -Este mês - -This year -Este ano - -Last x days -Últimos x dias - -Byte -Byte - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Deseja mesmo mover o seguinte item para a reciclagem? -Deseja mesmo mover os seguintes %x itens para a reciclagem? - - -Move -Mover - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Deseja mesmo eliminar o seguinte item? -Deseja mesmo eliminar os seguintes %x itens? - - -Exclude -Excluir - -Direct -Direto - -Follow -Seguir - -Copy NTFS permissions -Copiar permissões NTFS - -Integrate external applications into context menu. The following macros are available: -Integrar aplicações externas no menu de contexto. As seguintes macros estão disponíveis: - -- full file or folder name -- nome completo de ficheiro ou pasta - -- folder part only -- nome da pasta parcial - -- Other side's counterpart to %item_path% -- Contrapartida de %item_path% - -- Other side's counterpart to %item_folder% -- Contrapartida de %item_folder% - -Restore all hidden windows and warnings? -Restaurar todas as janelas e avisos escondidos? - -Leave as unresolved conflict -Deixar como conflito - -Replace -Substituir - -Move files and replace if existing -Mover ficheiros e substituir se existirem - -Time stamp -Selo temporal - -Append a timestamp to each file name -Juntar selo temporal a cada nome de ficheiro - -File -Ficheiro - -YYYY-MM-DD hhmmss -AAAA-MM-DD hhmmss - -Files -Ficheiros - -Items -Itens - -Percentage -Percentagem - -Cannot monitor directory %x. -Não é possível monitorizar o directório %x. - -Conversion error: -Erro de conversão: - -Cannot delete file %x. -Não é possível eliminar o ficheiro %x. - -The file is locked by another process: -O ficheiro está bloqueado por outro processo: - -Cannot move file %x to %y. -Não é possível mover o ficheiro %x para %y. - -Cannot delete directory %x. -Não é possível eliminar o directório %x. - -Cannot write file attributes of %x. -Não é possível escrever os atributos de %x. - -Cannot write modification time of %x. -Não é possível alterar a data de modificação a %x. - -Cannot read security context of %x. -Não é possível ler o contexto de segurança %x. - -Cannot write security context of %x. -Não é possível escrever o contexto de segurança %x. - -Cannot read permissions of %x. -Não é possível ler as permissões de %x. - -Cannot write permissions of %x. -Não é possível escrever as permissões de %x. - -Cannot create directory %x. -Não é possível criar o directório %x. - -Cannot create symbolic link %x. -Não é possível criar link simbólico %x. - -Cannot find system function %x. -Não é possível encontrar a função do sistema %x. - -Cannot copy file %x to %y. -Não é possível copiar o ficheiro %x para %y. - -Type of item %x is not supported: -Tipo de item %x não é suportado: - -Cannot resolve symbolic link %x. -Não é possível resolver o link simbólico %x. - -Cannot open directory %x. -Não é possível abrir o directório %x. - -Cannot enumerate directory %x. -Não é possível enumerar o directório %x. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -1 min -%x mins - - - -1 hour -%x hours - - -1 hora -%x horas - - - -1 day -%x days - - -1 dia -%x dias - - -Unable to register to receive system messages. -Não foi possível registar para receber mensagens do sistema. - -Cannot set privilege %x. -Não é possível definir o privilégio %x. - -Unable to suspend system sleep mode. -Não é possível retornar do modo de suspensão do sistema. - -Cannot change process I/O priorities. -Não é possível alterar as prioridades de E / S do processo. - -Unable to move %x to the recycle bin. -Não é possível mover %x para a reciclagem. - -Cannot determine final path for %x. -Não é possível determinar o caminho final de %x. - -Error Code %x: -Código de erro %x: - diff --git a/BUILD/Languages/portuguese_br.lng b/BUILD/Languages/portuguese_br.lng deleted file mode 100644 index 56a5cb8d..00000000 --- a/BUILD/Languages/portuguese_br.lng +++ /dev/null @@ -1,1523 +0,0 @@ -
    - Português (BR) - Edison Aranha - pt_BR - flag_brazil.png - 2 - n == 1 ? 0 : 1 -
    - -Retrying operation... - - -Both sides have changed since last synchronization. -Ambos os lados foram alterados desde a última sincronização. - -Cannot determine sync-direction: -Não foi possível determinar a direção de sincronização: - -No change since last synchronization. -Nenhuma mudança desde a última sincronização. - -The database entry is not in sync considering current settings. -A entrada de banco de dados não está em sincronia considerando as configurações atuais. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Configurando direções padrões de sincronização: Arquivos antigos serão substituídos por arquivos mais novos. - -Checking recycle bin availability for folder %x... -Verificando a disponibilidade da lixeira para a pasta %x... - -Moving file %x to the recycle bin -Movendo arquivo %x para a Lixeira - -Moving folder %x to the recycle bin -Movendo pasta %x para a Lixeira - -Moving symbolic link %x to the recycle bin -Movendo link simbólico %x para a Lixeira - -Deleting file %x -Apagando arquivo %x - -Deleting folder %x -Apagando pasta %x - -Deleting symbolic link %x -Apagando link simbólico %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -A Lixeira não está disponível para as seguintes pastas. Os arquivos serão apagados permanentemente: - -An exception occurred -Ocorreu uma exceção - -A directory path is expected after %x. -Um caminho de diretório é esperado após %x. - -Syntax error -Erro de sintaxe - -Cannot open file %x. -Não foi possível abrir o arquivo %x. - -Error -Erro - -File %x does not contain a valid configuration. -O arquivo %x não contém uma configuração válida. - -Unequal number of left and right directories specified. -Número de diretórios especificados na esquerda e na direita são diferentes. - -The config file must not contain settings at directory pair level when directories are set via command line. -O arquivo de configuração não deve conter configurações de diretórios quando os diretórios são definidos via linha de comando. - -Warning -Avisos - -Directories cannot be set for more than one configuration file. -Os diretórios não podem ser definidos para mais de um arquivo de configuração. - -Syntax: -Sintaxe: - -config files -arquivos de configuração - -directory -diretório - -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Qualquer número de arquivos FreeFileSync .ffs e/ou de tarefa em lote .ffs_batch. - -Any number of alternative directories for at most one config file. -Qualquer número de alternativas de diretórios para pelo menos um arquivo de configuração. - -Command line -Linha de comando - -A folder input field is empty. -Um campo de entrada de pasta está vazio. - -The corresponding folder will be considered as empty. -A pasta correspondente será considerada como vazia. - -Cannot find the following folders: -Não foi possível localizar as seguintes pastas: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -Você pode ignorar este erro para considerar cada pasta como vazia. As pastas serão criadas automaticamentes durante a sincronização. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -As seguintes pastas tem caminhos dependentes. Cuidado na hora de estabelecer as regras de sincronização: - -File %x has an invalid date. -O arquivo %x tem uma data inválida. - -Date: -Data: - -Files %x have the same date but a different size. -Os arquivos %x têm a mesma data mas tamanhos diferentes. - -Size: -Tamanho: - -Items differ in attributes only -Os itens diferem apenas nos atributos - -Resolving symbolic link %x -Resolvendo link simbólico %x - -Comparing content of files %x -Comparando conteúdo do arquivo %x - -Generating file list... -Gerando lista de arquivos... - -Starting comparison -Iniciando comparação - -Calculating sync directions... -Calculando as direções de sincronização... - -Out of memory. -Falta de memória. - -Item exists on left side only -Item existe apenas no lado esquerdo - -Item exists on right side only -Item existe apenas no lado direito - -Left side is newer -Lado esquerdo é novo - -Right side is newer -Lado direito é novo - -Items have different content -Itens têm conteúdos diferentes - -Both sides are equal -Ambos os lados são iguais - -Conflict/item cannot be categorized -Conflito/item não pode ser categorizado - -Copy new item to left -Copiar novo item para a esquerda - -Copy new item to right -Copiar novo item para a direita - -Delete left item -Apagar item à esquerda - -Delete right item -Apagar item à direita - -Move file on left -Mover arquivo à esquerda - -Move file on right -Mover arquivo à direita - -Overwrite left item -Sobrescrever item à esquerda - -Overwrite right item -Sobrescrever item à direita - -Do nothing -Não fazer nada - -Update attributes on left -Atualizar atributos à esquerda - -Update attributes on right -Atualizar atributos à direita - -Database file %x is incompatible. -O arquivo de banco de dados %x é incompatível. - -Initial synchronization: -Sincronização inicial: - -Database file %x does not yet exist. -O arquivo de banco de dados %x ainda não existe. - -Database file is corrupt: -O arquivo de banco de dados está corrompido - -Cannot write file %x. -Não foi possível escrever o arquivo %x. - -Cannot read file %x. -Não foi possível ler o arquivo %x. - -Database files do not share a common session. -Os arquivos de banco de dados são de sessões diferentes. - -Searching for folder %x... -Buscando pela pasta %x... - -Cannot read file attributes of %x. -Não foi possível ler os atributos do arquivo %x. - -Cannot get process information. -Não foi possível obter as informações do processo. - -Waiting while directory is locked (%x)... -Esperando enquanto o diretório é bloqueado (%x)... - - -1 sec -%x sec - - -1 seg -%x segs - - -Creating file %x -Criando arquivo %x - -Items processed: -Elementos processados: - -Items remaining: -Elementos restantes: - -Total time: -Tempo total: - - -1 byte -%x bytes - - -1 byte -%x bytes - - -%x MB -%x MB - -%x KB -%x kB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Erro analisando o arquivo %x, linha %y, coluna %z. - -Cannot set directory lock for %x. -Não foi possível travar o diretório %x. - -Scanning: -Pesquisando: - - -1 thread -%x threads - - -1 thread -%x threads - - -Encoding extended time information: %x -Codificando informações adicionais de tempo: %x - -/sec -/seg - -%x items/sec -%x itens/seg - -Configuration file %x loaded partially only. -O arquivo de configuração %x foi carregado parcialmente. - -Show in Explorer -Mostrar no Explorer - -Open with default application -Abrir com aplicativo padrão - -Browse directory -Procurar diretório - -Cannot access the Volume Shadow Copy Service. -Não foi possível acessar o Serviço de Cópia de Sombra de Volume. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Por favor, utilize a versão 64 bits do FreeFileSync para criar cópias de sombra neste sistema. - -Cannot load file %x. -Não foi possível carregar o aquivo %x. - -Cannot determine volume name for %x. -Não foi possível determinar o nome do volume para %x. - -Volume name %x is not part of file path %y. -O nome do volume %x não faz parte do caminho do arquivo %y. - -Stop requested: Waiting for current operation to finish... -Interromper solicitado: Aguardando operação ser finalizada... - -Unable to create timestamp for versioning: -Erro ao criar estampa de tempo para controle de versão: - -Cannot read the following XML elements: -Não foi possível ler os seguintes elementos XML: - -&Open... -&Abrir... - -Save &as... -Salvar &como... - -&Quit -Sai&r - -&Program -&Programa - -&View help -&Ver ajuda - -&About -&Sobre - -&Help -A&juda - -Usage: -Uso: - -1. Select folders to watch. -1. Selecione as pastas para monitorar. - -2. Enter a command line. -2. Entre uma linha de comando. - -3. Press 'Start'. -3. Pressione 'Iniciar'. - -To get started just import a .ffs_batch file. -Para iniciar importe um arquivo .ffs_batch. - -Folders to watch: -Pastas para comparar - -Add folder -Adicionar pasta - -Remove folder -Remover pasta - -Browse -Procurar - -Select a folder -Selecionar uma pasta - -Idle time (in seconds): -Tempo de espera (em segundos): - -Idle time between last detected change and execution of command -Tempo de espera entre última mudança detectada e execução do comando - -Command line: -Linha de comando: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -O comando é disparado se: -- arquivos ou subpastas são alterados -- novas pastas aparecem (p. ex. Uma pendrive é inserida) - - -&Start -&Iniciar - -&Retry -&Tentar Novamente - -Cancel -Cancelar - -Build: %x -Versão: %x - -About -Sobre - -All files -Todos os arquivos - -Automated Synchronization -Sincronização Automatizada - -Directory monitoring active -Monitoramento de diretórios ativado - -Waiting until all directories are available... -Aguardando até todos os diretórios estarem disponíveis... - -&Restore -&Restaurar - -&Show error -&Mostrar erro - -&Exit -&Sair - -Incorrect command line: -Linha de comando incorreta: - -File content -Conteúdo do arquivo - -File time and size -Data e tamanho do arquivo - -Two way -Dois sentidos - -Mirror -Espelhar - -Update -Atualizar - -Custom -Personalizado - -Multiple... -Múltiplo... - -Moving file %x to %y -Movendo arquivo %x para %y - -Moving folder %x to %y -Movendo pasta %x para %y - -Moving symbolic link %x to %y -Movendo link simbólico %x para %y - -Removing old versions... -Removendo versões antigas... - -Creating symbolic link %x -Criando link simbólico %x - -Creating folder %x -Criando pasta %x - -Overwriting file %x -Substituindo arquivo %x - -Overwriting symbolic link %x -Substituindo link simbólico %x - -Verifying file %x -Verificando arquivo %x - -Updating attributes of %x -Atualizando atributos de %x - -Cannot find %x. -Não foi possível localizar %x. - -Target folder %x already existing. -Pasta de destino %x já existe. - -Target folder input field must not be empty. -Campo de entrada da pasta de destino não pode ficar vazio. - -Please enter a target folder for versioning. -Por favor, entre com uma pasta destino para controle de versões. - -Source folder %x not found. -Pasta de origem %x não foi encontrada. - -The following items have unresolved conflicts and will not be synchronized: -Os seguintes itens possuem conflitos não resolvidos e não serão sincronizados: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -As seguintes pastas são significativamente diferentes. Tenha certeza que está comparando as pastas corretas para sincronização. - -Not enough free disk space available in: -Espaço em disco insuficiente em: - -Required: -Necessário: - -Available: -Disponível: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Uma pasta que é parte de múltiplos pares de pasta será modificada. Por favor, revise as configurações de sincronização. - -Synchronizing folder pair: -Sincronizando par de pastas: - -Generating database... -Gerando banco de dados... - -Creating a Volume Shadow Copy for %x... -Criando uma Cópia de Sombra de Volume para %x... - -Data verification error: %x and %y have different content. -Erro na verificação dos dados: %x e %y têm conteúdo diferente. - -job name -nome da tarefa - -Synchronization stopped -Sincronização interrompida - -Synchronization completed with errors -Sincronização finalizada com erros - -Synchronization completed with warnings -Sincronização finalizada com avisos - -Nothing to synchronize -Nada para sincronizar - -Synchronization completed successfully -Sincronização finalizada com sucesso - -Saving log file %x... -Salvando arquivo de log %x... - -You can switch to FreeFileSync's main window to resolve this issue. -Você pode alternar para a janela principal do FreeFileSync para resolver este problema. - -Switching to FreeFileSync's main window -Mudando para a janela principal do FreeFileSync - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - -Nova tentativa autompatica em 1 segundo... -Nova tentativa autompatica em %x segundos... - - -A new version of FreeFileSync is available: -Uma nova versão do FreeFileSync está disponível: - -Download now? -Baixar agora? - -Check for Program Updates -Verificar se existem atualizações - -&Download -&Baixar - -FreeFileSync is up to date. -FreeFileSync está atualizado. - -Information -Informação - -Unable to connect to sourceforge.net. -Não foi possível conectar a sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Não foi possível encontrar a versão atual do FreeFileSync online. Deseja verificar manualmente? - -Symlink -Link Simbólico - -Folder -Pasta - -Full path -Caminho completo - -Name -Nome - -Relative path -Caminho relativo - -Base folder -Pasta base - -Size -Tamanho - -Date -Data - -Extension -Extensão - -Category -Categoria - -Action -Ação - -Drag && drop -Arrastar && Soltar - -Close progress dialog -Fechar indicador de progresso - -Standby -Em espera - -Log off -Fazer logoff - -Shut down -Desligar - -Hibernate -Hibernar - -Alternate comparison settings -Configurações alternativas de comparação - -Alternate synchronization settings -Configurações alternativas de sincronização - -Local filter -Filtro local - -Active -Ativo - -None -Nenhum - -Remove alternate settings -Remover configurações alternativas - -Clear filter settings -Limpar configurações do filtro - -Copy -Copiar - -Paste -Colar - -Alternate Comparison Settings -Configurações Alternativas de Comparação - -Alternate Synchronization Settings -Configurações Alternativas de Sincronização - -Local Filter -Filtro Local - -&New -&Novo - -&Save -&Salvar - -Save as &batch job... -Salvar como &tarefa em lote... - -1. &Compare -1. C&omparar - -2. &Synchronize -2. S&incronizar - -&Global settings -&Configurações - -&Language -&Idioma - -&Find... -&Localizar... - -&Export file list... -&Exportar lista de arquivos... - -&Tools -&Ferramentas - -&Check now -&Verificar agora - -Check &automatically once a week -Verificar &automaticamente uma vez por semana - -&Check for new version -&Verificar se existe nova versão - -Compare -Comparar - -Synchronize -Sincronizar - -Add folder pair -Adicionar um par de pastas - -Remove folder pair -Remover o par de pastas - -Swap sides -Inverter lados - -Close search bar -Fechar barra de localização - -Find: -Localizar: - -Match case -Diferenciar maiúsculas e minúsculas - -Save as batch job -Salvar como tarefa em lote - -Hide excluded items -Ocultar itens excluídos - -Show filtered or temporarily excluded files -Mostra arquivos que foram filtrados ou excluídos temporariamente - -Number of files and folders that will be created -Número de arquivos e pastas que serão criados - -Number of files that will be overwritten -Número de arquivos que serão substituídos - -Number of files and folders that will be deleted -Número de arquivos e pastas que serão apagados - -Total bytes to copy -Bytes totais a serem copiados - -Select a variant: -Selecione uma variante: - -Identify equal files by comparing modification time and size. -Identifica arquivos iguais comparando modificações na data e no tamanho. - -Identify equal files by comparing the file content. -Identifica arquivos iguais comparando o conteúdo do arquivo. - -Symbolic links: -Links simbólicos: - -More information -Mais informação - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Identifica e propaga as mudanças em ambos os lados. Arquivos e pastas apagados e/ou movidos e conflitos são detectados automaticamente usando um banco de dados. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Cria uma cópia espelho da pasta da esquerda que é exatamente igual à pasta da esquerda após a sincronização. - -Copy new and updated files to the right folder. -Copia arquivos novos ou atualizados para a pasta da direita. - -Configure your own synchronization rules. -Configure as suas próprias regras de sincronização. - -Detect moved files -Detectar arquivos movidos - -Requires database files. Not supported by all file systems. -Requer um arquivo de banco de dados. Não é suportado por todos os sistemas de arquivos. - -Delete files: -Apagar arquivos: - -Permanent -Permanente - -Delete or overwrite files permanently -Apaga ou substitui os arquivos permanentemente - -Recycle bin -Lixeira - -Back up deleted and overwritten files in the recycle bin -Faz uma cópia dos arquivos apagados ou substituídos na Lixeira - -Versioning -Controle de versões - -Move files to a user-defined folder -Move os arquivos para uma pasta definida pelo usuário - -Naming convention: -Convenção de nomenclatura: - -Show examples -Mostrar exemplos - -Handle errors: -Tratamento de erros: - -Ignore -Ignorar - -Hide all error and warning messages -Oculta todas as mensagens de erros e avisos - -Pop-up -Pop-up - -Show pop-up on errors or warnings -Mostra pop-up em caso de erros ou avisos - -On completion: -Ao finalizar: - -Start synchronization now? -Iniciar sincronização agora? - -Variant: -Variante: - -Statistics -Estatísticas - -&Don't show this dialog again -&Não mostrar este diálogo novamente - -Items found: -Elementos encontrados: - -Speed: -Velocidade: - -Time remaining: -Tempo restante: - -Time elapsed: -Tempo decorrido: - -Synchronizing... -Sincronizando... - -Minimize to notification area -Minimizar para a área de notificação - -Close -Fechar - -&Pause -&Pausar - -Stop -Interromper - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Cria um arquivo de tarefa em lote para sincronização desatendida. Para iniciar, dê um clique duplo neste arquivo ou agende no agendador de tarefas: %x - -Stop synchronization at first error -Interromper sincronização ao primeiro erro - -Show progress dialog -Mostrar indicador de progresso - -Save log: -Salvar arquivo de log: - -Limit: -Limite: - -Limit maximum number of log files -Limita número máximo de arquivos de log - -How can I schedule a batch job? -Como posso agendar uma tarefa em lote? - -&Recycle bin -&Lixeira - -Delete on both sides -Apagar em ambos os lados - -Delete on both sides even if the file is selected on one side only -Apaga em ambos os lados mesmo se o arquivo está selecionado só em um lado - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Selecione regras de filtro para exlcuir certos arquivos da sincrinização. Entre com os caminhos relativos dos pares de pastas correspondentes. - -Include: -Incluir: - -Exclude: -Excluir: - -Time span: -Intervalo de tempo: - -File size: -Tamanho do arquivo: - -Minimum: -Mínimo: - -Maximum: -Máximo: - -&Clear -&Limpar - -The following settings are used for all synchronization jobs. -As seguintes configurações são utilizadas para todas as tarefas de sincronização. - -Fail-safe file copy -Cópia de arquivos a prova de falhas - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -Copia para um arquivo temporário (*.ffs_tmp) antes de substituir o destino. -Isto garante um estado consistente mesmo em caso de erro grave. - - -(recommended) -(recomendado) - -Copy locked files -Copiar arquivos bloqueados (em uso) - -Copy shared or locked files using the Volume Shadow Copy Service. -Copia arquivos compartilhados ou bloqueados usando o Serviço de Cópia de Sombra de Volume. - -(requires administrator rights) -(requer direitos de administrador) - -Copy file access permissions -Copiar permissões de acesso aos arquivos - -Transfer file and folder permissions. -Transfere permissões de arquivos e pastas. - -Automatic retry on error: -Nova tentativa automática em caso de erro: - -Retry count: -Número de tentativas: - -Delay (in seconds): -Atraso (em segundos): - -Customize context menu: -Personalizar menu de contexto: - -Description -Descrição - -Restore hidden windows -Restaurar janelas ocultadas - -&Default -&Config. Padrão - -Source code written in C++ using: -Código-fonte escrito em C++ utilizando: - -If you like FreeFileSync -Se você gosta do FreeFileSync - -Donate with PayPal -Doar usando PayPal - -Feedback and suggestions are welcome -Críticas e sugestões são bem-vindas - -Homepage -Homepage - -Email -E-mail - -Published under the GNU General Public License -Publicado sobre a GNU General Public License - -Many thanks for localization: -Pela tradução, um agradecimento a: - -Save as Batch Job -Salvar como Tarefa em Lote - -Delete Items -Apagar Itens - -Global Settings -Configurações - -Select Time Span -Selecionar Intervalo de Tempo - -Folder Pairs -Pares de Pastas - -Find -Localizar - -Overview -Parâmetros - -Configuration -Configuração - -Main Bar -Barra Principal - -Filter Files -Filtrar Arquivos - -Select View -Selecionar Visualização - -Open... -Abrir... - -Save -Salvar - -Compare both sides -Comparar os dois lados - -Comparison settings -Parâmetros de comparação - -Synchronization settings -Parâmetros de sincronização - -Start synchronization -Iniciar a sincronização - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - -Você realmente deseja executar o comando %y para um item? -Você realmente deseja executar o comando %y para %x itens? - - -Confirm -Confirmar - -&Execute -&Executar - - -1 directory -%x directories - - -1 diretório -%x diretórios - - - -1 file -%x files - - -1 arquivo -%x arquivos - - - -%y of 1 row in view -%y of %x rows in view - - -%y de 1 linha -%y de %x linhas - - -Set direction: -Configurar direção - -multiple selection -seleção múltipla - -Include via filter: -Incluir via filtro: - -Exclude via filter: -Excluir via filtro: - -Exclude temporarily -Excluir temporariamente - -Include temporarily -Incluir temporariamente - -Delete -Apagar - -Include all -Incluir todos - -Exclude all -Excluir todos - -Show icons: -Mostrar ícones: - -Small -Pequeno - -Medium -Médio - -Large -Grande - -Select time span... -Selecionar intervalo de tempo... - -Default view -Visualização padrão - -Show "%x" -Mostrar "%x" - -Last session -Última sessão - -Folder Comparison and Synchronization -Comparação e Sincronização de Pastas - -Configuration saved -Configuração salva - -FreeFileSync batch -Tarefa em lote do FreeFileSync - -Do you want to save changes to %x? -Gostaria de salvar as alterações para %x? - -Do&n't save -&Não salvar - -Never save &changes -Nunca salvar as &modificações - -Filter -Filtro - -Show files that exist on left side only -Mostrar arquivos que existem somente à esquerda - -Show files that exist on right side only -Mostrar arquivos que existem somente à direita - -Show files that are newer on left -Mostrar arquivos que são mais recentes à esquerda - -Show files that are newer on right -Mostrar arquivos que são mais recentes à direita - -Show files that are equal -Mostrar arquivos que são iguais - -Show files that are different -Mostrar arquivos que são diferentes - -Show conflicts -Mostrar conflitos - -Show files that will be created on the left side -Mostrar arquivos que serão criados no lado esquerdo - -Show files that will be created on the right side -Mostrar arquivos que serão criados no lado direito - -Show files that will be deleted on the left side -Mostrar arquivos que serão apagados no lado esquerdo - -Show files that will be deleted on the right side -Mostrar arquivos que serão apagados no lado direito - -Show files that will be overwritten on left side -Mostrar arquivos que serão substituídos no lado esquerdo - -Show files that will be overwritten on right side -Mostrar arquivos que serão substituídos no lado direito - -Show files that won't be copied -Mostrar arquivos que não serão copiados - -Set as default -Definir como padrão - -All folders are in sync -Todas as pastas estão sincronizadas - -Synchronization Settings -Configurações de Sincronização - -Comparison Settings -Configurações de Comparação - -Cannot find %x -Não foi possível localizar %x - -Comma-separated values -Valores separados por vírgula - -File list exported -Lista de arquivos exportada - -Searching for program updates... -Procurando atualizações do programa... - -&Ignore subsequent errors -&Ignorar erros subsequentes - -&Ignore -&Ignorar - -Serious Error -Erro Grave - -&Don't show this warning again -&Não mostrar este aviso novamente - -&Switch -&Alterar - -&Yes -&Sim - -&No -&Não - -Scanning... -Pesquisando... - -Comparing content... -Comparando conteúdo... - -Info -Informações - -Paused -Pausado - -Initializing... -Inicializando... - -Stopped -Interrompido - -Completed -Finalizado - -&Continue -&Continuar - -Log -Log - -Today -Hoje - -This week -Esta semana - -This month -Este mês - -This year -Este ano - -Last x days -Últimos x dias - -Byte -Byte - -KB -kB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Você realmente deseja mover o seguinte item para a Lixeira? -Você realmente deseja mover os seguintes %x itens para a Lixeira? - - -Move -Mover - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Você realmente deseja apagar o seguinte item? -Você realmente deseja apagar os seguintes %x itens? - - -Exclude -Excluir - -Direct -Direto - -Follow -Seguir - -Copy NTFS permissions -Copiar permissões NTFS - -Integrate external applications into context menu. The following macros are available: -Integrar aplicações externas no menu de contexto. As seguintes macros estão disponíveis: - -- full file or folder name -- nome completo do arquivo ou pasta - -- folder part only -- apenas parte da pasta - -- Other side's counterpart to %item_path% -- Contrapartida do outro lado para %item_path% - -- Other side's counterpart to %item_folder% -- Contrapartida do outro lado para %item_folder% - -Restore all hidden windows and warnings? -Restaurar todos as janelas e avisos ocultados? - -Leave as unresolved conflict -Deixar como conflito não resolvido - -Replace -Substituir - -Move files and replace if existing -Move arquivos e substitui se existente - -Time stamp -Estampa de tempo - -Append a timestamp to each file name -Atribuir uma estampa de tempo para cada nome de arquivo - -File -Arquivo - -YYYY-MM-DD hhmmss -AAAA-MM-DD hhmmss - -Files -Arquivos - -Items -Itens - -Percentage -Percentual - -Cannot monitor directory %x. -Não foi possível monitorar o diretório %x. - -Conversion error: -Erro de conversão: - -Cannot delete file %x. -Não foi possível apagar o arquivo %x. - -The file is locked by another process: -O arquivo está bloqueado por outro processo: - -Cannot move file %x to %y. -Não foi possível mover o arquivo %x para %y. - -Cannot delete directory %x. -Não foi possível apagar o diretório %x. - -Cannot write file attributes of %x. -Não foi possível escrever os atributos de arquivo de %x. - -Cannot write modification time of %x. -Não foi possível escrever a data de modificação de %x. - -Cannot read security context of %x. -Não foi possível ler o contexto de segurança de %x. - -Cannot write security context of %x. -Não foi possível escrever o contexto de segurança de %x. - -Cannot read permissions of %x. -Não foi possível ler as permissões de %x. - -Cannot write permissions of %x. -Não foi possível escrever as permissões de %x. - -Cannot create directory %x. -Não foi possível criar o diretório %x. - -Cannot create symbolic link %x. -Não foi possível criar o link simbólico %x. - -Cannot find system function %x. -Não foi possível localizar a função de sistema %x. - -Cannot copy file %x to %y. -Não foi possível copiar o arquivo %x para %y. - -Type of item %x is not supported: -Tipo de item %x não é suportado: - -Cannot resolve symbolic link %x. -Não foi possível resolver o link simbólico %x. - -Cannot open directory %x. -Não foi possível abrir o diretório %x. - -Cannot enumerate directory %x. -Não foi possível enumerar o diretório %x. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -1 min -%x min - - - -1 hour -%x hours - - -1 hora -%x horas - - - -1 day -%x days - - -1 dia -%x dias - - -Unable to register to receive system messages. -Não foi possível registrar para receber mensagens do sistema. - -Cannot set privilege %x. -Não foi possível estabelecer o privilégio %x. - -Unable to suspend system sleep mode. -Não foi possível suspender o modo de espera do sistema. - -Cannot change process I/O priorities. -Não foi possível mudar a prioridade de E/S do processo. - -Unable to move %x to the recycle bin. -Não foi possível mover %x para a Lixeira. - -Cannot determine final path for %x. -Não foi possível determinar o caminho final para %x. - -Error Code %x: -Código do Erro %x: - diff --git a/BUILD/Languages/romanian.lng b/BUILD/Languages/romanian.lng deleted file mode 100644 index b16b7db7..00000000 --- a/BUILD/Languages/romanian.lng +++ /dev/null @@ -1,1521 +0,0 @@ -
    - Română - Alexandru Bogdan Munteanu - ro_RO - flag_romania.png - 3 - n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2 -
    - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -&Check - - -Retrying operation... - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Both sides have changed since last synchronization. -Ambele părți s-au modificat de la ultima sincronizare. - -Cannot determine sync-direction: -Nu pot determina sensul de sincronizare: - -No change since last synchronization. -Nu sînt schimbări de la ultima sincronizare. - -The database entry is not in sync considering current settings. -Intrarea în baza de date nu e sincronizată, ținînd cont de setările curente. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Baza de date existentă va fi făcută compatibilă cu versiunea softului și apoi va fi setat sensul implicit de sincronizare: Filele vechi vor fi suprascrise de cele noi. - -Checking recycle bin availability for folder %x... -Verific dacă Reciclatorul e disponibil pentru dosarul %x... - -Moving file %x to the recycle bin -Mut fila %x în Reciclator - -Moving folder %x to the recycle bin -Mut dosarul %x în Reciclator - -Moving symbolic link %x to the recycle bin -Mut legătura simbolică %x în Reciclator - -Deleting file %x -Șterg fila %x - -Deleting folder %x -Șterg dosarul %x - -Deleting symbolic link %x -Șterg legătura simbolică %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -Reciclatorul nu este disponibil pentru dosarele următoare. Filele șterse de acolo vor dispărea în mod definitiv: - -An exception occurred -A apărut o excepție - -A directory path is expected after %x. -Este așteptată o cale de dosar după %x. - -Syntax error -Eroare de sintaxă - -Cannot open file %x. -Nu pot deschide fila %x. - -File %x does not contain a valid configuration. -Fila %x nu conține o configurație validă. - -Unequal number of left and right directories specified. -Numărul de dosare specificat pentru părțile stîngă și dreaptă este inegal. - -The config file must not contain settings at directory pair level when directories are set via command line. -Fila de configurare nu trebuie să conțină setări la nivelul perechii de dosare, cînd dosarele sînt setate prin linia de comandă. - -Directories cannot be set for more than one configuration file. -Dosarele nu pot fi setate pentru mai mult de o singură filă de configurare. - -Command line -Linie de comandă - -Syntax: -Sintaxă: - -config files -file de configurare - -directory -dosar - -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 directories for at most one config file. -Orice număr de dosare alternative pentru cel mult o filă de configurare. - -A folder input field is empty. -Un cîmp de introducere a dosarului este gol. - -The corresponding folder will be considered as empty. -Dosarul corespondent va fi considerat ca fiind gol. - -Cannot find the following folders: -Nu pot găsi dosarele următoare: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -Poți ignora această eroare dacă vrei ca ambele dosare să fie considerate goale. Dosarele vor fi apoi create automat în timpul sincronizării. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -Dosarele următoare au căi dependente. Atenție la setarea regulilor de sincronizare: - -File %x has an invalid date. -Fila %x are o dată nevalidă. - -Date: -Dată: - -Files %x have the same date but a different size. -Filele %x au dată identică, dar mărime diferită. - -Size: -Mărime: - -Items differ in attributes only -Elementele diferă doar prin atributele lor - -Resolving symbolic link %x -Rezolv legătura simbolică %x - -Comparing content of files %x -Compar conținutul filelor %x - -Generating file list... -Generez lista de file... - -Starting comparison -Pornesc compararea - -Calculating sync directions... -Calculez acțiunile de sincronizare... - -Out of memory. -Memorie epuizată. - -Item exists on left side only -Elementul există doar în partea stîngă - -Item exists on right side only -Elementul există doar în partea dreaptă - -Left side is newer -Elementul din partea stîngă e mai nou - -Right side is newer -Elementul din partea dreaptă e mai nou - -Items have different content -Elementele au conținut diferit - -Both sides are equal -Ambele părți sînt identice - -Conflict/item cannot be categorized -Conflictul/elementul nu poate fi inclus într-o anumită categorie - -Copy new item to left -Copiază elementul nou în stînga - -Copy new item to right -Copiază elementul nou în dreapta - -Delete left item -Șterge elementul stîng - -Delete right item -Șterge elementul drept - -Move file on left -Mută fila în stînga - -Move file on right -Mută fila în dreapta - -Overwrite left item -Suprascrie elementul stîng cu cel drept - -Overwrite right item -Suprascrie elementul drept cu cel stîng - -Do nothing -Nici o acțiune - -Update attributes on left -Actualizează atributele în partea stîngă - -Update attributes on right -Actualizează atributele în partea dreaptă - -Database file %x is incompatible. -Fila cu baza de date %x este incompatibilă. - -Initial synchronization: -Sincronizare inițială: - -Database file %x does not yet exist. -Fila cu baza de date %x nu există încă. - -Database file is corrupt: -Fila bazei de date este stricată (coruptă) - -Cannot write file %x. -Nu pot scrie fila %x. - -Cannot read file %x. -Nu pot citi fila %x. - -Database files do not share a common session. -Filele cu baze de date nu partajează o sesiune comună. - -Searching for folder %x... -Caut dosarul %x... - -Cannot read file attributes of %x. -Nu pot citi atributele filei %x. - -Cannot get process information. -Nu pot obține informații despre proces. - -Waiting while directory is locked (%x)... -Aștept ca dosarul să fie zăvorît (%x)... - - -1 sec -%x sec - - -1 sec -%x sec -%x de sec - - -Creating file %x -Creez fila %x - -Items processed: -Elemente Procesate: - -Items remaining: -Elemente Rămase: - -Total time: -Timp Total: - - -1 byte -%x bytes - - -1 bait -%x baiți -%x de baiți - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Eroare la parsarea filei %x, rîndul %y, coloana %z. - -Cannot set directory lock for %x. -Nu pot face zăvorîrea dosarului %x. - -Scanning: -Scanez: - - -1 thread -%x threads - - -1 fir -%x fire -%x de fire - - -Encoding extended time information: %x -Codez informațiile extinse despre timp: %x - -/sec -/sec - -%x items/sec -%x itemuri/sec - -Configuration file %x loaded partially only. -Fila de configurație %x a fost deschisă doar parțial. - -Show in Explorer -Arată în Exploratorul de File - -Open with default application -Deschide cu Aplicația Implicită - -Browse directory -Explorează Dosarul - -Cannot access the Volume Shadow Copy Service. -Nu pot accesa Serviciul de Conservare a Volumelor [Volume Shadow Copy]. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Folosește versiunea pe 64-biți a FreeFileSync pentru a crea conservări (copii de rezervă) pe acest sistem. - -Cannot load file %x. -Nu pot deschide fila %x. - -Cannot determine volume name for %x. -Nu pot determina numele volumului pentru %x. - -Volume name %x is not part of file path %y. -Numele volumului %x nu face parte din calea filei %y. - -Stop requested: Waiting for current operation to finish... -Oprire solicitată: Aștept terminarea operației în curs... - -Unable to create timestamp for versioning: -Nu pot crea marcajul de timp pentru versionare: - -Cannot read the following XML elements: -Nu pot citi următoarele elemente XML: - -&Open... -&Deschide... - -Save &as... -Salvează c&a... - -&Quit -&Ieși - -&Program -&Program - -&View help -&Cuprins - -&About -&Despre - -&Help -&Ajutor - -Usage: -Utilizare: - -1. Select folders to watch. -1. Selectează dosarele de monitorizat. - -2. Enter a command line. -2. Scrie calea. - -3. Press 'Start'. -3. Apasă 'Pornește'. - -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: - -Add folder -Adaugă Dosar - -Remove folder -Înlătură Dosarul - -Browse -Explorează - -Select a folder -Selectează un Dosar - -Idle time (in seconds): -Timp de inactivitate (în secunde): - -Idle time between last detected change and execution of command -Timpul de inactivitate între ultima schimbare detectată și executarea comenzii - -Command line: -Linie de comandă: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -Comanda este declanșată dacă: -- sînt modificate filele sau subdosarele -- apar dosare noi (de ex. prin atașarea unui dispozitiv USB) - - -&Start -&Pornește - -&Retry -&Reîncearcă - -Cancel -Anulează - -About -Despre - -Build: %x -Compilația: %x - -All files -Toate Filele - -Automated Synchronization -Sincronizare Automată - -Directory monitoring active -Monitorizarea dosarelor este activă - -Waiting until all directories are available... -Aștept pînă cînd toate dosarele vor fi disponibile... - -Error -Eroare - -&Restore -&Restaurează - -&Show error -A&rată eroarea - -&Exit -&Ieși - -Incorrect command line: -Linie de comandă incorectă: - -File content -Conținutul Filelor - -File time and size -Timpul și Mărimea Filelor - -Two way -Ambele Sensuri - -Mirror -Clonare - -Update -Actualizare - -Custom -Personalizat - -Multiple... -Multiplu... - -Moving file %x to %y -Mut fila %x în %y - -Moving folder %x to %y -Mut dosarul %x în %y - -Moving symbolic link %x to %y -Mut legătura simbolică %x în %y - -Removing old versions... -Înlătur versiunile vechi... - -Creating symbolic link %x -Creez legătura simbolică %x - -Creating folder %x -Creez dosarul %x - -Overwriting file %x -Suprascriu fila %x - -Overwriting symbolic link %x -Suprascriu legătura simbolică %x - -Verifying file %x -Verific fila %x - -Updating attributes of %x -Actualizez atributele lui %x - -Cannot find %x. -Nu pot găsi %x. - -Target folder %x already existing. -Dosarul țintă %x există deja. - -Target folder input field must not be empty. -Cîmpul de introducere a dosarului țintă nu trebuie să fie gol. - -Please enter a target folder for versioning. -Introdu dosarul țintă pentru versionare. - -Source folder %x not found. -Dosarul sursă %x nu a fost găsit. - -The following items have unresolved conflicts and will not be synchronized: -Există conflicte nerezolvate la elementele listate mai jos, deci ele nu vor fi sincronizate: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -Dosarele următoare diferă foarte mult. Asigură-te că ai potrivit pentru sincronizare dosarele corecte. - -Not enough free disk space available in: -Spațiu de stocare insuficient pe: - -Required: -Necesar: - -Available: -Disponibil: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Va fi modificat un dosar care face parte din mai multe perechi de dosare. Reverifică setările de sincronizare. - -Synchronizing folder pair: -Sincronizez perechea de dosare: - -Generating database... -Generez baza de date... - -Creating a Volume Shadow Copy for %x... -Creez o Conservare a Volumului [Volume Shadow Copy] pentru %x... - -Data verification error: %x and %y have different content. -Eroare la verificarea datelor: %x și %y au conținut diferit. - -job name -numele sarcinii - -Synchronization stopped -Sincronizare oprită - -Synchronization completed with errors -Sincronizare terminată cu erori - -Synchronization completed with warnings -Sincronizare realizată, dar cu avertismente - -Nothing to synchronize -Nu e nimic de sincronizat - -Synchronization completed successfully -Sincronizare realizată cu succes - -Saving log file %x... -Salvez fila jurnal %x... - -You can switch to FreeFileSync's main window to resolve this issue. -Poți comuta la fereastra principală FreeFileSync pentru a rezolva această problemă. - -&Don't show this warning again -&Nu arăta din nou această atenționare - -&Ignore -&OK - -&Switch -&Comută - -Switching to FreeFileSync's main window -Comut la fereastra principală FreeFileSync - -Serious Error -Eroare Serioasă - -&Ignore subsequent errors -&Ignoră erorile ulterioare - -Check for Program Updates -Caută Actualizări ale Programului - -A new version of FreeFileSync is available: -Este disponibilă o versiune nouă a softului: - -Download now? -Vrei s-o descarci acum ? - -&Download -&Descarcă - -FreeFileSync is up to date. -Ai deja ultima versiune a softului. - -Unable to connect to sourceforge.net. -Conectarea la situl sourceforge.net nu poate fi realizată. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Nu pot afla numărul versiunii FreeFileSync disponibile acum pe internet. Vrei să-l cauți manual? - -Symlink -Simlegătură - -Folder -Dosar - -Full path -Cale Completă - -Name -Nume - -Relative path -Cale Relativă - -Base folder -Dosar Bază (Rădăcină) - -Size -Mărime - -Date -Dată - -Extension -Extensie - -Category -Categorie - -Action -Acțiune - -Drag && drop -Trage și pune un dosar mai jos sau explorează către el - -Close progress dialog -Închide Fereastra cu Progresul Sincronizării - -Standby -Pune Calculatorul în Stare de Veghe [Stand-by] - -Log off -Deloghează Utilizatorul [Log off] - -Shut down -Închide Calculatorul [Shut down] - -Hibernate -Pune Calculatorul în Hibernare - -Alternate comparison settings -Setări alternative de comparare - -Alternate synchronization settings -Setări alternative de sincronizare - -Local filter -Filtru local - -Active -Activ - -None -Nimic - -Remove alternate settings -Înlătură setările alternative - -Clear filter settings -Curăță Setările Filtrului - -Copy -Copiază - -Paste -Lipește - -Alternate Comparison Settings -Setări Alternative de Comparare - -Alternate Synchronization Settings -Setări Alternative de Sincronizare - -Local Filter -Filtru Local - -&New -Configurație &Nouă - -&Save -&Salvează - -Save as &batch job... -Salvea&ză ca Sarcină Set... - -1. &Compare -1. &Compară - -2. &Synchronize -2. &Sincronizează - -&Global settings -&Setări Globale - -&Language -&Limbă - -&Find... -&Găsește... - -&Export file list... -&Exportă Lista de File... - -&Tools -&Unelte - -&Check now -&Caută Acum - -Check &automatically once a week -Caută &Automat în Fiecare Săptămînă - -&Check for new version -Caută &Versiune Nouă - -Compare -Compară - -Synchronize -Sincronizează - -Add folder pair -Adaugă Pereche Nouă de Dosare - -Remove folder pair -Înlătură Perechea de Dosare - -Swap sides -Schimbă compartimentele stîng și drept între ele - -Close search bar -Închide bara de căutare - -Find: -Găsește: - -Match case -Potrivește cu caseta literelor (MAJ/min) - -Save as batch job -Salvează ca Sarcină Set - -Hide excluded items -Ascunde elementele excluse - -Show filtered or temporarily excluded files -Arată filele filtrate sau excluse temporar - -Number of files and folders that will be created -Numărul de file și dosare care vor fi create - -Number of files that will be overwritten -Numărul de file care vor fi suprascrise - -Number of files and folders that will be deleted -Numărul de file și dosare care vor fi șterse - -Total bytes to copy -Numărul total de baiți copiați - -Select a variant: -Selectează o variantă: - -Identify equal files by comparing modification time and size. -Filele identice sînt descoperite prin compararea timpului modificării și a mărimii. - -Identify equal files by comparing the file content. -Filele identice sînt descoperite prin compararea conținutului filelor. - -Symbolic links: -Legături Simbolice: - -More information -Mai multe informații - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Identificare și propagare a modificărilor din ambele părți. Ștergerile, renumirile și conflictele sînt detectate automat, folosind o bază de date. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Creează o conservare [backup] în oglindă a dosarului stîng care se potrivește exact cu dosarul drept după sincronizare. - -Copy new and updated files to the right folder. -Copiază filele noi și cele actualizate în dosarul drept. - -Configure your own synchronization rules. -Reguli de sincronizare definite de utilizator pentru fiecare situație. - -Detect moved files -Detectează filele mutate - -Requires database files. Not supported by all file systems. -Necesită file cu baze de date. Nu e suportat de toate sistemele de file. - -Delete files: -Ștergerea Filelor: - -Permanent -Definitivă - -Delete or overwrite files permanently -Filele sînt șterse sau suprascrise în mod definitiv, fără a mai putea fi recuperate - -&Recycle bin -&Reciclator - -Back up deleted and overwritten files in the recycle bin -Conservă [back up] în Reciclator filele șterse sau suprascrise - -Versioning -Versionare - -Move files to a user-defined folder -Mută filele într-un dosar stabilit de utilizator - -Naming convention: -Convenție de numire: - -Show examples -Arată exemple - -Handle errors: -Gestionarea Erorilor: - -Ignore -Ignorare - -Hide all error and warning messages -Mesajele de eroare și de avertizare nu sînt arătate - -Pop-up -Popîc - -Show pop-up on errors or warnings -În caz de erori sau avertizări este arătată o fereastră popîc [popup] - -On completion: -La terminare: - -Start synchronization now? -Pornesc sincronizarea acum? - -Variant: -Variantă: - -Statistics -Statistici - -&Don't show this dialog again -Nu arăta acest &dialog din nou - -Items found: -Elemente Găsite: - -Speed: -Viteză: - -Time remaining: -Timp Rămas: - -Time elapsed: -Timp Scurs: - -Synchronizing... -Sincronizare Aflată în Curs... - -Minimize to notification area -Minimizează în aria de notificare (sertar) - -Close -Închide - -&Pause -&Pauzează - -Stop -Oprește - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Creează o filă set [batch file] pentru sincronizarea neasistată. Pentru efectuarea sincronizării, dublu-clichează această filă sau programează rularea ei folosind un Planificator de Sarcini [Task Scheduler]: %x - -Stop synchronization at first error -Oprește sincronizarea la prima eroare întîlnită - -Show progress dialog -Arată progresul sincronizării - -Save log: -Salvează jurnalul: - -Limit: -Limitare: - -Limit maximum number of log files -Limitează numărul maxim de file jurnal - -How can I schedule a batch job? -Cum pot planifica o sarcină set? - -Delete on both sides -Șterge din ambele părți - -Delete on both sides even if the file is selected on one side only -Șterge din ambele părți, chiar dacă fila e selecționată într-o singură parte - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Selectează regulile filtrului pentru a exclude anumite file de la sincronizare. Introdu căile filelor relative la perechea lor de dosare corespondentă. - -Include: -Incluse: - -Exclude: -Excluse: - -Time span: -Intervalul de Timp: - -File size: -Mărimea Filei: - -Minimum: -Minim: - -Maximum: -Maxim: - -&Clear -&Curăță - -The following settings are used for all synchronization jobs. -Setările următoare sînt folosite pentru toate sarcinile de sincronizare. - -Fail-safe file copy -Copiază filele în modul protejat la eșec [fail-safe] - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -Copiază într-o filă temporară (*.ffs_tmp) înainte de suprascrierea filei țintă. -Aceasta garantează consecvența stării filelor chiar și în cazul apariției de erori serioase. - - -(recommended) -(recomandat) - -Copy locked files -Copiază filele partajate [shared] sau zăvorîte [locked] - -Copy shared or locked files using the Volume Shadow Copy Service. -Filele partajate sau zăvorîte sînt copiate folosind Serviciul de Conservare a Volumelor din Windows [Volume Shadow Copy]. - -(requires administrator rights) -(necesită drepturi de Administrator) - -Copy file access permissions -Copiază permisiunile de acces ale filelor - -Transfer file and folder permissions. -Permisiunile filelor și dosarelor sînt și ele transferate. - -Automatic retry on error: -Reîncearcă automat în caz de eroare: - -Retry count: -Numărul reîncercărilor: - -Delay (in seconds): -Întîrziere (în secunde): - -Customize context menu: -Personalizează meniul contextual: - -Description -Descriere - -Restore hidden windows -Restaurează ferestrele ascunse - -&Default -Coloanele &Implicite - -Source code written in C++ using: -Cod sursă scris în C++ folosind: - -If you like FreeFileSync -Donează pentru FreeFileSync - -Donate with PayPal -Donează prin PayPal - -Feedback and suggestions are welcome -Opiniile și sugestiile despre program sînt binevenite. - -Homepage -Situl Softului - -Email -Adresa Autorului - -Published under the GNU General Public License -Publicat sub licența GNU GPL - -Many thanks for localization: -Multe mulțumiri pentru localizare: - -Save as Batch Job -Salvează ca Sarcină Set - -Delete Items -Șterge Elementele - -Global Settings -Setări Globale - -Select Time Span -Selectează Intervalul de Timp - -Folder Pairs -Perechi de Dosare - -Find -Găsește - -Overview -Panoramă - -Configuration -Configurație - -Main Bar -Bara Principală - -Filter Files -Filtrează Filele - -Select View -Selectează Vederea - -Open... -Deschide... - -Save -Salvează - -Compare both sides -Compară Părțile Stîngă și Dreaptă - -Comparison settings -Setările Comparării - -Synchronization settings -Setările Sincronizării - -Start synchronization -Pornește Sincronizarea - -Confirm -Confirmă - -&Execute -&Execută - - -1 directory -%x directories - - -1 dosar -%x dosare -%x de dosare - - - -1 file -%x files - - -1 filă -%x file -%x de file - - - -%y of 1 row in view -%y of %x rows in view - - -%y din 1 rînd afișat -%y din %x rînduri afișate -%y din %x de rînduri afișate - - -Set direction: -Setează Acțiunea ca în Icoana Alăturată: - -multiple selection -selectare multiplă - -Include via filter: -Include prin Filtrul: - -Exclude via filter: -Exclude prin Filtrul: - -Exclude temporarily -Exclude Temporar - -Include temporarily -Include Temporar - -Delete -Șterge - -Include all -Include Tot - -Exclude all -Exclude Tot - -Show icons: -Arată Icoane: - -Small -Mici - -Medium -Medii - -Large -Mari - -Select time span... -Selectează intervalul de timp... - -Default view -Vedere Implicită - -Show "%x" -Arată "%x" - -Last session -Ultima Sesiune - -Folder Comparison and Synchronization -Comparare și Sincronizare de Dosare - -Configuration saved -Configurație salvată - -FreeFileSync batch -Set FreeFileSync - -Do you want to save changes to %x? -Vrei să salvezi modificările făcute la %x? - -Never save &changes -Nu salva ni&ciodată modificările - -Do&n't save -&Nu salva - -Filter -Filtru - -Show files that exist on left side only -Arată filele care există doar în stînga - -Show files that exist on right side only -Arată filele care există doar în dreapta - -Show files that are newer on left -Arată filele din stînga mai noi decît cele din dreapta - -Show files that are newer on right -Arată filele din dreapta mai noi decît cele din stînga - -Show files that are equal -Arată elementele (file/dosare) identice - -Show files that are different -Arată elementele (file/dosare) diferite - -Show conflicts -Arată conflictele - -Show files that will be created on the left side -Arată elementele (file/dosare) care vor fi create în stînga - -Show files that will be created on the right side -Arată elementele (file/dosare) care vor fi create în dreapta - -Show files that will be deleted on the left side -Arată elementele (file/dosare) care vor fi șterse în stînga - -Show files that will be deleted on the right side -Arată elementele (file/dosare) care vor fi șterse în dreapta - -Show files that will be overwritten on left side -Arată elementele (file/dosare) care vor fi suprascrise în stînga - -Show files that will be overwritten on right side -Arată elementele (file/dosare) care vor fi suprascrise în dreapta - -Show files that won't be copied -Arată elementele (file/dosare) care nu vor fi copiate - -Set as default -Setează ca implicit - -All folders are in sync -Toate dosarele sînt sincronizate - -Synchronization Settings -Setările Sincronizării - -Comparison Settings -Setările Comparării - -Cannot find %x -Nu pot găsi %x - -Comma-separated values -Valori separate prin virgulă - -File list exported -Lista de file a fost exportată - -Searching for program updates... -Caut actualizări ale programului... - -Scanning... -Scanez... - -Comparing content... -Compar conținutul... - -Info -Informații - -Warning -Atenție - -Paused -Sincronizare Pauzată - -Initializing... -Inițializez... - -Stopped -Oprită - -Completed -Sincronizare Terminată - -&Continue -&Continuă - -Log -Jurnal - -Today -Azi - -This week -Săptămîna asta - -This month -Luna asta - -This year -Anul ăsta - -Last x days -Ultimele x zile - -Byte -Baiți - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Sigur vrei să muți în Reciclator elementul următor? -Sigur vrei să muți în Reciclator următoarele %x elemente? -Sigur vrei să muți în Reciclator următoarele %x de elemente? - - -Move -Mută - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Sigur vrei să ștergi definitiv elementul următor? -Sigur vrei să ștergi definitiv următoarele %x elemente? -Sigur vrei să ștergi definitiv următoarele %x de elemente? - - -Exclude -Exclude - -Direct -Direcționează - -Follow -Urmărește - -Copy NTFS permissions -Copiază permisiunile NTFS - -Integrate external applications into context menu. The following macros are available: -Comenzi pentru aplicații externe incluse în meniul contextual al softului. Sînt disponibile următoarele macrocomenzi: - -- full file or folder name -- numele complet al filei sau dosarului - -- folder part only -- doar pentru partea dosarului - -- Other side's counterpart to %item_path% -- corespondentul din partea opusă al lui %item_path% - -- Other side's counterpart to %item_folder% -- corespondentul din partea opusă al lui %item_folder% - -Restore all hidden windows and warnings? -Restaurezi toate ferestrele și avertismentele ascunse? - -Leave as unresolved conflict -Lasă ca conflict nerezolvat - -Replace -Înlocuiește - -Move files and replace if existing -Mută filele și înlocuiește-le pe cele existente - -Time stamp -Marcaj Temporal - -Append a timestamp to each file name -Adaugă un marcaj temporal la numele fiecărei file - -File -Filă - -YYYY-MM-DD hhmmss -AAAA-LL-ZZ hhmmss - -Files -File - -Items -Elemente - -Percentage -Procent - -Cannot monitor directory %x. -Nu pot monitoriza dosarul %x. - -Conversion error: -Eroare de convertire: - -Cannot delete file %x. -Nu pot șterge fila %x. - -The file is locked by another process: -Fila este zăvorîtă [locked] de alt proces: - -Cannot move file %x to %y. -Nu pot muta fila %x în %y. - -Cannot delete directory %x. -Nu pot șterge dosarul %x. - -Cannot write file attributes of %x. -Nu pot scrie atributele de filă ale lui %x. - -Cannot write modification time of %x. -Nu pot scrie modificarea timpului pentru %x. - -Cannot read security context of %x. -Nu pot citi contextul de securitate pentru %x. - -Cannot write security context of %x. -Nu pot scrie contextul de securitate pentru %x. - -Cannot read permissions of %x. -Nu pot citi permisiunile lui %x. - -Cannot write permissions of %x. -Nu pot scrie permisiunile lui %x. - -Cannot create directory %x. -Nu pot crea dosarul %x. - -Cannot create symbolic link %x. -Nu pot crea legătura simbolică %x. - -Cannot find system function %x. -Nu pot găsi funcția de sistem %x. - -Cannot copy file %x to %y. -Nu pot copia fila %x în %y. - -Type of item %x is not supported: -Tipul de element %x nu e suportat: - -Cannot resolve symbolic link %x. -Nu pot rezolva legătura simbolică %x. - -Cannot open directory %x. -Nu pot deschide dosarul %x. - -Cannot enumerate directory %x. -Nu pot enumera dosarul %x. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -1 min -%x min -%x de min - - - -1 hour -%x hours - - -1 oră -%x ore -%x de ore - - - -1 day -%x days - - -1 zi -%x zile -%x de zile - - -Unable to register to receive system messages. -Nu pot înregistra softul pentru a primi mesaje de la sistemul de operare. - -Cannot set privilege %x. -Nu pot seta privilegiul %x. - -Unable to suspend system sleep mode. -Nu pot suspenda modul de intrare în repaus [sleep] al sistemului. - -Cannot change process I/O priorities. -Nu pot schimba prioritățile I/O ale procesului. - -Unable to move %x to the recycle bin. -Nu pot muta %x în Reciclator. - -Cannot determine final path for %x. -Nu pot determina calea finală pentru %x. - -Error Code %x: -Cod de Eroare %x: - diff --git a/BUILD/Languages/russian.lng b/BUILD/Languages/russian.lng deleted file mode 100644 index 5735f4f3..00000000 --- a/BUILD/Languages/russian.lng +++ /dev/null @@ -1,1534 +0,0 @@ -
    - Pусский - Fayzullin T.N. aka Svobodniy - ru_RU - flag_russia.png - 3 - n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<11 || n%100>14) ? 1 : 2 -
    - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -&Check - - -Retrying operation... - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Both sides have changed since last synchronization. -Со времени последней синхронизации с обеих сторон произошли изменения. - -Cannot determine sync-direction: -Невозможно определить направление синхронизации: - -No change since last synchronization. -Никаких изменений с последней синхронизации. - -The database entry is not in sync considering current settings. -Запись в базе данных не находится в состоянии синхронизации, учитывая текущие настройки. - -Setting default synchronization directions: Old files will be overwritten with newer files. - -Настройка направления синхронизации по умолчанию: -Старые файлы будут заменены более новыми файлами. - - -Checking recycle bin availability for folder %x... -Проверка доступности "Корзины" для папки %x... - -Moving file %x to the recycle bin -Перемещение файла %x в "Корзину" - -Moving folder %x to the recycle bin -Перемещение папки %x в "Корзину" - -Moving symbolic link %x to the recycle bin -Перемещение символьной ссылки %x в "Корзину" - -Deleting file %x -Удаление файла %x - -Deleting folder %x -Удаление папки %x - -Deleting symbolic link %x -Удаление символьной ссылки %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -Для этого пути "Корзина" недоступна! Файлы будут удалены безвозвратно: - -An exception occurred -Исключение произошло - -A directory path is expected after %x. -Ожидается путь папки после %x. - -Syntax error -Синтаксическая ошибка - -Cannot open file %x. -Невозможно открыть файл %x. - -File %x does not contain a valid configuration. -Файл %x не содержит действительной конфигурации. - -Unequal number of left and right directories specified. -Указано неравное число папок слева и справа. - -The config file must not contain settings at directory pair level when directories are set via command line. -Конфигурационный файл не должен содержать настройки на уровне пар папок, когда папки задаются с помощью командной строки. - -Directories cannot be set for more than one configuration file. -Папки не могут быть установлены более чем в одном конфигурационном файле. - -Command line -Командная строка - -Syntax: -Синтаксис: - -config files -конфигурационные файлы - -directory -папка - -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Любое количество FreeFileSync .ffs_gui и/или .ffs_batch конфигурационных файлов. - -Any number of alternative directories for at most one config file. -Любое количество альтернативных папок для одного конфигурационного файла. - -A folder input field is empty. -Поле ввода папки пустое. - -The corresponding folder will be considered as empty. -Соответствующая папка будет считаться пустой. - -Cannot find the following folders: -Невозможно найти следующие папки: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -Вы можете проигнорировать эту ошибку, приняв каждую папку за пустую. При этом папки будут созданы автоматически во время синхронизации. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -Следующие папки имеют зависимые пути. Будьте осторожны при настройке правил синхронизации: - -File %x has an invalid date. -Файл %x имеет недействительную дату. - -Date: -Дата: - -Files %x have the same date but a different size. -Файлы %x имеют одинаковую дату, но различаются по размеру. - -Size: -Размер: - -Items differ in attributes only -Элементы различаются только атрибутами - -Resolving symbolic link %x -Разрешение символьной ссылки %x - -Comparing content of files %x -Сравнение содержания файлов %x - -Generating file list... -Создание списка файлов... - -Starting comparison -Начать сравнение - -Calculating sync directions... -Расчет направлений синхронизации... - -Out of memory. -Недостаточно памяти. - -Item exists on left side only -Элемент существует только на левой стороне - -Item exists on right side only -Элемент существует только на правой стороне - -Left side is newer -На левой стороне новее - -Right side is newer -На правой стороне новее - -Items have different content -Элемент имеют различное содержание - -Both sides are equal -Обе стороны равны - -Conflict/item cannot be categorized -Конфликт/элемент невозможно отнести к какой-либо категории - -Copy new item to left -Скопировать новый элемент налево - -Copy new item to right -Скопировать новый элемент направо - -Delete left item -Удалить элемент слева - -Delete right item -Удалить элемент справа - -Move file on left -Переместить файл налево - -Move file on right -Переместить файл направо - -Overwrite left item -Перезаписать элемент слева - -Overwrite right item -Перезаписать элемент справа - -Do nothing -Ничего не делать - -Update attributes on left -Обновление атрибутов слева - -Update attributes on right -Обновление атрибутов справа - -Database file %x is incompatible. -Файл базы данных %x несовместим. - -Initial synchronization: -Первоначальная синхронизация: - -Database file %x does not yet exist. -Файл базы данных %x еще не существует. - -Database file is corrupt: -Файл базы данных поврежден: - -Cannot write file %x. -Невозможно записать файл %x. - -Cannot read file %x. -Невозможно прочитать файл %x. - -Database files do not share a common session. -Файлы баз данных не имеют общей сессии. - -Searching for folder %x... -Поиск папки %x... - -Cannot read file attributes of %x. -Невозможно прочитать атрибуты файла %x. - -Cannot get process information. -Невозможно получить информацию о процессе. - -Waiting while directory is locked (%x)... -Ожидание снятия блокировки с папки %x... - - -1 sec -%x sec - - -%x секунда -%x секунды -%x секунд - - -Creating file %x -Создание файла %x - -Items processed: -Элементов обработано: - -Items remaining: -Элементов осталось: - -Total time: -Общее время: - - -1 byte -%x bytes - - -%x байт -%x байта -%x байт - - -%x MB -%x МБ - -%x KB -%x КБ - -%x GB -%x ГБ - -Error parsing file %x, row %y, column %z. -Ошибка при разборе файла %x, строка %y, колонка %z. - -Cannot set directory lock for %x. -Невозможно установить блокировку папки для %x. - -Scanning: -Сканирую: - - -1 thread -%x threads - - -%x поток -%x потока -%x потоков - - -Encoding extended time information: %x -Кодирование расширенной информации о времени: %x - -/sec - - -%x items/sec -%x элементов/с - -Configuration file %x loaded partially only. -Файл конфигурации %x загрузился частично. - -Show in Explorer -Показать в Проводнике - -Open with default application -Открыть с помощью приложения по умолчанию - -Browse directory -Обзор папок - -Cannot access the Volume Shadow Copy Service. -Невозможно получить доступ к службе Теневого Копирования Тома. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Пожалуйста, используйте 64-разрядную версию FreeFileSync для создания теневых копий на этой системе. - -Cannot load file %x. -Невозможно загрузить файл %x. - -Cannot determine volume name for %x. -Невозможно определить имя тома для %x. - -Volume name %x is not part of file path %y. -Имя тома %x не является частью имени файла %y. - -Stop requested: Waiting for current operation to finish... -Запрошена остановка: Ожидайте, пока текущая операция завершится... - -Unable to create timestamp for versioning: -Неспособность создать отметку времени для архивации файлов: - -Cannot read the following XML elements: -Невозможно прочитать следующие XML элементы: - -&Open... -&Открыть... - -Save &as... -Сохранить &как... - -&Quit -&Выход - -&Program -&Файл - -&View help -&Справка - -&About -&О программе - -&Help -&Помощь - -Usage: -Инструкция: - -1. Select folders to watch. -1. Выберите папки для наблюдения; - -2. Enter a command line. -2. Введите командную строку; - -3. Press 'Start'. -3. Нажмите 'Старт'. - -To get started just import a .ffs_batch file. -Для запуска просто импортируйте файл .ffs_batch. - -Folders to watch: -Папки для наблюдения: - -Add folder -Добавить папку - -Remove folder -Удалить папку - -Browse -Обзор - -Select a folder -Выбрать папку - -Idle time (in seconds): -Время ожидания (в секундах): - -Idle time between last detected change and execution of command -Время ожидания между последним обнаруженным изменением и выполнением командной строки в секундах - -Command line: -Командная строка: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -Команда выполняется, если: -- файлы или подпапки изменены -- появились новые папки (например, подключение переносного носителя) - - -&Start -&Старт - -About -О программе - -Build: %x -сборка %x - -All files -Все файлы - -Automated Synchronization -Автоматическая синхронизация - -Directory monitoring active -Слежение за папками активировано - -Waiting until all directories are available... -Ожидать, пока все папки станут доступны... - -Error -Ошибка - -&Restore -&Восстановить - -&Show error -&Показать ошибку - -&Exit -&Выход - -Incorrect command line: -Неверная командная строка: - -&Retry -&Повторить - -File content -Содержимое файла - -File time and size -Дата и размер файла - -Two way -В обе стороны - -Mirror -Зеркало - -Update -Обновить - -Custom -Выборочно - -Multiple... -Различные варианты синхронизации - -Moving file %x to %y -Перемещение файла %x в %y - -Moving folder %x to %y -Перемещение папки %x в %y - -Moving symbolic link %x to %y -Перемещение символьной ссылки %x в %y - -Removing old versions... -Удаление старых версий... - -Creating symbolic link %x -Создание символьной ссылки %x - -Creating folder %x -Создание папки %x - -Overwriting file %x -Перезапись файла %x - -Overwriting symbolic link %x -Перезапись символьной ссылки %x - -Verifying file %x -Проверка файла %x - -Updating attributes of %x -Обновление атрибутов %x - -Cannot find %x. -Невозможно найти %x. - -Target folder %x already existing. -Целевая папка %x уже существует. - -Target folder input field must not be empty. -Поле ввода целевой папки не должно быть пустым. - -Please enter a target folder for versioning. -Пожалуйста, введите целевую папку для архивации файлов. - -Source folder %x not found. -Исходная папка %x не найдена. - -The following items have unresolved conflicts and will not be synchronized: -Следующие элементы имеют неурегулированные конфликты и не будут синхронизированы: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. - -Следующие папки существенно различаются. -Убедитесь, что вы указали соответствующие папки для синхронизации. - - -Not enough free disk space available in: -Не достаточно свободного места в: - -Required: -Требуется: - -Available: -Доступно: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Папка, входящая в несколько пар папок, будет изменена. Пожалуйста, проверьте настройки синхронизации. - -Synchronizing folder pair: -Синхронизация пары папок: - -Generating database... -Создание базы данных... - -Creating a Volume Shadow Copy for %x... -Создание Тома Теневого Копирования для %x... - -Data verification error: %x and %y have different content. -Ошибка проверки данных: %x и %y имеют разное содержание! - -job name -название - -Synchronization stopped -Синхронизация остановлена - -Synchronization completed with errors -Синхронизация завершена. В процессе синхронизации возникли ошибки - -Synchronization completed with warnings -Синхронизация завершена. В процессе синхронизации возникли проблемы - -Nothing to synchronize -Ничего нет для синхронизации - -Synchronization completed successfully -Синхронизация завершена успешно - -Saving log file %x... -Сохранение лог-файла %x... - -You can switch to FreeFileSync's main window to resolve this issue. -Вы можете переключиться на главное окно FreeFileSync для решения этой проблемы. - -&Don't show this warning again -Больше &не показывать это предупреждение - -&Ignore -&Игнорировать - -&Switch -&Переключить - -Switching to FreeFileSync's main window -Переключение на главное окно FreeFileSync - -&Ignore subsequent errors -И&гнорировать последующие ошибки - -Serious Error -Серьезная ошибка - -Check for Program Updates -Проверка обновления программы - -A new version of FreeFileSync is available: -Доступна новая версия FreeFileSync: - -Download now? -Загрузить сейчас? - -&Download -&Загрузить - -FreeFileSync is up to date. -У Вас самая последняя версия FreeFileSync. - -Unable to connect to sourceforge.net. -Невозможно соединиться с sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Невозможно найти номер текущей версии FreeFileSync онлайн! Вы хотите проверить вручную? - -Symlink -Символьная ссылка - -Folder -Папка - -Full path -Полный путь - -Name -Имя - -Relative path -Относительный путь - -Base folder -Основная папка - -Size -Размер - -Date -Дата - -Extension -Расширение - -Category -Категория - -Action -Действие - -Drag && drop -Drag && drop - -Close progress dialog -Закрыть окно процесса - -Standby -Перейти в ожидании - -Log off -Выйти из системы (разлогиниться) - -Shut down -Выключить компьютер - -Hibernate -Гибернация - -Alternate comparison settings -Альтернативные настройки сравнения - -Alternate synchronization settings -Альтернативные настройки синхронизации - -Local filter -Локальный фильтр - -Active -активный - -None -неактивный - -Remove alternate settings -Удалить альтернативные настройки - -Clear filter settings -Очистить настройки фильтра - -Copy -Копировать - -Paste -Вставить - -Alternate Comparison Settings -Альтернативные настройки сравнения - -Alternate Synchronization Settings -Альтернативные настройки синхронизации - -Local Filter -Локальный фильтр - -&New -&Новая - -&Save -&Сохранить - -Save as &batch job... -Сохранить как &пакетное задание - -1. &Compare -1. С&равнить - -2. &Synchronize -2. С&инхронизировать - -&Global settings -&Глобальные настройки - -&Language -&Язык - -&Find... -&Найти... - -&Export file list... -&Экспортировать список файлов... - -&Tools -&Инструменты - -&Check now -&Проверить сейчас - -Check &automatically once a week -Проверять &автоматически раз в неделю - -&Check for new version -&Проверка обновлений - -Compare -Сравнить - -Cancel -Отмена - -Synchronize -Синхронизировать - -Add folder pair -Добавить пару папок - -Remove folder pair -Удалить пару папок - -Swap sides -Поменять направление - -Close search bar -Закрыть строку поиска - -Find: -Найти: - -Match case -Учитывать регистр - -Save as batch job -Сохранить как пакетное задание - -Hide excluded items -Скрыть исключенные элементы - -Show filtered or temporarily excluded files -Показать отфильтрованные или временно исключенные файлы - -Number of files and folders that will be created -Количество файлов и папок, которые будут созданы - -Number of files that will be overwritten -Количество файлов, которые будут перезаписаны - -Number of files and folders that will be deleted -Количество файлов и папок, которые будут удалены - -Total bytes to copy -Всего байт для копирования - -Select a variant: -Выберите вариант: - -Identify equal files by comparing modification time and size. -Определять одинаковые файлы путем сравнения времени изменения и размера. - -Identify equal files by comparing the file content. -Определять одинаковые файлы путем сравнения содержания файла. - -Symbolic links: -Символьные ссылки: - -More information -Подробнее - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Выявять и распространять изменения на обе стороны. Удаленные, перемещенные и конфликтующие файлы определяются автоматически с использованием базы данных. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Создавать зеркальную копию левой части. После синхронизации правая часть будет полностью соответствовать левой. - -Copy new and updated files to the right folder. -Копировать новые или обновленные файлы на правую сторону. - -Configure your own synchronization rules. -Настроить свои собственные правила синхронизации. - -Detect moved files -Обнаруживать перемещенные файлы - -Requires database files. Not supported by all file systems. -Требуются файлы баз данных. Поддерживается не всеми файловыми системами. - -Delete files: -Удаление файлов: - -Permanent -Удалять безвозвратно - -Delete or overwrite files permanently -Удалять или перезаписывать файлы, не помещая в "Корзину" - -&Recycle bin -В "&Корзину" - -Back up deleted and overwritten files in the recycle bin -Делать резервную копию удаленных и перезаписанных файлов в "Корзине" - -Versioning -Архивировать - -Move files to a user-defined folder -Перемещать файлы в пользовательскую папку - -Naming convention: -Условие переименования: - -Show examples -Подробнее - -Handle errors: -Обработка ошибкок: - -Ignore -Игнорировать - -Hide all error and warning messages -Скрывать все ошибки и сообщения с предупреждениями - -Pop-up -Показывать всплывающие окна - -Show pop-up on errors or warnings -Показывать всплывающие окна при ошибках и предупреждениях - -On completion: -По завершению: - -Start synchronization now? -Начать синхронизацию сейчас? - -Variant: -Вариант: - -Statistics -Статистика - -&Don't show this dialog again -Больше &не показывать это окно - -Items found: -Элементов найдено: - -Speed: -Скорость: - -Time remaining: -Времени осталось: - -Time elapsed: -Времени прошло: - -Synchronizing... -Синхронизация... - -Minimize to notification area -Свернуть в область уведомлений - -Close -Закрыть - -&Pause -&Пауза - -Stop -Остановить - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x - -Создать файл пакетного задания для автоматической синхронизации. -Для запуска дважды кликните этот файл или запланируйте -в планировщике задач: %x - - -Stop synchronization at first error -Остановить синхронизацию при первой ошибке - -Show progress dialog -Показать окно прогресса - -Save log: -Сохранить лог-файл: - -Limit: -Ограничение: - -Limit maximum number of log files -Ограничить максимальное количество лог-файлов - -How can I schedule a batch job? -Как запланировать пакетное задание? - -Delete on both sides -Удалить с обеих сторон - -Delete on both sides even if the file is selected on one side only -Удалить с обеих сторон, даже если файл выделен только на одной стороне - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Выберите правила фильтрации для исключения определенных файлов из синхронизации. Введите пути файлов относящиеся к соответствующим парам папок. - -Include: -Включить: - -Exclude: -Исключить: - -Time span: -Промежуток времени: - -File size: -Размер файла: - -Minimum: -минимум: - -Maximum: -максимум: - -&Clear -&Очистить - -The following settings are used for all synchronization jobs. -Следующие настройки используются для всех заданий синхронизации. - -Fail-safe file copy -Отказоустойчивое копирование файла - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -Копирование во временный файл (*.ffs_tmp) перед перезаписью целевого файла. -Это гарантирует целостность заменяемых файлов даже в случае возникновения серьезной ошибки. - - -(recommended) -(рекомендовано) - -Copy locked files -Копирование заблокированных файлов - -Copy shared or locked files using the Volume Shadow Copy Service. -Копирование общих или заблокированных файлов с использованием службы Теневого Копирования Тома - -(requires administrator rights) -(требуются права Администратора) - -Copy file access permissions -Копирование прав доступа к файлам - -Transfer file and folder permissions. -Передача прав доступа к файлам/папкам - -Automatic retry on error: -Автоматическое повторение при ошибках: - -Retry count: -Число повторений: - -Delay (in seconds): -Задержка (в секундах): - -Customize context menu: -Кастомизация контекстного меню: - -Description -Описание - -Restore hidden windows -Восстановить скрытые окна - -&Default -&По умолчанию - -Source code written in C++ using: -Исходный код написан на C++ с использованием: - -If you like FreeFileSync -Если Вам понравился FreeFileSync - -Donate with PayPal -Отправить деньги через PayPal - -Feedback and suggestions are welcome -Замечания и предложения приветствуются - -Homepage -Оф.сайт - -Email -Почта - -Published under the GNU General Public License -Издается под лицензией GNU General Public License - -Many thanks for localization: -Большое спасибо за перевод: - -Save as Batch Job -Сохранение пакетного задания - -Delete Items -Удаление элементов - -Global Settings -Глобальные настройки - -Select Time Span -Выбор промежутка времени - -Folder Pairs -Пары папок для синхронизации - -Find -Поиск - -Overview -Главная - -Configuration -Настройки - -Main Bar -Главная панель - -Filter Files -Фильтр - -Select View -Вид списка файлов - -Open... -Открыть... - -Save -Сохранить - -Compare both sides -Сравнить обе стороны - -Comparison settings -Настройки сравнения - -Synchronization settings -Настройки синхронизации - -Start synchronization -Начать синхронизацию - -Confirm -Подтвердить - -&Execute -&Выполнить - - -1 directory -%x directories - - -%x папка -%x папки -%x папок - - - -1 file -%x files - - -%x файл -%x файла -%x файлов - - - -%y of 1 row in view -%y of %x rows in view - - -%y из %x строки показано -%y из %x строк показано -%y из %x строк показано - - -Set direction: -Выберите направление: - -multiple selection -групповое выделение - -Include via filter: -Включить с помощью фильтра: - -Exclude via filter: -Исключить с помощью фильтра: - -Exclude temporarily -Временно исключить - -Include temporarily -Временно включить - -Delete -Удалить - -Include all -Включить все - -Exclude all -Исключить все - -Show icons: -Отображать иконки: - -Small -- маленькие - -Medium -- средние - -Large -- большие - -Select time span... -Выберите промежуток времени... - -Default view -Стандартный вид - -Show "%x" -Показать "%x" - -Last session -Последняя сессия - -Folder Comparison and Synchronization -Сравнение и синхронизация - -Configuration saved -Настройки синхронизации сохранены - -FreeFileSync batch -Пакетное задание FreeFileSync - -Do you want to save changes to %x? -Вы хотите сохранить изменения в %x? - -Never save &changes -Никогда не сохранять &изменения - -Do&n't save -&Не сохранять - -Filter -Фильтр - -Show files that exist on left side only -Показать файлы, существующие только слева - -Show files that exist on right side only -Показать файлы, существующие только справа - -Show files that are newer on left -Показать файлы, которые новее слева - -Show files that are newer on right -Показать файлы, которые новее справа - -Show files that are equal -Показать одинаковые файлы - -Show files that are different -Показать различающиеся файлы - -Show conflicts -Показать конфликтующие файлы - -Show files that will be created on the left side -Показать файлы, которые будут созданы на левой стороне - -Show files that will be created on the right side -Показать файлы, которые будут созданы на правой стороне - -Show files that will be deleted on the left side -Показать файлы, которые будут удалены на левой стороне - -Show files that will be deleted on the right side -Показать файлы, которые будут удалены на правой стороне - -Show files that will be overwritten on left side -Показать файлы, которые будут перезаписаны на левой стороне - -Show files that will be overwritten on right side -Показать файлы, которые будут перезаписаны на правой стороне - -Show files that won't be copied -Показать файлы, которые не будут скопированы - -Set as default -Установить по умолчанию - -All folders are in sync -Все папки синхронизированы - -Synchronization Settings -Настройки синхронизации - -Comparison Settings -Настройки сравнения - -Cannot find %x -Невозможно найти %x - -Comma-separated values -Значения, разделенные запятыми - -File list exported -Список файлов экспортирован - -Searching for program updates... -Проверка обновлений программы... - -Scanning... -Сканирование... - -Comparing content... -Сравнение содержания... - -Info -Информация - -Warning -Внимание - -Paused -Пауза - -Initializing... -Инициализация... - -Stopped -Остановлено - -Completed -Завершено - -&Continue -&Продолжить - -Log -Лог (журнал) - -Today -сегодня - -This week -на этой неделе - -This month -последний месяц - -This year -последний год - -Last x days -последние X дня(ей) - -Byte -Байт - -KB -КБ - -MB -МБ - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Вы точно хотите переместить следующий %x элемент в "Корзину"? -Вы точно хотите переместить следующие %x элемента в "Корзину"? -Вы точно хотите переместить следующие %x элементов в "Корзину"? - - -Move -Переместить - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Вы точно хотите удалить следующий %x элемент? -Вы точно хотите удалить следующие %x элемента? -Вы точно хотите удалить следующие %x элементов? - - -Exclude -Исключить - -Direct -Прямое - -Follow -Последовательное - -Copy NTFS permissions -Копирование NTFS прав доступа - -Integrate external applications into context menu. The following macros are available: - -Интегрируйте внешние приложения в контекстное меню. -Доступны следующие команды: - - -- full file or folder name -- полный путь файла или папки - -- folder part only -- часть пути папки - -- Other side's counterpart to %item_path% -- аналог %item_path% с другой стороны - -- Other side's counterpart to %item_folder% -- аналог %item_folder% с другой стороны - -Restore all hidden windows and warnings? -Восстановить все скрытые окна и предупреждения? - -Leave as unresolved conflict -Оставить как нерешенный конфликт - -Replace -Без переименования - -Move files and replace if existing -Переместить файлы и заменить, если существуют - -Time stamp -Добавить отметку времени - -Append a timestamp to each file name -Добавить отметку времени для каждого имени файла - -File -Файл - -YYYY-MM-DD hhmmss -ГГГГ-ММ-ДД ччммсс - -Files -Файлы - -Items -Элементы - -Percentage -Проценты - -Cannot monitor directory %x. -Невозможно наблюдать папку %x. - -Conversion error: -Ошибка преобразования: - -Cannot delete file %x. -Невозможно удалить файл %x. - -The file is locked by another process: -Файл заблокирован другим процессом: - -Cannot move file %x to %y. -Невозможно перенести файл %x в %y. - -Cannot delete directory %x. -Невозможно удалить папку %x. - -Cannot write file attributes of %x. -Невозможно записать атрибуты файла %x. - -Cannot write modification time of %x. -Невозможно записать время изменения файла %x. - -Cannot read security context of %x. -Невозможно прочитать контекст безобасности %x. - -Cannot write security context of %x. -Невозможно записать контекст безобасности %x. - -Cannot read permissions of %x. -Невозможно прочитать права доступа %x. - -Cannot write permissions of %x. -Невозможно записать права доступа %x. - -Cannot create directory %x. -Невозможно создать папку %x. - -Cannot create symbolic link %x. -Невозможно создать символьную ссылку %x. - -Cannot find system function %x. -Невозможно найти системную функцию %x. - -Cannot copy file %x to %y. -Невозможно скопировать файл %x в %y. - -Type of item %x is not supported: -Тип элемента %x не поддерживается: - -Cannot resolve symbolic link %x. -Невозможно разрешить символьную ссылку %x. - -Cannot open directory %x. -Невозможно открыть папку %x. - -Cannot enumerate directory %x. -Невозможно прочесть папку %x. - -%x TB -%x ТБ - -%x PB -%x ПБ - - -1 min -%x min - - -%x минута -%x минуты -%x минут - - - -1 hour -%x hours - - -%x час -%x часа -%x часов - - - -1 day -%x days - - -%x день -%x дня -%x дней - - -Unable to register to receive system messages. -Невозможно зарегистрироваться для получения системных сообщений. - -Cannot set privilege %x. -Невозможно установить привелегии %x. - -Unable to suspend system sleep mode. -Невозможно приостановить режим сна системы. - -Cannot change process I/O priorities. -Невозможно изменить приоритет процесса. - -Unable to move %x to the recycle bin. -Невозможно переместить %x в "Корзину". - -Cannot determine final path for %x. -Невозможно определить конечный путь для %x. - -Error Code %x: -Код ошибки %x: - diff --git a/BUILD/Languages/scottish_gaelic.lng b/BUILD/Languages/scottish_gaelic.lng deleted file mode 100644 index e7ded4f7..00000000 --- a/BUILD/Languages/scottish_gaelic.lng +++ /dev/null @@ -1,1543 +0,0 @@ -
    - Gàidhlig - Michael Bauer aka Akerbeltz - gd - flag_scotland.png - 4 - (n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3 -
    - -&Check - - -Retrying operation... - - -Both sides have changed since last synchronization. -Chaidh an dà thaobh atharrachadh on t-sioncronachadh mu dheireadh. - -Cannot determine sync-direction: -Cha ghabh comhair an t-sioncronachaidh aithneachadh: - -No change since last synchronization. -Cha deach dad atharrachadh on t-sioncronachadh mu dheireadh. - -The database entry is not in sync considering current settings. -Chan eil innteart an stòir-dhàta sioncronaicht a-rèir nan roghainnean làithreach - -Setting default synchronization directions: Old files will be overwritten with newer files. -A' suidheachadh comhair bhunaiteach an t-sioncronachaidh: Thèid faidhlichean nas ùire a sgrìobhadh thairis air seann-fhaidhlichean. - -Checking recycle bin availability for folder %x... -A' toirt sùil a bheil am biona ri fhaighin airson a' phasgain %x... - -Moving file %x to the recycle bin -A' gluasad an fhaidhle %x dhan bhiona - -Moving folder %x to the recycle bin -A' gluasad a' phasgain %x dhan bhiona - -Moving symbolic link %x to the recycle bin -A' gluasad an symbolic link %x dhan bhiona - -Deleting file %x -A' sguabadh às an fhaidhle %x - -Deleting folder %x -A' sguabadh às a' phasgain %x - -Deleting symbolic link %x -A' sguabadh às an symbolic link %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -Chan eil am biona ri làimh airson nam pasganan a leanas. Thèid an sguabadh às gu buan an àite sin: - -An exception occurred -Thachair eisgeachd - -A directory path is expected after %x. -Tha dùil ri slighe pasgain an dèidh %x - -Syntax error -Mearachd co-chàraidh - -Cannot open file %x. -Cha ghabh am faidhle %x fhosgladh. - -File %x does not contain a valid configuration. -Chan eil rèiteachadh dligheach san fhaidhle %x. - -Unequal number of left and right directories specified. -Chan eil àireamh nam pasganan air an taobh chlì 's an taobh deas co-ionnann - -The config file must not contain settings at directory pair level when directories are set via command line. -Chan fhaod roghainnean aig ìre paidhrichean nam pasganan a bhith san fhaidhle rèiteachaidh nuair a thathar a' cur pasganan slighe na loidhne-àithne. - -Directories cannot be set for more than one configuration file. -Cha ghabh pasganan a shuidheachadh airson barrachd air aon fhaidhle rèiteachaidh. - -Command line -Loidhne-àithne - -Syntax: -Co-chàradh: - -config files -faidhlichean rèiteachaidh - -directory -pasgan - -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Àireamh sam bith de dh'fhaidhlichean rèiteachaidh FreeFileSync .ffs_gui agus/no .ffs_batch - -Any number of alternative directories for at most one config file. -Àireamh sam bith de phasganan eile airson aon fhaidhle rèiteachaidh air a' char as fhaide. - -A folder input field is empty. -Tha co-dhiù aon raon pasgain ann a tha falamh. - -The corresponding folder will be considered as empty. -Thèid am pasgan a leanas a làimhseachadh mar phasgan falamh. - -Cannot find the following folders: -Chan urrainn dhuinn na pasgain a leanas a lorg: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -'S urrainn dhut a' mhearachd seo a leigeil seachad ma tha thu airson 's gun dèilig sinn ri gach pasgan mar gum biodh iad falamh. Thèid na pasgain a chruthachadh gu fèin-obrachail an uairsin rè an t-sioncronachaidh - -The following folders have dependent paths. Be careful when setting up synchronization rules: -Tha slighean eisimeileachdach aig na pasganan a leanas. Bi faiceallach nuair a chruthaicheas tu riaghailtean sioncronachaidh: - -File %x has an invalid date. -Tha ceann-là mì-dhligheach aig an fhaidhle %x. - -Date: -Ceann-là: - -Files %x have the same date but a different size. -Tha an dearbh cheann-là aig na faidhlichean %x ach chan eil am meud co-ionnann. - -Size: -Meud: - -Items differ in attributes only -Chan eil diofar eatarra ach a thaobh an cuid bhuadhan - -Resolving symbolic link %x -Duilgheadas a' rèiteachadh a' symbolic link %x - -Comparing content of files %x -A' dèanamh coimheas eadar na faidhlichean %x - -Generating file list... -A' gintinn liosta nam faidhle... - -Starting comparison -A' tòiseachadh air a' choimeas - -Calculating sync directions... -Ag àireamhachadh comhairean an t-sioncronachaidh... - -Out of memory. -Chan eil cuimhne gu leòr ann. - -Item exists on left side only -Chan eil an nì seo ann ach air an taobh chlì - -Item exists on right side only -Chan eil an nì seo ann ach air an taobh deas - -Left side is newer -Tha an taobh clì nas ùire - -Right side is newer -Tha an taobh deas nas ùire - -Items have different content -Tha diofar susbaint sna nithean - -Both sides are equal -Tha an dà thaobh co-ionnann - -Conflict/item cannot be categorized -Tha còmhstri/nì ann nach urrainn dhuinn aithneachadh - -Copy new item to left -Cuir lethbhreac dhen nì ùr dhan taobh chlì - -Copy new item to right -Cuir lethbhreac dhen nì ùr dhan taobh deas - -Delete left item -Sguab às an nì air an taobh chlì - -Delete right item -Sguab às an nì air an taobh deas - -Move file on left -Gluais am faidhle a tha air an taobh chlì - -Move file on right -Gluais am faidhle a tha air an taobh deas - -Overwrite left item -Sgrìobh thairis air an nì chlì - -Overwrite right item -Sgrìobh thairis air an nì deas - -Do nothing -Na dèan dad - -Update attributes on left -Ùraich na buadhan air an taobh chlì - -Update attributes on right -Ùraich na buadhan air an taobh deas - -Database file %x is incompatible. -Chan eil am faidhle stòir-dhàta %x co-chòrdail. - -Initial synchronization: -A' chiad sioncronachadh: - -Database file %x does not yet exist. -Chan eil am faidhle stòir-dhàta %x ann fhathast. - -Database file is corrupt: -Tha am faidhle stòir-dhàta coirbte: - -Cannot write file %x. -Cha ghabh am faidhle %x a sgrìobhadh. - -Cannot read file %x. -Cha ghabh am faidhle %x a leughadh. - -Database files do not share a common session. -Chan eil seisean an cumantas aig na faidhlichean stòir-dhàta. - -Searching for folder %x... -A' lorg a' phasgain %x... - -Cannot read file attributes of %x. -Cha ghabh buadhan an fhaidhle %x a leughadh. - -Cannot get process information. -Chan urrainn dhuinn greim fhaighinn air fiosrachadh a' phròiseis. - -Waiting while directory is locked (%x)... -A' feitheamh fhad 's a tha am pasgan glaiste (%x)... - - -1 sec -%x sec - - -%x diog -%x dhiog -%x diogan -%x diog - - -Creating file %x -A' cruthachadh an fhaidhle %x - -Items processed: -Nithean a tha deiseil: - -Items remaining: -Nithean a tha ri dhèanamh: - -Total time: -An ùine gu lèir: - - -1 byte -%x bytes - - -%x byte -%x bytes -%x bytes -%x bytes - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Mearachd le parsadh an fhaidhle %x, loidhne %y, colbh %z. - -Cannot set directory lock for %x. -Cha ghabh glas a' phasgain airson %x a shuidheachadh. - -Scanning: -'Ga sganadh: - - -1 thread -%x threads - - -%x shnàithlean -%x shnàithlean -%x snàithleanan -%x snàithlean - - -Encoding extended time information: %x -A' còdachadh fiosrachadh leudaichte an ama: %x - -/sec -/diog - -%x items/sec -%x nithean/diog - -Configuration file %x loaded partially only. -Cha deach faidhle an rèiteachaidh %x a luchdadh gu tur. - -Show in Explorer -Seall san taisgealaiche - -Open with default application -Fosgail leis an aplacaid bhunaiteach - -Browse directory -Rùraich am pasgan - -Cannot access the Volume Shadow Copy Service. -Chan fhaigh sinn cothrom air seirbheise lethbhreacan sgàil an draibh. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Nach cleachd sibh an tionndadh 64 biot de FreeFileSync gus lethbhreacan-sgàile a chruthachadh air an t-siostam seo? - -Cannot load file %x. -Cha ghabh am faidhle %x a lorg. - -Cannot determine volume name for %x. -Chan urrainn dhuinn ainm an draibh airson %x a dhearbhadh. - -Volume name %x is not part of file path %y. -Chan eil ainm an draibh %x 'na phàirt de shlighe an fhaodhle %y. - -Stop requested: Waiting for current operation to finish... -Dh'iarr thu oirnn sgur: A' feitheamh ri crìoch na h-obrach làithreach... - -Unable to create timestamp for versioning: -Chan urrainn dhuinn stampa-ama a chruthachadh airson versioning: - -Cannot read the following XML elements: -Chan urrainn dhuinn na h-eileamaidean XML a leanas a leughadh: - -&Open... -F&osgail... - -Save &as... -Sàbhail &mar... - -&Quit -&Fàg - -&Program -&Prògram - -&View help -&Faic a' chobhair - -&About -&Mu dheidhinn - -&Help -&Cobhair - -Usage: -Cleachdadh: - -1. Select folders to watch. -1. Tagh na pasgain air an cumar sùil. - -2. Enter a command line. -2. Cuir a-steach àithne. - -3. Press 'Start'. -3. Briog air "Tòisich". - -To get started just import a .ffs_batch file. -Cha leig thu leas ach faidhle .ffs_batch ion-phortadh airson toiseach tòiseachaidh. - -Folders to watch: -Pasganan air an cumar sùil: - -Add folder -Cuir pasgan ris - -Remove folder -Thoir am pasgan air falbh - -Browse -Rùraich - -Select a folder -Tagh pasgan - -Idle time (in seconds): -Ùine 'na thàmh (ann an diogan): - -Idle time between last detected change and execution of command -An tàmh eadar an t-atharrachadh mu dheireadh agus gnìomhachadh na h-àithne - -Command line: -Loidhne-àithne: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -Thèid an loidhne-àithne a chur gu dol: -- ma dh'atharraicheas faidhlichean no fo-phasgain -- ma nochdas pasgain ùra (m.e. ma chuireas tu a-steach bioran USB) - - -&Start -Tòi&sich - -About -Mu dheidhinn - -Build: %x -Build: %x - -All files -Gach faidhle - -Automated Synchronization -Sioncronachadh fèin-obrachail - -Directory monitoring active -Tha na pasganan 'gam marasgladh - -Waiting until all directories are available... -A' feitheamh gus am bi gach pasgan ri làimh... - -Error -Mearachd - -&Restore -&Aisig - -&Show error -&Seall a' mhearachd - -&Exit -&Fàg an-seo - -Incorrect command line: -Loidhne-àithne chearr: - -&Retry -&Feuch ris a-rithist - -File content -Susbaint an fhaidhle - -File time and size -Ceann-là is meud - -Two way -An dà chomhair - -Mirror -Sgàthanaich - -Update -Ùraich - -Custom -Gnàthaichte - -Multiple... -Iomadh fear... - -Moving file %x to %y -A' gluasad an fhaidhle %x gu %y - -Moving folder %x to %y -A' gluasad a' phasgain %x gu %y - -Moving symbolic link %x to %y -A' gluasad an symbolic link %x gu %y - -Removing old versions... -A' toirt air falbh nan seann tionndaidhean... - -Creating symbolic link %x -A' cruthachadh an symbolic link %x - -Creating folder %x -A' cruthachadh a' phasgain %x - -Overwriting file %x -A' sgrìobhadh thairis air an fhaidhle %x - -Overwriting symbolic link %x -A' sgrìobhadh thairis air an symbolic link %x - -Verifying file %x -A' dearbhadh an fhaidhle %x - -Updating attributes of %x -Ag ùrachadh buadhan %x - -Cannot find %x. -Cha ghabh %x a lorg. - -Target folder %x already existing. -Tha am pasgan-uidhe %x ann mu thràth. - -Target folder input field must not be empty. -Chan fhaod raon a' phasgain a bhith falamh. - -Please enter a target folder for versioning. -Cuir a-steach pasgan-targaide a chum versioning. - -Source folder %x not found. -Cha deach am pasgan tùsail %x a lorg. - -The following items have unresolved conflicts and will not be synchronized: -Tha còmstrithean aig na nithean a leanas fhathast is cha dèid an sioncronachadh: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -Tha diofar mòr eadar na pasganan a leanas. Dèan cinnteach gu bheil thu a' maidseadh nam pasganan ceart airson sioncronachadh. - -Not enough free disk space available in: -Chan eil rùm saor gu leòr air an diosga: - -Required: -Na tha feum air: - -Available: -Na tha ri làimh: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Thèid pasgan atharrachadh a tha 'na phàirt de dh'iomadh paidhir de phasgain. Nach doir thu sùil air roghainnean an t-sioncronachaidh? - -Synchronizing folder pair: -A' sioncronachadh paidhir nam pasgan: - -Generating database... -A' gintinn an stòir-dhàta... - -Creating a Volume Shadow Copy for %x... -A' cruthachadh lethbhreac sgàil draibh airson %x... - -Data verification error: %x and %y have different content. -Mearachd le dearbhadh an dàta: tha susbaint eadar-dhealaichte ann an %x agus %y. - -job name -ainm na h-obrach - -Synchronization stopped -Sguireadh dhen t-sioncronachadh - -Synchronization completed with errors -Chaidh an sioncronachadh a choileanadh ach bha mearachdan ann - -Synchronization completed with warnings -Chaidh a shioncronachadh ach bha rabhaidhean ann - -Nothing to synchronize -Chan eil dad ri shioncronachadh - -Synchronization completed successfully -Chaidh a shioncronachadh - -Saving log file %x... -A' sàbhaladh faidhle an loga %x... - -You can switch to FreeFileSync's main window to resolve this issue. -'S urrainn dhut leum a ghearradh gu prìomh-uinneag FreeFileSync gus an duilgheadas seo fhuasgladh. - -&Don't show this warning again -Na seall an rabha&dh seo a-rithist - -&Ignore -&Leig seachad - -&Switch -&Dèan suids - -Switching to FreeFileSync's main window -A' gearradh leum gu prìomh-uinneag FreeFileSync - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - -A' feuchainn ris às ùr gu fèin-obrachail ann an %x diog... -A' feuchainn ris às ùr gu fèin-obrachail ann an %x dhiog... -A' feuchainn ris às ùr gu fèin-obrachail ann an %x diogan... -A' feuchainn ris às ùr gu fèin-obrachail ann an %x diog... - - -&Ignore subsequent errors -Le&ig seachad mearachdan às a dhèidh seo - -Serious Error -Mearachd mhòr - -Check for Program Updates -Thoir sùil ach a bheil ùrachadh ann airson a' phrògraim - -A new version of FreeFileSync is available: -Tha tionndadh ùr de FreeFileSync ann: - -Download now? -A bheil thu airson a luchdadh a-nuas an-dràsta? - -&Download -&Luchdaich a-nuas - -FreeFileSync is up to date. -Tha FreeFileSync cho ùr 's a ghabhas. - -Unable to connect to sourceforge.net. -Cha b' urrainn dhuinn ceangal a dhèanamh ri Sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Chan urrainn dhuinn àireamh an tionndaidh làithrich aig FreeFileSync a lorg air loidhne. A bheil thu airson sùil a thoirt thu fhèin? - -Symlink -Symlink - -Folder -Pasgan - -Full path -Slighe shlan - -Name -Ainm - -Relative path -An t-slighe dhàimheach - -Base folder -Bun-phasgan - -Size -Meud - -Date -Ceann-là - -Extension -Leudachan - -Category -Roinn seòrsa - -Action -Gnìomh - -Drag && drop -Slaod ┐ leig às - -Close progress dialog -Dùin còmhradh an adhartais - -Standby -Cuir 'na fhuireachas - -Log off -Clàraich a-mach - -Shut down -Dùin sìos an siostam - -Hibernate -Geamhraich - -Alternate comparison settings -Roghainnean eile airson coimeasadh - -Alternate synchronization settings -Roghainnean eile airson sioncronachadh - -Local filter -Criathrag ionadail - -Active -Gnìomhach - -None -Chan eil gin - -Remove alternate settings -Thoir air falbh na roghainnean eile - -Clear filter settings -Falamhaich roghainnean na criathraige - -Copy -Dèan lethbhreac - -Paste -Cuir ann - -Alternate Comparison Settings -Roghainnean eile airson coimeasadh - -Alternate Synchronization Settings -Roghainnean eile airson sioncronachadh - -Local Filter -Criathrag ionadail - -&New -Ù&r - -&Save -&Sàbhail - -Save as &batch job... -Sàbhail mar obair &baidse... - -1. &Compare -1. &Dèan coimeas - -2. &Synchronize -2. &Dèan sioncronachadh - -&Global settings -&Na roghainnean uile-choitcheann - -&Language -&Cànan - -&Find... -L&org... - -&Export file list... -Às-p&hortaich liosta nam faidhle... - -&Tools -&Innealan - -&Check now -&Thoir sùil an-dràsta - -Check &automatically once a week -Thoir sùil gu &fèin-obrachail turas san t-seachdain - -&Check for new version -Thoir sùil a&ch a bheil tionndadh ùr ann - -Compare -Dèan coimeas - -Cancel -Sguir dheth - -Synchronize -Dèan sioncronachadh - -Add folder pair -Cuir paidhir de phasgain ris - -Remove folder pair -Thoir air falbh am paidhir seo de phasgain - -Swap sides -Cuir an dà thaobh an àite a chèile - -Close search bar -Dùin bàr an luirg - -Find: -Lorg: - -Match case -An aire do litrichean mòra 's beaga - -Save as batch job -Sàbhail mar obair baidse - -Hide excluded items -Falaich nithean a chaidh a dhùnadh às - -Show filtered or temporarily excluded files -Seall faidhlichean a tha 'gan dùnadh a-mach no air an criathradh a-mach an-dràsta fhèin - -Number of files and folders that will be created -Àireamh nam faidhle 's nam pasgan a thèid a chruthachadh - -Number of files that will be overwritten -Àireamh nam faidhle a thèid sgrìobhadh thairis orra - -Number of files and folders that will be deleted -Àireamh nam faidhle 's nam pasgan a thèid a sguabadh às - -Total bytes to copy -Co mheud baidht a thèid lethbhreac a dhèanamh dhiubh - -Select a variant: -Tagh eug-samhail: - -Identify equal files by comparing modification time and size. -Lorg faidhlichean a tha co-ionnann le bhith a' dèanamh coimeas eadar an cuid meud is ama. - -Identify equal files by comparing the file content. -Lorg faidhlichean a tha co-ionnann le bhith a' dèanamh coimeas eadar susbaint nam faidhlichean. - -Symbolic links: -Ceanglaichean samhlachail: - -More information -Barrachd fiosrachaidh - -OK -Ceart ma-thà - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Lorg is sìolaich na h-atharraichean air an dà thaobh. Mothaichidh sinn do rudan a chaidh a sguabadh às, a ghluasad no còmhstrithean gu fèin-obrachail le stòr-dàta. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Cruthaich lethbhreac-glèidhidh sgàthanaichte dhen phasgan air an taobh chlì a bhios gu tur co-ionnann ris a' phasgan air an taobh deas an dèidh sioncronachaidh. - -Copy new and updated files to the right folder. -Cuir lethbhreac de dh'fhaidhlichean ùra 's ùraichte dhan phasgan air an taobh deas. - -Configure your own synchronization rules. -Sònraich riaghailtean sioncronachaidh thu fhèin. - -Detect moved files -Mothaich do dh'fhaidhlichean a chaidh a ghluasad - -Requires database files. Not supported by all file systems. -Feumaidh seo faidhlichean stòir-dhàta. Chan eil gach siostam fhaidhlichean a' cur taic ri seo. - -Delete files: -Sguab às na faidhlichean: - -Permanent -Buan - -Delete or overwrite files permanently -Sguab às no sgrìobh thairis air faidhlichean gu buan - -Recycle bin -Am biona - -Back up deleted and overwritten files in the recycle bin -Dèan lethbhreac-glèidhidh de dh'fhaidhlichean sa bhiona a chaidh a sguabadh às no a chaidh sgrìobhadh thairis orra - -Versioning -Versioning - -Move files to a user-defined folder -Gluais na faidhlichean gu àite a shònraich an cleachdaiche - -Naming convention: -Gnàthas nan ainmean: - -Show examples -Seall buill-eisimpleir - -Handle errors: -Mearachdan làimhseachaidh: - -Ignore -Leig seachad - -Hide all error and warning messages -Falaich gach teachdaireachd mu mhearachdan no rabhaidhean - -Pop-up -Priob-uinneag - -Show pop-up on errors or warnings -Seall priob-uinneagan a thaobh mhearachdan no rabhaidhean - -On completion: -An dèidh coileanaidh: - -Start synchronization now? -A bheil thu airson tòiseachadh air an t-sioncronachadh an-dràsta? - -Variant: -Eug-samhail: - -Statistics -Stats - -&Don't show this dialog again -&Na seall an còmhradh seo a-rithist - -Items found: -Rudan a chaidh a lorg: - -Speed: -Astar: - -Time remaining: -An ùine a tha air fhàgail: - -Time elapsed: -An ùine a dh'fhalbh: - -Synchronizing... -A' sioncronachadh... - -Minimize to notification area -Lùghdach 's gluais gu raon nam brathan - -Close -Dùin - -&Pause -&Cuir 'na stad - -Stop -Sguir dheth - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Cruthaich faidhle batch airson sioncronachadh a dh'obraicheas gun thusa a bhith an làthair. Gus a thòiseachadh, dèan briogadh dùbailte air an fhaidhle seo ann am planadair shaothraichean: %x - -Stop synchronization at first error -Sguir dhen t-sioncronachadh aig a' chiad mhearachd - -Show progress dialog -Seall còmhradh an adhartais - -Save log: -Sàbhail an loga: - -Limit: -Crìoch: - -Limit maximum number of log files -Cuingich an àireamh as motha de dh'fhaidhlichean an loga - -How can I schedule a batch job? -Ciamar a chuireas mi obair baidse air an sgeideal? - -&Recycle bin -A&m biona - -Delete on both sides -Sguab às air an dà thaobh - -Delete on both sides even if the file is selected on one side only -Sguab às air an dà thaobh fiù mur an deach am faidhle a thaghadh ach air aon taobh - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Tagh riaghailteach criathraidh gus seòrsachan àraidh de dh'fhaidhlichean fhàgail às an t-sioncronachadh. Cuir a-steach slighean fhaidhlichean a-rèir paidhir an dà phasgan aca. - -Include: -Gabh a-staigh: - -Exclude: -Dùin a-mach: - -Time span: -An rainse ama: - -File size: -Meud an fhaidhle: - -Minimum: -Air a' char as lugha: - -Maximum: -Air a' char as motha: - -&Clear -Fala&mhaich - -The following settings are used for all synchronization jobs. -Thèid na roghainnean a leanas a chleachdadh airson a h-uile obair sioncronachaidh. - -Fail-safe file copy -Dèan lethbhreac nach gabh fàilligeadh - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -Cuir lethbhreac dheth ann am faidhle sealach (*.ffs_tmp) mus sgrìobhar thairis air an targaid. -Nì seo cinnteach gum bi fuasgladh ann ma thachras mearachd mhòr. - - -(recommended) -(mholamaid seo) - -Copy locked files -Dèan lethbhreac de dh'fhaidhlichean glaiste - -Copy shared or locked files using the Volume Shadow Copy Service. -Dèan lethbhreac de dh'fhaidhlichean co-roinnte no glaiste le seirbheis lethbhreacan sgàil an draibh. - -(requires administrator rights) -(feumaidh seo còraichean rianadair) - -Copy file access permissions -Dèan lethbhreac de cheadan-inntrigidh nam faidhle - -Transfer file and folder permissions. -Tar-chuir am faidhle 's ceadan a' phasgain - -Automatic retry on error: -Feuch ris a-rithist gu fèin-obrachail ri linn mearachd: - -Retry count: -Co mheud turas a dh'fheudadh ris a-rithist: - -Delay (in seconds): -An dàil (ann an diogan): - -Customize context menu: -Gnàthaich an clàr-taice co-theacsail - -Description -Tuairisgeul - -Restore hidden windows -Aisig na h-uinneagan falaichte - -&Default -&Bun-roghainn - -Source code written in C++ using: -Chaidh an còd tùsail a sgrìobhadh ann an C++ le taic: - -If you like FreeFileSync -Ma tha FreeFileSync a' còrdadh riut - -Donate with PayPal -Nach doir sibh tabhartas le PayPal? - -Feedback and suggestions are welcome -Tha sinn a' cur fàilte mhòr air beachd is moladh sam bith - -Homepage -An duilleag-dhachaigh - -Email -Post-d - -Published under the GNU General Public License -Air fhoillseachadh fo GNU General Public License - -Many thanks for localization: -Tha sinn fada an comain nan daoine a leanas airson eadar-theangachadh: - -Save as Batch Job -Sàbhail mar obair baidse - -Delete Items -Sguab às na nithean - -Global Settings -Na roghainnean uile-choitcheann - -Select Time Span -Tagh an rainse ama - -Folder Pairs -Paidhrichean nam pasganan - -Find -Lorg - -Overview -Foir-shealladh - -Configuration -Rèiteachadh - -Main Bar -Am prìomh-bhàr - -Filter Files -Criathraich na faidhlichean - -Select View -Tagh sealladh - -Open... -Fosgail... - -Save -Sàbhail - -Compare both sides -Dèan coimeas air an dà thaobh - -Comparison settings -Roghainnean a' choimeasaidh - -Synchronization settings -Roghainnean an t-sioncronachaidh - -Start synchronization -Tòisich air an t-sioncronachadh - -Confirm -Dearbh - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - -A bheil thu cinnteach gu bheil thu airson an àithne seo (%y) a ruith air %x nì? -A bheil thu cinnteach gu bheil thu airson an àithne seo (%y) a ruith air %x nì? -A bheil thu cinnteach gu bheil thu airson an àithne seo (%y) a ruith air %x nithean? -A bheil thu cinnteach gu bheil thu airson an àithne seo (%y) a ruith air %x nì? - - -&Execute -&Cuir an gnìomh - - -1 directory -%x directories - - -%x phasgan -%x phasgan -%x pasgain -%x pasgan - - - -1 file -%x files - - -%x fhaidhle -%x fhaidhle -%x faidhlichean -%x faidhle - - - -%y of 1 row in view -%y of %x rows in view - - -%y de %x ràgh san t-sealladh -%y de %x ràgh san t-sealladh -%y de %x ràghan san t-sealladh -%y de %x ràgh san t-sealladh - - -Set direction: -Suidhich a' chomhair: - -multiple selection -Ioma-thaghadh - -Include via filter: -Gach a-steach slighe na criathraige: - -Exclude via filter: -Dùin a-mach le criathrag: - -Exclude temporarily -Dùin a-mach gu sealach - -Include temporarily -Gabh a-steach gu sealach - -Delete -Sguab às - -Include all -Gabh a-steach na h-uile - -Exclude all -Dùin a-mach na h-uile - -Show icons: -Meud nan ìomhaigheagan: - -Small -Beag - -Medium -Meadhanach - -Large -Mòr - -Select time span... -Tagh an raon-ama... - -Default view -An sealladh bunaiteach - -Show "%x" -Seall "%x" - -Last session -An seisean mu dheireadh - -Folder Comparison and Synchronization -Coimeas eadar na pasgain is sioncronachadh - -Configuration saved -Chaidh an rèiteachadh a shàbhaladh - -FreeFileSync batch -FreeFileSync batch - -Do you want to save changes to %x? -An sàbhail sinn dhut na h-atharraichean air %x? - -Never save &changes -Na sàbhail atharrai&chean idir - -Do&n't save -&Na sàbhail - -Filter -Criathrag - -Show files that exist on left side only -Na seall ach faidhlichean a tha air an taobh chlì a-mhàin - -Show files that exist on right side only -Na seall ach faidhlichean a tha air an taobh deas a-mhàin - -Show files that are newer on left -Seall faidhlichean a tha nas ùire air an taobh chlì - -Show files that are newer on right -Seall faidhlichean a tha nas ùire air an taobh deas - -Show files that are equal -Seall faidhlichean a tha co-ionnann - -Show files that are different -Seall faidhlichean a tha eadar-dhealaichte - -Show conflicts -Seall còmhstrithean - -Show files that will be created on the left side -Seall faidhlichean a thèid a chruthachadh air an taobh chlì - -Show files that will be created on the right side -Seall faidhlichean a thèid a chruthachadh air an taobh deas - -Show files that will be deleted on the left side -Seall faidhlichean a thèid a sguabadh às air an taobh chlì - -Show files that will be deleted on the right side -Seall faidhlichean a thèid a sguabadh às air an taobh deas - -Show files that will be overwritten on left side -Seall faidhlichean a thèid a thar-sgrìobhadh air an taobh chlì - -Show files that will be overwritten on right side -Seall faidhlichean a thèid a thar-sgrìobhadh air an taobh deas - -Show files that won't be copied -Seall faidhlichean nach dèid lethbhreac a dhèanamh dhiubh - -Set as default -Suidhich mar a' bhun-roghainn - -All folders are in sync -Tha gach pasgan air a shioncronachadh - -Synchronization Settings -Roghainnean an t-sioncronachaidh - -Comparison Settings -Roghainnean a' choimeasaidh - -Cannot find %x -Chan urrainn dhuinn %x a lorg. - -Comma-separated values -Luachan le cromagan eatarra - -File list exported -Chaidh liosta nam faidhle às-phortadh - -Searching for program updates... -A' lorg ùrachaidhean a' phrògraim... - -Scanning... -'Ga sganadh... - -Comparing content... -A' dèanamh coimeas eadar an cuid susbaint... - -Info -Fiosrachadh - -Warning -Rabhadh - -Paused -'Na stad - -Initializing... -A' tòiseachadh... - -Stopped -Chaidh stad a chur air - -Completed -Deiseil - -&Continue -Lean air adhar&t - -Log -Logaich - -Today -An-diugh - -This week -An t-seachdain seo - -This month -Am mìos seo - -This year -Am bliadhna - -Last x days -Na x làithean seo chaidh - -Byte -Baidht - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -A bheil thu cinnteach gu bheil thu airson an %x nì seo a chur dhan bhiona? -A bheil thu cinnteach gu bheil thu airson an %x nì seo a chur dhan bhiona? -A bheil thu cinnteach gu bheil thu airson na %x nithean seo a chur dhan bhiona? -A bheil thu cinnteach gu bheil thu airson an %x nì seo a chur dhan bhiona? - - -Move -Gluais - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -A bheil thu cinnteach gu bheil thu airson an %x nì seo a sguabadh às? -A bheil thu cinnteach gu bheil thu airson na %x nì seo a sguabadh às? -A bheil thu cinnteach gu bheil thu airson na %x nithean seo a sguabadh às? -A bheil thu cinnteach gu bheil thu airson na %x nì seo a sguabadh às? - - -Exclude -Dùin a-mach - -Direct -Dìreach - -Follow -Lean - -Copy NTFS permissions -Dèan lethbhreac de cheadan NTFS - -Integrate external applications into context menu. The following macros are available: -Amalaichidh seo aplacaidean air an taobh a-muigh dhan chlàr-taice cho-theacsail. Tha na macrothan a leanas ri làimh: - -- full file or folder name -- ainm slàn dhen fhaidhle no dhen phasgan - -- folder part only -- cuid a' phasgain a-mhàin - -- Other side's counterpart to %item_path% -- Seise %item_folder% aig a' choimpiutair eile - -- Other side's counterpart to %item_folder% -- Seise %item_folder% aig a' choimpiutair eile - -Restore all hidden windows and warnings? -A bheil thu airson gach uinneag is rabhadh falaichte aiseaga? - -Leave as unresolved conflict -Fàg mar còmhstri gun rèiteachadh - -Replace -Cuir 'na àite - -Move files and replace if existing -Gluais na faidhlichean 's cuir iad an àite na feadhainn làithreach ma tha gin ann - -Time stamp -Stampa ama - -Append a timestamp to each file name -Cuir stampa-ama ris ainm gach faidhle - -File -Faidhle - -YYYY-MM-DD hhmmss -BBBB-MM-LL uummdd - -Files -Faidhlichean - -Items -Nithean - -Percentage -Ceudad - -Cannot monitor directory %x. -Chan urrainn dhuinn sùil a chumail air %x. - -Conversion error: -Mearachd iompachaidh: - -Cannot delete file %x. -Cha ghabh am faidhle %x a sguabadh às. - -The file is locked by another process: -Tha am faidhle glaiste aig pròiseas eile: - -Cannot move file %x to %y. -Cha ghabh am faidhle %x a ghluasad dha %y. - -Cannot delete directory %x. -Cha ghabh am pasgan %x a sguabadh às. - -Cannot write file attributes of %x. -Chan urrainn dhuinn buadhan an fhaidhle %x a sgrìobhadh. - -Cannot write modification time of %x. -Cha ghabh àm atharrachaidh %x a sgrìobhadh. - -Cannot read security context of %x. -Cha ghabh susbaint tèarainteachd %x a leughadh. - -Cannot write security context of %x. -Cha ghabh susbaint tèarainteachd %x a sgrìobhadh. - -Cannot read permissions of %x. -Cha ghabh ceadan %x a leughadh. - -Cannot write permissions of %x. -Cha ghabh ceadan %x a sgrìobhadh. - -Cannot create directory %x. -Cha ghabh am pasgan %x a chruthachadh. - -Cannot create symbolic link %x. -Cha ghabh an symbolic link %x a chruthachadh - -Cannot find system function %x. -Chan urrainn dhuinn foincsean an t-siostaim %x a lorg. - -Cannot copy file %x to %y. -Cha ghabh lethbhreac an fhaidhle %x a chur gu %y. - -Type of item %x is not supported: -Chan eil taic ri nì dhen t-seòrsa %x: - -Cannot resolve symbolic link %x. -Cha ghabh an symbolic link %x fhuasgladh. - -Cannot open directory %x. -Cha ghabh am pasgan %x fhosgladh. - -Cannot enumerate directory %x. -Cha ghabh am pasgan %x àireamhachadh. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -%x mhionaid -%x mhionaid -%x mionaidean -%x mionaid - - - -1 hour -%x hours - - -%x uair a thìde -%x uair a thìde -%x uairean a thìde -%x uair a thìde - - - -1 day -%x days - - -%x latha -%x latha -%x làithean -%x latha - - -Unable to register to receive system messages. -Chan urrainn dhuinn clàradh gus teachdaireachdan an t-siostaim fhaighinn. - -Cannot set privilege %x. -Cha ghabh a' phribhleid %x a shuidheachadh. - -Unable to suspend system sleep mode. -Chan urrainn dhuinn modh cadal an t-siostaim a chur dheth. - -Cannot change process I/O priorities. -Chan urrainn dhuinn na prìomhachasan I/O atharrachadh. - -Unable to move %x to the recycle bin. -Cha ghabh %x a ghluasad dhan bhiona. - -Cannot determine final path for %x. -Chan urrainn dhuinn an t-slighe dheireannach airson %x a dhearbhadh. - -Error Code %x: -Còd na mearachd %x: - diff --git a/BUILD/Languages/serbian.lng b/BUILD/Languages/serbian.lng deleted file mode 100644 index 679cfbe1..00000000 --- a/BUILD/Languages/serbian.lng +++ /dev/null @@ -1,1530 +0,0 @@ -
    - Cрпски - Балкански Шпијун - sr_RS - flag_serbia.png - 3 - n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2 -
    - -&Check - - -Retrying operation... - - -Both sides have changed since last synchronization. -Обе су стране промењене од последње синхронизације. - -Cannot determine sync-direction: -Не могу одредити смер синхронизације: - -No change since last synchronization. -Нема промена од задње синхронизације. - -The database entry is not in sync considering current settings. -Унос у бази података није синхронизован у односу на тренутна подешавања. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Постављам подразумеване синхронизацијске смерове: Старе датотеке биће замењене новијим датотекама. - -Checking recycle bin availability for folder %x... -Проверавам доступност Корпе за смеће за фолдер %x... - -Moving file %x to the recycle bin -Премештам датотеку %x у Корпи за смеће - -Moving folder %x to the recycle bin -Премештам фолдер %x у Корпи за смеће - -Moving symbolic link %x to the recycle bin -Премештам симболичну везу %x у Корпи за смеће - -Deleting file %x -Брисање датотеке %x - -Deleting folder %x -Брисање фолдера %x - -Deleting symbolic link %x -Брисање симболичне везе %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -Корпа за смеће није доступна за следеће фолдере. Уместо тога датотеке биће обрисане трајно: - -An exception occurred -Догодило се изузеће - -A directory path is expected after %x. -Путања фолдера се очекује после %x. - -Syntax error -Синтаксичка грешка - -Cannot open file %x. -Не могу отворити датотеку %x. - -File %x does not contain a valid configuration. -Датотека %x не садржи валидну конфигурацију. - -Unequal number of left and right directories specified. -Неједнак број левих и десних фолдера је дефинисан. - -The config file must not contain settings at directory pair level when directories are set via command line. -Конфигурациона датотека не сме садржавати подешавања на нивоу фолдерског пара када су фолдери подешени по командној линији. - -Directories cannot be set for more than one configuration file. -Фолдери не могу бити подешени за више од једне конфигурационе датотеке. - -Command line -Командна линија - -Syntax: -Синтакса: - -config files -конфигурационе датотеке - -directory -фолдер - -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Било који број FreeFileSync .ffs_gui и/или .ffs_batch конфигурационих датотека. - -Any number of alternative directories for at most one config file. -Било који број алтернативних фолдера за најмање једну конфигурациону датотеку. - -A folder input field is empty. -Поље за одабир фолдера је празно. - -The corresponding folder will be considered as empty. -Одговарајући фолдер сматраће се празним. - -Cannot find the following folders: -Не могу пронаћи следеће фолдере: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -Можете игнорисати ову грешку да би сматрали сваки фолдер празним. Фолдери ће бити креирани аутоматски током синхронизације. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -Следећи фолдери имају зависне путање. Будите пажљиви када подешавате синхронизациона правила: - -File %x has an invalid date. -Датотека %x има невалидни датум. - -Date: -Датум: - -Files %x have the same date but a different size. -Датотеке %x имају исти датум али другачију величину. - -Size: -Величина: - -Items differ in attributes only -Ставке се разликују само у атрибутима - -Resolving symbolic link %x -Разрешавање симболичне везе %x - -Comparing content of files %x -Упоређујем садржај датотека %x - -Generating file list... -Стварам листу датотека... - -Starting comparison -Покретање упоређивања - -Calculating sync directions... -Одређујем смерове синхронизације... - -Out of memory. -Недостатак меморије. - -Item exists on left side only -Ставка постоји само на левој страни - -Item exists on right side only -Ставка постоји само на десној страни - -Left side is newer -Лева страна је новија - -Right side is newer -Десна страна је новија - -Items have different content -Ставке имају различит садржај - -Both sides are equal -Обе стране су једнаке - -Conflict/item cannot be categorized -Конфликт/ставка не може бити разврстана - -Copy new item to left -Копирај нову ставку лево - -Copy new item to right -Копирај нову ставку десно - -Delete left item -Избриши леву ставку - -Delete right item -Избриши десну ставку - -Move file on left -Премести датотеку лево - -Move file on right -Премести датотеку десно - -Overwrite left item -Замени леву ставку - -Overwrite right item -Замени десну ставку - -Do nothing -Не ради ништа - -Update attributes on left -Освежи атрибуте лево - -Update attributes on right -Освежи атрибуте десно - -Database file %x is incompatible. -Датотека базе %x је некомпатибилна. - -Initial synchronization: -Почетна синхронизација: - -Database file %x does not yet exist. -Датотека базе %x још не постоји. - -Database file is corrupt: -Датотека базе је оштећена: - -Cannot write file %x. -Не могу уписати датотеку %x. - -Cannot read file %x. -Не могу читати датотеку %x. - -Database files do not share a common session. -Датотеке базе не деле заједничку сесију. - -Searching for folder %x... -Тражим фолдер %x... - -Cannot read file attributes of %x. -Не могу прочитати атрибуте од %x. - -Cannot get process information. -Не могу добити информације о процесу. - -Waiting while directory is locked (%x)... -Чека се док се фолдер не закључа (%x)... - - -1 sec -%x sec - - -%x сек -%x сек -%x сек - - -Creating file %x -Правим датотеку %x - -Items processed: -Обрађени елементи: - -Items remaining: -Преостали елементи: - -Total time: -Укупно време: - - -1 byte -%x bytes - - -%x бајт -%x бајта -%x бајтова - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Грешка у анализи датотеке %x, ред %y, колона %z. - -Cannot set directory lock for %x. -Не могу закључати фолдер %x. - -Scanning: -Претражујем: - - -1 thread -%x threads - - -%x нит -%x нити -%x нити - - -Encoding extended time information: %x -Кодирам проширене информације о времену: %x - -/sec -/сек - -%x items/sec -%x ставки/секунди - -Configuration file %x loaded partially only. -Датотека подешавања %x учитана само делимично. - -Show in Explorer -Прикажи у Експлореру - -Open with default application -Отвори са подразумеваном апликацијом - -Browse directory -Одабери фолдер - -Cannot access the Volume Shadow Copy Service. -Не могу приступити Volume Shadow Copy сервису. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Молимо користите FreeFileSync 64-битну верзију за израду shadow копија на овом систему. - -Cannot load file %x. -Не могу учитати датотеку %x. - -Cannot determine volume name for %x. -Не могу утврдити назив партиције за %x. - -Volume name %x is not part of file path %y. -Назив партиције %x није део путање датотеке %y. - -Stop requested: Waiting for current operation to finish... -Заустављање захтевано: Чека се да се тренутна акција заврши... - -Unable to create timestamp for versioning: -Није могуће стварање временске ознаке за верзију: - -Cannot read the following XML elements: -Не могу прочитати следеће XML елементе: - -&Open... -&Отвори... - -Save &as... -Сачувај &као... - -&Quit -&Излаз - -&Program -&Програм - -&View help -&Погледај помоћ - -&About -&О програму - -&Help -&Помоћ - -Usage: -Употреба: - -1. Select folders to watch. -1. Одаберите фолдере за надгледање. - -2. Enter a command line. -2. Унесите командну линију. - -3. Press 'Start'. -3. Притисните 'Старт'. - -To get started just import a .ffs_batch file. -Да би почели увезите .ffs_batch датотеку. - -Folders to watch: -Фолдери за надгледање: - -Add folder -Додај фолдер - -Remove folder -Уклони фолдер - -Browse -Одабери - -Select a folder -Одаберите фолдер - -Idle time (in seconds): -Време мировања (у секундама): - -Idle time between last detected change and execution of command -Време мировања између задње препознате промене и извршења наредбе - -Command line: -Командна линија: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -Наредба ће бити покренута ако се: -- датотеке или подфолдери промене -- нови фолдери појаве (нпр. укључење USB стика) - - -&Start -&Покрени - -About -О програму - -Build: %x -Подверзија: %x - -All files -Све датотеке - -Automated Synchronization -Аутоматска синхронизација - -Directory monitoring active -Активно надгледање фолдера - -Waiting until all directories are available... -Чекање да сви фолдери буду доступни... - -Error -Грешка - -&Restore -&Врати - -&Show error -&Прикажи грешку - -&Exit -&Излаз - -Incorrect command line: -Нетачна командна линија: - -&Retry -&Понови - -File content -Садржај датотеке - -File time and size -Време и величина датотеке - -Two way -Двосмерно - -Mirror -Огледално - -Update -Ажурирарно - -Custom -Специфично - -Multiple... -Многоструко... - -Moving file %x to %y -Премештам датотеку %x у %y - -Moving folder %x to %y -Премештам фолдер %x у %y - -Moving symbolic link %x to %y -Премештам симболичну везу %x у %y - -Removing old versions... -Уклањам старије верзије... - -Creating symbolic link %x -Креирам симболичну везу %x - -Creating folder %x -Креирам фолдер %x - -Overwriting file %x -Замењујем датотеку %x - -Overwriting symbolic link %x -Замењујем симболичну везу %x - -Verifying file %x -Проверавам датотеку %x - -Updating attributes of %x -Обнављам атрибуте од %x - -Cannot find %x. -Не могу пронаћи %x. - -Target folder %x already existing. -Одредишни фолдер %x већ постоји. - -Target folder input field must not be empty. -Поље за одабир одредишног фолдера не може бити празно. - -Please enter a target folder for versioning. -Молим унесите одредишни фолдер за верзионирање. - -Source folder %x not found. -Изворни фолдер %x није пронађен. - -The following items have unresolved conflicts and will not be synchronized: -Следеће ставке имају неразрешених конфликата и неће бити синхронизоване: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -Следећи фолдери су значајно различити. Обезбедите да сигурно упарујете праве фолдере за синхронизацију. - -Not enough free disk space available in: -Недовољно простора на диску у: - -Required: -Потребно: - -Available: -Доступно: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Фолдер ће бити промењен који је део више парова фолдера. Молимо проверите синхронизацијска подешавања. - -Synchronizing folder pair: -Синхронизовање фолдерског пара: - -Generating database... -Генерисање базе података... - -Creating a Volume Shadow Copy for %x... -Креирање Volume Shadow Copy за %x... - -Data verification error: %x and %y have different content. -Грешка при провери података: %x и %y имају различит садржај. - -job name -име задатка - -Synchronization stopped -Синхронизација заустављена - -Synchronization completed with errors -Синхронизација завршена с грешкама - -Synchronization completed with warnings -Синхронизација завршена с упозорењима - -Nothing to synchronize -Нема ничега за синхронизацију - -Synchronization completed successfully -Синхронизација успешно завршена - -Saving log file %x... -Уписујем лог датотеку %x... - -You can switch to FreeFileSync's main window to resolve this issue. -Можете се пребацити на главни прозор FreeFileSync-а да би разрешили ову ствар. - -&Don't show this warning again -&Не приказуј ово упозорење поновно - -&Ignore -&Игнориши - -&Switch -&Замени - -Switching to FreeFileSync's main window -Пребацујем се на главни прозор FreeFileSync-а - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - -Аутоматски покушај за %x секунду... -Аутоматски покушај за %x секунде... -Аутоматски покушај за %x секунди... - - -&Ignore subsequent errors -&Занемари грешке које даље следе - -Serious Error -Озбиљна грешка - -Check for Program Updates -Провери постојање надоградње програма - -A new version of FreeFileSync is available: -Нова верзија FreeFileSync је доступна: - -Download now? -Преузети сада? - -&Download -&Преузми - -FreeFileSync is up to date. -FreeFileSync је ажуриран. - -Unable to connect to sourceforge.net. -Не могу се повезати на sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Не могу на мрежи пронаћи тренутни број верзије FreeFileSync-а. Да ли желите да проверите ручно? - -Copy -Копирај - -Symlink -Сим-веза - -Folder -Фолдер - -Full path -Пуна путања - -Name -Име - -Relative path -Релативна путања - -Base folder -Основни фолдер - -Size -Величина - -Date -Датум - -Extension -Екстензија - -Category -Категорија - -Action -Акција - -Drag && drop -Вуци && пусти - -Close progress dialog -Затвори дијалог прогреса - -Standby -Пређи у стање приправности - -Log off -Одјави се - -Shut down -Искључи рачунар - -Hibernate -Хибернација - -Alternate comparison settings -Алтернативна подешавања упоређивања - -Alternate synchronization settings -Алтернативна подешавања синхронизације - -Local filter -Локални филтер - -Active -Активан - -None -Ниједан - -Remove alternate settings -Уклони алтернативна подешавања - -Clear filter settings -Уклони подешавања филтера - -Paste -Залепи - -Alternate Comparison Settings -Алтернативна подешавања упоређивања - -Alternate Synchronization Settings -Алтернативна подешавања синхронизације - -Local Filter -Локални филтер - -&New -&Ново - -&Save -&Сачувај - -Save as &batch job... -Сачувај као &беч задатак... - -1. &Compare -1. &Упореди - -2. &Synchronize -2. &Синхронизуј - -&Global settings -&Глобална подешавања - -&Language -&Језик - -&Find... -&Нађи... - -&Export file list... -&Извоз листе датотека... - -&Tools -&Алати - -&Check now -&Провери сада - -Check &automatically once a week -Провери &аутоматски једном недељно - -&Check for new version -&Провери постојање нове верзије - -Compare -Упореди - -Cancel -Одустани - -Synchronize -Синхронизуј - -Add folder pair -Додај фолдер пар - -Remove folder pair -Уклони фолдер пар - -Swap sides -Замени стране - -Close search bar -Затвори траку за претраживање - -Find: -Нађи: - -Match case -По величини слова - -Save as batch job -Сачувај као беч задатак - -Hide excluded items -Сакриј искључене ставке - -Show filtered or temporarily excluded files -Прикажи филтриране или привремено искључене датотеке - -Number of files and folders that will be created -Број датотека и фолдера који ће бити креирани - -Number of files that will be overwritten -Број датотека које ће бити замењене у садржају - -Number of files and folders that will be deleted -Број датотека и фолдера који ће бити избрисани - -Total bytes to copy -Укупно бајтова за копирање - -Select a variant: -Одаберите варијанту: - -Identify equal files by comparing modification time and size. -Идентификуј једнаке датотеке упоређивањем времена промена и величина. - -Identify equal files by comparing the file content. -Идентификуј једнаке датотеке упоређивањем садржаја датотека. - -Symbolic links: -Симболичне везе: - -More information -Више информација - -OK -У реду - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Пронађи и изврши промене на обе стране. Брисања, премештања и конфликти се откривају аутоматски употребом базе података. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Креирај огледални бекап левог фолдера који ће се тачно подударати са десним фолдером након синхронизације. - -Copy new and updated files to the right folder. -Копирај нове и ажуриране датотеке у десни фолдер. - -Configure your own synchronization rules. -Конфигуришите ваша властита синхронизациона правила. - -Detect moved files -Уочи премештене датотеке - -Requires database files. Not supported by all file systems. -Потребне датотеке базе. Није подржано за све системе датотека. - -Delete files: -Обриши датотеке: - -Permanent -Трајно - -Delete or overwrite files permanently -Трајно избриши или замени датотеке - -Recycle bin -Корпа за смеће - -Back up deleted and overwritten files in the recycle bin -Направи резервне копије обрисаних и замењених датотека у Корпи за смеће - -Versioning -Верзионирање - -Move files to a user-defined folder -Премести датотеке у кориснички одабран фолдер - -Naming convention: -Правило именовања: - -Show examples -Прикажи примере - -Handle errors: -Обрада грешака: - -Ignore -Игнориши - -Hide all error and warning messages -Сакриј све грешке и упозорења - -Pop-up -Искачући прозор - -Show pop-up on errors or warnings -Прикажи искачући прозор при грешкама и упозорењима - -On completion: -При завршетку: - -Start synchronization now? -Почни синхронизацију сада? - -Variant: -Варијанта: - -Statistics -Статистика - -&Don't show this dialog again -&Не приказуј овај диалог поновно - -Items found: -Пронађене ставке: - -Speed: -Брзина: - -Time remaining: -Преостало време: - -Time elapsed: -Протекло време: - -Synchronizing... -Синхронизујем... - -Minimize to notification area -Минимизирај у области за обавештења - -Close -Затвори - -&Pause -&Пауза - -Stop -Заустави - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Креирај беч датотеку за ненадзирану синхронизацију. Да би почели, кликните дуплим кликом ову датотеку или додајте задатак у таск менаџеру: %x - -Stop synchronization at first error -Заустави синхронизацију при првој грешци - -Show progress dialog -Прикажи дијалог прогреса - -Save log: -Сачувај лог: - -Limit: -Ограничи: - -Limit maximum number of log files -Ограничи максималан број лог датотека - -How can I schedule a batch job? -Како могу заказати беч задатак? - -&Recycle bin -&Корпа за смеће - -Delete on both sides -Избриши на обе стране - -Delete on both sides even if the file is selected on one side only -Избриши на обе стране чак иако је датотека селектована само на једној страни - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Одаберите филтерска подешавања да би искључили одређене датотеке из синхронизације. Унесите путање датотека релативно према њиховим одговарајућим фолдерским паровима. - -Include: -Укључи: - -Exclude: -Искључи: - -Time span: -Временско ограничење: - -File size: -Величина датотеке: - -Minimum: -Минимум: - -Maximum: -Максимум: - -&Clear -&Уклони - -The following settings are used for all synchronization jobs. -Следећа подешавања се користе за све синхронизацијске задатке. - -Fail-safe file copy -Копирање заштићено од грешака - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -Копирај у привремену датотеку (*.ffs_tmp) пре уклањања циљане ставке. -Ово гарантује непромењивост чак и у случају озбиљне грешке. - - -(recommended) -(препоручено) - -Copy locked files -Копирај закључане датотеке - -Copy shared or locked files using the Volume Shadow Copy Service. -Копирај дељене или закључане датотеке користећи Volume Shadow Copy сервис. - -(requires administrator rights) -(потребна администраторска права) - -Copy file access permissions -Копирај овлашћења приступа датотекама - -Transfer file and folder permissions. -Премести овлашћења приступа датотека и фолдера. - -Automatic retry on error: -Аутоматски покушај при грешци: - -Retry count: -Број покушаја: - -Delay (in seconds): -Паузирање (у секундама): - -Customize context menu: -Прилагоди контекстни мени: - -Description -Опис - -Restore hidden windows -Прикажи скривене прозоре - -&Default -&Подразумевано - -Source code written in C++ using: -Изворни код написан у C++ уз коришћење: - -If you like FreeFileSync -Ако вам се свиђа FreeFileSync - -Donate with PayPal -Донација са PayPal-ом - -Feedback and suggestions are welcome -Повратне информације и предлози су добродошли - -Homepage -Веб страница - -Email -И-меил - -Published under the GNU General Public License -Објављено под ГНУ Општом јавном лиценцом - -Many thanks for localization: -Велике похвале за локализацију: - -Save as Batch Job -Сачувај као беч задатак - -Delete Items -Избриши ставке - -Global Settings -Глобална подешавања - -Select Time Span -Изаберите временски распон - -Folder Pairs -Фолдерски парови - -Find -Пронађи - -Overview -Преглед - -Configuration -Подешавања - -Main Bar -Главна трака - -Filter Files -Филтрирање датотека - -Select View -Изаберите приказ - -Open... -Отвори... - -Save -Сачувај - -Compare both sides -Упореди обе стране - -Comparison settings -Подешавања упоређивања - -Synchronization settings -Подешавања синхронизације - -Start synchronization -Почни синхронизацију - -Confirm -Потврди - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - -Да ли стварно желите да извршите команду %y за %x ставку? -Да ли стварно желите да извршите команду %y за %x ставке? -Да ли стварно желите да извршите команду %y за %x ставки? - - -&Execute -&Изврши - - -1 directory -%x directories - - -%x фолдер -%x фолдера -%x фолдера - - - -1 file -%x files - - -%x датотека -%x датотеке -%x датотека - - - -%y of 1 row in view -%y of %x rows in view - - -%y од %x реда у приказу -%y од %x реда у приказу -%y од %x редова у приказу - - -Set direction: -Одабери смер: - -multiple selection -вишеструки одабир - -Include via filter: -Укључи по филтеру: - -Exclude via filter: -Искључи преко филтера: - -Exclude temporarily -Тренутно искључи - -Include temporarily -Тренутно укључи - -Delete -Избриши - -Include all -Укључи све - -Exclude all -Искључи све - -Show icons: -Прикажи иконе: - -Small -Мале - -Medium -Средње - -Large -Велике - -Select time span... -Одаберите временски распон... - -Default view -Подразумевани приказ - -Show "%x" -Прикажи "%x" - -Last session -Задња сесија - -Folder Comparison and Synchronization -Упоређивање и синхронизација фолдера - -Configuration saved -Подешавања сачувана - -FreeFileSync batch -FreeFileSync беч задатак - -Do you want to save changes to %x? -Да ли желите сачувати промене за %x? - -Never save &changes -Никад не сачувај &промене - -Do&n't save -Не&мој сачувати - -Filter -Филтрирање - -Show files that exist on left side only -Прикажи датотеке које постоје само на левој страни - -Show files that exist on right side only -Прикажи датотеке које постоје само на десној страни - -Show files that are newer on left -Прикажи датотеке које су новије лево - -Show files that are newer on right -Прикажи датотеке које су новије десно - -Show files that are equal -Прикажи једнаке датотеке - -Show files that are different -Прикажи датотеке које су различите - -Show conflicts -Прикажи конфликте - -Show files that will be created on the left side -Прикажи датотеке које ће бити креиране на левој страни - -Show files that will be created on the right side -Прикажи датотеке које ће бити креиране на десној страни - -Show files that will be deleted on the left side -Прикажи датотеке које ће бити избрисане на левој страни - -Show files that will be deleted on the right side -Прикажи датотеке које ће бити избрисане на десној страни - -Show files that will be overwritten on left side -Прикажи датотеке које ће бити замењене на левој страни - -Show files that will be overwritten on right side -Прикажи датотеке које ће бити замењене на десној страни - -Show files that won't be copied -Прикажи датотеке које неће бити копиране - -Set as default -Постави као подразумевано - -All folders are in sync -Сви фолдери су синхронизовани - -Synchronization Settings -Подешавања синхронизације - -Comparison Settings -Подешавања упоређивања - -Cannot find %x -Немогу пронаћи %x - -Comma-separated values -Зарезом одвојене вредности - -File list exported -Листа датотека експортована - -Searching for program updates... -Претражујем ажурирање за програм... - -Scanning... -Прегледавање... - -Comparing content... -Упоређујем садржај... - -Info -Инфо - -Warning -Упозорење - -Paused -Паузирано - -Initializing... -Покретање... - -Stopped -Заустављено - -Completed -Завршено - -&Continue -&Настави - -Log -Лог - -Today -Данас - -This week -Ове недеље - -This month -Овог месеца - -This year -Ове године - -Last x days -Задњих x дана - -Byte -Бајт - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Да ли стварно желите да преместите следећу %x ставку у Корпи за смеће? -Да ли стварно желите да преместите следеће %x ставке у Корпи за смеће? -Да ли стварно желите да преместите следећих %x ставки у Корпи за смеће? - - -Move -Премести - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Да ли стварно желите да обришете следећу %x ставку? -Да ли стварно желите да обришете следеће %x ставке? -Да ли стварно желите да обришете следећих %x ставки? - - -Exclude -Искључи - -Direct -Непосредно - -Follow -Следи - -Copy NTFS permissions -Копирај NTFS овлашћења - -Integrate external applications into context menu. The following macros are available: -Интегриши спољне апликације у контекстни мени. Следећи макрои су доступни: - -- full file or folder name -- цело име датотеке или фолдера - -- folder part only -- само фолдерски део - -- Other side's counterpart to %item_path% -- Дупликат с друге стране у %item_path% - -- Other side's counterpart to %item_folder% -- Дупликат с друге стране у %item_folder% - -Restore all hidden windows and warnings? -Прикажи све скривене прозоре и упозорења? - -Leave as unresolved conflict -Остави као неразрешени конфликт - -Replace -Замени - -Move files and replace if existing -Премести датотеке и замени их ако већ постоје - -Time stamp -Временска ознака - -Append a timestamp to each file name -Додај временску ознаку сваком имену датотеке - -File -Датотека - -YYYY-MM-DD hhmmss -ГГГГ-ММ-ДД ччммсс - -Files -Датотеке - -Items -Ставке - -Percentage -Проценат - -Cannot monitor directory %x. -Не могу надгледати фолдер %x. - -Conversion error: -Грешка при претварању: - -Cannot delete file %x. -Не могу избрисати датотеку %x. - -The file is locked by another process: -Датотека је блокирана другим процесом: - -Cannot move file %x to %y. -Не могу преместити датотеку %x у %y. - -Cannot delete directory %x. -Не могу избрисати фолдер %x. - -Cannot write file attributes of %x. -Не могу уписати својства од %x. - -Cannot write modification time of %x. -Не могу уписати време промене %x. - -Cannot read security context of %x. -Не могу читати безбедносни садржај %x. - -Cannot write security context of %x. -Не могу уписати безбедносни садржај %x. - -Cannot read permissions of %x. -Не могу читати овлашћења од %x. - -Cannot write permissions of %x. -Не могу уписати овлашћења за %x. - -Cannot create directory %x. -Не могу креирати фолдер %x. - -Cannot create symbolic link %x. -Не могу креирати симболичну везу %x. - -Cannot find system function %x. -Не могу пронаћи системску функцију %x. - -Cannot copy file %x to %y. -Не могу копирати датотеку %x на %y. - -Type of item %x is not supported: -Тип ставке %x није подржан: - -Cannot resolve symbolic link %x. -Не могу разрешити симболичну везу %x. - -Cannot open directory %x. -Не могу отворити фолдер %x. - -Cannot enumerate directory %x. -Не могу излистати фолдер %x. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -%x мин -%x мин -%x мин - - - -1 hour -%x hours - - -%x сат -%x сата -%x сати - - - -1 day -%x days - - -%x дан -%x дана -%x дана - - -Unable to register to receive system messages. -Није могућа регистрација примања системских порука. - -Cannot set privilege %x. -Не могу поставити права за %x. - -Unable to suspend system sleep mode. -Није могуће суспендовање мода спавања система. - -Cannot change process I/O priorities. -Не може се променити процес I/O приоритета - -Unable to move %x to the recycle bin. -Није могуће преместити %x у Корпи за смеће. - -Cannot determine final path for %x. -Не могу утврдити коначну путању за %x. - -Error Code %x: -Грешка број %x: - diff --git a/BUILD/Languages/slovenian.lng b/BUILD/Languages/slovenian.lng deleted file mode 100644 index 6591bc68..00000000 --- a/BUILD/Languages/slovenian.lng +++ /dev/null @@ -1,1539 +0,0 @@ -
    - Slovenščina - Matej Badalič, Tine Mlakar - sl_SI - flag_slovenia.png - 4 - n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3 -
    - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -&Check - - -Retrying operation... - - -Both sides have changed since last synchronization. -Obe strani sta se spremenili od zadnje sinhronizacije. - -Cannot determine sync-direction: -Ne morem določiti sinhronizacijske smeri. - -No change since last synchronization. -Ni sprememb od zadnje sinhronizacije. - -The database entry is not in sync considering current settings. -Glede na trenutne nastavitve vnos v podatkovni bazi ni sinhroniziran. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Nastavljanje privzetih smeri sinhronizacije: Stare datoteke bodo prepisane z novimi datotekami. - -Checking recycle bin availability for folder %x... -Preverjam razpoložljivost koša za mapo %x... - -Moving file %x to the recycle bin -Premikam datoteko %x koš - -Moving folder %x to the recycle bin -Premikam imenik %x v koš - -Moving symbolic link %x to the recycle bin -Premikam simbolično povezavo %x v koš - -Deleting file %x -Brisanje datoteke %x - -Deleting folder %x -Brisanje mape %x - -Deleting symbolic link %x -Brisanje simboličnih povezav %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -Za sledeče imenike koš ni na voljo. Datoteke bodo namesto tega dokončno izbrisane: - -An exception occurred -Zgodila se je napaka - -A directory path is expected after %x. -Za %x se pričakuje pot do imenika. - -Syntax error -Sintaktična napaka - -Cannot open file %x. -Ne morem odpreti datoteke %x. - -File %x does not contain a valid configuration. -Datoteka %x ne vsebuje veljavnih nastavitev - -Unequal number of left and right directories specified. -Vnešeno je neenako število levih in desnih imenikov. - -The config file must not contain settings at directory pair level when directories are set via command line. -Konfiguracijska datoteka ne sme vsebovati nastavitev na ravni imeniških parov, če so imeniki nastavljeni prek ukazne vrstice. - -Directories cannot be set for more than one configuration file. -Imeniki ne morejo biti nastavljeni za eč kot eno konfiguracijsko datoteko. - -Command line -Ukazna vrstica - -Syntax: -Sintaksa: - -config files -konfiguracijske datoteke - -directory -imenik - -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Poljubno število FreeFileSync .ffs_gui in/ali .ffs_batch konfigracijskih datotek. - -Any number of alternative directories for at most one config file. -Poljubno število alternatvnih imenikov za največ eno konfiguracijsko datoteko. - -A folder input field is empty. -Vnosno polje za mapo je prazno. - -The corresponding folder will be considered as empty. -Ustrezajoča mapa bo smatrana kot prazna. - -Cannot find the following folders: -Ne morem najti naslednjih map: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -To napako, ki smatra vsako mapo kot prazno, lahko ignorirate. Mape bodo potem samodejno ustvarjene med sinhronizacijo. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -Naslednje mape imajo odvisne poti. Bodite previdni pri nastavitvi sinhronizacijskih pravil: - -File %x has an invalid date. -Datoteka %x ima neveljaven datum. - -Date: -Datum: - -Files %x have the same date but a different size. -Datoteki %x imata enak datum ampak različno velikost. - -Size: -Velikost: - -Items differ in attributes only -Elementi se razlikujejo samo v atributih - -Resolving symbolic link %x -Razrešujem simbolično povezavo %x - -Comparing content of files %x -Primerjam vsebino datotek %x - -Generating file list... -Ustvarjam seznam datotek... - -Starting comparison -Začenjam primerjavo - -Calculating sync directions... -Preračunavam sinhronizacijske smeri... - -Out of memory. -Zmanjkalo pomnilnika. - -Item exists on left side only -Element obstaja samo na levi strani - -Item exists on right side only -Element obstaja samo na desni strani - -Left side is newer -Leva stran je novejša - -Right side is newer -Desna stran je novejša - -Items have different content -Elementi imajo različno vsebino - -Both sides are equal -Obe strani sta enaki - -Conflict/item cannot be categorized -Spor/element ne more biti kategoriziran - -Copy new item to left -Kopiraj nov element na levo - -Copy new item to right -Kopiraj nov element na desno - -Delete left item -Izbriši levi element - -Delete right item -Izbriše desni element - -Move file on left -Premakni datoteko na levo - -Move file on right -Premakni datoteko na desno - -Overwrite left item -Prepiši levi element - -Overwrite right item -Prepiši desni element - -Do nothing -Ne naredi ničesar - -Update attributes on left -Posodobi atribute na levi - -Update attributes on right -Posodobi atribute na desni - -Database file %x is incompatible. -Datoteka podatkovne baze %x je nekompatibilna. - -Initial synchronization: -Začetna sinhronizacija: - -Database file %x does not yet exist. -Datoteka podatkovne baze %x še ne obstaja. - -Database file is corrupt: -Datoteka podatkovne baze je poškodovana: - -Cannot write file %x. -Ne morem zapisati datoteke %x. - -Cannot read file %x. -Ne morem prebrati datoteke %x. - -Database files do not share a common session. -Datoteke podatkovne baze si ne delijo skupne seje. - -Searching for folder %x... -Iskanje mape %x... - -Cannot read file attributes of %x. -Ne morem brati datotečnih atributov od %x. - -Cannot get process information. -Ne morem pridobiti informacij o procesu. - -Waiting while directory is locked (%x)... -Čakam, medtem ko se zaklepa imenik (%x)... - - -1 sec -%x sec - - -%x sek -%x sek -%x sek -%x sek - - -Creating file %x -Ustvarjam datoteko %x - -Items processed: -Obdelanih elementov: - -Items remaining: -Preostalih elementov: - -Total time: -Celoten čas: - - -1 byte -%x bytes - - -%x bajt -%x bajta -%x bajti -%x bajtov - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Napaka pri razčlenjevanju datoteke %x, vrstica %y, stolpec %z. - -Cannot set directory lock for %x. -Ne morem nastaviti zaklepanja imenikov za %x. - -Scanning: -Pregledujem: - - -1 thread -%x threads - - -%x nit -%x niti -%x niti -%x niti - - -Encoding extended time information: %x -Podrobne informacije o času enkodiranja: %x - -/sec -/sek - -%x items/sec -%x elementov/s - -Configuration file %x loaded partially only. -Nastavitvena datoteka %x naložena samo delno. - -Show in Explorer -Prikaži v Raziskovalcu - -Open with default application -Odpri s privzeto aplikacijo - -Browse directory -Brskaj po imeniku - -Cannot access the Volume Shadow Copy Service. -Ne morem dostopati do Volume Shadov Copy servisa. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Prosimo uporabite 64-bitno različico FreeFileSync za ustvarjanje senčnih kopij na tem sistemu. - -Cannot load file %x. -Ne morem naložiti datoteko %x. - -Cannot determine volume name for %x. -Ne morem določiti ime nosilca za %x. - -Volume name %x is not part of file path %y. -Ime nosilca %x ni del poti datoteke %y. - -Stop requested: Waiting for current operation to finish... -Zahteva za ustavitev: Čakam da se trenutni proces zaključi... - -Unable to create timestamp for versioning: -Ne morem ustvariti časovnega žiga za verzioniranje: - -Cannot read the following XML elements: -Ne morem brati naslednje XML elemente: - -&Open... -&Odpri... - -Save &as... -Shr&ani kot... - -&Quit -&Zapri - -&Program -&Program - -&View help -&Prikaži pomoč - -&About -&O programu - -&Help -&Pomoč - -Usage: -Uporaba: - -1. Select folders to watch. -1. Izberite imenike za opazovanje - -2. Enter a command line. -2. Vnesite ukazno-vrstico. - -3. Press 'Start'. -3. Pritisnite 'Začni'. - -To get started just import a .ffs_batch file. -Da začnete uvozite datoteko .ffs_batch - -Folders to watch: -Imeniki za pregled: - -Add folder -Dodaj imenik - -Remove folder -Odstrani v imenik - -Browse -Brskaj - -Select a folder -Izberite imenik - -Idle time (in seconds): -Nedejavni čas (v sekundah): - -Idle time between last detected change and execution of command -Čas nedejavnosti med zadnjo zaznano spremembo in izvršitvijo ukaza - -Command line: -Ukazna vrstica: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -Ukaz se sproži če: -- se spremenijo datoteke ali podmape -- pridejo nove mape (npr. ob vstavitvi USB ključka) - - -&Start -&Zaženi - -About -O programu(1) - -Build: %x -Izgradnja: %x - -All files -Vse datoteke - -Automated Synchronization -Avtomatska sinhnorizacija - -Directory monitoring active -Nadzor imenikov je aktven - -Waiting until all directories are available... -Čakam da so vsi imeniki dostopni... - -Error -Napaka - -&Restore -&Obnovi - -&Show error -&Pokaži napako - -&Exit -&Izhod - -Incorrect command line: -Napačna ukazna vrstica: - -&Retry -&Ponovi - -File content -Vsebina datoteke - -File time and size -Čas in velikost datoteke - -Two way -Obojesmerno - -Mirror -Zrcalno - -Update -Posodobi - -Custom -Po meri - -Multiple... -Večkratno... - -Moving file %x to %y -Premikam datoteko %x v %y - -Moving folder %x to %y -Premikam mapo %x v %y - -Moving symbolic link %x to %y -Premikam simbolično povezavo %x v %y - -Removing old versions... -Odstranjujem stare različice... - -Creating symbolic link %x -Ustvarjam simbolično povezavo %x - -Creating folder %x -Ustvarjam mapo %x - -Overwriting file %x -Prepisujem datoteko %x - -Overwriting symbolic link %x -Prepisujem simbolično povezavo %x - -Verifying file %x -Preverjam datoteko %x - -Updating attributes of %x -Posodabljam atribute od %x - -Cannot find %x. -Ne morem najti %x. - -Target folder %x already existing. -Ciljna mapa %x že obstaja. - -Target folder input field must not be empty. -Vnosno polje za ciljno mapo ne sme biti prazno. - -Please enter a target folder for versioning. -Prosimo vnesite ciljno mapo za verzioniranje. - -Source folder %x not found. -Izvorna mapa %x se ne najde. - -The following items have unresolved conflicts and will not be synchronized: -Naslednji elementi imajo nerešene konflikte in ne bodo sinhronizirani: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -Naslednji imeniki so občutno različni. Preverite, če primerjate pravilne imenike za sinhnorizacijo. - -Not enough free disk space available in: -Na voljo ni dovolj prostega prostora na disku v: - -Required: -Zahtevano: - -Available: -Na voljo: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Spremenjena bo mapa, ki je del večkratnih parov map. Prosim preglejte nastavitve sinhronizacije. - -Synchronizing folder pair: -Sinhroniziram par map: - -Generating database... -Ustvarjam podatkovno bazo... - -Creating a Volume Shadow Copy for %x... -Ustvarjam Volume Shadow Copy za %x... - -Data verification error: %x and %y have different content. -Napaka pri preverjanju podatkov: %x in %y imata drugačno vsebino. - -job name -naziv opravila - -Synchronization stopped -Sinhnorizacija ustavljena - -Synchronization completed with errors -Sinhronizacija se je končala z napakami - -Synchronization completed with warnings -Sinhronizacija se je končala z opozorili - -Nothing to synchronize -Nič za sinhronizirati - -Synchronization completed successfully -Sinhronizacija se je uspešno končala - -Saving log file %x... -Shranjujem datoteko beleženja %x... - -You can switch to FreeFileSync's main window to resolve this issue. -Preklopite na FreeFileSync glavno okno za odpravo težave. - -&Don't show this warning again -&Ne pokaži več tega opozorila - -&Ignore -&Ignoriraj - -&Switch -&Preklopi - -Switching to FreeFileSync's main window -Preklopi na FreeFileSync glavno okno - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - -Ponovni poskus čez %x sekundo... -Ponovni poskus čez %x sekundi... -Ponovni poskus čez %x sekunde... -Ponovni poskus čez %x sekund... - - -&Ignore subsequent errors -&Ignoriraj nadaljnje napake - -Serious Error -Resna napaka - -Check for Program Updates -Prevri obstoj nadgradnje programa - -A new version of FreeFileSync is available: -Nova različica FreeFileSync je na voljo: - -Download now? -Prenesem sedaj? - -&Download -&Prenesi - -FreeFileSync is up to date. -FreeFileSync je posodobljen. - -Unable to connect to sourceforge.net. -Ne morem se povezati na sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Na omrežju ne najdem obstoječe verzije FreeFileSync-a. Ali želite preveriti lastnoročno? - -Symlink -Simbolična povezava - -Folder -Mapa - -Full path -Polna pot - -Name -Ime - -Relative path -Relativna pot - -Base folder -Osnovna mapa - -Size -Velikost - -Date -Datum - -Extension -Razširitev - -Category -Kategorija - -Action -Ukrep - -Drag && drop -Povleci && spusti - -Close progress dialog -Zapri pogovorno okno z napredkom - -Standby -V pripravljenost - -Log off -Odjavi - -Shut down -Ugasni - -Hibernate -Hibernacija - -Alternate comparison settings -Alternativne nastavitve primerjave - -Alternate synchronization settings -Alternativne nastavitve sinhnorizacije - -Local filter -Lokalni filter - -Active -Aktivno - -None -Nič - -Remove alternate settings -Odstrani nadomestne nastavitve - -Clear filter settings -Počisti nastavitve filtra - -Copy -Kopiraj - -Paste -Prilepi - -Alternate Comparison Settings -Alternativne nastavitve primerjave - -Alternate Synchronization Settings -Alternativne nastavitve sinhnorizacije - -Local Filter -Lokalni filter - -&New -&Novo - -&Save -&Shrani - -Save as &batch job... -Shrani kot serijsko op&ravilo... - -1. &Compare -1. &Primerjaj - -2. &Synchronize -2. &Sinhroniziraj - -&Global settings -&Skupne nastavitve - -&Language -&Jezik - -&Find... -&Išči... - -&Export file list... -&Izvozi seznam datotek... - -&Tools -&Orodja - -&Check now -P&reveri zdaj - -Check &automatically once a week -S&amodejno preveri enkrat tedensko - -&Check for new version -&Preveri, če obstaja nova verzija - -Compare -Primerjaj - -Cancel -Prekliči - -Synchronize -Sinhroniziraj - -Add folder pair -Dodaj par imenikov - -Remove folder pair -Odstrani par imenikov - -Swap sides -Zamenjaj strani - -Close search bar -Zapri iskalno vrstico - -Find: -Išči: - -Match case -Ujemaj se s primerom - -Save as batch job -Shrani kot serijsko opravilo - -Hide excluded items -Skrij izključene elemente - -Show filtered or temporarily excluded files -Pokaži filtrirane ali začasno izključene datoteke - -Number of files and folders that will be created -Število datotek in map, ki bodo ustvarjene - -Number of files that will be overwritten -Število datotek, ki bodo prepisane - -Number of files and folders that will be deleted -Število datotek in map, ki bodo izbrisane - -Total bytes to copy -Skupno bajtov za kopiranje - -Select a variant: -Izberi možnost: - -Identify equal files by comparing modification time and size. -Določi enake datoteke s primerjavo datuma spremembe in velikosti. - -Identify equal files by comparing the file content. -Določi enake datoteke s primerjavo vsebine. - -Symbolic links: -Simbolične povezave: - -More information -Več informacij - -OK -V redu - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Identificiraj in razširjaj spremembe na obeh straneh. Izbrisi, premiki in spori so samodejno zaznani z uporabo podatkovne baze. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Ustvari zrcalno kopijo levega imenika, tako da se bo desni imenik po sinhnorizaciji popolnoma ujemal. - -Copy new and updated files to the right folder. -Kopiraj nove in posodobljene datoteke v desni imenik. - -Configure your own synchronization rules. -Konfigurirajte vaša lastna sinhronizacijska pravila. - -Detect moved files -Zaznaj premaknjene datoteke - -Requires database files. Not supported by all file systems. -Zahteva podatkovno bazo. Ni podprt s strani vseh datotečnih sistemov. - -Delete files: -Izbriši datoteke: - -Permanent -Trajno - -Delete or overwrite files permanently -Trajno izbriši ali prepiši datoteke - -Recycle bin -Koš - -Back up deleted and overwritten files in the recycle bin -Naredi varnostno kopijo izbrisanih in prepisanih datotek v koš - -Versioning -Ustvarjanje različič - -Move files to a user-defined folder -Premakni datoteke v izbran imenik - -Naming convention: -Konvencija poimenovanja: - -Show examples -Pokaži primere - -Handle errors: -Upravljanje napak: - -Ignore -Ignoriraj - -Hide all error and warning messages -Skrij vsa obvestila o napakah in opozorilih - -Pop-up -Pogovorno okno - -Show pop-up on errors or warnings -Prikaži pojavne napaka ali opozorila - -On completion: -Ob zaključku: - -Start synchronization now? -Zaženem sinhnorizacijo takoj? - -Variant: -Možnost: - -Statistics -Statistika - -&Don't show this dialog again -&Ne pokaži več tega sporočila - -Items found: -Najdenih elementov: - -Speed: -Hitrost: - -Time remaining: -Preostali čas: - -Time elapsed: -Pretečeni čas: - -Synchronizing... -Sinhroniziram... - -Minimize to notification area -Pomanjšaj v območje obvestil - -Close -Zapri - -&Pause -&Premor - -Stop -Ustavi - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Ustvari skriptno datoteko za samodejno sinhnorizacijo. Za zagon dvojno kliknite to datoteko ali pa jo umestite v razporejevalnik opravil: %x - -Stop synchronization at first error -Ustavi sinhnorizacojo ob prvi napaki - -Show progress dialog -Prikazuj pogovorno okno z napredkom - -Save log: -Shrani dnevnik: - -Limit: -Omejitev: - -Limit maximum number of log files -Omeji maksimalno število datotek beleženja - -How can I schedule a batch job? -Kako nastavim urnik za serijsko opravilo? - -&Recycle bin -&Koš - -Delete on both sides -Izbriši na obeh straneh - -Delete on both sides even if the file is selected on one side only -Izbriši na obeh straneh, četudi je datoteka izbrana na samo eni strani - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Izberi filtrirna pravila za izključitev določenih datotek iz sinhnorizacije. Vpiši pot datotek relativno na imenik v obdelavi - -Include: -Vključi: - -Exclude: -Izključi: - -Time span: -Časovno obdobje - -File size: -Velikost datoteke: - -Minimum: -Minimum: - -Maximum: -Maksimum: - -&Clear -P&očisti - -The following settings are used for all synchronization jobs. -Naslednje nastavitve se uporabljajo pri vseh sinhronizacijskih opravilih. - -Fail-safe file copy -Kopiranje datotek varno pred odpovedjo - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -Kopiraj v začasno datoteko (*.ffs_tmp) preden prepišeš cilj. -To zagotavlja konsistenco podatkov v primeru napake. - - -(recommended) -(priporočeno) - -Copy locked files -Kopiraj zaklenjene datoteke - -Copy shared or locked files using the Volume Shadow Copy Service. -Kopiraj zaklenjene in datoteke v skupni rabi s pomočjo Shadow Copy Service. - -(requires administrator rights) -(zahteva pravice administratorja) - -Copy file access permissions -Kopiraj dovoljenja dostopov datoteke - -Transfer file and folder permissions. -Prenesi pravice datotek in map. - -Automatic retry on error: -Ob napaki avtomatsko poskusi znova: - -Retry count: -Število poiskusov: - -Delay (in seconds): -Zakasnitev (v sekundah): - -Customize context menu: -Prilagodi vsebinski meni: - -Description -Opis - -Restore hidden windows -Obnovi skrita okna - -&Default -&Privzeto - -Source code written in C++ using: -Izvorna koda napisana v C++ z uporabo: - -If you like FreeFileSync -Če vam je FreeFileSync všeč - -Donate with PayPal -Doniraj s PayPal - -Feedback and suggestions are welcome -Povratne informacije in predlogi so dobrodošli - -Homepage -Domača stran - -Email -Email - -Published under the GNU General Public License -Objavljeno pod licenco GNU General Public - -Many thanks for localization: -Zahvale za lokalizacijo: - -Save as Batch Job -Shrani kot serijsko opravilo - -Delete Items -Izbriši elemente - -Global Settings -Skupne nastavitve - -Select Time Span -Izberi časovno obdobje - -Folder Pairs -Pari imenikov - -Find -Najdi - -Overview -Pregled - -Configuration -Konfiguracija - -Main Bar -Glavna vrstica - -Filter Files -Filtriraj datoteke - -Select View -Izberi pogled - -Open... -Odpri... - -Save -Shrani - -Compare both sides -Primerjaj obe strani - -Comparison settings -Nastavitve primerjanja - -Synchronization settings -Nastavitve sinhronizacije - -Start synchronization -Začni sinhronizacijo - -Confirm -Potrdi - -&Execute -&Izvedi - - -1 directory -%x directories - - -%x imenik -%x imenika -%x imeniki -%x imenikov - - - -1 file -%x files - - -%x datoteka -%x datoteki -%x datoteke -%x datotek - - - -%y of 1 row in view -%y of %x rows in view - - -%y od %x vrstice prikazana -%y od %x vrstic prikazanih -%y od %x vrstic prikazanih -%y od %x vrstic prikazanih - - -Set direction: -Nastavi smer: - -multiple selection -mnogokratna izbira - -Include via filter: -Vključi preko filtra: - -Exclude via filter: -Izključi preko filtra: - -Exclude temporarily -Začasno izključi - -Include temporarily -Trenutno vključi - -Delete -Izbriši - -Include all -Vključi vse - -Exclude all -Izključi vse - -Show icons: -Prikaži ikone: - -Small -Majhna - -Medium -Srednja - -Large -Velika - -Select time span... -Izberite časovni okvir... - -Default view -Privzeti pogled - -Show "%x" -Prikaži "%x" - -Last session -Zadnja seja - -Folder Comparison and Synchronization -Primerjava in sinhronizacija imenika - -Configuration saved -Konfiguracija shranjena - -FreeFileSync batch -FreeFileSync paket - -Do you want to save changes to %x? -Ali želite shraniti spremembe v %x? - -Never save &changes -Nikoli ne shrani &sprememb - -Do&n't save -Ne shra&ni - -Filter -Filter - -Show files that exist on left side only -Prikaži datoteke, ki obstajajo samo na levi - -Show files that exist on right side only -Prikaži datoteke, ki obstajajo samo na desni - -Show files that are newer on left -Prikaži datoteke, ki so novejše na levi - -Show files that are newer on right -Prikaži datoteke, ki so novejše na desni - -Show files that are equal -Prikaži datoteke, ki so enake - -Show files that are different -Prikaži datoteke, ki so različne - -Show conflicts -Prikaži spore - -Show files that will be created on the left side -Prikaži datoteke, ki bodo ustvarjene na levi strani - -Show files that will be created on the right side -Prikaži datoteke, ki bodo ustvarjene na desni strani - -Show files that will be deleted on the left side -Prikaži datoteke, ki bodo izbrisane na levi strani - -Show files that will be deleted on the right side -Prikaži datoteke, ki bodo izbrisane na desni strani - -Show files that will be overwritten on left side -Prikaži datoteke, ki bodo prepisane na levi strani - -Show files that will be overwritten on right side -Prikaži datoteke, ki bodo prepisane na desni strani - -Show files that won't be copied -Prikaži datoteke, ki ne bodo kopirane - -Set as default -Nastavi kot privzeto - -All folders are in sync -Vse mape so sinhronizirane - -Synchronization Settings -Nastavitve sinhnorizacije - -Comparison Settings -Nastavitve primerjave - -Cannot find %x -Ne najdem %x - -Comma-separated values -Vrednosti ločene z vejico - -File list exported -Seznam datotek je bil izvožen - -Searching for program updates... -Iščem posodobitve programa... - -Scanning... -Pregledujem... - -Comparing content... -Primerjam vsebino... - -Info -Info - -Warning -Pozor - -Paused -Na premoru - -Initializing... -Inicializiram... - -Stopped -Ustavljen - -Completed -Zaključeno - -&Continue -&Nadaljuj - -Log -Dnevnik - -Today -Danes - -This week -Ta teden - -This month -Ta mesec - -This year -To leto - -Last x days -Zadnjih x dni - -Byte -Bajt - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Ali res želite premakniti sledeč %x element v koš? -Ali res želite premakniti sledeča %x elementa v koš? -Ali res želite premakniti sledeče %x elemente v koš? -Ali res želite premakniti sledečih %x elementov v koš? - - -Move -Premakni - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Ali resnično želite izbrisati sledeči %x element? -Ali resnično želite izbrisati naslednja %x elementa? -Ali resnično želite izbrisati naslednje %x elemente? -Ali resnično želite izbrisati naslednjih %x elementov? - - -Exclude -Izključi - -Direct -Neposredno - -Follow -Sledi - -Copy NTFS permissions -Kopiraj NTFS dovoljenja - -Integrate external applications into context menu. The following macros are available: -Integriraj zunanje aplikacije v kontekstni menu. Na voljo so naslednji makri: - -- full file or folder name -- polno ime datoteke ali mape - -- folder part only -- samo del glede mape - -- Other side's counterpart to %item_path% -- Na drugi strani nasprotno v %item_path% - -- Other side's counterpart to %item_folder% -- Na drugi strani nasprotno v %item_folder% - -Restore all hidden windows and warnings? -Obnovim vsa skrita okna in opozorila? - -Leave as unresolved conflict -Pusti kot nerešeni spor - -Replace -Zamenjaj - -Move files and replace if existing -Premakne datoteke in jih zamenja, če obstajajo - -Time stamp -Časovna oznaka - -Append a timestamp to each file name -Dodaj časovno oznako k vsakemu imenu datoteke - -File -Datoteka - -YYYY-MM-DD hhmmss -LLLL-MM-DD hhmmss - -Files -Datoteke - -Items -Elementi - -Percentage -Odstotek - -Cannot monitor directory %x. -Ne morem nadzorovati imenika %x. - -Conversion error: -Napaka pri pretvorbi: - -Cannot delete file %x. -Ne morem izbrisati datoteke %x. - -The file is locked by another process: -Datoteka je zaklenjena s strani drugega procesa: - -Cannot move file %x to %y. -Ne morem premakniti datoteko %x v %y. - -Cannot delete directory %x. -Ne morem izbrisati imenika %x. - -Cannot write file attributes of %x. -Ne morem zapisati datotečnih atributov od %x. - -Cannot write modification time of %x. -Ne morem zapisati časa spremembe od %x. - -Cannot read security context of %x. -Ne morem prebrati varnostnega konteksta od %x. - -Cannot write security context of %x. -Ne morem zapisati varnostni kontekst od %x - -Cannot read permissions of %x. -Ne morem prebrati dovoljenja od %x. - -Cannot write permissions of %x. -Ne morem zapisati dovoljenj od %x. - -Cannot create directory %x. -Ne morem ustvariti imenika %x. - -Cannot create symbolic link %x. -Ne morem ustvariti simbolične povezave %x. - -Cannot find system function %x. -Ne morem najti sistemske funkcije %x. - -Cannot copy file %x to %y. -Ne morem kopirati datoteke %x v %y. - -Type of item %x is not supported: -Element tipa %x ni podprt: - -Cannot resolve symbolic link %x. -Ne morem razrešiti simbolične povezave %x. - -Cannot open directory %x. -Ne morem odpreti imenika %x. - -Cannot enumerate directory %x. -Ne morem oštevilčiti imenika %x. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -%x min -%x min -%x min -%x min - - - -1 hour -%x hours - - -%x ura -%x uri -%x ure -%x ur - - - -1 day -%x days - - -%x dan -%x dni -%x dni -%x dni - - -Unable to register to receive system messages. -Ne morem se registriratiza prejem sistemskih sporočil. - -Cannot set privilege %x. -Ne morem nastaviti privilegija %x. - -Unable to suspend system sleep mode. -Ne morem preprečiti mirovanja sistema. - -Cannot change process I/O priorities. -Ne morem spremeniti V/I prioritet procesa. - -Unable to move %x to the recycle bin. -Ne morem premakniti %x v koš- - -Cannot determine final path for %x. -Ne morem določiti končne poti za %x. - -Error Code %x: -Koda napake %x: - diff --git a/BUILD/Languages/spanish.lng b/BUILD/Languages/spanish.lng deleted file mode 100644 index e5ae052c..00000000 --- a/BUILD/Languages/spanish.lng +++ /dev/null @@ -1,1517 +0,0 @@ -
    - Español - I.R.Maturana (irmlab.com) - es_ES - flag_spain.png - 2 - n == 1 ? 0 : 1 -
    - -&Check - - -Retrying operation... - - -Both sides have changed since last synchronization. -Ambos lados han cambiado desde la última sincronización. - -Cannot determine sync-direction: -No se puede determinar la dirección de sincronización: - -No change since last synchronization. -Ningún cambio desde la última sincronización. - -The database entry is not in sync considering current settings. -La entrada de la base de datos no está sincronizada, de acuerdo con la configuración actual. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Fijando direcciones de sincronización predeterminadas: los archivos nuevos sobrescribirán los archivos antiguos. - -Checking recycle bin availability for folder %x... -Comprobando disponibilidad de la papelera de reciclaje para la carpeta %x… - -Moving file %x to the recycle bin -Mover archivo %x a la papelera de reciclaje - -Moving folder %x to the recycle bin -Mover carpeta %x a la papelera de reciclaje - -Moving symbolic link %x to the recycle bin -Mover vínculo simbólico %x a la papelera de reciclaje - -Deleting file %x -Borrar archivo %x - -Deleting folder %x -Borrar carpeta %x - -Deleting symbolic link %x -Borrar vínculo simbólico %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -La papelera de reciclaje no está disponible para las carpetas siguientes. Los archivos se borrarán de forma permanente: - -An exception occurred -Ha ocurrido una excepción - -A directory path is expected after %x. -Se esperaba una ruta de directorio después de %x. - -Syntax error -Error de sintaxis - -Cannot open file %x. -No se puede abrir el archivo %x. - -File %x does not contain a valid configuration. -El archivo %x no contiene una configuración válida. - -Unequal number of left and right directories specified. -Desigualdad en el número de directorios especificados a izquierda y derecha. - -The config file must not contain settings at directory pair level when directories are set via command line. -El archivo de configuración no debe incluir parámetros al nivel de un par de directorios cuando éstos se especifican desde la línea de comandos. - -Directories cannot be set for more than one configuration file. -No se pueden definir directorios para más de un archivo de configuración. - -Command line -Línea de comandos - -Syntax: -Sintaxis: - -config files -archivos de configuración - -directory -directorio - -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 directories for at most one config file. -Cualquier número de directorios alternativos para un archivo de configuración como máximo. - -A folder input field is empty. -Un campo de entrada de la carpeta está vacío. - -The corresponding folder will be considered as empty. -La siguiente carpeta será considerada como vacía. - -Cannot find the following folders: -No se pudieron encontrar las siguiente carpetas: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -Ignore este error si desea tratar cada carpeta como vacía. En tal caso, se crearán estas carpetas automáticamente durante la sincronización. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -Las carpetas siguientes usan rutas dependientes. Tenga cuidado al definir reglas de sincronización: - -File %x has an invalid date. -El archivo %x tiene una fecha inválida. - -Date: -Fecha: - -Files %x have the same date but a different size. -Los archivos %x tienen la misma fecha pero un tamaño diferente. - -Size: -Tamaño: - -Items differ in attributes only -Los elementos sólo se diferencian en los atributos - -Resolving symbolic link %x -Resolviendo el vínculo simbólico %x - -Comparing content of files %x -Comparación del contenido de los archivos %x - -Generating file list... -Generando lista de archivos… - -Starting comparison -Iniciando la comparación - -Calculating sync directions... -Calculando direcciones de sincronización… - -Out of memory. -Sin memoria. - -Item exists on left side only -El elemento existe sólo en el lado izquierdo - -Item exists on right side only -El elemento existe sólo en el lado derecho - -Left side is newer -El lado izquierdo es más reciente - -Right side is newer -El lado derecho es más reciente - -Items have different content -Los elementos tienen contenido distinto - -Both sides are equal -Ambos lados son iguales - -Conflict/item cannot be categorized -No se puede categorizar el conflicto o elemento - -Copy new item to left -Copiar nuevo elemento a la izquierda - -Copy new item to right -Copiar nuevo elemento a la derecha - -Delete left item -Eliminar elemento izquierdo - -Delete right item -Eliminar elemento derecho - -Move file on left -Mover archivo a la izquierda - -Move file on right -Mover archivo a la derecha - -Overwrite left item -Sobrescribir elemento izquierdo - -Overwrite right item -Sobrescribir elemento derecho - -Do nothing -No hacer nada - -Update attributes on left -Actualizar atributos en la izquierda - -Update attributes on right -Actualizar atributos en la derecha - -Database file %x is incompatible. -El archivo de base de datos %x es incompatible. - -Initial synchronization: -Sincronización inicial: - -Database file %x does not yet exist. -El archivo de base de datos %x aún no existe. - -Database file is corrupt: -El archivo de base de datos está dañado: - -Cannot write file %x. -No se puede escribir el archivo %x. - -Cannot read file %x. -No se puede leer el archivo %x. - -Database files do not share a common session. -Los archivos de base de datos no comparten una sesión común. - -Searching for folder %x... -Buscando carpeta %x… - -Cannot read file attributes of %x. -No se puede leer archivo de atributos de %x. - -Cannot get process information. -No se puede obtener información del proceso. - -Waiting while directory is locked (%x)... -Esperando mientras el directorio se encuentre bloqueado (%x)… - - -1 sec -%x sec - - -1 seg. -%x seg. - - -Creating file %x -Creando archivo %x - -Items processed: -Elementos procesados: - -Items remaining: -Elementos restantes: - -Total time: -Tiempo total: - - -1 byte -%x bytes - - -1 byte -%x bytes - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Error analizando archivo %x, fila %y, columna %z. - -Cannot set directory lock for %x. -No se pudo bloquear el directorio %x. - -Scanning: -Escanear: - - -1 thread -%x threads - - -1 hilo -%x hilos - - -Encoding extended time information: %x -Codificando información de hora extendida: %x - -/sec -/seg - -%x items/sec -%x elementos/seg - -Configuration file %x loaded partially only. -Archivo de configuración %x cargado sólo parcialmente. - -Show in Explorer -Mostrar en el Explorador - -Open with default application -Abrir con la aplicación predeterminada - -Browse directory -Examinar directorio - -Cannot access the Volume Shadow Copy Service. -No se puede acceder al servicio de Instantánea de volumen. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Por favor, use la versión de 64 bits de FreeFileSync para crear copias de Shadow en el sistema. - -Cannot load file %x. -No se puede cargar el archivo %x. - -Cannot determine volume name for %x. -No se puede determinar nombre del volumen de %x. - -Volume name %x is not part of file path %y. -El nombre de volumen %x no es parte de la ruta de archivo %y. - -Stop requested: Waiting for current operation to finish... -Detención solicitada: esperando a que la operación actual finalice… - -Unable to create timestamp for versioning: -No es posible crear fecha y hora para la versión: - -Cannot read the following XML elements: -No se pueden leer los siguientes elementos XML: - -&Open... -&Abrir… - -Save &as... -Guardar &como… - -&Quit -&Salir - -&Program -&Programa - -&View help -&Ver ayuda - -&About -&Acerca de - -&Help -&Ayuda - -Usage: -Uso: - -1. Select folders to watch. -1. Seleccionar carpetas para mostrar. - -2. Enter a command line. -2. Introduzca una línea de comandos. - -3. Press 'Start'. -3. Presione 'Inicio'. - -To get started just import a .ffs_batch file. -Para comenzar, importe un archivo .ffs_batch. - -Folders to watch: -Carpetas para examinar: - -Add folder -Añadir carpeta - -Remove folder -Eliminar carpeta - -Browse -Examinar - -Select a folder -Seleccione una carpeta - -Idle time (in seconds): -Tiempo de inactividad (en segundos): - -Idle time between last detected change and execution of command -Tiempo ocioso entre el último cambio detectado y la ejecución del comando - -Command line: -Línea de comandos: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -El comando es disparado si: -- hay cambios en los archivos o subcarpetas -- aparecen nuevas carpetas (en una memoria USB, por ejemplo) - - -&Start -&Iniciar - -About -Acerca de - -Build: %x -Completado: %x - -All files -Todos los archivos - -Automated Synchronization -Sincronización Automática - -Directory monitoring active -Supervisión de directorios activada - -Waiting until all directories are available... -Esperando que todos los directorios estén disponibles… - -Error -Error - -&Restore -&Restaurar - -&Show error -Mo&strar error - -&Exit -&Salir - -Incorrect command line: -Línea de comandos incorrecta: - -&Retry -&Reintentar - -File content -Contenido del archivo - -File time and size -Fecha y tamaño del archivo - -Two way -Bidireccional - -Mirror -Espejo - -Update -Actualizar - -Custom -Personalizado - -Multiple... -Múltiple… - -Moving file %x to %y -Mover archivo de %x a %y - -Moving folder %x to %y -Mover carpeta de %x a %y - -Moving symbolic link %x to %y -Mover vínculo simbólico de %x a %y - -Removing old versions... -Eliminando versiones antiguas… - -Creating symbolic link %x -Creando vínculo simbólico %x - -Creating folder %x -Creando carpeta %x - -Overwriting file %x -Sobrescribir archivo %x - -Overwriting symbolic link %x -Sobrescribir vínculo simbólico %x - -Verifying file %x -Verificación del archivo %x - -Updating attributes of %x -Actualizar atributos de %x - -Cannot find %x. -No se pudo encontrar %x. - -Target folder %x already existing. -La carpeta de destino %x ya existe. - -Target folder input field must not be empty. -El campo de entrada de la carpeta de destino no debe estar vacío. - -Please enter a target folder for versioning. -Indique una carpeta de destino para la versión. - -Source folder %x not found. -El archivo de origen %x no ha sido encontrado. - -The following items have unresolved conflicts and will not be synchronized: -Los siguientes elementos tienen conflictos sin resolver y no serán sincronizados: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -Las carpetas siguientes muestran diferencias significativas. Compruebe que hace coincidir las carpetas correctas para su sincronización. - -Not enough free disk space available in: -Espacio en disco insuficiente en: - -Required: -Requerido: - -Available: -Disponible: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Una carpeta, que es parte de múltiple pares de carpetas, será modificada. Por favor, revise las opciones de sincronización. - -Synchronizing folder pair: -Sincronizando par de carpetas: - -Generating database... -Generando base de datos… - -Creating a Volume Shadow Copy for %x... -Creando una Instantánea de volumen para %x… - -Data verification error: %x and %y have different content. -Error al comprobar los datos: %x y %y tienen contenidos diferentes. - -job name -nombre de tarea - -Synchronization stopped -Sincronización detenida - -Synchronization completed with errors -Sincronización completada con errores - -Synchronization completed with warnings -Sincronización completada con avisos - -Nothing to synchronize -Nada que sincronizar - -Synchronization completed successfully -Sincronización completada satisfactoriamente - -Saving log file %x... -Guardando registro %x… - -You can switch to FreeFileSync's main window to resolve this issue. -Puede cambiar a la ventana principal de FreeFileSync para resolver este problema. - -&Don't show this warning again -&No volver a mostrar este aviso - -&Ignore -&Ignorar - -&Switch -&Cambiar - -Switching to FreeFileSync's main window -Cambiar a la ventana principal de FreeFileSync - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - -Reintento automático en 1 segundo… -Reintento automático en %x segundos… - - -&Ignore subsequent errors -&Ignorar errores posteriores - -Serious Error -Error grave - -Check for Program Updates -Buscar actualizaciones del programa - -A new version of FreeFileSync is available: -Una nueva versión de FreeFileSync está disponible: - -Download now? -¿Descargar ahora? - -&Download -&Descargar - -FreeFileSync is up to date. -FreeFileSync está actualizado. - -Unable to connect to sourceforge.net. -No se puede conectar con sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -No se encuentra el número de versión actual de FreeFileSync en línea. ¿Desea comprobarla manualmente? - -Symlink -Enlace simbólico - -Folder -Carpeta - -Full path -Ruta completa - -Name -Nombre - -Relative path -Ruta relativa - -Base folder -Carpeta base - -Size -Tamaño - -Date -Fecha - -Extension -Extensión - -Category -Categoría - -Action -Acción - -Drag && drop -Arrastrar y soltar - -Close progress dialog -Cerrar diálogo de progreso - -Standby -Suspender - -Log off -Finalizar sesión - -Shut down -Apagar - -Hibernate -Hibernar - -Alternate comparison settings -Opciones avanzadas de comparación - -Alternate synchronization settings -Opciones avanzadas de sincronización - -Local filter -Filtro local - -Active -Activa - -None -Ninguna - -Remove alternate settings -Eliminar opciones avanzadas - -Clear filter settings -Limpiar opciones del filtrado - -Copy -Copiar - -Paste -Pegar - -Alternate Comparison Settings -Opciones avanzadas de comparación - -Alternate Synchronization Settings -Opciones avanzadas de sincronización - -Local Filter -Filtro local - -&New -&Nuevo - -&Save -&Guardar - -Save as &batch job... -Guardar como tarea por &lotes… - -1. &Compare -1. &Comparar - -2. &Synchronize -2. &Sincronizar - -&Global settings -&Opciones globales - -&Language -&Idioma - -&Find... -&Buscar… - -&Export file list... -&Exportar lista de archivos… - -&Tools -Herramien&tas - -&Check now -&Comprobar ahora - -Check &automatically once a week -Comprobar &automáticamente una vez por semana - -&Check for new version -&Comprobar si hay una nueva versión - -Compare -Comparar - -Cancel -Cancelar - -Synchronize -Sincronizar - -Add folder pair -Añadir un par de carpetas - -Remove folder pair -Eliminar un par de carpetas - -Swap sides -Intercambiar lados - -Close search bar -Cerrar la barra de búsqueda - -Find: -Find: - -Match case -Distinción entre mayúsculas y minúsculas - -Save as batch job -Salvar como tarea por lotes - -Hide excluded items -Ocultar elementos excluidos - -Show filtered or temporarily excluded files -Mostrar archivos excluidos temporalmente o filtrados - -Number of files and folders that will be created -Número de archivos y carpetas que serán creados - -Number of files that will be overwritten -Número de archivos que serán sobrescritos - -Number of files and folders that will be deleted -Número de archivos y carpetas que serán eliminados - -Total bytes to copy -Total de bytes a copiar - -Select a variant: -Seleccione una variante: - -Identify equal files by comparing modification time and size. -Comparar archivos iguales por la hora de modificación y el tamaño. - -Identify equal files by comparing the file content. -Comparar archivos iguales por el contenido. - -Symbolic links: -Vínculos simbólicos: - -More information -Más información - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Identificar y propagar cambios en ambos lados. Eliminaciones, movimientos y conflictos serán detectados automáticamente usando una base de datos. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Crear una copia de respaldo reflejo de la carpeta izquierda que corresponda exactamente a la carpeta derecha después de la sincronización. - -Copy new and updated files to the right folder. -Copiar archivos nuevos y actualizados a la carpeta de la derecha. - -Configure your own synchronization rules. -Configuración de sus propias reglas de sincronización. - -Detect moved files -Detectar archivos movidos - -Requires database files. Not supported by all file systems. -Archivos de base de datos requeridos. No son compatibles en todos los sistemas de archivos. - -Delete files: -Eliminar archivos: - -Permanent -Permanente - -Delete or overwrite files permanently -Borrar o Sobrescribir archivos permanentemente - -Recycle bin -Papelera de reciclaje - -Back up deleted and overwritten files in the recycle bin -Se eliminó la copia de respaldo y se reemplazaron archivos en la papelera de reciclaje - -Versioning -Control de versiones - -Move files to a user-defined folder -Mover archivos a una carpeta del usuario - -Naming convention: -Convención de nombrado: - -Show examples -Mostrar ejemplos - -Handle errors: -Tratar errores: - -Ignore -Ignorar - -Hide all error and warning messages -Ocultar todos los mensajes de error y aviso - -Pop-up -Automático - -Show pop-up on errors or warnings -Mostrar ventana emergente de errores o avisos - -On completion: -Al completar: - -Start synchronization now? -¿Iniciar la sincronización ahora? - -Variant: -Variante: - -Statistics -Estadísticas - -&Don't show this dialog again -&No volver a mostrar este diálogo - -Items found: -Elementos encontrados: - -Speed: -Velocidad: - -Time remaining: -Tiempo restante: - -Time elapsed: -Tiempo transcurrido: - -Synchronizing... -Sincronizando… - -Minimize to notification area -Minimizar en el área de notificación - -Close -Cerrar - -&Pause -&Pausa - -Stop -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 - -Stop synchronization at first error -Detener la sincronización con el primer error - -Show progress dialog -Mostrar diálogo de progreso - -Save log: -Guardar registro: - -Limit: -Limite: - -Limit maximum number of log files -Limitar el número máximo de archivos de registro - -How can I schedule a batch job? -Cómo puedo programar una tarea por lotes? - -&Recycle bin -Papelera de &reciclaje - -Delete on both sides -Borrar en ambos lados - -Delete on both sides even if the file is selected on one side only -Borrar en ambos lados incluso si el archivo está seleccionado en un solo lado - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Seleccione reglas para excluir archivos durante la sincronización. Indique rutas relativas a la carpeta emparejada correspondiente. - -Include: -Incluir: - -Exclude: -Excluir: - -Time span: -Espacio de tiempo: - -File size: -Tamaño de archivo: - -Minimum: -Mínimo: - -Maximum: -Máximo: - -&Clear -&Borrar - -The following settings are used for all synchronization jobs. -Las opciones siguientes se utilizan para todas las tareas de sincronización. - -Fail-safe file copy -Copia de archivo a prueba de fallos - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -Copiar a archivo temporal (*.ffs_tmp) antes de sobrescribir en destino. -Se garantiza un estado coherente incluso en caso de error grave. - - -(recommended) -(recomendado) - -Copy locked files -Copiar archivos bloqueados - -Copy shared or locked files using the Volume Shadow Copy Service. -Copiar archivos compartidos o bloqueados usando el servicio de Instantánea de volumen. - -(requires administrator rights) -(requiere derechos de administrador) - -Copy file access permissions -Copiar permisos de acceso al archivo - -Transfer file and folder permissions. -Transferir permisos de archivos y carpetas. - -Automatic retry on error: -Retardo automático en caso de error : - -Retry count: -Cuenta de reintentos: - -Delay (in seconds): -Retardo (en segundos): - -Customize context menu: -Personalizar menú contextual : - -Description -Descripción - -Restore hidden windows -Restaurar ventanas ocultas - -&Default -&Configuración predeterminada - -Source code written in C++ using: -Código fuente original en C++ con apoyo de: - -If you like FreeFileSync -¿Te gusta FreeFileSync? : - -Donate with PayPal -Haz una donación por PayPal - -Feedback and suggestions are welcome -Comentarios y sugerencias bienvenidos : - -Homepage -Página de inicio - -Email -Correo electrónico - -Published under the GNU General Public License -Publicado con derechos GNU General Public License : - -Many thanks for localization: -Agradecimientos por las traducciones a: - -Save as Batch Job -Guardar como tarea por lotes - -Delete Items -Eliminar elementos - -Global Settings -Opciones globales - -Select Time Span -Seleccionar duración - -Folder Pairs -Pares de carpetas - -Find -Buscar - -Overview -Visión global - -Configuration -Configuración - -Main Bar -Barra principal - -Filter Files -Filtrar archivos - -Select View -Seleccione vista - -Open... -Abrir… - -Save -Guardar - -Compare both sides -Comparar ambos lados - -Comparison settings -Opciones de comparación - -Synchronization settings -Opciones de sincronización - -Start synchronization -Iniciar sincronización - -Confirm -Confirmar - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - -¿Realmente desea ejecutar el comando %y para un elemento? -¿Realmente desea ejecutar el comando %y para %x elementos? - - -&Execute -&Ejecutar - - -1 directory -%x directories - - -1 directorio -%x directorios - - - -1 file -%x files - - -1 archivo -%x archivos - - - -%y of 1 row in view -%y of %x rows in view - - -%y de 1 fila en la vista -%y de %x filas en vista - - -Set direction: -Indicar dirección: - -multiple selection -selección múltiple - -Include via filter: -Incluir a través del filtro: - -Exclude via filter: -Excluir a través del filtro: - -Exclude temporarily -Excluir temporalmente - -Include temporarily -Incluir temporalmente - -Delete -Eliminar - -Include all -Incluir todo - -Exclude all -Excluir todo - -Show icons: -Mostrar iconos: - -Small -Pequeño - -Medium -Medio - -Large -Grande - -Select time span... -Seleccionar duración… - -Default view -Vista predeterminada - -Show "%x" -Mostrar "%x" - -Last session -Última sesión - -Folder Comparison and Synchronization -Comparación y sincronización de carpetas - -Configuration saved -Configuración guardada - -FreeFileSync batch -Tarea por lotes de FreeFileSync - -Do you want to save changes to %x? -¿Quiere guardar los cambios de %x? - -Never save &changes -Nunca guardar &cambios - -Do&n't save -&No guardar - -Filter -Filtro - -Show files that exist on left side only -Mostrar sólo archivos existentes en la izquierda - -Show files that exist on right side only -Mostrar sólo archivos existentes en la derecha - -Show files that are newer on left -Mostrar archivos más recientes a la izquierda - -Show files that are newer on right -Mostrar archivos más recientes a la derecha - -Show files that are equal -Mostrar archivos iguales - -Show files that are different -Mostrar archivos diferentes - -Show conflicts -Mostrar conflictos - -Show files that will be created on the left side -Mostrar archivos que serán creados en el lado izquierdo - -Show files that will be created on the right side -Mostrar archivos que serán creados en el lado derecho - -Show files that will be deleted on the left side -Mostrar archivos que serán eliminados en el lado izquierdo - -Show files that will be deleted on the right side -Mostrar archivos que serán eliminados en el lado derecho - -Show files that will be overwritten on left side -Mostrar archivos que serán sobrescritos en el lado izquierdo - -Show files that will be overwritten on right side -Mostrar archivos que serán sobrescritos en el lado derecho - -Show files that won't be copied -Mostrar archivos que no serán copiados - -Set as default -Predeterminado - -All folders are in sync -Todas las carpetas están sincronizadas - -Synchronization Settings -Opciones de sincronización - -Comparison Settings -Opciones de comparación - -Cannot find %x -No se puede encontrar %x - -Comma-separated values -Valores separados por comas - -File list exported -Lista de archivos exportada - -Searching for program updates... -Buscando actualizaciones del programa… - -Scanning... -Escaneando… - -Comparing content... -Comparando contenido… - -Info -Info - -Warning -Atención - -Paused -Pausado - -Initializing... -Inicializando… - -Stopped -Detenido - -Completed -Terminado - -&Continue -&Continuar - -Log -Registro - -Today -Hoy - -This week -Esta semana - -This month -Este mes - -This year -Este año - -Last x days -Últimos x días - -Byte -Byte - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -¿Realmente desea mover este elemento a la papelera de reciclaje? -¿Realmente desea mover estos %x elementos a la papelera de reciclaje? - - -Move -Mover - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -¿Realmente desea eliminar este elemento? -¿Realmente desea eliminar estos %x elementos? - - -Exclude -Excluir - -Direct -Enviar - -Follow -Seguir - -Copy NTFS permissions -Copiar permisos NTFS - -Integrate external applications into context menu. The following macros are available: -Integrar aplicaciones externas en el menú de contexto. Los siguientes macros están disponibles: - -- full file or folder name -- nombre completo de archivo o carpeta - -- folder part only -- sólo parte de la carpeta - -- Other side's counterpart to %item_path% -- El otro lado de %item_path% - -- Other side's counterpart to %item_folder% -- El otro lado de %item_folder% - -Restore all hidden windows and warnings? -Restaurar todas las ventanas y mensajes de advertencia ocultados ? - -Leave as unresolved conflict -Dejar como conflicto sin resolver - -Replace -Reemplazar - -Move files and replace if existing -Mover archivos y reemplazar si ya existen - -Time stamp -Intervalo de tiempo - -Append a timestamp to each file name -Incluir fecha y hora a cada nombre de archivo - -File -Archivo - -YYYY-MM-DD hhmmss -YYYY-MM-DD hhmmss - -Files -Archivos - -Items -Elementos - -Percentage -Porcentaje - -Cannot monitor directory %x. -No se puede monitorizar el directorio %x. - -Conversion error: -Error de conversión: - -Cannot delete file %x. -No se puede eliminar el archivo %x. - -The file is locked by another process: -El archivo está bloqueado por otro proceso: - -Cannot move file %x to %y. -No se puede mover el archivo %x a %y. - -Cannot delete directory %x. -No se puede eliminar el directorio %x. - -Cannot write file attributes of %x. -No se pueden escribir los atributos de archivo de %x. - -Cannot write modification time of %x. -No se puede escribir el tiempo de modificación de %x. - -Cannot read security context of %x. -No se puede leer el contexto de seguridad de %x. - -Cannot write security context of %x. -No se puede escribir el contexto de seguridad de %x. - -Cannot read permissions of %x. -No se pueden leer los permisos de %x. - -Cannot write permissions of %x. -No se pueden escribir los permisos de %x. - -Cannot create directory %x. -No se puede crear el directorio %x. - -Cannot create symbolic link %x. -No se puede crear el vínculo simbólico %x. - -Cannot find system function %x. -No se puede encontrar la función del sistema %x. - -Cannot copy file %x to %y. -No se puede copiar el archivo %x a %y. - -Type of item %x is not supported: -El tipo de objeto %x no esta soportado: - -Cannot resolve symbolic link %x. -No se puede resolver el vínculo simbólico %x. - -Cannot open directory %x. -No se puede abrir el directorio %x. - -Cannot enumerate directory %x. -No se puede enumerar el directorio %x. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -1 minuto -%x minutos - - - -1 hour -%x hours - - -1 hora -%x horas - - - -1 day -%x days - - -1 día -%x días - - -Unable to register to receive system messages. -No es posible registrar la recepción de mensajes sistema. - -Cannot set privilege %x. -No se puede asignar el privilegio %x. - -Unable to suspend system sleep mode. -No es posible suspender el sistema. - -Cannot change process I/O priorities. -No se pudieron cambiar las prioridades de E/S del proceso. - -Unable to move %x to the recycle bin. -Incapaz de mover %x a la papelera de reciclaje. - -Cannot determine final path for %x. -No se puede determinar la ruta final de %x. - -Error Code %x: -Error: %x: - diff --git a/BUILD/Languages/swedish.lng b/BUILD/Languages/swedish.lng deleted file mode 100644 index 38c590ff..00000000 --- a/BUILD/Languages/swedish.lng +++ /dev/null @@ -1,1519 +0,0 @@ -
    - Svenska - Åke Engelbrektson - sv_SE - flag_sweden.png - 2 - n == 1 ? 0 : 1 -
    - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -Retrying operation - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Both sides have changed since last synchronization. -Bägge sidor har ändrats sedan senaste synkroniseringen. - -Cannot determine sync-direction: -Kan inte bestämma synkroniseringsriktning: - -No change since last synchronization. -Inga ändringar sedan senaste synkronisering. - -The database entry is not in sync considering current settings. -Databasposten är inte synkroniserad, i förhållande till aktuella inställningar - -Setting default synchronization directions: Old files will be overwritten with newer files. -Standardsynkronisering: Gamla filer kommer att skrivas över av nyare versioner. - -Checking recycle bin availability for folder %x... -Kontrollerar papperskorgens tillgänglighet för %x... - -Moving file %x to the recycle bin -Flyttar %x till papperskorgen - -Moving folder %x to the recycle bin -Flyttar mappen %x till papperskorgen - -Moving symbolic link %x to the recycle bin -Flyttar den symboliska länken %x till papperskorgen - -Deleting file %x -Tar bort filen %x - -Deleting folder %x -Tar bort mappen %x - -Deleting symbolic link %x -Tar bort den symboliska länken %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -Papperskorgen är inte tillgänglig för följande mappar. Filerna kommer istället att tas bort permanent: - -An exception occurred -Ett undantag inträffade - -A directory path is expected after %x. -En sökväg förväntas efter %x. - -Syntax error -Syntaxfel - -Cannot open file %x. -Kan inte öppna %x - -Error -Fel - -File %x does not contain a valid configuration. -Filen %x innehåller ingen giltig konfiguration. - -Unequal number of left and right directories specified. -Ett ojämnt antal vänster- och högermappar har specificerats. - -The config file must not contain settings at directory pair level when directories are set via command line. -Konfigurationsfilen kan inte innehålla inställningar på katalogparnivå när mappar anges via kommandorad. - -Warning -Varning - -Directories cannot be set for more than one configuration file. -Mappar kan inte anges för mer än en konfigurationsfil - -Syntax: -Syntax: - -config files -konfigurationsfiler - -directory -mapp - -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 directories for at most one config file. -Valfritt antal alternativa mappar för max en konfigurationsfil. - -Command line -Kommandofält - -A folder input field is empty. -Ett inmatningsfält är tomt - -The corresponding folder will be considered as empty. -Motsvarande mapp kommer att betraktas som tom. - -Cannot find the following folders: -Kan inte hitta följande mappar: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -Du kan bortse från detta fel, och betrakta varje mapp som tom. Mapparna kommer då att skapas automatiskt, under synkroniseringen - -The following folders have dependent paths. Be careful when setting up synchronization rules: -Följande mappar har beroende sökvägar. Var försiktig vid konfigurering av synkroniseringsregler: - -File %x has an invalid date. -Filen %x har ett ogiltigt datum. - -Date: -Datum: - -Files %x have the same date but a different size. -Filerna %x har samma datum men olika storlek. - -Size: -Storlek: - -Items differ in attributes only -Endast attribut skiljer objekten åt - -Resolving symbolic link %x -Översätter den symboliska länken %x - -Comparing content of files %x -Jämför filinnehåll för %x - -Generating file list... -Skapar fillista... - -Starting comparison -Startar jämföelse - -Calculating sync directions... -Beräknar synkroniseringsmappar... - -Out of memory. -Minnesbrist. - -Item exists on left side only -Objektet finns bara på vänster sida - -Item exists on right side only -Objektet finns bara på höger sida - -Left side is newer -Vänster sida är nyare - -Right side is newer -Höger sida är nyare - -Items have different content -Objekten har olika innehåll - -Both sides are equal -Bägge sidor är lika - -Conflict/item cannot be categorized -Konflikten/Objektet kan inte kategoriseras - -Copy new item to left -Kopiera nytt objekt åt vänster - -Copy new item to right -Kopiera nytt objekt åt höger - -Delete left item -Ta bort vänster objekt - -Delete right item -Ta bort höger objekt - -Move file on left -Flytta fil på vänster sida - -Move file on right -Flytta fil på höger sida - -Overwrite left item -Skriv över vänster objekt - -Overwrite right item -Skriv över höger objekt - -Do nothing -Gör ingenting - -Update attributes on left -Uppdatera attribut på vänster sida - -Update attributes on right -Uppdatera attribut på höger sida - -Database file %x is incompatible. -Databasfilen %x är inkompatibel - -Initial synchronization: -Initial synkronisering: - -Database file %x does not yet exist. -Databasfilen %x finns ännu inte - -Database file is corrupt: -Databafilen är korrupt - -Cannot write file %x. -Filen %x kan inte skrivas - -Cannot read file %x. -Filen %x kan inte läsas in - -Database files do not share a common session. -Databasfilerna har ingen gemensam session - -Searching for folder %x... -Söker efter mappen %x... - -Cannot read file attributes of %x. -Kan inte läsa filattribut för %x - -Cannot get process information. -Processinformation kan inte inhämtas - -Waiting while directory is locked (%x)... -Väntar medan mappen låses (%x)... - - -1 sec -%x sec - - -1 sek -%x sek - - -Creating file %x -Skapar fil %x - -Items processed: -Processade poster - -Items remaining: -Återstående poster: - -Total time: -Total tid: - - -1 byte -%x bytes - - -1 byte -%x byte - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -Tolkningsfel på filen %x, rad %y, kolumn %z. - -Cannot set directory lock for %x. -Kan inte låsa %x. - -Scanning: -Skannar: - - -1 thread -%x threads - - -1 tråd -%x trådar - - -Encoding extended time information: %x -Kodar utökad tidsinformation: %x - -/sec -/s - -%x items/sec -%x objekt/sek. - -Configuration file %x loaded partially only. -Konfigurationsfilen %x lästes bara delvis in. - -Show in Explorer -Visa i Utforskaren - -Open with default application -Öppna med standardprogram - -Browse directory -Sök upp mapp - -Cannot access the Volume Shadow Copy Service. -Kan inte komma åt tjänsten 'Volume Shadow Copy' - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Använd FreeFileSync 64-bitarsversion för att skapa skuggkopior på detta system. - -Cannot load file %x. -Kan inte läsa in %x - -Cannot determine volume name for %x. -Kan inte utläsa volymnamn för %x - -Volume name %x is not part of file path %y. -Volymnamnet %x är inte en del av sökvägen %y. - -Stop requested: Waiting for current operation to finish... -Stopp begärt: Väntar på att aktuell åtgärd skall slutföras... - -Unable to create timestamp for versioning: -Kunde inte skapa tidsstämpel för versionshantering: - -Cannot read the following XML elements: -Kan inte läsa följande XML-element: - -&Open... -&Öppna... - -Save &as... -S¶ som... - -&Quit -&Avsluta - -&Program -&Program - -&View help -&Visa hjälpen - -&About -&Om - -&Help -&Hjälp - -Usage: -Användning: - -1. Select folders to watch. -1. Välj mapp att bevaka. - -2. Enter a command line. -2. Mata in ett kommando. - -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 - -Folders to watch: -Mappar att övervaka: - -Add folder -Lägg till mapp - -Remove folder -Ta bort mapp - -Browse -Bläddra - -Select a folder -Välj en mapp - -Idle time (in seconds): -Vilotid (i sekunder) - -Idle time between last detected change and execution of command -Väntetid mellan senast upptäckta förändring och kommandoexekvering - -Command line: -Kommandorad: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -Kommandot triggas om: -- filer eller undermappar förändras -- nya mappar upptäcks (ex. USB-minne ansluts) - - -&Start -&Start - -&Retry -&Försök igen - -Cancel -Avbryt - -Build: %x -Bygge: %x - -About -Om - -All files -Alla filer - -Automated Synchronization -Automatiserad synkronisering - -Directory monitoring active -Mappövervakning aktiv - -Waiting until all directories are available... -Väntar på att samtliga mappar skall bli tillgängliga... - -&Restore -&Återställ - -&Show error -&Visa fel - -&Exit -&Avsluta - -Incorrect command line: -Felaktig kommandorad: - -File content -Filinnehåll - -File time and size -Tidsstämpling och storlek - -Two way -Tvåvägs - -Mirror -Spegla - -Update -Uppdatera - -Custom -Anpassat - -Multiple... -Flera... - -Moving file %x to %y -Flyttar filen %x till %y - -Moving folder %x to %y -Flyttar mappen %x till %y - -Moving symbolic link %x to %y -Flyttar den symboliska länken %x till %y - -Removing old versions... -Tar bort gamla versioner... - -Creating symbolic link %x -Skapar den symboliska länken %x - -Creating folder %x -Skapar mappen %x - -Overwriting file %x -Skriver över filen %x - -Overwriting symbolic link %x -Skriver över den symboliska länken %x - -Verifying file %x -Verifierar %x - -Updating attributes of %x -Uppdaterar attribut för %x - -Cannot find %x. -Kan inte hitta %x. - -Target folder %x already existing. -Målmappen %x finns redan. - -Target folder input field must not be empty. -Indatafältet för målmapp får inte vara tomt. - -Please enter a target folder for versioning. -Ange en målmapp för versionshantering. - -Source folder %x not found. -Källmappen %x kan inte hittas. - -The following items have unresolved conflicts and will not be synchronized: -Följande objekt har olösta konflikter, och kommer inte att synkroniseras: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -Följande mappar är signifikant olika. Kontrollera att du jämför rätt mappar för synkronisering. - -Not enough free disk space available in: -Ej tillräckligt ledigt diskutrymme på: - -Required: -Utrymmeskrav: - -Available: -Tillgängligt: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -En mapp som tillhör flera mapp-par är på väg att ändras. Kontrollera synkroniseringsinställningarna - -Synchronizing folder pair: -Synkroniserar mapp-par: - -Generating database... -Skapar databas... - -Creating a Volume Shadow Copy for %x... -Skapar en 'Volume Shadow Copy' för %x... - -Data verification error: %x and %y have different content. -Dataverifieringsfel: %x och %y har olika innehåll. - -job name -åtgärdsnamn - -Synchronization stopped -Synkroniseringen stoppad - -Synchronization completed with errors -Synkronisering slutförd med fel - -Synchronization completed with warnings -Synkronisering slutförd med varningar - -Nothing to synchronize -Det finns inget att synkronisera - -Synchronization completed successfully -Synkronisering slutförd - -Saving log file %x... -Sparar loggfil %x... - -You can switch to FreeFileSync's main window to resolve this issue. -Du kan växla till FreeFileSyncs programfönster för att lösa problemet. - -Switching to FreeFileSync's main window -Växla till FreeFileSyncs programfönster - -A new version of FreeFileSync is available: -Det finns en ny version av FreeFileSync - -Download now? -Ladda ner nu? - -Check for Program Updates -Sök efter programuppdateringar - -&Download -&Ladda ner - -FreeFileSync is up to date. -FreeFileSync är uppdaterad. - -Information -Information - -Unable to connect to sourceforge.net. -Kan inte ansluta sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Kan inte hitta aktuellt versionsnummer online. Vill du kontrollera manuellt? - -Symlink -Symboliska länkar - -Folder -Mapp - -Full path -Fullständig sökväg - -Name -Namn - -Relative path -Sökväg - -Base folder -Basmapp - -Size -Storlek - -Date -Datum - -Extension -Filformat - -Category -Kategori - -Action -Aktivitet - -Drag && drop -Dra && släpp - -Close progress dialog -Stäng förloppsindikator - -Standby -Strömsparläge - -Log off -Logga ut - -Shut down -Stäng av datorn - -Hibernate -Viloläge - -Alternate comparison settings -Alternativa jämförelseinställningar - -Alternate synchronization settings -Alternativa synkroniseringsinställningar - -Local filter -Lokalt filter - -Active -Aktiv - -None -Inget - -Remove alternate settings -Ta bort alternativa inställningar - -Clear filter settings -Rensa filterinställningar - -Copy -Kopiera - -Paste -Klistra in - -Alternate Comparison Settings -Alternativa jämförelseinställningar - -Alternate Synchronization Settings -Alternativa synkroniseringsinställningar - -Local Filter -Lokalt filter - -&New -&Nytt - -&Save -&Spara - -Save as &batch job... -Spara som &batch-fil... - -1. &Compare -1. &Jämför - -2. &Synchronize -2. &Synkronisera - -&Global settings -&Allmäna inställningar - -&Language -&Språk - -&Find... -&Sök... - -&Export file list... -&Exportera fillista... - -&Tools -&Verktyg - -&Check now -&Sök nu - -Check &automatically once a week -Sök &automatiskt en gång per vecka - -&Check for new version -&Sök efter uppdateringar - -Compare -Jämför - -Synchronize -Synkronisera - -Add folder pair -Lägg till mapp-par - -Remove folder pair -Ta bort mapp-par - -Swap sides -Byt sida - -Close search bar -Stäng sökfältet - -Find: -Sök: - -Match case -Matcha gemener/VERSALER - -Save as batch job -Spara som batch-fil - -Hide excluded items -Dölj undantagna objekt - -Show filtered or temporarily excluded files -Dölj filtrerade eller temporärt undantagna filer - -Number of files and folders that will be created -Antal filer och mappar som kommer att skapas - -Number of files that will be overwritten -Antal filer som kommer att skrivas över - -Number of files and folders that will be deleted -Antal filer och mappar som kommer att tas bort - -Total bytes to copy -Byte att kopiera - -Select a variant: -Välj ett alternativ: - -Identify equal files by comparing modification time and size. -Identifiera likadana filer genom att jämföra tidsstämpling och filstorlek. - -Identify equal files by comparing the file content. -Identifiera likadana filer genom att jämföra filinnehåll. - -Symbolic links: -Symboliska länkar: - -More information -Mer information - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Identifierar och sprider förändringar på båda sidor. Borttagningar, förflyttningar och konflikter detekteras automatiskt med hjälp av en databas. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Skapar en speglad kopia av vänster mapp, som exakt matchas av höger mapp efter synkronisering. - -Copy new and updated files to the right folder. -Kopierar nya och uppdaterade filer till höger mapp. - -Configure your own synchronization rules. -Konfigurera dina egna synkroniseringsregler. - -Detect moved files -Hitta flyttade filer - -Requires database files. Not supported by all file systems. -Kräver databasfiler som inte stöds av alla filsystem - -Delete files: -Ta bort filer: - -Permanent -Permanent - -Delete or overwrite files permanently -Ta bort eller skriv över permanent - -Recycle bin -Papperskorgen - -Back up deleted and overwritten files in the recycle bin -Kopiera borttagna och överskrivna filer till papperskorgen - -Versioning -Versionshantering - -Move files to a user-defined folder -Flytta filer till en fördefinierad mapp - -Naming convention: -Regler för namngivning - -Show examples -Visa exempel - -Handle errors: -Felhantering: - -Ignore -Ignorera - -Hide all error and warning messages -Visa inte fel- och varningsmeddelanden - -Pop-up -Popup - -Show pop-up on errors or warnings -Visa popup vid fel och varningar - -On completion: -Vid slutfört: - -Start synchronization now? -Vill du starta synkroniseringen nu? - -Variant: -Alternativ: - -Statistics -Statistik - -&Don't show this dialog again -&Visa inte den här dialogen igen - -Items found: -Funna poster: - -Speed: -Hastighet: - -Time remaining: -Återstående tid: - -Time elapsed: -Förfluten tid: - -Synchronizing... -Synkroniserar... - -Minimize to notification area -Minimera till meddelandefältet - -Close -Stäng - -&Pause -&Paus - -Stop -Stoppa - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Skapa en batch-fil för obevakad synkronisering. Dubbelklicka på filen för att starta den, eller schemalägg i en åtgärdshanterare: %x - -Stop synchronization at first error -Stoppa synkroniseringen vid första fel som uppstår - -Show progress dialog -Visa förloppsindikator - -Save log: -Spara logg: - -Limit: -Gräns: - -Limit maximum number of log files -Begränsa antalet loggfiler - -How can I schedule a batch job? -Hur schemalägger jag en batch-fil? - -&Recycle bin -&Papperskorgen - -Delete on both sides -Ta bort på båda sidor - -Delete on both sides even if the file is selected on one side only -Ta bort på båda sidor, även om filen är markerad på endast en sida - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Skapa filtreringsregler för att undanta vissa filer från synkronisering. Sökvägar relateras till motsvarande mapp-par - -Include: -Inkludera: - -Exclude: -Undanta: - -Time span: -Tidsrymd: - -File size: -Filstorlek: - -Minimum: -Min: - -Maximum: -Max: - -&Clear -&Rensa - -The following settings are used for all synchronization jobs. -Följande inställningar används för all synkronisering - -Fail-safe file copy -Felsäker filkopiering - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -Kopierar till en temporär fil (*.ffs_tmp), före överskrivning av målet. -Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. - - -(recommended) -(rekommenderas) - -Copy locked files -Kopiera låsta filer - -Copy shared or locked files using the Volume Shadow Copy Service. -Kopierar delade eller låsta filer med hjälp av tjänsten 'Volume Shadow Copy' - -(requires administrator rights) -(kräver administratörsbehörighet) - -Copy file access permissions -Kopiera filåtkomstbehörigheter - -Transfer file and folder permissions. -Överför behörighetsinställningar - -Automatic retry on error: -Automatiska återförsök vid fel: - -Retry count: -Antal försök: - -Delay (in seconds): -Fördröjning (i sekunder) - -Customize context menu: -Anpassad kontextmeny: - -Description -Beskrivning - -Restore hidden windows -Återställ dolda vyer - -&Default -&Standard - -Source code written in C++ using: -Källkod skriven i C++ med hjälp av: - -If you like FreeFileSync -Om du gillar FreeFileSync - -Donate with PayPal -Donera via PayPal - -Feedback and suggestions are welcome -Återkoppling och förslag är välkommna - -Homepage -Hemsida - -Email -E-post - -Published under the GNU General Public License -Publiserad under GNU General Public License - -Many thanks for localization: -Tack för översättning: - -Save as Batch Job -Spara som batch-fil - -Delete Items -Ta bort objekt - -Global Settings -Globala inställningar - -Select Time Span -Välj tidsrymd - -Folder Pairs -Mapp-par - -Find -Sök - -Overview -Översikt - -Configuration -Inställningar - -Main Bar -Primärt verktygsfält - -Filter Files -Filtrera filer - -Select View -Välj vy - -Open... -Öppna... - -Save -Spara - -Compare both sides -Jämför båda sidor - -Comparison settings -Jämförelseinställningar - -Synchronization settings -Synkroniseringsinställningar - -Start synchronization -Starta synkronisering - -Confirm -Bekräfta - -&Execute -&Kör - - -1 directory -%x directories - - -1 mapp -%x mappar - - - -1 file -%x files - - -1 fil -%x filer - - - -%y of 1 row in view -%y of %x rows in view - - -%y av 1 rad i vyn -%y av %x rader i vyn - - -Set direction: -Ange riktning: - -multiple selection -flerval - -Include via filter: -Inkludera via filter: - -Exclude via filter: -Lägg till i undantag: - -Exclude temporarily -Undanta tillfälligt - -Include temporarily -Inkludera tillfälligt - -Delete -Ta bort - -Include all -Inkludera alla - -Exclude all -Exkludera alla - -Show icons: -Visa ikoner - -Small -Liten - -Medium -Normal - -Large -Stor - -Select time span... -Välj tidsintervall... - -Default view -Standardvy - -Show "%x" -Visa "%x" - -Last session -Senaste session - -Folder Comparison and Synchronization -Mappjämförelse och synkronisering - -Configuration saved -Inställningar sparade - -FreeFileSync batch -FreeFileSync batch - -Do you want to save changes to %x? -Vill du spara ändringar till %x? - -Do&n't save -Spara &inte - -Never save &changes -Spara aldrig &ändringar - -Filter -Filter - -Show files that exist on left side only -Visa filer som endast finns till vänster - -Show files that exist on right side only -Visa filer som endast finns till höger - -Show files that are newer on left -Visa filer som är nyare till vänster - -Show files that are newer on right -Visa filer som är nyare till höger - -Show files that are equal -Visa filer som är lika - -Show files that are different -Visa filer som är olika - -Show conflicts -Visa konflikter - -Show files that will be created on the left side -Visa filer som kommer att skapas till vänster - -Show files that will be created on the right side -Visa filer som kommer att skapas till höger - -Show files that will be deleted on the left side -Visa filer som kommer att tas bort till vänster - -Show files that will be deleted on the right side -Visa filer som kommer att tas bort till höger - -Show files that will be overwritten on left side -Visa filer som skrivas över till vänster - -Show files that will be overwritten on right side -Visa filer som skrivas över till höger - -Show files that won't be copied -Visa filer som inte kommer att kopieras - -Set as default -Ange som standard - -All folders are in sync -Alla mappar är synkroniserade - -Synchronization Settings -Synkroniseringsinställningar - -Comparison Settings -Jämförelseinställningar - -Cannot find %x -Kan inte hitta %x - -Comma-separated values -Kommaseparerade värden - -File list exported -Fillista exporterad - -Searching for program updates... -Söker efter programuppdateringar... - -&Ignore subsequent errors -&Ignorera efterföljande fel - -&Ignore -&Ignorera - -Serious Error -Allvarligt fel - -&Don't show this warning again -&Visa inte den här varningen igen - -&Switch -&Växla - -&Yes -&Ja - -&No -&Nej - -Scanning... -Skannar... - -Comparing content... -Jämför innehåll... - -Info -Info - -Paused -Pausad - -Initializing... -Initierar... - -Stopped -Stoppad - -Completed -Slutförd - -&Continue -&Fortsätt - -Log -Logg - -Today -Idag - -This week -Denna veckan - -This month -Denna månaden - -This year -I år - -Last x days -Senaste x dagarna - -Byte -Byte - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Vill du verkligen flytta följande objekt till papperskorgen? -Vill du verkligen flytta följande %x objekt till papperskorgen? - - -Move -Flytta - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Vill du verkligen ta bort följande objekt? -Vill du verkligen ta bort följande %x objekt? - - -Exclude -Undanta - -Direct -Direkt - -Follow -Följ - -Copy NTFS permissions -Kopiera NTFS-behörigheter - -Integrate external applications into context menu. The following macros are available: -Integrera externa program i högerklicksmeny. Följande variabler finns tillgängliga: - -- full file or folder name -- fullständigt fil- eller mappnamn - -- folder part only -- endast mappdelen - -- Other side's counterpart to %item_path% -- Andra sidans motsvarighet till %item_path% - -- Other side's counterpart to %item_folder% -- Andra sidans motsvarighet till %item_folder% - -Restore all hidden windows and warnings? -Vill du återställa alla dolda vyer och varningar - -Leave as unresolved conflict -Ignorera konflikt - -Replace -Byt ut - -Move files and replace if existing -Flytta filer och byt ut om de redan finns - -Time stamp -Tidsstämpel - -Append a timestamp to each file name -Lägg till en tidsstämpel till varje filnamn - -File -Fil - -YYYY-MM-DD hhmmss -YYYY-MM-DD hhmmss - -Files -Filer - -Items -Objekt - -Percentage -Procent - -Cannot monitor directory %x. -Mappen %x kan inte övervakas. - -Conversion error: -Konversionsfel: - -Cannot delete file %x. -Filen %x kan inte tas bort. - -The file is locked by another process: -Filen är låst av en annan process: - -Cannot move file %x to %y. -Kan inte flytta filen %x till %y. - -Cannot delete directory %x. -Kan inte ta bort mappen %x. - -Cannot write file attributes of %x. -Kan inte skriva filattribut för %x. - -Cannot write modification time of %x. -Kan inte ändra tidsangivelsen för %x. - -Cannot read security context of %x. -Kan inte läsa säkerhetskontext för %x. - -Cannot write security context of %x. -Kan inte skriva säkerhetskontext för %x. - -Cannot read permissions of %x. -Kan inte läsa behörigheter för %x. - -Cannot write permissions of %x. -Kan inte skriva behörigheter för %x. - -Cannot create directory %x. -Kan inte skapa mappen %x. - -Cannot create symbolic link %x. -Kan inte skapa den symboliska länken, %x - -Cannot find system function %x. -Kan inte hitta systemfunktion %x - -Cannot copy file %x to %y. -Kan inte kopiera filen %x till %y. - -Type of item %x is not supported: -Objekttyp %x stöds ej: - -Cannot resolve symbolic link %x. -Den symboliska länken %x kan inte matchas. - -Cannot open directory %x. -Kan inte öppna %x. - -Cannot enumerate directory %x. -Kan inte räkna upp %x. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -1 min -%x min - - - -1 hour -%x hours - - -1 timma -%x timmar - - - -1 day -%x days - - -1 dag -%x dagar - - -Unable to register to receive system messages. -Det gick inte att registrera mottagning av systemmeddelanden - -Cannot set privilege %x. -Kan inte att ange behörigheten %x. - -Unable to suspend system sleep mode. -Det går inte att avbryta systemets viloläge - -Cannot change process I/O priorities. -Kan inte ändra process I/O-prioritet - -Unable to move %x to the recycle bin. -Kan inte att flytta %x till papperskorgen - -Cannot determine final path for %x. -Kan inte utläsa slutlig sökväg för %x. - -Error Code %x: -Felkod %x: - diff --git a/BUILD/Languages/turkish.lng b/BUILD/Languages/turkish.lng deleted file mode 100644 index 7ab30b7f..00000000 --- a/BUILD/Languages/turkish.lng +++ /dev/null @@ -1,1516 +0,0 @@ -
    - Türkçe - Kaya Zeren - tr_TR - flag_turkey.png - 2 - n == 1 ? 0 : 1 -
    - -Unable to suspend system sleep mode. - - -Unable to register to receive system messages. - - -Restore all hidden windows and warnings? - - -Move - - -Stopped - - -Serious Error - - -Comparison Settings - - -Synchronization Settings - - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -Select View - - -Filter Files - - -Main Bar - - -Folder Pairs - - -Select Time Span - - -Global Settings - - -Delete Items - - -Save as Batch Job - - -Restore hidden windows - - -Customize context menu: - - -Delay (in seconds): - - -Retry count: - - -Automatic retry on error: - - -Transfer file and folder permissions. - - -(requires administrator rights) - - -Copy shared or locked files using the Volume Shadow Copy Service. - - -(recommended) - - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - - -The following settings are used for all synchronization jobs. - - -Maximum: - - -Minimum: - - -File size: - - -Time span: - - -Exclude: - - -Include: - - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. - - -&Recycle bin - - -How can I schedule a batch job? - - -Limit: - - -Save log: - - -Stop synchronization at first error - - -Stop - - -Variant: - - -Start synchronization now? - - -On completion: - - -Handle errors: - - -Show examples - - -Delete files: - - -Copy new and updated files to the right folder. - - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. - - -More information - - -Symbolic links: - - -Identify equal files by comparing the file content. - - -Identify equal files by comparing modification time and size. - - -Select a variant: - - -Find: - - -Close search bar - - -&Check for new version - - -&Find... - - -Local Filter - - -Alternate Synchronization Settings - - -Alternate Comparison Settings - - -None - - -Active - - -Local filter - - -Alternate synchronization settings - - -Alternate comparison settings - - -Check for Program Updates - - -Retrying operation - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Switching to FreeFileSync's main window - - -You can switch to FreeFileSync's main window to resolve this issue. - - -Synchronization stopped - - -Please enter a target folder for versioning. - - -Incorrect command line: - - -Automated Synchronization - - -&Start - - -Command line: - - -Idle time (in seconds): - - -Folders to watch: - - -&View help - - -Unable to create timestamp for versioning: - - -Stop requested: Waiting for current operation to finish... - - -%x items/sec - - -Both sides have changed since last synchronization. -Son eşleştirmeden bu yana iki tarafın da içeriği değişmiş. - -Cannot determine sync-direction: -Eşleştirme yönü belirlenemedi: - -No change since last synchronization. -Son eşleştirmeden bu yana bir değişiklik olmamış. - -The database entry is not in sync considering current settings. -Geçerli kayıtlar ile veritabanı kaydı aynı değil. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Varsayılan eşleştirme yönleri ayarlanıyor: Yeni dosyalar eski dosyaların üzerine yazılacak. - -Checking recycle bin availability for folder %x... -%x klasörü için Geri Dönüşüm Kutusu kullanılabilir mi diye bakılıyor... - -Moving file %x to the recycle bin -%x dosyası Geri Dönüşüm Kutusuna atılıyor - -Moving folder %x to the recycle bin -%x klasörü Geri Dönüşüm Kutusuna atılıyor - -Moving symbolic link %x to the recycle bin -%x simgesel bağlantısı Geri Dönüşüm Kutusuna atılıyor - -Deleting file %x -%x dosyası siliniyor - -Deleting folder %x -%x klasörü siliniyor - -Deleting symbolic link %x -%x sembolik bağlantısı siliniyor - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -Şu klasörler için Geri Dönüşüm Kutusu kullanılamaz. Dosyalar kalıcı olarak silinecek: - -An exception occurred -Olağan dışı bir durumla karşılaşıldı - -A directory path is expected after %x. -%x ardından bir klasör yolu gelmelidir. - -Syntax error -Yazım hatası - -Cannot open file %x. -%x dosyası açılamıyor. - -Error -Hata - -File %x does not contain a valid configuration. -%x dosyası geçerli ayar bilgilerini içermiyor. - -Unequal number of left and right directories specified. -Sağdan ve soldan seçilen klasör sayısı aynı değil. - -The config file must not contain settings at directory pair level when directories are set via command line. -Klasörler komut satırından seçildiğinde, ayar dosyasında klasör çifti düzeyinde ayar bulunmamalıdır. - -Warning -Uyarı - -Directories cannot be set for more than one configuration file. -Klasörler bir ayar dosyasından fazlasında kullanılamaz. - -Syntax: -Yazım: - -config files -ayar dosyaları - -directory -klasör - -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -FreeFileSync .ffs_gui ya da .ffs_batch ayar dosyalarının sayısı. - -Any number of alternative directories for at most one config file. -En çok bir ayar dosyası için alternatif klasörlerin sayısı. - -Command line -Komut satırı - -A folder input field is empty. -Bir klasör giriş alanı boş. - -The corresponding folder will be considered as empty. -Karşıdaki klasör boş olarak kabul edilecek. - -Cannot find the following folders: -Aşağıdaki klasörler bulunamadı: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -Bu hatayı yok sayarak karşıdaki klasörleri boş kabul edebilirsiniz. Bu klasörler eşleştirme sırasında kendiliğinden oluşturulur. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -Şu klasörlerin bağlı yolları var. Eşleştirme kurallarını ayarlarken dikkatli olun: - -File %x has an invalid date. -%x dosyasının tarihi geçersiz. - -Date: -Tarih: - -Files %x have the same date but a different size. -%x dosyalarının tarihleri aynı fakat boyutları farklı. - -Size: -Boyut: - -Items differ in attributes only -Yalnız öznitelikleri farklı ögeler - -Resolving symbolic link %x -%x sembolik bağlantısı çözümleniyor - -Comparing content of files %x -%x dosyalarının içeriği karşılaştırılıyor - -Generating file list... -Dosya listesi oluşturuluyor... - -Starting comparison -Karşılaştırmaya başlanıyor - -Calculating sync directions... -Eşleştirme yönleri hesaplanıyor... - -Out of memory. -Bellek yetersiz. - -Item exists on left side only -Yalnız solda bulunan ögeler - -Item exists on right side only -Yalnız sağda bulunan ögeler - -Left side is newer -Soldaki daha yeni ögeler - -Right side is newer -Sağdaki daha yeni ögeler - -Items have different content -İçeriği farklı ögeler - -Both sides are equal -Öge iki taraftada aynı - -Conflict/item cannot be categorized -Uyuşmayan/sınıflanamayan ögeler - -Copy new item to left -Yeni öge sola kopyalansın - -Copy new item to right -Yeni öge sağa kopyalansın - -Delete left item -Soldaki öge silinsin - -Delete right item -Sağdaki öge silinsin - -Move file on left -Soldaki dosya taşınsın - -Move file on right -Sağdaki dosya taşınsın - -Overwrite left item -Soldaki ögenin üzerine yazılsın - -Overwrite right item -Sağdaki ögenin üzerine yazılsın - -Do nothing -Hiçbir işlem yapılmasın - -Update attributes on left -Soldaki öznitelikleri güncelle - -Update attributes on right -Sağdaki öznitelikler güncellensin - -Database file %x is incompatible. -%x veritabanı dosyası uyumsuz - -Initial synchronization: -Başlangıç eşleştirmesi: - -Database file %x does not yet exist. -%x veritabanı dosyası henüz yok. - -Database file is corrupt: -Veritabanı dosyası bozulmuş: - -Cannot write file %x. -%x dosyası yazılamadı. - -Cannot read file %x. -%x dosyası okunamadı. - -Database files do not share a common session. -Veritabanı dosyaları ortak bir oturumu paylaşamaz. - -Searching for folder %x... -%x klasörü aranıyor... - -Cannot read file attributes of %x. -%x dosyasının özellikleri okunamadı. - -Cannot get process information. -İşlem bilgisi alınamadı. - -Waiting while directory is locked (%x)... -Klasör kilitli olduğundan bekleniyor (%x)... - - -1 sec -%x sec - - -1 saniye -%x saniye - - -Creating file %x -%x dosyası ekleniyor - -Items processed: -İşlenen öge: - -Items remaining: -Kalan öge: - -Total time: -Toplam süre: - - -1 byte -%x bytes - - -1 bayt -%x bayt - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Error parsing file %x, row %y, column %z. -%x dosyası işlenirken hata, satır %y, sütun %z. - -Cannot set directory lock for %x. -%x için klasör kilidi uygulanamıyor. - -Scanning: -Taranıyor: - - -1 thread -%x threads - - -1 iş parçacığı -%x iş parçacığı - - -Encoding extended time information: %x -Uzatılmış zaman bilgisi kodlanıyor: %x - -/sec -/saniye - -Configuration file %x loaded partially only. -%x ayarlar dosyası kısmen yüklendi. - -Show in Explorer -Tarayıcıda Görüntüleyin - -Open with default application -Varsayılan uygulama ile açın - -Browse directory -Klasöre gözatın - -Cannot access the Volume Shadow Copy Service. -Birim Gölge Hizmetine erişilemiyor. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Bu sistem üzerinde gölge kopyalar oluşturmak için FreeFileSync 64-bit sürümünü kullanın. - -Cannot load file %x. -%x dosyası yüklenemedi. - -Cannot determine volume name for %x. -%x için birim adı belirlenemedi. - -Volume name %x is not part of file path %y. -%x birim adı %y dosya yolunun bir parçası değil. - -Cannot read the following XML elements: -Şu XML elemanları okunamadı: - -&Open... -&Açın... - -Save &as... -F&arklı kaydedin... - -&Quit -Çı&kın - -&Program -&Dosya - -&About -H&akkında - -&Help -&Yardım - -Usage: -Kullanım: - -1. Select folders to watch. -1. İzlenecek klasörleri seçin - -2. Enter a command line. -2. Bir komut satırı yazın. - -3. Press 'Start'. -3. 'Başlat'a Tıklayın. - -To get started just import a .ffs_batch file. -.ffs_batch dosyasını yükleyerek başlayabilirsiniz. - -Add folder -Klasör ekleyin - -Remove folder -Klasörü silin - -Browse -Gözatın - -Select a folder -Bir klasör seçin - -Idle time between last detected change and execution of command -Son algılanan değişiklik ile komutun yürütülmesi arasında beklenecek süre - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -Komut şu durumlarda yürütülür: -- dosya ya alt klasörler değiştiğinde -- yeni klasörler algılandığında (örneğin bir USB bellek takıldığında) - - -&Retry -&Yeniden deneyin - -Cancel -İptal - -Build: %x -Yapım: %x - -About -Hakkında - -All files -Tüm dosyalar - -Directory monitoring active -Klasör izleme kullanılıyor - -Waiting until all directories are available... -Tüm klasörlerin uygun olması bekleniyor... - -&Restore -Gö&rüntülensin - -&Show error -Hataya &bakın - -&Exit -Çı&kın - -File content -İçeriğe göre - -File time and size -Tarih ve saate göre - -Two way -Çift yönlü - -Mirror -Yansıtma - -Update -Güncelleme - -Custom -Özel - -Multiple... -Çoklu... - -Moving file %x to %y -%x dosyası %y içine taşınıyor - -Moving folder %x to %y -%x klasörü %y içine taşınıyor - -Moving symbolic link %x to %y -%x sembolik bağlantısı %y içine taşınıyor - -Removing old versions... -Eski sürümler siliniyor... - -Creating symbolic link %x -%x sembolik bağlantısı ekleniyor - -Creating folder %x -%x klasörü ekleniyor - -Overwriting file %x -%x dosyasının üzerine yazılıyor - -Overwriting symbolic link %x -%x sembolik bağlantısının üzerine yazılıyor - -Verifying file %x -%x dosyası doğrulanıyor - -Updating attributes of %x -%x öznitelikleri güncelleniyor - -Cannot find %x. -%x bulunamadı. - -Target folder %x already existing. -%x hedef klasörü zaten var. - -Target folder input field must not be empty. -Hedef klasör giriş alanı boş olmamalı. - -Source folder %x not found. -%x kaynak klasörü bulunamadı. - -The following items have unresolved conflicts and will not be synchronized: -Uyuşmazlığı çözülmemiş şu ögeler eşleştirilmeyecek: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -Şu klasörler arasında oldukça büyük farklar var. Eşleştirme için doğru klasörleri seçtiğinize emin olun. - -Not enough free disk space available in: -Şurada yeterli disk alanı yok : - -Required: -Zorunlu: - -Available: -Kullanılabilir: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Çoklu klasör çiftlerinin parçası olan bir klasör değiştirilecek. Lütfen eşleştirme ayarlarını gözden geçirin. - -Synchronizing folder pair: -Eşleştirilen klasör çifti: - -Generating database... -Veri tabanı oluşturuluyor... - -Creating a Volume Shadow Copy for %x... -%x için Birim Gölge Hizmeti oluşturuluyor... - -Data verification error: %x and %y have different content. -Veri doğrulama hatası: %x ve %y farklı içeriklere sahip. - -job name -iş adı - -Synchronization completed with errors -Eşleştirme bazı hatalarla tamamlandı - -Synchronization completed with warnings -Eşleştirme işlemi bazı uyarılarla tamamlandı - -Nothing to synchronize -Eşleştirilecek bir şey yok - -Synchronization completed successfully -Eşleştirme tamamlandı - -Saving log file %x... -%x günlük dosyası kaydediliyor... - -A new version of FreeFileSync is available: -Yeni bir FreeFileSync sürümü yayınlanmış: - -Download now? -İndirilsin mi? - -&Download -İn&dirin - -FreeFileSync is up to date. -FreeFileSync güncel. - -Information -Bilgi - -Unable to connect to sourceforge.net. -sourceforge.net sitesine bağlanılamıyor. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Geçerli FreeFileSync sürüm numarası çevrimiçi olarak bulunamıyor. El ile denetlemek ister misiniz? - -Symlink -Smblkbağlantı - -Folder -Klasör - -Full path -Tam yol - -Name -Ad - -Relative path -Bağıl klasör yolu - -Base folder -Başlangıç klasörü - -Size -Boyut - -Date -Tarih - -Extension -Uzantı - -Category -Öge tipi - -Action -İşlem - -Drag && drop -Klasör ya da dosyaları sürükleyip buraya bırakabilirsiniz - -Close progress dialog -İşlem penceresi kapatılsın - -Standby -Uykuya dalınsın - -Log off -Oturum kapatılsın - -Shut down -Bilgisayar kapatılsın - -Hibernate -Hazırda bekletilsin - -Remove alternate settings -Alternatif ayarları silin - -Clear filter settings -Süzgeç ayarlarını temizleyin - -Copy -Kopyalayın - -Paste -Yapıştırın - -&New -Ye&ni - -&Save -&Kaydedin - -Save as &batch job... -&Toplu iş olarak kaydedin... - -1. &Compare -1. &Karşılaştırın - -2. &Synchronize -2. &Eşleştirin - -&Global settings -&Genel ayarlar - -&Language -Di&l - -&Export file list... -Dosya list&esini verin... - -&Tools -A&raçlar - -&Check now -&Sürümü Denetleyin - -Check &automatically once a week -&Haftada bir kendiliğinden denetlensin - -Compare -Karşılaştırın - -Synchronize -Eşleştirin - -Add folder pair -Klasör çifti ekleyin - -Remove folder pair -Klasör çiftini silin - -Swap sides -Sağ ve sol tarafları değiştirin - -Match case -Büyük/küçük harfe uyulsun - -Save as batch job -Toplu iş olarak kaydedin - -Hide excluded items -Katılmayan ögeler görüntülensin - -Show filtered or temporarily excluded files -Süzülmüş ya da geçici olarak katılmayan dosyalar görüntülenir - -Number of files and folders that will be created -Eklenecek dosya ve klasör sayısı - -Number of files that will be overwritten -Üzerine yazılacak dosya sayısı - -Number of files and folders that will be deleted -Silinecek dosya ve klasör sayısı - -Total bytes to copy -Toplam kopyalanacak bayt - -OK -Tamam - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -İki taraftaki değişiklikler belirlenir ve kopyalanır. Silme, taşıma ve çakışmalar, veritabanı kullanılarak kendiliğinden algılanır. - -Configure your own synchronization rules. -Eşleştirme kurallarını istediğiniz şekilde ayarlayabilirsiniz. - -Detect moved files -Taşınmış dosyalar algılansın - -Requires database files. Not supported by all file systems. -Veritabanı dosyalarına gerek duyar. Tüm dosya sistemleri tarafından desteklenmez. - -Permanent -Kalıcı olarak silinsin - -Delete or overwrite files permanently -Dosyalar kalıcı olarak silinir ya da üzerine yazılır - -Recycle bin -Geri Dönüşüm Kutusuna atılsın - -Back up deleted and overwritten files in the recycle bin -Geri Dönüşüm Kutusundaki silinmiş ya da üzerine yazılmış dosyalar yedeklensin - -Versioning -Eski sürüm olarak saklansın - -Move files to a user-defined folder -Dosyalar kullanıcı tarafından belirtilen bir klasöre taşınsın - -Naming convention: -Adlandırma kuralı: - -Ignore -Yoksayılsın - -Hide all error and warning messages -Tüm hata ve uyarı iletileri gizlenir - -Pop-up -Görüntülensin - -Show pop-up on errors or warnings -Hata ya da uyarılar açılır pencerede görüntülenir - -Statistics -İstatistikler - -&Don't show this dialog again -Bu &pencere bir daha görüntülenmesin - -Items found: -Bulunan öge: - -Speed: -Hız: - -Time remaining: -Kalan süre: - -Time elapsed: -Geçen süre: - -Synchronizing... -Eşleştiriliyor... - -Minimize to notification area -Bildirim alanına küçültün - -Close -Kapatın - -&Pause -&Duraklatılsın - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Katılımsız eşleştirme için bir toplu iş dosyası oluşturun. İşlemi başlatmak için bu dosyaya çift tıklayın ya da görev zamanlayıcı ile programlayın: %x - -Show progress dialog -İşlem penceresi görüntülensin - -Limit maximum number of log files -Tutulacak en fazla günlük dosyası sayısı - -Delete on both sides -Her iki taraftaki de silinsin - -Delete on both sides even if the file is selected on one side only -Dosya yalnız bir tarafta seçili olsa bile her iki taraftaki de silinir - -&Clear -&Temizleyin - -Fail-safe file copy -Dosyalar hatasız kopyalansın - -Copy locked files -Kilitli dosyalar da kopyalansın - -Copy file access permissions -Dosya erişim izinleri de kopyalansın - -Description -Açıklama - -&Default -&Varsayılan - -Source code written in C++ using: -Kaynak kodu C++ kullanılarak yazılmıştır: - -If you like FreeFileSync -FreeFileSync’i beğendiyseniz - -Donate with PayPal -PayPal üzerinden bağış yapın - -Feedback and suggestions are welcome -Öneri ve geri bildirimlerinizi bekleriz - -Homepage -Web sitesi - -Email -E-posta - -Published under the GNU General Public License -GNU Genel Kamu Lisansı koşulları altında yayınlanmıştır - -Many thanks for localization: -Çeviriler için çok teşekkürler: - -Find -Arayın - -Overview -Genel - -Configuration -İşlemler - -Open... -Açın... - -Save -Kaydedin - -Compare both sides -İki tarafı karşılaştırır - -Comparison settings -Karşılaştırma ayarları - -Synchronization settings -Eşleştirme ayarları - -Start synchronization -Eşleştirmeyi başlatın - -Confirm -Onaylayın - -&Execute -Çalış&tırın - - -1 directory -%x directories - - -1 klasör -%x klasör - - - -1 file -%x files - - -1 dosya -%x dosya - - - -%y of 1 row in view -%y of %x rows in view - - -%y / 1 satır görüntüleniyor -%y / %x satır görüntüleniyor - - -Set direction: -Yönü seçin: - -multiple selection -çoklu seçim - -Include via filter: -Şu süzgeçle katılsın: - -Exclude via filter: -Süzerek dışarda bırakılsın: - -Exclude temporarily -Geçici olarak katılmasın - -Include temporarily -Geçici olarak katılsın - -Delete -Silin - -Include all -Tümü katılsın - -Exclude all -Hiçbiri katılmasın - -Show icons: -Simgeler görüntülensin: - -Small -Küçük - -Medium -Orta - -Large -Büyük - -Select time span... -Zaman aralığını seçin... - -Default view -Varsayılan görünüm - -Show "%x" -"%x" panelini görüntülensin - -Last session -Önceki oturum - -Folder Comparison and Synchronization -Klasör Karşılaştırma ve Eşleştirme - -Configuration saved -Ayarlar kaydedildi - -FreeFileSync batch -FreeFileSync toplu işi - -Do you want to save changes to %x? -Değişiklikleri %x dosyasına kaydetmek istiyor musunuz? - -Do&n't save -Kaydedilmesi&n - -Never save &changes -Değişiklikler asla &kaydedilmesin - -Filter -Süzgeç - -Show files that exist on left side only -Yalnız sol tarafta bulunan dosyaları görüntüler ya da gizler - -Show files that exist on right side only -Yalnız sağ tarafta bulunan dosyaları görüntüler ya da gizler - -Show files that are newer on left -Solda daha yeni olan dosyaları görüntüler ya da gizler - -Show files that are newer on right -Sağda daha yeni olan dosyaları görüntüler ya da gizler - -Show files that are equal -Aynı olan dosyaları görüntüler ya da gizler - -Show files that are different -Farklı olan dosyaları görüntüler ya da gizler - -Show conflicts -Uyuşmazlıkları görüntüler ya da gizler - -Show files that will be created on the left side -Sol tarafa eklenecek dosyalar görüntülensin - -Show files that will be created on the right side -Sağ tarafa eklenecek dosyalar görüntülensin - -Show files that will be deleted on the left side -Sol tarafta silinecek dosyalar görüntülensin - -Show files that will be deleted on the right side -Sağ tarafta silinecek dosyalar görüntülensin - -Show files that will be overwritten on left side -Sol tarafta üzerine yazılacak dosyalar görüntülensin - -Show files that will be overwritten on right side -Sağ tarafta üzerine yazılacak dosyalar görüntülensin - -Show files that won't be copied -Kopyalanmayacak dosyalar görüntülensin - -Set as default -Varsayılan olarak belirleyin - -All folders are in sync -Tüm klasörler eşleştirildi - -Cannot find %x -%x bulunamadı - -Comma-separated values -Virgül ile ayrılmış değerler - -File list exported -Dosya listesi verildi - -Searching for program updates... -Yazılım güncellemesine bakılıyor... - -&Ignore subsequent errors -Sonraki &hatalar yoksayılsın - -&Ignore -&Yoksayın - -&Don't show this warning again -Bu uyarı bir daha &görüntülenmesin - -&Switch -&Değiştirin - -&Yes -&Evet - -&No -&Hayır - -Scanning... -Taranıyor... - -Comparing content... -İçerik karşılaştırılıyor... - -Info -Bilgi - -Paused -Duraklatıldı - -Initializing... -Başlatılıyor... - -Completed -Tamamlandı - -&Continue -&Devam - -Log -Günlük - -Today -Bugün - -This week -Bu hafta - -This month -Bu ay - -This year -Bu yıl - -Last x days -Son x günde - -Byte -Bayt - -KB -KB - -MB -MB - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Aşağıdaki ögeyi Geri Dönüşüm Kutusuna atmak istediğinize emin misiniz? -Aşağıdaki %x ögeyi Geri Dönüşüm Kutusuna atmak istediğinize emin misiniz? - - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Aşağıdaki 1 ögeyi silmek istediğinize emin misiniz? -Aşağıdaki %x ögeyi silmek istediğinize emin misiniz? - - -Exclude -Katılmayacak ögeler - -Direct -Doğrudan katılsın - -Follow -Hedefleri katılsın - -Copy NTFS permissions -NTFS izinleri de kopyalansın - -Integrate external applications into context menu. The following macros are available: -Sağ tık menüsüne dış uygulamalar ekler. Şu etiketler kullanılabilir: - -- full file or folder name -- tam dosya ya da klasör adı - -- folder part only -- yalnız klasör bölümü - -- Other side's counterpart to %item_path% -%item_path% ögesinin diğer taraftaki karşılığı - -- Other side's counterpart to %item_folder% -%item_folder% ögesinin diğer taraftaki karşılığı - -Leave as unresolved conflict -Uyuşmazlık çözülmeden bırakılsın - -Replace -Değiştirin - -Move files and replace if existing -Dosyalar taşınsın ve varsa üzerine yazılsın - -Time stamp -Zaman Damgası - -Append a timestamp to each file name -Dosya adlarına zaman damgası eklensin - -File -Dosya - -YYYY-MM-DD hhmmss -YYYY-AA-GG SSddss - -Files -Dosyalar - -Items -Ögeler - -Percentage -Yüzde - -Cannot monitor directory %x. -%x klasörü izlenemiyor. - -Conversion error: -Dönüştürme hatası: - -Cannot delete file %x. -%x dosyası silinemiyor. - -The file is locked by another process: -Dosya başka bir işlem tarafından kilitlenmiş: - -Cannot move file %x to %y. -%x dosyası %y olarak taşınamadı. - -Cannot delete directory %x. -%x klasörü silinemedi. - -Cannot write file attributes of %x. -%x dosya öznitelikleri yazılamadı. - -Cannot write modification time of %x. -%x son değişiklik zamanı yazılamadı. - -Cannot read security context of %x. -%x için güvenlik bağlamı okunamadı. - -Cannot write security context of %x. -%x için güvenlik bağlamı yazılamadı. - -Cannot read permissions of %x. -%x izinleri okunamadı. - -Cannot write permissions of %x. -%x izinleri yazılamadı. - -Cannot create directory %x. -%x klasörü eklenemedi. - -Cannot create symbolic link %x. -%x sembolik bağlantısı oluşturulamadı. - -Cannot find system function %x. -%x sistem işlevi bulunamadı. - -Cannot copy file %x to %y. -%x dosyası %y olarak kopyalanamadı. - -Type of item %x is not supported: -%x ögesi tipi desteklenmiyor: - -Cannot resolve symbolic link %x. -%x simge bağlantısı çözümlenemedi - -Cannot open directory %x. -%x klasörü açılamadı. - -Cannot enumerate directory %x. -%x klasörü sayılamadı. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -1 dakika -%x dakika - - - -1 hour -%x hours - - -1 saat -%x saat - - - -1 day -%x days - - -1 gün -%x gün - - -Cannot set privilege %x. -%x izni verilemedi. - -Cannot change process I/O priorities. -Giriş/Çıkış işlemi öncelikleri değiştirilemedi - -Unable to move %x to the recycle bin. -%x Geri Dönüşüm Kutusuna atılamıyor. - -Cannot determine final path for %x. -Son %x yolu belirlenemedi. - -Error Code %x: -Hata Kodu %x: - diff --git a/BUILD/Languages/ukrainian.lng b/BUILD/Languages/ukrainian.lng deleted file mode 100644 index 539524e0..00000000 --- a/BUILD/Languages/ukrainian.lng +++ /dev/null @@ -1,1527 +0,0 @@ -
    - Українська - Roman Ardan - uk_UA - flag_ukraine.png - 3 - n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2 -
    - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - - - -&Check - - -Retrying operation... - - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - - - -Both sides have changed since last synchronization. -З моменту останньої синхронізації з обох сторін відбулися зміни. - -Cannot determine sync-direction: -Не можна визначити напрям синхронізації: - -No change since last synchronization. -Жодних змін з останньої синхронізації. - -The database entry is not in sync considering current settings. -Запис бази даних не синхронізований з урахуванням поточних налаштувань. - -Setting default synchronization directions: Old files will be overwritten with newer files. - -Налаштування напряму синхронізації за замовчуванням: -Старі файли будуть замінені новішими файлами. - - -Checking recycle bin availability for folder %x... -Перевірка доступності Корзини для папки %x... - -Moving file %x to the recycle bin -Переміщення файлу %x до Корзини - -Moving folder %x to the recycle bin -Переміщення папки %x до Корзини - -Moving symbolic link %x to the recycle bin -Переміщення символічного посилання %x до Корзини - -Deleting file %x -Вилучення файлу %x - -Deleting folder %x -Вилучення папки %x - -Deleting symbolic link %x -Вилучення символьного посилання %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -Корзина не доступна для таких папок. Файли замість цього будуть видалені назавжди: - -An exception occurred -Відбулось виключення - -A directory path is expected after %x. -Після %x очікується шлях до каталогу. - -Syntax error -Синтаксична помилка - -Cannot open file %x. -Не вдається фідкрити файл %x. - -File %x does not contain a valid configuration. -Файл %x не містить правильної конфігурації. - -Unequal number of left and right directories specified. -Вказано різну кількість лівих і правих каталогів - -The config file must not contain settings at directory pair level when directories are set via command line. -Конфігураційний файл не повинен містити налаштувань на рівні пар каталогів, якщо каталоги задаються командним рядком. - -Directories cannot be set for more than one configuration file. -Каталоги не можуть бути призначені більш ніж одному файлу конфігурації. - -Command line -Командний рядок - -Syntax: -Синтаксис: - -config files -файли конфігурації - -directory -каталог - -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Будь-яка кількість FreeFileSync .ffs_gui та/або .ffs_batch файлів конфігурації. - -Any number of alternative directories for at most one config file. -Будь-яка кількість альтернативних каталогів для щонайбільше одного конфігураційного файлу. - -A folder input field is empty. -Порожнє поле папки. - -The corresponding folder will be considered as empty. -Відповідна папка буде вважатися порожньою. - -Cannot find the following folders: -Не вдається знайти такі папки: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -Ви можете ігнорувати цю помилку, вважаючи кожну папку порожньою. Папки будуть автоматично створені під час синхронізації. - -The following folders have dependent paths. Be careful when setting up synchronization rules: -Наступні папки мають залежні шляхи. Будьте обережні при налаштуванні правил синхронізації: - -File %x has an invalid date. -Файл %x має неіснуючу дату. - -Date: -Дата: - -Files %x have the same date but a different size. -Файли %x мають однакову дату, але різні за розміром. - -Size: -Розмір: - -Items differ in attributes only -Елементи відрізняються тільки атрибутами - -Resolving symbolic link %x -Вирішення символьного посилання %x - -Comparing content of files %x -Порівнювання вмісту файлів %x - -Generating file list... -Створення списку файлів... - -Starting comparison -Початок порівняння - -Calculating sync directions... -Встановлення напрямку синхронізації... - -Out of memory. -Бракує пам'яті. - -Item exists on left side only -Елемент існує тільки ліворуч - -Item exists on right side only -Елемент існує тільки праворуч - -Left side is newer -Ліва сторона новіша - -Right side is newer -Права сторона новіша - -Items have different content -Елементи мають різний вміст - -Both sides are equal -Сторони ідентичні - -Conflict/item cannot be categorized -Не вдається категоризувати конфлікт/елемент - -Copy new item to left -Копіювати нові елементи ліворуч - -Copy new item to right -Копіювати нові елементи праворуч - -Delete left item -Вилучити елемент ліворуч - -Delete right item -Вилучити елемент праворуч - -Move file on left -Перемістити файли ліворуч - -Move file on right -Перемістити файли праворуч - -Overwrite left item -Перезаписати елемент ліворуч - -Overwrite right item -Перезаписати елемент праворуч - -Do nothing -Нічого не робити - -Update attributes on left -Оновити атрибути ліворуч - -Update attributes on right -Оновити атрибути праворуч - -Database file %x is incompatible. -Несумісний файл бази даних %x. - -Initial synchronization: -Вступна синхронізація: - -Database file %x does not yet exist. -Файл бази даних %x ще не існує. - -Database file is corrupt: -Файл бази даних пошкоджений: - -Cannot write file %x. -Не вдається записати файл %x. - -Cannot read file %x. -Не вдається прочитати файл %x. - -Database files do not share a common session. -Файли баз даних не поділяють спільну сесію. - -Searching for folder %x... -Пошук каталогу %x... - -Cannot read file attributes of %x. -Не вдається прочитати атрибути файла %x. - -Cannot get process information. -Не вдається отримати інформацію процесу - -Waiting while directory is locked (%x)... -Очікування зняття блокування з каталогу (%x)... - - -1 sec -%x sec - - -%x сек -%x сек -%x сек - - -Creating file %x -Створення файлу %x - -Items processed: -Елементів оброблено: - -Items remaining: -Елементів залишилось: - -Total time: -Загальний час: - - -1 byte -%x bytes - - -%x байт -%x байти -%x байтів - - -%x MB -%x МБ - -%x KB -%x КБ - -%x GB -%x ГБ - -Error parsing file %x, row %y, column %z. -Помилка розбору файла %x, рядок %y, колонка %z. - -Cannot set directory lock for %x. -Не вдається замкнути каталога %x. - -Scanning: -Сканую: - - -1 thread -%x threads - - -%x нить виконання -%x ниті виконання -%x нитей виконання - - -Encoding extended time information: %x -Кодування розширеної інформації про час: %x - -/sec -/сек - -%x items/sec -%x елемента/сек - -Configuration file %x loaded partially only. -Файл конфігурації %x завантажено лише частково. - -Show in Explorer -Показати у Провіднику - -Open with default application -Відкрити за допомогою програми за замовчуванням - -Browse directory -Переглянути каталог - -Cannot access the Volume Shadow Copy Service. -Не вдається отримати доступ до послуги Тіньового Копіювання Тому. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Будь ласка, використовуйте 64-розрядну версію FreeFileSync для створення тіньових копій у цій системі. - -Cannot load file %x. -Не вдається завантажити файл %x. - -Cannot determine volume name for %x. -Не вдалося встановити ім'я тому для %x. - -Volume name %x is not part of file path %y. -Ім'я тому %x не є частиною файловго шляху %y. - -Stop requested: Waiting for current operation to finish... -Запит зупинки: очікування завершення поточної операції... - -Unable to create timestamp for versioning: -Не вдається створити часової мітки для версій: - -Cannot read the following XML elements: -Не вдається прочитати такі елементи XML: - -&Open... -&Відкрити... - -Save &as... -Зберегти &як... - -&Quit -&Вихід - -&Program -&Програма - -&View help -&Перегляд довідки - -&About -&Про програму - -&Help -&Допомога - -Usage: -Використання: - -1. Select folders to watch. -1. Виберіть папки для моніторингу; - -2. Enter a command line. -2. Уведіть рядок команди; - -3. Press 'Start'. -3. Натисніть 'Старт'. - -To get started just import a .ffs_batch file. -Щоб розпочати імпортуйте .ffs_batch файл. - -Folders to watch: -Папки для спостеження - -Add folder -Додати папку - -Remove folder -Вилучити папку - -Browse -Переглянути - -Select a folder -Вибрати папку - -Idle time (in seconds): -Час очікування (секунд): - -Idle time between last detected change and execution of command -Час простою між виявленням останньої зміни та виконанням команди - -Command line: -Командний рядок: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -Команда спрацьовує, якщо: -- змінилися файли або вкладені папки -- появилися нові папки (наприклад, підключена USB-пам'ять) - - -&Start -&Старт - -About -Про - -Build: %x -компіляція %x - -All files -Всі файли - -Automated Synchronization -Автоматична Синхронізація - -Directory monitoring active -Моніторинг каталогів активний - -Waiting until all directories are available... -Очікування доступності всіх каталогів... - -Error -Помилка - -&Restore -&Відновити - -&Show error -&Показати помилку - -&Exit -&Вихід - -Incorrect command line: -Неправильний командний рядок: - -&Retry -&Повторити - -File content -Вміст файлу - -File time and size -Дата та розмір файлу - -Two way -Обидва напрямки - -Mirror -Дзеркало - -Update -Оновити - -Custom -Вибірково - -Multiple... -Різні варіанти... - -Moving file %x to %y -Переміщення файлу %x до %y - -Moving folder %x to %y -Переміщення папки %x до %y - -Moving symbolic link %x to %y -Переміщення символьного посилання %x до %y - -Removing old versions... -Видалення старих версій... - -Creating symbolic link %x -Створення символьного посилання %x - -Creating folder %x -Створення папки %x - -Overwriting file %x -Перезапис файлу %x - -Overwriting symbolic link %x -Перезапис символьного посилання %x - -Verifying file %x -Перевірка файлу %x - -Updating attributes of %x -Оновлення атрибутів %x - -Cannot find %x. -Не вдається знайти %x. - -Target folder %x already existing. -Цільова папка %x вже існує. - -Target folder input field must not be empty. -Поле цільової папки не повинно бути порожнім. - -Please enter a target folder for versioning. -Будь ласка, введіть цільову папку для версій. - -Source folder %x not found. -Вихідний каталог %x не знайдено. - -The following items have unresolved conflicts and will not be synchronized: -Наступні елементи мають невирішені конфлікти і не будуть синхронізовані: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -Ці папки істотно відрізняються. Переконайтеся, що ви вказали відповідні папки для синхронізації. - -Not enough free disk space available in: -Не достатньо вільного місця в: - -Required: -Потрібно: - -Available: -Доступно: - -A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -Буде змінена папка, яка є частиною кількох пар папок. Будь ласка, перегляньте налаштування синхронізації. - -Synchronizing folder pair: -Синхронізація пари папок: - -Generating database... -Створення бази даних... - -Creating a Volume Shadow Copy for %x... -Створення Тіньової Копії для %x... - -Data verification error: %x and %y have different content. -Помилка перевірки даних: %x та %y мають різний вміст. - -job name -назва завдання - -Synchronization stopped -Синхронізацію зупинено - -Synchronization completed with errors -Синхронізація закінчилася з помилками - -Synchronization completed with warnings -Синхронізація завершена з попередженнями - -Nothing to synchronize -Нічого синхронізувати - -Synchronization completed successfully -Синхронізація успішно завершена - -Saving log file %x... -Збереження файла журналу %x... - -You can switch to FreeFileSync's main window to resolve this issue. -Ви можете перейти до головного вікна FreeFileSync щоб вирішити це питання. - -&Don't show this warning again -&Надалі не показувати це попередження - -&Ignore -&Ігнорувати - -&Switch -&Змінити - -Switching to FreeFileSync's main window -Перехід до головного вікна FreeFileSync - -&Ignore subsequent errors -&Ігнорувати наступні помилки - -Serious Error -Серйозна помилка - -Check for Program Updates -Перевірка Оновлень Програми - -A new version of FreeFileSync is available: -Доступна нова версія FreeFileSync: - -Download now? -Завантажити зараз? - -&Download -&Завантажити - -FreeFileSync is up to date. -У Вас найновіша версія FreeFileSync. - -Unable to connect to sourceforge.net. -Не можна з’єднатися з sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Не вдається знайти номер поточної версії FreeFileSync онлайн. Бажаєте перевірити вручну? - -Last session -Остання сесія - -Copy -Копіювати - -Symlink -Символьне посилання - -Folder -Папка - -Full path -Повний шлях - -Name -Назва - -Relative path -Відносний шлях - -Base folder -Базова папка - -Size -Розмір - -Date -Дата - -Extension -Розширення - -Category -Категорія - -Action -Дія - -Drag && drop -Drag && drop - -Close progress dialog -Закрити вікно прогресу - -Standby -Сплячий режим - -Log off -Вилогувати - -Shut down -Вимкнути комп'ютер - -Hibernate -Гібернація - -Alternate comparison settings -Альтернативні налаштування порівняння - -Alternate synchronization settings -Альтернативні налаштування синхронізації - -Local filter -Локальний фільтр - -Active -Активні - -None -Відсутні - -Remove alternate settings -Вилучити альтернативні налаштування - -Clear filter settings -Очистити налаштування фільтра - -Paste -Вклеїти - -Alternate Comparison Settings -Альтернативні Налаштування Порівняння - -Alternate Synchronization Settings -Альтернативні Налаштування Синхронізації - -Local Filter -Локальний Фільтр - -&New -&Нова - -&Save -&Зберегти - -Save as &batch job... -Зберегти як &пакетне завдання - -1. &Compare -1. &Порівняти - -2. &Synchronize -2. &Синхронізувати - -&Global settings -&Глобальні налаштування - -&Language -&Мова - -&Find... -&Знайти... - -&Export file list... -&Експортувати список файлів... - -&Tools -&Інструменти - -&Check now -&Перевірити тепер - -Check &automatically once a week -Перевіряти &автоматично щотижня - -&Check for new version -&Перевірка наявності нової версії - -Compare -Порівняти - -Cancel -Відмінити - -Synchronize -Синхронізувати - -Add folder pair -Додати пару папок - -Remove folder pair -Вилучити пару папок - -Swap sides -Поміняти місцями - -Close search bar -Закрити панель пошуку - -Find: -Знайти: - -Match case -Враховувати регістр - -Save as batch job -Зберегти як пакетне завдання - -Hide excluded items -Приховати виключені елементи - -Show filtered or temporarily excluded files -Показати відфільтровані чи тимчасово виключені елементи - -Number of files and folders that will be created -Кількість файлів і папок, які будуть створені - -Number of files that will be overwritten -Кількість файлів, які будуть перезаписані - -Number of files and folders that will be deleted -Кількість файлів і папок, які будуть вилучені - -Total bytes to copy -Всього зкопіювати байтів - -Select a variant: -Виберіть варіант: - -Identify equal files by comparing modification time and size. -Визначити однакові файли порівнюючи час модифікації та розмір. - -Identify equal files by comparing the file content. -Визначити однакові файли порівнюючи їх вміст. - -Symbolic links: -Символьні посилання: - -More information -Додаткова інформація - -OK -OK - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Виявити та поширити зміни на обидві сторони. Видалення, перейменування та конфлікти визначаються автоматично використовуючи базу даних. - -Create a mirror backup of the left folder which exactly matches the right folder after synchronization. -Створити дзеркальну копію лівої частини, після синхронізації права папка повністю дорівнюватиме лівій. - -Copy new and updated files to the right folder. -Скопіювати нові та оновлені файли в праву папку. - -Configure your own synchronization rules. -Налаштувати власні правила синхронізації. - -Detect moved files -Виявляти переміщені файли - -Requires database files. Not supported by all file systems. -Потрібні файли бази даних. Підтримується не всіма файловими системами. - -Delete files: -Вилучити файли: - -Permanent -Назавжди - -Delete or overwrite files permanently -Вилучати чи перезаписати файли назавжди - -Recycle bin -Корзина - -Back up deleted and overwritten files in the recycle bin -Резервно зберегти вилучені та перезаисані файли в Корзині - -Versioning -Запис версій - -Move files to a user-defined folder -Перемістити файли у визначену користувачем папку - -Naming convention: -Метод іменування: - -Show examples -Показати приклади - -Handle errors: -Обробка помилок: - -Ignore -Ігнорувати - -Hide all error and warning messages -Приховати всі помилки і повідомлення з попередженнями - -Pop-up -Виринаючі вікна - -Show pop-up on errors or warnings -Показувати виринаючі вікна при помилках та попередженнях - -On completion: -Після завершення: - -Start synchronization now? -Розпочати синхронізацію? - -Variant: -Варіант: - -Statistics -Статистика - -&Don't show this dialog again -Більше &не показувати цей діалог - -Items found: -Елементів знайдено: - -Speed: -Швидкість: - -Time remaining: -Залишилось часу: - -Time elapsed: -Пройшло часу: - -Synchronizing... -Синхронізація... - -Minimize to notification area -Згорнути в область повідомлень - -Close -Замкнути - -&Pause -&Пауза - -Stop -Зупинити - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Створити пакетний файл для автоматичної синхронізації. Щоб розпочати двічі клацніть цей файл або заплануйте в планувальнику завдань: %x - -Stop synchronization at first error -Зупинити синхронізацію при першій помилці - -Show progress dialog -Показувати вікно прогресу - -Save log: -Зберегти журнал: - -Limit: -Обмеження: - -Limit maximum number of log files -Обмежити максимальну кількість файлів журналу - -How can I schedule a batch job? -Як можна запланувати пакетне завдання? - -&Recycle bin -&Корзина - -Delete on both sides -Вилучити з обох сторін - -Delete on both sides even if the file is selected on one side only -Вилучити з обох сторін, навіть якщо файл виділений тільки з однієї сторони - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Виберіть правила фільтрації для виключення деяких файлів із синхронізації. Введіть шляхи файлів відносно відповідної пари папок. - -Include: -Включити - -Exclude: -Виключити - -Time span: -Часовий інтервал - -File size: -Розмір Файла - -Minimum: -Мінімум: - -Maximum: -Максимум: - -&Clear -&Очистити - -The following settings are used for all synchronization jobs. -Наступні налаштування використовуються для всіх завдань синхронізації. - -Fail-safe file copy -Безпечне копіювання файлів - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -Скопіювати в тимчасовий файл (*.ffs_tmp) перед перезаписом цілі. -Це гарантує узгоджений стан навіть у випадку серйозної помилки. - - -(recommended) -(рекомендовано) - -Copy locked files -Копіювати заблоковані файли - -Copy shared or locked files using the Volume Shadow Copy Service. -Копіювати спільні та заблоковані файли за допомогою сервісу Тіньового Копіювання Тому. - -(requires administrator rights) -(потрібні права адміністратора) - -Copy file access permissions -Копіювати права доступу до файлу - -Transfer file and folder permissions. -Перенести права файлів і папок. - -Automatic retry on error: -Автоматичний повтор при помилці: - -Retry count: -Кількість спроб: - -Delay (in seconds): -Затримка (секунд): - -Customize context menu: -Налаштування контекстного меню: - -Description -Опис - -Restore hidden windows -Відновити приховані вікна - -&Default -&За замовчуванням - -Source code written in C++ using: -Код програми написаний на C++ з використанням: - -If you like FreeFileSync -Якщо Вам сподобався FreeFileSync - -Donate with PayPal -Пожертвувати через PayPal - -Feedback and suggestions are welcome -Відгуки та пропозиції вітаються - -Homepage -Оф.сайт - -Email -Почта - -Published under the GNU General Public License -Видано за ліцензією GNU General Public License - -Many thanks for localization: -Подяка за локалізацію: - -Save as Batch Job -Зберегти як пакетне завдання - -Delete Items -Вилучити Елементи - -Global Settings -Глобальні Налаштування - -Select Time Span -Виберіть Інтервал Часу - -Folder Pairs -Пари Папок - -Find -Знайти - -Overview -Головна - -Configuration -Налаштування - -Main Bar -Головна панель - -Filter Files -Фільтрування файлів - -Select View -Вибрати перегляд - -Open... -Відкрити... - -Save -Зберегти - -Compare both sides -Порівняти обидві сторони - -Comparison settings -Налаштування порівнювання - -Synchronization settings -Налаштування синхронізації - -Start synchronization -Розпочати синхронізацію - -Confirm -Підтвердити - -&Execute -&Виконати - - -1 directory -%x directories - - -%x каталог -%x каталоги -%x каталогів - - - -1 file -%x files - - -%x файл -%x файли -%x файлів - - - -%y of 1 row in view -%y of %x rows in view - - -%y of %x рядка -%y of %x рядків -%y of %x рядків - - -Set direction: -Виберіть напрям: - -multiple selection -груповий вибір - -Include via filter: -Включити згідно фільтру: - -Exclude via filter: -Виключити через фільтр: - -Exclude temporarily -Виключити тимчасово - -Include temporarily -Включити - -Delete -Видалити - -Include all -Включити все - -Exclude all -Виключити все - -Show icons: -Показати іконки: - -Small -Малий - -Medium -середній - -Large -великий - -Select time span... -Виберіть інтервал часу... - -Default view -Вигляд за замовчуванням - -Show "%x" -Показати "%x" - -Folder Comparison and Synchronization -Порівнювання та Синхронізація папок - -Configuration saved -Налаштування синхронізації збережено - -FreeFileSync batch -Командний файл FreeFileSync - -Do you want to save changes to %x? -Зберегти зміни в %x? - -Never save &changes -Ніколи не зберігати &зміни - -Do&n't save -&Не зберігати - -Filter -Фільтр - -Show files that exist on left side only -Показати файли, які є тільки ліворуч - -Show files that exist on right side only -Показати файли, які є тільки праворуч - -Show files that are newer on left -Показати файли, які новіші ліворуч - -Show files that are newer on right -Показати файли, які новіші праворуч - -Show files that are equal -Показати однакові файли - -Show files that are different -Показати різні файли - -Show conflicts -Показати конфлікти - -Show files that will be created on the left side -Показати файли, які будуть створені ліворуч - -Show files that will be created on the right side -Показати файли, які будуть створені праворуч - -Show files that will be deleted on the left side -Показати файли, які будуть вилучені ліворуч - -Show files that will be deleted on the right side -Показати файли, які будуть вилучені праворуч - -Show files that will be overwritten on left side -Показати файли, які будуть перезаписані ліворуч - -Show files that will be overwritten on right side -Показати файли, які будуть перезаписані праворуч - -Show files that won't be copied -Показати файли, які не будуть зкопійовані - -Set as default -Встановити за замовчуванням - -All folders are in sync -Всі папки синхронізовано - -Synchronization Settings -Налаштування Синхронізації - -Comparison Settings -Параметри Порівняння - -Cannot find %x -Не можна знайти %x - -Comma-separated values -Значення розділені комою - -File list exported -Список файлів експортовано - -Searching for program updates... -Пошук оновлень програми ... - -Scanning... -Сканування... - -Comparing content... -Порівнювання вмісту... - -Info -Інформація - -Warning -Увага - -Paused -Призупинено - -Initializing... -Ініціалізація... - -Stopped -Зупинено - -Completed -Завершено - -&Continue -&Продовжити - -Log -Лог - -Today -Сьогодні - -This week -Цього тижня - -This month -Цього місяця - -This year -Цього року - -Last x days -Останні x днів - -Byte -Байт - -KB -КБ - -MB -МБ - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Ви дійсно хочете перемістити цей %x елемент у Корзину? -Ви дійсно хочете перемістити ці %x елементи у Корзину? -Ви дійсно хочете перемістити ці %x елементів у Корзину? - - -Move -Перемістити - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Ви дійсно хочете вилучити цей %x елемент? -Ви дійсно хочете вилучити ці %x елементи? -Ви дійсно хочете вилучити ці %x елементів? - - -Exclude -Виключити - -Direct -Прямо - -Follow -Послідовно - -Copy NTFS permissions -Копіювати права доступу NTFS - -Integrate external applications into context menu. The following macros are available: -Інтеграція зовнішніх додатків в контекстному меню. Доступні макроси: - -- full file or folder name -- повна назва файлу або папки - -- folder part only -- тільки папка - -- Other side's counterpart to %item_path% -- Елемент з протилежної сторони до %item_path% - -- Other side's counterpart to %item_folder% -- Елемент з протилежної сторони до %item_folder% - -Restore all hidden windows and warnings? -Відновити всі приховані вікна та попередження? - -Leave as unresolved conflict -Залишити як невирішений конфлікт - -Replace -Замінити - -Move files and replace if existing -Перемістити файли замінюючи існуючі - -Time stamp -Часова мітка - -Append a timestamp to each file name -Додати часову мітку до кожного імені файлу - -File -Файл - -YYYY-MM-DD hhmmss -YYYY-MM-DD hhmmss - -Files -Файли - -Items -Елементи - -Percentage -Проценти - -Cannot monitor directory %x. -Не вдається контролювати каталог %x. - -Conversion error: -Помилка перетворення: - -Cannot delete file %x. -Не вдається видалити файл %x. - -The file is locked by another process: -Файл заблоковано іншим процесом: - -Cannot move file %x to %y. -Не вдається перемістити файл %x до %y. - -Cannot delete directory %x. -Не вдається видалити каталог %x. - -Cannot write file attributes of %x. -Не вдається записати атрибути файла %x. - -Cannot write modification time of %x. -Не вдається записати часу модифікації %x. - -Cannot read security context of %x. -Не вдається прочитати контексту безпеки %x. - -Cannot write security context of %x. -Не вдається записати контексту безпеки %x. - -Cannot read permissions of %x. -Не вдається прочитати дозволів %x. - -Cannot write permissions of %x. -Не вдається записати дозволів %x. - -Cannot create directory %x. -Не вдається створити каталогу %x. - -Cannot create symbolic link %x. -Не вдається створити символьного посилання %x. - -Cannot find system function %x. -Не вдається знайти системної функції %x. - -Cannot copy file %x to %y. -Не вдається зкопіювати файл %x до %y. - -Type of item %x is not supported: -Тип елемента %x не підтримується: - -Cannot resolve symbolic link %x. -Не вдається вирішити символьне посилання %x. - -Cannot open directory %x. -Не вдається відкрити каталогу %x. - -Cannot enumerate directory %x. -Не вдається вчитати каталог %x. - -%x TB -%x ТБ - -%x PB -%x ПБ - - -1 min -%x min - - -%x хв -%x хв -%x хв - - - -1 hour -%x hours - - -%x година -%x години -%x годин - - - -1 day -%x days - - -%x день -%x дні -%x днів - - -Unable to register to receive system messages. -Не вдається зареєструватися для отримання системних повідомлень. - -Cannot set privilege %x. -Не вдається встановити привілеї %x. - -Unable to suspend system sleep mode. -Не вдається призупинbnb режим сну системи. - -Cannot change process I/O priorities. -Не вдалося змінити пріоритетів Вх/Вих процесу. - -Unable to move %x to the recycle bin. -Не вдається перемістити %x до Корзини. - -Cannot determine final path for %x. -Не вдається визначити кінцевого шляху для %x. - -Error Code %x: -Код помилки %x: - diff --git a/BUILD/Resources.zip b/BUILD/Resources.zip deleted file mode 100644 index f5e1c651..00000000 Binary files a/BUILD/Resources.zip and /dev/null differ diff --git a/BUILD/Sync_Complete.wav b/BUILD/Sync_Complete.wav deleted file mode 100644 index 96dd2a15..00000000 Binary files a/BUILD/Sync_Complete.wav and /dev/null differ diff --git a/BUILD/styles.gtk_rc b/BUILD/styles.gtk_rc deleted file mode 100644 index c6b1b5ab..00000000 --- a/BUILD/styles.gtk_rc +++ /dev/null @@ -1,8 +0,0 @@ -style "no-inner-border" -{ - GtkButton::focus-padding = 0 - GtkButton::focus-line-width = 0 - GtkButton::inner-border = {0, 0, 0, 0} -} - -class "GtkButton" style "no-inner-border" diff --git a/FreeFileSync.vcxproj b/FreeFileSync.vcxproj deleted file mode 100644 index abe818b1..00000000 --- a/FreeFileSync.vcxproj +++ /dev/null @@ -1,311 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {86C36CC7-9418-4253-9928-828486F59A00} - Win32Proj - FreeFileSync - $(VCTargetsPath11) - - - - Application - true - Unicode - v120_xp - - - Application - true - Unicode - v120_xp - - - Application - false - true - Unicode - v120_xp - - - Application - false - true - Unicode - v120_xp - - - - - - - - - - - - - - - - - - - BUILD\Bin\ - OBJ\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ - false - $(ProjectName)_$(PlatformName) - - - BUILD\Bin\ - OBJ\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ - false - $(ProjectName)_$(PlatformName) - - - false - BUILD\Bin\ - OBJ\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ - false - $(ProjectName)_$(PlatformName) - - - false - BUILD\Bin\ - OBJ\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ - false - $(ProjectName)_$(PlatformName) - false - - - - Use - Level4 - Disabled - _SCL_SECURE_NO_WARNINGS;wxUSE_UNICODE;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;__WXDEBUG__;WXUSINGDLL;_DEBUG;%(PreprocessorDefinitions) - C:\Data\C++\Boost;C:\Data\C++\wxWidgets\include;C:\Data\C++\wxWidgets\lib\vc12_x86_debug_dll\mswud;. - wx+/pch.h - 4100;4512 - true - $(IntDir)pch.obj - zen/warn_static.h;wx+/pch.h - EditAndContinue - false - true - - - Windows - true - $(OutDir)$(TargetName)$(TargetExt) - wxmsw29ud_aui.lib;wxmsw29ud_adv.lib;wxmsw29ud_core.lib;wxbase29ud_net.lib;wxbase29ud.lib;wxpngd.lib;wxzlibd.lib;comctl32.lib;ws2_32.lib;Rpcrt4.lib;winmm.lib;%(AdditionalDependencies);Wininet.lib - C:\Data\C++\Boost\stage\lib;C:\Data\C++\wxWidgets\lib\vc12_x86_debug_dll - - - - - C:\Data\C++\wxWidgets\include - %(PreprocessorDefinitions);ZEN_ARCHITECTURE_X86 - - - - - Use - Level4 - Disabled - _SCL_SECURE_NO_WARNINGS;wxUSE_UNICODE;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;__WXDEBUG__;WXUSINGDLL;_DEBUG;%(PreprocessorDefinitions) - C:\Data\C++\Boost;C:\Data\C++\wxWidgets\include;C:\Data\C++\wxWidgets\lib\vc12_x64_debug_dll\mswud;. - wx+/pch.h - 4100;4512 - true - $(IntDir)pch.obj - zen/warn_static.h;wx+/pch.h - ProgramDatabase - true - false - true - false - - - Windows - true - $(OutDir)$(TargetName)$(TargetExt) - wxbase29ud.lib;wxmsw29ud_core.lib;wxmsw29ud_adv.lib;wxmsw29ud_aui.lib;wxbase29ud_net.lib;wxpngd.lib;wxzlibd.lib;comctl32.lib;ws2_32.lib;Rpcrt4.lib;winmm.lib;%(AdditionalDependencies);Wininet.lib - C:\Data\C++\Boost\stage_x64\lib;C:\Data\C++\wxWidgets\lib\vc12_x64_debug_dll - - - - - - - C:\Data\C++\wxWidgets\include - %(PreprocessorDefinitions);ZEN_ARCHITECTURE_X64 - - - - - Level4 - NotUsing - MaxSpeed - true - _SCL_SECURE_NO_WARNINGS;wxUSE_UNICODE;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;%(PreprocessorDefinitions) - C:\Data\C++\Boost;C:\Data\C++\wxWidgets\include;C:\Data\C++\wxWidgets\lib\vc12_x86_release_lib\mswu;. - Speed - 4100;4512;4996 - MultiThreaded - true - zen/warn_static.h - - - Windows - false - true - true - wxmsw29u_aui.lib;wxmsw29u_adv.lib;wxmsw29u_core.lib;wxbase29u.lib;wxpng.lib;wxzlib.lib;wxbase29u_net.lib;comctl32.lib;ws2_32.lib;winmm.lib;Rpcrt4.lib;%(AdditionalDependencies);Wininet.lib - $(OutDir)$(TargetName)$(TargetExt) - C:\Data\C++\Boost\stage\lib;C:\Data\C++\wxWidgets\lib\vc12_x86_release_lib - - - - - C:\Data\C++\wxWidgets\include - %(PreprocessorDefinitions);ZEN_ARCHITECTURE_X86 - - - "C:\Data\C++\CodeSigning\SignCode.cmd" "$(TargetPath)" - - - - - Level4 - NotUsing - MaxSpeed - true - _SCL_SECURE_NO_WARNINGS;wxUSE_UNICODE;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;%(PreprocessorDefinitions) - C:\Data\C++\Boost;C:\Data\C++\wxWidgets\include;C:\Data\C++\wxWidgets\lib\vc12_x64_release_lib\mswu;. - Speed - 4100;4512;4996 - MultiThreaded - true - zen/warn_static.h - false - - - Windows - true - true - wxmsw29u_aui.lib;wxmsw29u_adv.lib;wxmsw29u_core.lib;wxbase29u.lib;wxpng.lib;wxzlib.lib;wxbase29u_net.lib;comctl32.lib;ws2_32.lib;winmm.lib;Rpcrt4.lib;%(AdditionalDependencies);Wininet.lib - $(OutDir)$(TargetName)$(TargetExt) - C:\Data\C++\Boost\stage_x64\lib;C:\Data\C++\wxWidgets\lib\vc12_x64_release_lib - - - true - - - C:\Data\C++\wxWidgets\include - %(PreprocessorDefinitions);ZEN_ARCHITECTURE_X64 - - - "C:\Data\C++\CodeSigning\SignCode.cmd" "$(TargetPath)" - - - - - Use - Use - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Create - Create - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Designer - - - - - - - \ No newline at end of file diff --git a/FreeFileSync/Build/Changelog.txt b/FreeFileSync/Build/Changelog.txt new file mode 100644 index 00000000..4e96b14b --- /dev/null +++ b/FreeFileSync/Build/Changelog.txt @@ -0,0 +1,1352 @@ +FreeFileSync 5.23 +----------------- +Allow sorting root nodes on overview panel +Support retry on failure to resolve path by volume name +Copy high-precision modification times for files and symlinks +Align top panel height with comparison and sync buttons +Show lock owner while waiting on a locked directory +Resolved help file W3C validation issues +Fixed high contrast accessibility issues in help +Fixed crash for CPUs without SSE2 when using VSS (Windows XP) +Prevent progress statistics timer overflow +Save RealtimeSync settings before forced exit due to shutdown or log off +Resolved contract violation error due to out of memory +RealtimeSync does not block system shutdown anymore +Added "select all" context menu option for progess log +Handle progress log keyboard input ignoring input focus +Fixed category icon background color issues +Report error when reading active config file failed during save +Preload adjacent file icons on grid + + +FreeFileSync 5.22 [2013-10-01] +------------------------------ +New options for automatic retry after error +Improved compliance with Windows User Experience guidelines +Harmonized popup dialog layouts +Correctly show program menu when main dialog receives focus (OS X) +Revised configuration dialog layouts and designs +Fixed crash on startup for CPUs without SSE2 support (Windows XP) +Work around wxWidgets bug for sorted list boxes (Linux) +Updated and revised help file +Early parameter validation for filter and sync config dialogs +Fixed followed directory symlinks being incorrectly excluded +Automatically calculate best initial message box size +Progress graph and status icons support high contrast color schemes +Include directory child-elements when manually setting filter +Allow manual filter for short name on overview panel +Don't treat file drops on directory input fields as URI (Linux) +Updated translation files + + +FreeFileSync 5.21 [2013-09-02] +------------------------------ +Detect moved/renamed files in mirror and custom variants +New database format for two way variant: old database files are converted automatically +Support double-clicking ffs_gui/ffs_batch files (OS X) +Integrated search panel (Ctrl + F, F3) into main dialog +Merged variant names into top button labels +Hide dock icon while minimized to notification area (OS X) +New keyboard shortcuts: F5, F6, F7, F8, F9, F10 +Further reduced size of database files by 10% +Fixed Outlook *.ost files found missing on VSS snapshot volumes +Added include filter context menu option +Correctly scroll to search hits on different grid +Always remove .ffs_tmp files permanently +Fixed layout for buttons with text and graphics for RTL languages (Arabic, Hebrew) +Revised file filter parser: new syntax for excluding items in subdirectories +Improved configuration merge algorithm +Fixed crash when showing help due to wxWidgets 64-bit bug in help component (Windows 8) +Avoid progress dialog graph flicker during resize when too few samples are available +Progress status when deleting files not greyed out anymore +Increased time-out to 20 seconds when checking for directory existence +Exclude broken symlinks via filter before showing error message +Follow symlinks when checking file/directory existence (Linux) +Consistently set batch error codes during startup phase +Updated translation files + + +FreeFileSync 5.20 [2013-08-03] +------------------------------ +Fixed crash on startup due to wxWidgets 64-bit bug in font enumeration (Windows 8) + + +FreeFileSync 5.19 [2013-08-02] +------------------------------ +Redesigned progress dialog including new items graph +New command line syntax: set directory names of a .ffs_gui/.ffs_batch externally +Explicit button on progress dialog to minimize to systray +Fixed progress graph labels being truncated (Debian, Ubuntu, openSUSE) +Resolved main dialog z-order issues during sync (OS X) +Reduced progress dialog layout twitching +Further improved comparison speed by 10% +Use proper config file path in file picker dialog (OS X) +Never interrupt when updating a file with fail-safe file copy after target was deleted +Prevent crash when closing progress dialog while paused (OS X) +Support external command lines starting with whitespace (Windows) +Show warning before starting external applications for more than 10 items +Start external applications synchronously if needed to avoid running out of system resources +Don't show hidden progress dialog when showing an error message in silent batch mode (OS X) +Correctly show file names containing ampersand characters in progress dialog +Adapt size of results dialog to fit contents +Correctly execute file move before parent directory will be deleted +Show a blinking system tray icon on errors instead of a modal dialog in RealtimeSync +Added installation size for Windows' Add/Remove Programs + + +FreeFileSync 5.18 [2013-07-02] +------------------------------ +Work around boost 1.54 bug "The procedure entry point GetTickCount64 could not be located in the dynamic link library KERNEL32.dll" (Windows XP) + + +FreeFileSync 5.17 [2013-07-02] +------------------------------ +Consider target file when updating followed file symlinks +Support moving files to recycle bin contained in followed directory symlinks +Move instead of copy updated files into versioning directory +Reduced memory peak when loading large database files after comparison +Check recycle bin existence only once per base folder and only if deletions occur (Windows) +Revised and enhanced error messages +Show moved files in same category as updated files +More pessimistic calculation of required disk space reducing false positives +Implemented platform-specific standard button ordering (Linux, OS X) +Set configuration panel primary orientation to vertical +Added new checks and error message strings for translation file parser +Revised middle grid inactive color and duplicate equality symbol +Skip XML comments while parsing config files +Redesigned confirmation popup dialogs +Standard button spacing conforms to operating system conventions +Shrinked memory consumption of file hierarchy data structures +Don't show file deletion dialog if selection is empty +Fixed incorrect progress statistics if a file or directory is deleted externally after comparison +Focus grid cursor row after switching sides with keyboard direction keys +Improved localization process: find translation deltas more easily, better error reporting +Reset initiated grid selection when changing grid cursor +Improved sync progress dialog layout +Suppress dubious wxWidgets error message "locale 'es_AR' can not be set". (OS X) +Don't show busy cursor on synchronization results dialog +Log error message upon retry as type info only +Updated translation files + + +FreeFileSync 5.16 [2013-06-01] +------------------------------ +Integrated both category and sync action view into middle grid +Condensed folder pair display names on overview panel +Consider symlinks and junctions when copying locked files (Windows Vista) +Resolved failure to set directory lock within Windows XP as Virtual Box guest +Period resolves to working directory again +Fixed "DecodePointer could not be located in KERNEL32.dll" (Windows 2000) +Support closing progress dialog forcefully during sync (OS X) +Don't disable all child items if directory traversal fails for a single item only +Simplified deletion confirmation dialog (removed "delete on both sides") +Work around wxWidgets leaking memory on exit (OS X) +Avoid wxWidgets crash when deleting folder pair control (OS X) +Prevent wxWidgets corrupting stack when wxLocale is allocated statically (Linux) +Use GetUserDefaultLangID to determine installer default language +Avoid progress speed and remaining time jitter +Check existence only once for duplicate base directories +Detect invalid file symlinks pointing to directories (Windows) +Disable unsuitable buttons in pop up dialogs when checkbox is set +Copy folder attributes if source is a junction already on Windows XP instead of Vista +Mark failed UTF conversions with replacement character +Do not restore main dialog position outside visible screen area (multi monitor setup) +Support detection of moved files through symlinks +Reduced memory consumption when detecting moved files +Check for duplicate file ids when detecting renamed files +Redetermine volume id for followed directory symlinks +Removed "Compare_Complete.wav" +Don't accept file deletion confirmation in less than 50ms +Systematically resolved translation bugs +Added Serbian language + + +FreeFileSync 5.15 [2013-05-01] +------------------------------ +New menu option to activate/deactivate automatic update checking +Show status message while checking for program updates +Faster start up times through asynchronous config file checking +Automatically migrate configuration files to new format +New context menu options to copy and paste filter settings +Support file and folder names with trailing space or period characters +Do not show superfluous scroll bars for multiple folder pairs +Correctly show long file paths when moving to recycle bin failed (Windows Vista and later) +Status feedback before blocking while creating a Volume Shadow Copy +Do not show dummy texts while initializing progress dialog (OS X) +Allow to maximize filter dialog +New column for item count on overview panel +Allow CTRL + C to copy selection to clipboard on overview panel +Consider current view filter for file selection on overview panel +Workaround silent failure to set modification times on NTFS volumes (Linux) +Avoid main dialog flash when closing progress dialog (Linux) +Do not show middle grid tool tip when dragging outside visible area +Reduced file accesses when loading XML files +Simplified structure of GlobalSettings.xml +Allow to change default exclusion filter via GlobalSettings.xml: "DefaultExclusionFilter" +Split filter entries over multiple rows in ffs_gui/ffs_batch XML files +Resolved failed assert during start up (ReactOS) +Create directory locks after one-time existence check +Show warning when locking directory failed +Reset main dialog layout to fix top panel default height being too small +New help file topic "Expert Settings" +Updated translation files + + +FreeFileSync 5.14 [2013-03-31] +------------------------------ +Do not process child elements when parent directory creation fails +Start comparison after pressing Enter in directory input fields +Lead grid is determined via keyboard input instead of input focus change +Ignore empty directory entries in RealtimeSync +Restored mouse cursor "snap to default button" +Implemented file icon support for sync preview (OS X) +RealtimeSync exit via menu working again +Restore main dialog even if "close progress dialog" is selected +Show full path when failing to create directory on not existing target drive +Middle grid tool tip shown correctly again (Suse Linux/X11) +Prevent process hang when manually writing to directory history (Linux and OS X, wxWidgets 2.9.4) +Resolved crash after showing help dialog (OS X) +Properly handle non-ASCII characters for external commands (OS X) +Support UTF8 format restrictions on file systems like HFS (OS X) +Do not stretch small thumbnail icons (Linux) +Use 32x32 instead of 48x48 as medium icon size on Windows XP +Properly size non-jumbo icons in thumbnail view (Windows Vista and later) +Reduced GDI resources for file icon buffer (Windows) +Automatically check for updates weekly without showing pop up on first start +Restored program logo in systray progress indicator +Fit grid row label to match wide font sizes +Added macros %csidl_Downloads%, %csidl_PublicDownloads%, %csidl_QuickLaunch% (Windows Vista and later) + + +FreeFileSync 5.13 [2013-03-06] +------------------------------ +Prepared support for new build on Mac OS X +Time out for not existing directories after 10 seconds +Check directory existence in parallel +Inform about all missing directories via a single error message +Show remaining time considering relative error of 10% +Check for grid icon updates only when needed +Revised directory lock process detection +Implemented high resolution icons +Accessibility: fixed unreadable labels +More polished user interfaces +Fixed time stamp not being set on NFS/Samba shares (Linux) + + +FreeFileSync 5.12 [2013-02-04] +------------------------------ +Dynamic statistics adjustment during synchronization +Allow to save active view filter settings as default (context menu) +Stay responsive while checking recycle bin existence on slow disks +Reset option "Delete on both sides" upon each manual deletion +Added context menu to allow deletion of last used configurations +Support numpad add/subtract keys for overview tree +Revised external application integration +Call external applications for multiple selected items +Automatically schedule abandoned recycle bin temp directories (.ffs_tmp) for deletion +Binary comparison speed estimate considers errors and short-circuit evaluation +Use full time window of sync phase when calculating overall speed +Added Arabic language + + +FreeFileSync 5.11 [2013-01-06] +------------------------------ +New file versioning scheme: move to folder replacing existing files +Fixed high CPU consumption after longer syncs +Improved .ffs_batch configuration file handling +Allow to quick save .ffs_batch files on main dialog and program exit +Convert batch-exclusive settings when opening a .ffs_batch file on main dialog +Redesigned configuration dialog layout +Enhanced all file I/O error messages to show locking processes (Windows Vista and later) +Separator in CSV file now locale dependent +Avoid "Windows Error Code 2" for truly empty directories +Macro %month% resolves to decimal number +New macro %timestamp% +Revised sync progress graph +Fixed progress graph graphics glitch for RTL layout +Allow XML element values to contain non-escaped quotation marks +Updated help file +Updated translation files + + +FreeFileSync 5.10 [2012-12-03] +------------------------------ +Show synchronization log as a grid in results dialog +Improved grid scrolling performance (most noticeable on Linux) +Allow grid selection starting from outside of the grid +RealtimeSync: Support drag & drop on main dialog for *.ffs_real and *.ffs_batch files +Optimized memory consumption when generating log for millions of items +Optimized memory consumption when exporting to CSV file +Have grid row height match window default font size +Catch out of memory when copying huge lists into clipboard +Fixed failure to resume aborted sync after having FFS implicitly create target directory +Fixed horizontal mouse wheel scrolling direction for RTL languages (Hebrew) +RealtimeSync: Fixed drag and drop not working (Linux) +Set maximum size of LastSyncs.log in GlobalSettings.xml element +Show error when trying to copy a named pipe rather than hang (Linux) +Improved copy routine minimizing file accesses (Linux) +Copy file access permissions by default (Linux) +Fixed unexpected "File or Directory not existing" error during file copy (Linux) + + +FreeFileSync 5.9 [2012-11-03] +----------------------------- +Scroll grid under mouse cursor +Move files directly to recycle bin without parent "FFS 2012-05-15 131513" temporary folders +Offer $HOME directory alias in directory drop down list (Linux) +Support for tilde (~) character in input folder paths (Linux) +New environment variables for RealtimeSync: %change_action%, "%change_path% +Use Internet Explorer proxy settings for new version check (Windows) +Show proper error message after failed symlink creation +Start comparison upon double-clicking config list +New batch return code: "Synchronization completed with warnings" +Hide files that won't be copied by default if direction "none" is part of the rule set (e.g. update variant) +Remember save config and folder picker dialog positions separately +New sync completion sound +Fixed sync completion sound not playing (Ubuntu) + + +FreeFileSync 5.8 [2012-10-01] +----------------------------- +New icon theme +Dynamic save button and dialog title show unsaved configuration +Exclude all folders if file size or time span filters are active +Added macros %csidl_Nethood%, %csidl_Programs%, %csidl_Startup% +Fixed crash on failed CRT parameter validation (Windows) +Auto-updater handles moved web address for version check +Fixed configuration conversion error when deleting into versioning folder +Avoid modal error dialogs in batch mode unless error handling is set to "pop up" +Set return codes in batch mode even if modal dialogs are shown +Disabled UAC virtualization for 32-bit user-mode process +Descriptive error message when setting invalid dates on FAT volumes + + +FreeFileSync 5.7 [2012-09-04] +----------------------------- +Modern directory selection dialog (Windows Vista and later) +New file versioning scheme appending revision number to files +New sync option to limit number of versions per file +Revised configuration format for *.ffs_gui/*.ffs_batch files: old format will be supported for some time +Fixed crash on invalid file modification times +Fixed zlib error on empty database stream +GlobalSettings.xml: added "MaxSize" parameter to "ConfigHistory" +Fixed occasional crash on GTK 2 (Linux) +Always show "items processed" in log file +Simplified configuration dialogs +Fixed password prompt not always coming up when connecting to a network share +Support environment variables everywhere: +on completion; +external applications; +RTS command +Harmonized external application macros: %item_path%, %item_folder%, %item2_path%, %item2_folder% +Updated translation files + + +FreeFileSync 5.6 [2012-08-02] +----------------------------- +Resize left and right grids equally +Allow to move middle grid position via mouse +Automatically resize file name columns +Do not follow reparse points other than symlinks and mount points +Warn if Recycle Bin is not available during manual deletion +Fixed error when saving log file into volume root directory +Show files which differ in attributes only in the same category as "equal" files +Apply hidden attribute to lock file +Fixed potential "access denied" problem when updating the database file +Show errors when saving configuration files during exit (ignore for batch mode) +Mark begin of comparison phase in the log file +More detailed tool tip describing items that differ in attributes only +Added Scottish Gaelic translation + + +FreeFileSync 5.5 [2012-07-01] +----------------------------- +New database format for variant: old database files are converted automatically +Tuned performance for variant when saving database for millions of files: > 95% faster +Support partial database updates for variant respecting current filter +Reduced size of database files by 30% +Fine-tuned algorithm to avoid certain conflicts after changing comparison settings +Lower peak memory consumption when reading database participating in multiple sync jobs +Refined symlink categorization and variant handling +Always save log of last syncs to %appdata%\FreeFileSynce\LastSyncs.log (128 kB limit) +"Save" and "Save As" menu options +Properly show status message after save configuration +Avoid issues applying file modification time on certain NAS +Refined last-used configuration handling +Avoid race-condition: database file is only read if directory is existing +Protect against temporary network drop between comparison and synchronization +Rearranged statistics panel to save vertical space when vertically aligned +Removed limitation for number of conflicts shown in the warning message and log +Consider both global and local filter when estimating whether folder could contain matches +Updated translation files + + +FreeFileSync 5.4 [2012-06-01] +----------------------------- +Copy all NTFS extended attributes +Improved statistics panel +Improved main grid +Support context menu for files in overview tree +Process double-clicks outside main grid +Allow quoted paths ending with backslash in command line: "C:\" +Fully localized number formatting (Windows) +Fixed deletion dialog header being trimmed (Linux) +Fixed exclusion via context menu (Linux) +Preserve row label width after comparison (Linux) +Updated help file +New batch mode return codes, see help file +Prefix custom deletion directory with job name +Use the same time stamp for log file and versioning +Handle folder drag and drop outside main grid +Avoid name clash having multiple folder pairs delete into the same versioning folder +Exit FreeFileSync automatically while upgrading to new version +Accessibility: Support high contrast color schemes +Yet another UI design overhaul +Fixed "access denied" issue on OS X-hosted network shares +Support Citrix folder shares +Support Arch Linux (Chakra) +Updated translation files + + +FreeFileSync 5.3 [2012-05-02] +----------------------------- +Show which processes lock a file during synchronization (Windows Vista and later) +Use unbuffered copy to speed up copying large files (Windows Vista and later) +Preserve NTFS sparse files +Support referencing all logical volumes by name (including FreeOTFE virtual drives) +Fixed lag showing "Searching for directory" on comparison +New context menu filter option: exclude by short name +Use clicked-on row rather than anchor when determining action for shift-selection +Refresh grid after pressing "CTRL + A" +Add base folder pairs to CSV export +Show full path in tool tip if multiple folder pairs are used +Show child dialogs on same monitor as parent dialog on multiple monitor systems +Added statistics at beginning of batch log file +Fixed batch mode final speed statistic and reset graph after binary comparison +RealtimeSync: Automatically retry after 15 seconds if an error occurs +Show button images untrimmed (Linux) +Fixed problems with auto-closing progress dialog (Linux) +Fixed unresponsive progress dialog and systray icon (Linux) +New option in GlobalSettings.xml: "LockDirectoriesDuringSync" +Added Lithuanian translation +Added Norwegian translation +Updated translation files + + +FreeFileSync 5.2 [2012-04-01] +----------------------------- +Fixed runtime error "Error comparing strings! (LCMapString)" (Windows 2000, XP only) + + +FreeFileSync 5.1 [2012-03-31] +----------------------------- +New category for time span filter: last x days +Fixed "Error loading library function: GetVolumeInformationByHandleW" if NTFS permissions are copied +Fixed command line issues: allow config name without extension, allow multiple directories instead of a config file +Reenabled global shortcut F8 to toggle data shown in middle grid +Unified error handling on failure to create log directory +Do not close batch creation dialog after save +Tree view: compress and filter root nodes the same way as regular folder nodes +Fixed wrong tool tip being shown if directory name changes +Date range selector does not trim year field anymore +Show action "do nothing" on mouse-hover for conflicts in middle grid +Fixed "Windows Error Code 59: An unexpected network error occurred" +New filter pattern: *\* matches all files in sub directories of base directories +Fixed "*?" filter sub-sequence +Fixed "Cannot convert from the charset 'Unknown encoding (-1)'!" +Support Ctrl + A in filter dialog +Support large filter lists > 32 kByte +Allow to hide file icons +Avoid switching monitor when main dialog is maximized on multiple monitor systems +Improved huge XML file loading times by a factor of 3000, saving by a factor of 3 +Restore grid scroll position after repeated comparisons +Show log after sync when non-fatal errors occurred +Fixed crash in UTF8 conversion when processing a corrupted ffs_db file +Even more pedantic user interface fine-tuning +Compiles and runs on openSuse 12.1 +Fixed grid page-up/down keys scrolling twice (Linux, wxGTK 2.9.3) +Fixed unwanted grid scrolling when toggling middle column (Linux, wxGTK 2.9.3) +Fixed middle grid tool tip occasionally going blank (Linux) +Support single shift-click to check/set direction of multiple rows +Removed gtkmm dependency (Linux) +Installer remembers all settings for next installation (local installation only) +All executables digitally signed +Updated translation files + + +FreeFileSync 5.0 [2012-01-30] +----------------------------- +New grid control +New tree control +Revised Right to Left layout for Hebrew +Updated translation files + + +FreeFileSync 4.6 [2011-12-25] +----------------------------- +Execute user-defined command after synchronization +Option to automatically close synchronization progress dialog +Automatically adjust statistics during sync if changes happened after comparison +Fixed "DecodePointer could not be located in KERNEL32.dll" (Windows 2000) +Fixed "Windows Error Code 31: A device attached to the system is not functioning" +Mouse wheel will scroll list of folder pairs instead of toggle through directory history +No error message when scanning a single directory +Minimized disk accesses when deleting files +Less mouse-clicks required when overwriting configuration +Pause timers while showing error messages +Show error message for malformed external commands +Support detection of moved files over "subst" alias +New default font: Segoe UI (Windows Vista and later) +Save settings before forced exit due to shutdown or log off +Updated translation files + + +FreeFileSync 4.5 [2011-11-25] +----------------------------- +Fixed "Windows Error Code 50: The request is not supported" +Fixed "Windows Error Code 124: The system call level is not correct" +Fixed config load performance problem if network drive is not reachable +Support traversing truly empty directories (no ., ..) (Windows) + + +FreeFileSync 4.4 [2011-11-22] +----------------------------- +Fixed error copying files containing alternate data streams (Windows) + + +FreeFileSync 4.3 [2011-11-20] +----------------------------- +Detection of moved and renamed files +New database format for mode: a full sync is suggested before upgrading +Fixed overwrite symlink with regular file +Fixed synchronization result dialog GUI glitch (Windows XP) +Fixed macro %weekday% +RealtimeSync: Fixed support for manual volume unmount (Windows) +Added Croatian language +Updated translation files + + +FreeFileSync 4.2 [2011-11-02] +----------------------------- +Implemented workaround for compiler bug leading to uncaught exceptions (Windows 32 bit) +Shadow Copy Service: Native support for Windows7/Server 2008 +Fixed reference by volume name parsing issue +Rearranged synchronization progress dialog +More concise log message format +Fixed default file icon (Kubuntu) +Support for wxWidgets 2.9 series (Ubuntu/Kubuntu) +FAT 2 sec tolerance for files dated in the future +Honor DACL/SACL inheritance flags when copying NTFS permissions (Windows) +New option in GlobalSettings.xml: "RunWithBackgroundPriority" (Windows Vista and later) + + +FreeFileSync 4.1 [2011-10-09] +----------------------------- +Improved synchronization progress dialog +Show all available aliases in directory history list +Show password prompt when connecting to mapped network share +Removed busy cursor after program start up +RealtimeSync: atomically detect missing directories +Handle not existing reference by volume name as an invalid path +Improved start up responsiveness by checking dir/file existence asynchronously +Fixed loading incorrect directory name when using multiple folder pairs +Allow passing multiple configurations via command line +Allow passing multiple directory names via command line + + +FreeFileSync 4.0 [2011-09-25] +----------------------------- +Thumbnail list view +Option to specify comparison settings at folder pair level +Correctly update parent-child relationship when changing sync directions +Show history list for additional folder pairs +Switch between volume name and full path in directory history list +Perf: shrinked folder matching CPU time by over 70% +Show windows environment strings in directory history list +Show windows special folder IDs in directory history list +Fixed progress dialog going into background on heavy load +Support creating old 8.3 directories +Take over configuration name when creating new batch job +Remember batch-specific settings when loading a ffs_batch file from main dialog +Drag & drop ffs_batch files on main dialog to test and edit batch settings +Automatically resolve objects deleted externally after comparison +Date column context menu: manual time range selector +New categories for time span filter: today, this week, this month, this year +Respect both sides when sorting by relative path +Updated COM error message reporting resolving "Unknown error" +Smarter configuration merge algorithm +Correctly show existing folders on both sides when using include filter +Fixed network access using WebDrive +Update modification times during file copy to write current values to database +RealtimeSync: write name of changed file into environment variable "changed_file" +RealtimeSync: fixed network drop incorrectly being handled as a failure +Set default direction according to current configuration when deleting manually +Plenty of GUI improvements +Updated help file +Updated translation files + + +FreeFileSync 3.21 [2011-08-19] +------------------------------ +Fixed deleting to user-defined directory +Fixed crash when using include filter +New global option to disable transactional file copy + + +FreeFileSync 3.20 [2011-08-11] +------------------------------ +Scan multiple directories in parallel +Automatically resolve disconnected network maps +Fixed temporal hang when dropping large files on main dialog + mode: Fixed issue regarding directory names differing in case during first sync +Delete permanently if recycle bin is not available (Linux) +Keep FreeFileSync responsive when trying to access non-existent network folder +Support for Ubuntu Unity Launcher (Linux) +RealtimeSync: Failure notification if command line is invalid (Linux) + + +FreeFileSync 3.19 [2011-07-23] +------------------------------ +Exclude sub directories from synchronization which cannot be accessed during comparison +Warning if Recycle Bin is not available instead of deleting silently (Windows) +Adapted log message if missing recycler leads to permanent deletion (Windows) +Revert to per file recycle bin handling if creating temp recycler folder fails +Avoid orphaned deletion temp directories on network drives +Quick-select comparison and synchronization options via double-click +New right-click drop down menu on comparison and synchronization settings button +New database design: copying the database file does not lead to complications anymore +Full support for "retry" while comparing +Don't copy empty folders when filtering by time span +Allow loading/merging multiple configurations files via file open dialog +Allow loading/merging multiple configurations in last used config list +Fixed system shutdown interruption during batch mode +Allow saving log files in both silent and non-silent batch jobs +Reduced main dialog flicker when switching configurations +Database and lock files created by FreeFileSync do not trigger RealtimeSync anymore +Restrict maximum number of visible folder pairs to 6 (configurable via GlobalSettings.xml) +New macros: %day%, %hour%, %min%, %sec% + + +FreeFileSync 3.18 [2011-07-03] +------------------------------ +Launcher running synchronously and returning application error code +Fixed sort by file extension +Fixed drag and drop of SAMBA network folder +Render (all) invalid file dates correctly on GUI +Correct layout selection for RTL and LTR languages +Correct GUI status texts while waiting for directory lock +Properly set default directory when loading configuration +New XML framework: zen::Xml +Added Hebrew language +Added Danish language +Updated translation files + + +FreeFileSync 3.17 [2011-05-20] +------------------------------ +Filter files by size +Filter latest files by time span +Launcher automatically selecting 32/64 bit executable on start up +More detailed systray progress indicator +New database format for mode: a full sync is suggested before upgrading +Update database at individual file level (support for partial and aborted syncs) +New translation file format +Dynamically load existing translation files +Correct translation plural forms +Improved directory locking strategy +Restructured installation package +One button-click synchronization +Fixed CSV character encoding +Put CSV values in quotes if they contain semicolons +Explicit button and settings for "Custom" sync variant -> old configurations need to be migrated +Keyboard shortcuts also on middle grid +Minimize progress dialog by clicking on taskbar +Render invalid file dates correctly on GUI +Process user-defined commands via shell execution (FFS and RTS) +Allow base directory names having trailing white-space +Added Ukrainian language +Updated translation files + + +FreeFileSync 3.16 [2011-04-21] +------------------------------ +Fixed file copy issues on SAMBA shares +Small GUI fixes + + +FreeFileSync 3.15 [2011-04-19] +------------------------------ +Overwriting a file as fully transactional operation +Optimized synchronization speed (non-cached volumes, e.g. memory sticks in particular) +Volumes can be specified by name: []\ (use case: variable drive letters, RealtimeSync) +Copy NTFS compressed, encrypted and sparse file attributes +Copy NTFS compressed and encrypted directory attributes +Copy NTFS alternate data stream +Improved performance: CSV export, copy to clipboard, sync log display +Improved color theme support +Fixed crash on certain system text color settings +Fixed progress numbers for manual deletion +Allow aborting manual deletion via escape key +Use relative name for file tool tip +Automatically redirect arrow keys to main grid +More tolerant directory creation (operation not supported/wrong parameter) +More tolerant file move: ignore existing files (user-defined deletion directory) +Added macro %weekday% + + +FreeFileSync 3.14 [2011-03-20] +------------------------------ +New keyboard shortcuts: F5: compare F6: synchronize +Skip to next folder pair if fatal error occurred (instead of abort) +Reload last selected configuration on start up +Abort with error when copying to empty directory field +Full log information after comparison (including file transfer) +Check read access for source file before overwriting target +Fixed possible application crash after comparison +Fixed possible network freeze when comparing +Maximum number of log files can be specified +Don't condense white-space when loading XML configuration +RealtimeSync: Put executable name in quotes when parsing *.ffs_batch file +Large program icons - 256 x 256 +Handle daylight saving time(DST) on FAT network shares +Skip DST handling if drive does not support accurate file times +Many small GUI/usability fixes +Added Korean translation + + +FreeFileSync 3.13 [2011-01-16] +------------------------------ +Implemented Advanced User Interface to allow user specified layout customizations +Process case sensitive file/directory/symlink names +Synchronize name/attributes only avoiding full copy if appropriate +Prevent hibernation/sleep mode during comparison and synchronization (Windows) +New database format: single file for FreeFileSync 32 and 64 bit versions + - full sync suggested before migrating to v3.13 + - old sync.x64.ffs_db files may be deleted +Improved algorithm to calculate remaining time +Allow resizing window containing multiple folder pairs +Show folder short names in column file name +Correctly report message "nothing to sync" in batch mode +Removed libjpg-8 dependency (Linux) +Fixed loading correct maximized position on multi-screen desktop +RealtimeSync: Removed blank icons in ALT-TAB list during execution of command line +Show RealtimeSync job name as systray tool tip +Last used configurations as sorted list without size limitation +Remove redundant configuration when merging multiple ffs_gui/ffs_batch files +Warning if folder is modified that is part of multiple folder pairs +Aggregated warning messages for all folder pairs instead of one per pair +Added privilege to access restricted symlink content +Added Greek translation + + +FreeFileSync 3.12 [2010-11-28] +------------------------------ +Allow empty folder pairs without complaining +Automatically exclude database and lock files from all (sub-)directories (not only from base) +Resize grid columns on both sides in parallel +Fixed tool tip foreground text color (Linux) +Search via CTRL + F and F3 now as global hotkeys +Fully portable use of directory locking (Windows/Linux, 32/64 bit) +RealtimeSync: Treat missing network path the same as missing local path +Show current job name during synchronization (batch/gui) +Allow copying dereferenced (=followed) directory Symlinks over network share +Fail to copy Symlinks (=direct) over network share instead of silently creating empty folder (Windows XP) +Copy NTFS junctions as Symlinks (avoiding permission checks) +RealtimeSync: ignore request for device removal on network mapped drives +Support for copying SELinux security contexts +Fixed moving buttons in synchronization dialog +Allow deleting currently selected item from list of last used folders (not before wxWidgets 2.9.1) +Avoid losing focus after manually deleting a file +Preserve custom changes to sync directions after manually deleting a file +Handle empty tool tips correctly (Linux) +Updated translation files + + +FreeFileSync 3.11 [2010-09-20] +------------------------------ +Fixed migration issue: reasonable default value for number of folder pairs +Better message box background color + + +FreeFileSync 3.10 [2010-09-19] +------------------------------ +Automatically solve daylight saving time and time zone shift issues on FAT/FAT32 (finally) +Instantly resolve abandoned directory locks associated with local computer +Show expanded directory name as tool tip and label text (resolves macros and relative paths) +Do not copy relative file attributes for base target directories that are created implicitly +Move dialogs by clicking (almost) anywhere +RealtimeSync: ignore request for device removal on Samba shares +Added UTF-8 BOM for CSV export +Correctly handle window position on multi-screen desktop +Disabled warning "database not yet existing" +RealtimeSync: replaced delay by minimum idle time +Maximum number of folder pairs configurable via GlobalSettings.xml (XML node ) +Added tool tips to display long filenames on main grid +Keep application responsive when deleting large directories +Vista/Windows 7: harmonize modification times shown on main grid with Windows Explorer +Changed background color to avoid unreadable texts in combination with certain color themes +Toggle middle grid comparison result/sync preview with right mouse button click +Further GUI enhancements/polishment/standard conformance +Updated translation files + + +FreeFileSync 3.9 [2010-08-10] +----------------------------- +Advanced locking strategy to allow multiple processes synchronize the same directories (e.g. via network share) +Merge multiple *.ffs_batch, *.ffs_gui files or combinations of both via drag & drop +Copy file and folder permissions (requires admin rights): + - Windows: owner, group, DACL, SACL + - Linux: owner, group, permissions + - correctly handle Symbolic Links + - new option in global settings +Compare by content evaluates Symbolic Links +32-Bit build compiled with MinGW/GCC to preserve Windows 2000 compatibility +RealtimeSync: Handle requests for device removal (USB stick) while monitoring +Sort by file size: group symlinks before directories +Added macros %week%, %month%, %year% for creating time-stamped directories +Touch database file when changes occurred only +Moved settings "file time tolerance" and "verify copied files" to GlobalSettings.xml +Updated translation files + + +FreeFileSync 3.8 [2010-06-20] +----------------------------- +New options handling Symlinks: ignore/direct/follow => warning: new database format for mode +Fixed crash when starting sync for Windows XP SP2 +Prevent tool tip from stealing focus +Show associated file icons (Linux) +Run folder existence checks in separate thread (faster network share access) +Write mode database file even if both sides are already in sync +Don't raise status dialog to the top after synchronization +Embedded version information into executable (Windows) +Migrated compiler to Visual C++ 2010 (Windows) +Avoid losing manual changes when excluding via context menu +Adjusted auto-updater web-address +Updated translation files + + +FreeFileSync 3.7 [2010-05-16] +----------------------------- +RealtimeSync: Trigger command line only if all directories are existing +Allow for drag and drop of very large files +Batch modus: New "Switch" button opens GUI modus when warnings occur +Support copying old 8.3 filenames correctly +Handling of Symbolic Links configurable via GUI +Fine tuned calculation of remaining disk space for custom deletion directories +Save default config files only if actually changed +NSIS installer: Support for /D and /S switches +Fixed resource loading if installation folder is not working directory (Linux build) +Consolidated batch creation dialog + mode: Detect conflict when a directory shall be deleted while new sub-elements are to be copied +Automatically mark left behind temporary files (*.ffs_tmp) for deletion with next sync +New Project website: http://freefilesync.sourceforge.net +A lot of small GUI fixes +Updated translation files + + +FreeFileSync 3.6 [2010-03-31] +----------------------------- +Fixed occasional crash when starting FreeFileSync + + +FreeFileSync 3.5 [2010-03-27] +----------------------------- +Allow mode syncs between 32 bit, 64 bit, Windows and Linux builds +Show progess indicator in window title +Support for progess indicator in Windows 7 Superbar +Reduced progress indicator flicker +Prevent silent batch mode from taking keyboard focus +Improved error messages (loading/saving/copying files) +Improved environment variable tolerance: strip blanks and double-quotes +RealtimeSync: Fixed crash when double-clicking systray icon +Allow aborting all operations via Escape key +Added British English translation + + +FreeFileSync 3.4 [2010-03-04] +----------------------------- +Performance: Reduced Recycle Bin access time by 90% +Recycle Bin support for Linux +Performance: Reduced binary comparison sequential read time (by up to 75% for CD/DVD access) +Improved synchronization sequence to avoid disk space shortage: overwrite large files by small ones first +Fixed problems with file renaming on Samba share +New free text grid search via shortcuts CTRL + F and F3 +Show number of processed files at end of synchronization +New optional grid column: file extension +New comparison category icons +Fixed handling sync-config of first folder pair +Allow moving main dialog by dragging client area instead of title bar only +Enhanced help file: Run RealtimeSync as Service +Prefix log files with name of batch job +Fixed GUI right-to-left mirroring for locales Hebrew and Arabic +Portable version: save configuration in installation folder +Many small GUI enhancements +Updated translation files +New Linux .deb package: ppa:freefilesync/ffs + + +FreeFileSync 3.3 [2010-02-02] +----------------------------- +New installer package for portable/local/32/64-bit versions +Built-in support for very long filenames: apply \\?\-prefix automatically +New button for synchronization preview: show equal files +RealtimeSync: Respond to directory or volume arrival, e.g. USB stick insert +Start comparison automatically when double-clicking on *.ffs_gui files +Visual progress indicator for sys-tray icon +Fixed string comparison for 'ß' and 'ss' (all Windows versions) +Fixed general string comparison for Windows 2000 +Significantly faster file icon loading +Applied new IFileOperation interface for recycle bin (Windows >= Vista) +Patched mode to handle FAT32 2-second file time precision +Play optional sound after comparison: "Compare_Complete.wav" +Allow environment variables for log file-directory +Enhanced conflict reporting +Added Swedish translation +Updated translation files + + +FreeFileSync 3.2 [2009-12-13] +----------------------------- +Native Windows 64-Bit version (including Volume Shadow Copy Service) +Harmonized filter handling: global and local file filters +Unified handling of first folder pair: all pairs now semantically equal +Use environment variables within directory names (e.g. %USERNAME%) +New keyboard shortcuts to set sync-direction: ALT + +Allow copying to non-encrypted target directory +Fixed sort by filename +Fixed GDI resource leak when scrolling large grids +Fixed string comparison for 'ß' and 'ss' (Windows >= Vista) +Faster file icon loading +Remove elements in folder drop down list via DEL key +New integrated help file +Play optional sound after synchronization: "Sync_Complete.wav" +Several GUI/usability improvements +Created package for PortableApps.com +Added Finnish translation +Updated translation files + + +FreeFileSync 3.1 [2009-10-26] +----------------------------- +Support for multiple data sources in Automatic mode +Copy file and folder create/access/modification times when synchronizing +Progress dialog can be minimized to systray (Batch and GUI mode) +Allow switching between silent/non-silent batch mode interactively +Some GUI improvements + + +FreeFileSync 3.0 [2009-10-15] +----------------------------- +New synchronization mode: +Consolidated batch mode error handling +Fixed crash when comparing multiple pairs by content +Fixed calculation of remaining objects +Fixed swapping grids +Show scanned files when traversing with filter enabled +New default filter values +New macros %time%, %date% for creating time-stamped directories +Avoid corrupted data when program is terminated unexpectedly +Prevent deletion when source-directory (temporarily) is not accessible +Native Unicode support for Linux build +Added Romanian translation +Added Turkish translation +Updated translation files + + +FreeFileSync 2.3 [2009-09-27] +----------------------------- +New filter and sync configuration at folder pair level +Improved sorting: sort across multiple folder pairs + stable sorting in middle grid + consolidated sorting of sync-direction +Open external applications via context menu(customizable) +Removed performance penalty when using include filters +Improved filter syntax for strings beginning with wildcards +Default handling for conflict files now configurable +New option to show all hidden dialogs again +Fixed issue with macros %nameCo, %dirCo +New option in *.ffs_gui/ffs_batch files: Verify copied files +Use Windows Volume Shadow Copy for shared and locked files(new) +More detailed information in *.cvs export +Use current working directory to save global configuration (portable version) +Respect sub directories when manually changing sync-direction +Allow import of batch configuration into GUI mode +Some small GUI improvements +New shortcuts: SPACE: (de-)select rows; ENTER: start external application +Performance improvements: Reduced CPU time by 28%, (peak) memory consumption by 20% +Added Traditional Chinese translation +Updated translation files + + +FreeFileSync 2.2 [2009-08-16] +----------------------------- +New user-defined recycle bin directory +Possibility to create synchronization directories automatically (if not existing) +Support for relative directory names (e.g. \foo, ..\bar) respecting current working directory +New tool tip in middle grid showing detailed information (including conflicts) +Status feedback and new abort button for manual deletion +Options to add/remove folder pairs in batch dialog +Added tool tip showing progress for silent batchmode +New view filter buttons in synchronization preview +Revisioned handling of symbolic links (Linux/Windows) +GUI optimizations removing flicker +Possibility to create new folders via browse folder dialog +Open files with associated application by special command string +Improved warning/error handling +Auto-adjust columns automatically or manually with CTRL + '+' +New macros for double-click command line: %name, %dir, %nameCo, %dirCo +Fixed runtime error when multiple folder pairs are used +New tool 'RealtimeSync': Watch directories for changes and start synchronization automatically +Improved XML parsing, fault tolerance and concept revisioned +More detailed statistics before start of synchronization +Removed superfluous border for bitmap buttons (Linux only) +Added Czech translation +Updated translation files + + +FreeFileSync 2.1 [2009-07-03] +----------------------------- +Fixed bug that could cause FreeFileSync to crash after synchronization +Compiled with MS Visual C++ 2008 using static runtime library + + +FreeFileSync 2.0 [2009-06-30] +----------------------------- +Copy locked files using Windows Volume Shadow Copy +Load file icons asynchronously for maximum display performance +Handle include filter correctly when comparing +Display optional summary window before starting synchronization +Adjust sync direction properly when switching sides +Info about sync variant on main dialog +Issue a warning message for each conflict type when comparing +Save default configuration in user application path (Installer based version) +Limit main dialog minimum size +Update grid row labels while scrolling +Right-click selects cell before opening context menu +New context menu options to manually assign a sync-direction +Moved sync-preview switch into middle grid's context menu +Possibility to remove top folder pair +Fixed calculation of row total in sync preview +File icons configurable for each side +Many small GUI improvements +Compiled successfully with GCC 4.4.0 and MS Visual C++ 2008 +Added Russian translation +Updated translation files + + +FreeFileSync 1.19 [2009-06-01] +------------------------------ +New synchronization preview +Sync-direction can be adapted manually +New category type "conflict" +New check for unresolved conflicts +Improved overall GUI layout +New check for erroneous file modification dates +Optional pop up to notify on changed configuration +Files with invalid dates (e.g. year 30.000) do not result in a program abort anymore +Replaced column "full name" by "full path" to be combined with "filename" +Apply filtering WHILE comparing (if activated) and avoid traversing excluded directories +New filter paradigm: use relative instead of absolute names +New option "ignore DST +/- 1-hour" to correctly handle daylight saving changes +Sync preview statistics now on main dialog +Show only relevant synchronization options +File icon display configurable via grid column context menu +Updated translation files + + +FreeFileSync 1.18 [2009-05-10] +------------------------------ +Linux build officially released: all major problems solved! +New statistic: remaining time +New statistic: bytes per second +Automatically check for program updates every week +Finally got rid of scroll bar in middle grid for Linux build +Fixed issue with file icon display +Fixed overlapping grid cells +Alternate log file directory configurable via GUI +Added drag & drop support for batch job assembly +Simplyfied filter usage: - matches "\*" as well as "\" + - only distinct filter entries are considered +Platform dependent linebreaks in configuration *.xml files +"Significant difference check" runs at folder pair level +Sorting runs at folder pair level +New check for sufficient free disk space (considering recycle bin usage) +New optional grid column: directory +New sort by directory name +Reduced memory consumption by 10% +A lot of smaller improvements +Added Brazilian Portuguese translation +Updated translation files + + +FreeFileSync 1.17 [2009-04-05] +------------------------------ +Full support for Windows/Linux symbolic links: + - traverse, copy, delete symbolic links + - handle broken symbolic links + - new options in GlobalSettings.xml: TraverseDirectorySymlinks, CopyFileSymlinks +New menu option: "Check for new version" +Copy folder attributes and security settings when implicitly creating folders +Maximum file time difference now fully configurable +New history of last selected folders +Fixed "Year-2038-Problem" for time_t +Upgraded to wxWidgets 2.8.10 +Individual folder pairs can be selected for removal +Performance: Reduced CPU time by 9%, memory consumption by 36% +Support for cancellation when copying and comparing large files +Smooth progress indicators when copying and comparing large files +Support for Shift-PageUp/PageDown +Support for Home/End and Shift-Home/End +Alternative log file directory configurable via *.ffs_batch Xml +Show explorer file icons in grid (windows only) +Fixed compilation issues for Linux build +Fixed grid alignment issue in Linux build +Enhanced error messages for Linux build +Optimized traversing algorithm for Linux build +Fixed graphical misalignment with multiple folder pairs +Added Slovenian translation +Added Hungarian translation +Added Spanish translation +Updated translation files + + +FreeFileSync 1.16 [2009-03-13] +------------------------------ +Support for \\?\ path prefix for unrestricted path length (directory names > 255 characters) (windows only) +Copy files even if target folder does not exist +Fixed occasional error when switching languages +Added sys-tray icon for silent batch mode (pause, abort, about) +Support for numeric del-key +Avoid endless loops with Vista symbolic links (don't traverse into symbolic links - configurable) +New functionality for loading batch files (load button or drag & drop to main/batch window) +New options for batch file error handling: "pop up, ignore errors, exit with returncode < 0" +New option to reset all warning messages +Allow marking both sides of the main grid via CTRL + mouse-click +Allow manual deletion of files on both or one side only (respecting selections on both sides) +Special recycler option for manual deletion +New optional grid column: Full name +Fixed locale related issue when comparing. Big thanks to Persson Henric for providing support! +New check if more than 50% of files will be overwritten/deleted +Save memory by clearing old results before re-comparing +Usability improvements: + - name of config file in window title + - refresh view filters on configuration load + - default to ascending sort when changing column + - maximum length of config file history customizable through xml + - new "load configuration" button + - check/uncheck option for middle grid + - support for CTRL + A (select all) + - enhanced error messages (windows only) +Updated translation files + + +FreeFileSync 1.15 [2009-02-22] +------------------------------ +Fixed performance bottleneck in batch mode (non-silent) +Improved performance of comparison by another 10% +Configure column settings by right-click context menu +Remember column positions on main grid +Hide/Show individual columns +Added "sort by comparison result" +Sort file list by relative name after comparison (GUI mode only) +Removed Windows registry usage for portable version +Restored linebreaks in status texts for better readability +Revised German translation. Thanks to «Latino»! +Created custom button control to finally translate "compare" and "synchronize" +Allow manual setup of file manager integration (Windows and Linux) +Added Step-By-Step guide for manual compilation (Windows and Linux) +Added checkboxes to manually select/deselect rows +New option: Treat files with time deviation of less-equal 1 hour as equal (FAT/FAT32 drives only) +Added Polish translation +Added Portuguese translation +Added Italian translation +Updated translation files + + +FreeFileSync 1.14 [2009-02-01] +------------------------------ +Massive performance improvements: +- comprehensive analysis and optimization of comparison functionality +- new, fast directory traversing algorithm +- improved folder hierarchy compare algorithm +- lazy evaluation of formatted date strings +- new high-performance string class +=> reduction of CPU time by more than 90%! +Folder attributes are copied during synchronization +Sorting now case-insensitive (Windows-only) +Allow column positioning on main grid +Many small fixes +Added Chinese translation +Updated translation files + + +FreeFileSync 1.13 [2009-01-06] +------------------------------ +Automatically detect daylight saving time (DST) change for FAT/FAT32 drives +Added directory dependency check when synchronizing multiple folder pairs +New synchronization option: "update" +Reduced status screen flicker when comparing and synchronizing +Fixed bug when sorting by filename +Further GUI improvements +Updated translation files + + +FreeFileSync 1.12 [2008-12-23] +------------------------------ +Significantly improved speed of all sorting algorithms +Keep sorting sequence when adding or removing rows +'Sort by relative path' secondarily sorts by filename and respects folders +Allow adding multiple files/folders to exclude filter via context menu +Exclude full relative path instead of short filenames via context menu +Fixed possible memory leak when canceling compare +New option to manually adjust file modification times (To be used e.g. for FAT32 volumes on DST switch) +Handling of different types of configuration (GUI, batch, global) +Enhanced exception handling +Multiple GUI improvements +Added Dutch translation +Updated translation files + + +FreeFileSync 1.11 [2008-11-23] +------------------------------ +Support for multiple folder pairs +Optimized performance of multiple pairs to scan each folder just once +Enhanced batch file format +New context menu option to add files, file types or directories to exclude filter +Reworked file filter dialog +Updated translation files + + +FreeFileSync 1.10 [2008-11-09] +------------------------------ +Transformed configuration file format to XML +Exchanged batch files with shell links for full Unicode support (Windows-only) +Improved filter usage: ignore leading/trailing white-space, upper/lower-case (Windows-only) chars +Removed screen-flicker when clicking on compare: +Added elapsed time to compare status +Calculate height of middle grid independently of OS window layout +Multiple GUI improvements +Added Japanese translation +Updated translation files + + +FreeFileSync 1.9 [2008-10-26] +----------------------------- +Fixed wxWidgets multithreading issue that could cause synchronization to hang occasionally +Fixed issue with %1 parameter +Fixed issue with recycle bin usage in unicode mode +Added uninstaller +New installer option to associate *.ffs files with FreeFileSync +Transformed language files to Unicode (UTF-8) +Delete elements in configuration history list via DELETE key + + +FreeFileSync 1.8 [2008-10-19] +----------------------------- +Enhanced status bar information +Enhanced log file information +Enhanced progress information +Added Unicode support +Program now waits until work is completed when abort is triggered during synchronization +Added French translation +Updated German translation + + +FreeFileSync 1.7 [2008-10-12] +----------------------------- +Display only those view filter buttons that are actually needed +Compare by size and date: last write time may differ by up to 2 seconds (NTFS vs FAT32) +Fixed minor issue with trailing path separator when creating batch jobs +Fixed minor issue with window sizes not being remembered in some special situation +Further improved Unicode compliance +Updated German translation + + +FreeFileSync 1.6 [2008-10-05] +----------------------------- +Significantly improved speed of filtering files and view (< 10 ms for > 200.000 rows(!)) +Fixed minor grid mis-alignment under some special conditions +Enhanced status bar with centered texts +Flexible filter options depending on compare variant +Improved synchronization statistics +Fixed issue when trying to delete system folders +Usability improvements +Recycle Bin usage as command line parameter +New menu bar +Program language selectable from menu +UI-option to create sync jobs (batch files) for automated synchronization +Updated German translation + + +FreeFileSync 1.5 [2008-09-21] +----------------------------- +Improved speed of comparison by file content +Simplified and optimized calculation of accumulated file sizes +Added right-click context menu to main dialog +New installer for Windows +Improved usability of filtering and selecting rows +Solved possible issue with different file time precisions in multi-OS environments +Updated German translation + + +FreeFileSync 1.4 [2008-09-14] +----------------------------- +Implemented generic multithreading class to keep "compare by content" and "file synchronization" responsive +Added status bar when comparing files (with additional status information for "compare by content") +Some further speed optimizations +Added option to skip error messages and have them listed after synchronization +Restructured loading of configuration files +The result grid after synchronization now always consists of items that have not been synchronized (even if abort was pressed) +Added "remaining files" as sync-progress information +Updated German translation + + +FreeFileSync 1.3 [2008-09-07] +----------------------------- +Maintain and load different configurations by drag&drop, load-button or command line +New function to delete files (or move them to recycle bin) manually on the UI (without having to re-compare): + Deleting folders results in deletion of all dependent files, subfolders on UI grid (also no re-compare needed) + while catching error situations and allowing to resolve them +Improved manual filtering of rows: If folders are marked all dependent subfolders and files are marked as well +(keeping sort sequence when "hide filtered elements" is marked) +Comprehensive performance optimization of the two features above (manual filtering, deletion) for large grids (> 200,000 rows) +Improved usability: resizable borders, keyboard shortcuts, default buttons, dialog standard focus +Main window will remember restored position even if maximized +Updated sources to become more Linux and Unicode friendly +Updated German translation + + +FreeFileSync 1.2 [2008-08-31] +----------------------------- +New progress indicator and status information when synchronizing: + ->available for command line mode and UI mode: Status update and final error report +New progress information when comparing directories +Multithreading for copying of files to keep program responsive +Optimized all status dialogs and progress indicators for high performance: practically NO performance loss +Possibility to abort all performance critical operations (comparison, synchronization) at any time +New options in case of an error: "Continue, retry, abort" for UI and command line +New command line option "-skiperrors" to continue synchronization despite errors +Enhanced log file (-silent mode) to include all errors during compare and synchronization +Do not synchronize folders that have been deleted externally (but show an error message) +Manually filter out ranges from synchronization instead of just single rows +Some UI improvements +New option to use Recycle Bin when deleting or overwriting files +New synchronization sequence: first delete files, then copy files to avoid disc space shortages +Added different return values when used in command line mode to report success or failure +Updated German translation + + +FreeFileSync 1.1 [2008-08-24] +----------------------------- +Some further speed optimizations (sorting) +Written custom wxGrid class to avoid mapping of data to UI: huge performance increase (especially with formatted grids > 100000 items) +Filter files to include/exclude them from synchronization +Minor UI and text adaptions +Allow direct keyboard input for directory names +Added possibility to continue on error +Added indicator for sort direction +Simplified code concerning loading of UI resources +Prepared code to support unicode in some future version +Updated German translation + + +FreeFileSync 1.0 [2008-08-10] +----------------------------- +Initial release diff --git a/FreeFileSync/Build/Help/FreeFileSync.hhc b/FreeFileSync/Build/Help/FreeFileSync.hhc new file mode 100644 index 00000000..1902cb14 --- /dev/null +++ b/FreeFileSync/Build/Help/FreeFileSync.hhc @@ -0,0 +1,92 @@ + + + + + + + + + + +
      +
    • + + + + +
        +
      • + + + +
      • + + + +
      • + + + +
      • + + + +
      • + + + +
      • + + + +
      • + + + +
      • + + + +
      • + + + +
      • + + + +
      • + + + +
      • + + + +
      • + + + +
      +
    • + + + + +
        +
      • + + + +
      • + + + +
      +
    • + + + + +
    + diff --git a/FreeFileSync/Build/Help/FreeFileSync.hhp b/FreeFileSync/Build/Help/FreeFileSync.hhp new file mode 100644 index 00000000..a9f19ebc --- /dev/null +++ b/FreeFileSync/Build/Help/FreeFileSync.hhp @@ -0,0 +1,31 @@ +[OPTIONS] +Compatibility=1.1 or later +Compiled file=..\FreeFileSync.chm +Contents file=FreeFileSync.hhc +Default topic=html\FreeFileSync.html +Display compile progress=No +Full-text search=Yes +Language=0x409 Englisch (USA) +Title=FreeFileSync - Help + + +[FILES] +html\FreeFileSync.html +html\Versioning.html +html\Command Line.html +html\Expert Settings.html +html\Comparison Settings.html +html\Daylight Saving Time.html +html\Exclude Items.html +html\External Applications.html +html\Macros.html +html\Schedule a Batch Job.html +html\Synchronize with FTP.html +html\Variable Drive Letters.html +html\Volume Shadow Copy.html +html\RealtimeSync.html +html\Run as Service.html +html\Links.html + +[INFOTYPES] + diff --git a/FreeFileSync/Build/Help/html/Command Line.html b/FreeFileSync/Build/Help/html/Command Line.html new file mode 100644 index 00000000..bea7c61a --- /dev/null +++ b/FreeFileSync/Build/Help/html/Command Line.html @@ -0,0 +1,140 @@ + + + + + + + + + +

    Command Line Usage

    + +

    FreeFileSync enables additional synchronization scenarios via a command line interface. +To get a syntax overview, open the console, go to the directory where FreeFileSync is installed and type: +

    + +
    +
    + FreeFileSync -h +
    +
    +
    + +
    +
    + +

    1. Run a FreeFileSync batch job

    + +

    In order to start synchronization in batch mode, supply the path of a ffs_batch configuration file as the first argument for FreeFileSync.exe:

    + +
    +
    + FreeFileSync "D:\Backup Projects.ffs_batch" +
    +
    +
    + +
    + +

    After synchronization one of the following status codes is returned:

    + +
    +
    + Return Codes
    + 0 - Synchronization completed successfully
    + 1 - Synchronization completed with warnings
    + 2 - Synchronization completed with errors
    + 3 - Synchronization was aborted +
    +
    +
    + +
    + +

    +You can evaluate these codes from a script (e.g. a cmd or bat file on Windows) +and check if synchronization completed successfully: +

    + +
    +
    + + "C:\Program Files\FreeFileSync\FreeFileSync.exe" "D:\Backup Projects.ffs_batch"
    + if errorlevel 1 (
    +   ::if return code is 1 or greater, something went wrong, add special treatment here
    +   echo Errors occurred during synchronization...
    +   pause
    + )
    +
    +
    +
    + +
    + +

    Instead of displaying "An error occurred!" you can also send an email notification (using a third party tool).

    + +
    + +
    +
    + Attention
    + Make sure your script is not blocked by a popup dialog. Consider the + following options when setting up a FreeFileSync batch job: +
      + +
      +
    • Disable checkbox Show progress dialog or have On completion automatically close the results dialog after synchronization. + +
    • Set error handling to Stop or Ignore. +
    +
    +
    +
    + +
    +
    + +

    2. Start a FreeFileSync GUI configuration

    + +

    If you pass a ffs_gui file, FreeFileSync will start in GUI mode and immediately start comparison (but only if all directories exist):

    + +
    +
    + FreeFileSync "D:\Manual Backup.ffs_gui" +
    +
    +
    + +
    +
    + +

    3. Customize an existing configuration

    + +

    You can replace the directories of a given ffs_gui or ffs_batch configuration file by using the -leftdir +and -rightdir parameters: +

    + +
    +
    + FreeFileSync "D:\Manual Backup.ffs_gui" -leftdir C:\NewSource -rightdir D:\NewTarget +
    +
    +
    + +
    +
    + +

    4. Merge multiple configurations

    + +

    When more than one configuration file is provided, FreeFileSync will merge +everything into a single configuration with multiple folder pairs and start in GUI mode:

    + +
    +
    + FreeFileSync "D:\Manual Backup.ffs_gui" "D:\Backup Projects.ffs_batch" +
    +
    +
    + + + \ No newline at end of file diff --git a/FreeFileSync/Build/Help/html/Comparison Settings.html b/FreeFileSync/Build/Help/html/Comparison Settings.html new file mode 100644 index 00000000..08d3b44b --- /dev/null +++ b/FreeFileSync/Build/Help/html/Comparison Settings.html @@ -0,0 +1,104 @@ + + + + + + + + + +

    Comparison Settings

    + +

    +
      +

    + +

    Symbolic Link Handling

    + +

    FreeFileSync offers three options to configure handling of symbolic links (also called symlinks or soft links):

    + +
      +
    1. Exclude: + Skip symbolic links while scanning directories.
        + +
    2. Direct: + Evaluate the symbolic link object + directly. Symbolic links will be shown as a separate entity on grid. + Links pointing to directories are not traversed and the link object + is copied directly during synchronization.
        + +
    3. Follow: + Treat symbolic links like the object they are pointing to. Links + pointing to directories are traversed like ordinary directories and + the target of each link is copied during synchronization. +
    + +
    + +
    +
    + Note +
      +
    • Under Windows the symbolic link options apply to symbolic links, volume mount points and NTFS junction points. +
    • Copying symbolic links requires FreeFileSync to be started with administrator rights. +
    +
    +
    +
    + +
    +
    + + +

    I. Compare by File time and size

    + +

    This variant considers two files with the same name as equal when both modification +time and file size match. The following categories are distinguished:

    + +
      +
    1. file exists on one side only +
        +
      1. left only +
      2. right only +
      + +
    2. file exists on both sides +
        +
      1. different date +
          +
        • left newer +
        • right newer +
        +
      2. same date +
          +
        • equal +
        • conflict (same date, different size) +
        +
      +
    + +
    + +

    II. Compare by File content

    + +

    Two files with the same name are marked as equal if and only if they have +the same content. This option is more useful for consistency checks +rather than backup operations since it is naturally slower. The file +modification time is not taken into account at all. +

    + +
      +
    1. file exists on one side only +
        +
      1. left only +
      2. right only +
      +
    2. file exists on both sides +
        +
      1. equal +
      2. different content +
      +
    + + + \ No newline at end of file diff --git a/FreeFileSync/Build/Help/html/Daylight Saving Time.html b/FreeFileSync/Build/Help/html/Daylight Saving Time.html new file mode 100644 index 00000000..8d51498e --- /dev/null +++ b/FreeFileSync/Build/Help/html/Daylight Saving Time.html @@ -0,0 +1,62 @@ + + + + + + + + + +

    Daylight Saving Time (Windows)

    + +

    A common problem synchronization software has to handle are +-1 hour +file time shifts after a Daylight Saving Time (DST) switch has +occurred. This can be observed for example when a FAT-formatted +volume is compared against an NTFS volume as is the case when synchronizing a local disk against a +USB memory stick. Files that previously appeared to be in sync are +now shown with an one hour modification time offset, although they +have not been modified by the user or the operating system.

    + +

    The reason for this behavior lies in the way NTFS and FAT drives +store file times: NTFS stores time in UTC format, while FAT uses +local time.

    + +

    When times of these two different formats are compared, one format +has to be converted into the other first. In either way Windows uses +the current DST status as well as the current time zone for +its calculations. Consequently the result of this comparison is +dependent from current system settings and in particular file times +that used to be the same can show up as different after a DST switch or when the time zone is changed.

    + +

    For a detailed discussion about this issue refer to: +http://www.codeproject.com/KB/datetime/dstbugs.aspx +

    + +
    + +

    Solution:

    + +

    FreeFileSync automatically handles this problem by adding the missing time information. Each file on a +FAT volume automatically gets additional meta data encoded in its +creation date that enables a correct file time calculation. This not +only solves all DST issues but also time shifts that occur due to +travel between different time zones.

    + +
    + +
    +
    + Note +
      +
    • In order for FreeFileSync to start handling DST and timezone differences, an initial full synchronization + is required. Subsequent syncs will then never show a time difference again for unchanged files. + +
    • If a FAT volume is scanned the first time by FreeFileSync this will take longer than usual + since additional meta data is written for each file. +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/FreeFileSync/Build/Help/html/Exclude Items.html b/FreeFileSync/Build/Help/html/Exclude Items.html new file mode 100644 index 00000000..65142cc7 --- /dev/null +++ b/FreeFileSync/Build/Help/html/Exclude Items.html @@ -0,0 +1,136 @@ + + + + + + + + + +

    Exclude Items

    + +

    Files and directories are only considered for synchronization if they pass +all filter rules. They have to match at least one entry in the +include list and none of the entries in the exclude list: +

    + +
    +
    +
      +
    • Each list item must be a file or directory path relative to synchronization base directories. + +
    • Multiple items must be separated by ; or a new line. + +
    • Wild cards * and ? may be used: * means zero or more characters while ? represents exactly one character. +
    +
    +
    +
    + +
    +
    + +

    Example: Exclude items for mirror-sync from C:\Source to D:\Target +

    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Description

    +
    +

    Exclude

    +
    +

    Single file C:\Source\file.txt

    +
    +

    \file.txt

    +
    +

    Single folder C:\Source\SubFolder

    +
    +

    \SubFolder\

    +
    +

    All files (and folders) named thumbs.db +

    +
    +

    *\thumbs.db

    +
    +

    All *.tmp files located in SubFolder only

    +
    +

    \SubFolder\*.tmp

    +
    +

    Files and folders containing temp somewhere in their path

    +
    +

    *temp*

    +
    +

    Multiple entries separated by semicolon

    +
    +

    *.tmp; *.doc; *.bak

    +
    +

    Exclude all files and folders located in subdirectories of base directories

    +
    +

    \*\*

    +
    +

    Exclude only *.txt files located in subdirectories of base directories

    +
    +

    \*\*.txt

    +
    +
    +
    +
    + +
    + +
    +
    + Note +
      +
    • For simple exclusions just right-click and exclude one item or a list + of items directly on main grid via context menu. + +
    • A filter phrase is compared against + both file and directory paths. If you want to consider directories + only, you can give a hint by appending a path separator (\). +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/FreeFileSync/Build/Help/html/Expert Settings.html b/FreeFileSync/Build/Help/html/Expert Settings.html new file mode 100644 index 00000000..17c73533 --- /dev/null +++ b/FreeFileSync/Build/Help/html/Expert Settings.html @@ -0,0 +1,86 @@ + + + + + + + + + +

    Expert Settings

    + +

    FreeFileSync has a number of special purpose settings that can only be accessed +directly via the global configuration file GlobalSettings.xml. +To locate this file enter %appdata%\FreeFileSync in the Windows Explorer address bar or go to the FreeFileSync +installation folder if you are using the portable installation. +Note that this file is read once when FreeFileSync starts and saved when it closes. Therefore do only apply changes while FreeFileSync is not running. +

    + +
    +
    + + <?xml version="1.0" encoding="UTF-8"?>
    + <FreeFileSync XmlType="GLOBAL">
    +     <Shared>
    +         <FileTimeTolerance Seconds="2"/>
    +         <RunWithBackgroundPriority Enabled="false"/>
    +         <LockDirectoriesDuringSync Enabled="true"/>
    +         <VerifyCopiedFiles Enabled="false"/>
    +         <LastSyncsLogSizeMax Bytes="100000"/> +
    +
    +
    +
    + +
    + +

    FileTimeTolerance:
    + +By default file modification times +are allowed to have a 2 second difference while still being +considered equal. This is required by FAT/FAT32 file systems which +store file times with a 2 second precision only.
    This setting +can also be used to simulate a "compare by file size", +ignoring last modification times: If tolerance is set to some high +value, e.g. 2000000000, then changed files will be detected as a +conflict (same date, different file size) and the +synchronization direction for conflicts can be set accordingly. +

    + +

    RunWithBackgroundPriority:
    + +While synchronization is running, other applications which are accessing the same +data locations may experience a noticeable slowdown. Enable this +setting to lower FreeFileSync's resource consumption at the cost of a +significantly slower synchronization speed. +

    + +

    LockDirectoriesDuringSync:
    + +In order to avoid race conditions of multiple FreeFileSync instances +writing to the same folder at the same time, accesses are serialized +by lock files (sync.ffs_lock). +This allows to operate FreeFileSync with an arbitrary number of users +in a network out of the box. +

    + +

    VerifyCopiedFiles:
    + +If active, FreeFileSync will binary-compare source and target files after +copying and report verification errors. Note that this may double +file copy times and is no guarantee that data has not already been +corrupted prior to copying and corruption is not hidden by +deceptively reading valid data from various buffers in the +application and hardware stack.
    +Does +the CopyFile function verify that the data reached its final destination successfully? +

    + +

    LastSyncsLogSizeMax:
    + +The progress logs of the most recent synchronizations (for both GUI and batch jobs) are collected automatically in the file LastSyncs.log. +The maximum size of this log file can be set here. +

    + + + \ No newline at end of file diff --git a/FreeFileSync/Build/Help/html/External Applications.html b/FreeFileSync/Build/Help/html/External Applications.html new file mode 100644 index 00000000..8d88ce1f --- /dev/null +++ b/FreeFileSync/Build/Help/html/External Applications.html @@ -0,0 +1,60 @@ + + + + + + + + + +

    External Applications

    + +

    When you double-click on one of the rows on the main dialog, FreeFileSync opens the operating system's file browser +by default. On Windows it calls explorer /select, "%item_path%", on +Linux xdg-open "%item_folder%" and on OS X open -R "%item_path%". +

    + +

    You can customize this behavior and integrate other external applications into FreeFileSync: +Navigate to Menu → Tools → Global settings: Customize context menu +and add or replace a command. The first entry is executed when double-clicking a row on main grid or pressing ENTER while all other entries can be accessed via the +context menu shown after a right mouse click. The following macros can be used: +

    + +
    +
    + %item_path%     - full file or folder name
    + %item_folder%   - folder part only
    + %item2_path%    - Counterpart of %item_path% on the opposite grid
    + %item2_folder%  - Counterpart of %item_folder% on the opposite grid +
    +
    +
    + +
    + +

    Examples:

    + +
      +
    • Start visual difference tool:
      + "C:\Program Files (x86)\WinMerge\WinMergeU.exe" "%item_path%" "%item2_path%"
       
      + +
    • Show file in Windows Explorer:
      + explorer /select, "%item_path%"
       
      + +
    • Open file in associated application:
      + cmd /c start "" "%item_path%" or simply "%item_path%"
        + +
    • Open console dialog:
      + cmd /k cd /D "%item_folder%" +
    + +
    +
    + Note
    + You need to protect macros with quotation marks if they can resolve to a file path with space characters. +
    +
    +
    + + + \ No newline at end of file diff --git a/FreeFileSync/Build/Help/html/FreeFileSync.html b/FreeFileSync/Build/Help/html/FreeFileSync.html new file mode 100644 index 00000000..964cb7e3 --- /dev/null +++ b/FreeFileSync/Build/Help/html/FreeFileSync.html @@ -0,0 +1,58 @@ + + + + + + + + + +

    +
    +Folder Comparison and Synchronization +

    + +

    Usage:

    + +
      +
    1. Choose left and right directories.
      + +
        + +
    2. Compare them.
      + +
        + +
    3. Select synchronization settings.
      + +
        + +
    4. Press Synchronize to begin synchronization.
      + +
    + +
    + +

    Main Dialog Overview

    + + + +
    + +
      +
    1. Start comparison +
    2. Change comparison settings +
    3. Change synchronization settings +
    4. Start synchronization +
    5. Tree overview panel +
    6. Add additional folder pairs +
    7. Select left and right folders +
    8. Synchronization preview +
    9. Save/load configuration +
    10. Include/exclude specific files +
    11. Select categories to show on grid +
    12. Synchronization statistics +
    + + + \ No newline at end of file diff --git a/FreeFileSync/Build/Help/html/Links.html b/FreeFileSync/Build/Help/html/Links.html new file mode 100644 index 00000000..5f7ca317 --- /dev/null +++ b/FreeFileSync/Build/Help/html/Links.html @@ -0,0 +1,29 @@ + + + + + + + + + +

    FreeFileSync Links

    + +
    +
    + Homepage:
    + http://freefilesync.sf.net/ +

    + + Project on SourceForge: feedback, suggestions and bug-reports
    + http://sourceforge.net/projects/freefilesync/ +

    + + If you like FreeFileSync:
    + Support the project by a donation +
    +
    +
    + + + \ No newline at end of file diff --git a/FreeFileSync/Build/Help/html/Macros.html b/FreeFileSync/Build/Help/html/Macros.html new file mode 100644 index 00000000..b9b803ab --- /dev/null +++ b/FreeFileSync/Build/Help/html/Macros.html @@ -0,0 +1,147 @@ + + + + + + + + + +

    Macros

    + +

    All directory names may contain macros that are expanded during +synchronization. Begin and end of each macro is marked by a % character. Besides special macros +handling time and date, the operating system's environment variables can also be used. +

    + +
    + +

    Internal macros

    + +
    +
    + %date%       + e. g. 2012-12-22        format: [YYYY-MM-DD]
    + + %time%       + e. g. 123044            format: [hhmmss]
    + + %timestamp%  + e. g. 2012-12-22 123044 format: [YYYY-MM-DD hhmmss]

    + + %year%       + e. g. 2012
    + + %month%      + e. g. 12
    + + %day%        + e. g. 22

    + + %hour%       + e. g. 12
    + + %min%        + e. g. 30
    + + %sec%        + e. g. 44

    + + %weekday%    + e. g. Monday (day of the week)
    + + %week%       + e. g. 28     (calendar week) +
    +
    +
    + +
    + +

    Environment variables (Windows)

    + +
    +
    + %AllUsersProfile%   e. g. C:\ProgramData
    + %AppData%           
    e. g. C:\Users\<username>\AppData\Roaming
    + %ComputerName%      
    e. g. Zenju-PC
    + %LocalAppData%      
    e. g. C:\Users\<username>\AppData\Local
    + %ProgramData%       
    e. g. C:\ProgramData
    + %ProgramFiles%      
    e. g. C:\Program Files
    + %ProgramFiles(x86)% 
    e. g. C:\Program Files (x86)
    + %Public%            
    e. g. C:\Users\Public
    + %Temp%              
    e. g. C:\Windows\Temp
    + %UserName%          
    e. g. Zenju
    + %UserProfile%       
    e. g. C:\Users\<username>
    + %WinDir%            
    e. g. C:\Windows +
    +
    +
    + +
    + +

    Special folder locations (Windows)

    + +
    +
    + %csidl_Desktop%     e. g. C:\Users\<username>\Desktop
    + %csidl_Downloads%   
    e. g. C:\Users\<username>\Downloads
    + %csidl_Favorites%   
    e. g. C:\Users\<username>\Favorites
    + %csidl_MyDocuments% 
    e. g. C:\Users\<username>\Documents
    + %csidl_MyMusic%     
    e. g. C:\Users\<username>\Music
    + %csidl_MyPictures%  
    e. g. C:\Users\<username>\Pictures
    + %csidl_MyVideos%    
    e. g. C:\Users\<username>\Videos
    + %csidl_Nethood%     
    e. g. C:\Users\<username>\AppData\Roaming\Microsoft\Windows\Network Shortcuts
    + %csidl_Programs%    
    e. g. C:\Users\<username>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs
    + %csidl_Quicklaunch% 
    e. g. C:\Users\<username>\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch
    + %csidl_Resources%   
    e. g. C:\Windows\Resources
    + %csidl_StartMenu%   
    e. g. C:\Users\<username>\AppData\Roaming\Microsoft\Windows\Start Menu
    + %csidl_Startup%     
    e. g. C:\Users\<username>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\StartUp
    + %csidl_Templates%   
    e. g. C:\Users\<username>\AppData\Roaming\Microsoft\Windows\Templates + +

    + Note: Most macros listed here also have a variant for public folders. + E.g. csidl_MyMusiccsidl_PublicMusic +

    +
    +
    +
    + +
    +
    + +

    +Hint: You can add a great amount of flexibility to a ffs_batch configuration file + by creating new temporary environment variables in a bat or cmd file that are evaluated by FreeFileSync at runtime. +

    + +
    + +

    Example: +The FreeFileSync batch file C:\SyncJob.ffs_batch contains +macro %MyVar% instead of an absolute target folder and is invoked by a cmd file: +

    + +
    +
    + set MyVar=C:\Target
    + "C:\Program files\FreeFileSync\FreeFileSync.exe" C:\SyncJob.ffs_batch
    + ::%MyVar% is resolved as C:\Target during synchronization +
    +
    +
    +
    + +
    + +
    +
    + Note
    + Temporary environment variables created with the set command are only valid if the synchronization is started by calling the + FreeFileSync executable directly. Using start /wait creates a new program context without these temporal variables. +
    +
    +
    + + + \ No newline at end of file diff --git a/FreeFileSync/Build/Help/html/RealtimeSync.html b/FreeFileSync/Build/Help/html/RealtimeSync.html new file mode 100644 index 00000000..dfb46b7b --- /dev/null +++ b/FreeFileSync/Build/Help/html/RealtimeSync.html @@ -0,0 +1,125 @@ + + + + + + + + + +

    +
    +Automated Synchronization +

    + +

    The primary purpose of RealtimeSync is to execute a command line each time a directory becomes +available (e. g. insert of a USB-stick) or when it detects changes in one of the monitored directories. Usually this command line will trigger +a FreeFileSync batch job. +

    + +
    + +Example: Real time synchronization - in combination with FreeFileSync
    +

    +Start RealtimeSync.exe located in FreeFileSync's installation directory and +enter all folders you want to monitor. Instead of doing this manually you can import a ffs_batch +file via Menu → Program → Open. This not only extracts all directories relevant for synchronization +but also sets up the command line to execute the ffs_batch file each time changes are detected. +Now press Start to begin monitoring. +

    +
    + +
    + +
    +
    + +
    +
    + Note +
      +
    • The command should not block progress. If you call a FreeFileSync batch job, make + sure it does not show any popup windows. See notes in Command Line Usage. +
        + +
    • RealtimeSync will skip showing the main dialog and begin monitoring immediately if + you pass a ffs_real configuration file or a FreeFileSync ffs_batch file as first + command line argument to RealtimeSync.exe. This helps you integrate RealtimeSync into your operating system's auto start:
      +       "C:\Program Files\FreeFileSync\RealtimeSync.exe" "D:\Backup Projects.ffs_real"
      +       "C:\Program Files\FreeFileSync\RealtimeSync.exe" "D:\Backup Projects.ffs_batch" +
        + +
    • RealtimeSync is not tied to starting FreeFileSync. It can also be used in other scenarios, like sending an email whenever a certain directory is modified. +
    +
    +
    +
    + +
    +
    + +Example: Automatic synchronization when a USB stick is inserted +

    Save a ffs_batch configuration in the USB stick's root directory, +e.g. H:\, to let RealtimeSync call it when the stick is mounted. Configure RealtimeSync as follows:
    +

    +
    + +
    +
    + +

    Whenever directory H:\Data becomes available, RealtimeSync executes the command line which starts the batch job located +on the stick. RealtimeSync will also trigger each time files are modified in H:\Data. +

    + +
    +
    + Note
    + The full path of the last changed file and the action that triggered the + change notification (create, update or delete) are written + to the environment variables %change_path% and %change_action%. +
    +
    +
    + +
    +
    + +

    Example: Log names of changed files and directories (Windows)

    +
    +
    + Show which file or directory has triggered a change. Enter command line:
    +     cmd /c echo %change_action% "%change_path%" & pause +

    + Write a list of all changes to a log file:
    + + +     cmd /c echo %change_action% "%change_path%" >> C:\log.txt + +
    +
    +
    + +
    + +
    +
    + Note
    + When RealtimeSync executes a Windows batch file (bat or cmd) a black console window is shown. You can hide it using the Visual Basic script + HideConsole.vbs located in FreeFileSync's installation directory: +

    + wscript "C:\Program files\FreeFileSync\HideConsole.vbs" C:\MyBatchFile.cmd +
    +
    +
    + +
    +
    + +

    Limitations:

    +
      +
    • If multiple changes happen at the same time, only the name of the first file is written to variable %changed_file%. +
    • While RealtimeSync is executing the command line, monitoring is inactive and changes occurring during this time are lost. +
    + + + \ No newline at end of file diff --git a/FreeFileSync/Build/Help/html/Run as Service.html b/FreeFileSync/Build/Help/html/Run as Service.html new file mode 100644 index 00000000..022e6463 --- /dev/null +++ b/FreeFileSync/Build/Help/html/Run as Service.html @@ -0,0 +1,56 @@ + + + + + + + + + +

    Run as Service (Windows)

    + +

    + RealtimeSync is designed to run as a background process which does not need further + attention. Depending on your requirements there are a number of ways you can start it automatically. + Generally the goal is to execute a command line of the form: +

    + +
    +
    + <FreeFileSync installation folder>\RealtimeSync.exe <path to *.ffs_real or *.ffs_batch file> +
    +
    +
    + +
    +
    + +

    Example:

    + +
    +
    + "C:\Program Files\FreeFileSync\RealtimeSync.exe" "D:\Backup Projects.ffs_real" +
    +
    +
    + +
    + +
      +
    1. RealtimeSync should be monitoring only while a specific user is logged in: Create + a new shortcut, enter the command line from above as target and place it into the user's autostart folder. +

      +

      + +
        + +
    2. RealtimeSync should be monitoring while Windows is running irrespective of + currently logged in users: Create a new task in your operating systems's task scheduler and have it execute the command line above + when the system starts. See Schedule a Batch Job for an example how to add a task. Then change + the user which runs the task to SYSTEM - a special user account always running in the background. +

      + +
    + + + \ No newline at end of file diff --git a/FreeFileSync/Build/Help/html/Schedule a Batch Job.html b/FreeFileSync/Build/Help/html/Schedule a Batch Job.html new file mode 100644 index 00000000..96f67fe3 --- /dev/null +++ b/FreeFileSync/Build/Help/html/Schedule a Batch Job.html @@ -0,0 +1,90 @@ + + + + + + + + + +

    Schedule a Batch Job

    + +
      +
    1. Create a new batch job via FreeFileSync's main dialog: Menu → Program → Save as batch job...
        + +
    2. If the batch job shall run without user interaction or as part of an + unattended batch script, make sure that no popup dialog stops the progress:
      Disable checkbox Show progress dialog to avoid blocking while showing + the result after synchronization. Alternatively you can leave this option checked and select the On completion action Close progress dialog + located in synchronization settings. FreeFileSync will then show a progress dialog but close it automatically when it is finished.
      +
      + Note: Even if the progress dialog is not shown at the beginning, you can make it + visible during synchronization by double-clicking the FreeFileSync notification area icon.
      +
      + +

      + +
    3. In order to prevent error or warning popup messages from stopping progress, set Handle errors to either Ignore or Stop. +
        + +
    4. Setup your operating system's scheduler +
        + +
        +
      1. Windows 7 Task Scheduler: +
          +
        • Go to Start and run taskschd.msc. + +
        • Create a new basic task and follow the wizard. + +
        • Make Program/script point to the location of FreeFileSync.exe and insert the ffs_batch file into Add arguments. + +
        • Use quotation marks to protect spaces in path names, e.g. "D:\Backup Projects.ffs_batch"
          +
          + +
        + +
        + +
        +
        + Note
        + Beginning with Windows Vista the Program/script always needs point to an executable file like FreeFileSync.exe even + if ffs_batch file association is set. If a ffs_batch file is entered instead the task will return with + error code 0xC1, "%1 is not a valid Win32 application". +
        +
        +
        + +
        + +
      2. Windows XP Scheduled Tasks: +
          +
        • Go to Start → Control Panel → Scheduled Tasks and select Add Scheduled Task. + +
        • Follow the wizard and select FreeFileSync.exe as program to run. + +
        • Fill the input field Run: + <FreeFileSync installation folder>\FreeFileSync.exe <job name>.ffs_batch
          +
          +
            +
        + +
      3. Ubuntu Linux Gnome-schedule: +
          +
        • Install Gnome-schedule, if necessary: sudo apt-get install gnome-schedule + +
        • Go to System → Preferences → Scheduled tasks + +
        • Enter the command: + <FreeFileSync installation folder>/FreeFileSync <job name>.ffs_batch
          +
          + +
        + +
      + + +
    + + + \ No newline at end of file diff --git a/FreeFileSync/Build/Help/html/Synchronize with FTP.html b/FreeFileSync/Build/Help/html/Synchronize with FTP.html new file mode 100644 index 00000000..801b5b6f --- /dev/null +++ b/FreeFileSync/Build/Help/html/Synchronize with FTP.html @@ -0,0 +1,56 @@ + + + + + + + + + +

    Synchronize with FTP/WebDAV (Windows)

    + +

    FreeFileSync does not support accessing FTP volumes directly. But this +functionality can be integrated by mapping the FTP web space to a drive letter: +

    + +

    Example: Use the free utility NetDrive (http://www.netdrive.net)

    + +
      +
    • Add a New Site and specify site name, site URL, drive letter, account and password. +
    • Use the newly created drive as if it were a normal hard disk. +
    + +
    +
    + Note
    Most FTP drives set a file's time stamp to the current time when + synchronizing ignoring the source file's time and date. As a workaround you can do a + Compare by File Size. +
    +
    +
    + +
    +
    + +

    Synchronize with SFTP (Linux)

    + +

    An SFTP share can be easily mapped onto a local folder for use with FreeFileSync:

    + +
    +
    +
      +
    • Install:
      + sudo apt-get install sshfs
       
      + +
    • Mount SFTP share:
      + sshfs ssh-account@ssh-server:<path> mountpoint
       
      + +
    • Unmount:
      + fusermount -u mountpoint +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/FreeFileSync/Build/Help/html/Variable Drive Letters.html b/FreeFileSync/Build/Help/html/Variable Drive Letters.html new file mode 100644 index 00000000..a93430aa --- /dev/null +++ b/FreeFileSync/Build/Help/html/Variable Drive Letters.html @@ -0,0 +1,59 @@ + + + + + + + + + +

    Variable Drive Letters

    + +

    USB memory sticks or external hard disks often get different +drive letters assigned when plugged into distinct computers. FreeFileSync +offers two solutions to handle this problem: +

    + +

    Option 1: Specify a folder path by using the volume name:

    + +
    +
    + Use [ZENJU-USB]\folder instead of G:\folder where ZENJU-USB + is the volume name of the USB stick which is currently mounted in drive G:\. +
    +
    +
    + +
    + +
    +
    + Note
    + It is not required to look up and enter the volume name manually. Just select the corresponding entry in the drop down menu.
    + +
    +
    +
    + +
    + +

    Option 2: Use a relative directory name:

    + +
    +
    +
      +
    • Use \folder instead of G:\folder + +
    • Save and copy synchronization settings to the USB stick: G:\Backup.ffs_gui + +
    • Start FreeFileSync by double-clicking on G:\Backup.ffs_gui
      +
    +
    + The working directory is then automatically set to G:\ by the operating system so that the + relative path \folder will be resolved as G:\folder during synchronization. +
    +
    +
    + + + \ No newline at end of file diff --git a/FreeFileSync/Build/Help/html/Versioning.html b/FreeFileSync/Build/Help/html/Versioning.html new file mode 100644 index 00000000..cd1d49db --- /dev/null +++ b/FreeFileSync/Build/Help/html/Versioning.html @@ -0,0 +1,89 @@ + + + + + + + + + +

    File Versioning

    + +

    When you need to preserve files that have been deleted or overwritten it's +often sufficient to select Recycle bin in synchronization +settings. However this is only available for local drives and offers +little control on how to store and how long to keep the files. +FreeFileSync therefore has an additional option, Versioning. +

    + +
    + +

    1. Keep all versions of old files

    + +

    In synchronization settings set deletion handling to Versioning +and naming convention to Time stamp. FreeFileSync will move +deleted files into the provided folder and add a time stamp to each +file name. The structure of the synchronized folders is preserved so +that old versions of a file can be conveniently accessed via a file +browser. +

    + +

    Example: +A file Folder\File.txt was updated three times and old versions were moved to folder C:\Revisions +

    + +
    +
    + + C:\Revisions\Folder\File.txt2012-12-12 111111.txt
    + C:\Revisions\Folder\File.txt2012-12-12 122222.txt
    + C:\Revisions\Folder\File.txt2012-12-12 133333.txt +
    +
    +
    +
    + +
    +
    + +

    2. Save only the most recent version

    + +

    Set deletion handling to Versioning and naming convention to +Replace. Deleted files will be moved to the specified folder +without any decoration and will replace already existing older +versions. +

    + +
    + +

    3. Save versions at certain intervals

    + +

    With naming convention Replace +it is possible to refine the granularity of versions to keep by adding macros +to the versioning folder path. For example you can save deleted files +on a per sync session basis by adding the %timestamp% macro: +

    + +

    Example: Using the dynamically generated folder name C:\Revisions\%timestamp%

    + +
    +
    + + C:\Revisions\2012-12-12 111111\Folder\File.txt
    + C:\Revisions\2012-12-12 122222\Folder\File.txt
    + C:\Revisions\2012-12-12 133333\Folder\File.txt +
    +
    +
    +
    + +
    + +

    This allows for a simple manual undo by moving the deleted files from the +last synchronization session back to their original folders. Other +macros like %date% or %weekday% can be used to reduce the granularity +to days and weeks. +

    + + + \ No newline at end of file diff --git a/FreeFileSync/Build/Help/html/Volume Shadow Copy.html b/FreeFileSync/Build/Help/html/Volume Shadow Copy.html new file mode 100644 index 00000000..4fda439a --- /dev/null +++ b/FreeFileSync/Build/Help/html/Volume Shadow Copy.html @@ -0,0 +1,60 @@ + + + + + + + + + +

    Shadow Copy Service (Windows only)

    + +

    FreeFileSync supports copying locked or shared files by creating a Volume Shadow +Copy of the source drive. This feature can be configured via Menu → Tools → Global settings: Copy locked files. +

    + +
    +
    + Note +
      +
    • The volume snapshot created by the Volume Shadow Copy Service is used when copying locked files only. +
    • Accessing the Volume Shadow Copy Service requires FreeFileSync to be started with administrator rights. +
    +
    +
    +
    + +
    +
    + +

    Troubleshooting

    + +

    If you experience problems using the Volume Shadow Copy Service a renewal of +registration might help. Create and execute a cmd batch file and insert the following lines or enter directly via command line: +

    + +
    +
    + + cd /d %windir%\system32
    + Net stop vss
    + Net stop swprv
    + regsvr32 ole32.dll
    + regsvr32 oleaut32.dll
    + regsvr32 vss_ps.dll
    + Vssvc /register
    + regsvr32 /i swprv.dll
    + regsvr32 /i eventcls.dll
    + regsvr32 es.dll
    + regsvr32 stdprov.dll
    + regsvr32 vssui.dll
    + regsvr32 msxml.dll
    + regsvr32 msxml3.dll
    + regsvr32 msxml4.dll
    +
    +
    +
    +Reference: http://support.microsoft.com/kb/940032 + + + \ No newline at end of file diff --git a/FreeFileSync/Build/Help/html/base.css b/FreeFileSync/Build/Help/html/base.css new file mode 100644 index 00000000..c71a2ddc --- /dev/null +++ b/FreeFileSync/Build/Help/html/base.css @@ -0,0 +1,49 @@ +* +{ +} + +body +{ + font-family: Tahoma, Verdana, sans-serif; +} + +h2 +{ + font-size: 20px; +} + +h3 +{ + font-size: 17px; +} + +.bluebox +{ + margin-left: 1.3cm; + float: left; + width: 80%; + border: 1px solid #000080; + color:black; + background: #ccccff; + padding: 0.2cm; +} + +.bluebox_inner +{ + padding-left: 0.6cm; +} + +.greybox +{ + margin-left: 1.3cm; + float: left; + width: 80%; + color:black; + background: #e6e6e6; + padding: 0.2cm; +} + +.greybox_inner +{ + padding-left: 0.6cm; +} \ No newline at end of file diff --git a/FreeFileSync/Build/Help/img/CmpSettings.png b/FreeFileSync/Build/Help/img/CmpSettings.png new file mode 100644 index 00000000..16c30698 Binary files /dev/null and b/FreeFileSync/Build/Help/img/CmpSettings.png differ diff --git a/FreeFileSync/Build/Help/img/CompareButton.png b/FreeFileSync/Build/Help/img/CompareButton.png new file mode 100644 index 00000000..3fe37be1 Binary files /dev/null and b/FreeFileSync/Build/Help/img/CompareButton.png differ diff --git a/FreeFileSync/Build/Help/img/FFS_logo.png b/FreeFileSync/Build/Help/img/FFS_logo.png new file mode 100644 index 00000000..6a1a53e5 Binary files /dev/null and b/FreeFileSync/Build/Help/img/FFS_logo.png differ diff --git a/FreeFileSync/Build/Help/img/MainDialog.png b/FreeFileSync/Build/Help/img/MainDialog.png new file mode 100644 index 00000000..9f2f2566 Binary files /dev/null and b/FreeFileSync/Build/Help/img/MainDialog.png differ diff --git a/FreeFileSync/Build/Help/img/RTS_logo.png b/FreeFileSync/Build/Help/img/RTS_logo.png new file mode 100644 index 00000000..344da623 Binary files /dev/null and b/FreeFileSync/Build/Help/img/RTS_logo.png differ diff --git a/FreeFileSync/Build/Help/img/RealtimeSync.png b/FreeFileSync/Build/Help/img/RealtimeSync.png new file mode 100644 index 00000000..971baefd Binary files /dev/null and b/FreeFileSync/Build/Help/img/RealtimeSync.png differ diff --git a/FreeFileSync/Build/Help/img/ScheduleBatch.png b/FreeFileSync/Build/Help/img/ScheduleBatch.png new file mode 100644 index 00000000..214a2870 Binary files /dev/null and b/FreeFileSync/Build/Help/img/ScheduleBatch.png differ diff --git a/FreeFileSync/Build/Help/img/SetupBatch.png b/FreeFileSync/Build/Help/img/SetupBatch.png new file mode 100644 index 00000000..e1789917 Binary files /dev/null and b/FreeFileSync/Build/Help/img/SetupBatch.png differ diff --git a/FreeFileSync/Build/Help/img/SourceTarget.png b/FreeFileSync/Build/Help/img/SourceTarget.png new file mode 100644 index 00000000..5d8df5a4 Binary files /dev/null and b/FreeFileSync/Build/Help/img/SourceTarget.png differ diff --git a/FreeFileSync/Build/Help/img/SyncConfigButton.png b/FreeFileSync/Build/Help/img/SyncConfigButton.png new file mode 100644 index 00000000..53e4ecb1 Binary files /dev/null and b/FreeFileSync/Build/Help/img/SyncConfigButton.png differ diff --git a/FreeFileSync/Build/Help/img/SynchronizeButton.png b/FreeFileSync/Build/Help/img/SynchronizeButton.png new file mode 100644 index 00000000..0ab74cb8 Binary files /dev/null and b/FreeFileSync/Build/Help/img/SynchronizeButton.png differ diff --git a/FreeFileSync/Build/Help/img/VolumeName.png b/FreeFileSync/Build/Help/img/VolumeName.png new file mode 100644 index 00000000..da39c9a5 Binary files /dev/null and b/FreeFileSync/Build/Help/img/VolumeName.png differ diff --git a/FreeFileSync/Build/Help/img/WatchUsbInsert.png b/FreeFileSync/Build/Help/img/WatchUsbInsert.png new file mode 100644 index 00000000..0e90278a Binary files /dev/null and b/FreeFileSync/Build/Help/img/WatchUsbInsert.png differ diff --git a/FreeFileSync/Build/Help/img/create_shortcut.png b/FreeFileSync/Build/Help/img/create_shortcut.png new file mode 100644 index 00000000..7b0a5e2c Binary files /dev/null and b/FreeFileSync/Build/Help/img/create_shortcut.png differ diff --git a/FreeFileSync/Build/Help/img/schedule_realtimesync.png b/FreeFileSync/Build/Help/img/schedule_realtimesync.png new file mode 100644 index 00000000..ce52fd28 Binary files /dev/null and b/FreeFileSync/Build/Help/img/schedule_realtimesync.png differ diff --git a/FreeFileSync/Build/Help/img/shortcut_properties.png b/FreeFileSync/Build/Help/img/shortcut_properties.png new file mode 100644 index 00000000..77e9a773 Binary files /dev/null and b/FreeFileSync/Build/Help/img/shortcut_properties.png differ diff --git a/FreeFileSync/Build/Help/img/ubuntuScheduler.png b/FreeFileSync/Build/Help/img/ubuntuScheduler.png new file mode 100644 index 00000000..82bf329f Binary files /dev/null and b/FreeFileSync/Build/Help/img/ubuntuScheduler.png differ diff --git a/FreeFileSync/Build/Help/img/win7scheduler.png b/FreeFileSync/Build/Help/img/win7scheduler.png new file mode 100644 index 00000000..eabf331f Binary files /dev/null and b/FreeFileSync/Build/Help/img/win7scheduler.png differ diff --git a/FreeFileSync/Build/Languages/arabic.lng b/FreeFileSync/Build/Languages/arabic.lng new file mode 100644 index 00000000..4a893332 --- /dev/null +++ b/FreeFileSync/Build/Languages/arabic.lng @@ -0,0 +1,1557 @@ +
    + العربية + MEinea + ar + flag_arabic.png + 6 + n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5 +
    + +Both sides have changed since last synchronization. +كلا الجانبين قد تغير منذ المزامنة الأخيرة. + +Cannot determine sync-direction: +لا يمكن تحديد اتجاه المزامنة: + +No change since last synchronization. +لم يطرأ أي تغيير منذ المزامنة الأخيرة. + +The database entry is not in sync considering current settings. +مدخلات قواعد البيانات غير متزامنة حسب إعدادات المزامنة الحالية. + +Setting default synchronization directions: Old files will be overwritten with newer files. +تحديد الاتجاهات الافتراضية للمزامنة: ستتم الكتابة فوق الملفات القديمة بالملفات الأحدث. + +Checking recycle bin availability for folder %x... +التحقق من توافر سلة المحذوفات من أجل المجلد %x... + +Moving file %x to the recycle bin +نقل الملف %x إلى سلة المهملات + +Moving folder %x to the recycle bin +نقل المجلد %x إلى سلة المهملات + +Moving symbolic link %x to the recycle bin +نقل الارتباط الرمزي %x إلى سلة المهملات + +Deleting file %x +حذف الملف %x + +Deleting folder %x +حذف المجلد %x + +Deleting symbolic link %x +حذف الارتباط الرمزي %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +سلة المهملات غير متوفرة للمجلدات التالية. سيتم حذف الملفات بشكل نهائي بدلاً عن ذلك: + +An exception occurred +حدث استثناء + +A directory path is expected after %x. +مسار متوقع بعد %x. + +Syntax error +خطأ في البنية + +Cannot open file %x. +تعذر فتح الملف %x. + +File %x does not contain a valid configuration. +لا يحتوي الملف %x تكويناً صحيحاً. + +Unequal number of left and right directories specified. +لم يتم تحديد عدد متساوي من المسارات على الطرفين + +The config file must not contain settings at directory pair level when directories are set via command line. +يجب أن يحتوي ملف الخيارات الخيارات على مستوى أزواج المسارات عند تحديد المسارات بواسطة سطر الأوامر + +Directories cannot be set for more than one configuration file. +لا يمكن اختيار المسارات لأكثر من ملف خيارات واحد + +Command line +سطر الأوامر + +Syntax: +بنية: + +config files +ملفات الخيارات + +directory +مسار + +Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. +أي عدد من ملفات خيارات FreeFileSyn أو\و _gui and/or .ffs_batch + +Any number of alternative directories for at most one config file. +أي عدد من المسارات البديلة لملف خيارات وحيد على الأكثر. + +A folder input field is empty. +حقل إدخال خاص بمجلد فارغ. + +The corresponding folder will be considered as empty. +سيتم اعتبار المجلد الموافق كمجلد فارغ + +Cannot find the following folders: +تعذر العثور على المجلدات التالية: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +بإمكانك إهمال هذا الخطأ لاعتبار كل مجلد على أنه مجلد فارغ. سيتم إنشاء المجلدات تلقائياً خلال المزامنة + +The following folders have dependent paths. Be careful when setting up synchronization rules: +للمجلدات التالية مسارات مستقلة عن بعضها. انتبه عند ضبط إعدادات المزامنة: + +File %x has an invalid date. +يحتوي الملف %x تاريخ غير صالح. + +Date: +التاريخ: + +Files %x have the same date but a different size. +الملفات %x لها نفس التاريخ ولكن حجم مختلف. + +Size: +الحجم: + +Items differ in attributes only +العناصر مختلفة في السمات فقط + +Resolving symbolic link %x +جاري حل المسار الرمزي %x + +Comparing content of files %x +مقارنة محتويات الملفات %x + +Generating file list... +إنشاء قائمة الملفات... + +Starting comparison +بدأ عملية المقارنة + +Calculating sync directions... +جاري حساب اتجاهات المزامنة... + +Out of memory. +نفدت الذاكرة. + +Item exists on left side only +العنصر موجود على الجانب الأيمن فقط + +Item exists on right side only +العنصر موجود في الجانب الأيسر فقط + +Left side is newer +الجانب الأيمن أحدث + +Right side is newer +الجانب الأيسر أحدث + +Items have different content +العناصر مختلفة بالمحتوى + +Both sides are equal +كلا الجانبين متماثلان + +Conflict/item cannot be categorized +الاختلاف\العنصر لا يمكن تصنيفه + +Copy new item to left +نسخ عنصر جديد إلى اليمين + +Copy new item to right +نسخ عنصر جديد إلى اليسار + +Delete left item +حذف العنصر الأيمن + +Delete right item +حذف العنصر الأيسر + +Move file on left +نقل ملف على اليمين + +Move file on right +نقل ملف على اليسار + +Overwrite left item +الكتابة فوق العنصر الأيمن + +Overwrite right item +الكتابة فوق العنصر الأيسر + +Do nothing +لا تفعل شيئا + +Update attributes on left +تحديث السمات على اليمين + +Update attributes on right +تحديث السمات على اليسار + +Database file %x is incompatible. +ملف قاعدة البيانات %x غير متوافق. + +Initial synchronization: +المزامنة الأولية: + +Database file %x does not yet exist. +ملف قاعدة البيانات %x غير موجود حتى الآن. + +Database file is corrupt: +ملف قاعدة البيانات معطوب: + +Cannot write file %x. +لا يمكن كتابة الملف %x. + +Cannot read file %x. +لا يمكن قراءة الملف %x. + +Database files do not share a common session. +ملفات قواعد البيانات لا تشترك في جلسة عمل مشتركة. + +Searching for folder %x... +البحث عن المجلد %x... + +Cannot read file attributes of %x. +لا يمكن قراءة سمات الملف %x. + +Cannot get process information. +لا يمكن الحصول على معلومات العملية. + +Waiting while directory is locked (%x)... +في انتظار تأمين قفل للمسار (%x)... + + +1 sec +%x sec + + +0 ثانية +1 ثانية واحدة +2 ثانيتين +%x ثواني +%x ثانية +%x ثانية + + +Creating file %x +إنشاء الملف %x + +Items processed: +معالجة العناصر: + +Items remaining: +العناصر المتبقية: + +Total time: +مجموع الوقت: + + +1 byte +%x bytes + + +0 byte +1 byte +2 bytes +%x bytes +%x bytes +%x bytes + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +حدث خطأ أثناء تحليل الملف %x، الصف %y، و العمود %z. + +Cannot set directory lock for %x. +تعذر إنشاء قفل للمسار %x. + +Scanning: +الفحص: + + +1 thread +%x threads + + +0 بند +1 بند واحد +2 بندان +%x بنود +%x بنداً +%x بند + + +Encoding extended time information: %x +ترميز المعلومات الموسعة للوقت: %x + +/sec +\ثانية + +%x items/sec +%x عنصر\الثانية + +Configuration file %x loaded partially only. +تم تحميل ملف التكوين %x بشكلٍ جزئي فقط. + +Show in Explorer +إظهار في المستكشف + +Open with default application +فتح باستخدام التطبيق الافتراضي + +Browse directory +تصفح المسار + +Cannot access the Volume Shadow Copy Service. +لا يمكن الوصول إلى خدمة النسخ الظلي الوسيط. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +الرجاء استخدام إصدار الـ 64-bit للبرنامج لإنشاء ملفات الظل الاحتياطية على هذا النظام. + +Cannot load file %x. +لا يمكن فتح الملف %x. + +Cannot determine volume name for %x. +تعذر تحديد اسم الوسط %x + +Volume name %x is not part of file path %y. +اسم الوسط %x ليس جزءا من مسار الملق %y. + +Stop requested: Waiting for current operation to finish... +طلب إحباط المهمة: في انتظار انتهاء المهمة الحالية... + +Unable to create timestamp for versioning: +فشل إنشاء طبعة زمنية من أجل عملية الوسم حسب الإصدار: + +Cannot read the following XML elements: +لا يمكن قراءة عناصر XML التالية: + +&Open... +&فتح... + +Save &as... +&حفظ باسم... + +&Quit +&إنهاء + +&Program +&البرنامج + +&View help +&إظهار المساعدة + +&About +&حول + +&Help +&تعليمات + +Usage: +الاستخدام: + +1. Select folders to watch. +1. حدد المجلدات للمتابعة. + +2. Enter a command line. +2. إدخال سطر أوامر. + +3. Press 'Start'. +3. اضغط على 'ابدأ'. + +To get started just import a .ffs_batch file. +للبدء قم باستيراد ملف .ffs_batch. + +Folders to watch: +المجلدات المراقبة + +Add folder +إضافة مجلد + +Remove folder +إزالة مجلد + +Browse +تصفح + +Select a folder +تحديد مجلد + +Idle time (in seconds): +وقت الخمول (بالثانية) + +Idle time between last detected change and execution of command +وقت الخمول بين آخر تغيير تم الكشف عنه وتنفيذ الأوامر + +Command line: +سطر الأوامر: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +يتم تشغيل الأمر إذا: +-حدوث تغيير في الملفات أو المجلدات الفرعية +-ظهور مجلدات جديدة (مثال: إدخال USB stick) + + +&Start +&ابدأ + +About +حول + +Build: %x +بناء: %x + +All files +جميع الملفات + +Automated Synchronization +مزامنة تلقائية + +Directory monitoring active +مراقبة المسارات فعالة + +Waiting until all directories are available... +انتظر حتى توفر جميع المسارات... + +Error +خطأ + +&Restore +&استعادة + +&Show error +&إظهار الخطأ + +&Exit +&خروج + +Incorrect command line: +سطر أوامر خاطئ: + +&Retry +&إعادة المحاولة + +File content +محتوى الملف + +File time and size +تاريخ الملف و حجمه + +Two way +بالاتجاهين + +Mirror +انعكاس + +Update +تحديث + +Custom +مخصص + +Multiple... +متعددة... + +Moving file %x to %y +نقل الملف %x إلى %y + +Moving folder %x to %y +نقل المجلد %x إلى %y + +Moving symbolic link %x to %y +نقل الارتباط الرمزي %x إلى %y + +Removing old versions... +إزالة الإصدارات القديمة... + +Creating symbolic link %x +إنشاء ارتباط رمزي %x + +Creating folder %x +إنشاء مجلد %x + +Overwriting file %x +الكتابة فوق الملف %x + +Overwriting symbolic link %x +الكتابة فوق الارتباط الرمزي %x + +Verifying file %x +التحقق من الملف %x + +Updating attributes of %x +تحديث سمات %x + +Cannot find %x. +تعذر العثور على %x. + +Target folder %x already existing. +المجلد الهدف %x موجود سابقاً. + +Target folder input field must not be empty. +يجب أن لا يكون حقل إدخال المجلد الهدف فارغاً. + +Please enter a target folder for versioning. +الرجاء تحديد مجلد هدف من أجل الوسم حسب الإصدار. + +Source folder %x not found. +لم يتم العثور على المجلد المصدر %x. + +The following items have unresolved conflicts and will not be synchronized: +العناصر التالية لم تحل اختلافاتها، و لن يتم مزامنتها: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +المجلدات التالية مختلفة بشكل كبير. تأكد من تقابل المجلدات بشكل صحيح من أجل المزامنة. + +Not enough free disk space available in: +المساحة الحرة المتوفرة على القرص غير كافية: + +Required: +مطلوب: + +Available: +متاح: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +سيتم تعديل مجلد الذي هو جزء من أزواج المجلدات المتعددة. الرجاء مراجعة إعدادات المزامنة. + +Synchronizing folder pair: +مزامنة زوج مجلدات: + +Generating database... +إنشاء قاعدة بيانات... + +Creating a Volume Shadow Copy for %x... +جاري إنشاء نسخة ظل وسيطة لـ %x... + +Data verification error: %x and %y have different content. +خطأ في التحقق من البيانات: يحتوي %x و %y بيانات مختلفة. + +job name +اسم المهمة + +Synchronization stopped +توقفت عملية المزامنة + +Synchronization completed with errors +انتهاء عملية المزامنة مع وجود أخطء + +Synchronization completed with warnings +انتهاء عملية المزامنة مع وجود تحذيرات + +Nothing to synchronize +لا يوجد شيء للمزامنة + +Synchronization completed successfully +تمت المزامنة بنجاح + +Saving log file %x... +حفظ ملف السجل %x... + +You can switch to FreeFileSync's main window to resolve this issue. +بإمكانك العودة إلى نافذة FreeFileSync الرئيسية لحل هذه المشكلة. + +&Don't show this warning again +&لا تظهر هذا التنبيه مرة ثانية + +&Ignore +&تجاهل + +&Switch +&تبديل + +Switching to FreeFileSync's main window +العودة إلى نافذة FreeFileSync الرئيسية + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + + + +&Ignore subsequent errors +&تجاهلا الأخطاء المماثلة + +Retrying operation... + + +Serious Error +خطأ فادح + +Check for Program Updates +تفقد وجود تحديثات للبرنامج + +A new version of FreeFileSync is available: +يتوفر إصدار جديد من FreeFileSync: + +Download now? +تنزيل الآن؟ + +&Download +&تنزيل + +FreeFileSync is up to date. +البرنامج هو الأحدث حتى الآن. + +Unable to connect to sourceforge.net. +تعذر الاتصال بـ sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +لم نستطع العثور على على رقم إصدار FreeFileSync على الشبكة. هل تريد التحقق يدوياً؟ + +&Check + + +Symlink +ارتباط-رمزي + +Folder +المجلد + +Full path +المسار الكامل + +Name +الاسم + +Relative path +المسار النسبي + +Base folder +المجلد الأساسي (القاعدي) + +Size +الحجم + +Date +تاريخ + +Extension +اللاحقة + +Category +الفئة + +Action +التصرف + +Drag && drop +سحب و إفلات + +Close progress dialog +إنهاء نافذة حوار تقدم العملية + +Standby +وضع الاستعداد + +Log off +تسجيل الخروج + +Shut down +إيقاف التشغيل + +Hibernate +السبات + +Alternate comparison settings +خيارات المفارنة البديلة + +Alternate synchronization settings +خيارات المزامنة البديلة + +Local filter +فلتر محلي + +Active +مفعل + +None +لا شيء + +Remove alternate settings +إزالة الإعدادات البديلة + +Clear filter settings +مسح إعدادات عامل الفلترة + +Copy +نسخ + +Paste +لصق + +Alternate Comparison Settings +خيارات المفارنة البديلة + +Alternate Synchronization Settings +خيارات المزامنة البديلة + +Local Filter +فلتر محلي + +&New +&جديد + +&Save +&حفظ + +Save as &batch job... +&حفظ كمهمة دفعية... + +1. &Compare +1-&مقارنة + +2. &Synchronize +2. &مزامنة + +&Global settings +&الإعدادات العامة + +&Language +&اللغة + +&Find... +&بحث... + +&Export file list... +&تصدير قائمة الملفات... + +&Tools +&أدوات + +&Check now +&تحقق الآن + +Check &automatically once a week +تحقق &أوتوماتيكياً بشكل أسبوعي + +&Check for new version +&التحقق من وجود نسخة جديدة + +Compare +قارن + +Cancel +إلغاء الأمر + +Synchronize +مزامنة + +Add folder pair +إضافة زوج مجلدات + +Remove folder pair +إزالة زوج مجلدات + +Swap sides +مبادلة الجانبين + +Close search bar +إغلاق شريط البحث + +Find: +بحث + +Match case +مطابقة حالة الأحرف (Match case) + +Save as batch job +حفظ كمهمة دفعية + +Hide excluded items +إخفاء العناصر المستبعدة + +Show filtered or temporarily excluded files +إظهار الملفات التي تم فلترتها أو استبعادها بشكل مؤقت + +Number of files and folders that will be created +عدد الملفات و المجلدات التي سيتم إنشاؤها + +Number of files that will be overwritten +عدد الملفات التي سيتم استبدالها + +Number of files and folders that will be deleted +عدد الملفات و المجلدات التي سيتم حذفها + +Total bytes to copy +إجمالي عدد الـ bytes التي سيتم نسخها + +Select a variant: +اختيار بديل: + +Identify equal files by comparing modification time and size. +التعرف على الملفات المتساوية عن طريق مقارنة التاريخ و الحجم + +Identify equal files by comparing the file content. +التعرف على الملفات المتساوية عن طريق مقارنة محتوى الملف + +Symbolic links: +ارتباطات رمزية: + +More information +المزيد من المعلومات + +OK +موافق + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +تحديد التغيرات و مواكبتها على الجانبين. عمليات الحذف, النقل و المشاكل المكتشفة بواسطة قواعد البيانات + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +إنشاء نسخة نظيرة للمجلد على اليسار بحيث يطابق المجلد على اليمين بعد المزامنة + +Copy new and updated files to the right folder. +نسخ الملفات المحدثة إلى المجلد المناسب + +Configure your own synchronization rules. +تحديد قواعد المزامنة الخاصة بك. + +Detect moved files +اكتشاف الملفات المنقولة + +Requires database files. Not supported by all file systems. +يتطلب ملفات قواعد بيانات. غير مدعوم من قبل جميع أنظمة الملفات. + +Delete files: +حذف الملفات: + +Permanent +الدائم + +Delete or overwrite files permanently +حذف أو الكتابة فوق الملفات بشكل دائم + +Recycle bin +سلة المهملات + +Back up deleted and overwritten files in the recycle bin +نسخ احتياطي للملفات المحذوفة و المستبدلة في سلة المهملات + +Versioning +الوسم برقم الإصدار + +Move files to a user-defined folder +نقل الملفات إلى المجلد المحدد من قبل المستخدم + +Naming convention: +اصطلاح التسمية: + +Show examples +إظهار أمثلة + +Handle errors: +التعامل مع الأخطاء: + +Ignore +تجاهل + +Hide all error and warning messages +إخفاء جميع رسائل الأخطاء و التحذير + +Pop-up +إطار منبثق + +Show pop-up on errors or warnings +إظهار إطارات منبثقة عند حصول أخطاء أو تحذيرات + +On completion: +عند الانتهاء: + +Start synchronization now? +بدأ المزامنة الآن؟ + +Variant: +بديل + +Statistics +إحصائيات + +&Don't show this dialog again +&لا تظهر نافذة الحوار هذه مرة ثانية + +Items found: +العناصر التي تم العثور عليها: + +Speed: +السرعة: + +Time remaining: +الوقت المتبقي: + +Time elapsed: +الوقت المنقضي: + +Synchronizing... +مزامنة... + +Minimize to notification area +تصغير إلى منطقة التنبيهات + +Close +إغلاق + +&Pause +&إيقاف مؤقت + +Stop +توقف + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +إنشاء ملف دفعي من أجل عمليات المزامنة غير المحضورة. للبدأ, انقر نقراً مزدوجاً على الملف أو المهمة المجدولة في منظم المهام: %x + +Stop synchronization at first error +إيقاف المزامنة عند أول خطأ + +Show progress dialog +إظهار نافذة حوار تقدم العملية + +Save log: +حفظ السجل + +Limit: +حدود + +Limit maximum number of log files +تقييد الحد الأقصى لعدد ملفات السجل + +How can I schedule a batch job? +كيف يمكنني جدولة مهمة دفعية؟ + +&Recycle bin +&سلة المهملات + +Delete on both sides +حذف على كلا الجانبين + +Delete on both sides even if the file is selected on one side only +حذف الملف في كلا الجانبين حتى إذا كان الملف محدداً على جانب واحد فقط + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +اختيار قوانين فلترة لاستثناء ملفات معينة من المزامنة. أدخل مسارات الملفات منسوبة إلى زوج المجلدات المقابل. + +Include: +إدخال: + +Exclude: +استثناء: + +Time span: +المجال الزمني: + +File size: +حجم الملف: + +Minimum: +أصغري + +Maximum: +أعظمي + +&Clear +&إزالة + +The following settings are used for all synchronization jobs. +هذه الإعدادات مستخدمة لجميع مهمات المزامنة + +Fail-safe file copy +نسخ ملفات آمن من الفشل + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +قم بالنسخ إلى ملف مؤقت (*.ffs_tmp) قبل استبدال الملف الهدف. +هذه العملية تضمن حالة مستقرة حتى في حال حدوث خطأ فادح. + + +(recommended) +(موصى به) + +Copy locked files +نسخ الملفات المقفلة + +Copy shared or locked files using the Volume Shadow Copy Service. +نسخ الملفات المشتركة مع مستخدمين آخرين أو المقفولة باستخدام خدمة نسخ الظل الوسيط + +(requires administrator rights) +(بحاجة امتيازات المدير) + +Copy file access permissions +نسخ أذونات الوصول إلى الملف + +Transfer file and folder permissions. +نقل أذونات الملفات و المجلدات. + +Automatic retry on error: +إعادة المحاولة بشكل تلقائي عند حصول خطأ: + +Retry count: +تعداد محاولات الإعادة: + +Delay (in seconds): +التأخير (بالثواني): + +Customize context menu: +تخصيص القائمة المحلية + +Description +الوصف + +Restore hidden windows +استعادة نوافذ الحوار المخفية + +&Default +&الافتراضي + +Source code written in C++ using: +الرماز المصدري مكتوب بلغة C++ باستخدام: + +If you like FreeFileSync +إذا أعجبك FreeFileSync + +Donate with PayPal +تبرع باستخدام PayPal + +Feedback and suggestions are welcome +التعليقات و الاقتراحات موضع ترحيب + +Homepage +الصفحة الرئيسية + +Email +البريد الإلكتروني + +Published under the GNU General Public License +نشر تحت رخصة GNU General Public License + +Many thanks for localization: +شكرا جزيلا للترجمة: + +Save as Batch Job +حفظ كمهمة دفعية + +Delete Items +حذف العناصر + +Global Settings +الخيارات العامة + +Select Time Span +اختيار المطال الزمني + +Folder Pairs +أزواج المجلدات + +Find +بحث + +Overview +نظرة عامة + +Configuration +التكوين + +Main Bar +الشريط الرئيسي + +Filter Files +فلترة الملفات + +Select View +اخيار طريقة العرض + +Open... +فتح... + +Save +حفظ + +Compare both sides +مقارنة بين كلا الجانبين + +Comparison settings +إعدادات المقارنة + +Synchronization settings +إعدادات المزامنة + +Start synchronization +بدء المزامنة + +Confirm +تأكيد + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + + + +&Execute +&تنفيذ + + +1 directory +%x directories + + +0 مسار +1 مسار واحد +2 مساران +%x مسارات +%x مساراً +%x مسار + + + +1 file +%x files + + +0 ملف +1 ملف واحد +2 ملفان +%x ملفات +%x ملفاً +%x ملف + + + +%y of 1 row in view +%y of %x rows in view + + +الظاهر %y من أصل 0 سطر +الظاهر %y من أصل 1 (سطر وحيد) +الظاهر %y من أصل 2 (سطرين) +الظاهر %y من أصل %x سطور +الظاهر %y من أصل %x سطراً +الظاهر %y من أصل %x سطر + + +Set direction: +تحديد الاتجاه: + +multiple selection +تحديد متعدد + +Include via filter: +تضمن عن طريق فلتر + +Exclude via filter: +استبعاد باستخدام عامل الفلترة: + +Exclude temporarily +استبعاد مؤقتاً + +Include temporarily +شمول مؤقتاً + +Delete +حذف + +Include all +شمول الكل + +Exclude all +استبعاد الكل + +Show icons: +إظهار الأيقونات: + +Small +صغيرة + +Medium +متوسطة + +Large +كبيرة + +Select time span... +حدد المجال الزمني... + +Default view +طريقة العرض الافتراضية + +Show "%x" +إظهار "%x" + +Last session +مصدر الجلسة + +Folder Comparison and Synchronization +مقارنة و مزامنة المجلد + +Configuration saved +تم حفظ التكوين + +FreeFileSync batch +دفعة FreeFileSync + +Do you want to save changes to %x? +هل تريد حفظ التغييرات إلى %x؟ + +Never save &changes +لا تقم بحفظ &التغيرات + +Do&n't save +&لا تحفظ + +Filter +عامل الفلترة + +Show files that exist on left side only +إظهار الملفات الموجودة في الجانب الأيمن فقط + +Show files that exist on right side only +إظهار الملفات الموجودة في الجانب الأيسر فقط + +Show files that are newer on left +إظهار الملفات الأحدث في اليمين + +Show files that are newer on right +إظهار الملفات الأحدث في اليسار + +Show files that are equal +إظهار الملفات المتماثلة على الطرفين + +Show files that are different +إظهار الملفات المختلفة على الطرفين + +Show conflicts +إظهار الاختلافات + +Show files that will be created on the left side +إظهار الملفات التي سيتم إنشاؤها في الجانب الأيمن + +Show files that will be created on the right side +إظهار الملفات التي سيتم إنشاؤها في الجانب الأيسر + +Show files that will be deleted on the left side +إظهار الملفات التي سيتم حذفها من من الجانب الأيمن + +Show files that will be deleted on the right side +إظهار الملفات التي سيتم حذفها من الجانب الأيسر + +Show files that will be overwritten on left side +إظهار الملفات التي سيتم الكتابة فوقها في الجانب الأيمن + +Show files that will be overwritten on right side +إظهار الملفات التي سيتم الكتابة فوقها في الجانب الأيسر + +Show files that won't be copied +إظهار الملفات التي لن يتم نسخها + +Set as default +تحديد كوضع افتراضي + +All folders are in sync +جميع المجلدات متزامنة + +Synchronization Settings +خيارات المزامنة + +Comparison Settings +خيارات المقارنة + +Cannot find %x +لا يمكن العثور على %x + +Comma-separated values +قيم مفصولة بواسطة فواصل + +File list exported +تم تصدير قائمة الملفات + +Searching for program updates... +جاري البحث عن تحديثات للبرنامج... + +Scanning... +جاري الفحص... + +Comparing content... +مقارنة المحتوى... + +Info +معلومات + +Warning +تحذير + +Paused +تم الإيقاف مؤقتاً + +Initializing... +التجهيز للبدأ... + +Stopped +توقف + +Completed +تم الإنتهاء + +&Continue +&متابعة + +Log +السجل + +Today +اليوم + +This week +هذا الأسبوع + +This month +هذا الشهر + +This year +هذه السنة + +Last x days +الأيام x الماضية + +Byte +Byte + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +هل تريد حقاً نقل 0 ملف إلى سلة المهملات ؟ +هل تريد حقاً نقل هذا الملف الوحيد 1 إلى سلة المهملات ؟ +هل تريد حقاً نقل هذين الملفبن 2 إلى سلة المهملات ؟ +هل تريد حقاً نقل %x ملفات إلى سلة المهملات ؟ +هل تريد حقاً نقل %x ملفاً إلى سلة المهملات ؟ +هل تريد حقاً نقل %x ملف إلى سلة المهملات ؟ + + +Move +نقل + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +هل تريد حقاً حذف 0 ملف التالي ؟ +هل تريد حقاً حذف الملف 1 التالي ؟ +هل تريد حقاً حذف الملفين 2 التاليين ؟ +هل تريد حقاً حذف الـ %x ملفات التالية ؟ +هل تريد حقاً حذف الـ %x ملفاً التالية ؟ +هل تريد حقاً حذف الـ %x ملف التالية ؟ + + +Exclude +استبعاد + +Direct +مباشر + +Follow +اتبع + +Copy NTFS permissions +نسخ أذونات NTFS + +Integrate external applications into context menu. The following macros are available: +دمج تطبيقات خارجية في قائمة السياق. تتوفر وحدات الماكرو التالية: + +- full file or folder name +- اسم الملف أو المجلد كاملاً + +- folder part only +- جزء مجلد فقط + +- Other side's counterpart to %item_path% +- النظير للجانب الآخر لـ %item_path% + +- Other side's counterpart to %item_folder% +- االنظير للجانب الآخر لـ %item_folder% + +Restore all hidden windows and warnings? +استعادة جميع نوافذ الحوار و التحذيرات المخفية؟ + +Leave as unresolved conflict +ترك كاختلافات من دون حل + +Replace +استبدال + +Move files and replace if existing +نقل الملفات و استبدال الموجودة بها إن وجدت + +Time stamp +البصمة الزمنية + +Append a timestamp to each file name +إلحاق طابع زمني اسم كل ملف + +File +ملف + +YYYY-MM-DD hhmmss +YYYY-MM-DD hhmmss + +Files +ملفات + +Items +عناصر + +Percentage +النسبة المئوية + +Cannot monitor directory %x. +لا يمكن مراقبة المسار %x. + +Conversion error: +خطأ في تحويل: + +Cannot delete file %x. +لا يمكن حذف الملف %x. + +The file is locked by another process: +الملف مقفول من قبل عملية أخرى: + +Cannot move file %x to %y. +لا يمكن نقل الملف %x إلى %y. + +Cannot delete directory %x. +لا يمكن حذف المسار %x. + +Cannot write file attributes of %x. +لا يمكن كتابة سمات الملف %x. + +Cannot write modification time of %x. +لا يمكن كتابة وقت تعديل %x. + +Cannot read security context of %x. +لا يمكن قراءة سياق الأمان %x. + +Cannot write security context of %x. +لا يمكن كتابة سياق الأمان %x. + +Cannot read permissions of %x. +لا يمكن قراءة أذونات %x. + +Cannot write permissions of %x. +لا يمكن كتابة أذونات %x. + +Cannot create directory %x. +لا يمكن إنشاء المسار %x. + +Cannot create symbolic link %x. +تعذر إنشاء رابط رمزي %x + +Cannot find system function %x. +لا يمكن العثور على وظيفة نظام %x. + +Cannot copy file %x to %y. +لا يمكن نسخ الملف %x إلى %y. + +Type of item %x is not supported: +نوع العنصر %x غير مدعوم: + +Cannot resolve symbolic link %x. +لا يمكن حل الارتباط الرمزي %x. + +Cannot open directory %x. +لا يمكن فتح المسار %x. + +Cannot enumerate directory %x. +لا يمكن تعداد المسار %x. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +0 دقيقة +1 دقيقة واحدة +2 دقيقتان +%x دقائق +%x دقيقة +%x دقيقة + + + +1 hour +%x hours + + +0 ساعة +1 ساعة واحدة +2 ساعتين +%x ساعات +%x ساعة +%x ساعة + + + +1 day +%x days + + +0 يوم +1 يوم واحد +2 يومان +%x أيام +%x يوماً +%x يوم + + +Unable to register to receive system messages. +تعذر التسجيل لاستقبال رسائل النظام. + +Cannot set privilege %x. +لا يمكن تعيين امتيازات %x. + +Unable to suspend system sleep mode. +تعذر تعليق وضع النوم للنظام. + +Cannot change process I/O priorities. +تعذر تغيير أولويات I/O للعملية + +Unable to move %x to the recycle bin. +تعذر نقل %x إلى سلة المهملات. + +Cannot determine final path for %x. +تعذر تحديد المسار النهائي لـ %x. + +Error Code %x: +خطأ رقم %x: + diff --git a/FreeFileSync/Build/Languages/chinese_simple.lng b/FreeFileSync/Build/Languages/chinese_simple.lng new file mode 100644 index 00000000..d204ffd0 --- /dev/null +++ b/FreeFileSync/Build/Languages/chinese_simple.lng @@ -0,0 +1,1499 @@ +
    + 简体中文 + CyberCowBoy + zh_CN + flag_china.png + 1 + 0 +
    + +Both sides have changed since last synchronization. +在最后的同步之后两边均已改变. + +Cannot determine sync-direction: +不能检测同步方向: + +No change since last synchronization. +自从最后一次同步以来没有变动. + +The database entry is not in sync considering current settings. +考虑到当前设置,数据库入口不同步. + +Setting default synchronization directions: Old files will be overwritten with newer files. +设置默认的同步方向:旧文件会被新文件覆盖. + +Checking recycle bin availability for folder %x... +正在为文件夹 %x 检测回收站可用性... + +Moving file %x to the recycle bin +移动文件 %x 到回收站 + +Moving folder %x to the recycle bin +移动文件夹 %x 到回收站 + +Moving symbolic link %x to the recycle bin +移动符号连接 %x 到回收站 + +Deleting file %x +正删除文件 %x + +Deleting folder %x +正删除文件夹 %x + +Deleting symbolic link %x +正在删除符号连接 %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +对于如下的文件夹回收站不可用. 文件将被永久性删除: + +An exception occurred +发生异常 + +A directory path is expected after %x. +在 %x 之后预期是一个目录路径. + +Syntax error +语法错误 + +Cannot open file %x. +无法打开文件 %x. + +File %x does not contain a valid configuration. +文件 %x 并未包含合法的配置. + +Unequal number of left and right directories specified. +左边和右边指定了数量不相等的目录. + +The config file must not contain settings at directory pair level when directories are set via command line. +当目录通过命令行设定时配置文件必须不包含目录对级别的设置. + +Directories cannot be set for more than one configuration file. +无法为多于一个配置文件设置目录. + +Command line +命令行 + +Syntax: +语法: + +config files +配置文件 + +directory +目录 + +Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. +任意数量的 FreeFileSync .ffs_gui 和/或 .ffs_batch 配置文件. + +Any number of alternative directories for at most one config file. +任意数量的替代目录中至多有一个配置文件. + +A folder input field is empty. +有一个文件夹输入框为空. + +The corresponding folder will be considered as empty. +相应的文件夹将被视为空. + +Cannot find the following folders: +无法找到如下文件夹: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +你可忽略此错误而将每个文件夹视为空. 这些文件夹将会在同步过程中被自动创建. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +如下的文件夹有路径依赖性. 请在设置同步规则时特别小心: + +File %x has an invalid date. +文件 %x 的日期非法. + +Date: +日期: + +Files %x have the same date but a different size. +文件 %x 日期相同但大小不同. + +Size: +大小: + +Items differ in attributes only +项目仅是文件属性不同 + +Resolving symbolic link %x +正在解决符号连接 %x + +Comparing content of files %x +正在比较文件 %x 的内容 + +Generating file list... +生成文件列表... + +Starting comparison +正在开始比较 + +Calculating sync directions... +正在计算同步方向... + +Out of memory. +内存不足. + +Item exists on left side only +项目仅存在于左侧 + +Item exists on right side only +项目仅存在于右侧 + +Left side is newer +左侧较新 + +Right side is newer +右侧较新 + +Items have different content +项目有不同的内容 + +Both sides are equal +两侧相等 + +Conflict/item cannot be categorized +冲突/项目未能被分类 + +Copy new item to left +复制新项目到左侧 + +Copy new item to right +复制新项目到右侧 + +Delete left item +删除左侧项目 + +Delete right item +删除右侧项目 + +Move file on left +移动左侧的文件 + +Move file on right +移动右侧的文件 + +Overwrite left item +覆盖左侧的项目 + +Overwrite right item +覆盖右侧的项目 + +Do nothing +保持不动 + +Update attributes on left +更新左侧的文件属性 + +Update attributes on right +更新右侧的文件属性 + +Database file %x is incompatible. +数据库文件 %x 不兼容. + +Initial synchronization: +初始化同步: + +Database file %x does not yet exist. +数据库文件 %x 还未存在. + +Database file is corrupt: +数据库文件已损坏: + +Cannot write file %x. +无法写入文件 %x . + +Cannot read file %x. +无法读取文件 %x . + +Database files do not share a common session. +数据库文件并未共享一个公共会话. + +Searching for folder %x... +正在搜索文件夹 %x... + +Cannot read file attributes of %x. +无法读取 %x 的文件属性. + +Cannot get process information. +无法取得进程信息. + +Waiting while directory is locked (%x)... +由于目录已锁定而正在等待(%x)... + + +1 sec +%x sec + + +%x 秒 + + +Creating file %x +正在创建文件 %x + +Items processed: +已处理的项目: + +Items remaining: +剩余的项目: + +Total time: +总共时间: + + +1 byte +%x bytes + + +%x 字节 + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +当分析文件 %x , 行 %y, 列 %z 时出错. + +Cannot set directory lock for %x. +无法为 %x 设置目录锁定. + +Scanning: +扫描中: + + +1 thread +%x threads + + +%x 线程 + + +Encoding extended time information: %x +正在编码扩展时间信息:%x + +/sec +/秒 + +%x items/sec +%x 个项目/秒 + +Configuration file %x loaded partially only. +配置文件 %x 只是部分载入. + +Show in Explorer +在Explorer中显示 + +Open with default application +用默认应用软件打开 + +Browse directory +浏览目录 + +Cannot access the Volume Shadow Copy Service. +无法访问卷影复制服务. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +请使用 FreeFileSync 64位版本来在这个系统上创建卷影复制. + +Cannot load file %x. +不能载入文件 %x. + +Cannot determine volume name for %x. +无法为 %x 确定卷名. + +Volume name %x is not part of file path %y. +卷名 %x 不是文件路径 %y 的一部分. + +Stop requested: Waiting for current operation to finish... +停止要求: 等待当前操作完成... + +Unable to create timestamp for versioning: +无法为历史版本创建时间戳: + +Cannot read the following XML elements: +无法读取如下XML元素: + +&Open... +打开(&O)... + +Save &as... +另存为(&A)... + +&Quit +退出(&Q) + +&Program +程序(&P) + +&View help +查看帮助(&V) + +&About +关于(&A) + +&Help +帮助(&H) + +Usage: +用法: + +1. Select folders to watch. +1. 选择要监视的文件夹. + +2. Enter a command line. +2. 输入一个命令行. + +3. Press 'Start'. +3. 点击'开始'. + +To get started just import a .ffs_batch file. +要开始只需导入一个 .ffs_batch文件. + +Folders to watch: +要监视的文件夹: + +Add folder +添加文件夹 + +Remove folder +移除文件夹 + +Browse +浏览 + +Select a folder +选择一个文件夹 + +Idle time (in seconds): +空闲时间(秒): + +Idle time between last detected change and execution of command +最后检测到改变和命令执行之间的空闲时间 + +Command line: +命令行: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +命令将被触发如果: +- 文件或子文件夹改变 +- 新文件夹到达(例如U盘插入) + + +&Start +开始(&S) + +About +关于 + +Build: %x +Build: %x + +All files +所有文件 + +Automated Synchronization +自动同步 + +Directory monitoring active +目录监视激活 + +Waiting until all directories are available... +正在等待直到所有目录都可用... + +Error +错误 + +&Restore +恢复(&R) + +&Show error +显示错误(&S) + +&Exit +退出(&E) + +Incorrect command line: +不正确的命令行: + +&Retry +重试(&R) + +File content +文件内容 + +File time and size +文件时间和大小 + +Two way +双向 + +Mirror +镜像 + +Update +更新 + +Custom +自定义 + +Multiple... +并联... + +Moving file %x to %y +正在移动文件 %x 到 %y + +Moving folder %x to %y +正在移动文件夹 %x 到 %y + +Moving symbolic link %x to %y +正在移动符号连接 %x 到 %y + +Removing old versions... +移除旧版本 + +Creating symbolic link %x +正在创建符号连接 %x + +Creating folder %x +正创建文件夹 %x + +Overwriting file %x +正在覆盖文件 %x + +Overwriting symbolic link %x +正在覆盖符号连接 %x + +Verifying file %x +校验文件 %x + +Updating attributes of %x +更新 %x 的属性 + +Cannot find %x. +无法找到 %x. + +Target folder %x already existing. +目标文件夹 %x 已经存在. + +Target folder input field must not be empty. +目标文件夹输入框必须不为空. + +Please enter a target folder for versioning. +请输入一个用于保存历史版本的目标文件夹. + +Source folder %x not found. +无法找到源文件夹 %x. + +The following items have unresolved conflicts and will not be synchronized: +如下项目有无法解决的冲突并将不会被同步: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +如下的文件夹有显著的不同. 请确认你为同步配对了正确的文件夹. + +Not enough free disk space available in: +没有足够的可用磁盘空间用于: + +Required: +必需: + +Available: +可用: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +一个将被修改的文件夹是多个文件夹对的一部分. 请重新检视同步设置. + +Synchronizing folder pair: +正在同步成对的文件夹: + +Generating database... +正在生成数据库... + +Creating a Volume Shadow Copy for %x... +为 %x 创建一个卷影副本... + +Data verification error: %x and %y have different content. +数据校验错误: %x 和 %y 有不同的内容. + +job name +作业名称 + +Synchronization stopped +同步已停止 + +Synchronization completed with errors +同步已完成但有错误 + +Synchronization completed with warnings +同步已完成但有警告 + +Nothing to synchronize +没有什么可同步 + +Synchronization completed successfully +同步成功完成 + +Saving log file %x... +正在保存日志文件 %x... + +You can switch to FreeFileSync's main window to resolve this issue. +你可以切换至 FreeFileSync 的主窗口来解决这个问题. + +&Don't show this warning again +不再显示这个警告(&D) + +&Ignore +忽略(&I) + +&Switch +切换(&S) + +Switching to FreeFileSync's main window +切换至 FreeFileSync 的主窗口 + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + + + +&Ignore subsequent errors +忽略后续的错误(&I) + +Retrying operation... + + +Serious Error +严重错误 + +Check for Program Updates +检查程序的更新 + +A new version of FreeFileSync is available: +已经有新版本的FreeFileSync可用: + +Download now? +立即下载? + +&Download +下载(&D) + +FreeFileSync is up to date. +FreeFileSync 已是最新. + +Unable to connect to sourceforge.net. +无法链接到 Sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +无法在线找到当前FreeFileSync版本号. 你要手动检查吗? + +&Check + + +Symlink +符号连接 + +Folder +文件夹 + +Full path +完整路径 + +Name +文件名 + +Relative path +相对路径 + +Base folder +主文件夹 + +Size +大小 + +Date +日期 + +Extension +扩展名 + +Category +分类 + +Action +动作 + +Drag && drop +拖放 + +Close progress dialog +关闭进度对话框 + +Standby +待机 + +Log off +注销 + +Shut down +关机 + +Hibernate +休眠 + +Alternate comparison settings +备用比较设置 + +Alternate synchronization settings +备用同步设置 + +Local filter +本地过滤器 + +Active +生效 + +None + + +Remove alternate settings +移除替换设置 + +Clear filter settings +清除过滤器设置 + +Copy +复制 + +Paste +粘贴 + +Alternate Comparison Settings +备用比较设置 + +Alternate Synchronization Settings +备用同步设置 + +Local Filter +本地过滤器 + +&New +新建(&N) + +&Save +保存(&S) + +Save as &batch job... +另存为批处理作业(&b) + +1. &Compare +1. 比较(&C) + +2. &Synchronize +2. 同步(&S) + +&Global settings +全局设置(&G) + +&Language +切换语言(&L) + +&Find... +查找...(&F) + +&Export file list... +导出文件列表(&E)... + +&Tools +工具(&T) + +&Check now +现在检查(&C) + +Check &automatically once a week +每周自动检查一次(&A) + +&Check for new version +检查新版本(&C) + +Compare +比较 + +Cancel +取消 + +Synchronize +同步 + +Add folder pair +添加成对文件夹 + +Remove folder pair +移除文件夹对 + +Swap sides +两侧互换 + +Close search bar +关闭搜索条 + +Find: +查找: + +Match case +匹配大小写 + +Save as batch job +另存为批处理作业 + +Hide excluded items +隐藏排除项目 + +Show filtered or temporarily excluded files +显示已被过滤或被临时排除的文件 + +Number of files and folders that will be created +将被创建的文件和文件夹数 + +Number of files that will be overwritten +将被覆盖的文件数 + +Number of files and folders that will be deleted +将被删除的文件和文件夹数 + +Total bytes to copy +要复制的总字节数 + +Select a variant: +选择一个变化: + +Identify equal files by comparing modification time and size. +以比较修改时间和文件大小来识别相同文件. + +Identify equal files by comparing the file content. +以比较文件内容来识别相同的文件. + +Symbolic links: +符号连接: + +More information +更多信息 + +OK +确定 + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +识别和传播两侧的变化. 删除, 移动和冲突会使用一个数据库来自动检测. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +创建左侧文件夹的镜像备份, 在同步之后右侧文件夹完全精确匹配. + +Copy new and updated files to the right folder. +复制新的和已更新的文件到右侧文件夹. + +Configure your own synchronization rules. +配置你自己的同步规则. + +Detect moved files +检测被移动的文件 + +Requires database files. Not supported by all file systems. +需要数据库文件. 不是所有文件系统都支持. + +Delete files: +删除文件: + +Permanent +永久 + +Delete or overwrite files permanently +永久性删除或覆盖文件 + +Recycle bin +回收站 + +Back up deleted and overwritten files in the recycle bin +备份被删除和被覆盖的文件到回收站 + +Versioning +保留历史版本 + +Move files to a user-defined folder +移动文件到一个用户定义的文件夹 + +Naming convention: +命名规则: + +Show examples +显示范例 + +Handle errors: +处理错误: + +Ignore +忽略 + +Hide all error and warning messages +隐藏所有错误与警告信息 + +Pop-up +弹出框 + +Show pop-up on errors or warnings +在错误或警告时显示弹出对话框 + +On completion: +在完成时: + +Start synchronization now? +现在开始同步吗? + +Variant: +变化: + +Statistics +统计 + +&Don't show this dialog again +不要再显示这个对话框(&D) + +Items found: +已找到的项目: + +Speed: +速度: + +Time remaining: +剩余时间: + +Time elapsed: +已用时间: + +Synchronizing... +同步中... + +Minimize to notification area +最小化到托盘 + +Close +关闭 + +&Pause +暂停(&P) + +Stop +停止 + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +创建一个用于无人值守同步的批处理文件. 要开始, 双击这个文件或任务计划器中的计划调度: %x + +Stop synchronization at first error +在遇到第一个错误时停止同步 + +Show progress dialog +显示进度对话框 + +Save log: +保存日志: + +Limit: +限制: + +Limit maximum number of log files +限制日志文件的量大个数 + +How can I schedule a batch job? +我如何计划一个批处理作业? + +&Recycle bin +回收站(&R) + +Delete on both sides +从两侧删除 + +Delete on both sides even if the file is selected on one side only +从两侧删除(即使仅在一侧选择文件) + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +选择过滤器规则以将某些文件从同步中排除. 输入与相关的文件夹对相对的与文件路径. + +Include: +包括: + +Exclude: +排除: + +Time span: +时间跨度: + +File size: +文件大小: + +Minimum: +最小: + +Maximum: +最大: + +&Clear +清除(&C) + +The following settings are used for all synchronization jobs. +如下的设置使用于所有同步作业. + +Fail-safe file copy +无风险的文件复制 + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + +在覆盖目标之前先复制到临时文件(*.ffs_tmp). 这样即使在发生严重错误的情况下也可以保证有一致的状态. + +(recommended) +(推荐) + +Copy locked files +复制被锁定的文件 + +Copy shared or locked files using the Volume Shadow Copy Service. +使用卷影复制服务来复制已共享或已锁定的文件. + +(requires administrator rights) +(需要管理员权限) + +Copy file access permissions +复制文件存取权限 + +Transfer file and folder permissions. +传输文件及文件夹的权限. + +Automatic retry on error: +在出错时自动重试: + +Retry count: +重试计数: + +Delay (in seconds): +延时 (秒): + +Customize context menu: +自定义右键菜单: + +Description +描述 + +Restore hidden windows +恢复隐藏窗口 + +&Default +默认(&D) + +Source code written in C++ using: +源代码用如下C++工具写成: + +If you like FreeFileSync +如果你喜欢 FreeFileSync + +Donate with PayPal +通过PayPal捐赠 + +Feedback and suggestions are welcome +欢迎反馈意见和提出建议 + +Homepage +主页 + +Email +邮箱 + +Published under the GNU General Public License +在GNU通用公共许可下发布 + +Many thanks for localization: +非常感谢以下本地化翻译者: + +Save as Batch Job +另存为批处理作业 + +Delete Items +删除项目 + +Global Settings +全局设置 + +Select Time Span +选择时间跨度 + +Folder Pairs +文件夹对 + +Find +查找 + +Overview +摘要 + +Configuration +配置 + +Main Bar +主工具栏 + +Filter Files +过滤器文件 + +Select View +选择视图 + +Open... +打开... + +Save +保存 + +Compare both sides +比较两侧 + +Comparison settings +比较设置 + +Synchronization settings +同步设置 + +Start synchronization +开始同步 + +Confirm +确认 + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + + + +&Execute +排除(&E) + + +1 directory +%x directories + + +%x 目录 + + + +1 file +%x files + + +%x 文件 + + + +%y of 1 row in view +%y of %x rows in view + + +%x 中的 %y 行在视图中 + + +Set direction: +设置方向: + +multiple selection +多选 + +Include via filter: +通过过滤器包括: + +Exclude via filter: +通过过滤器排除: + +Exclude temporarily +暂时排除 + +Include temporarily +暂时包括 + +Delete +删除 + +Include all +包括所有 + +Exclude all +排除所有 + +Show icons: +显示图标: + +Small + + +Medium + + +Large + + +Select time span... +选择时间跨度... + +Default view +默认视图 + +Show "%x" +显示 "%x" + +Last session +最后会话 + +Folder Comparison and Synchronization +文件夹比较与同步 + +Configuration saved +配置已保存 + +FreeFileSync batch +FreeFileSync批处理文件 + +Do you want to save changes to %x? +是否要保存修改到 %x ? + +Never save &changes +不再保存更改(&C) + +Do&n't save +不保存(&N) + +Filter +过滤器 + +Show files that exist on left side only +显示仅存在左侧的文件 + +Show files that exist on right side only +显示仅存在右侧的文件 + +Show files that are newer on left +显示左侧较新的文件 + +Show files that are newer on right +显示右侧较新的文件 + +Show files that are equal +显示相同的文件 + +Show files that are different +显示不同的文件 + +Show conflicts +显示冲突 + +Show files that will be created on the left side +显示将在左侧被建立的文件 + +Show files that will be created on the right side +显示将在右侧被建立的文件 + +Show files that will be deleted on the left side +显示将在左侧被删除的文件 + +Show files that will be deleted on the right side +显示将在右侧被删除的文件 + +Show files that will be overwritten on left side +显示将在左侧被覆盖的文件 + +Show files that will be overwritten on right side +显示将在右侧被覆盖的文件 + +Show files that won't be copied +显示将不被复制的文件 + +Set as default +设置为默认值 + +All folders are in sync +所有文件夹都是同步的 + +Synchronization Settings +同步设置 + +Comparison Settings +比较设置 + +Cannot find %x +找不到 %x + +Comma-separated values +逗号分割的数值(CSV) + +File list exported +文件清单已经导出 + +Searching for program updates... +正在搜索程序的更新... + +Scanning... +正扫描... + +Comparing content... +正在比较文件内容... + +Info +信息 + +Warning +警告 + +Paused +已暂停 + +Initializing... +正在初始化... + +Stopped +已停止 + +Completed +完成 + +&Continue +继续(&C) + +Log +日志 + +Today +今天 + +This week +本周 + +This month +本月 + +This year +今年 + +Last x days +最后 x 天 + +Byte +字节 + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +你是否要将如下 %x 个项目移动到回收站? + + +Move +移动 + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +是否真的要删除如下 %x 个项目? + + +Exclude +排除 + +Direct +直接 + +Follow +跟随 + +Copy NTFS permissions +复制NTFS权限 + +Integrate external applications into context menu. The following macros are available: +集成外部应用程序到右键菜单. 如下宏可用: + +- full file or folder name +- 完整的文件或文件夹名 + +- folder part only +- 只对文件夹部分 + +- Other side's counterpart to %item_path% +- 另一边的对应项到 %item_path% + +- Other side's counterpart to %item_folder% +- 另一边的对应项到 %item_folder% + +Restore all hidden windows and warnings? +要恢复所有隐藏的窗口和警告吗? + +Leave as unresolved conflict +遗留为未解决的冲突 + +Replace +替换 + +Move files and replace if existing +移动文件, 若文件已存在则替换 + +Time stamp +时间戳 + +Append a timestamp to each file name +附加时间戳到每一个文件名 + +File +文件 + +YYYY-MM-DD hhmmss +YYYY-MM-DD hhmmss + +Files +文件 + +Items +项目 + +Percentage +百分比 + +Cannot monitor directory %x. +无法监视目录 %x. + +Conversion error: +转换错误: + +Cannot delete file %x. +无法删除文件 %x. + +The file is locked by another process: +此文件被另一进程锁定: + +Cannot move file %x to %y. +无法移动文件 %x 到 %y. + +Cannot delete directory %x. +无法删除目录 %x. + +Cannot write file attributes of %x. +无法写入 %x 的文件属性. + +Cannot write modification time of %x. +无法写入 %x 的最后修改时间. + +Cannot read security context of %x. +无法读取 %x 的安全上下文. + +Cannot write security context of %x. +无法写入 %x 的安全上下文. + +Cannot read permissions of %x. +无法读取 %x 的权限. + +Cannot write permissions of %x. +无法写入 %x 的权限. + +Cannot create directory %x. +无法创建目录 %x. + +Cannot create symbolic link %x. +无法创建符号连接 %x. + +Cannot find system function %x. +无法找到系统功能 %x. + +Cannot copy file %x to %y. +无法复制文件 %x 到 %y. + +Type of item %x is not supported: +%x 的类型不被支持: + +Cannot resolve symbolic link %x. +无法解决符号连接 %x. + +Cannot open directory %x. +无法打开目录 %x. + +Cannot enumerate directory %x. +无法列举目录 %x. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +%x 分 + + + +1 hour +%x hours + + +%x 小时 + + + +1 day +%x days + + +%x 天 + + +Unable to register to receive system messages. +无法注册以接收系统信息. + +Cannot set privilege %x. +无法设置 %x 的特权. + +Unable to suspend system sleep mode. +无法路上系统睡眠模式. + +Cannot change process I/O priorities. +无法更改进程的I/O优先级. + +Unable to move %x to the recycle bin. +无法将 %x 移动到回收站. + +Cannot determine final path for %x. +无法确定 %x 的最终路径. + +Error Code %x: +错误代码 %x: + diff --git a/FreeFileSync/Build/Languages/chinese_traditional.lng b/FreeFileSync/Build/Languages/chinese_traditional.lng new file mode 100644 index 00000000..b13ffb1a --- /dev/null +++ b/FreeFileSync/Build/Languages/chinese_traditional.lng @@ -0,0 +1,1504 @@ +
    + 正體中文 + Carlos Chen + Chinese (Traditional) + flag_taiwan.png + 1 + 0 +
    + +Both sides have changed since last synchronization. +自上次同步後,兩邊均已變更過。 + +Cannot determine sync-direction: +無法確定同步方向: + +No change since last synchronization. +自上次同步以來都沒有變更。 + +The database entry is not in sync considering current settings. +資料庫條目在同步時,不會考慮到目前的設定。 + +Setting default synchronization directions: Old files will be overwritten with newer files. +設定預設同步方向:舊檔案會被較新的檔案覆蓋。 + +Checking recycle bin availability for folder %x... +正在檢查資源回收筒的可用性資料夾 %x... + +Moving file %x to the recycle bin +正在移動檔案 %x 到資源回收筒 + +Moving folder %x to the recycle bin +正在移動資料夾 %x 到資源回收筒 + +Moving symbolic link %x to the recycle bin +正在移動符號連結 %x 到資源回收筒 + +Deleting file %x +正在刪除檔案 %x + +Deleting folder %x +正在刪除資料夾 %x + +Deleting symbolic link %x +正在刪除符號連結 %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +資源回收筒不可用於以下資料夾。檔案將被永久刪除替代: + +An exception occurred +發生異常 + +A directory path is expected after %x. +%x 後預期的目錄路徑。 + +Syntax error +語法錯誤 + +Cannot open file %x. +無法開啟檔案 %x。 + +File %x does not contain a valid configuration. +檔案 %x 不包含一個有效的配置。 + +Unequal number of left and right directories specified. +左邊和右邊指定的目錄數不相等。 + +The config file must not contain settings at directory pair level when directories are set via command line. +目錄設定透過命令列時,該設定檔必須不包含配對目錄級別的設定。 + +Directories cannot be set for more than one configuration file. +目錄無法設定多個配置檔。 + +Command line +命令列 + +Syntax: +語法: + +config files +配置檔案 + +directory +目錄 + +Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. +FreeFileSync .ffs_gui和/或.ffs_batch配置檔案的任意數量。 + +Any number of alternative directories for at most one config file. +備用目錄的任意數量中最多有一個配置文件。 + +A folder input field is empty. +資料夾輸入欄位是空的。 + +The corresponding folder will be considered as empty. +對應的資料夾將視為空的。 + +Cannot find the following folders: +找不到下列資料夾: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +您可以忽略每個資料夾視為空的錯誤。資料夾會在同步過程中自動新建。 + +The following folders have dependent paths. Be careful when setting up synchronization rules: +下列資料夾有相依路徑。請小心設定同步規則: + +File %x has an invalid date. +檔案 %x 的日期無效。 + +Date: +日期: + +Files %x have the same date but a different size. +檔案 %x 日期相同但大小不同。 + +Size: +大小: + +Items differ in attributes only +只有項目的屬性不同 + +Resolving symbolic link %x +正在解析符號連結 %x + +Comparing content of files %x +正在比對檔案内容 %x + +Generating file list... +正在產生檔案清單... + +Starting comparison +開始比對 + +Calculating sync directions... +正在計算同步方向... + +Out of memory. +記憶體不足。 + +Item exists on left side only +只存在於左邊的項目 + +Item exists on right side only +只存在於右邊的項目 + +Left side is newer +左邊較新 + +Right side is newer +右邊較新 + +Items have different content +項目具有不同內容 + +Both sides are equal +兩邊都相同 + +Conflict/item cannot be categorized +衝突/項目不能被分類 + +Copy new item to left +複製新的項目到左邊 + +Copy new item to right +複製新的項目到右邊 + +Delete left item +刪除左邊項目 + +Delete right item +刪除右邊項目 + +Move file on left +移動左邊的檔案 + +Move file on right +移動右邊的檔案 + +Overwrite left item +覆蓋左邊項目 + +Overwrite right item +覆蓋右邊項目 + +Do nothing +維持原狀 + +Update attributes on left +更新左邊的屬性 + +Update attributes on right +更新右邊的屬性 + +Database file %x is incompatible. +資料庫檔案 %x 是不相容的。 + +Initial synchronization: +初始化同步: + +Database file %x does not yet exist. +資料庫檔案 %x 並不存在。 + +Database file is corrupt: +資料庫檔案已損毀: + +Cannot write file %x. +無法寫入檔案 %x。 + +Cannot read file %x. +無法讀取檔案 %x。 + +Database files do not share a common session. +資料庫檔案不會共享一個共同的連線。 + +Searching for folder %x... +正在搜尋資料夾 %x... + +Cannot read file attributes of %x. +無法讀取 %x 的檔案屬性。 + +Cannot get process information. +無法得到進程資訊。 + +Waiting while directory is locked (%x)... +等待同時目錄被鎖定(%x)... + + +1 sec +%x sec + + +%x 秒 + + +Creating file %x +正在新建檔案 %x + +Items processed: +已處理項目: + +Items remaining: +剩餘項目: + +Total time: +全部時間: + + +1 byte +%x bytes + + +%x 個位元組 + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +解析 %x 檔案,第 %y 列,第 %z 行出現錯誤。 + +Cannot set directory lock for %x. +無法對 %x 設定目錄鎖。 + +Scanning: +正在掃瞄: + + +1 thread +%x threads + + +%x 個執行緒 + + +Encoding extended time information: %x +編碼延長時間資訊:%x + +/sec +/秒 + +%x items/sec +%x 個項目/秒 + +Configuration file %x loaded partially only. +只載入設定檔 %x 的一部份。 + +Show in Explorer +在資源管理器中顯示 + +Open with default application +使用預設的應用程式開啟 + +Browse directory +瀏覽目錄 + +Cannot access the Volume Shadow Copy Service. +無法讀取卷影複製服務。 + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +請在此系統上使用FreeFileSync 64位元版本來新建卷影副本。 + +Cannot load file %x. +無法載入檔案 %x。 + +Cannot determine volume name for %x. +無法確定卷名為 %x。 + +Volume name %x is not part of file path %y. +卷名 %x 不是檔案路徑 %y 的一部分。 + +Stop requested: Waiting for current operation to finish... +停止要求:等待目前的操作結束... + +Unable to create timestamp for versioning: +無法新建版本控制的時間戳記: + +Cannot read the following XML elements: +無法讀取下列XML元素: + +&Open... +開啟(&O)... + +Save &as... +另存新檔(&A)... + +&Quit +離開(&Q) + +&Program +程式(&P) + +&View help +檢視說明(&V) + +&About +關於(&A) + +&Help +說明(&H) + +Usage: +使用量: + +1. Select folders to watch. +1. 選擇要監看的資料夾。 + +2. Enter a command line. +2. 輸入命令列。 + +3. Press 'Start'. +3. 按下 '開始'。 + +To get started just import a .ffs_batch file. +開始只是導入一個.ffs_batch檔。 + +Folders to watch: +要監看的資料夾: + +Add folder +新增資料夾 + +Remove folder +移除資料夾 + +Browse +瀏覽 + +Select a folder +選擇一個資料夾 + +Idle time (in seconds): +閒置時間(以秒為單位) + +Idle time between last detected change and execution of command +最後檢測到變更和執行命令之間的閒置時間 + +Command line: +命令列: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +假如要觸發該命令: +- 檔案或子資料夾變更 +- 新資料夾到來(例如插入USB隨身碟) + + +&Start +開始(&S) + +About +關於 + +Build: %x +建立:%x + +All files +所有檔案 + +Automated Synchronization +自動化同步 + +Directory monitoring active +目錄監測啟動 + +Waiting until all directories are available... +等到所有目錄可用... + +Error +錯誤 + +&Restore +還原(&R) + +&Show error +顯示錯誤(&S) + +&Exit +結束(&E) + +Incorrect command line: +不正確的命令列: + +&Retry +重試(&R) + +File content +檔案内容 + +File time and size +檔案大小和日期 + +Two way +雙向 + +Mirror +鏡像 + +Update +更新 + +Custom +自訂 + +Multiple... +多個... + +Moving file %x to %y +正在移動檔案 %x 到 %y + +Moving folder %x to %y +正在移動資料夾 %x 到 %y + +Moving symbolic link %x to %y +正在移動符號連結 %x 到 %y + +Removing old versions... +正在刪除舊版本... + +Creating symbolic link %x +正在新建符號連結 %x + +Creating folder %x +正在新建資料夾 %x + +Overwriting file %x +正在覆蓋檔案 %x + +Overwriting symbolic link %x +正在覆蓋符號連結 %x + +Verifying file %x +正在驗證檔案 %x + +Updating attributes of %x +正在更新 %x 個屬性 + +Cannot find %x. +找不到 %x。 + +Target folder %x already existing. +目標資料夾 %x 已存在。 + +Target folder input field must not be empty. +目標資料夾輸入欄位不能為空。 + +Please enter a target folder for versioning. +請輸入版本控制的目的資料夾。 + +Source folder %x not found. +來源資料夾 %x 找不到。 + +The following items have unresolved conflicts and will not be synchronized: +下列項目有未解決的衝突,將不會同步: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +下列資料夾明顯不同。請確定要進行同步的資料夾匹配正確。 + +Not enough free disk space available in: +沒有足夠的可用空間: + +Required: +必要: + +Available: +可用: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +一個將被修改的資料夾,是多個配對資料夾的一部分。請檢查同步設定。 + +Synchronizing folder pair: +同步配對資料夾: + +Generating database... +正在產生資料庫... + +Creating a Volume Shadow Copy for %x... +正在新建卷影複製為 %x... + +Data verification error: %x and %y have different content. +資料驗證錯誤:%x 和 %y 內容不同! + +job name +作業名稱 + +Synchronization stopped +同步已停止。 + +Synchronization completed with errors +同步完成但有錯誤 + +Synchronization completed with warnings +同步已完成,但出現警告 + +Nothing to synchronize +沒有什麼東西可同步 + +Synchronization completed successfully +同步已成功完成 + +Saving log file %x... +正在儲存日誌檔 %x... + +You can switch to FreeFileSync's main window to resolve this issue. +您可以切換到FreeFileSync的主視窗中來解決此問題。 + +&Don't show this warning again +不要再顯示此警告(&D) + +&Ignore +忽略(&I) + +&Switch +切換(&S) + +Switching to FreeFileSync's main window +切換到FreeFileSync的主視窗 + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + +在 %x 秒自動重試... + + +&Ignore subsequent errors +忽略後續的錯誤(&I) + +Retrying operation... + + +Serious Error +嚴重錯誤 + +Check for Program Updates +檢查程式更新 + +A new version of FreeFileSync is available: +FreeFileSync有新版本可用: + +Download now? +要立即下載嗎? + +&Download +下載(&D) + +FreeFileSync is up to date. +FreeFileSync已經是最新版本。 + +Unable to connect to sourceforge.net. +無法連接到sourceforge.net。 + +Cannot find current FreeFileSync version number online. Do you want to check manually? +找不到目前線上FreeFileSync版號!是否要手動檢查? + +&Check + + +Symlink +符號連結 + +Folder +資料夾 + +Full path +完整路徑 + +Name +名稱 + +Relative path +相對路徑 + +Base folder +底層資料夾 + +Size +大小 + +Date +日期 + +Extension +擴展 + +Category +分類 + +Action +動作 + +Drag && drop +拖放 + +Close progress dialog +關閉進度對話框 + +Standby +待機 + +Log off +登出 + +Shut down +關機 + +Hibernate +休眠 + +Alternate comparison settings +備用比對設定 + +Alternate synchronization settings +備用同步設定 + +Local filter +本機篩選器 + +Active +啟用 + +None + + +Remove alternate settings +移除備用設定 + +Clear filter settings +清除篩選器設定 + +Copy +複製 + +Paste +貼上 + +Alternate Comparison Settings +備用比對設定 + +Alternate Synchronization Settings +備用同步設定 + +Local Filter +本機篩選器 + +&New +新增(&N) + +&Save +儲存(&S) + +Save as &batch job... +另存為批次處理作業(&b) + +1. &Compare +1. 比對(&C) + +2. &Synchronize +2. 同步(&S) + +&Global settings +整體設定(&G) + +&Language +語言(&L) + +&Find... +尋找(&F) + +&Export file list... +匯出檔案清單(&E)... + +&Tools +工具(&T) + +&Check now +現在檢查(&C) + +Check &automatically once a week +一週自動檢查一次(&a) + +&Check for new version +檢查新版本(&C) + +Compare +比對 + +Cancel +取消 + +Synchronize +同步 + +Add folder pair +新增配對資料夾 + +Remove folder pair +移除配對資料夾 + +Swap sides +兩邊交換 + +Close search bar +關閉搜尋欄位 + +Find: +尋找: + +Match case +區分大小寫 + +Save as batch job +另存為批次處理作業 + +Hide excluded items +隱藏排除項目 + +Show filtered or temporarily excluded files +顯示已篩選或暫時排除的檔案 + +Number of files and folders that will be created +將被新建的檔案和資料夾數量 + +Number of files that will be overwritten +一些檔案和目錄會被覆蓋 + +Number of files and folders that will be deleted +將被刪除的檔案和資料夾數量 + +Total bytes to copy +要複製的位元組總數 + +Select a variant: +選擇一個變數: + +Identify equal files by comparing modification time and size. +由修改時間和檔案大小比對來判斷相同檔案。 + +Identify equal files by comparing the file content. +由比對檔案內容來判斷相同檔案。 + +Symbolic links: +符號連結: + +More information +詳細資訊 + +OK +確定 + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +識別和傳播兩邊的變更。自動檢測刪除、移動和衝突使用的資料庫。 + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +新建左邊資料夾的鏡像備份,同步後完整匹配右邊資料夾。 + +Copy new and updated files to the right folder. +將新的和已更新的檔案複製到右邊資料夾。 + +Configure your own synchronization rules. +配置你自己的同步規則。 + +Detect moved files +檢測被移動的檔案 + +Requires database files. Not supported by all file systems. +需要資料庫檔案。不支援所有檔案系統。 + +Delete files: +刪除檔案: + +Permanent +常駐 + +Delete or overwrite files permanently +永久刪除或覆蓋檔案 + +Recycle bin +資源回收筒 + +Back up deleted and overwritten files in the recycle bin +在資源回收筒中刪除備份和覆蓋檔案 + +Versioning +版本控制 + +Move files to a user-defined folder +將檔案移動到一個使用者定義的資料夾 + +Naming convention: +命名慣例: + +Show examples +顯示範例 + +Handle errors: +錯誤處理: + +Ignore +忽略 + +Hide all error and warning messages +隱藏所有錯誤和警告訊息 + +Pop-up +彈出視窗 + +Show pop-up on errors or warnings +在彈出視窗上顯示錯誤或警告訊息 + +On completion: +完成後: + +Start synchronization now? +現在開始同步? + +Variant: +變數: + +Statistics +統計 + +&Don't show this dialog again +不要再顯示此對話框(&D) + +Items found: +尋找項目: + +Speed: +速度: + +Time remaining: +剩餘時間: + +Time elapsed: +經過時間: + +Synchronizing... +正在同步... + +Minimize to notification area +最小化到通知區域 + +Close +關閉 + +&Pause +暫停(&P) + +Stop +停止 + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +新建一個批次處理檔,用於無人值守同步。開始,點兩下此檔案,或安排在任務規劃中:%x + +Stop synchronization at first error +在第一個錯誤出現停止同步 + +Show progress dialog +顯示進度對話框 + +Save log: +儲存日誌 + +Limit: +限制: + +Limit maximum number of log files +限制日誌檔的最大數量 + +How can I schedule a batch job? +如何安排批次處理作業? + +&Recycle bin +資源回收筒(&R) + +Delete on both sides +兩邊都刪除 + +Delete on both sides even if the file is selected on one side only +即使只在一邊中選好檔案,還是會刪除兩邊檔案。 + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +選擇從同步中排除某些檔案的篩選器規則。輸入相對於其對應的配對資料夾路徑。 + +Include: +包括: + +Exclude: +排除: + +Time span: +時間間隔: + +File size: +檔案大小: + +Minimum: +最小: + +Maximum: +最大: + +&Clear +清除(&C) + +The following settings are used for all synchronization jobs. +以下設定用於所有同步作業。 + +Fail-safe file copy +故障保護檔案複製 + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +覆蓋目標之前先複製到一個臨時檔案(*.ffs_tmp)。 +即使發生嚴重錯誤時,還能確保一致的狀態。 + + +(recommended) +(建議) + +Copy locked files +複製被鎖定的檔案 + +Copy shared or locked files using the Volume Shadow Copy Service. +共用的副本或鎖定的檔案使用卷影複製服務 + +(requires administrator rights) +(需要管理員權限) + +Copy file access permissions +複製檔案系統權限 + +Transfer file and folder permissions. +傳輸檔案和資料夾的權限。 + +Automatic retry on error: +錯誤時自動重試: + +Retry count: +重試次數: + +Delay (in seconds): +延遲(以秒為單位): + +Customize context menu: +自訂內容功能表: + +Description +描述 + +Restore hidden windows +還原隱藏視窗 + +&Default +預設(&D) + +Source code written in C++ using: +使用C++編寫的原始碼 + +If you like FreeFileSync +如果你喜歡FreeFileSync + +Donate with PayPal +使用PayPal捐款 + +Feedback and suggestions are welcome +歡迎反饋意見和建議 + +Homepage +首頁 + +Email +信箱 + +Published under the GNU General Public License +在GNU通用公共許可證下發佈 + +Many thanks for localization: +非常感謝本地化語系翻譯工作人員: + +Save as Batch Job +另存為批次處理作業 + +Delete Items +刪除項目 + +Global Settings +整體設定 + +Select Time Span +選擇時間間隔 + +Folder Pairs +配對資料夾 + +Find +尋找 + +Overview +摘要 + +Configuration +配置 + +Main Bar +主欄位 + +Filter Files +篩選檔案 + +Select View +選擇檢視 + +Open... +開啟... + +Save +儲存 + +Compare both sides +比對兩邊 + +Comparison settings +比對設定 + +Synchronization settings +同步設定 + +Start synchronization +開始同步 + +Confirm +確認 + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + +你真的要執行 %x 項目的命令 %y 嗎? + + +&Execute +執行(&E) + + +1 directory +%x directories + + +%x 個目錄 + + + +1 file +%x files + + +%x 個檔案 + + + +%y of 1 row in view +%y of %x rows in view + + +視圖中的第 %x 列中的第 %y 列 + + +Set direction: +設定方向: + +multiple selection +多重選擇 + +Include via filter: +透過篩選器包括: + +Exclude via filter: +透過篩選器排除: + +Exclude temporarily +暫時排除 + +Include temporarily +暫時包括 + +Delete +刪除 + +Include all +包括所有 + +Exclude all +排除所有 + +Show icons: +顯示圖示: + +Small + + +Medium + + +Large + + +Select time span... +選擇時間間隔... + +Default view +預設檢視 + +Show "%x" +顯示 "%x" + +Last session +最後連線 + +Folder Comparison and Synchronization +資料夾比對和同步 + +Configuration saved +配置已儲存 + +FreeFileSync batch +FreeFileSync批次處理 + +Do you want to save changes to %x? +是否要儲存變更到 %x? + +Never save &changes +從不保存更改(&c) + +Do&n't save +不儲存(&N) + +Filter +篩選器 + +Show files that exist on left side only +顯示只存在於左邊的檔案 + +Show files that exist on right side only +顯示只存在於右邊的檔案 + +Show files that are newer on left +顯示左邊較新的檔案 + +Show files that are newer on right +顯示右邊較新的檔案 + +Show files that are equal +顯示相同的檔案 + +Show files that are different +顯示不同的檔案 + +Show conflicts +顯示衝突 + +Show files that will be created on the left side +顯示左邊將被新建的檔案 + +Show files that will be created on the right side +顯示右邊將被新建的檔案 + +Show files that will be deleted on the left side +顯示左邊將被刪除的檔案 + +Show files that will be deleted on the right side +顯示右邊將被刪除的檔案 + +Show files that will be overwritten on left side +顯示左邊將被覆蓋的檔案 + +Show files that will be overwritten on right side +顯示右邊將被覆蓋的檔案 + +Show files that won't be copied +顯示將不會被複製的檔案 + +Set as default +設為預設值 + +All folders are in sync +所有資料夾都是同步的 + +Synchronization Settings +同步設定 + +Comparison Settings +比對設定 + +Cannot find %x +找不到 %x + +Comma-separated values +以逗號分隔值 + +File list exported +檔案清單已匯出 + +Searching for program updates... +正在搜尋程式更新... + +Scanning... +正在掃瞄... + +Comparing content... +正在比對内容... + +Info +訊息 + +Warning +警告 + +Paused +已暫停 + +Initializing... +正在初始化... + +Stopped +已停止 + +Completed +已完成 + +&Continue +繼續(&C) + +Log +日誌 + +Today +今日 + +This week +本週 + +This month +本月 + +This year +今年 + +Last x days +最後 x 天 + +Byte +位元組 + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +你真的要將以下項目 %x 移動到資源回收筒嗎? + + +Move +移動 + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +您真的要將下列 %x 項目刪除嗎? + + +Exclude +排除 + +Direct +直接 + +Follow +遵從 + +Copy NTFS permissions +複製NTFS權限 + +Integrate external applications into context menu. The following macros are available: +整合內容功能表中的外部應用程式。可以使用下面的巨集: + +- full file or folder name +- 完整檔案或資料夾名稱 + +- folder part only +- 只有資料夾一部分 + +- Other side's counterpart to %item_path% +- 另一邊對應到 %item_path% + +- Other side's counterpart to %item_folder% +- 另一邊對應到 %item_folder% + +Restore all hidden windows and warnings? +還原所有隱藏的視窗和警告? + +Leave as unresolved conflict +保留給未解決的衝突 + +Replace +取代 + +Move files and replace if existing +如果存在,移動檔案並取代 + +Time stamp +時間戳記 + +Append a timestamp to each file name +將時間戳記附加到每個檔案名稱上 + +File +檔案 + +YYYY-MM-DD hhmmss +YYYY-MM-DD hhmmss + +Files +檔案 + +Items +項目 + +Percentage +百分比 + +Cannot monitor directory %x. +無法監測目錄 %x。 + +Conversion error: +轉換錯誤: + +Cannot delete file %x. +無法刪除目錄 %x。 + +The file is locked by another process: +檔案被另一個進程鎖定: + +Cannot move file %x to %y. +無法移動檔案 %x 到 %y。 + +Cannot delete directory %x. +無法刪除目錄 %x。 + +Cannot write file attributes of %x. +無法寫入 %x 的檔案屬性。 + +Cannot write modification time of %x. +無法寫入 %x 的修改時間。 + +Cannot read security context of %x. +無法讀取 %x 的安全內容。 + +Cannot write security context of %x. +無法寫入 %x 的安全內容。 + +Cannot read permissions of %x. +無法讀取 %x 的權限。 + +Cannot write permissions of %x. +無法寫入 %x 的權限。 + +Cannot create directory %x. +無法新建目錄 %x。 + +Cannot create symbolic link %x. +無法新建符號連結 %x。 + +Cannot find system function %x. +找不到系統函數 %x。 + +Cannot copy file %x to %y. +無法複製檔案 %x 到 %y。 + +Type of item %x is not supported: +項目類型 %x 不被支援: + +Cannot resolve symbolic link %x. +無法解析符號連結 %x。 + +Cannot open directory %x. +無法開啟目錄 %x。 + +Cannot enumerate directory %x. +無法枚舉目錄 %x。 + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +%x 分 + + + +1 hour +%x hours + + +%x 小時 + + + +1 day +%x days + + +%x 天 + + +Unable to register to receive system messages. +無法登錄接收系統訊息。 + +Cannot set privilege %x. +無法設定權限 %x。 + +Unable to suspend system sleep mode. +無法中止系統睡眠模式。 + +Cannot change process I/O priorities. +無法更改I/O處理優先順序。 + +Unable to move %x to the recycle bin. +無法將 %x 移動到資源回收筒。 + +Cannot determine final path for %x. +無法確定最後路徑為 %x。 + +Error Code %x: +錯誤代碼 %x: + diff --git a/FreeFileSync/Build/Languages/croatian.lng b/FreeFileSync/Build/Languages/croatian.lng new file mode 100644 index 00000000..a5458089 --- /dev/null +++ b/FreeFileSync/Build/Languages/croatian.lng @@ -0,0 +1,1533 @@ +
    + Hrvatski + Slavko Blažević + hr_HR + flag_croatia.png + 3 + n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2 +
    + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + + + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + + + +Detecting abandoned lock... + + +Lock owner: + + +Waiting while directory is locked: + + +Both sides have changed since last synchronization. +Obje su strane promjenjene od posljednje sinkronizacije. + +Cannot determine sync-direction: +Ne mogu odrediti smjer sinkronizacije. + +No change since last synchronization. +Nema promjena od zadnje sinkronizacije. + +The database entry is not in sync considering current settings. +Unos podataka nije u sinkronizaciji uzevši u obzir trenutne postavke. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Postavljam zadani sinkronizacijski smjer: Stare datoteke će biti prepisane novim datotekama. + +Checking recycle bin availability for folder %x... +Provjeravam dosupnost koša za smeće za mapu %x... + +Moving file %x to the recycle bin +Premještam datoteku %x u koš + +Moving folder %x to the recycle bin +Premještam datoteku %x u koš + +Moving symbolic link %x to the recycle bin +Premještam simobličnu poveznicu %x u koš + +Deleting file %x +Brisanje datoteke %x + +Deleting folder %x +Brisanje mape %x + +Deleting symbolic link %x +Brisanje simboličnih poveznica %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +Koš nije dosuutpan za sljedeće mape. Zbog toga će datoteke biti trajno obrisane: + +An exception occurred +Dogodilo se izuzeće + +A directory path is expected after %x. +Putanja direktorija se očekuje nakon %x. + +Syntax error +Greška sintakse + +Cannot open file %x. +Ne mogu otvoriti datoteku %x. + +File %x does not contain a valid configuration. +Datoteka %x ne sadrži valjanu konfiguraciju + +Unequal number of left and right directories specified. +Neravnomjeran broj lijevih i desnih direkotrija specificiran. + +The config file must not contain settings at directory pair level when directories are set via command line. +Config datoteka ne smije sadržavati postavke na direktorij par razini ukoliko su direktoriji upisani preko naredbene linije. + +Directories cannot be set for more than one configuration file. +Direkotrij nemože biti postavljen za više od jedne konfigracijske datoteke. + +Command line +Naredbena linija + +Syntax: +Sintaksa: + +config files +config datoteke + +directory +direktorij + +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 directories for at most one config file. +Bilo koji broj alternativnih mapa za najviše jedanu config datoteku. + +Cannot find the following folders: +Ne mogu pronaći slijedeće mape: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Možete ignorirati ovu pogrešku te uzeti u obzir svaku mapu praznom. Mape se tada stvaraju automatski tokom sinkronizacije. + +A folder input field is empty. +Polje za odabir foldera je prazno. + +The corresponding folder will be considered as empty. +Odgovarajuća mapa će se smatrati prazna. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +Sljedeće mape imaju zavisnu putanju. Budite pažljivi prilikom postavljanja pravila za sinkronizaciju: + +File %x has an invalid date. +Datoteka %x ima nevaljan datum. + +Date: +Datum: + +Files %x have the same date but a different size. +Datoteke %x imaju isti datum ali drugačiju veličinu. + +Size: +Veličina: + +Items differ in attributes only +Stavke se razlikuju samo u atributima + +Resolving symbolic link %x +Rješavam simboličku vezu %x + +Comparing content of files %x +Uspoređujem sadržaj datoteka %x + +Generating file list... +Generiram listu datoteka... + +Starting comparison +Počinjem usporedbu + +Calculating sync directions... +Izračunavam smjerove sinkronizacije... + +Out of memory. +Nedostatak memorije. + +Item exists on left side only +Stavka postoji samo na lijevoj strani + +Item exists on right side only +Stavka postoji samo na desnoj strani + +Left side is newer +Lijeva strana je novija + +Right side is newer +Desna strana je novija + +Items have different content +Stavke imaju različit sadržaj + +Both sides are equal +Obje strane su jednake + +Conflict/item cannot be categorized +Sukob/stavka ne može biti razvrstana + +Copy new item to left +Kopiraj novu stavku lijevo + +Copy new item to right +Kopiraj novu stavku desno + +Delete left item +Izbriši lijevu stavku + +Delete right item +Izbriši desnu stavku + +Move file on left +Premjesti datoteku lijevo + +Move file on right +Premjest datoteku desno + +Overwrite left item +Prepiši lijevu stavku + +Overwrite right item +Prepiši desnu stavku + +Do nothing +Ne radi ništa + +Update attributes on left +Osvježi atribute lijevo + +Update attributes on right +Osvježi atribute desno + +Database file %x is incompatible. +Datoteka baze %x je nekompatibilna + +Initial synchronization: +Početna sinkronizacija: + +Database file %x does not yet exist. +Datoteka baze %x još ne postoji + +Database file is corrupt: +Baza je oštećena: + +Cannot write file %x. +Ne mogu zapisati datoteku %x. + +Cannot read file %x. +Ne mogu čitati datoteku %x. + +Database files do not share a common session. +Datoteke baze ne dijele zajednički protokol. + +Searching for folder %x... +Tražim mapu %x... + +Cannot read file attributes of %x. +Ne mogu pročitati osobine od %x. + +Cannot get process information. +Ne mogu dobit informacije o procesu + + +1 sec +%x sec + + +%x sek +%x sek +%x sek + + +Creating file %x +Izrađujem datoteku %x + +Items processed: +Obrađeni elementi: + +Items remaining: +Preostale stavke: + +Total time: +Ukupno vrijeme: + + +1 byte +%x bytes + + +%x bajt +%x bajta +%x bajtova + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Greška u analizi datoteke %x, red %y, stupac %z. + +Cannot set directory lock for %x. +Ne mogu zaključiti mapu %x. + +Scanning: +Pretražujem: + + +1 thread +%x threads + + +%x nit +%x niti +%x niti + + +Encoding extended time information: %x +:Spremam informacije o vremenu %x + +/sec +/sek + +%x items/sec +%x stavki/sek + +Configuration file %x loaded partially only. +Datoteka postavki %x učitana samo djelomično + +Show in Explorer +Prikaži u Exploreru + +Open with default application +Otvori s zadanom aplikacijom + +Browse directory +Odaberi direktorij + +Cannot access the Volume Shadow Copy Service. +Ne mogu pristupiti Voulme Shadow Copy servisu. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Molimo koristite FreeFileSync 64-bitnu verziju za izradu shadow kopija na ovom sustavu + +Cannot load file %x. +Ne mogu učitati datoteku %x. + +Cannot determine volume name for %x. +Ne mogu ustanoviti ime volumen for %x. + +Volume name %x is not part of file path %y. +Ime voumena %x nije dio puta datotke %y. + +Stop requested: Waiting for current operation to finish... +Zaustavljam naredbu: Čekam trenutni zadatak da se dovrši... + +Unable to create timestamp for versioning: +Nije moguća izrada vremenske oznake za označavanje: + +Cannot read the following XML elements: +Ne mogu čitati slijedeće XML elemente + +&Open... +&Otvori... + +Save &as... +Spremiti &kao... + +&Quit +&Izlaz + +&Program +&Program + +&View help +&Pogledaj pomoć + +&About +&O programu + +&Help +&Pomoć + +Usage: +Uporaba: + +1. Select folders to watch. +1. Odaberite mape za nadzirati + +2. Enter a command line. +2. Unesite naredbu. + +3. Press 'Start'. +3. Pretisnite 'Start'. + +To get started just import a .ffs_batch file. +Za započeti uvezite .ffs Slijednu datoteku. + +Folders to watch: +Mape za upotrebu: + +Add folder +Dodaj mapu + +Remove folder +Ukloni mapu + +Browse +Odaberi + +Select a folder +Odaberite mapu + +Idle time (in seconds): +Vrijeme pripravnosti (u sekundama): + +Idle time between last detected change and execution of command +Vrijeme čekanja između zadnje prepoznate promjene i izvršenja naredbe + +Command line: +Naredbena linija: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +Naredba će biti pokrenuta ako se: +- datoteke ili podmape promijene +- nove mape pojave (npr. umetanje USB sticka) + + +&Start +&Započni + +About +O programu + +Build: %x +Inačnica: %x + +All files +Sve datoteke + +Automated Synchronization +Automatska Sinkronizacija + +Directory monitoring active +Nadzor direktorija je aktivan + +Waiting until all directories are available... +Čekanje na dostupnost svih direktorija... + +Error +Greška + +&Restore +&Vrati + +&Show error +&Prikaži grešku + +&Exit +&Izlaz + +Incorrect command line: +Netočna naredbena linija: + +&Retry +&Ponovi + +File content +Sadržaj datoteke + +File time and size +Vrijeme i veličina datoteke + +Two way +Dvosmjerno + +Mirror +Zrcalno + +Update +Ažuriraj + +Custom +Uobičajeno + +Multiple... +Mnogostruko... + +Moving file %x to %y +Premještam datoteku %x u %y + +Moving folder %x to %y +Premještam mapu %x u %y + +Moving symbolic link %x to %y +Premještam simboličnu poveznicu %x u %y + +Removing old versions... +Uklanjam starije verzije... + +Creating symbolic link %x +Izrađujem simboličnu poveznicu %x + +Creating folder %x +Izrađujem mapu %x + +Overwriting file %x +Prepisujem datoteku %x + +Overwriting symbolic link %x +Prepisujem simboličnu poveznicu %x + +Verifying file %x +Provjeravam datoteku %x + +Updating attributes of %x +Obnavljam atribute od %x + +Creating a Volume Shadow Copy for %x... +Lreiram Volume Shadow Copy za %x... + +Data verification error: %x and %y have different content. +Greška pri provjeri podataka: %x i %y imaju različit sadržaj. + +Cannot find %x. +Ne mogu pronaći %x. + +Target folder %x already existing. +Odredišna mapa %x već postoji. + +Target folder input field must not be empty. +Odredišna mapa ne može biti prazna. + +Please enter a target folder for versioning. +Molimo odaberite ciljnu mapu za označavanje + +Source folder %x not found. +Izvorna mapa %x nije pronađena. + +The following items have unresolved conflicts and will not be synchronized: +Slijedeće stavke imaju nedefiniranih sukoba te neće biti sinkronizirane: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +Navedene mape se značajno razlikuju. Prvojerite dali uspoređujete ispravne mape za sinkronizaciju. + +Not enough free disk space available in: +Nedovoljno prostora na disku: + +Required: +Potrebno: + +Available: +Dostupno: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Mapa će biti uređena koja je dio više pari mapa. Molimo provjerite sinkronizacijske postavke. + +Synchronizing folder pair: +Par sinkronizirane mape: + +Generating database... +Izrađujem bazu podataka... + +job name +Ime zadatka + +Synchronization stopped +Sinkronizacija zaustavljena + +Synchronization completed with errors +Sinkronizacija završena s greškama + +Synchronization completed with warnings +Sinkronizacija završena s upozorenjima + +Nothing to synchronize +Ništa za sinkronizirati + +Synchronization completed successfully +Sinkronizacija uspješno dovršena + +Saving log file %x... +Spremam izješće %x... + +You can switch to FreeFileSync's main window to resolve this issue. +Možete prijeći na glavni prozor FreeFileSynca da bi rješili ovaj problem. + +&Don't show this warning again +&Ne prikazuj više ovo upozorenje. + +&Ignore +&Ignoriraj + +&Switch +&Zamjeni + +Switching to FreeFileSync's main window +Prebacujem na glavni prozor FreeFileSync + +&Ignore subsequent errors +&Zanemari naknadne pogreške + +Retrying operation... +Ponavljam operaciju... + +Serious Error +Ozbiljna Pogreška + +Check for Program Updates +Provjeriti Nadogradnje Programa + +A new version of FreeFileSync is available: +Nova verzija FreeFileSync je dostupna: + +Download now? +Preuzeti sada? + +&Download +&Preuzmi + +FreeFileSync is up to date. +FreeFileSync je ažuriran. + +Unable to connect to sourceforge.net. +Ne mogu se povezati na sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Ne mogu pronaći trenutnu verziju FreeFileSync-a online. Dali želite ručno potražiti? + +&Check +&Provjeri + +Symlink +Poveznica simbola + +Folder +Mapa + +Full path +Puna putanja + +Name +Naziv + +Relative path +Relativna Putanja + +Base folder +Izvorna mapa + +Size +Veličina + +Date +Datum + +Extension +Ekstenzija + +Category +Kategorija + +Action +Radnja + +Drag && drop +Povuci && ispusti + +Close progress dialog +Zatvori prozor zadatka + +Standby +Spavaj + +Log off +Odlogiraj se + +Shut down +Isključi računalo + +Hibernate +Hibernacija + +Alternate comparison settings +Alternativne postavke usporedbe + +Alternate synchronization settings +Alternativne postavke sinkronizacije + +Local filter +Lokalni filter + +Active +Aktivno + +None +Ništa + +Remove alternate settings +Ukloni alternativne postavke + +Clear filter settings +Počisti postavke filtera + +Copy +Kopiraj + +Paste +Zalijepi + +Alternate Comparison Settings +Alternativne Postavke Usporedbe + +Alternate Synchronization Settings +Alternativne Postavke Sinkronizacije + +Local Filter +Lokalni Filter + +&New +&Novo + +&Save +&Spremi + +Save as &batch job... +Spremi kao &Slijedni zadatak... + +1. &Compare +1. &Usporedi + +2. &Synchronize +2. &Sinkroniziraj + +&Global settings +&Globalne postavke + +&Language +&Jezik + +&Find... +&Pronađi... + +&Export file list... +&Izvoz liste datoteka... + +&Tools +&Alati + +&Check now +&Provjeri sada + +Check &automatically once a week +Provjeri &automatski jednom tjedno + +&Check for new version +&Dostupnost nove verzije + +Compare +Usporedi + +Cancel +Odustani + +Synchronize +Sinkroniziraj + +Add folder pair +Dodaj mapu + +Remove folder pair +Odstrani mapu + +Swap sides +Zamjeni strane + +Close search bar +Zatvori traku pretraživanja + +Find: +Pronađi: + +Match case +Identičan naziv + +Save as batch job +Spremi kao Slijedni zadatak + +Hide excluded items +Sakrij isključene stavke + +Show filtered or temporarily excluded files +Prikaži filtrirane ili privremeno izdvojene datoteke + +Number of files and folders that will be created +Broj datoteka i foldera koji će se stvoriti + +Number of files that will be overwritten +Broj datoteka koje će biti prepisane + +Number of files and folders that will be deleted +Broj datoteka i mapa koje će se izbrisati + +Total bytes to copy +Ukupno bajta za kopirati + +Select a variant: +Odaberi opciju: + +Identify equal files by comparing modification time and size. +Odredi jednake datoteke uspoređujući datum izmjene i veličinu + +Identify equal files by comparing the file content. +Odredi jednake datoteke uspoređujući sadržaj datoteke. + +Symbolic links: +Simbolične poveznice: + +More information +Više informacija + +OK +U redu + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Pronađi i izvrši izmjene na obje strane. Izbrisano, premješteno te sukobi se otkriju automatski pomoću baze. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Izradi zrcalnu kopiju lijeve mape koja će se točno podudarati s desnom mapom nakon sinkronizacije. + +Copy new and updated files to the right folder. +Kopiraj nove i ažurirane datoteke u desnu mapu. + +Configure your own synchronization rules. +Postavite vaša vlastita sinkronizacijska pravila. + +Detect moved files +Otkrij premještene datoteke + +Requires database files. Not supported by all file systems. +Potrebne datoteke baze. Nije podržano kod svih datotečnih sustava. + +Delete files: +Izbriši datoteke: + +Permanent +Trajno + +Delete or overwrite files permanently +Trajno izbriši ili prepiši datoteke + +Recycle bin +Koš + +Back up deleted and overwritten files in the recycle bin +Spremi obrisane ili prepisane datoteke u koš + +Versioning +Odabir + +Move files to a user-defined folder +Premjesti datoteke u mapu određenu od korsnika + +Naming convention: +Pravilo naziva: + +Show examples +Prikaži primjere + +Handle errors: +Obradi pogreške: + +Ignore +Ignoriraj + +Hide all error and warning messages +Sakrij sve greške i upozorenja + +Pop-up +Pop-up + +Show pop-up on errors or warnings +Prikaži skočni prozor pri greškama i upozorenjima + +On completion: +Pri završetku: + +Start synchronization now? +Započeti sinkronizaciju sada? + +Variant: +Opcija: + +Statistics +Statistika + +&Don't show this dialog again +&Nemoj više prikazivati ovaj dijaloški prozor + +Items found: +Pronađene stavke: + +Speed: +Brzina: + +Time remaining: +Preostalo vremena: + +Time elapsed: +Proteklo vremena: + +Synchronizing... +Sinkroniziram... + +Minimize to notification area +Spusti u notifikacije + +Close +Zatvori + +&Pause +&Pauziraj + +Stop +Zaustavi + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Stvaranje Slijednih datoteka za sinkronizaciju bez nadzora. Za početak, dva-put kliknite na ovu datoteku ili planirajte u planeru zadataka: %x + +Stop synchronization at first error +Zaustavi sinkronizaciju pri prvoj pogrešci + +Show progress dialog +Prikaži napredak + +Save log: +Spremi izvještaj: + +Limit: +Granica: + +Limit maximum number of log files +Ograniči maksimalan broj izvješća + +How can I schedule a batch job? +Kako zakazati Slijedni zadatak? + +&Recycle bin +&Koš za smeće + +Delete on both sides +Izbriši na obje strane + +Delete on both sides even if the file is selected on one side only +Izbriši na obje strane iako je označena datoteka samo na jednoj strani + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Odaberite filterska pravila za odvojiti određene datoteke iz sinkronizacije. Unesite putanju datoteka prema njihovim odgovarajućim parovima. + +Include: +Obuhvati + +Exclude: +Odvojiti: + +Time span: +Vremenski raspon: + +File size: +Veličina datoteke: + +Minimum: +Minimum: + +Maximum: +Maksimum: + +&Clear +&Brisati + +The following settings are used for all synchronization jobs. +Ove postavke se koriste za sve sinkronizacijske zadatke + +Fail-safe file copy +Kopiranje zaštićeno od grešaka + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Kopiraj u privremenu datoteku (*.ffs_tmp) prije prepisivanja ciljne datoteke +To jamči dosljedno stanje čak i u slučaju ozbiljne pogreške + + +(recommended) +(preporučeno) + +Copy locked files +Kopiraj zaključane datoteke + +Copy shared or locked files using the Volume Shadow Copy Service. +Kopiraj djeljene ili zaključane datoteke pomoću Volume Shadow Copy servisa. + +(requires administrator rights) +(zahtjeva administratorske ovlasti) + +Copy file access permissions +Kopiraj datotečna dopuštenja + +Transfer file and folder permissions. +Dopuštenja za prijenos datoteka i foldera. + +Automatic retry on error: +Automatski pokušaj pri pogrešci: + +Retry count: +Broj pokušaja: + +Delay (in seconds): +Odgoda (u sekundama) : + +Customize context menu: +Prilagodite kontekstni izbornik + +Description +Opis + +Restore hidden windows +Prikaži skrivene prozore + +&Default +&Zadano + +Source code written in C++ using: +Izvorni kod napisan u C++ uz korištenje: + +If you like FreeFileSync +Ako volite FreeFileSync + +Donate with PayPal +Doniraj s PayPal + +Feedback and suggestions are welcome +Povratne informacije i prijedlozi su dobrodošli + +Homepage +Početna stranica + +Email +Email + +Published under the GNU General Public License +Objavljeno pod licencom GNU General Public + +Many thanks for localization: +Velike zahvale idu: + +Save as Batch Job +Spremi kao Slijedni zadatak + +Delete Items +Izbriši stavke + +Global Settings +Globalne postavke + +Select Time Span +Odaberite vremenski raspon + +Folder Pairs +Parovi Foldera + +Find +Pronađi + +Overview +Pregled + +Configuration +Postavke + +Main Bar +Glavna traka + +Filter Files +Filtriraj datoteke + +Select View +Odaberi pogled + +Open... +Otvori... + +Save +Spremi + +Compare both sides +Usporedi obje strane + +Comparison settings +Postavke usporedbe + +Synchronization settings +Postavke sinkronizacije + +Start synchronization +Započni sinkronizaciju + +Confirm +Potvrdi + +&Execute +&Izvrši + + +1 directory +%x directories + + +%x direktorij +%x direktorija +%x direktorija + + + +1 file +%x files + + +%x datoteka +%x datoteke +%x datoteka + + + +%y of 1 row in view +%y of %x rows in view + + +%y od %x reda u prikazu +%y od %x reda u prikazu +%y od %x reda u prikazu + + +Set direction: +Odaberi smjer: + +multiple selection +višestruki odabir + +Include via filter: +Pridruži preko filtera: + +Exclude via filter: +Isključi preko filtra: + +Exclude temporarily +Izuzmi privremeno + +Include temporarily +Trenutno uključi + +Delete +Izbriši + +Include all +Uključi sve + +Exclude all +Izdvoji sve + +Show icons: +Prikaži ikone: + +Small +Malo + +Medium +Srednje + +Large +Veliko + +Select time span... +Izaberite vremenski raspon... + +Default view +Standardni prikaz + +Show "%x" +Prikaži "%x" + +Last session +Zadnja sesija + +Folder Comparison and Synchronization +Usporedba i sinkronizacija mapa + +Configuration saved +Postavke spremljene + +FreeFileSync batch +FreeFileSync Slijedni zadatak + +Do you want to save changes to %x? +Da li želite spremiti izmjene za %x? + +Never save &changes +Nikad ne spremaj &promjene + +Do&n't save +&Nemoj spremiti + +Filter +Filtriranje + +Show files that exist on left side only +Prikaži datoteke koje postoje samo na lijevoj strani + +Show files that exist on right side only +Prikaži datoteke koje postoje samo na desnoj strani + +Show files that are newer on left +Prikaži datoteke koje su novije lijevo + +Show files that are newer on right +Prikaži datoteke koje su novije desno + +Show files that are equal +Prikaži jednake datoteke + +Show files that are different +Prikaži datoteke koje su različite + +Show conflicts +Prikaži sukobe + +Show files that will be created on the left side +Prikaži datoteke koje će biti napravljene na lijevoj strani + +Show files that will be created on the right side +Prikaži datoteke koje će biti napravljene na desnoj strani + +Show files that will be deleted on the left side +Prikaži datoteke koje će biti izbrisane na lijevoj strani + +Show files that will be deleted on the right side +Prikaži datoteke koje će biti izbrisane na desnoj strani + +Show files that will be overwritten on left side +Prikaži datoteke koje će biti prepisane na lijevoj strani + +Show files that will be overwritten on right side +Prikaži datoteke koje će biti prepisane na desnoj strani + +Show files that won't be copied +Prikaži datoteke koje neće biti kopirane + +Set as default +Postavi kao zadano + +All folders are in sync +Sve mape su u sinkronizaciji + +Synchronization Settings +Postavke Sinkronizacije + +Comparison Settings +Postavke Usporedbe + +Cannot find %x +Ne mogu pronaći %x + +Comma-separated values +Zarezom odvojene vrijednosti + +File list exported +Datotečna lista izvezena + +Searching for program updates... +Pretražujem ažuriranja programa... + +Scanning... +Tražim... + +Comparing content... +Uspoređujem sadržaj... + +Info +Info + +Warning +Oprez + +Select all +Odaberi sve + +Paused +Pauzirano + +Initializing... +Učitavam + +Stopped +Zaustavljeno + +Completed +Završeno + +&Continue +&Nastavi + +Log +Dnevnik + +Today +Danas + +This week +Ovaj tjedan + +This month +Ovaj mjesec + +This year +Ove godine + +Last x days +Zadnjih x dana + +Byte +Bajt + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Dali stvarno želite premjestiti sljedeću %x stavku u koš? +Dali stvarno želite premjestiti sljedeće %x stavke u koš? +Dali stvarno želite premjestiti sljedećih %x stavki u koš? + + +Move +Premjesti + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Dali stvarno želite obrisati sljedeću %x stavku? +Dali stvarno želite obristi sljedeće %x stavke? +Dali stvarno želite obristi sljedećih %x stavki? + + +Exclude +Isključi + +Direct +Neposredno + +Follow +Slijedi + +Copy NTFS permissions +Kopiraj NTFS dopuštenja + +Integrate external applications into context menu. The following macros are available: +Integriraj vanjske aplikacije u kontekstni meni. Sljedeći makroi su dostupni: + +- full file or folder name +- cijelo ime datoteke ili mape + +- folder part only +- samo mapa + +- Other side's counterpart to %item_path% +- Duplikat s druge strane u %item_path% + +- Other side's counterpart to %item_folder% +- Duplikat s druge strane u %item_folder% + +Restore all hidden windows and warnings? +Vratiti sve skrivene prozore i upozorenja? + +Leave as unresolved conflict +Ostavi kao neriješeni sukob + +Replace +Zamjeni + +Move files and replace if existing +Premjesti datoteke i zamjeni ako već postoje + +Time stamp +Vremenska oznaka + +Append a timestamp to each file name +Dodaj vremensku oznaku svakom imenu datoteke + +File +Datoteka + +YYYY-MM-DD hhmmss +GGGG-MM-DD hhmmss + +Files +Datoteke + +Items +Stavke + +Percentage +Postotak + +Cannot monitor directory %x. +Ne mogu posmatrati direktorij %x. + +Conversion error: +Greška pri pretvorbi: + +Cannot delete file %x. +Ne mogu izbrisati datoteku %x. + +The file is locked by another process: +Datoteka je blokirana drugim procesom: + +Cannot move file %x to %y. +Ne mogu premjestiti datoteku %x u %y. + +Cannot delete directory %x. +Ne mogu izbrisati mapu %x. + +Cannot write file attributes of %x. +Ne mogu zapisati svojstva od %x. + +Cannot write modification time of %x. +Ne mogu zapisati vrijeme izmjene %x. + +Cannot read security context of %x. +Ne mogu čitati zaštićeni sadržaj %x. + +Cannot write security context of %x. +Ne mogu zapisati zaštićeni sadržaj %x. + +Cannot read permissions of %x. +Ne mogu čitati dopuštenja od %x. + +Cannot write permissions of %x. +Ne mogu zapisati dopuštenja za %x. + +Cannot create directory %x. +Ne mogu izraditi mapu %x. + +Cannot create symbolic link %x. +Ne mogu stvoriti simboličku vezu %x. + +Cannot find system function %x. +Ne mogu pronaći sistemsku funkciju %x. + +Cannot copy file %x to %y. +Ne mogu kopirati datoteku %x na %y. + +Type of item %x is not supported: +Tip stavke %x koji nije podržan: + +Cannot resolve symbolic link %x. +Ne mogu odrediti poveznicu %x. + +Cannot open directory %x. +Ne mogu otvoriti mapu %x. + +Cannot enumerate directory %x. +Ne mogu izlistati mapu %x. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +%x min +%x min +%x min + + + +1 hour +%x hours + + +%x sat +%x sata +%x sati + + + +1 day +%x days + + +%x dan +%x dana +%x dana + + +Unable to register to receive system messages. +Nije moguće registriranje sistemskih poruka + +Cannot set privilege %x. +Ne mogu postaviti prava za %x. + +Unable to suspend system sleep mode. +Nije moguće obustaviti sistemsko stanje propravnosti + +Cannot change process I/O priorities. +Ne može se promjeniti proces I/O prioriteta + +Unable to move %x to the recycle bin. +Nije moguće prebaciti %x u koš za smeće. + +Cannot determine final path for %x. +Ne mogu odrediti završnu putanju za %x. + +Error Code %x: +Pogreška broj %x: + diff --git a/FreeFileSync/Build/Languages/czech.lng b/FreeFileSync/Build/Languages/czech.lng new file mode 100644 index 00000000..8e4db524 --- /dev/null +++ b/FreeFileSync/Build/Languages/czech.lng @@ -0,0 +1,1521 @@ +
    + Čeština + ViCi + cs_CZ + flag_czech_republic.png + 3 + n==1 ? 0 : n>=2 && n<=4 ? 1 : 2 +
    + +Both sides have changed since last synchronization. +Došlo ke změně obou stran od poslední synchronizace. + +Cannot determine sync-direction: +Nelze určit směr synchronizace: + +No change since last synchronization. +Žádné změny od poslední synchronizace. + +The database entry is not in sync considering current settings. +Databázové položky nejsou podle aktuální konfigurace synchronní. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Nastaven výchozí způsob synchronizace: Staré soubory budou nahrazeny novými. + +Checking recycle bin availability for folder %x... +Kontrola Koše na složku %x ... + +Moving file %x to the recycle bin +Přesouvání souboru %x do Koše + +Moving folder %x to the recycle bin +Přesouvání adresáře %x do Koše + +Moving symbolic link %x to the recycle bin +Přesouvání symbolického odkazu %x do Koše + +Deleting file %x +Mazání souboru %x + +Deleting folder %x +Mazání adresáře %x + +Deleting symbolic link %x +Mazání symbolického odkazu %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +Nelze použít Koš pro následující složky. Soubory tak budou odstraněny permanentně: + +An exception occurred +Vyskytla se chyba + +A directory path is expected after %x. +Očekávána adresářová cesta po %x. + +Syntax error +Chyba syntaxe + +Cannot open file %x. +Nelze otevřít soubor %x. + +File %x does not contain a valid configuration. +Soubor %x neobsahuje platnou konfiguraci. + +Unequal number of left and right directories specified. +Zjištěn nestejný počet levých a pravých adresářů. + +The config file must not contain settings at directory pair level when directories are set via command line. +Konfigurační soubor nesmí obsahovat nastavení na úrovni adresářových párů pokud je zadán přes příkazovou řádku. + +Directories cannot be set for more than one configuration file. +Adresáře nemohou obsahovat více než jeden konfigurační soubor. + +Command line +Příkazová řádka + +Syntax: +Syntaxe: + +config files +konfigurační soubory + +directory +adresář + +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 directories for at most one config file. +Libovolný počet alternativních adresářů na alespoň jednu konfiguraci. + +A folder input field is empty. +Není zadána vstupní složka. + +The corresponding folder will be considered as empty. +Odpovídající složka bude považována za prázdnou. + +Cannot find the following folders: +Nelze najít následující složky: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Tuto chybu můžete přeskočit a považovat neexistující složku za prázdnou. Složka pak bude vytvořena při synchronizaci automaticky. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +Následující složky mají navzájem závislé cesty! Buďte opatrní s definicí synchronizačních pravidel: + +File %x has an invalid date. +Soubor %x má chybné datum. + +Date: +Čas: + +Files %x have the same date but a different size. +Soubory %x mají stejné datum a čas ale rozdílnou velikost. + +Size: +Velikost: + +Items differ in attributes only +Položky se liší pouze ve vlastnostech + +Resolving symbolic link %x +Hledání odkazu symbolického zástupce %x + +Comparing content of files %x +Porovnávání obsahu souborů %x + +Generating file list... +Vytváření seznamu souborů... + +Starting comparison +Začátek porovnávání + +Calculating sync directions... +Příprava adresářů synchronizace... + +Out of memory. +Nedostatek pracovní paměti. + +Item exists on left side only +Položky existující pouze vlevo + +Item exists on right side only +Položky existující pouze vpravo + +Left side is newer +Vlevo je novější + +Right side is newer +Vpravo je novější + +Items have different content +Obsah položek je rozdílný + +Both sides are equal +Obě strany jsou shodné + +Conflict/item cannot be categorized +Konflikty/položky které nelze zařadit + +Copy new item to left +Kopírovat novou položku doleva + +Copy new item to right +Kopírovat novou položku doprava + +Delete left item +Smazat položku zleva + +Delete right item +Smazat položku zprava + +Move file on left +Přesunout soubor nalevo + +Move file on right +Přesunout soubor napravo + +Overwrite left item +Přepsat levou položku tou zprava + +Overwrite right item +Přepsat pravou položku tou zleva + +Do nothing +Nic nedělat + +Update attributes on left +Nastavit vlastnosti vlevo + +Update attributes on right +Nastavit vlastnosti vpravo + +Database file %x is incompatible. +Chybný formát databáze %x. + +Initial synchronization: +Prvotní synchronizace: + +Database file %x does not yet exist. +Databázový soubor %x neexistuje. + +Database file is corrupt: +Databáze je poškozená: + +Cannot write file %x. +Nelze zapsat soubor %x. + +Cannot read file %x. +Nelze číst soubor %x. + +Database files do not share a common session. +Databázové soubory nejsou navzájem komplementární. + +Searching for folder %x... +Otevírání složky %x + +Cannot read file attributes of %x. +Nelze číst atributy souboru %x. + +Cannot get process information. +Nelze získat informace procesu. + +Waiting while directory is locked (%x)... +Čekání na uzamčení adresáře (%x) + + +1 sec +%x sec + + +1 sekunda +%x sekundy +%x sekund + + +Creating file %x +Vytváření souboru %x + +Items processed: +Zpracováno položek: + +Items remaining: +Zbývá položek: + +Total time: +Celkový čas: + + +1 byte +%x bytes + + +%x B +%x B +%x B + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Chyba zpracování souboru %x: na řádku %y ve sloupci %z + +Cannot set directory lock for %x. +Nelze nastavit zámek adresáře %x. + +Scanning: +Zpracováváno: + + +1 thread +%x threads + + +1 proces +%x procesy +%x procesů + + +Encoding extended time information: %x +Zpracování rozšířené informace o čase: %x + +/sec +/s + +%x items/sec +%x položek/s + +Configuration file %x loaded partially only. +Konfigurace ze souboru %x byla načtena jen částečně. + +Show in Explorer +Zobrazit v Průzkumníkovi + +Open with default application +Otevřít výchozí aplikací + +Browse directory +Procházet adresář + +Cannot access the Volume Shadow Copy Service. +Nepodařil se přístup ke službě Stínové kopie. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Prosím použijte FreeFileSync 64-bitovou verzi pro použití služby Stínové kopie. + +Cannot load file %x. +Nelze načíst soubor %x. + +Cannot determine volume name for %x. +>Nelze zjistit jméno jednotky souboru %x. + +Volume name %x is not part of file path %y. +Název disku %x není součástí cesty souboru %y. + +Stop requested: Waiting for current operation to finish... +Zastavování: Čekání na dokončení právě probíhající operace... + +Unable to create timestamp for versioning: +Nelze vytvořit časové značky verzování: + +Cannot read the following XML elements: +Nelze načíst následující XML elementy: + +&Open... +&Otevřít... + +Save &as... +Uložit &jako... + +&Quit +U&končit + +&Program +&Nástroje + +&View help +&Nápověda (pouze anglicky) + +&About +O &Programu + +&Help +&Pomoc + +Usage: +Použití: + +1. Select folders to watch. +1. Vyberte složku ke sledování. + +2. Enter a command line. +2. Zadejte příkazovou řádku. + +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 + +Folders to watch: +Sledovaná složka: + +Add folder +Přidat adresář + +Remove folder +Odstranit adresář + +Browse +Procházet + +Select a folder +Vyberte adresář + +Idle time (in seconds): +Prodleva (v sekundách): + +Idle time between last detected change and execution of command +Prodleva mezi zjištěním poslední změny a spuštěním příkazu + +Command line: +Příkazová řádka: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +Příkaz je spuštěn když: +- dojde ke změně v souboru nebo ve složce +- je zjištěna nová složka (např. vložením USB disku) + + +&Start +&Start + +About +O programu + +Build: %x +Verze: %x + +All files +Všechny soubory + +Automated Synchronization +Automatická synchronizace + +Directory monitoring active +Sledování adresářů je aktivní + +Waiting until all directories are available... +Čekání na nedostupné adresáře... + +Error +Chyba + +&Restore +&Obnovit + +&Show error +&Zobrazit chybu + +&Exit +&Konec + +Incorrect command line: +Neplatný příkaz: + +&Retry +&Opakovat + +File content +Podle obsahu souboru + +File time and size +Podle velikosti a data souboru + +Two way +Databáze + +Mirror +Zrcadlení + +Update +Aktualizace + +Custom +Vlastní + +Multiple... +Různé... + +Moving file %x to %y +Přesouvání souboru %x do %y + +Moving folder %x to %y +Přesouvání adresáře %x do %y + +Moving symbolic link %x to %y +Přesouvání symbolického odkazu %x do %y + +Removing old versions... +Odstraňování starých verzí... + +Creating symbolic link %x +Vytváření symbolického odkazu %x + +Creating folder %x +Vytváření adresáře %x + +Overwriting file %x +Přepisování souboru %x + +Overwriting symbolic link %x +Přepisování symbolického odkazu %x + +Verifying file %x +Kontrola souboru %x + +Updating attributes of %x +Aktualizace atributů souboru %x + +Cannot find %x. +Nelze najít %x. + +Target folder %x already existing. +Cílová složka %x již existuje. + +Target folder input field must not be empty. +Cílová složka nesmí být prázdná. + +Please enter a target folder for versioning. +Prosím zadejte cílovou složku pro verzování. + +Source folder %x not found. +Zdrojovou složku %x nelze najít. + +The following items have unresolved conflicts and will not be synchronized: +Následující položky jsou nevyřešené konflikty a nebudou synchronizovány: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +Nsledující složky se významně liší. Zkontrolujte zda porovnáváte správnou dvojici složek pro synchronizaci. + +Not enough free disk space available in: +Nedostatek místa na disku: + +Required: +Požadováno + +Available: +K dispozici + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Bude změněna složka, která je součástí adresářového páru vícenásobného porovnání. Prosím zkontrolujte si nastavení synchronizace. + +Synchronizing folder pair: +Zpracovávání páru složek: + +Generating database... +Vytváření databáze... + +Creating a Volume Shadow Copy for %x... +Vytváření Stínové kopie pro %x... + +Data verification error: %x and %y have different content. +Chyba porovnání dat: %x má jiný obsah než %y + +job name +název úlohy + +Synchronization stopped +Synchronizace zastavena + +Synchronization completed with errors +Synchronizace dokončena s chybami + +Synchronization completed with warnings +Synchronizace dokončena s varováními + +Nothing to synchronize +Není co synchronizovat + +Synchronization completed successfully +Synchronizace dokončena úspěšně + +Saving log file %x... +Ukládání žurnálu %x... + +You can switch to FreeFileSync's main window to resolve this issue. +K odstranění tohoto problému se přepněte do hlavního okna FreeFileSync. + +&Don't show this warning again +&Nezobrazovat již příště toto varování + +&Ignore +&Pokračovat + +&Switch +&Přepnout + +Switching to FreeFileSync's main window +Přepínání do hlavního okna FreeFileSync + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + + + +&Ignore subsequent errors +Pře&skočit další chyby + +Retrying operation... + + +Serious Error +Závažná chyba + +Check for Program Updates +Hledání aktualizací programu + +A new version of FreeFileSync is available: +Je dostupná novější verze FreeFileSync: + +Download now? +Stáhnout nyní? + +&Download +&Stahování + +FreeFileSync is up to date. +FreeFileSync je aktuální. + +Unable to connect to sourceforge.net. +Není možné se připojit k sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Současná verze FreeFileSync nebyla nalezena online! Chcete verzi zkontrolovat ručně? + +&Check + + +Symlink +Symlink + +Folder +Složka + +Full path +Plná cesta + +Name +Jméno + +Relative path +Relativní cesta + +Base folder +Složka + +Size +Velikost + +Date +Čas + +Extension +Přípona + +Category +Kategorie + +Action +Akce + +Drag && drop +Přetáhni sem && pusť + +Close progress dialog +Zavřít průběh zpracování + +Standby +Přepnout do úsporného režimu + +Log off +Odhlásit uživatele + +Shut down +Vypnout počítač + +Hibernate +Přepnout do režimu spánku + +Alternate comparison settings +Jiné nastavení porovnání + +Alternate synchronization settings +Jiné nastavení synchronizace + +Local filter +Místní filtr + +Active +Zapnutý + +None +Žádný + +Remove alternate settings +Zrušit jiné nastavení + +Clear filter settings +Zrušit nastavení filtru + +Copy +Kopírovat + +Paste +Vložit + +Alternate Comparison Settings +Jiné nastavení porovnání + +Alternate Synchronization Settings +Jiné nastavení synchronizace + +Local Filter +Místní filtr + +&New +&Nový + +&Save +&Uložit + +Save as &batch job... +Uložit jako &dávku + +1. &Compare +1. &Porovnat + +2. &Synchronize +2. &Synchronizovat + +&Global settings +&Nastavení programu + +&Language +&Jazyk + +&Find... +&Najít... + +&Export file list... +&Exportovat seznam souborů... + +&Tools +&Upřesnit + +&Check now +Zkontrolovat &nyní + +Check &automatically once a week +Kontrolovat &automaticky jednou týdně + +&Check for new version +&Zkontrolovat aktualizace + +Compare +Porovnání + +Cancel +Zrušit + +Synchronize +Synchronizace + +Add folder pair +Přidat adresář pro porovnání + +Remove folder pair +Odstranit dvojici adresářů + +Swap sides +Změna stran + +Close search bar +Zavřít hledání + +Find: +Najít: + +Match case +Rozlišovat malá a velká písmena + +Save as batch job +Uložit jako dávku + +Hide excluded items +Skrýt vynechané položky + +Show filtered or temporarily excluded files +Zobrazit filtrované nebo dočasně vynechané soubory + +Number of files and folders that will be created +Počet souborů a složek k vytvoření + +Number of files that will be overwritten +Počet souborů a adresářů k přepsání + +Number of files and folders that will be deleted +Počet souborů a složek ke smazání + +Total bytes to copy +Celkový objem dat + +Select a variant: +Výběr varianty: + +Identify equal files by comparing modification time and size. +Rozpoznat shodné soubory porovnáním jejich data změny a velikosti. + +Identify equal files by comparing the file content. +Rozpoznat shodné soubory porovnáním jejich obsahu. + +Symbolic links: +Symbolickéh odkazy: + +More information +Více informací + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Rozpoznat a provést změny na obou stranách. Odstraněné, přesunuté nebo přejmenované soubory a konflikty budou detekovány automaticky pomocí databáze. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Vytvořit zrcadlovou kopii levé složky tak, aby po synchronizci pravá složka přesně odpovídala levé. + +Copy new and updated files to the right folder. +Kopírovat nové a aktualizované soubory do pravé složky. + +Configure your own synchronization rules. +Nastavení vlastních pravidel synchronizace. + +Detect moved files +Detekce přesunutých souborů + +Requires database files. Not supported by all file systems. +Je nutný soubor databáze. Toto není dostupné na všech systémech. + +Delete files: +Mazání souborů: + +Permanent +Trvale + +Delete or overwrite files permanently +Smazat nebo přepsat soubory trvale + +Recycle bin +Koš + +Back up deleted and overwritten files in the recycle bin +Použít Koš při mazání nebo přepisu souborů + +Versioning +Verzování + +Move files to a user-defined folder +Přesunout soubory do uživatelského adresáře + +Naming convention: +Pojmenování: + +Show examples +Ukázat příklady + +Handle errors: +Zpracování chyb: + +Ignore +Přeskočit + +Hide all error and warning messages +Skrýt všechny chyby a varování + +Pop-up +Hlášení + +Show pop-up on errors or warnings +Zobrazit hlášení při chybě nebo varování + +On completion: +Po dokončení: + +Start synchronization now? +Začít synchronizovat nyní? + +Variant: +Varianta: + +Statistics +Statistika + +&Don't show this dialog again +Tento dialog již &nezobrazovat + +Items found: +Nalezeno položek: + +Speed: +Rychlost: + +Time remaining: +Zbývající čas: + +Time elapsed: +Uplynulý čas: + +Synchronizing... +Synchronizuji... + +Minimize to notification area +Minimalizovat do oznamovací oblasti + +Close +Zavřít + +&Pause +&Pauza + +Stop +Ukončit + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Vytvoří dávkový souboru pro automatickou synchronizaci. Ke spuštění dávky jednoduše poklikejte na vytvořený soubor nebo využijte plánovač úloh vašeho systému: %x + +Stop synchronization at first error +Ukončit synchronizaci při první chybě + +Show progress dialog +Zobrazit průběh zpracování + +Save log: +Uložit žurnál: + +Limit: +Omezení: + +Limit maximum number of log files +Omezení maximálního počtu žurnálů + +How can I schedule a batch job? +Jak nastavit spouštění dávky? + +&Recycle bin +&Koš + +Delete on both sides +Smazat z obou stran + +Delete on both sides even if the file is selected on one side only +Smazat na obou stranách i když je soubor vybrán pouze na jedné z nich + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Nastavte pravidla filtru pro vynechání některých souborů ze synchronizace. Zadávajte cesty relativně k odpovídající složce. + +Include: +Zahrnout: + +Exclude: +Vynechat: + +Time span: +Časové rozmezí: + +File size: +Velikost soubor: + +Minimum: +Od: + +Maximum: +Do: + +&Clear +&Smazat + +The following settings are used for all synchronization jobs. +Toto nastavení je platné pro všechny synchronizační úlohy. + +Fail-safe file copy +Bezpečné kopírování souborů + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + +Kopírovat nejprve do pomocného souboru (*.ffs_tmp) před přepisem cíle. Tento postup zajišťuje bezpečné chování i v případě závažné chyby. + +(recommended) +(doporučeno) + +Copy locked files +Kopírovat zamčené soubory + +Copy shared or locked files using the Volume Shadow Copy Service. +Kopírovat sdílené nebo zamčené soubory pomocí služby Stínové kopie + +(requires administrator rights) +(vyžaduje administrátorská oprávnění) + +Copy file access permissions +Kopírovat přístupová oprávnění k souborům + +Transfer file and folder permissions. +Přenést přístupová oprávnění souborů a složek + +Automatic retry on error: +Automaticky opakovat při chybě: + +Retry count: +Počet opakování: + +Delay (in seconds): +Prodleva (v sekundách): + +Customize context menu: +Přizpůsobit kontextovou nabídku: + +Description +Popis + +Restore hidden windows +Obnovit skryté dialogy + +&Default +&Předdefinované + +Source code written in C++ using: +Zdrojový kód byl napsán kompletně v C++ pomocí: + +If you like FreeFileSync +Pokud se Vám FreeFileSync líbí + +Donate with PayPal +Přispět pomocí PayPal + +Feedback and suggestions are welcome +Komentáře a náměty jsou vždy vítány + +Homepage +Navštivte + +Email +Napište + +Published under the GNU General Public License +Vydáno pod GNU General Public License (GPL) + +Many thanks for localization: +Poděkování za překlad FreeFileSync: + +Save as Batch Job +Uložit jako dávku + +Delete Items +Smazané položky + +Global Settings +Nastavení programu + +Select Time Span +Časové rozmezí + +Folder Pairs +Adresářové páry + +Find +Najít + +Overview +Přehled + +Configuration +Konfigurace + +Main Bar +Hlavní lišta + +Filter Files +Filtr souborů + +Select View +Zobrazení + +Open... +Otevřít... + +Save +Uložit + +Compare both sides +Porovnat obě strany + +Comparison settings +Nastavení porovnání + +Synchronization settings +Nastavení synchronizace + +Start synchronization +Start synchronizace + +Confirm +Potvrdit + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + + + +&Execute +&Spustit + + +1 directory +%x directories + + +1 adresář +%x adresáře +%x adresářů + + + +1 file +%x files + + +1 soubor +%x soubory +%x souborů + + + +%y of 1 row in view +%y of %x rows in view + + +%y z 1 řádku +%y z %x řádků +%y z %x řádků + + +Set direction: +Nastavit adresář: + +multiple selection +vícenásobný výběr + +Include via filter: +Zahrnout pomocí filtru: + +Exclude via filter: +Vynechat pomocí filtru: + +Exclude temporarily +Vynechat dočasně + +Include temporarily +Přidat dočasně + +Delete +Smazat + +Include all +Zahrnout vše + +Exclude all +Vynechat vše + +Show icons: +Ikony: + +Small +Malé + +Medium +Střední + +Large +Velké + +Select time span... +Zadejte časové rozmezí... + +Default view +Výchozí zobrazení + +Show "%x" +Zobrazit "%x" + +Last session +Poslední sezení + +Folder Comparison and Synchronization +porovnání a synchronizace složek + +Configuration saved +Konfigurace uložena + +FreeFileSync batch +FreeFileSync dávka + +Do you want to save changes to %x? +Uložit změny do %x? + +Never save &changes +Nikdy &neukládat změny + +Do&n't save +&Neukládat + +Filter +Filtr + +Show files that exist on left side only +Zobrazit soubory existující pouze vlevo + +Show files that exist on right side only +Zobrazit soubory existující pouze vpravo + +Show files that are newer on left +Zobrazit soubory novější vlevo + +Show files that are newer on right +Zobrazit soubory novější vpravo + +Show files that are equal +Zobrazit shodné soubory + +Show files that are different +Zobrazit rozdílené soubory + +Show conflicts +Zobrazit konflikty + +Show files that will be created on the left side +Zobrazit soubory, které budou vlevo vytvořeny + +Show files that will be created on the right side +Zobrazit soubory, které budou vpravo vytvořeny + +Show files that will be deleted on the left side +Zobrazit soubory, které budou vlevo smazány + +Show files that will be deleted on the right side +Zobrazit soubory, které budou vpravo smazány + +Show files that will be overwritten on left side +Zobrazit soubory, které budou vlevo přepsány + +Show files that will be overwritten on right side +Zobrazit soubory, které budou vpravo přepsány + +Show files that won't be copied +Zobrazit soubory, které nebudou kopírovány + +Set as default +Nastavit jako výchozí + +All folders are in sync +Všechny složky jsou synchronizovány + +Synchronization Settings +Nastavení synchronizace + +Comparison Settings +Nastavení porovnání + +Cannot find %x +Nelze najít %x + +Comma-separated values +Text oddělený čárkami + +File list exported +Seznam souborů exportován + +Searching for program updates... +Hledání aktualizací programu ... + +Scanning... +Zpracovávání... + +Comparing content... +Porovnávání obsahu... + +Info +Info + +Warning +Varování + +Paused +Pauza + +Initializing... +Inicializace... + +Stopped +Zastaveno + +Completed +Hotovo + +&Continue +&Pokračovat + +Log +Záznam zpracování + +Today +Dnes + +This week +Tento týden + +This month +Tento měsíc + +This year +Tento rok + +Last x days +dní zpětně + +Byte +Byte + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Opravdu chcete přesunout následující položku do Koše? +Opravdu chcete přesunout následující %x položky do Koše? +Opravdu chcete přesunout následujících %x položek do Koše? + + +Move +Přesunout + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Opravdu chcete smazat následující položku? +Opravdu chcete smazat následující %x položky? +Opravdu chcete smazat následujících %x položek? + + +Exclude +Vynechat + +Direct +Zachovat + +Follow +Použít cíl + +Copy NTFS permissions +Kopírovat oprávnění NTFS + +Integrate external applications into context menu. The following macros are available: +Integrace externí aplikace do kontextového menu. K dispozici jsou následující makra: + +- full file or folder name +- celá cesta nebo jméno souboru + +- folder part only +- pouze cesta + +- Other side's counterpart to %item_path% +- celá cesta nebo jméno z opačného panelu pro %item_path% + +- Other side's counterpart to %item_folder% +- pouze cesta z opačného panelu pro %item_folder% + +Restore all hidden windows and warnings? +Obnovit všechny skryté dialogy a varování? + +Leave as unresolved conflict +Ponechat jako nevyřešený konflikt + +Replace +Nahradit + +Move files and replace if existing +Přesunout a nahradit pokud soubory existují + +Time stamp +Časová značka + +Append a timestamp to each file name +Přidat časovou značku ke jménu souboru + +File +Soubor + +YYYY-MM-DD hhmmss +RRRR-MM-DD hhmmss + +Files +Soubory + +Items +Položky + +Percentage +Procentní podíl + +Cannot monitor directory %x. +Nelze nastavit monitorování adresáře %x. + +Conversion error: +Chyba konverze: + +Cannot delete file %x. +Nelze smazat soubor %x. + +The file is locked by another process: +Soubor je uzamčen jiným procesem: + +Cannot move file %x to %y. +Nelze přesunout soubor %x do %y. + +Cannot delete directory %x. +Nelze smazat adresář %x. + +Cannot write file attributes of %x. +Nelze zapsat atributy souboru %x. + +Cannot write modification time of %x. +Nelze nastavit atribut času změny pro %x. + +Cannot read security context of %x. +Nelze číst přístupová práva pro %x. + +Cannot write security context of %x. +Nelze zapsat přístupová práva pro %x. + +Cannot read permissions of %x. +Nelze číst oprávnění pro %x. + +Cannot write permissions of %x. +Nelze zapsat oprávnění pro %x. + +Cannot create directory %x. +Nelze vytvořit adresář %x. + +Cannot create symbolic link %x. +Nelze vytvořit zástupce %x. + +Cannot find system function %x. +Nelze najít systémovou funkci %x. + +Cannot copy file %x to %y. +Nelze kopírovat soubor %x do %y. + +Type of item %x is not supported: +Typ položky %x není podporován: + +Cannot resolve symbolic link %x. +Nelze najít odkaz zástupce %x. + +Cannot open directory %x. +Nelze otevřít adresář %x. + +Cannot enumerate directory %x. +Nelze procházet adresář %x. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +1 minuta +%x minuty +%x minut + + + +1 hour +%x hours + + +1 hodina +%x hodiny +%x hodin + + + +1 day +%x days + + +1 den +%x dny +%x dnů + + +Unable to register to receive system messages. +Nepodařilo se zaregistrovat k odběru systémových zpráv. + +Cannot set privilege %x. +Nelze nastavit práva pro %x. + +Unable to suspend system sleep mode. +Nelze pozastavit Režim spánku. + +Cannot change process I/O priorities. +Nelze nastavit priority procesu. + +Unable to move %x to the recycle bin. +Není možné přesunout %x do Koše. + +Cannot determine final path for %x. +Nelze určit výslednou cestu pro %x. + +Error Code %x: +Chybový kód %x + diff --git a/FreeFileSync/Build/Languages/danish.lng b/FreeFileSync/Build/Languages/danish.lng new file mode 100644 index 00000000..092686cd --- /dev/null +++ b/FreeFileSync/Build/Languages/danish.lng @@ -0,0 +1,1510 @@ +
    + Dansk + Regmos + da_DK + flag_denmark.png + 2 + n == 1 ? 0 : 1 +
    + +Both sides have changed since last synchronization. +Begge sider ændret siden sidste synkronisering. + +Cannot determine sync-direction: +Kan ikke bestemme retning: + +No change since last synchronization. +Ingen ændringer siden sidste synkronisering. + +The database entry is not in sync considering current settings. +Databaseemnet er ikke i synk med de aktuelle indstillinger. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Sætter standardretning: Gamle filer overskrives med nyere. + +Checking recycle bin availability for folder %x... +Tjekker papirkurvs tilgængelighed for mappen %x... + +Moving file %x to the recycle bin +Flytter filen %x til papirkurv + +Moving folder %x to the recycle bin +Flytter mappen %x til papirkurv + +Moving symbolic link %x to the recycle bin +Flytter symbolsk link %x til papirkurv + +Deleting file %x +Sletter filen %x + +Deleting folder %x +Sletter mappen %x + +Deleting symbolic link %x +Sletter symlink %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +Papirkurven kan ikke bruges til følgende mapper. Filerne slettes permanent: + +An exception occurred +Undtagelse opstod + +A directory path is expected after %x. +Der forventes en mappestil efter %x. + +Syntax error +Syntaksfejl + +Cannot open file %x. +Filen %x kan ikke åbnes. + +File %x does not contain a valid configuration. +Filen %x indeholder ikke gyldige indstillinger. + +Unequal number of left and right directories specified. +Der er angivet et ulige antal højre og venstre mapper. + +The config file must not contain settings at directory pair level when directories are set via command line. +Indstillingsfilen må ikke indeholde indstillinger på mappepar niveau når mapper er sat via kommando. + +Directories cannot be set for more than one configuration file. +Mapper kan ikke sættes til mere end en indstillingsfil. + +Command line +Kommando + +Syntax: +Syntaks: + +config files +indstillingsfiler + +directory +mappe + +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 directories for at most one config file. +Vilkårligt antal alternative mappe til højst en indstillingsfil + +A folder input field is empty. +Der er ikke valgt nogen mapper. + +The corresponding folder will be considered as empty. +Den tilsvarende mappe betragtes som tom. + +Cannot find the following folders: +Kan ikke finde følgende mapper: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Ignorer denne fejl for at betragte hver mappe som tom. Mapperne bliver så automatisk oprettet ved synkronisering. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +Følgende mapper har afhængige stier. Opsæt synkroniseringsreglerne med omhu: + +File %x has an invalid date. +Filen %x har en ugyldig dato. + +Date: +Dato: + +Files %x have the same date but a different size. +Filerne %x har den samme dato men forskellig størrelse. + +Size: +Størrelse: + +Items differ in attributes only +Enhederne har kun attributter til forskel + +Resolving symbolic link %x +Løser symbolsk link %x + +Comparing content of files %x +Sammenligner indhold af filer %x + +Generating file list... +Opretter filliste... + +Starting comparison +Starter analyse + +Calculating sync directions... +Forbereder synkronisering... + +Out of memory. +Ikke nok hukommelse. + +Item exists on left side only +Emnet findes kun på venstre side + +Item exists on right side only +Emnet findes kun på højre side + +Left side is newer +Venstre er nyest + +Right side is newer +Højre er nyest + +Items have different content +Emnerne har forskelligt indhold + +Both sides are equal +Begge sider er ens + +Conflict/item cannot be categorized +Konflikt/ukendt emne + +Copy new item to left +Kopier nyt emne mod venstre + +Copy new item to right +Kopier nyt emne mod højre + +Delete left item +Slet emne til venstre + +Delete right item +Slet emne til højre + +Move file on left +Flyt filen til venstre + +Move file on right +flyt filen til højre + +Overwrite left item +Overskriv venstre emne + +Overwrite right item +Overskriv højre emne + +Do nothing +Gør intet + +Update attributes on left +Opdater attributter mod venstre + +Update attributes on right +Opdater attributter mod højre + +Database file %x is incompatible. +Databasefilen %x er inkompatibel. + +Initial synchronization: +Forbereder synkronisering: + +Database file %x does not yet exist. +Databasefilen %x findes ikke. + +Database file is corrupt: +Databasefilen er ødelagt: + +Cannot write file %x. +Kan ikke oprette filen %x. + +Cannot read file %x. +Kan ikke læse filen %x. + +Database files do not share a common session. +Databasefiler kan ikke dele handling. + +Searching for folder %x... +Søger efter mappen %x... + +Cannot read file attributes of %x. +Kan ikke læse filattributterne på %x. + +Cannot get process information. +Kan ikke hente procesinformation. + +Waiting while directory is locked (%x)... +Venter mens mappe låses (%x)... + + +1 sec +%x sec + + +1 sek +%x sek + + +Creating file %x +Opretter filen %x + +Items processed: +Emner behandlet: + +Items remaining: +Emner tilbage: + +Total time: +Samlet tid: + + +1 byte +%x bytes + + +1 byte +%x byte + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Behandlingsfejl i filen %x, række %y, kolonne %z. + +Cannot set directory lock for %x. +Kan ikke låse mappen %x. + +Scanning: +Skanner: + + +1 thread +%x threads + + +1 tråd +%x tråde + + +Encoding extended time information: %x +Opretter udvidet tidsinformation: %x + +/sec +/sek + +%x items/sec +%x emner/sek + +Configuration file %x loaded partially only. +Indstillingsfilen %x er kun delvist indlæst. + +Show in Explorer +Åben filplacering + +Open with default application +Åben med standardprogram + +Browse directory +Gennemse mappe + +Cannot access the Volume Shadow Copy Service. +VSS tjenesten er ikke tilgængelig + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Brug FreeFileSync 64-bit til at lave VSS kopier på dette system. + +Cannot load file %x. +Kan ikke indlæse filen %x. + +Cannot determine volume name for %x. +Kan ikke bestemme volumennavn for %x. + +Volume name %x is not part of file path %y. +Volumennavnet %x er ikke del af filstien %y. + +Stop requested: Waiting for current operation to finish... +Afbrydelse: Venter på aktuel opgave afsluttes... + +Unable to create timestamp for versioning: +Kunne ikke oprette versioneringstid: + +Cannot read the following XML elements: +Kan ikke læse følgende XML emner: + +&Open... +&Åben... + +Save &as... +Gem &som... + +&Quit +&Afslut + +&Program +&Filer + +&View help +&Åben hjælp + +&About +&Om + +&Help +&Hjælp + +Usage: +Gør sådan: + +1. Select folders to watch. +1. Vælg mapper til jobbet. + +2. Enter a command line. +2. Angiv en kommando. + +3. Press 'Start'. +3. Klik 'Start'. + +To get started just import a .ffs_batch file. +Importer en .ffs_batchfil (Filer > Åben...) for at komme igang. + +Folders to watch: +Jobbets mapper: + +Add folder +Tilføj mappe + +Remove folder +Fjern mappe + +Browse +Gennemse + +Select a folder +Vælg en mappe + +Idle time (in seconds): +Efter PC tomgang i (sek) + +Idle time between last detected change and execution of command +Tid imellem sidst fundne ændring og udførsel + +Command line: +Kommando: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +Kommandoen udføres hvis: +- filer eller undermapper ændres +- nye mapper findes (f.eks USB nøgle) + + +&Start +&Start + +About +Om + +Build: %x +Udgivet: %x + +All files +Alle filer + +Automated Synchronization +Automatisk synkronisering + +Directory monitoring active +Mappeovervågning aktiv + +Waiting until all directories are available... +Venter til alle mapper er tilgængelige... + +Error +Fejl + +&Restore +&Vis vindue + +&Show error +Vi&s fejl + +&Exit +&Luk + +Incorrect command line: +Ugyldig kommando: + +&Retry +&Prøv igen + +File content +Indhold + +File time and size +Størrelse og tid + +Two way +Tovejs + +Mirror +Spejling + +Update +Opdater + +Custom +Tilpasset + +Multiple... +Flere... + +Moving file %x to %y +Flytter filen %x til %y + +Moving folder %x to %y +Flytter mappen %x til %y + +Moving symbolic link %x to %y +Flytter symlinket %x til %y + +Removing old versions... +Fjerner gamle udgaver... + +Creating symbolic link %x +Opretter symlinket %x + +Creating folder %x +Opretter mappen %x + +Overwriting file %x +Overskriver filen %x + +Overwriting symbolic link %x +Overskriver symlinket %x + +Verifying file %x +Verificerer filen %x + +Updating attributes of %x +Opdaterer attributter for %x + +Cannot find %x. +Kan ikke finde %x. + +Target folder %x already existing. +Destinationsmappen %x findes allerede. + +Target folder input field must not be empty. +Destinationsmappen skal angives. + +Please enter a target folder for versioning. +Angiv mappe til versionering. + +Source folder %x not found. +Kildemappen %x blev ikke fundet. + +The following items have unresolved conflicts and will not be synchronized: +Følgende emner har uløste konflikter og synkroniseres ikke: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +Følgende mapper har markante forskelle. Kontroller at du synkroniserer de rigtige mapper + +Not enough free disk space available in: +Ikke nok ledig diskplads på: + +Required: +Krævet: + +Available: +Tilgængeligt: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Der ændres en mappe der tilhører flere mappepar. Kontroller dine synkroniseringsindstillinger. + +Synchronizing folder pair: +Synkroniserer mappepar: + +Generating database... +Opretter database... + +Creating a Volume Shadow Copy for %x... +Oprette VSS kopi for %x... + +Data verification error: %x and %y have different content. +Godkendelsesfejl: %x og %y har forskelligt indhold + +job name +Jobnavn + +Synchronization stopped +Synkronisering afbrudt + +Synchronization completed with errors +Synkronisering gennemført med fejl + +Synchronization completed with warnings +Synkronisering gennemført med advarsel + +Nothing to synchronize +Alt er synkroniseret + +Synchronization completed successfully +Synkronisering gennemført + +Saving log file %x... +Gemmer rapport %x... + +You can switch to FreeFileSync's main window to resolve this issue. +Skift til FreeFileSyncs hovedvindue for at løse problemet. + +&Don't show this warning again +&Vis ikke igen + +&Ignore +&Ignorer + +&Switch +&Skift + +Switching to FreeFileSync's main window +Skifter til hovedvinduet + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + + + +&Ignore subsequent errors +&Ignorer efterfølgende fejl + +Retrying operation... + + +Serious Error +Kritisk fejl + +Check for Program Updates +Søg efter opdatering + +A new version of FreeFileSync is available: +Opdatering tilgængelig: + +Download now? +Download nu? + +&Download +&Download + +FreeFileSync is up to date. +FreeFileSync er opdateret. + +Unable to connect to sourceforge.net. +Kan ikke kontakte sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Kunne ikke finde FreeFileSync's versionsnummer online. Vil du kontrollere manuelt? + +&Check + + +Symlink +Symlink + +Folder +Mappe + +Full path +Fuld sti + +Name +Navn + +Relative path +Relativ sti + +Base folder +Hovedmappe + +Size +Størrelse + +Date +Dato + +Extension +Filtype + +Category +Status + +Action +Handling + +Drag && drop +Træk emner hertil + +Close progress dialog +Luk dialogen + +Standby +Standby + +Log off +Log af + +Shut down +Luk ned + +Hibernate +Dvale + +Alternate comparison settings +Tilpas analyse + +Alternate synchronization settings +Tilpas synkronisering + +Local filter +Lokalt filter + +Active +Aktiv + +None +Ingen + +Remove alternate settings +Fjern tilpassede indstillinger + +Clear filter settings +Nulstil filter + +Copy +Kopiér + +Paste +Indsæt + +Alternate Comparison Settings +Tilpas analyse + +Alternate Synchronization Settings +Tilpas synkronisering + +Local Filter +Lokalt filter + +&New +&Ny + +&Save +&Gem + +Save as &batch job... +Gem som &batchfil + +1. &Compare +1. &Analyser + +2. &Synchronize +2. &Synkroniser + +&Global settings +&Programindstillinger + +&Language +&Sprog + +&Find... +S&øg + +&Export file list... +&Eksporter filliste... + +&Tools +&Værktøj + +&Check now +Søg &nu + +Check &automatically once a week +Søg &automatisk en gang om ugen + +&Check for new version +Søg &efter opdatering + +Compare +Analysér + +Cancel +Annuller + +Synchronize +Synkronisér + +Add folder pair +Tilføj mappepar + +Remove folder pair +Fjern mappepar + +Swap sides +Byt side + +Close search bar +Luk søgelinie + +Find: +Søg: + +Match case +Versalfølsom (a/A) + +Save as batch job +Gem som batchfil + +Hide excluded items +Skjul ekskluderede emner + +Show filtered or temporarily excluded files +Vis filtrerede eller midlertidigt ekskluderede filer + +Number of files and folders that will be created +Antal filer og mapper der oprettes + +Number of files that will be overwritten +Antal filer der overskrives + +Number of files and folders that will be deleted +Antal filer og mapper der slettes + +Total bytes to copy +Antal bytes der kopieres + +Select a variant: +Vælg metode: + +Identify equal files by comparing modification time and size. +Genkend ens filer efter filtid og størrelse. + +Identify equal files by comparing the file content. +Genkend ens filer efter indhold. + +Symbolic links: +Symbolske link: + +More information +Mere information + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Find og udbred ændringer på begge sider. Sletninger, omdøbninger og konflikter findes automatisk i en database. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Spejl venstre mappe. Efter synkronisering er højre mappe en kopi af venstre. + +Copy new and updated files to the right folder. +Kopier nye og opdaterede filer til højre mappe. + +Configure your own synchronization rules. +Opret dine egne synkroniseringsregler. + +Detect moved files +Genkend flyttede filer + +Requires database files. Not supported by all file systems. +Kræver databasefiler. Ikke understøttet i alle filsystemer + +Delete files: +Filsletning: + +Permanent +Permanent + +Delete or overwrite files permanently +Slet eller overskriv filer permanent + +Recycle bin +Papirkurv + +Back up deleted and overwritten files in the recycle bin +Backup slettede og overskrevne filer i papirkurven + +Versioning +Versionering + +Move files to a user-defined folder +Flyt filer til brugerdefineret mappe + +Naming convention: +Navneregler: + +Show examples +Vis eksempler + +Handle errors: +Fejlhåndtering: + +Ignore +Ignorer + +Hide all error and warning messages +Skjul fejlbeskeder og advarsler + +Pop-up +Besked + +Show pop-up on errors or warnings +Vis fejlbeskeder og advarsler + +On completion: +Ved gennemført: + +Start synchronization now? +Synkroniser nu? + +Variant: +Jobtype: + +Statistics +Statistik + +&Don't show this dialog again +&Vis ikke igen + +Items found: +Emner fundet: + +Speed: +Hastighed: + +Time remaining: +Resterende tid: + +Time elapsed: +Brugt tid: + +Synchronizing... +Synkroniserer... + +Minimize to notification area +Minimér til uret + +Close +Luk + +&Pause +&Pause + +Stop +Stop + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Opret batchfil til automatisk synkronisering. Start ved at dobbeltklikke på filen eller planlæg via opgavestyring: %x + +Stop synchronization at first error +Stop synkronisering ved første fejl + +Show progress dialog +Vis fremskridt + +Save log: +Gem log: + +Limit: +Højst: + +Limit maximum number of log files +Begræns antal rapporter + +How can I schedule a batch job? +Hvordan oprettes en batchfil? + +&Recycle bin +&Papirkurv + +Delete on both sides +Slet på begge sider + +Delete on both sides even if the file is selected on one side only +Slet på begge sider selvom selvom filen kun er valgt på en side + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Angiv filterregler til ekskludering af bestemte filer fra synkroniseringen. Filstier skal relatere til kildemapperne. + +Include: +Inkludér: + +Exclude: +Ekskludér: + +Time span: +Interval: + +File size: +Filstørrelse: + +Minimum: +Minimum: + +Maximum: +Maksimum: + +&Clear +&Ryd + +The following settings are used for all synchronization jobs. +Disse indstillinger gælder alle synkroniseringer. + +Fail-safe file copy +Sikker filkopiering + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + +Kopiér til midlertidig fil (*.ffs_tmp) før overskrivning. Sikrer processen ved alvorlige fejl. + +(recommended) +(anbefalet) + +Copy locked files +Kopier låste filer + +Copy shared or locked files using the Volume Shadow Copy Service. +Kopiér delte eller låste filer med VSS kopiering. + +(requires administrator rights) +(administrator) + +Copy file access permissions +Kopier adgangstilladelser + +Transfer file and folder permissions. +Overfør fil og mappetilladelser. + +Automatic retry on error: +Antal forsøg ved fejl: + +Retry count: +Antal forsøg: + +Delay (in seconds): +Forsinkelse (sek): + +Customize context menu: +Tilpas kontekstmenu: + +Description +Beskrivelse + +Restore hidden windows +Gendan skjulte vinduer + +&Default +S&tandard + +Source code written in C++ using: +Kildekoden er skrevet i C++ med hjælp fra: + +If you like FreeFileSync +Er du glad for FreeFileSync + +Donate with PayPal +Donér med PayPal + +Feedback and suggestions are welcome +Kritik og forslag er meget velkomne + +Homepage +Hjemmeside + +Email +Email + +Published under the GNU General Public License +Udgivet under GNU General Public Licence + +Many thanks for localization: +Tak for oversættelse: + +Save as Batch Job +Gem som batchfil + +Delete Items +Slet emner + +Global Settings +Programindstillinger + +Select Time Span +Vælg tidsinterval + +Folder Pairs +Mappepar + +Find +Søg + +Overview +Oversigt + +Configuration +Indstilling + +Main Bar +Hovedlinie + +Filter Files +Filtrer filer + +Select View +Tilpas visning + +Open... +Åben... + +Save +Gem + +Compare both sides +Analyser begge sider + +Comparison settings +Analyseindstillinger + +Synchronization settings +Synkroniseringsindstillinger + +Start synchronization +Start synkronisering + +Confirm +Bekræft + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + + + +&Execute +&Udfør + + +1 directory +%x directories + + +1 mappe +%x mapper + + + +1 file +%x files + + +1 fil +%x filer + + + +%y of 1 row in view +%y of %x rows in view + + +%y af 1 række i visning +%y af %x rækker i visning + + +Set direction: +Retning: + +multiple selection +vælg flere + +Include via filter: +Inkludér via filter: + +Exclude via filter: +Ekskluder m. filter: + +Exclude temporarily +Ekskluder midlertidigt + +Include temporarily +Inkluder midlertidigt + +Delete +Slet + +Include all +Vælg alle + +Exclude all +Fravælg alle + +Show icons: +Vis ikoner: + +Small +Små + +Medium +Medium + +Large +Store + +Select time span... +Vælg tidsinterval... + +Default view +Standardvisning + +Show "%x" +Vis "%x" + +Last session +Sidste opgave + +Folder Comparison and Synchronization +Mappeanalyse og synkronisering + +Configuration saved +Indstillinger gemt + +FreeFileSync batch +FreeFileSync batchfil + +Do you want to save changes to %x? +Vil du gemme ændringer i %x? + +Never save &changes +Gem &aldrig ændringer + +Do&n't save +&Gem ikke + +Filter +Filter + +Show files that exist on left side only +Vis filer der kun findes på venstre side + +Show files that exist on right side only +Vis filer der kun findes på højre side + +Show files that are newer on left +Vis nyere filer på venstre side + +Show files that are newer on right +Vis nyere filer på højre side + +Show files that are equal +Vis ens filer + +Show files that are different +Vis uens filer + +Show conflicts +Vis konflikter + +Show files that will be created on the left side +Vis filer der oprettes på venstre side + +Show files that will be created on the right side +Vis filer der oprettes på højre side + +Show files that will be deleted on the left side +Vis filer der slettes på venstre side + +Show files that will be deleted on the right side +Vis filer der slettes på højre side + +Show files that will be overwritten on left side +Vis filer der overskrives på venstre side + +Show files that will be overwritten on right side +Vis filer der overskrives på højre side + +Show files that won't be copied +Vis filer der ikke kopieres + +Set as default +Sæt som standard + +All folders are in sync +Alle mapper er synkrone + +Synchronization Settings +Synkroniseringsindstillinger + +Comparison Settings +Analyseindstillinger + +Cannot find %x +Kan ikke finde %x + +Comma-separated values +Kommaopdelte værdier + +File list exported +Fillisten blev eksporteret + +Searching for program updates... +Søger efter opdatering... + +Scanning... +Skanner... + +Comparing content... +Analyserer indhold... + +Info +Info + +Warning +Advarsel + +Paused +Pauset + +Initializing... +Forbereder... + +Stopped +Afbrudt + +Completed +Gennemført + +&Continue +&Fortsæt + +Log +Log + +Today +Idag + +This week +Denne uge + +This month +Denne måned + +This year +Dette år + +Last x days +Sidste x dage + +Byte +Byte + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Vil du flytte følgende emne til papirkurven? +Vil du flytte følgende %x emner til papirkurven? + + +Move +Flyt + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Vil du slette følgende emne? +Vil du slette følgende %x emner? + + +Exclude +Ekskludér + +Direct +Direkte + +Follow +Følg + +Copy NTFS permissions +Kopier NTFS tilladelser + +Integrate external applications into context menu. The following macros are available: +Integrer eksterne programmer i kontekstmenu. Brug følgende macroer: + +- full file or folder name +Komplet fil- eller mappenavn + +- folder part only +Kun mappedelen + +- Other side's counterpart to %item_path% +Modsat sides pendant til %item_path% + +- Other side's counterpart to %item_folder% +Modsat sides pendant til %item_folder% + +Restore all hidden windows and warnings? +Gendan skjulte vinduer og advarsler? + +Leave as unresolved conflict +Efterlad som uløst konflikt + +Replace +Erstat + +Move files and replace if existing +Flyt fil og erstat eventuelt eksisterende + +Time stamp +Tidsstempel + +Append a timestamp to each file name +Føj tidsstempel til hvert filnavn + +File +Fil + +YYYY-MM-DD hhmmss +YYYY-MM-DD hhmmss + +Files +Filer + +Items +Emner + +Percentage +Procent + +Cannot monitor directory %x. +Kan ikke overvåge mappen %x. + +Conversion error: +Konverteringsfejl: + +Cannot delete file %x. +Kan ikke slette filen %x. + +The file is locked by another process: +Filen er låst af en anden process: + +Cannot move file %x to %y. +Kan ikke flytte filen %x til %y. + +Cannot delete directory %x. +Kan ikke slette mappen %x. + +Cannot write file attributes of %x. +Kan ikke skrive filattributter til %x. + +Cannot write modification time of %x. +Kan ikke opdatere tidsændring på %x. + +Cannot read security context of %x. +Kan ikke læse sikkerhedsindstillinger på %x. + +Cannot write security context of %x. +Kan ikke skrive sikkerhedsindstillinger til %x. + +Cannot read permissions of %x. +Kan ikke læse tilladelserne på %x. + +Cannot write permissions of %x. +Kan ikke skrive tilladelserne til %x. + +Cannot create directory %x. +Kan ikke oprette mappen %x. + +Cannot create symbolic link %x. +Kan ikke oprette symbolsk link %x. + +Cannot find system function %x. +Kan ikke finde systemfunktionen %x. + +Cannot copy file %x to %y. +Kan ikke kopiere filen %x til %y. + +Type of item %x is not supported: +Filtypen %x understøttes ikke: + +Cannot resolve symbolic link %x. +Kan ikke følge symlinket %x. + +Cannot open directory %x. +Kan ikke åbne mappen %x. + +Cannot enumerate directory %x. +Kan ikke optælle mappen %x. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +1 min +%x min + + + +1 hour +%x hours + + +1 time +%x timer + + + +1 day +%x days + + +1 dag +%x dage + + +Unable to register to receive system messages. +Kunne ikke registrere modtagelse af systembeskeder. + +Cannot set privilege %x. +Kan ikke sætte %x privilegier. + +Unable to suspend system sleep mode. +Kunne ikke ophæve standby. + +Cannot change process I/O priorities. +Kan ikke ændre I/O prioriteter. + +Unable to move %x to the recycle bin. +Kunne ikke flytte %x til papirkurv. + +Cannot determine final path for %x. +Kan ikke bestemme endelig sti for %x + +Error Code %x: +Fejlkode %x: + diff --git a/FreeFileSync/Build/Languages/dutch.lng b/FreeFileSync/Build/Languages/dutch.lng new file mode 100644 index 00000000..e5bd8c9e --- /dev/null +++ b/FreeFileSync/Build/Languages/dutch.lng @@ -0,0 +1,1510 @@ +
    + Nederlands + Edwin Dierssen + nl_NL + flag_holland.png + 2 + n == 1 ? 0 : 1 +
    + +Both sides have changed since last synchronization. +Beide zijdes zijn veranderd sinds de laatste synchronisatie. + +Cannot determine sync-direction: +Kan de synchronisatie-richting niet bepalen: + +No change since last synchronization. +Geen veranderingen sinds de laatste synchronisatie. + +The database entry is not in sync considering current settings. +De database is niet gesynchroniseerd volgens de huidige instellingen. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Stel standaard synchronisatie richtingen in: Oude bestanden worden door nieuwere bestanden overschreven. + +Checking recycle bin availability for folder %x... +Prullebak beschikbaarheid voor map %x te controleren... + +Moving file %x to the recycle bin +Bezig met verplaatsen van bestand %x naar de prullenbak + +Moving folder %x to the recycle bin +Bezig met verplaatsen van map %x naar de prullenbak + +Moving symbolic link %x to the recycle bin +Bezig met verplaatsen van snelkoppeling %x naar de prullenbak + +Deleting file %x +Verwijderen van bestand %x + +Deleting folder %x +Verwijderen van map %x + +Deleting symbolic link %x +Verwijderen van snelkoppeling %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +De prullenbak is niet beschikbaar voor de volgende mappen. Bestanden zullen permanent verwijderd worden: + +An exception occurred +Er heeft een uitzondering plaatsgevonden + +A directory path is expected after %x. +Een bestandslocatie pad is verwacht na %x. + +Syntax error +Syntax fout + +Cannot open file %x. +Kan bestand %x niet openen. + +File %x does not contain a valid configuration. +Bestand %x bevat geen valide configuratie. + +Unequal number of left and right directories specified. +Oneven nummer van linker en rechter bestandslocaties gespecificeerd. + +The config file must not contain settings at directory pair level when directories are set via command line. +Het configuratiebestand mag geen instellingen bevatten voor een bestandslocatie level als bestandslocaties in de command line ingesteld zijn. + +Directories cannot be set for more than one configuration file. +Bestandslocaties kunnen niet voor meer dan een configuratiebestand gemaakt worden. + +Command line +Opdrachtregel + +Syntax: +Syntax: + +config files +configuratiebestanden + +directory +bestandslocatie + +Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. +Elk aantal FreeFileSync .ffs_gui en/of .ffs_batch configuratiebestanden. + +Any number of alternative directories for at most one config file. +Elk aantal alternatieve bestandslocaties voor een configuratiebestand. + +A folder input field is empty. +Een map invoerveld is leeg. + +The corresponding folder will be considered as empty. +De overeenkomstige map zal als leeg worden beschouwd. + +Cannot find the following folders: +Kan de volgende mappen niet vinden: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +U kunt deze foutmelding negeren om elke map als leeg te markeren. De mappen worden dan automatisch aangemaakt tijdens de synchronisatie. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +De volgende mappen hebben afhankelijke paden. Wees voorzichtig bij het opzetten van synchronisatieregels: + +File %x has an invalid date. +Bestand %x heeft een ongeldige datum. + +Date: +Datum: + +Files %x have the same date but a different size. +Bestanden %x hebben dezelfde datums maar een afwijkende grootte. + +Size: +Grootte: + +Items differ in attributes only +Items verschillen alleen in attributen + +Resolving symbolic link %x +Bezig met vinden van snelkoppeling %x + +Comparing content of files %x +De inhoud van %x bestanden wordt vergeleken + +Generating file list... +Genereren van bestandslijst... + +Starting comparison +Vergelijking starten + +Calculating sync directions... +Synchronisatie richtingen calculeren... + +Out of memory. +Onvoldoende geheugen. + +Item exists on left side only +Item bestaat alleen aan de linkerkant + +Item exists on right side only +Item bestaat alleen aan de rechterkant + +Left side is newer +De linkerkant is nieuwer + +Right side is newer +De rechterkant is nieuwer + +Items have different content +De items hebben een andere inhoud + +Both sides are equal +Beide kanten zijn gelijk + +Conflict/item cannot be categorized +Conflict/item kan niet worden gecategoriseerd + +Copy new item to left +Kopieër nieuw item naar de linkerkant + +Copy new item to right +Kopieër nieuw item naar de rechterkant + +Delete left item +Verwijder linker item + +Delete right item +Verwijder rechter item + +Move file on left +Verplaats bestand aan de linkerkant + +Move file on right +Verplaats bestand aan de rechterkant + +Overwrite left item +Overschrijf linker item + +Overwrite right item +Overschrijf rechter item + +Do nothing +Geen actie ondernemen + +Update attributes on left +Update attributen aan de linkerkant + +Update attributes on right +Update attributen aan de rechterkant + +Database file %x is incompatible. +Databasebestand %x is niet compatibel. + +Initial synchronization: +Initiële synchronisatie: + +Database file %x does not yet exist. +Databasebestand %x bestaat nog niet. + +Database file is corrupt: +Databasebestand is corrupt: + +Cannot write file %x. +Kan bestand %x niet schrijven. + +Cannot read file %x. +Kan bestand %x niet vinden. + +Database files do not share a common session. +Databasebestanden delen geen gezamelijke sessie. + +Searching for folder %x... +Bezig met zoeken naar map %x... + +Cannot read file attributes of %x. +Kan bestandskenmerken van %x niet uitlezen. + +Cannot get process information. +Kan geen procesinformatie verkrijgen. + +Waiting while directory is locked (%x)... +Wachten totdat map is vergrendeld (%x)... + + +1 sec +%x sec + + +1 sec +%x sec + + +Creating file %x +Bestand %x wordt aangemaakt + +Items processed: +Onderdelen verwerkt: + +Items remaining: +Onderdelen te gaan: + +Total time: +Totale tijd: + + +1 byte +%x bytes + + +1 byte +%x bytes + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Fout bij het parsen van bestand %x, rij %y, kolom %z. + +Cannot set directory lock for %x. +Kan locatie %x niet op slot zetten. + +Scanning: +Doorzoekt: + + +1 thread +%x threads + + +1 thread +%x threads + + +Encoding extended time information: %x +Coderen uitgebreide tijdinformatie: %x + +/sec +/sec + +%x items/sec + + +Configuration file %x loaded partially only. +Configuratiebestand %x alleen deels geladen. + +Show in Explorer +Toon in Verkenner + +Open with default application +Open met standaardapplicatie + +Browse directory +Verken map + +Cannot access the Volume Shadow Copy Service. +Kan de Volume Shadow Copy Service niet benaderen. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Gebruik alstublieft de FreeFileSync 64-bit versie om schaduwkopieën te maken op dit systeem. + +Cannot load file %x. +Kan bestand %x niet laden. + +Cannot determine volume name for %x. +Kan volumenaam voor %x niet bepalen. + +Volume name %x is not part of file path %y. +Schijfnaam %x is geen deel van bestandspad %y. + +Stop requested: Waiting for current operation to finish... + + +Unable to create timestamp for versioning: + + +Cannot read the following XML elements: +Kan de volgende XML elementen niet lezen: + +&Open... +&Open... + +Save &as... +Ops&laan als... + +&Quit +&Afsluiten + +&Program +&Programma + +&View help + + +&About +&Over + +&Help +&Help + +Usage: +Gebruik: + +1. Select folders to watch. +1. Selecteer mappen om te bekijken. + +2. Enter a command line. +2. Geef een opdrachtregel in. + +3. Press 'Start'. +3. Klik op 'Start'. + +To get started just import a .ffs_batch file. +Importeer een .ffs_batch bestand om te beginnen. + +Folders to watch: + + +Add folder +Map toevoegen + +Remove folder +Verwijder map + +Browse +Verkennen + +Select a folder +Selecteer een map + +Idle time (in seconds): + + +Idle time between last detected change and execution of command +Tijd tussen de laatste gedetecteerde verandering en de uitvoering van het commando + +Command line: + + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +De opdracht word geactiveerd als: +- bestanden of subfolders veranderen +- nieuwe folders worden gevonden (bijvoorbeeld bij invoeging van USB stick) + + +&Start + + +About +Informatie + +Build: %x +Build: %x + +All files +Alle bestanden + +Automated Synchronization + + +Directory monitoring active +Bestandslocatie controle actief + +Waiting until all directories are available... +Wachten tot alle bestandslocaties beschikbaar zijn... + +Error +Fout + +&Restore +&Herstellen + +&Show error +Laat &fouten zien + +&Exit +&Afsluiten + +Incorrect command line: + + +&Retry +&Opnieuw proberen + +File content +Bestandsinhoud + +File time and size +Bestandstijd- en grootte + +Two way +Twee kanten op + +Mirror +Spiegelen + +Update +Bijwerken + +Custom +Aangepast + +Multiple... +Meerdere... + +Moving file %x to %y +Bezig met verplaatsen van bestand %x naar %y + +Moving folder %x to %y +Bezig met verplaatsen van map %x naar %y + +Moving symbolic link %x to %y +Bezig met verplaatsen van snelkoppeling %x naar %y + +Removing old versions... +Bezig met verwijderen van oude versies... + +Creating symbolic link %x +Snelkoppeling %x wordt aangemaakt + +Creating folder %x +Map %x wordt aangemaakt + +Overwriting file %x +Bezig met overschrijven van bestand %x + +Overwriting symbolic link %x +Bezig met overschrijven van snelkoppeling %x + +Verifying file %x +Verifieert bestand %x + +Updating attributes of %x +Attributen bijwerken van %x + +Cannot find %x. +Kan %x niet vinden. + +Target folder %x already existing. +Doelmap %x bestaat al. + +Target folder input field must not be empty. +Doelmap mag niet leeg zijn. + +Please enter a target folder for versioning. + + +Source folder %x not found. +Bronmap %x niet gevonden. + +The following items have unresolved conflicts and will not be synchronized: +De volgende items hebben onopgeloste conflicten en zullen niet worden gesynchroniseerd: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +De volgende mappen zijn heel verschillend. Wees er zeker van dat dit de goede mappen zijn voor synchronisatie. + +Not enough free disk space available in: +Niet genoeg vrije schijfruimte beschikbaar op: + +Required: +Vereist: + +Available: +Beschikbaar: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Een map die onderdeel is van meerdere map paren word aangepast. Kijk alstublieft uw synchronisatie instellingen na. + +Synchronizing folder pair: +Bezig met synchroniseren van folder paar: + +Generating database... +Genereren van database... + +Creating a Volume Shadow Copy for %x... +Aanmaken van een Volume Shadow Copy voor %x... + +Data verification error: %x and %y have different content. +Data verificatie fout: %x en %y hebben een verschillende inhoud. + +job name +taaknaam + +Synchronization stopped + + +Synchronization completed with errors +Synchronisatie is met fouten afgerond + +Synchronization completed with warnings +Synchronisatie afgerond met waarschuwingen + +Nothing to synchronize +Niets om te synchroniseren + +Synchronization completed successfully +Synchronisatie succesvol + +Saving log file %x... +Opslaan van logbestand %x... + +You can switch to FreeFileSync's main window to resolve this issue. + + +&Don't show this warning again +Laat deze &waarschuwing niet meer zien + +&Ignore +&Negeren + +&Switch +&Omschakelen + +Switching to FreeFileSync's main window + + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + + + +&Ignore subsequent errors +&Negeer volgende foutmeldingen + +Retrying operation... + + +Serious Error + + +Check for Program Updates + + +A new version of FreeFileSync is available: +Er is een nieuwe versie van FreeFileSync beschikbaar: + +Download now? +Nu downloaden? + +&Download +&Download + +FreeFileSync is up to date. +U gebruikt de nieuwste versie van FreeFileSync. + +Unable to connect to sourceforge.net. +Kan geen verbinding maken met sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Kan huidige FreeFileSync versienummer niet online vinden. Wilt u handmatig controleren? + +&Check + + +Symlink +Symlink + +Folder +Map + +Full path +Volledig pad + +Name +Naam + +Relative path +Relatief pad + +Base folder +Hoofdmap + +Size +Grootte + +Date +Datum + +Extension +Extensie + +Category +Categorie + +Action +Actie + +Drag && drop +Drag en drop + +Close progress dialog +Sluit voortgangsvenster + +Standby +Stand-by + +Log off +Afmelden + +Shut down +Afsluiten + +Hibernate +Slaapstand + +Alternate comparison settings + + +Alternate synchronization settings + + +Local filter + + +Active + + +None + + +Remove alternate settings +Verwijder alternatieve instellingen + +Clear filter settings +Verwijder filterinstellingen + +Copy +Kopiëren + +Paste +Plakken + +Alternate Comparison Settings + + +Alternate Synchronization Settings + + +Local Filter + + +&New +&Nieuw + +&Save +O&pslaan + +Save as &batch job... +Opslaan als &batch opdracht... + +1. &Compare +1. &Vergelijk + +2. &Synchronize +2. &Synchroniseer + +&Global settings +&Algemene instellingen + +&Language +&Taal + +&Find... + + +&Export file list... +&Exporteer bestandslijst... + +&Tools +&Gereedschap + +&Check now +&Controleer nu + +Check &automatically once a week +Controleer &automatisch eens per week + +&Check for new version + + +Compare +Vergelijk + +Cancel +Annuleren + +Synchronize +Synchroniseer + +Add folder pair +Voeg gekoppelde mappen toe + +Remove folder pair +Verwijder gekoppelde mappen + +Swap sides +Wissel zijdes + +Close search bar + + +Find: + + +Match case +Hoofdlettergevoelig + +Save as batch job +Opslaan als batch opdracht + +Hide excluded items +Verberg uitgesloten bestanden + +Show filtered or temporarily excluded files +Laat gefilterde of tijdelijk uitgesloten bestanden zien + +Number of files and folders that will be created +Aantal bestanden en mappen die zullen worden aangemaakt + +Number of files that will be overwritten +Aantal bestanden die overschreven zullen worden + +Number of files and folders that will be deleted +Aantal bestanden en mappen die verwijderd zullen worden + +Total bytes to copy +Aantal bytes om te kopiëren + +Select a variant: + + +Identify equal files by comparing modification time and size. + + +Identify equal files by comparing the file content. + + +Symbolic links: + + +More information + + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Identificeer en pas veranderingen toe aan beide kanten. Verwijderingen, verplaatsingen en conflicten worden automatisch gevonden met behulp van een database. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. + + +Copy new and updated files to the right folder. + + +Configure your own synchronization rules. +Configureer uw eigen synchronisatieregels. + +Detect moved files +Detecteer verplaatste bestanden + +Requires database files. Not supported by all file systems. +Heeft database bestanden nodig. Wordt niet ondersteund door alle bestandssystemen + +Delete files: + + +Permanent +Permanent + +Delete or overwrite files permanently +Bestanden definitief verwijderen of overschrijven + +Recycle bin +Prullenbak + +Back up deleted and overwritten files in the recycle bin +Maak een backup van verwijderde en overschreven bestanden in de prullenbak + +Versioning +Versiebeheer + +Move files to a user-defined folder +Verplaats bestanden naar een gebruikers gedefineerde map + +Naming convention: +Naamgevingsconventie + +Show examples + + +Handle errors: + + +Ignore +Negeer + +Hide all error and warning messages +Verberg alle fout- en waarschuwingsberichten + +Pop-up +Pop-up + +Show pop-up on errors or warnings +Laat pop-up zien bij foutmeldingen of waarschuwingen + +On completion: + + +Start synchronization now? + + +Variant: + + +Statistics +Statistieken + +&Don't show this dialog again +Laat deze dialoog &niet meer zien + +Items found: +Onderdelen gevonden: + +Speed: +Snelheid: + +Time remaining: +Resterende tijd: + +Time elapsed: +Verstreken tijd: + +Synchronizing... +Synchroniseert... + +Minimize to notification area +Minimaliseer naar systeemvak + +Close +Sluiten + +&Pause +&Pauze + +Stop + + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Maak een batchbestand voor onbeheerde synchronisatie. Om te starten dubbelklikt u dit bestand of rooster in een taakplanner: %x + +Stop synchronization at first error + + +Show progress dialog +Toon voortgangsdialoogvenster + +Save log: + + +Limit: + + +Limit maximum number of log files +Limiteer maximaal aantal log bestanden + +How can I schedule a batch job? + + +&Recycle bin + + +Delete on both sides +Verwijder aan beide zijdes + +Delete on both sides even if the file is selected on one side only +Verwijder aan beide zijdes ook al is het bestand maar aan één zijde geselecteerd + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. + + +Include: + + +Exclude: + + +Time span: + + +File size: + + +Minimum: + + +Maximum: + + +&Clear +&Leegmaken + +The following settings are used for all synchronization jobs. + + +Fail-safe file copy +Fail-safe bestandskopie + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + + +(recommended) + + +Copy locked files +Kopiëer vergrendelde bestanden + +Copy shared or locked files using the Volume Shadow Copy Service. + + +(requires administrator rights) + + +Copy file access permissions +Kopiëer toegangsrechten van bestand. + +Transfer file and folder permissions. + + +Automatic retry on error: + + +Retry count: + + +Delay (in seconds): + + +Customize context menu: + + +Description +Omschrijving + +Restore hidden windows + + +&Default +&Standaard + +Source code written in C++ using: +Broncode geschreven in C++ met behulp van: + +If you like FreeFileSync +Indien FreeFileSync u bevalt + +Donate with PayPal +Doneer met PayPal + +Feedback and suggestions are welcome +Feedback en suggesties zijn welkom + +Homepage +Homepage + +Email +E-mail + +Published under the GNU General Public License +Gepubliceerd onder de GNU General Public License + +Many thanks for localization: +Veel dank voor de vertalingen: + +Save as Batch Job + + +Delete Items + + +Global Settings + + +Select Time Span + + +Folder Pairs + + +Find +Vind + +Overview +Overzicht + +Configuration +Configuratie + +Main Bar + + +Filter Files + + +Select View + + +Open... +Open... + +Save +Opslaan + +Compare both sides +Vergelijk beide zijdes + +Comparison settings +Vergelijksinstellingen + +Synchronization settings +Synchronisatieinstellingen + +Start synchronization +Start synchronisatie + +Confirm +Bevestigen + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + + + +&Execute +&Uitvoeren + + +1 directory +%x directories + + +1 map +%x mappen + + + +1 file +%x files + + +1 bestand +%x bestanden + + + +%y of 1 row in view +%y of %x rows in view + + +%y van 1 rij in weergave +%y van %x rijen in weergave + + +Set direction: +Stel richting in: + +multiple selection +meervoudige selectie + +Include via filter: +Invoegen via filter: + +Exclude via filter: +Sluit via filter uit: + +Exclude temporarily +Tijdelijk uitsluiten + +Include temporarily +Tijdelijk opnemen + +Delete +Verwijderen + +Include all +Alles opnemen + +Exclude all +Alles uitsluiten + +Show icons: +Laat pictogrammen zien: + +Small +Klein + +Medium +Middel + +Large +Groot + +Select time span... +Selecteer tijdsspanne... + +Default view +Standaard weergave + +Show "%x" +Toon "%x" + +Last session +Laatste sessie + +Folder Comparison and Synchronization +Mappen vergelijken en synchroniseren + +Configuration saved +Configuratie opgeslagen + +FreeFileSync batch +FreeFileSync taak + +Do you want to save changes to %x? +Wilt u de wijzigingen in %x opslaan? + +Never save &changes +Sla &veranderingen nooit op + +Do&n't save +&Niet opslaan + +Filter +Filter + +Show files that exist on left side only +Toon bestanden die alleen aan de linkerzijde bestaan + +Show files that exist on right side only +Toon bestanden die alleen aan de rechterzijde bestaan + +Show files that are newer on left +Toon bestanden die aan de linkerzijde nieuwer zijn + +Show files that are newer on right +Toon bestanden die aan de rechterzijde nieuwer zijn + +Show files that are equal +Toon bestanden die gelijk zijn + +Show files that are different +Toon bestanden die verschillend zijn + +Show conflicts +Toon conflicten + +Show files that will be created on the left side +Toon bestanden die aan de linkerzijde aangemaakt zullen worden + +Show files that will be created on the right side +Toon bestanden die aan de rechterzijde aangemaakt zullen worden + +Show files that will be deleted on the left side +Toon bestanden die van de linkerzijde verwijderd zullen worden + +Show files that will be deleted on the right side +Toon bestanden die van de rechterzijde verwijderd zullen worden + +Show files that will be overwritten on left side +Toon bestanden die aan de linkerzijde overschreven zullen worden + +Show files that will be overwritten on right side +Toon bestanden die aan de rechterzijde overschreven zullen worden + +Show files that won't be copied +Toon bestanden die niet gekopiëerd zullen worden + +Set as default +Instellen als standaard + +All folders are in sync +Alle mappen zijn gesynchroniseerd + +Synchronization Settings + + +Comparison Settings + + +Cannot find %x +Kan %x niet vinden + +Comma-separated values +Door komma gescheiden waarden + +File list exported +Bestandslijst geëxporteerd + +Searching for program updates... +Bezig met zoeken naar programma updates... + +Scanning... +Doorzoekt... + +Comparing content... +Inhoud vergelijken... + +Info +Info + +Warning +Waarschuwing + +Paused +Gepauzeerd + +Initializing... +Initialiseren... + +Stopped + + +Completed +Voltooid + +&Continue +&Doorgaan + +Log +Log + +Today +Vandaag + +This week +Deze week + +This month +Deze maand + +This year +Dit jaar + +Last x days +Laatste x dagen + +Byte +Byte + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Wilt u dit item echt naar de prullenbak verplaatsen? +Wilt u echt de volgende %x items naar de prullenbak verplaatsen? + + +Move + + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Wilt u het volgende item echt verwijderen? +Wilt u echt de volgende %x items verwijderen? + + +Exclude +Uitsluiten + +Direct +Direct + +Follow +Volgen + +Copy NTFS permissions +Kopiëer NTFS permissies + +Integrate external applications into context menu. The following macros are available: +Integreer externe applicaties in het context menu. De volgende macros zijn beschikbaar: + +- full file or folder name +- volledige bestands- of mapnaam + +- folder part only +- alleen het map gedeelte + +- Other side's counterpart to %item_path% +- Tegenhanger van de andere kant naar %item_path% + +- Other side's counterpart to %item_folder% +- Tegenhanger van de andere kant naar %item_folder% + +Restore all hidden windows and warnings? + + +Leave as unresolved conflict +Beschouw als onopgelost conflict + +Replace +Vervangen + +Move files and replace if existing +Verplaats bestanden en overschrijf bestaande bestanden + +Time stamp +Tijdstempel + +Append a timestamp to each file name +Voeg een timestamp aan elke bestandsnaam toe + +File +Bestand + +YYYY-MM-DD hhmmss +JJJJ-MM-DD hhmmss + +Files +Bestanden + +Items +Items + +Percentage +Percentage + +Cannot monitor directory %x. +Kan locatie %x niet controleren. + +Conversion error: +Converteerfout: + +Cannot delete file %x. +Kan bestand %x niet verwijderen. + +The file is locked by another process: +Het bestand is in gebruik door een ander proces: + +Cannot move file %x to %y. +Kan bestand %x niet verplaatsen naar %y. + +Cannot delete directory %x. +Kan bestand %x niet verwijderen. + +Cannot write file attributes of %x. +Kan bestandskenmerken van %x niet schrijven. + +Cannot write modification time of %x. +Kan wijzigingstijd van %x niet toevoegen. + +Cannot read security context of %x. +Kan de beveiligingscontext van %x niet lezen. + +Cannot write security context of %x. +Kan de beveiligingscontext van %x niet schrijven. + +Cannot read permissions of %x. +Kan de bevoegdheden van %x niet lezen. + +Cannot write permissions of %x. +Kan de bevoegdheden van %x niet schrijven. + +Cannot create directory %x. +Kan map %x niet aanmaken. + +Cannot create symbolic link %x. +Kan snelkoppeling %x niet aanmaken. + +Cannot find system function %x. +Kan systeemfunctie %x niet vinden. + +Cannot copy file %x to %y. +Kan bestand %x niet kopiëren naar %y. + +Type of item %x is not supported: +Type van bestand %x is niet ondersteund: + +Cannot resolve symbolic link %x. +Kan snelkoppeling %x niet vinden. + +Cannot open directory %x. +Kan map %x niet openen. + +Cannot enumerate directory %x. +Kan map %x niet opsommen. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +1 min +%x min + + + +1 hour +%x hours + + +1 uur +%x uren + + + +1 day +%x days + + +1 dag +%x dagen + + +Unable to register to receive system messages. + + +Cannot set privilege %x. +Kan privilege %x niet instellen. + +Unable to suspend system sleep mode. + + +Cannot change process I/O priorities. +Kan de I/O prioriteiten niet aanpassen. + +Unable to move %x to the recycle bin. +Kan %x niet verplaatsen naar de prullenbak. + +Cannot determine final path for %x. +Kan pad voor %x niet vaststellen. + +Error Code %x: +Foutcode %x: + diff --git a/FreeFileSync/Build/Languages/english_uk.lng b/FreeFileSync/Build/Languages/english_uk.lng new file mode 100644 index 00000000..c793b010 --- /dev/null +++ b/FreeFileSync/Build/Languages/english_uk.lng @@ -0,0 +1,1526 @@ +
    + English (UK) + Robert Readman + en_GB + flag_england.png + 2 + n == 1 ? 0 : 1 +
    + +Both sides have changed since last synchronization. +Both sides have changed since last synchronisation. + +Cannot determine sync-direction: +Cannot determine sync-direction: + +No change since last synchronization. +No change since last synchronisation. + +The database entry is not in sync considering current settings. +The database entry is not in sync considering current settings. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Setting default synchronisation directions: Old files will be overwritten with newer files. + +Checking recycle bin availability for folder %x... +Checking recycle bin availability for folder %x... + +Moving file %x to the recycle bin +Moving file %x to the recycle bin + +Moving folder %x to the recycle bin +Moving folder %x to the recycle bin + +Moving symbolic link %x to the recycle bin +Moving symbolic link %x to the recycle bin + +Deleting file %x +Deleting file %x + +Deleting folder %x +Deleting folder %x + +Deleting symbolic link %x +Deleting symbolic link %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +The recycle bin is not available for the following folders. Files will be deleted permanently instead: + +An exception occurred +An exception occurred + +A directory path is expected after %x. +A directory path is expected after %x. + +Syntax error +Syntax error + +Cannot open file %x. +Cannot open file %x. + +File %x does not contain a valid configuration. +File %x does not contain a valid configuration. + +Unequal number of left and right directories specified. +Unequal number of left and right directories specified. + +The config file must not contain settings at directory pair level when directories are set via command line. +The config file must not contain settings at directory pair level when directories are set via command line. + +Directories cannot be set for more than one configuration file. +Directories cannot be set for more than one configuration file. + +Command line +Command line + +Syntax: +Syntax: + +config files +config files + +directory +directory + +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 directories for at most one config file. +Any number of alternative directories for at most one config file. + +Cannot find the following folders: +Cannot find the following folders: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronisation. + +A folder input field is empty. +A folder input field is empty. + +The corresponding folder will be considered as empty. +The corresponding folder will be considered as empty. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +The following folders have dependent paths. Be careful when setting up synchronisation rules: + +File %x has an invalid date. +File %x has an invalid date. + +Date: +Date: + +Files %x have the same date but a different size. +Files %x have the same date but a different size. + +Size: +Size: + +Items differ in attributes only +Items differ in attributes only + +Resolving symbolic link %x +Resolving symbolic link %x + +Comparing content of files %x +Comparing content of files %x + +Generating file list... +Generating file list... + +Starting comparison +Starting comparison + +Calculating sync directions... +Calculating sync directions... + +Out of memory. +Out of memory. + +Item exists on left side only +Item exists on left side only + +Item exists on right side only +Item exists on right side only + +Left side is newer +Left side is newer + +Right side is newer +Right side is newer + +Items have different content +Items have different content + +Both sides are equal +Both sides are equal + +Conflict/item cannot be categorized +Conflict/item cannot be categorised + +Copy new item to left +Copy new item to left + +Copy new item to right +Copy new item to right + +Delete left item +Delete left item + +Delete right item +Delete right item + +Move file on left +Move file on left + +Move file on right +Move file on right + +Overwrite left item +Overwrite left item + +Overwrite right item +Overwrite right item + +Do nothing +Do nothing + +Update attributes on left +Update attributes on left + +Update attributes on right +Update attributes on right + +Database file %x is incompatible. +Database file %x is incompatible. + +Initial synchronization: +Initial synchronisation: + +Database file %x does not yet exist. +Database file %x does not yet exist. + +Database file is corrupt: +Database file is corrupt: + +Cannot write file %x. +Cannot write file %x. + +Cannot read file %x. +Cannot read file %x. + +Database files do not share a common session. +Database files do not share a common session. + +Searching for folder %x... +Searching for folder %x... + +Cannot read file attributes of %x. +Cannot read file attributes of %x. + +Cannot get process information. +Cannot get process information. + +Waiting while directory is locked: +Waiting while directory is locked: + +Lock owner: +Lock owner: + + +1 sec +%x sec + + +1 sec +%x sec + + +Detecting abandoned lock... +Detecting abandoned lock... + +Creating file %x +Creating file %x + +Items processed: +Elements processed: + +Items remaining: +Elements remaining: + +Total time: +Total time: + + +1 byte +%x bytes + + +1 byte +%x bytes + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Error parsing file %x, row %y, column %z. + +Cannot set directory lock for %x. +Cannot set directory lock for %x. + +Scanning: +Scanning: + + +1 thread +%x threads + + +1 thread +%x threads + + +Encoding extended time information: %x +Encoding extended time information: %x + +/sec +/sec + +%x items/sec +%x items/sec + +Configuration file %x loaded partially only. +Configuration file %x loaded partially only. + +Show in Explorer +Show in Explorer + +Open with default application +Open with default application + +Browse directory +Browse directory + +Cannot access the Volume Shadow Copy Service. +Cannot access the Volume Shadow Copy Service. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Please use FreeFileSync 64-bit version to create shadow copies on this system. + +Cannot load file %x. +Cannot load file %x. + +Cannot determine volume name for %x. +Cannot determine volume name for %x. + +Volume name %x is not part of file path %y. +Volume name %x is not part of file path %y. + +Stop requested: Waiting for current operation to finish... +Stop requested: Waiting for current operation to finish... + +Unable to create timestamp for versioning: +Unable to create timestamp for versioning: + +Cannot read the following XML elements: +Cannot read the following XML elements: + +&Open... +&Open... + +Save &as... +Save &as... + +&Quit +&Quit + +&Program +&Program + +&View help +&View help + +&About +&About + +&Help +&Help + +Usage: +Usage: + +1. Select folders to watch. +1. Select folders to watch. + +2. Enter a command line. +2. Enter a command line. + +3. Press 'Start'. +3. Press 'Start'. + +To get started just import a .ffs_batch file. +To get started just import a .ffs_batch file. + +Folders to watch: +Folders to watch: + +Add folder +Add folder + +Remove folder +Remove folder + +Browse +Browse + +Select a folder +Select a folder + +Idle time (in seconds): +Idle time (in seconds): + +Idle time between last detected change and execution of command +Idle time between last detected change and execution of command + +Command line: +Command line: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +&Start +&Start + +About +About + +Build: %x +Build: %x + +All files +All files + +Automated Synchronization +Automated Synchronisation + +Directory monitoring active +Directory monitoring active + +Waiting until all directories are available... +Waiting until all directories are available... + +Error +Error + +&Restore +&Restore + +&Show error +&Show error + +&Exit +&Exit + +Incorrect command line: +Incorrect command line: + +&Retry +&Retry + +File content +File content + +File time and size +File time and size + +Two way +Two way + +Mirror +Mirror + +Update +Update + +Custom +Custom + +Multiple... +Multiple... + +Moving file %x to %y +Moving file %x to %y + +Moving folder %x to %y +Moving folder %x to %y + +Moving symbolic link %x to %y +Moving symbolic link %x to %y + +Removing old versions... +Removing old versions... + +Creating symbolic link %x +Creating symbolic link %x + +Creating folder %x +Creating folder %x + +Overwriting file %x +Overwriting file %x + +Overwriting symbolic link %x +Overwriting symbolic link %x + +Verifying file %x +Verifying file %x + +Updating attributes of %x +Updating attributes of %x + +Creating a Volume Shadow Copy for %x... +Creating a Volume Shadow Copy for %x... + +Data verification error: %x and %y have different content. +Data verification error: %x and %y have different content. + +Cannot find %x. +Cannot find %x. + +Target folder %x already existing. +Target folder %x already existing. + +Target folder input field must not be empty. +Target folder input field must not be empty. + +Please enter a target folder for versioning. +Please enter a target folder for versioning. + +Source folder %x not found. +Source folder %x not found. + +The following items have unresolved conflicts and will not be synchronized: +The following items have unresolved conflicts and will not be synchronised: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +The following folders are significantly different. Make sure you are matching the correct folders for synchronisation. + +Not enough free disk space available in: +Not enough free disk space available in: + +Required: +Required: + +Available: +Available: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +A folder will be modified which is part of multiple folder pairs. Please review synchronisation settings. + +Synchronizing folder pair: +Synchronising folder pair: + +Generating database... +Generating database... + +job name +job name + +Synchronization stopped +Synchronisation stopped + +Synchronization completed with errors +Synchronisation completed with errors + +Synchronization completed with warnings +Synchronization completed with warnings + +Nothing to synchronize +Nothing to synchronise + +Synchronization completed successfully +Synchronisation completed successfully + +Saving log file %x... +Saving log file %x... + +You can switch to FreeFileSync's main window to resolve this issue. +You can switch to FreeFileSync's main window to resolve this issue. + +&Don't show this warning again +&Don't show this warning again + +&Ignore +&Ignore + +&Switch +&Switch + +Switching to FreeFileSync's main window +Switching to FreeFileSync's main window + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + +&Ignore subsequent errors +&Ignore subsequent errors + +Retrying operation... +Retrying operation... + +Serious Error +Serious Error + +Check for Program Updates +Check for Program Updates + +A new version of FreeFileSync is available: +A new version of FreeFileSync is available: + +Download now? +Download now? + +&Download +&Download + +FreeFileSync is up to date. +FreeFileSync is up to date. + +Unable to connect to sourceforge.net. +Unable to connect to sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Cannot find current FreeFileSync version number online. Do you want to check manually? + +&Check +&Check + +Symlink +Symlink + +Folder +Folder + +Full path +Full path + +Name +Name + +Relative path +Relative path + +Base folder +Base folder + +Size +Size + +Date +Date + +Extension +Extension + +Category +Category + +Action +Action + +Drag && drop +Drag && drop + +Close progress dialog +Close progress dialogue + +Standby +Standby + +Log off +Log off + +Shut down +Shut down + +Hibernate +Hibernate + +Alternate comparison settings +Alternate comparison settings + +Alternate synchronization settings +Alternate synchronisation settings + +Local filter +Local filter + +Active +Active + +None +None + +Remove alternate settings +Remove alternate settings + +Clear filter settings +Clear filter settings + +Copy +Copy + +Paste +Paste + +Alternate Comparison Settings +Alternate Comparison Settings + +Alternate Synchronization Settings +Alternate Synchronisation Settings + +Local Filter +Local Filter + +&New +&New + +&Save +&Save + +Save as &batch job... +Save as &batch job... + +1. &Compare +1. &Compare + +2. &Synchronize +2. &Synchronise + +&Global settings +&Global settings + +&Language +&Language + +&Find... +&Find... + +&Export file list... +&Export file list... + +&Tools +&Tools + +&Check now +&Check now + +Check &automatically once a week +Check &automatically once a week + +&Check for new version +&Check for new version + +Compare +Compare + +Cancel +Cancel + +Synchronize +Synchronise + +Add folder pair +Add folder pair + +Remove folder pair +Remove folder pair + +Swap sides +Swap sides + +Close search bar +Close search bar + +Find: +Find: + +Match case +Match case + +Save as batch job +Save as batch job + +Hide excluded items +Hide excluded items + +Show filtered or temporarily excluded files +Show filtered or temporarily excluded files + +Number of files and folders that will be created +Number of files and folders that will be created + +Number of files that will be overwritten +Number of files that will be overwritten + +Number of files and folders that will be deleted +Number of files and folders that will be deleted + +Total bytes to copy +Total bytes to copy + +Select a variant: +Select a variant: + +Identify equal files by comparing modification time and size. +Identify equal files by comparing modification time and size. + +Identify equal files by comparing the file content. +Identify equal files by comparing the file content. + +Symbolic links: +Symbolic links: + +More information +More information + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Create a mirror backup of the left folder which exactly matches the right folder after synchronisation. + +Copy new and updated files to the right folder. +Copy new and updated files to the right folder. + +Configure your own synchronization rules. +Configure your own synchronisation rules. + +Detect moved files +Detect moved files + +Requires database files. Not supported by all file systems. +Requires database files. Not supported by all file systems. + +Delete files: +Delete files: + +Permanent +Permanent + +Delete or overwrite files permanently +Delete or overwrite files permanently + +Recycle bin +Recycle bin + +Back up deleted and overwritten files in the recycle bin +Back up deleted and overwritten files in the recycle bin + +Versioning +Versioning + +Move files to a user-defined folder +Move files to a user-defined folder + +Naming convention: +Naming convention: + +Show examples +Show examples + +Handle errors: +Handle errors: + +Ignore +Ignore + +Hide all error and warning messages +Hide all error and warning messages + +Pop-up +Pop-up + +Show pop-up on errors or warnings +Show pop-up on errors or warnings + +On completion: +On completion: + +Start synchronization now? +Start synchronisation now? + +Variant: +Variant: + +Statistics +Statistics + +&Don't show this dialog again +&Don't show this dialogue again + +Items found: +Elements found: + +Speed: +Speed: + +Time remaining: +Time remaining: + +Time elapsed: +Time elapsed: + +Synchronizing... +Synchronising... + +Minimize to notification area +Minimize to notification area + +Close +Close + +&Pause +&Pause + +Stop +Stop + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Create a batch file for unattended synchronisation. To start, double-click this file or schedule in a task planner: %x + +Stop synchronization at first error +Stop synchronisation at first error + +Show progress dialog +Show progress dialogue + +Save log: +Save log: + +Limit: +Limit: + +Limit maximum number of log files +Limit maximum number of log files + +How can I schedule a batch job? +How can I schedule a batch job? + +&Recycle bin +&Recycle bin + +Delete on both sides +Delete on both sides + +Delete on both sides even if the file is selected on one side only +Delete on both sides even if the file is selected on one side only + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Select filter rules to exclude certain files from synchronisation. Enter file paths relative to their corresponding folder pair. + +Include: +Include: + +Exclude: +Exclude: + +Time span: +Time span: + +File size: +File size: + +Minimum: +Minimum: + +Maximum: +Maximum: + +&Clear +&Clear + +The following settings are used for all synchronization jobs. +The following settings are used for all synchronisation jobs. + +Fail-safe file copy +Fail-safe file copy + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +(recommended) +(recommended) + +Copy locked files +Copy locked files + +Copy shared or locked files using the Volume Shadow Copy Service. +Copy shared or locked files using the Volume Shadow Copy Service. + +(requires administrator rights) +(requires administrator rights) + +Copy file access permissions +Copy file access permissions + +Transfer file and folder permissions. +Transfer file and folder permissions. + +Automatic retry on error: +Automatic retry on error: + +Retry count: +Retry count: + +Delay (in seconds): +Delay (in seconds): + +Customize context menu: +Customise context menu: + +Description +Description + +Restore hidden windows +Restore hidden windows + +&Default +&Default + +Source code written in C++ using: +Source code written in C++ using: + +If you like FreeFileSync +If you like FreeFileSync + +Donate with PayPal +Donate with PayPal + +Feedback and suggestions are welcome +Feedback and suggestions are welcome + +Homepage +Homepage + +Email +E-mail + +Published under the GNU General Public License +Published under the GNU General Public Licence + +Many thanks for localization: +Many thanks for localisation: + +Save as Batch Job +Save as Batch Job + +Delete Items +Delete Items + +Global Settings +Global Settings + +Select Time Span +Select Time Span + +Folder Pairs +Folder Pairs + +Find +Find + +Overview +Overview + +Configuration +Configuration + +Main Bar +Main Bar + +Filter Files +Filter Files + +Select View +Select View + +Open... +Open... + +Save +Save + +Compare both sides +Compare both sides + +Comparison settings +Comparison settings + +Synchronization settings +Synchronisation settings + +Start synchronization +Start synchronisation + +Confirm +Confirm + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + +&Execute +&Execute + + +1 directory +%x directories + + +1 directory +%x directories + + + +1 file +%x files + + +1 file +%x files + + + +%y of 1 row in view +%y of %x rows in view + + +%y of 1 row in view +%y of %x rows in view + + +Set direction: +Set direction: + +multiple selection +multiple selection + +Include via filter: +Include via filter: + +Exclude via filter: +Exclude via filter: + +Exclude temporarily +Exclude temporarily + +Include temporarily +Include temporarily + +Delete +Delete + +Include all +Include all + +Exclude all +Exclude all + +Show icons: +Show icons: + +Small +Small + +Medium +Medium + +Large +Large + +Select time span... +Select time span... + +Default view +Default view + +Show "%x" +Show "%x" + +Last session +Last session + +Folder Comparison and Synchronization +Folder Comparison and Synchronisation + +Configuration saved +Configuration saved + +FreeFileSync batch +FreeFileSync batch + +Do you want to save changes to %x? +Do you want to save changes to %x? + +Never save &changes +Never save &changes + +Do&n't save +Do&n't save + +Filter +Filter + +Show files that exist on left side only +Show files that exist on left side only + +Show files that exist on right side only +Show files that exist on right side only + +Show files that are newer on left +Show files that are newer on left + +Show files that are newer on right +Show files that are newer on right + +Show files that are equal +Show files that are equal + +Show files that are different +Show files that are different + +Show conflicts +Show conflicts + +Show files that will be created on the left side +Show files that will be created on the left side + +Show files that will be created on the right side +Show files that will be created on the right side + +Show files that will be deleted on the left side +Show files that will be deleted on the left side + +Show files that will be deleted on the right side +Show files that will be deleted on the right side + +Show files that will be overwritten on left side +Show files that will be overwritten on left side + +Show files that will be overwritten on right side +Show files that will be overwritten on right side + +Show files that won't be copied +Show files that won't be copied + +Set as default +Set as default + +All folders are in sync +All folders are in sync + +Synchronization Settings +Synchronisation Settings + +Comparison Settings +Comparison Settings + +Cannot find %x +Cannot find %x + +Comma-separated values +Comma-separated values + +File list exported +File list exported + +Searching for program updates... +Searching for program updates... + +Scanning... +Scanning... + +Comparing content... +Comparing content... + +Info +Info + +Warning +Warning + +Select all +Select all + +Paused +Paused + +Initializing... +Initialising... + +Stopped +Stopped + +Completed +Completed + +&Continue +&Continue + +Log +Log + +Today +Today + +This week +This week + +This month +This month + +This year +This year + +Last x days +Last x days + +Byte +Byte + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Move +Move + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Exclude +Exclude + +Direct +Direct + +Follow +Follow + +Copy NTFS permissions +Copy NTFS permissions + +Integrate external applications into context menu. The following macros are available: +Integrate external applications into context menu. The following macros are available: + +- full file or folder name +- full file or folder name + +- folder part only +- folder part only + +- Other side's counterpart to %item_path% +- Other side's counterpart to %item_path% + +- Other side's counterpart to %item_folder% +- Other side's counterpart to %item_folder% + +Restore all hidden windows and warnings? +Restore all hidden windows and warnings? + +Leave as unresolved conflict +Leave as unresolved conflict + +Replace +Replace + +Move files and replace if existing +Move files and replace if existing + +Time stamp +Time stamp + +Append a timestamp to each file name +Append a timestamp to each file name + +File +File + +YYYY-MM-DD hhmmss +YYYY-MM-DD hhmmss + +Files +Files + +Items +Items + +Percentage +Percentage + +Cannot monitor directory %x. +Cannot monitor directory %x. + +Conversion error: +Conversion error: + +Cannot delete file %x. +Cannot delete file %x. + +The file is locked by another process: +The file is locked by another process: + +Cannot move file %x to %y. +Cannot move file %x to %y. + +Cannot delete directory %x. +Cannot delete directory %x. + +Cannot write file attributes of %x. +Cannot write file attributes of %x. + +Cannot write modification time of %x. +Cannot write modification time of %x. + +Cannot read security context of %x. +Cannot read security context of %x. + +Cannot write security context of %x. +Cannot write security context of %x. + +Cannot read permissions of %x. +Cannot read permissions of %x. + +Cannot write permissions of %x. +Cannot write permissions of %x. + +Cannot create directory %x. +Cannot create directory %x. + +Cannot create symbolic link %x. +Cannot create symbolic link %x. + +Cannot find system function %x. +Cannot find system function %x. + +Cannot copy file %x to %y. +Cannot copy file %x to %y. + +Type of item %x is not supported: +Type of item %x is not supported: + +Cannot resolve symbolic link %x. +Cannot resolve symbolic link %x. + +Cannot open directory %x. +Cannot open directory %x. + +Cannot enumerate directory %x. +Cannot enumerate directory %x. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +1 min +%x min + + + +1 hour +%x hours + + +1 hour +%x hours + + + +1 day +%x days + + +1 day +%x days + + +Unable to register to receive system messages. +Unable to register to receive system messages. + +Cannot set privilege %x. +Cannot set privilege %x. + +Unable to suspend system sleep mode. +Unable to suspend system sleep mode. + +Cannot change process I/O priorities. +Cannot change process I/O priorities. + +Unable to move %x to the recycle bin. +Unable to move %x to the recycle bin. + +Cannot determine final path for %x. +Cannot determine final path for %x. + +Error Code %x: +Error Code %x: + diff --git a/FreeFileSync/Build/Languages/finnish.lng b/FreeFileSync/Build/Languages/finnish.lng new file mode 100644 index 00000000..eae610ca --- /dev/null +++ b/FreeFileSync/Build/Languages/finnish.lng @@ -0,0 +1,1513 @@ +
    + Suomi + Nalle Juslén + fi_FI + flag_finland.png + 2 + n == 1 ? 0 : 1 +
    + +Both sides have changed since last synchronization. +Molemmat puolet muuttuneet edellisestä täsmäyksestä. + +Cannot determine sync-direction: +Tuntematon suunta täsmäykselle: + +No change since last synchronization. +Ei muutoksia edellisen täsmäyksen jälkeen. + +The database entry is not in sync considering current settings. +Tietokanta ei vastaa nykyisiä asetuksia. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Aseta oletussuunta täsmäykselle: Vanhat tiedostot korvataan uudemilla tiedostoilla. + +Checking recycle bin availability for folder %x... +Tarkistetaan Roskakorin käyttö hakemistolle %x... + +Moving file %x to the recycle bin +Siirrä tiedosto %x Roskakoriin + +Moving folder %x to the recycle bin +Siirrä hakemisto %x Roskakoriin + +Moving symbolic link %x to the recycle bin +Siirrä linkki %x Roskakoriin + +Deleting file %x +Poista tiedosto %x + +Deleting folder %x +Poista hakemisto %x + +Deleting symbolic link %x +Pistetaan pikakuvake %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +Roskakori puuttuu näiltä hakemistoilta. Tiedostot poistetaan ehdotta: + +An exception occurred +Virhe havaittu + +A directory path is expected after %x. +Tarvitaan hakemistopolku %x jälkeen. + +Syntax error +Komento virhe + +Cannot open file %x. +Tiedosto %x ei aukea. + +File %x does not contain a valid configuration. +Tiedosto %x ei sisällä kelvollista kokoonpanoa. + +Unequal number of left and right directories specified. +Hakemisto määrät ei täsmää (oikea<>vasen). + +The config file must not contain settings at directory pair level when directories are set via command line. +Määrittelyssä on oltava viittaukset hakemistopareista, jos hakemistot kutsutaam komentoriviltä. + +Directories cannot be set for more than one configuration file. +Hakemisto määriteltävä vain yhteen määrittelyyn. + +Command line +Komentokehote + +Syntax: +Syntaksi: + +config files +määrittelytiedosto + +directory +hakemisto + +Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. +Vapaa määrä FreeFileSync .ffs_gui ja/tai .ffs_batch määrittelytiedostoja. + +Any number of alternative directories for at most one config file. +Vapaa määrä eri hakemistoja yhdessä määrittelytiedostossa. + +A folder input field is empty. +Hakemiston syöte on tyhjä. + +The corresponding folder will be considered as empty. +Hakemisto tulkitaan tyhjäksi. + +Cannot find the following folders: +Hakemistot ei löydy: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Voit ohita virhe olettamalla hakemistot tyhjiksi. Hakemistot luodaan täsmäytyksen aikana. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +Hakemistolla on polkumäärittely. Luo täsmäytyksen vertailusäännöt huolella: + +File %x has an invalid date. +Tiedostolla %x on virheellinen päiväys. + +Date: +Pvm: + +Files %x have the same date but a different size. +Tiedostoilla %x on sama päiväys mutta eri koko. + +Size: +Koko: + +Items differ in attributes only +Kohteiden eroaa vain määritteissä + +Resolving symbolic link %x +Tulkitaan pikakuvike %x + +Comparing content of files %x +Vertaa tiedostojen %x tietosisältöä + +Generating file list... +Luodaan tiedostolista... + +Starting comparison +Vertailu alkaa + +Calculating sync directions... +Lasketaa täsmäytyksen suuntaa... + +Out of memory. +Muisti loppui. + +Item exists on left side only +Kohde löytyy vain vasemmalta + +Item exists on right side only +Kohde löytyy vain oikealta + +Left side is newer +Uudempi vasemmalla + +Right side is newer +Uudempi oikealla + +Items have different content +Kohteilla eri sisältö + +Both sides are equal +Puolet ovat identtiset + +Conflict/item cannot be categorized +Ristiriita, ei määriteltävissä + +Copy new item to left +Monista uusi vasemmalle + +Copy new item to right +Monista uusi oikealle + +Delete left item +Poista vasen + +Delete right item +Poista oikea + +Move file on left +Siirä vasen tiedosto + +Move file on right +Siirrä oikea tiedosto + +Overwrite left item +Korvaa vasen + +Overwrite right item +Korvaa oikea + +Do nothing +Älä tee mitään + +Update attributes on left +Päivitä oikeudet vasemmalla + +Update attributes on right +Päivitä oikeudet oikealla + +Database file %x is incompatible. +Tietokanta %x vierasta muotoa. + +Initial synchronization: +Ensi täsmäytys: + +Database file %x does not yet exist. +Tietokanta %x on vielä luomatta. + +Database file is corrupt: +Tietokanta viottunut: + +Cannot write file %x. +Kirjoittaminen ei onnistu %x. + +Cannot read file %x. +Lukeminen ei onnistu %x. + +Database files do not share a common session. +Tietokannan tiedostot eri sessioista. + +Searching for folder %x... +Etsitään hakemistoa %x... + +Cannot read file attributes of %x. +Tiedoston %x määritteitä ei voitu lukea. + +Cannot get process information. +Prosessin tietoja ei saada. + +Waiting while directory is locked (%x)... +Odotan hakemiston lukitusta (%x)... + + +1 sec +%x sec + + +1 s +%x s + + +Creating file %x +Luodaan tiedosto %x + +Items processed: +Osia käsitelty: + +Items remaining: +Osia jäljellä: + +Total time: +Kokonaisaika: + + +1 byte +%x bytes + + +1 tavu +%x tavua + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Virhe jäsennys tiedo %x, rivi %y, sarake %z. + +Cannot set directory lock for %x. +Hakemiston %x lukitus ei onnistu. + +Scanning: +Skannaus: + + +1 thread +%x threads + + +1 säije +%x säijettä + + +Encoding extended time information: %x +Tulkitaan laajennettua aikatietoa: %x + +/sec +/s + +%x items/sec +%x olioo/s + +Configuration file %x loaded partially only. +Kokoonpanotiedosto %x ladattu vain osittain. + +Show in Explorer +Näytä Explorerissa + +Open with default application +Avaa oletussovelluksessa + +Browse directory +Selaa hakemistoa + +Cannot access the Volume Shadow Copy Service. +Volume Shadow Copy palvelu ei vastaa. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Tilannevedoksia varten käytä tässä järjestelmässä FreeFileSync 64-bittinen versio. + +Cannot load file %x. +Tiedostoa %x ei voida ladata. + +Cannot determine volume name for %x. +Nimen %x tunnistus ei onnistu. + +Volume name %x is not part of file path %y. +Nimi %x ei esiinny tiedostopolussa %y. + +Stop requested: Waiting for current operation to finish... +Keskeytyspyyntö: Odotetaan tehtävän valmistumista... + +Unable to create timestamp for versioning: +Versiohallinan aikaleimaa ei voida luoda: + +Cannot read the following XML elements: +XML elementit lukukelvottimia: + +&Open... +&Avaa... + +Save &as... +Tallenna n&imellä... + +&Quit +&Lopeta + +&Program +&Ohjelma + +&View help +&Näytä ohje + +&About +&Ohje + +&Help +&Ohje + +Usage: +Käyttö: + +1. Select folders to watch. +1. Valitse seurattavat hakemistot. + +2. Enter a command line. +2. Anna komentokehote + +3. Press 'Start'. +3. Paina 'Käynnistä'. + +To get started just import a .ffs_batch file. +Aloita lataamalla joku .ffs_batch -tiedosto. + +Folders to watch: +Seurattavat hakemistot: + +Add folder +Lisää hakemisto + +Remove folder +Poista hakemisto + +Browse +Selaa + +Select a folder +Valitse hakemisto + +Idle time (in seconds): +Jouten (s) + +Idle time between last detected change and execution of command +Joutoaika edellisen muutoksen ja käskyn suorittamisen välillä + +Command line: +Komentokehoite + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +Käsky suoritetaan jos: +- tiedosto tai alihakemisto muuttu +- uusi hakemisto ilmestyy (esim. USB tikku) + + +&Start +&Käynnistä + +About +Ohje + +Build: %x +Versio: %x + +All files +Kaikki tiedostot + +Automated Synchronization +Automaattinen täsmäytys + +Directory monitoring active +Hakemistovalvonta päällä + +Waiting until all directories are available... +Odota kunnes kaikki hakemistot on saatavilla... + +Error +Virhe + +&Restore +&Palauta + +&Show error +&Näytä virheet + +&Exit +&Poistu + +Incorrect command line: +Virheellinen komento: + +&Retry +&Uudestaan + +File content +Tiedoston sisältö + +File time and size +Tiedoston aika ja koko + +Two way +Molemmat + +Mirror +Peilaava + +Update +Päivittävä + +Custom +Oma määritelmä + +Multiple... +Moninkertainen... + +Moving file %x to %y +Siirrä tiedosto %x -> %y + +Moving folder %x to %y +Siirrä hakemisto %x -> %y + +Moving symbolic link %x to %y +Siirrä pikakuvike %x -> %y + +Removing old versions... +Vanhat poistetaa... + +Creating symbolic link %x +Luodaan pikakuvake %x + +Creating folder %x +LuoDAAN hakemisto %x + +Overwriting file %x +Korvaa tiedosto %x + +Overwriting symbolic link %x +Korvaa pikakuvake %x + +Verifying file %x +Tarkistan tiedostoa %x + +Updating attributes of %x +Päivitän %x:n ominaisuudet + +Cannot find %x. +%x ei löydy. + +Target folder %x already existing. +Kohdehakemisto %x on olemassa. + +Target folder input field must not be empty. +Kohde hakemiston kenttä on annettava. + +Please enter a target folder for versioning. +Anna versioinnin kohdehakemisto. + +Source folder %x not found. +Lähdehakemisto %x ei löydy. + +The following items have unresolved conflicts and will not be synchronized: +Näissä kohteissa on selvittämättömiä ristiriitoja, niitä ei täsmäytetä: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +Hakemistoissa on oleelliset erot. Varmista että hakemistot on oikein valittu. + +Not enough free disk space available in: +Vapaa levytila ei riitä: + +Required: +Vaadittu: + +Available: +Saatavilla: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Moniosaisen hakemistoparin hakemistoa muutetaan. Tarkista täsmäytyksen asetuksia. + +Synchronizing folder pair: +Täsmäytetään hakemistoparia: + +Generating database... +Tietokanta luodaan... + +Creating a Volume Shadow Copy for %x... +Luo %x:lle Volume Shadow Copy ... + +Data verification error: %x and %y have different content. +Tiedon verifiointie virhe: %x ja %y sisältö on erilainen. + +job name +työnimi + +Synchronization stopped +Täsmäytys on pysäytetty + +Synchronization completed with errors +Täsmäytys päättyi virheisiin + +Synchronization completed with warnings +Täsmäytys päättyi varoituksin + +Nothing to synchronize +Ei mitään täsmäytettävää + +Synchronization completed successfully +Täsmäytys päättyi onnistuneesti + +Saving log file %x... +Lokitiedosto: tallennetaan %x... + +You can switch to FreeFileSync's main window to resolve this issue. +Siirry FreeFileSync -pääikkunaan korjataksesi virheen. + +&Don't show this warning again +&Älä enää näytä tätä varoitusta + +&Ignore +&Unohda + +&Switch +&Vaihda + +Switching to FreeFileSync's main window +Vaihdä FreeFileSync -pääikkunaan + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + + + +&Ignore subsequent errors +&Hylkää toistuvat virheet + +Retrying operation... + + +Serious Error +Vakava virhe + +Check for Program Updates +Etsi ohjelmaan päivitystä + +A new version of FreeFileSync is available: +FreeFileSync:n uusi verio on saatavilla: + +Download now? +Lataa nyt? + +&Download +&Lataa + +FreeFileSync is up to date. +FreeFileSync on ajan tasalla. + +Unable to connect to sourceforge.net. +Yhteys sourceforge.net:n ei onnistu. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Nykyinen FreeFileSync versio ei löydy verkosta, etsitäänkö manuaalisesti? + +&Check + + +Symlink +Pikakuvake + +Folder +Hakemisto + +Full path +Koko polku + +Name +Nimi + +Relative path +Suhteellinen polku + +Base folder +Juurihakemisto + +Size +Koko + +Date +Päiväys + +Extension +Laajennus + +Category +Luokka + +Action +Suorita + +Drag && drop +Vedä ja pudota + +Close progress dialog +Sulje etenämän dialoogi + +Standby +Lepotila + +Log off +Kirjaudu ulos + +Shut down +Sulje + +Hibernate +Horrostila + +Alternate comparison settings +Vaihtoehtoinen asetus vertailulle + +Alternate synchronization settings +Vaihtoehtoinen asetus täsmäytykselle + +Local filter +Paikallinen suodin + +Active +Aktiivinen + +None +Ei yhtään + +Remove alternate settings +Poista muut asetukset + +Clear filter settings +Nollaa suodin + +Copy +Monista + +Paste +Liitä + +Alternate Comparison Settings +Vaihtoehtoinen asetus vertailulle + +Alternate Synchronization Settings +Vaihtoehtoinen asetus täsmäytykselle + +Local Filter +Paikallinen suodin + +&New +&Uusi + +&Save +&Tallenna + +Save as &batch job... +Tallenna &eräajona... + +1. &Compare +1. &Vertaa + +2. &Synchronize +2. &Täsmäytä + +&Global settings +&Yleiset asetukset + +&Language +&Kieli + +&Find... +&Etsi... + +&Export file list... +&Vie tiedostojoukko... + +&Tools +&Asetukset + +&Check now +&Tarkista nyt + +Check &automatically once a week +Tarkista &viikoittain + +&Check for new version +&Hae uusi versio + +Compare +Vertaile + +Cancel +Lopeta + +Synchronize +Täsmäytä + +Add folder pair +Lisää hakemistopari + +Remove folder pair +Poista hakemistopari + +Swap sides +Puolten vaihto + +Close search bar +Sulje hakukenttä + +Find: +Etsi + +Match case +Täsmää kirjainkoko + +Save as batch job +Tallenna eräajona + +Hide excluded items +Piilota ohitettavat + +Show filtered or temporarily excluded files +Näytä suodatetut/nyt pois suljetut tiedostot + +Number of files and folders that will be created +Luotavien tiedostojen ja hakemistojen määrä + +Number of files that will be overwritten +Korvattavien tiedostojen ja hakemistojen määrä + +Number of files and folders that will be deleted +Poistettavien tiedostojen ja hakemistojen määrä + +Total bytes to copy +Kopioitava määrä tavuja + +Select a variant: +Valitse vaihtoehto: + +Identify equal files by comparing modification time and size. +Tunnista tiedostojen vastaavuus ajan ja koon mukaan. + +Identify equal files by comparing the file content. +Tunnista tiedostojen vastaavuus sisällöstä. + +Symbolic links: +Pikakuvike: + +More information +Lisää tietoa + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Löydä ja suorita muutokset molemmilla puolilla. Poistot, siirrot ja eroavuudet tunnistetaan tietokannan avulla. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Tee tarkka kopio vasemmasta oikealle. + +Copy new and updated files to the right folder. +Monista muuttuneet ja uudet oikealle. + +Configure your own synchronization rules. +Määritä omat täsmäytyssäännöt. + +Detect moved files +Tunnista siirretyt tiedostot + +Requires database files. Not supported by all file systems. +Vaatii tietokantaa. Ei toimi kaikissa käyttöjärjestelmissä. + +Delete files: +Poista tiedosto: + +Permanent +Pysyvä + +Delete or overwrite files permanently +Poista tai korvaa tiedostoja pysyvästi + +Recycle bin + + +Back up deleted and overwritten files in the recycle bin +Tallenna poistetut/ylikirjoitetut tiedostot Roskakoriin + +Versioning +Versiointi + +Move files to a user-defined folder +Siirrä tiedostot määrättyyn hakemistoon + +Naming convention: +Nimeämis käytäntö: + +Show examples +Näytä esimerkki + +Handle errors: +Hoida virheet: + +Ignore +Ohita + +Hide all error and warning messages +Piilota kaikki virhe- ja varoitusviestit + +Pop-up +Pop-up + +Show pop-up on errors or warnings +Näytä Pop-Up (virheet/varoitukset) + +On completion: +Kun valmis: + +Start synchronization now? +Kännistetäänkö täsmäyts? + +Variant: +Vaihtoehto + +Statistics +Tilastot + +&Don't show this dialog again +&Älä näytä valitaa uudestaan + +Items found: +Osia löytyi: + +Speed: +Nopeus: + +Time remaining: +Aikaa jäljellä: + +Time elapsed: +Aikaa kulunut: + +Synchronizing... +Täsmäytetään... + +Minimize to notification area +Pienennä huomiokenttään + +Close +Sulje + +&Pause +&Keskeytä + +Stop +Keskeytä + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Luo eräajo-tiedosto automaattiseen täsmäytykseen. Käynnista tuplaklickillä tai ajasta: %x + +Stop synchronization at first error +Keskeytä täsmäytys ensimmäiseen virheeseen + +Show progress dialog +Näytä etenemä ikkuna + +Save log: +Tallenna loki: + +Limit: +Raja: + +Limit maximum number of log files +Lokeja enintäin + +How can I schedule a batch job? +Miten ajastan eräajon? + +&Recycle bin +&Roskakori + +Delete on both sides +Poista molemmilta puolilta + +Delete on both sides even if the file is selected on one side only +Poista molemmin puolin, vaikka tiedosto on valittu vain toisella puolella + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Ohitettavien tiedostojen ehdot täsmäytysessä. Anna hakemistopariin nähden suhteellinen osoite. + +Include: +Sisällytä: + +Exclude: +Sulje pois: + +Time span: +Aikajakso: + +File size: +Tiedosto koko: + +Minimum: +Vähintäin: + +Maximum: +Enintäin: + +&Clear +&Pyyhi + +The following settings are used for all synchronization jobs. +Nämä asetukset käytetään kaikkiin täsmäytyksiin. + +Fail-safe file copy +Varmennettu tiedostokopiointi + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Kopioi väliaikaistiedostoon (*.ffs_tmp) ennen kohteen ylikirjoitusta. +Tällä varmistetaa eheys vaikka vakava virhe tapahtuisi. + + +(recommended) +(suositus) + +Copy locked files +Kopioi lukitut tiedostot + +Copy shared or locked files using the Volume Shadow Copy Service. +Jaettujen tai lukittujen tiedostojen kopiointi käyttäen Volume Shadow Copy Service + +(requires administrator rights) +(edellyttää pääkäyttäjän oikeuksia) + +Copy file access permissions +Kopioi tiedoston käyttöoikeuksia + +Transfer file and folder permissions. +Tiedosto ja hakemisto määreet siirretään. + +Automatic retry on error: +Automaattinen uusintayritys virheestä: + +Retry count: +Uusintayrityksiä: + +Delay (in seconds): +Viive (s): + +Customize context menu: +Yksilöi valikko: + +Description +Seloste + +Restore hidden windows +Palauta piiloitettu ikkuna + +&Default +&Vakio + +Source code written in C++ using: +Koodikieli on C++ käyttäen: + +If you like FreeFileSync +Jos pidät FreeFileSync:tä + +Donate with PayPal +Lahjoita PayPal:lla + +Feedback and suggestions are welcome +Palaute ja uudet ideat ovat tervetulleita + +Homepage +Kotisivu + +Email +S-posti + +Published under the GNU General Public License +Julkaistu lisenssillä GNU General Public License + +Many thanks for localization: +Paljon kiitoksia kääntäjille: + +Save as Batch Job +Tallenna eräajona + +Delete Items +Poista kohteet + +Global Settings +Yleinen asetus + +Select Time Span +Valitse aikajakso + +Folder Pairs +Hakemisto parit + +Find +Etsi + +Overview +Yleiskatsaus + +Configuration +Asetukset + +Main Bar +Pääpalkki + +Filter Files +Suodata tiedostot + +Select View +Valitse näkymä + +Open... +Avaa... + +Save +Tallenna + +Compare both sides +Vertaile molemmat puolet + +Comparison settings +Vertailun asetukset + +Synchronization settings +Täsmäytyksen asetukset + +Start synchronization +Käynnistä täsmäytys + +Confirm +Vahvista + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + + + +&Execute +&Suorita + + +1 directory +%x directories + + +1 hakemisto +%x hakemistoja + + + +1 file +%x files + + +1 tiedosto +%x tiedostoja + + + +%y of 1 row in view +%y of %x rows in view + + +%y rivi 1:stä näytetään +%y riviä %x:stä näytetään + + +Set direction: +Aseta suunta: + +multiple selection +monivalinta + +Include via filter: +Lisää suotimella: + +Exclude via filter: +Sulje pois suotimella: + +Exclude temporarily +Sulje pois, tilapäisesti + +Include temporarily +Sisällytä, tilapäisesti + +Delete +Poista + +Include all +Valitse kaikki + +Exclude all +Hylkää kaikki + +Show icons: +Näytä kuvakkeet: + +Small +Pieni + +Medium +Keskikoko + +Large +Iso + +Select time span... +Valitse aikajana... + +Default view +Oletusnäkymä + +Show "%x" +Näytä "%x" + +Last session +Edellinen istunto + +Folder Comparison and Synchronization +Hakemistojen vertailu ja täsmäytys + +Configuration saved +Asetukset tallennettu + +FreeFileSync batch +FreeFileSync eräajo + +Do you want to save changes to %x? +Haluatko tallentaa muutokset: %x? + +Never save &changes +Älä tallenna &muutoksia + +Do&n't save +Äl&ä tallenna + +Filter +Suodin + +Show files that exist on left side only +Näytä vain vasemmalla esiintyvät tiedostot + +Show files that exist on right side only +Näytä vain oikealla esiintyvät tiedostot + +Show files that are newer on left +Näytä vasemmalla olevat uudemmat tiedostot + +Show files that are newer on right +Näytä uudemmat tiedostot oikealla + +Show files that are equal +Näytä yhteneväiset tiedostot + +Show files that are different +Näytä erilaiset tiedostot + +Show conflicts +Näytä ristiriidat + +Show files that will be created on the left side +Näytä vasemmalle luotavat tiedostot + +Show files that will be created on the right side +Näytä oikealle luotavat tiedostot + +Show files that will be deleted on the left side +Näytä vasemmalta poistettavat tiedostot + +Show files that will be deleted on the right side +Näytä oikealta poistettavat tiedostot + +Show files that will be overwritten on left side +Näytä vasemmalla korvattavat tiedostot + +Show files that will be overwritten on right side +Näytä oikealla korvattavat tiedostot + +Show files that won't be copied +Näytä kopioimatta jäävät tiedostot + +Set as default +Aseta oletukseksi + +All folders are in sync +Kaikki hakemistot täsmäytetty + +Synchronization Settings +Täsmäytyksen asetukset + +Comparison Settings +Vertailun asetukset + +Cannot find %x +En löydä %x + +Comma-separated values +Pilkulla erotetut arvot + +File list exported +Tiedostolista viety + +Searching for program updates... +Ohjelmapäivytys haetaa... + +Scanning... +Tiedostoja haetaan... + +Comparing content... +Sisällön vertailu... + +Info +Info + +Warning +Varoitus + +Paused +Pysäytetty + +Initializing... +Alustus... + +Stopped +Keskeytys + +Completed +Valmis + +&Continue +&Jatka + +Log +Loki + +Today +Tänään + +This week +Tällä viikolla + +This month +Tässä kuussa + +This year +Tänä vuonna + +Last x days +Viimeiset x päivää + +Byte +tavua + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Haluatko varmasti siirtää kohde Roskakoriin? +Haluatko varmasti siirtää nämä %x kohteet Roskakoriin? + + +Move +Siirrä + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Haluatko todella poistta tämä? +Haluatko todella poistaa nämä %x kohdetta? + + +Exclude +Sulje pois + +Direct +Suoraan + +Follow +Seuraa + +Copy NTFS permissions +Monista NTFS oikeudet + +Integrate external applications into context menu. The following macros are available: +Liitä ulkoinen sovellus viitekehysvalikkoon. Seuraavat makrot ovat valittavissa: + +- full file or folder name +- tiedoston/hakemiston koko nimi + +- folder part only +- vain hakemisto-osa + +- Other side's counterpart to %item_path% +- Toisen puolen osa liittyy %item_path% + +- Other side's counterpart to %item_folder% +- Toisen puolen osa liittyy %item_folder% + +Restore all hidden windows and warnings? +Palauta kaikki piiloitetut ikkunat ja varoitukset? + +Leave as unresolved conflict +Jätä ratkaisemattomana virheenä + +Replace +Korvaa + +Move files and replace if existing +Siirrä tiedostot ja korvaa olemassaolevat + +Time stamp +Aikaleima + +Append a timestamp to each file name +Lisää aikaleima tiedostoihin + +File +Tiedosto + +YYYY-MM-DD hhmmss +VVVV-KK-PP ttmmss + +Files +Tiedostot + +Items +Kappaletta + +Percentage +Prosenttia + +Cannot monitor directory %x. +Hakemistoa %x ei voida tarkkaila. + +Conversion error: +Muunnosvirhe: + +Cannot delete file %x. +Ei voi poistaa tiedostoa %x. + +The file is locked by another process: +Tiedosto on toisen prosessin lukitsema: + +Cannot move file %x to %y. +Tiedostoa %x ei voida siirtää kohtaan %y. + +Cannot delete directory %x. +Hakemistoa %x ei voida poistaa. + +Cannot write file attributes of %x. +Tiedoston %x ominaisuuksia ei voitu tallentaa. + +Cannot write modification time of %x. +Tiedoston %x muutosaikaa ei voida kirjoittaa. + +Cannot read security context of %x. +Ei voi lukea %x -suojauskontekstia. + +Cannot write security context of %x. +Ei voi tallentaa %x -suojauskontekstia. + +Cannot read permissions of %x. +Ei voi lukea %x -oikeuksia. + +Cannot write permissions of %x. +Ei voi tallentaa %x -oikeuksia. + +Cannot create directory %x. +Hakemistoa %x ei voitu luoda. + +Cannot create symbolic link %x. +Pikakuvikkeen luonti epäonnistui %x. + +Cannot find system function %x. +Järjestelmäfunktio ei löydy, %x + +Cannot copy file %x to %y. +Tiedostoa %x ei voida kopioida kohtaan %y. + +Type of item %x is not supported: +Kohteen %x tyyppiä ei tueta: + +Cannot resolve symbolic link %x. +Tämä linkki on virheellinen %x. + +Cannot open directory %x. +Hakemistoa %x ei voida avata. + +Cannot enumerate directory %x. +Hakemistoa %x ei voitu luetella. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +1 min +%x min + + + +1 hour +%x hours + + +1 tunti +%x tuntia + + + +1 day +%x days + + +1 päivä +%x päivää + + +Unable to register to receive system messages. +Virheviestien vastaanotto ei onnistu. + +Cannot set privilege %x. +Oikeuksia %x ei voitu asettaa. + +Unable to suspend system sleep mode. +Lepotilan katkaisu ei onnistu. + +Cannot change process I/O priorities. +Prosessin I/O -pririteetin muutos ei onnistu. + +Unable to move %x to the recycle bin. +%x siirto Roskakoriin epäonnistui. + +Cannot determine final path for %x. +Polkua ei voida määrittää, %x. + +Error Code %x: +Virhe %x: + diff --git a/FreeFileSync/Build/Languages/french.lng b/FreeFileSync/Build/Languages/french.lng new file mode 100644 index 00000000..b0fa82e5 --- /dev/null +++ b/FreeFileSync/Build/Languages/french.lng @@ -0,0 +1,1513 @@ +
    + Français + Jean-François Hartmann + fr_FR + flag_france.png + 2 + n <= 1 ? 0 : 1 +
    + +Both sides have changed since last synchronization. +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 : + +No change since last synchronization. +Aucun changement depuis la dernière synchronisation. + +The database entry is not in sync considering current settings. +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. + +Checking recycle bin availability for folder %x... +Contrôle de la disponibilité de la Corbeille pour le dossier %x ... + +Moving file %x to the recycle bin +Déplacement du fichier %x vers la Corbeille< + +Moving folder %x to the recycle bin +Déplacement du dossier %x vers la Corbeille< + +Moving symbolic link %x to the recycle bin +Déplacement du lien symbolique %x vers la Corbeille + +Deleting file %x +Suppression du fichier %x + +Deleting folder %x +Suppression du dossier %x + +Deleting symbolic link %x +Suppression du lien symbolique %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +La Corbeille n'est pas disponible por les dossiers suivants. Les fichiers seront détruits définitivement : + +An exception occurred +Une erreur s'est produite + +A directory path is expected after %x. +Un chemin est attendu après %x. + +Syntax error +Erreur de syntaxe + +Cannot open file %x. +Impossible d'ouvrir le fichier %x. + +File %x does not contain a valid configuration. +Le fichier %x ne contient pas une configuration valide. + +Unequal number of left and right directories specified. +Le nombre de répertoires à gauche et à droite n'est pas identique. + +The config file must not contain settings at directory pair level when directories are set via command line. +Le fichier de configuration ne peut pas contenir de paramètres au niveau de paires de répertoires quand ceux-ci sont définis par une ligne de commande. + +Directories cannot be set for more than one configuration file. +Les répertoires ne peuvent pas être définis pour plus d'un fichier de configuration. + +Command line +Ligne de commande + +Syntax: +Syntaxe : + +config files +fichiers de configuration + +directory +répertoire + +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 directories for at most one config file. +N'importe quel nombre de répertoires alternatifs pour au plus un fichier de configuration. + +A folder input field is empty. +Une entrée dossier est vide. + +The corresponding folder will be considered as empty. +Le dossier correspondant sera considéré comme vide. + +Cannot find the following folders: +Impossible de trouver les dossiers suivants : + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Vous pouvez ignorer cette erreur en considérant chaque dossier comme vide. Les dossiers seront automatiquement créés pendant la synchronisation. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +Les fichiers suivants ont des chemins interdépendants. Attention à la mise à jour des règles de synchronisation : + +File %x has an invalid date. +Le fichier %x a une date invalide. + +Date: +Date : + +Files %x have the same date but a different size. +Les fichiers %x ont la même date mais une taille différente. + +Size: +Taille : + +Items differ in attributes only +Seuls les attributs des éléments diffèrent + +Resolving symbolic link %x +Résolution du lien symbolique %x + +Comparing content of files %x +Comparaison du contenu des fichiers %x + +Generating file list... +Génération de la liste des fichiers... + +Starting comparison +Comparaison en cours + +Calculating sync directions... +Evaluation du sens des synchronisations ... + +Out of memory. +Mémoire insuffisante. + +Item exists on left side only +Cet élément existe seulement à gauche + +Item exists on right side only +Cet élément existe seulement à droite + +Left side is newer +L'élément de gauche est plus récent + +Right side is newer +L'élément de droite est plus récent + +Items have different content +Les élement ont un contenu différent + +Both sides are equal +Les deux cotés sont identiques + +Conflict/item cannot be categorized +Conflit/élément impossible à classer + +Copy new item to left +Copie du nouvel élément à gauche + +Copy new item to right +Copie du nouvel élément à droite + +Delete left item +Suppression de l'élément de gauche + +Delete right item +Suppression de l'élément de droite + +Move file on left +Déplacer les fichiers à gauche + +Move file on right +Déplacer les fichiers à droite + +Overwrite left item +Remplacement de l'élément de gauche + +Overwrite right item +Remplacement de l'élément de droite + +Do nothing +Ne rien faire + +Update attributes on left +Mise à jour des attributs à gauche + +Update attributes on right +Mise à jour des attributs à droite + +Database file %x is incompatible. +La base de données %x n'est pas compatible. + +Initial synchronization: +Première synchronisation : + +Database file %x does not yet exist. +La base de données %x n'existe plus. + +Database file is corrupt: +Le fichier Base de données est endommagé : + +Cannot write file %x. +Impossible d'écrire le fichier %x. + +Cannot read file %x. +Impossible de lire le fichier %x. + +Database files do not share a common session. +Les fichiers de la base de données ne font pas partie de la même session. + +Searching for folder %x... +Recherche du dossier %x... + +Cannot read file attributes of %x. +Impossible de lire les attributs du fichier %x. + +Cannot get process information. +Impossible d'obtenir les informations du traitement. + +Waiting while directory is locked (%x)... +En attente tant que le répertoire est verrouillé (%x)... + + +1 sec +%x sec + + +%x sec +%x sec + + +Creating file %x +Création du fichier %x + +Items processed: +Élements traités : + +Items remaining: +Élements restants : + +Total time: +Durée totale : + + +1 byte +%x bytes + + +%x octet +%x octets + + +%x MB +%x Mo + +%x KB +%x Ko + +%x GB +%x Go + +Error parsing file %x, row %y, column %z. +Erreur lors de l'analyse du fichier %x, ligne %y, colonne %z. + +Cannot set directory lock for %x. +Impossible de verrouiller le dossier %x + +Scanning: +Lecture en cours : + + +1 thread +%x threads + + +%x tâche +%x tâches + + +Encoding extended time information: %x +Codage de l'heure au format étendu : %x + +/sec +/sec + +%x items/sec +%x éléments/sec + +Configuration file %x loaded partially only. +Le fichier de configuration %x a été chargé seulement partiellement. + +Show in Explorer +Montrer dans l'explorateur + +Open with default application +Ouvrir avec l'application par défaut + +Browse directory +Parcourir le répertoire + +Cannot access the Volume Shadow Copy Service. +Impossible d'accéder au service Volume Shadow Copy. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Utilisez la version FreeFileSync 64-bit pour créer des "shadow copies" sur ce système. + +Cannot load file %x. +Impossible de charger le fichier %x. + +Cannot determine volume name for %x. +Impossible de déterminer le nom du volume de %x. + +Volume name %x is not part of file path %y. +Le nom de volume %x ne fait pas partie du chemin %y. + +Stop requested: Waiting for current operation to finish... +Arrêt demandé : En attente de la fin de l'opération en cours... + +Unable to create timestamp for versioning: +Impossible de créer l'horodatage des versions : + +Cannot read the following XML elements: +Impossible de lire les données XML suivantes : + +&Open... +&Ouvrir... + +Save &as... +S&auvegarder sous... + +&Quit +&Quitter + +&Program +&Programme + +&View help +&Afficher l'aide + +&About +A &Propos de + +&Help +&Aide + +Usage: +Utilisation : + +1. Select folders to watch. +1. Choisir les dossiers à examiner. + +2. Enter a command line. +2. Entrez une ligne de commande. + +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. + +Folders to watch: +Dossiers à surveiller : + +Add folder +Ajout d'un dossier + +Remove folder +Supprimer le dossier + +Browse +Parcourir + +Select a folder +Choisissez un dossier + +Idle time (in seconds): +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 : + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +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) + + +&Start +&Démarrer + +About +A propos de + +Build: %x +Généré : %x + +All files +Tous les fichiers + +Automated Synchronization +Synchronisation Automatique + +Directory monitoring active +Gestion des répertoires active + +Waiting until all directories are available... +En attente de la disponibilité de tous les répertoires ... + +Error +Erreur + +&Restore +&Restaurer + +&Show error +&Afficher l'erreur + +&Exit +&Quitter + +Incorrect command line: +Ligne de commande incorrecte : + +&Retry +&Réessayer + +File content +Contenu du fichier + +File time and size +Date et taille du fichier + +Two way +Deux sens + +Mirror +Miroir + +Update +Mise à Jour + +Custom +Personnaliser + +Multiple... +Multiple... + +Moving file %x to %y +Déplacement du fichier %x vers %y + +Moving folder %x to %y +Déplacement du dossier %x vers %y + +Moving symbolic link %x to %y +Déplacement du lien symbolique %x vers %y + +Removing old versions... +Suppression des anciennes versions... + +Creating symbolic link %x +Création du lien symbolique %x + +Creating folder %x +Création du dossier %x + +Overwriting file %x +Remplacement du fichier %x + +Overwriting symbolic link %x +Remplacement du lien symbolique %x + +Verifying file %x +Contrôle du fichier %x + +Updating attributes of %x +Mise à jour des attributs de %x + +Cannot find %x. +Impossible de trouver %x + +Target folder %x already existing. +Le dossier destination %x existe déjà. + +Target folder input field must not be empty. +L'entrée dossier de destination ne doit pas être vide. + +Please enter a target folder for versioning. +Veuillez entrer le dossier destinataire pour la gestion des versions. + +Source folder %x not found. +Dossier source %x non trouvé. + +The following items have unresolved conflicts and will not be synchronized: +Les éléments suivants sont en conflit non résolu et ne seront pas synchronisés : + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +Les dossiers suivants sont significativement différents. Assurez-vous que les dossiers correspondent avant de lancer la synchronisation. + +Not enough free disk space available in: +Espace disque insuffisant sur : + +Required: +Requis : + +Available: +Disponible : + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Un dossier faisant partie de paires multiples sera modifié. Veuillez vérifier les paramètres de synchronisation. + +Synchronizing folder pair: +Synchronisation de la paire de dossiers + +Generating database... +Génération de la base de données... + +Creating a Volume Shadow Copy for %x... +Création d'un Volume Shadow Copy pour %x ... + +Data verification error: %x and %y have different content. +Erreur lors de la vérification des données : %x et %y ont des contenus différents. + +job name +nom du job + +Synchronization stopped +Synchronisation arrêtée + +Synchronization completed with errors +Synchronisation terminée avec des erreurs + +Synchronization completed with warnings +Synchronisation terminée avec avertissements + +Nothing to synchronize +Rien à synchroniser + +Synchronization completed successfully +Synchronisation terminée sans erreurs + +Saving log file %x... +Enregistrement du fichier log %x... + +You can switch to FreeFileSync's main window to resolve this issue. +Vous pouvez basculer vers la fenêtre principale de FreeFileSync pour résoudre ce problème. + +&Don't show this warning again +&Ne plus afficher cet avertissement + +&Ignore +&Ignorer + +&Switch +&Changer + +Switching to FreeFileSync's main window +Basculer vers la fenêtre principale de FreeFileSync + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + + + +&Ignore subsequent errors +&Ignorer les erreurs suivantes + +Retrying operation... + + +Serious Error +Erreur Grave + +Check for Program Updates +Recherche des Mises à Jour + +A new version of FreeFileSync is available: +Une nouvelle version de FreeFileSync est disponible + +Download now? +Télécharger maintenant ? + +&Download +&Télécharger + +FreeFileSync is up to date. +FreeFileSync est à jour. + +Unable to connect to sourceforge.net. +Impossible de se connecter à sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Impossible de trouver en ligne une nouvelle version de FreeFileSync.Voulez-vous le faire manuellement ? + +&Check + + +Symlink +Lien symbolique + +Folder +Dossier + +Full path +Chemin complet + +Name +Nom + +Relative path +Chemin relatif + +Base folder +Dossier de base + +Size +Taille + +Date +Date + +Extension +Extension + +Category +Catégorie + +Action +Action + +Drag && drop +Glisser && Déposer + +Close progress dialog +Fermer la fenêtre de progression + +Standby +Pause + +Log off +Quitter + +Shut down +Arrêter + +Hibernate +Figer + +Alternate comparison settings +configuration distincte de la comparaison + +Alternate synchronization settings +configuration distincte de la synchronisation + +Local filter +Filtre local + +Active +Actif + +None +Aucun + +Remove alternate settings +Supprimer les paramètres de rechange + +Clear filter settings +Effacer la configuration du filtrage + +Copy +Copier + +Paste +Coller + +Alternate Comparison Settings +Configuration Distincte de la Comparaison + +Alternate Synchronization Settings +Configuration Distincte de la Synchronisation + +Local Filter +Filtre Local + +&New +&Nouveau + +&Save +&Sauvegarder + +Save as &batch job... +Enregistrer en temps que job &batch... + +1. &Compare +1. &Comparer + +2. &Synchronize +2. &Synchroniser + +&Global settings +&Paramètres généraux + +&Language +&Langue + +&Find... +&Rechercher... + +&Export file list... +&Exportation de la liste des fichiers... + +&Tools +Ou&tils + +&Check now +&Contrôler maintenant + +Check &automatically once a week +Contrôler &automatiquement une fois par semaine + +&Check for new version +&Vérifier les nouvelles versions + +Compare +Comparer + +Cancel +Annuler + +Synchronize +Synchroniser + +Add folder pair +Ajout d'un couple de dossiers + +Remove folder pair +Supprimer le couple de dossiers + +Swap sides +Permuter les côtés + +Close search bar +Fermer la barre de recherche + +Find: +Rechercher : + +Match case +Respecter la casse + +Save as batch job +Enrgistrer en temps que fichier batch + +Hide excluded items +Cacher les éléments exclus + +Show filtered or temporarily excluded files +Afficher les fichiers filtrés ou temporairement exclus + +Number of files and folders that will be created +Nombre de fichiers et de dossiers qui seront créés + +Number of files that will be overwritten +Nombre de fichiers qui seront remplacés + +Number of files and folders that will be deleted +Nombre de fichiers et de dossiers qui seront supprimés + +Total bytes to copy +Nombre total d'octets à copier + +Select a variant: +Choisir une variante : + +Identify equal files by comparing modification time and size. +Reconnaître les fichiers identiques à l'aide de leur taille et de leur date. + +Identify equal files by comparing the file content. +Reconnaître les fichiers identiques à l'aide de leur contenu. + +Symbolic links: +Liens symboliques + +More information +Plus d'informations + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Identifier et propager les modifications des deux côtés. Suppressions, déplacements et conflits sont détectés automatiquement en utilisant une base de données. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Créer une copie miroir du dossier de gauche qui correspondra exactement à celui de droite après la synchronisation. + +Copy new and updated files to the right folder. +Copier les fichiers nouveaux ou mis à jour dans le dossier de droite. + +Configure your own synchronization rules. +Paramétrage de vos règles de synchronisation. + +Detect moved files +Détection des fichiers déplacés + +Requires database files. Not supported by all file systems. +Exige les fichiers de la base de données. Non supporté par tous les systèmes de fichiers. + +Delete files: +Supprimer les fichiers : + +Permanent +Permanent + +Delete or overwrite files permanently +Supprimer ou écraser les fichiers définitivement + +Recycle bin +Corbeille + +Back up deleted and overwritten files in the recycle bin +Sauvegarder les fichier détruits ou écrasés dans la Corbeille + +Versioning +Gestion des versions + +Move files to a user-defined folder +Déplacer les fichiers vers un dossier utilisateur + +Naming convention: +Convention de nommage : + +Show examples +Afficher les exemples + +Handle errors: +Gestion des erreurs : + +Ignore +Ignorer + +Hide all error and warning messages +Masquer tous les messages d'erreurs et les avertissements + +Pop-up +Pop-up + +Show pop-up on errors or warnings +Montrer les messages d'avertissement ou d'erreur + +On completion: +A la fin : + +Start synchronization now? +Démarrer la synchronisation maintenant ? + +Variant: +Variante : + +Statistics +Statistiques + +&Don't show this dialog again +&Ne plus afficher cette boîte de dialogue + +Items found: +Élements trouvés : + +Speed: +Vitesse : + +Time remaining: +Temps restant : + +Time elapsed: +Temps écoulé : + +Synchronizing... +Synchronisation en cours... + +Minimize to notification area +Réduction à la zone de notification + +Close +Fermer + +&Pause +&Pause + +Stop +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 + +Stop synchronization at first error +Arrêter la synchronisation à la première erreur + +Show progress dialog +Montrer la fenêtre de progression + +Save log: +Sauvegarder le fichier log : + +Limit: +Mimite : + +Limit maximum number of log files +Nombre maximal de fichiers log + +How can I schedule a batch job? +Comment plannifier un job batch ? + +&Recycle bin +&Corbeille + +Delete on both sides +Suppression des deux côtés + +Delete on both sides even if the file is selected on one side only +Suppression des deux côtés même si le fichier n'est sélectionné que d'un seul côté + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Sélectionnez les règles de filtrage pour exclure certains fichiers de la synchronisation. Entrez les chemins des fichiers par rapport à leur paire de dossiers. + +Include: +Inclure : + +Exclude: +Exclure : + +Time span: +Intervalle de temps : + +File size: +Taille du fichier : + +Minimum: +Minimum : + +Maximum: +Maximum : + +&Clear +&Effacer + +The following settings are used for all synchronization jobs. +Les paramètres suivants sont utilisés lors de toutes les synchronisations. + +Fail-safe file copy +Copie de fichiers sécurisé + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Copie vers un fichier temporaire (*.ffs_tmp) avant le remplacement de la cible. +Cela garantit la cohérence du système de fichiers en cas d'erreur grave. + + +(recommended) +(recommandé) + +Copy locked files +Copier les fichiers verrouillés + +Copy shared or locked files using the Volume Shadow Copy Service. +Copie les fichiers partagés ou verrouillés à l'aide du Service Volume Shadow Copy. + +(requires administrator rights) +(nécessite les droits administrateur) + +Copy file access permissions +Copie des droits d'accès aux fichiers + +Transfer file and folder permissions. +Transfert des autorisations des fichiers et dossiers. + +Automatic retry on error: +Nouvelle tentative automatique en cas d'erreur : + +Retry count: +Nombre de tentatives : + +Delay (in seconds): +Délai (en secondes) : + +Customize context menu: +Personnaliser le menu contextuel : + +Description +Description + +Restore hidden windows +Restaurer les fenêtres masquées + +&Default +&Défaut + +Source code written in C++ using: +Code source écrit en C++ utilisant : + +If you like FreeFileSync +Si vous aimez FreeFileSync + +Donate with PayPal +Faites un don avec PayPal + +Feedback and suggestions are welcome +Vos commentaires et vos suggestions sont les bienvenus + +Homepage +Accueil + +Email +Email + +Published under the GNU General Public License +Publié sous licence GNU General Public License + +Many thanks for localization: +Un grand merci pour la traduction à : + +Save as Batch Job +Sauvegarder comme Job Batch + +Delete Items +Supprimer les Éléments + +Global Settings +Configuration Générale + +Select Time Span +Sélection de l'Intervalle de Temps + +Folder Pairs +Paires de Dossiers + +Find +Rechercher + +Overview +Présentation + +Configuration +Configuration + +Main Bar +Barre Principale + +Filter Files +Filtrage des Fichiers + +Select View +Sélection de l'Affichage + +Open... +Ouvrir ... + +Save +Sauvegarder + +Compare both sides +Comparer les deux listes + +Comparison settings +Paramètres de comparaison + +Synchronization settings +Paramétrage de la synchronisation + +Start synchronization +Démarrer la synchronisation + +Confirm +Confirmer + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + + + +&Execute +&Exécuter + + +1 directory +%x directories + + +%x répertoire +%x répertoires + + + +1 file +%x files + + +%x fichier +%x fichiers + + + +%y of 1 row in view +%y of %x rows in view + + +vue de %y ligne sur %x +vue de %y lignes sur %x + + +Set direction: +Choix de la direction : + +multiple selection +sélection multiple + +Include via filter: +Inclure via le filtre : + +Exclude via filter: +Exclure à l'aide du filtre : + +Exclude temporarily +Exclure temporairement + +Include temporarily +Inclure temporairement + +Delete +Supprimer + +Include all +Inclure tout + +Exclude all +Exclure tout + +Show icons: +Afficher les icônes : + +Small +Petit + +Medium +Moyen + +Large +Grand + +Select time span... +Choisir un intervalle de temps... + +Default view +Vue par défaut + +Show "%x" +Afficher "%x" + +Last session +Dernière session + +Folder Comparison and Synchronization +Comparaison de dossiers et Synchronisation + +Configuration saved +Configuration enregistrée + +FreeFileSync batch +FreeFileSync batch + +Do you want to save changes to %x? +Voulez-vous enregistrer les modifications dans %x ? + +Never save &changes +Ne jamais sauvegarder les &modifications + +Do&n't save +&Ne pas Sauvegarder + +Filter +Filtre + +Show files that exist on left side only +Afficher les fichiers existant seulement à gauche + +Show files that exist on right side only +Afficher les fichiers existant seulement à droite + +Show files that are newer on left +Afficher les fichiers de gauche plus récents que ceux de droite + +Show files that are newer on right +Afficher les fichiers de droite plus récents que ceux de gauche + +Show files that are equal +Afficher les fichiers identiques + +Show files that are different +Afficher les fichiers différents + +Show conflicts +Afficher les conflits + +Show files that will be created on the left side +Afficher les fichiers qui seront créés à gauche + +Show files that will be created on the right side +Afficher les fichiers qui seront créés à droite + +Show files that will be deleted on the left side +Afficher les fichiers qui seront supprimés à gauche + +Show files that will be deleted on the right side +Afficher les fichiers qui seront supprimés à droite + +Show files that will be overwritten on left side +Afficher les fichiers qui seront écrasés à gauche + +Show files that will be overwritten on right side +Afficher les fichiers qui seront écrasés à droite + +Show files that won't be copied +Afficher les fichiers qui ne seront pas copiés + +Set as default +Définir par défaut + +All folders are in sync +Tous les dossiers sont synchronisés + +Synchronization Settings +Configuration de la Synchronisation + +Comparison Settings +Configuration de la Comparaison + +Cannot find %x +Impossible de trouver %x + +Comma-separated values +Valeurs séparées par une virgule + +File list exported +Liste des fichiers exportée + +Searching for program updates... +Recherche de mises à jour ... + +Scanning... +Lecture en cours... + +Comparing content... +Comparaison du contenu... + +Info +Info + +Warning +Attention + +Paused +En pause + +Initializing... +Initialisation... + +Stopped +Arrêté + +Completed +Terminé + +&Continue +&Continuer + +Log +Log + +Today +Aujourd'hui + +This week +Cette semaine + +This month +Ce mois + +This year +Cette année + +Last x days +Derniers x jours + +Byte +Octet + +KB +Ko + +MB +Mo + + +Do you really want to move the following item to the recycle bin? +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 ? + + +Move +Déplacer + + +Do you really want to delete the following item? +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 ? + + +Exclude +Exclure + +Direct +Direct + +Follow +Suivre + +Copy NTFS permissions +Copie des droits NTFS + +Integrate external applications into context menu. The following macros are available: +Inclure les applications externes dans le menu contextuel. Les macros suivantes sont disponibles : + +- full file or folder name +- nom du fichier ou du dossier complet + +- folder part only +- Partie dossier seulement + +- Other side's counterpart to %item_path% +- Duplication vers %item_path% + +- Other side's counterpart to %item_folder% +- Duplication vers %item_folder% + +Restore all hidden windows and warnings? +Reataurer toutes les fenêtres masquées et les avertissements ? + +Leave as unresolved conflict +Abandonner en tant que conflit non résolu + +Replace +Remplacer + +Move files and replace if existing +Déplacer les fichiers et remplacer ceux existant + +Time stamp +Horodatage + +Append a timestamp to each file name +Ajouter un horodatage à chaque fichier + +File +Fichier + +YYYY-MM-DD hhmmss +AAAA-MM-JJ hhmmss + +Files +Fichiers + +Items +Éléments + +Percentage +Pourcentage + +Cannot monitor directory %x. +Impossible de gérer le répertoire %x. + +Conversion error: +Erreur de conversion : + +Cannot delete file %x. +Impossible de supprimer le fichier %x. + +The file is locked by another process: +Le fichier est verrouillé par un autre process : + +Cannot move file %x to %y. +Impossible de déplacer le fichier %x vers %y. + +Cannot delete directory %x. +Impossible de supprimer le répertoire %x. + +Cannot write file attributes of %x. +Impossible d'écrire les attributs de fichier de %x. + +Cannot write modification time of %x. +Impossible d'écrire la date de modification de %x. + +Cannot read security context of %x. +Impossible de lire les paramètres de sécurité de %x. + +Cannot write security context of %x. +Impossible d'écrire les paramètres de sécurité de %x. + +Cannot read permissions of %x. +Impossible de lire les permissions de %x. + +Cannot write permissions of %x. +Impossible d'écrire les permissions de %x. + +Cannot create directory %x. +Impossible de créer le répertoire %x. + +Cannot create symbolic link %x. +Impossible de créer le lien symbolique %x. + +Cannot find system function %x. +Impossible de trouver la fonction système %x. + +Cannot copy file %x to %y. +Impossible de copier le fichier %x vers %y. + +Type of item %x is not supported: +Le type de l'élément %x n'est pas accepté : + +Cannot resolve symbolic link %x. +Impossible de résoudre le lien symbolique %x. + +Cannot open directory %x. +Impossible d'ouvrir le répertoire %x. + +Cannot enumerate directory %x. +Impossible de parcourir le répertoire %x. + +%x TB +%x To + +%x PB +%x Po + + +1 min +%x min + + +%x min +%x min + + + +1 hour +%x hours + + +%x heure +%x heures + + + +1 day +%x days + + +%x jour +%x jours + + +Unable to register to receive system messages. +Impossible d'enregistrer la réception des messages système. + +Cannot set privilege %x. +Impossible de fixer le privilège %x. + +Unable to suspend system sleep mode. +Impossible de suspendre le mode veille du système. + +Cannot change process I/O priorities. +Impossible de modifier les priorités E/S des tâches + +Unable to move %x to the recycle bin. +Impossible de déplacer %x dans la Corbeille. + +Cannot determine final path for %x. +Impossible de déterminer le chemin pour %x. + +Error Code %x: +Code erreur %x : + diff --git a/FreeFileSync/Build/Languages/german.lng b/FreeFileSync/Build/Languages/german.lng new file mode 100644 index 00000000..3a8e264e --- /dev/null +++ b/FreeFileSync/Build/Languages/german.lng @@ -0,0 +1,1526 @@ +
    + Deutsch + Zenju + de_DE + flag_germany.png + 2 + n == 1 ? 0 : 1 +
    + +Both sides have changed since last synchronization. +Beide Seiten wurden seit der letzten Synchronisation verändert. + +Cannot determine sync-direction: +Die Synchronisationsrichtung kann nicht bestimmt werden: + +No change since last synchronization. +Keine Änderungen seit der letzten Synchronisation. + +The database entry is not in sync considering current settings. +Der Datenbankeintrag ist nicht synchron gemäß den aktuellen Einstellungen. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Setze Standardwerte für Synchronisationsrichtungen: Alte Dateien werden durch neuere überschrieben. + +Checking recycle bin availability for folder %x... +Prüfe Verfügbarkeit des Papierkorbs für Ordner %x... + +Moving file %x to the recycle bin +Verschiebe Datei %x in den Papierkorb + +Moving folder %x to the recycle bin +Verschiebe Ordner %x in den Papierkorb + +Moving symbolic link %x to the recycle bin +Verschiebe Symbolischen Link %x in den Papierkorb + +Deleting file %x +Lösche Datei %x + +Deleting folder %x +Lösche Ordner %x + +Deleting symbolic link %x +Lösche Symbolischen Link %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +Der Papierkorb ist für die folgenden Ordner nicht verfügbar. Die Dateien werden stattdessen permanent gelöscht: + +An exception occurred +Eine Ausnahme ist aufgetreten + +A directory path is expected after %x. +Ein Verzeichnispfad wird nach %x erwartet. + +Syntax error +Syntaxfehler + +Cannot open file %x. +Die Datei %x kann nicht geöffnet werden. + +File %x does not contain a valid configuration. +Die Datei %x enthält keine gültige Konfiguration. + +Unequal number of left and right directories specified. +Ungleiche Anzahl linker und rechter Verzeichnisse angegeben. + +The config file must not contain settings at directory pair level when directories are set via command line. +Die Konfigurationsdatei darf keine Einstellungen auf Verzeichnispaarebene enthalten, wenn Verzeichnisse über die Befehlszeile gesetzt werden. + +Directories cannot be set for more than one configuration file. +Verzeichnisse können nicht für mehr als eine Konfigurationsdatei gesetzt werden. + +Command line +Kommandozeile + +Syntax: +Syntax: + +config files +Konfigurationsdateien + +directory +Verzeichnis + +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 directories for at most one config file. +Beliebige Anzahl von alternativen Verzeichnissen für höchstens eine Konfigurationsdatei. + +Cannot find the following folders: +Die folgenden Ordner wurden nicht gefunden: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Dieser Fehler kann ignoriert werden, um die Ordner als leer anzusehen und beim Synchronisieren automatisch zu erstellen. + +A folder input field is empty. +Ein Ordnereingabefeld ist leer. + +The corresponding folder will be considered as empty. +Der entsprechende Ordner wird als leer angesehen. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +Die folgenden Ordner haben abhängige Pfade. Achtung beim Festlegen der Synchronisationsregeln: + +File %x has an invalid date. +Die Datei %x hat ein ungültiges Datum. + +Date: +Datum: + +Files %x have the same date but a different size. +Die Dateien %x haben dasselbe Datum, aber unterschiedliche Größen. + +Size: +Größe: + +Items differ in attributes only +Die Elemente unterscheiden sich nur in Attributen + +Resolving symbolic link %x +Folge dem Symbolischen Link %x + +Comparing content of files %x +Vergleiche Inhalt der Dateien %x + +Generating file list... +Erzeuge Dateiliste... + +Starting comparison +Starte Vergleich + +Calculating sync directions... +Berechne Synchronisationsrichtungen... + +Out of memory. +Nicht genügend Arbeitsspeicher. + +Item exists on left side only +Element existiert nur links + +Item exists on right side only +Element existiert nur rechts + +Left side is newer +Linke Seite ist neuer + +Right side is newer +Rechte Seite ist neuer + +Items have different content +Elemente haben unterschiedlichen Inhalt + +Both sides are equal +Beide Seiten sind gleich + +Conflict/item cannot be categorized +Konflikt/Element, das nicht eingeordnet werden kann + +Copy new item to left +Kopiere neues Element nach links + +Copy new item to right +Kopiere neues Element nach rechts + +Delete left item +Lösche linkes Element + +Delete right item +Lösche rechtes Element + +Move file on left +Verschiebe linke Datei + +Move file on right +Verschiebe rechte Datei + +Overwrite left item +Überschreibe linkes Element + +Overwrite right item +Überschreibe rechtes Element + +Do nothing +Nichts tun + +Update attributes on left +Aktualisiere Attribute des linken Elements + +Update attributes on right +Aktualisiere Attribute des rechten Elements + +Database file %x is incompatible. +Die Datenbankdatei %x ist nicht kompatibel. + +Initial synchronization: +Erstmalige Synchronisation: + +Database file %x does not yet exist. +Die Datenbankdatei %x existiert noch nicht. + +Database file is corrupt: +Die Datenbankdatei ist beschädigt: + +Cannot write file %x. +Die Datei %x kann nicht geschrieben werden. + +Cannot read file %x. +Die Datei %x kann nicht gelesen werden. + +Database files do not share a common session. +Die Datenbankdateien teilen keine gemeinsame Sitzung. + +Searching for folder %x... +Suche Ordner %x... + +Cannot read file attributes of %x. +Die Dateiattribute von %x können nicht gelesen werden. + +Cannot get process information. +Prozessinformationen können nicht gelesen werden. + +Waiting while directory is locked: +Warte während das Verzeichnis gesperrt ist: + +Lock owner: +Sperreigentümer: + + +1 sec +%x sec + + +1 Sek. +%x Sek. + + +Detecting abandoned lock... +Ermittle aufgegebene Sperre... + +Creating file %x +Erstelle Datei %x + +Items processed: +Verarbeitete Elemente: + +Items remaining: +Verbleibende Elemente: + +Total time: +Gesamtzeit: + + +1 byte +%x bytes + + +1 Byte +%x Bytes + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Fehler beim Auswerten der Datei %x, Zeile %y, Spalte %z. + +Cannot set directory lock for %x. +Die Verzeichnissperre für %x kann nicht gesetzt werden. + +Scanning: +Suche Dateien: + + +1 thread +%x threads + + +1 Thread +%x Threads + + +Encoding extended time information: %x +Speichere erweiterte Zeitinformation: %x + +/sec +/sek + +%x items/sec +%x Elemente/sek + +Configuration file %x loaded partially only. +Die Konfigurationsdatei %x wurde nur teilweise geladen. + +Show in Explorer +Im Explorer anzeigen + +Open with default application +Mit Standardanwendung öffnen + +Browse directory +Verzeichnis öffnen + +Cannot access the Volume Shadow Copy Service. +Auf den Volumenschattenkopiedienst kann nicht zugegriffen werden. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Bitte benutzen Sie die FreeFileSync 64-Bit Version, um Schattenkopien auf diesem System zu erstellen. + +Cannot load file %x. +Die Datei %x kann nicht geladen werden. + +Cannot determine volume name for %x. +Der Laufwerksname für %x kann nicht bestimmt werden. + +Volume name %x is not part of file path %y. +Laufwerksname %x ist kein Teil des Dateipfades %y. + +Stop requested: Waiting for current operation to finish... +Stop wurde eingeleitet: Warte bis die aktuelle Operation beendet ist... + +Unable to create timestamp for versioning: +Der Zeitstempel für die Versionierung kann nicht erstellt werden: + +Cannot read the following XML elements: +Die folgenden XML-Elemente können nicht gelesen werden: + +&Open... +Ö&ffnen... + +Save &as... +Speichern &unter... + +&Quit +&Beenden + +&Program +&Programm + +&View help +&Hilfe anzeigen + +&About +&Über + +&Help +&Hilfe + +Usage: +Verwendung: + +1. Select folders to watch. +1. Zu überwachende Ordner wählen. + +2. Enter a command line. +2. Eine Befehlszeile angeben. + +3. Press 'Start'. +3. 'Start' drücken. + +To get started just import a .ffs_batch file. +Zum Schnelleinstieg einfach eine .ffs_batch Datei importieren. + +Folders to watch: +Zu überwachende Ordner: + +Add folder +Ordner hinzufügen + +Remove folder +Ordner entfernen + +Browse +Auswählen + +Select a folder +Ordner auswählen + +Idle time (in seconds): +Ruhezeit (in Sekunden): + +Idle time between last detected change and execution of command +Ruhezeit zwischen der letzten erkannten Änderung und dem Aufruf der Befehlszeile + +Command line: +Befehlszeile: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +Die Befehlszeile wird ausgelöst, wenn: +- Dateien oder Unterordner sich ändern +- neue Ordner erscheinen (z.B. Anschluss eines USB Sticks) + + +&Start +&Start + +About +Über + +Build: %x +Build: %x + +All files +Alle Dateien + +Automated Synchronization +Automatisierte Synchronisation + +Directory monitoring active +Verzeichnisüberwachung ist aktiv + +Waiting until all directories are available... +Warte bis alle Verzeichnisse verfügbar sind... + +Error +Fehler + +&Restore +&Wiederherstellen + +&Show error +&Zeige Fehler + +&Exit +&Beenden + +Incorrect command line: +Ungültige Befehlszeile: + +&Retry +&Wiederholen + +File content +Dateiinhalt + +File time and size +Datum und Größe + +Two way +Zwei Wege + +Mirror +Spiegeln + +Update +Aktualisieren + +Custom +Eigene + +Multiple... +Verschiedene... + +Moving file %x to %y +Verschiebe Datei %x nach %y + +Moving folder %x to %y +Verschiebe Ordner %x nach %y + +Moving symbolic link %x to %y +Verschiebe Symbolischen Link %x nach %y + +Removing old versions... +Entferne alte Versionen... + +Creating symbolic link %x +Erstelle Symbolischen Link %x + +Creating folder %x +Erstelle Ordner %x + +Overwriting file %x +Überschreibe Datei %x + +Overwriting symbolic link %x +Überschreibe Symbolischen Link %x + +Verifying file %x +Verifiziere Datei %x + +Updating attributes of %x +Aktualisiere Attribute von %x + +Creating a Volume Shadow Copy for %x... +Erstelle eine Volumenschattenkopie für %x... + +Data verification error: %x and %y have different content. +Verifizierungsfehler: %x und %y haben unterschiedlichen Inhalt. + +Cannot find %x. +%x wurde nicht gefunden. + +Target folder %x already existing. +Der Zielordner %x existiert bereits. + +Target folder input field must not be empty. +Das Eingabefeld für den Zielordner darf nicht leer sein. + +Please enter a target folder for versioning. +Bitte geben Sie einen Zielorder für die Versionierung ein. + +Source folder %x not found. +Der Quellordner %x wurde nicht gefunden. + +The following items have unresolved conflicts and will not be synchronized: +Die folgenden Elemente haben ungelöste Konflikte und werden nicht synchronisiert werden: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +Die folgenden Ordner unterscheiden sich erheblich. Stellen Sie sicher, dass die richtigen Ordner für die Synchronisation ausgewählt sind. + +Not enough free disk space available in: +Nicht genügend freier Speicher verfügbar unter: + +Required: +Benötigt: + +Available: +Verfügbar: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Ein Ordner wird verändert werden, der Teil mehrerer Ordnerpaare ist. Bitte überprüfen Sie die Synchronisationseinstellungen. + +Synchronizing folder pair: +Synchronisiere Ordnerpaar: + +Generating database... +Erzeuge Synchronisationsdatenbank... + +job name +Auftragsname + +Synchronization stopped +Synchronisation gestoppt + +Synchronization completed with errors +Synchronisation mit Fehlern abgeschlossen + +Synchronization completed with warnings +Synchronisation mit Warnungen abgeschlossen + +Nothing to synchronize +Es gibt nichts zu synchronisieren + +Synchronization completed successfully +Synchronisation erfolgreich abgeschlossen + +Saving log file %x... +Speichere Protokolldatei %x... + +You can switch to FreeFileSync's main window to resolve this issue. +Sie können auf FreeFileSyncs Hauptfenster wechseln, um das Problem zu beheben. + +&Don't show this warning again +&Diese Warnung nicht mehr anzeigen + +&Ignore +&Ignorieren + +&Switch +&Wechseln + +Switching to FreeFileSync's main window +Wechsle auf FreeFileSyncs Hauptfenster + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + +Automatische Wiederholung in 1 Sekunde... +Automatische Wiederholung in %x Sekunden... + + +&Ignore subsequent errors +&Nachfolgende Fehler ignorieren + +Retrying operation... +Wiederhole Operation... + +Serious Error +Schwerer Fehler + +Check for Program Updates +Suche nach neuer Programmversion + +A new version of FreeFileSync is available: +Eine neue Version von FreeFileSync ist verfügbar: + +Download now? +Jetzt herunterladen? + +&Download +&Download + +FreeFileSync is up to date. +FreeFileSync ist auf dem neuesten Stand. + +Unable to connect to sourceforge.net. +Es kann keine Verbindung zu Sourceforge.net aufgebaut werden. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Die aktuelle FreeFileSync Versionsnummer wurde online nicht gefunden. Möchten Sie manuell prüfen? + +&Check +&Prüfen + +Symlink +Symlink + +Folder +Ordner + +Full path +Absoluter Pfad + +Name +Name + +Relative path +Relativer Pfad + +Base folder +Basisordner + +Size +Größe + +Date +Datum + +Extension +Erweiterung + +Category +Kategorie + +Action +Aktion + +Drag && drop +Drag && Drop + +Close progress dialog +Schließe Fortschrittsdialog + +Standby +Energie sparen + +Log off +Abmelden + +Shut down +Herunterfahren + +Hibernate +Ruhezustand + +Alternate comparison settings +Alternative Vergleichseinstellungen + +Alternate synchronization settings +Alternative Synchronisationseinstellungen + +Local filter +Lokaler Filter + +Active +Aktiv + +None +Ohne + +Remove alternate settings +Alternative Einstellungen entfernen + +Clear filter settings +Filtereinstellungen löschen + +Copy +Kopieren + +Paste +Einfügen + +Alternate Comparison Settings +Alternative Vergleichseinstellungen + +Alternate Synchronization Settings +Alternative Synchronisationseinstellungen + +Local Filter +Lokaler Filter + +&New +&Neu + +&Save +&Speichern + +Save as &batch job... +Speichern als Batch-&Auftrag... + +1. &Compare +1. &Vergleichen + +2. &Synchronize +2. &Synchronisieren + +&Global settings +&Globale Einstellungen + +&Language +&Sprache + +&Find... +S&uchen... + +&Export file list... +Dateiliste e&xportieren... + +&Tools +E&xtras + +&Check now +&Jetzt prüfen + +Check &automatically once a week +&Automatisch wöchentlich prüfen + +&Check for new version +&Auf neue Version prüfen + +Compare +Vergleichen + +Cancel +Abbrechen + +Synchronize +Synchronisieren + +Add folder pair +Ordnerpaar hinzufügen + +Remove folder pair +Ordnerpaar entfernen + +Swap sides +Seiten vertauschen + +Close search bar +Suchleiste schließen + +Find: +Suchen: + +Match case +Groß-/Kleinschreibung + +Save as batch job +Als Batch-Auftrag speichern + +Hide excluded items +Ausgeschlossene Elemente verstecken + +Show filtered or temporarily excluded files +Gefilterte oder temporär ausgeschlossene Dateien zeigen + +Number of files and folders that will be created +Anzahl der zu erstellenden Dateien und Ordner + +Number of files that will be overwritten +Anzahl der zu überschreibenden Dateien + +Number of files and folders that will be deleted +Anzahl der zu löschenden Dateien und Ordner + +Total bytes to copy +Gesamtmenge der zu kopierenden Daten + +Select a variant: +Variante wählen: + +Identify equal files by comparing modification time and size. +Erkenne gleiche Dateien anhand der Änderungszeit und Größe. + +Identify equal files by comparing the file content. +Erkenne gleiche Dateien durch Vergleich des Dateiinhaltes. + +Symbolic links: +Symbolische Links: + +More information +Mehr Information + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Identifiziere und propagiere Änderungen auf beiden Seiten. Löschungen, Verschiebungen und Konflikte werden automatisch mit Hilfe einer Datenbank erkannt. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Eine Spiegelkopie des linken Ordners erstellen, indem der rechte Ordner angeglichen wird. + +Copy new and updated files to the right folder. +Neue und aktualisierte Dateien in den rechten Ordner kopieren. + +Configure your own synchronization rules. +Eigene Synchronisationsregeln definieren. + +Detect moved files +Verschobene Dateien erkennen + +Requires database files. Not supported by all file systems. +Benötigt Datenbankdateien. Wird nicht von allen Dateisystemen unterstützt. + +Delete files: +Dateien löschen: + +Permanent +Permanent + +Delete or overwrite files permanently +Dateien endgültig löschen oder überschreiben + +Recycle bin +Papierkorb + +Back up deleted and overwritten files in the recycle bin +Gelöschte und überschriebene Dateien im Papierkorb sichern + +Versioning +Versionierung + +Move files to a user-defined folder +Dateien in einen benutzerdefinierten Ordner verschieben + +Naming convention: +Namenskonvention: + +Show examples +Zeige Beispiele + +Handle errors: +Fehlerbehandlung: + +Ignore +Ignorieren + +Hide all error and warning messages +Alle Fehler- und Warnmeldungen unterdrücken + +Pop-up +Nachfragen + +Show pop-up on errors or warnings +Ein Auswahlfenster bei Fehlern oder Warnungen anzeigen + +On completion: +Nach Abschluss: + +Start synchronization now? +Synchronisation jetzt starten? + +Variant: +Variante: + +Statistics +Statistiken + +&Don't show this dialog again +&Diesen Dialog nicht mehr anzeigen + +Items found: +Gefundene Elemente: + +Speed: +Geschwindigkeit: + +Time remaining: +Verbleibende Zeit: + +Time elapsed: +Vergangene Zeit: + +Synchronizing... +Synchronisiere... + +Minimize to notification area +In das Benachrichtigungsfeld minimieren + +Close +Schließen + +&Pause +&Pause + +Stop +Stop + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Erstellt eine Batchdatei für die unbeaufsichtigte Synchronisation. Zum Starten die Datei doppelklicken oder in einen Taskplaner eintragen: %x + +Stop synchronization at first error +Synchronisation beim ersten Fehler anhalten + +Show progress dialog +Fortschrittsdialog zeigen + +Save log: +Protokoll speichern: + +Limit: +Limit: + +Limit maximum number of log files +Maximale Anzahl der Protokolldateien begrenzen + +How can I schedule a batch job? +Wie können Batch-Aufträge in den Taskplaner eingetragen werden? + +&Recycle bin +&Papierkorb + +Delete on both sides +Auf beiden Seiten löschen + +Delete on both sides even if the file is selected on one side only +Lösche auf beiden Seiten, auch wenn die Datei nur auf einer Seite markiert ist + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Wählen Sie Filterregeln aus, um einzelne Dateien von der Synchronisation auszuschließen. Geben Sie Dateipfade relativ zu ihrem zugehörigen Ordnerpaar an. + +Include: +Einschließen: + +Exclude: +Ausschließen: + +Time span: +Zeitspanne: + +File size: +Dateigröße: + +Minimum: +Minimum: + +Maximum: +Maximum: + +&Clear +&Löschen + +The following settings are used for all synchronization jobs. +Die folgenden Einstellungen werden für alle Synchronisationsaufgaben verwendet. + +Fail-safe file copy +Dateien ausfallsicher kopieren + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Kopiere in eine temporäre Datei (*.ffs_tmp) bevor das Ziel überschrieben wird. +Dadurch wird ein konsistenter Datenstand auch im schweren Fehlerfall garantiert. + + +(recommended) +(Empfohlen) + +Copy locked files +Gesperrte Dateien kopieren + +Copy shared or locked files using the Volume Shadow Copy Service. +Kopiere gesperrte Dateien mit Hilfe des Volumenschattenkopie-Dienstes. + +(requires administrator rights) +(Benötigt Administratorrechte) + +Copy file access permissions +Dateizugriffsberechtigungen kopieren + +Transfer file and folder permissions. +Übertrage Datei- und Ordnerberechtigungen. + +Automatic retry on error: +Automatische Wiederholung bei Fehlern: + +Retry count: +Wiederholungen: + +Delay (in seconds): +Verzögerung (in Sekunden): + +Customize context menu: +Kontextmenü anpassen: + +Description +Beschreibung + +Restore hidden windows +Versteckte Fenster wiederherstellen + +&Default +&Standard + +Source code written in C++ using: +Der Quellcode wurde in C++ geschrieben mit: + +If you like FreeFileSync +Wenn Sie FreeFileSync mögen + +Donate with PayPal +Mit PayPal spenden + +Feedback and suggestions are welcome +Feedback und Vorschläge sind willkommen + +Homepage +Homepage + +Email +Email + +Published under the GNU General Public License +Veröffentlicht unter der Allgemeinen Öffentlichen GNU-Lizenz + +Many thanks for localization: +Vielen Dank für die Lokalisation: + +Save as Batch Job +Als Batch-Auftrag speichern + +Delete Items +Elemente löschen + +Global Settings +Globale Einstellungen + +Select Time Span +Zeitspanne auswählen + +Folder Pairs +Ordnerpaare + +Find +Suchen + +Overview +Übersicht + +Configuration +Konfiguration + +Main Bar +Hauptleiste + +Filter Files +Dateien filtern + +Select View +Ansicht auswählen + +Open... +Öffnen... + +Save +Speichern + +Compare both sides +Beide Seiten vergleichen + +Comparison settings +Vergleichseinstellungen + +Synchronization settings +Synchronisationseinstellungen + +Start synchronization +Synchronisation starten + +Confirm +Bestätigen + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + +Soll der Befehl %y wirklich für ein Element ausgeführt werden? +Soll der Befehl %y wirklich für %x Elemente ausgeführt werden? + + +&Execute +&Ausführen + + +1 directory +%x directories + + +1 Verzeichnis +%x Verzeichnisse + + + +1 file +%x files + + +1 Datei +%x Dateien + + + +%y of 1 row in view +%y of %x rows in view + + +Zeige %y von 1 Zeile +Zeige %y von %x Zeilen + + +Set direction: +Setze Richtung: + +multiple selection +Mehrfachauswahl + +Include via filter: +Über Filter einschließen: + +Exclude via filter: +Über Filter ausschließen: + +Exclude temporarily +Temporär ausschließen + +Include temporarily +Temporär einschließen + +Delete +Löschen + +Include all +Alle einschließen + +Exclude all +Alle ausschließen + +Show icons: +Symbole anzeigen: + +Small +Klein + +Medium +Mittel + +Large +Groß + +Select time span... +Zeitspanne auswählen... + +Default view +Standardansicht + +Show "%x" +Zeige "%x" + +Last session +Letzte Sitzung + +Folder Comparison and Synchronization +Ordnervergleich und Synchronisation + +Configuration saved +Konfiguration gespeichert + +FreeFileSync batch +FreeFileSync Batch + +Do you want to save changes to %x? +Möchten Sie die Änderungen an %x speichern? + +Never save &changes +Änderungen nie &speichern + +Do&n't save +&Nicht speichern + +Filter +Filter + +Show files that exist on left side only +Nur links existierende Dateien anzeigen + +Show files that exist on right side only +Nur rechts existierende Dateien anzeigen + +Show files that are newer on left +Auf beiden Seiten existierende Dateien anzeigen; linke Datei ist neuer + +Show files that are newer on right +Auf beiden Seiten existierende Dateien anzeigen; rechte Datei ist neuer + +Show files that are equal +Gleiche Dateien anzeigen + +Show files that are different +Ungleiche Dateien anzeigen + +Show conflicts +Konflikte zeigen + +Show files that will be created on the left side +Dateien die links erstellt werden anzeigen + +Show files that will be created on the right side +Dateien die rechts erstellt werden anzeigen + +Show files that will be deleted on the left side +Dateien die links gelöscht werden anzeigen + +Show files that will be deleted on the right side +Dateien die rechts gelöscht werden anzeigen + +Show files that will be overwritten on left side +Dateien die links überschrieben werden anzeigen + +Show files that will be overwritten on right side +Dateien die rechts überschrieben werden anzeigen + +Show files that won't be copied +Dateien die nicht kopiert werden anzeigen + +Set as default +Als Standard festlegen + +All folders are in sync +Alle Ordner sind synchron + +Synchronization Settings +Synchronisationseinstellungen + +Comparison Settings +Vergleichseinstellungen + +Cannot find %x +%x wurde nicht gefunden. + +Comma-separated values +Kommagetrennte Werte + +File list exported +Dateiliste exportiert + +Searching for program updates... +Suche nach aktualisierten Programmversionen... + +Scanning... +Suche Dateien... + +Comparing content... +Vergleiche Dateiinhalt... + +Info +Info + +Warning +Warnung + +Select all +Alle auswählen + +Paused +Angehalten + +Initializing... +Initialisiere... + +Stopped +Angehalten + +Completed +Fertig + +&Continue +&Fortfahren + +Log +Protokoll + +Today +Heute + +This week +Diese Woche + +This month +Dieser Monat + +This year +Dieses Jahr + +Last x days +Letzte x Tage + +Byte +Byte + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Soll das folgende Element wirklich in den Papierkorb verschoben werden? +Sollen die folgenden %x Elemente wirklich in den Papierkorb verschoben werden? + + +Move +Verschieben + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Soll das folgende Element wirklich gelöscht werden? +Sollen die folgenden %x Elemente wirklich gelöscht werden? + + +Exclude +Ausschließen + +Direct +Direkt + +Follow +Folgen + +Copy NTFS permissions +NTFS Berechtigungen kopieren + +Integrate external applications into context menu. The following macros are available: +Integriert externe Anwendungen in das Kontextmenü. Die folgenden Makros stehen zur Verfügung: + +- full file or folder name +- kompletter Datei oder Ordnername + +- folder part only +- nur Ordneranteil + +- Other side's counterpart to %item_path% +- Entsprechung der anderen Seite zu %item_path% + +- Other side's counterpart to %item_folder% +- Entsprechung der anderen Seite zu %item_folder% + +Restore all hidden windows and warnings? +Sollen alle versteckten Fenster und Warnungen wiederhergestellt werden? + +Leave as unresolved conflict +Als unbehandelten Konflikt belassen + +Replace +Ersetzen + +Move files and replace if existing +Dateien verschieben und vorhandene ersetzen + +Time stamp +Zeitstempel + +Append a timestamp to each file name +Einen Zeitstempel an jeden Dateinamen anhängen + +File +Datei + +YYYY-MM-DD hhmmss +JJJJ-MM-TT hhmmss + +Files +Dateien + +Items +Elemente + +Percentage +Prozent + +Cannot monitor directory %x. +Das Verzeichnis %x kann nicht überwacht werden. + +Conversion error: +Fehler bei Konvertierung: + +Cannot delete file %x. +Die Datei %x kann nicht gelöscht werden. + +The file is locked by another process: +Die Datei wird von einem anderen Prozess gesperrt: + +Cannot move file %x to %y. +Die Datei %x kann nicht nach %y verschoben werden. + +Cannot delete directory %x. +Das Verzeichnis %x kann nicht gelöscht werden. + +Cannot write file attributes of %x. +Die Dateiattribute von %x können nicht geschrieben werden. + +Cannot write modification time of %x. +Die Änderungszeit von %x kann nicht geschrieben werden. + +Cannot read security context of %x. +Der Sicherheitskontext von %x kann nicht gelesen werden. + +Cannot write security context of %x. +Der Sicherheitskontext von %x kann nicht geschrieben werden. + +Cannot read permissions of %x. +Die Berechtigungen von %x können nicht gelesen werden. + +Cannot write permissions of %x. +Die Berechtigungen von %x können nicht geschrieben werden. + +Cannot create directory %x. +Das Verzeichnis %x kann nicht erstellt werden. + +Cannot create symbolic link %x. +Der Symbolische Link %x kann nicht erstellt werden. + +Cannot find system function %x. +Die Systemfunktion %x wurde nicht gefunden. + +Cannot copy file %x to %y. +Die Datei %x kann nicht nach %y kopiert werden. + +Type of item %x is not supported: +Der Typ des Elements %x wird nicht unterstützt: + +Cannot resolve symbolic link %x. +Der Symbolische Link %x kann nicht aufgelöst werden. + +Cannot open directory %x. +Das Verzeichnis %x kann nicht geöffnet werden. + +Cannot enumerate directory %x. +Das Verzeichnis %x kann nicht gelistet werden. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +1 Min. +%x Min. + + + +1 hour +%x hours + + +1 Stunde +%x Stunden + + + +1 day +%x days + + +1 Tag +%x Tage + + +Unable to register to receive system messages. +Die Registrierung zum Empfang von Systemmeldungen ist fehlgeschlagen. + +Cannot set privilege %x. +Das Privileg %x kann nicht gesetzt werden. + +Unable to suspend system sleep mode. +Der Schlafmodus des Betriebssystems kann nicht ausgesetzt werden. + +Cannot change process I/O priorities. +Die Eingabe/Ausgabe Prioritäten für den Prozess kann nicht geändert werden. + +Unable to move %x to the recycle bin. +%x kann nicht in den Papierkorb verschoben werden. + +Cannot determine final path for %x. +Der endgültige Pfad für %x kann nicht ermittelt werden. + +Error Code %x: +Fehlercode %x: + diff --git a/FreeFileSync/Build/Languages/greek.lng b/FreeFileSync/Build/Languages/greek.lng new file mode 100644 index 00000000..176f0409 --- /dev/null +++ b/FreeFileSync/Build/Languages/greek.lng @@ -0,0 +1,1515 @@ +
    + Eλληνικά + Γιώργος Δ. Γιαγλής + el_GR + flag_greece.png + 2 + n == 1 ? 0 : 1 +
    + +Both sides have changed since last synchronization. +Και οι δυο πλευρές έχουν αλλάξει από τον τελευταίο συγχρονισμό. + +Cannot determine sync-direction: +Δεν μπορεί να προσδιοριστεί η κατεύθυνση συγχρονισμού: + +No change since last synchronization. +Καμία αλλαγή από τον προηγούμενο συγχρονισμό. + +The database entry is not in sync considering current settings. +Με βάση τις τρέχουσες ρυθμίσεις η βάση δεδομένων δεν είναι συγχρονισμένη. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Ρύθμιση προεπιλεγμένης κατεύθυνσης συγχρονισμού: Τα νεότερα αρχεία θα αντικαταστήσουν τα παλιότερα. + +Checking recycle bin availability for folder %x... +Έλεγχος της διαθεσιμότητας του κάδου ανακύκλωσης για τον υποκατάλογο %x... + +Moving file %x to the recycle bin +Μεταφορά του αρχείου %x στον κάδο ανακύκλωσης + +Moving folder %x to the recycle bin +Μεταφορά του υποκαταλόγου %x στον κάδο ανακύκλωσης + +Moving symbolic link %x to the recycle bin +Μεταφορά του συμβολικού δεσμού %x στον κάδο ανακύκλωσης + +Deleting file %x +Διαγραφή του αρχείου %x + +Deleting folder %x +Διαγραφή του υποκαταλόγου %x + +Deleting symbolic link %x +Διαγραφή του συμβολικού δεσμού %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +Ο κάδος ανακύκλωσης δεν είναι διαθέσιμος για τους ακόλουθους υποκαταλόγους. Τα αρχεία θα διαγραφούν μόνιμα: + +An exception occurred +Παρουσιάστηκε σφάλμα + +A directory path is expected after %x. +Αναμένεται μια διαδρομή υποκαταλόγου μετά το %x. + +Syntax error +Σφάλμα σύνταξης + +Cannot open file %x. +Δεν είναι δυνατό το άνοιγμα του αρχείου %x. + +File %x does not contain a valid configuration. +Το αρχείο %x δεν περιέχει μια έγκυρη διάταξη. + +Unequal number of left and right directories specified. +Έχει οριστεί άνισος αριθμός υποκαταλόγων δεξιά από ό,τι αριστερά. + +The config file must not contain settings at directory pair level when directories are set via command line. +Το αρχείο διάταξης δεν πρέπει να περιλαμβάνει ρυθμίσεις στο επίπεδο του ζεύγους των υποκαταλόγων, όταν οι υποκατάλογοι ορίζονται μέσω της γραμμής εργασιών. + +Directories cannot be set for more than one configuration file. +Δεν μπορούν να οριστούν υποκατάλογοι για περισσότερα από ένα αρχεία διάταξης. + +Command line +Γραμμή εντολών + +Syntax: +Σύνταξη: + +config files +αρχεία διάταξης + +directory +υποκατάλογος + +Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. +Οποιοσδήποτε αριθμός αρχείων διάταξης .ffs_gui ή/και .ffs_batch του FreeFileSync + +Any number of alternative directories for at most one config file. +Οποιοσδήποτε αριθμός εναλλακτικών υποκαταλόγων για το πολύ ένα αρχείο διάταξης. + +A folder input field is empty. +Ένα πεδίο εισαγωγής υποκαταλόγων είναι άδειο. + +The corresponding folder will be considered as empty. +Ο αντίστοιχος υποκατάλογος θα θεωρηθεί κενός. + +Cannot find the following folders: +Οι ακόλουθοι υποκατάλογοι δεν ήταν δυνατό να βρεθούν: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Μπορείτε να αγνοήσετε αυτό το σφάλμα, για να αντιμετωπίσετε κάθε υποκατάλογο ως κενό. Οι υποκατάλογοι θα δημιουργηθούν αυτόματα κατά τη διάρκεια του συγχρονισμού. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +Οι παρακάτω υποκατάλογοι δεν έχουν σταθερή διαδρομή. Προσοχή όταν ορίσετε κανόνες συγχρονισμού: + +File %x has an invalid date. +Το αρχείο %x δεν έχει έγκυρη ημερομηνία. + +Date: +Ημερομηνία: + +Files %x have the same date but a different size. +Τα αρχεία %x έχουν την ίδια ημερομηνία αλλά διαφορετικό μέγεθος. + +Size: +Μέγεθος: + +Items differ in attributes only +Τα στοιχεία διαφέρουν μόνο ως προς τα χαρακτηριστικά τους + +Resolving symbolic link %x +Επίλυση του συμβολικού δεσμού %x + +Comparing content of files %x +Σύγκριση του περιεχομένου των αρχείων %x + +Generating file list... +Δημιουργία καταλόγου αρχείων... + +Starting comparison +Έναρξη της σύγκρισης + +Calculating sync directions... +Υπολογισμός των κατευθύνσεων συγχρονισμού... + +Out of memory. +Ανεπαρκής μνήμη. + +Item exists on left side only +Το αντικείμενο υπάρχει μόνο στην αριστερή πλευρά + +Item exists on right side only +Το αντικείμενο υπάρχει μόνο στη δεξιά πλευρά + +Left side is newer +Το αντικείμενο στην αριστερή πλευρά είναι πιο πρόσφατο + +Right side is newer +Το αντικείμενο στη δεξιά πλευρά είναι πιο πρόσφατο + +Items have different content +Τα αντικείμενα έχουν διαφορετικό περιεχόμενο + +Both sides are equal +Οι δυο πλευρές είναι ίδιες + +Conflict/item cannot be categorized +Διένεξη/το αντικείμενο δεν μπορεί να κατηγοριοποιηθεί + +Copy new item to left +Αντιγραφή του νέου στοιχείου στα αριστερά + +Copy new item to right +Αντιγραφή του νέου στοιχείου στα δεξιά + +Delete left item +Διαγραφή του στοιχείου στα αριστερά + +Delete right item +Διαγραφή του στοιχείου στα δεξιά + +Move file on left +Μεταφορά του αρχείου που βρίσκεται αριστερά + +Move file on right +Μεταφορά του αρχείου που βρίσκεται δεξιά + +Overwrite left item +Αντικατάσταση του στοιχείου που βρίσκεται στα αριστερά + +Overwrite right item +Αντικατάσταση του στοιχείου που βρίσκεται στα δεξιά + +Do nothing +Καμία ενέργεια + +Update attributes on left +Ενημέρωση των στοιχείων στα αριστερά + +Update attributes on right +Ενημέρωση των στοιχείων στα δεξιά + +Database file %x is incompatible. +Το αρχείο βάσης δεδομένων %x δεν είναι συμβατό + +Initial synchronization: +Αρχικός συγχρονισμός: + +Database file %x does not yet exist. +Το αρχείο βάσης δεδομένων %x δεν υπάρχει ακόμα. + +Database file is corrupt: +Το αρχείο βάσης δεδομένων είναι αλλοιωμένο: + +Cannot write file %x. +Δεν μπορεί να γίνει εγγραφή του αρχείου %x. + +Cannot read file %x. +Δεν μπορεί να γίνει ανάγνωση του αρχείου %x. + +Database files do not share a common session. +Τα αρχεία βάσης δεδομένων δεν έχουν χρησιμοποιηθεί από κοινού σε συγχρονισμό. + +Searching for folder %x... +Αναζήτηση του υποκαταλόγου %x... + +Cannot read file attributes of %x. +Δεν μπορεί να γίνει ανάγνωση των χαρακτηριστικών του αρχείου %x. + +Cannot get process information. +Δεν μπορούν να ληφθούν πληροφορίες για τις τρέχουσες διαδικασίες. + +Waiting while directory is locked (%x)... +Αναμονή μέχρι να κλειδωθεί ο υποκατάλογος (%x)... + + +1 sec +%x sec + + +1 δ/λεπτο +%x δ/λεπτα + + +Creating file %x +Δημιουργία του αρχείου %x + +Items processed: +Επεξεργάστηκαν στοιχεία: + +Items remaining: +Περισσεύουν στοιχεία: + +Total time: +Συνολική διάρκεια: + + +1 byte +%x bytes + + +1 byte +%x bytes + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Σφάλμα κατά την ανάλυση του αρχείου %x, γραμμή %y, στήλη %z. + +Cannot set directory lock for %x. +Ο υποκατάλογος για το %x δεν μπορεί να κλειδωθεί. + +Scanning: +Ανίχνευση: + + +1 thread +%x threads + + +1 νήμα +%x νήματα + + +Encoding extended time information: %x +Κωδικοποίηση εκτεταμένων πληροφοριών για την ώρα: %x + +/sec +/δευτερόλεπτο + +%x items/sec +%x στοιχεία + +Configuration file %x loaded partially only. +Το αρχείο διάταξης %x έχει φορτωθεί μόνο κατά ένα μέρος. + +Show in Explorer +Εμφάνιση στην Εξερεύνηση + +Open with default application +Άνοιγμα με την προεπιλεγμένη εφαρμογή + +Browse directory +Αναζήτηση υποκαταλόγου + +Cannot access the Volume Shadow Copy Service. +Δεν είναι δυνατή η πρόσβαση στην Υπηρεσία Σκιώδους Αντίγραφου Τόμου. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Χρησιμοποιήστε την 64-μπιτη έκδοση του FreeFileSync για να δημιουργήσετε σκιώδη αντίγραφα σε αυτόν τον υπολογιστή. + +Cannot load file %x. +Το αρχείο %x δεν ήταν δυνατόν να φορτωθεί. + +Cannot determine volume name for %x. +Δεν μπορεί να προσδιοριστεί το όνομα τόμου για το %x. + +Volume name %x is not part of file path %y. +Το όνομα τόμου %x δεν είναι μέρος της διαδρομής %y. + +Stop requested: Waiting for current operation to finish... +Αίτημα διακοπής: Αναμονή για την ολοκλήρωση της τρέχουσας εργασίας... + +Unable to create timestamp for versioning: +Αδυναμία δημιουργίας χρονικής σήμανσης για διατήρηση εκδόσεων: + +Cannot read the following XML elements: +Δεν ήταν δυνατό να αναγνωσθούν τα ακόλουθα στοιχεία XML: + +&Open... +Ά&νοιγμα... + +Save &as... +Αποθήκευση &ως... + +&Quit +Έ&ξοδος + +&Program +&Πρόγραμμα + +&View help +&Προβολή βοήθειας + +&About +&Σχετικά + +&Help +&Βοήθεια + +Usage: +Χρήση: + +1. Select folders to watch. +1. Επιλέξτε τους υποκαταλόγους που θα παρακολουθούνται. + +2. Enter a command line. +2. Εισάγετε μια γραμμή εντολών. + +3. Press 'Start'. +3. Πατήστε το 'Έναρξη'. + +To get started just import a .ffs_batch file. +Για να ξεκινήσετε μπορείτε απλά να εισάγετε ένα αρχείο .ffs_batch. + +Folders to watch: +Υποκατάλογοι για παρακολούθηση: + +Add folder +Προσθήκη υποκαταλόγου + +Remove folder +Διαγραφή του υποκαταλόγου + +Browse +Αναζήτηση + +Select a folder +Επιλογή υποκαταλόγου + +Idle time (in seconds): +Λανθάνων χρόνος (σε δευτερόλεπτα): + +Idle time between last detected change and execution of command +Λανθάνων χρόνος μεταξύ τελευταίας αλλαγής που ανιχνεύθηκε και εκτέλεσης της εντολής + +Command line: +Γραμμή εντολών + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +Αυτή η εντολή πραγματοποιείται εάν: +-αρχεία ή υποκτάλογοι αλλάξουν +-παρουσιαστούν νέοι υποκατάλογοι (π.χ. εισαχθεί ένα φλασάκι USB) + + +&Start +Έ&ναρξη + +About +Σχετικά με το... + +Build: %x +Δημιουργήθηκε : %x + +All files +Όλα τα αρχεία + +Automated Synchronization +Αυτοματοποιημένος Συγχρονισμός + +Directory monitoring active +Η παρακολούθηση των υποκαταλόγων είναι ενεργή + +Waiting until all directories are available... +Αναμονή μέχρι να είναι διαθέσιμοι όλοι οι υποκατάλογοι... + +Error +Σφάλματα + +&Restore +&Επαναφορά + +&Show error +&Εμφάνιση σφάλματος + +&Exit +Έ&ξοδος + +Incorrect command line: +Εσφαλμένη γραμμή εντολών: + +&Retry +&Επανάληψη + +File content +Περιεχόμενο αρχείων + +File time and size +Ημερομηνία και μέγεθος αρχείων + +Two way +Διπλής κατεύθυνσης + +Mirror +Κατοπτρισμός + +Update +Ενημέρωση + +Custom +Εξατομίκευση + +Multiple... +Πολλαπλές ρυθμίσεις... + +Moving file %x to %y +Μεταφορά του αρχείου %x στο %y + +Moving folder %x to %y +Μεταφορά του υποκαταλόγου %x στο %y + +Moving symbolic link %x to %y +Μεταφορά του συμβολικού δεσμού %x στο %y + +Removing old versions... +Διαγραφή παλιών εκδόσεων... + +Creating symbolic link %x +Δημιουργία του συμβολικού δεσμού %x + +Creating folder %x +Δημιουργία του υποκαταλόγου %x + +Overwriting file %x +Αντικατάσταση του αρχείου %x + +Overwriting symbolic link %x +Αντικατάσταση του συμβολικού δεσμού %x + +Verifying file %x +Επικύρωση του αρχείου %x + +Updating attributes of %x +Ενημέρωση των χαρακτηριστικών αρχείου του %x + +Cannot find %x. +Το %x δεν μπορεί να βρεθεί. + +Target folder %x already existing. +Ο υποκατάλογος-στόχος %x υπάρχει ήδη. + +Target folder input field must not be empty. +Το πεδίο εισαγωγής του υποκαταλόγου-στόχου πρέπει να μην είναι κενό. + +Please enter a target folder for versioning. +Παρακαλώ εισάγετε έναν υποκατάλογο για αποθήκευση εκδόσεων. + +Source folder %x not found. +Ο υποκατάλογος %x δε βρέθηκε. + +The following items have unresolved conflicts and will not be synchronized: +Τα ακόλουθα στοιχεία έχουν ανεπίλυτες διενέξεις και δε θα συγχρονιστούν: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +Οι ακόλουθοι υποκατάλογοι είναι σημαντικά διαφορετικοί. Σιγουρευτείτε ότι έχετε επιλέξει τους κατάλληλους υποκαταλόγους για συγχρονισμό. + +Not enough free disk space available in: +Δεν υπάρχει αρκετός διαθέσιμος χώρος στο δίσκο: + +Required: +Απαιτείται: + +Available: +Διαθέσιμος: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Θα τροποποιηθεί ένας υποκατάλογος που είναι μέρος από πολλαπλά ζεύγη υποκαταλόγων. Παρακαλούμε επανελέγξτε τις ρυθμίσεις συγχρονισμού. + +Synchronizing folder pair: +Συγχρονίζεται το ζευγάρι υποκαταλόγων: + +Generating database... +Δημιουργία βάσης δεδομένων... + +Creating a Volume Shadow Copy for %x... +Δημιουργία Σκιώδους Αντίγραφου Τόμου για το %x... + +Data verification error: %x and %y have different content. +Σφάλμα επαλήθευσης δεδομένων: το %x και το %y έχουν διαφορετικό περιεχόμενο. + +job name +όνομα ενέργειας + +Synchronization stopped +Ο συγχρονισμός διακόπηκε + +Synchronization completed with errors +Ο συγχρονισμός ολοκληρώθηκε με σφάλματα + +Synchronization completed with warnings +Ο συγχρονισμός ολοκληρώθηκε με προειδοποιήσεις + +Nothing to synchronize +Δεν υπάρχει τίποτα προς συγχρονισμό + +Synchronization completed successfully +Ο συγχρονισμός ολοκληρώθηκε επιτυχώς + +Saving log file %x... +Αποθήκευση του αρχείου καταγραφής %x... + +You can switch to FreeFileSync's main window to resolve this issue. +Μπορείτε να επιστρέψετε στο κύριο παράθυρο του FreeFileSync για να επιλύσετε αυτό το θέμα. + +&Don't show this warning again +&Να μην εμφανιστεί ξανά αυτή η προειδοποίηση + +&Ignore +&Παράβλεψη + +&Switch +&Εναλλαγή + +Switching to FreeFileSync's main window +Επιστροφή στο κύριο παράθυρο του FreeFileSync + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + +Αυτόματη επανάληψη σε 1 δευτερόλεπτο... +Αυτόματη επανάληψη σε %x δευτερόλεπτα... + + +&Ignore subsequent errors +&Αγνόηση επόμενων σφαλμάτων + +Retrying operation... + + +Serious Error +Σοβαρό Σφάλμα + +Check for Program Updates +Έλεγχος για Ενημερώσεις του Προγράμματος + +A new version of FreeFileSync is available: +Μια νέα έκδοση του FreeFileSync είναι διαθέσιμη: + +Download now? +Λήψη τώρα; + +&Download +&Λήψη + +FreeFileSync is up to date. +Το FreeFileSync είναι ενημερωμένο. + +Unable to connect to sourceforge.net. +Δεν είναι δυνατή η σύνδεση με το sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Ο αριθμός της τρέχουσας έκδοσης του FreeFileSync δεν βρέθηκε στο δίκτυο. Θέλετε να το ελέγξετε εσείς; + +&Check + + +Symlink +Συμβολικός δεσμός + +Folder +Υποκατάλογος + +Full path +Πλήρης διαδρομή + +Name +Όνομα + +Relative path +Σχετική διαδρομή + +Base folder +Βασικός υποκατάλογος + +Size +Μέγεθος + +Date +Ημερομηνία + +Extension +Επέκταση + +Category +Κατηγορία + +Action +Ενέργεια + +Drag && drop +Μεταφορά && Απόθεση + +Close progress dialog +Κλείσιμο του παράθυρου αναφοράς + +Standby +Αναστολή λειτουργίας + +Log off +Αποσύνδεση + +Shut down +Τερματισμός λειτουργίας + +Hibernate +Αδρανοποίηση + +Alternate comparison settings +Διαφοροποιημένες ρυθμίσεις σύγκρισης + +Alternate synchronization settings +Διαφοροποιημένες ρυθμίσεις συγχρονισμού + +Local filter +Τοπικό φίλτρο + +Active +Ενεργό + +None +Καθόλου + +Remove alternate settings +Διαγραφή των διαφοροποιημένων ρυθμίσεων + +Clear filter settings +Διαγραφή όλων των ρυθμίσεων φίλτρου + +Copy +Αντιγραφή + +Paste +Επικόλληση + +Alternate Comparison Settings +Διαφοροποιημένες Ρυθμίσεις Σύγκρισης + +Alternate Synchronization Settings +Διαφοροποιημένες Ρυθμίσεις Συγχρονισμού + +Local Filter +Τοπικό Φίλτρο + +&New +&Δημιουργία + +&Save +&Αποθήκευση + +Save as &batch job... +Αποθήκευση ως δέσ&μη ενεργειών... + +1. &Compare +1. &Σύγκριση + +2. &Synchronize +2. Συ&γχρονισμός + +&Global settings +Γενικές &ρυθμίσεις + +&Language +&Γλώσσα + +&Find... +&Εύρεση + +&Export file list... +Ε&ξαγωγή καταλόγου αρχείων... + +&Tools +&Εργαλεία + +&Check now +Έλεγχος &τώρα + +Check &automatically once a week +&Aυτόματος έλεγχος μια φορά την εβδομάδα + +&Check for new version +Έλεγχος για &νέα έκδοση + +Compare +Σύγκριση + +Cancel +Άκυρο + +Synchronize +Συγχρονισμός + +Add folder pair +Προσθήκη ζεύγους υποκαταλόγων + +Remove folder pair +Διαγραφή του ζεύγους υποκαταλόγων + +Swap sides +Ανταλλαγή πλευρών + +Close search bar +Κλείσιμο γραμμής εύρεσης + +Find: +Εύρεση: + +Match case +Αντιστοίχιση πεζών-κεφαλαίων + +Save as batch job +Αποθήκευση ως δέσμη ενεργειών + +Hide excluded items +Απόκρυψη στοιχείων που εξαιρέθηκαν + +Show filtered or temporarily excluded files +Εμφάνιση φιλτραρισμένων ή προσωρινά εξαιρεθέντων αρχείων + +Number of files and folders that will be created +Αριθμός αρχείων και υποκαταλόγων που θα δημιουργηθούν + +Number of files that will be overwritten +Αριθμός αρχείων και υποκαταλόγων που θα αντικατασταθούν + +Number of files and folders that will be deleted +Αριθμός αρχείων και υποκαταλόγων που θα διαγραφούν + +Total bytes to copy +Συνολικός αριθμός bytes προς αντιγραφή + +Select a variant: +Επιλογή μεθόδου: + +Identify equal files by comparing modification time and size. +Αναγνώριση των ταυτόσημων αρχείων με σύγκριση του χρόνου τροποποίησης και του μεγέθους. + +Identify equal files by comparing the file content. +Αναγνώριση των ταυτόσημων αρχείων με σύγκριση του περιεχομένου τους. + +Symbolic links: +Συμβολικοί δεσμοί: + +More information +Περισσότερες πληροφορίες + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Αναγνώριση και εφαρμογή αλλαγών και στις δυο πλευρές. Οι διαγραφές, οι μεταφορές και οι διενέξεις αναγνωρίζονται αυτόματα με τη βοήθεια μιας βάσης δεδομένων. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Δημιουργία ενός κατοπτρικού αντιγράφου του υποκαταλόγου αριστερά, ώστε μετά το συγχρονισμό ο υποκατάλογος δεξιά να είναι ένα ακριβές αντίγραφό του. + +Copy new and updated files to the right folder. +Αντιγραφή των νέων και των τροποποιημένων αρχείων στον υποκατάλογο δεξιά. + +Configure your own synchronization rules. +Ορίστε τους δικούς σας κανόνες συγχρονισμού. + +Detect moved files +Ανίχνευση των αρχείων που μεταφέρθηκαν + +Requires database files. Not supported by all file systems. +Απαιτεί αρχεία βάσης δεδομένων. Δεν υποστηρίζεται από όλα τα συστήματα αρχείων. + +Delete files: +Διαγραφή αρχείων: + +Permanent +Μόνιμα + +Delete or overwrite files permanently +Μόνιμη διαγραφή ή αντικατάσταση των αρχείων + +Recycle bin +Κάδος ανακύκλωσης + +Back up deleted and overwritten files in the recycle bin +Μεταφορά όλων των διαγραμμένων και αντικατεστημένων αρχείων στον κάδο ανακύκλωσης. + +Versioning +Διατήρηση παλιών εκδόσεων + +Move files to a user-defined folder +Μεταφορά αρχείων σε έναν υποκατάλογο που ορίζεται από το χρήστη + +Naming convention: +Τρόπος ονομασίας: + +Show examples +Εμφάνιση παραδειγμάτων + +Handle errors: +Χειρισμός σφαλμάτων: + +Ignore +Παράβλεψη + +Hide all error and warning messages +Απόκρυψη όλων των σφαλμάτων και προειδοποιήσεων + +Pop-up +Αναδυόμενο μήνυμα + +Show pop-up on errors or warnings +Εμφάνιση αναδυόμενου παράθυρου σε σφάλματα ή προειδοποιήσεις + +On completion: +Μετά την ολοκλήρωση: + +Start synchronization now? +Να ξεκινήσει ο συγχρονισμός τώρα; + +Variant: +Μέθοδος: + +Statistics +Στατιστικά + +&Don't show this dialog again +&Να μην εμφανιστεί ξανά αυτό το μήνυμα + +Items found: +Βρέθηκαν στοιχεία: + +Speed: +Ταχύτητα: + +Time remaining: +Απομένει χρόνος: + +Time elapsed: +Πέρασε χρόνος: + +Synchronizing... +Γίνεται συγχρονισμός... + +Minimize to notification area +Ελαχιστοποίηση στην περιοχή ειδοποιήσεων + +Close +Κλείσιμο + +&Pause +&Παύση + +Stop +Διακοπή + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Δημιουργία ενός αρχείου δέσμης για αυτόματο συγχρονισμό. Για να ξεκινήσετε, κάντε διπλό κλικ σε αυτό το αρχείο ή ενσωματώστε το σε ένα χρονοδιάγραμμα εργασιών: %x + +Stop synchronization at first error +Διακοπή του συγχρονισμού με το πρώτο σφάλμα + +Show progress dialog +Εμφάνιση της αναφοράς προόδου + +Save log: +Αποθήκευση αρχείου καταγραφής: + +Limit: +Όριο: + +Limit maximum number of log files +Μέγιστος αριθμός αρχείων καταγραφής + +How can I schedule a batch job? +Πώς μπορώ να προγραμματίσω μια εργασία με δέσμη ενεργειών; + +&Recycle bin +&Κάδος ανακύκλωσης + +Delete on both sides +Διαγραφή και στις δυο πλευρές + +Delete on both sides even if the file is selected on one side only +Διαγραφή και στις δυο πλευρές, ακόμα κι αν το αρχείο έχει επιλεχθεί μόνο στη μια πλευρά + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Επιλογή κανόνων φιλτραρίσματος, ώστε να αποκλειστούν ορισμένα αρχεία από το συγχρονισμό. Εισάγετε τις διαδρομές των αρχείων σχετικά με το αντίστοιχο ζεύγος υποκαταλόγων. + +Include: +Συμπερίληψη: + +Exclude: +Αποκλεισμός: + +Time span: +Χρονικό εύρος: + +File size: +Μέγεθος αρχείου: + +Minimum: +Ελάχιστο: + +Maximum: +Μέγιστο + +&Clear +&Καθαρισμός + +The following settings are used for all synchronization jobs. +Οι ακόλουθες ρυθμίσεις χρησιμοποιούνται για όλες τις εργασίες συγχρονισμού. + +Fail-safe file copy +Ασφαλής αντιγραφή αρχείων + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Αντιγραφή σε ένα προσωρινό αρχείο (*.ffs_tmp) πριν την αντικατάσταση του προορισμού. +Αυτό εγγυάται μια συνεπή κατάσταση ακόμα και στην περίπτωση σοβαρού σφάλματος. + + +(recommended) +(προτεινόμενο) + +Copy locked files +Αντιγραφή κλειδωμένων αρχείων + +Copy shared or locked files using the Volume Shadow Copy Service. +Αντιγραφή των κοινών ή κλειδωμένων αρχείων με τη χρήση της Υπηρεσίας Σκιωδών Αντιγράφων Τόμου. + +(requires administrator rights) +(απαιτεί δικαιώματα διαχειριστή) + +Copy file access permissions +Αντιγραφή των αδειών προσπέλασης των αρχείων + +Transfer file and folder permissions. +Μεταφορά των αδειών προσπέλασης αρχείων και υποκαταλόγων. + +Automatic retry on error: +Αυτόματη επανάληψη σε περίπτωση σφάλματος: + +Retry count: +Αριθμός προσπαθειών: + +Delay (in seconds): +Καθυστέρηση (σε δευτερόλεπτα): + +Customize context menu: +Προσαρμογή μενού επιλογών: + +Description +Περιγραφή + +Restore hidden windows +Επαναφορά των κρυμμένων διαλόγων + +&Default +&Προεπιλογή + +Source code written in C++ using: +Ο πηγαίος κώδικας γράφτηκε σε C++ χρησιμοποιώντας τα: + +If you like FreeFileSync +Αν σας αρέσει το FreeFileSync + +Donate with PayPal +Κάντε μια δωρεά μέσω PayPal + +Feedback and suggestions are welcome +Τα σχόλια και οι προτάσεις σας είναι ευπρόσδεκτα + +Homepage +Ιστοσελίδα + +Email +Email + +Published under the GNU General Public License +Διανέμεται υπό την Γενική Άδεια Δημόσιας Χρήσης GNU + +Many thanks for localization: +Πολλές ευχαριστίες για τις μεταφράσεις: + +Save as Batch Job +Αποθήκευση ως Δέσμη Εργασιών + +Delete Items +Διαγραφή Στοιχείων + +Global Settings +Γενικές Ρυθμίσεις + +Select Time Span +Επιλογή Χρονικού Εύρους + +Folder Pairs +Ζεύγη Υποκαταλόγων + +Find +Αναζήτηση + +Overview +Σύνοψη + +Configuration +Διάταξη + +Main Bar +Κύρια Γραμμή + +Filter Files +Φιλτράρισμα Αρχείων + +Select View +Επιλογή Εμφάνισης + +Open... +Άνοιγμα... + +Save +Αποθήκευση + +Compare both sides +Σύγκριση των δύο πλευρών + +Comparison settings +Ρυθμίσεις σύγκρισης + +Synchronization settings +Ρυθμίσεις συγχρονισμού + +Start synchronization +Έναρξη του συγχρονισμού + +Confirm +Επιβεβαίωση + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + + + +&Execute +&Εκτέλεση + + +1 directory +%x directories + + +1 υποκατάλογος +%x υποκατάλογοι + + + +1 file +%x files + + +1 αρχείο +%x αρχεία + + + +%y of 1 row in view +%y of %x rows in view + + +%y από τη 1 φανερή γραμμή +%y από τις %x φανερές γραμμές + + +Set direction: +Επιλογή κατεύθυνσης: + +multiple selection +πολλαπλή επιλογή + +Include via filter: +Συμπερίληψη μέσω του φίλτρου: + +Exclude via filter: +Εξαίρεση με βάση το φίλτρο: + +Exclude temporarily +Προσωρινή εξαίρεση + +Include temporarily +Προσωρινή συμπερίληψη + +Delete +Διαγραφή + +Include all +Συμπερίληψη όλων + +Exclude all +Εξαίρεση όλων + +Show icons: +Εμφάνιση εικονιδίων: + +Small +Μικρό + +Medium +Μεσαίο + +Large +Μεγάλο + +Select time span... +Επιλέξτε το χρονικό εύρος... + +Default view +Προεπιλεγμένη εμφάνιση + +Show "%x" +Εμφάνιση της γραμμής "%x" + +Last session +Τελευταία χρήση + +Folder Comparison and Synchronization +Σύγκριση υποκαταλόγων και Συγχρονισμός + +Configuration saved +Η διάταξη αποθηκεύτηκε + +FreeFileSync batch +Δέσμη ενεργειών του FreeFileSync + +Do you want to save changes to %x? +Θέλετε να αποθηκεύσετε τις αλλαγές στο %x; + +Never save &changes +Να &μην αποθηκεύονται οι αλλαγές + +Do&n't save +Να &μην αποθηκευθούν + +Filter +Φίλτρο + +Show files that exist on left side only +Εμφάνιση των αρχείων που υπάρχουν μόνο στα αριστερά + +Show files that exist on right side only +Εμφάνιση των αρχείων που υπάρχουν μόνο στα δεξιά + +Show files that are newer on left +Εμφάνιση των αρχείων που είναι πιο πρόσφατα στα αριστερά + +Show files that are newer on right +Εμφάνιση των αρχείων που είναι πιο πρόσφατα στα δεξιά + +Show files that are equal +Εμφάνιση των αρχείων που είναι ίδια + +Show files that are different +Εμφάνιση των αρχείων που είναι διαφορετικά + +Show conflicts +Εμφάνιση διενέξεων + +Show files that will be created on the left side +Εμφάνιση των αρχείων που θα δημιουργηθούν στα αριστερά + +Show files that will be created on the right side +Εμφάνιση των αρχείων που θα δημιουργηθούν στα δεξιά + +Show files that will be deleted on the left side +Εμφάνιση των αρχείων που θα διαγραφούν στα αριστερά + +Show files that will be deleted on the right side +Εμφάνιση των αρχείων που θα διαγραφούν στα δεξιά + +Show files that will be overwritten on left side +Εμφάνιση των αρχείων που θα αντικατασταθούν στα αριστερά + +Show files that will be overwritten on right side +Εμφάνιση των αρχείων που θα αντικατασταθούν στα αριστερά + +Show files that won't be copied +Εμφάνιση των αρχείων που δε θα αντιγραφούν + +Set as default +Ορισμός ως προεπιλογής + +All folders are in sync +Όλοι οι υποκατάλογοι είναι συγχρονισμένοι + +Synchronization Settings +Ρυθμίσεις συγχρονισμού + +Comparison Settings +Ρυθμίσεις Σύγκρισης + +Cannot find %x +Δεν μπορεί να βρεθεί το %x + +Comma-separated values +Αρχείο χωρισμένο με κόμματα + +File list exported +Ο κατάλογος των αρχείων έχει εξαχθεί + +Searching for program updates... +Αναζήτηση καινούριας έκδοσης... + +Scanning... +Ανίχνευση... + +Comparing content... +Σύγκριση του περιεχομένου... + +Info +Πληροφορίες + +Warning +Προειδοποίηση + +Paused +Σε παύση + +Initializing... +Αρχικοποίηση... + +Stopped +Διεκόπη + +Completed +Ολοκληρώθηκε + +&Continue +&Συνέχεια + +Log +Καταγραφή + +Today +Σήμερα + +This week +Αυτήν την εβδομάδα + +This month +Αυτόν το μήνα + +This year +Αυτό το έτος + +Last x days +Τελευταίες x ημέρες + +Byte +Byte + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Είστε σίγουροι ότι θέλετε να μετακινήσετε το ακόλουθο αντικείμενο στον κάδο ανακύκλωσης; +Είστε σίγουροι ότι θέλετε να μετακινήσετε τα ακόλουθα %x αντικείμενα στον κάδο ανακύκλωσης; + + +Move +Μεταφορά + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Είσαι σίγουρος ότι θέλεις να διαγραφεί το ακόλουθο αντικείμενο; +Είσαι σίγουρος ότι θέλεις να διαγραφούν τα ακόλουθα %x αντικείμενα; + + +Exclude +Εξαίρεση + +Direct +Ως δεσμοί + +Follow +Ως περιεχόμενο + +Copy NTFS permissions +Αντιγραφή αδειών προσπέλασης NTFS + +Integrate external applications into context menu. The following macros are available: +Ένταξη εξωτερικών εφαρμογών στο μενού περιβάλλοντος. Οι ακόλουθες μακροεντολές είναι διαθέσιμες: + +- full file or folder name +- πλήρες όνομα αρχείου ή υποκαταλόγου + +- folder part only +- μέρος μόνο του υποκαταλόγου + +- Other side's counterpart to %item_path% +- Το αντίστοιχο του %item_path% της άλλης πλευράς + +- Other side's counterpart to %item_folder% +- Το αντίστοιχο του %item_folder% της άλλης πλευράς + +Restore all hidden windows and warnings? +Να γίνει επαναφορά όλων των κρυμμένων μηνυμάτων και προειδοποιήσεων; + +Leave as unresolved conflict +Παράβλεψη ως ανεπίλυτη διένεξη + +Replace +Αντικατάσταση προηγούμενης + +Move files and replace if existing +Μετακίνηση αρχείων και αντικατάσταση + +Time stamp +Χρονική σήμανση + +Append a timestamp to each file name +Προσάρτηση χρονικής σήμανσης σε κάθε όνομα αρχείου + +File +Αρχείο + +YYYY-MM-DD hhmmss +ΕΕΕΕ-ΜΜ-ΗΗ ωωλλδδ + +Files +Αρχεία + +Items +Στοιχεία + +Percentage +Ποσοστό + +Cannot monitor directory %x. +Δεν είναι δυνατή η παρακολύθηση του υποκαταλόγου %x. + +Conversion error: +Σφάλμα μετατροπής: + +Cannot delete file %x. +Δεν μπορεί να διαγραφεί το αρχείο %x. + +The file is locked by another process: +Το αρχείο είναι κλειδωμένο από μια άλλη διαδικασία: + +Cannot move file %x to %y. +Δεν μπορεί το αρχείο %x να μεταφερθεί στο %y. + +Cannot delete directory %x. +Δεν μπορεί να διαγραφεί ο υποκατάλογος %x. + +Cannot write file attributes of %x. +Δεν μπορεί να γίνει εγγραφή των χαρακτηριστικών αρχείου του %x. + +Cannot write modification time of %x. +Δεν μπορεί να γίνει εγγραφή της ώρας τροποποίησης του %x. + +Cannot read security context of %x. +Δεν μπορεί να αναγνωσθεί το περιβάλλον ασφαλείας του %x. + +Cannot write security context of %x. +Δεν μπορεί να γίνει εγγραφή του περιβάλλοντος ασφαλείας του %x. + +Cannot read permissions of %x. +Δεν μπορούν να αναγνωσθούν οι άδειες προσπέλασης του %x. + +Cannot write permissions of %x. +Δεν μπορεί να γίνει εγγραφή των αδειών προσπέλασης του %x. + +Cannot create directory %x. +Δεν μπορεί να δημιουργηθεί ο υποκατάλογος %x. + +Cannot create symbolic link %x. +Ο συμβολικός δεσμός %x δεν μπορεί να δημιουργηθεί. + +Cannot find system function %x. +Δεν ανευρίσκεται η λειτουργία συστήματος %x. + +Cannot copy file %x to %y. +Δεν μπορεί να αντιγραφεί το αρχείο %x στο %y. + +Type of item %x is not supported: +Ο τύπος του στοιχείου %x δεν υποστηρίζεται: + +Cannot resolve symbolic link %x. +Ο συμβολικός δεσμός %x δεν μπορεί να επιλυθεί + +Cannot open directory %x. +Δεν είναι δυνατό το άνοιγμα του υποκαταλόγου %x. + +Cannot enumerate directory %x. +Δεν είναι δυνατή η καταλογογράφηση του υποκαταλόγου %x. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +1 λεπτό +%x λεπτά + + + +1 hour +%x hours + + +1 ώρα +%x ώρες + + + +1 day +%x days + + +1 μέρα +%x μέρες + + +Unable to register to receive system messages. +Δεν μπορεί να καταχωρηθεί η λήψη μηνυμάτων συστήματος. + +Cannot set privilege %x. +Τα δικαιώματα %x δεν μπορούν να οριστούν. + +Unable to suspend system sleep mode. +Δεν μπορεί να διακοπεί η αναστολή του συστήματος. + +Cannot change process I/O priorities. +Δεν μπορούν να αλλάξουν οι προτεραιότητες I/O της διεργασίας. + +Unable to move %x to the recycle bin. +Δεν ήταν δυνατή η μεταφορά του %x στον κάδο ανακύκλωσης. + +Cannot determine final path for %x. +Δεν μπορεί να προσδιοριστεί η τελική διαδρομή για το %x. + +Error Code %x: +Κωδικός Σφάλματος %x + diff --git a/FreeFileSync/Build/Languages/hebrew.lng b/FreeFileSync/Build/Languages/hebrew.lng new file mode 100644 index 00000000..1667333d --- /dev/null +++ b/FreeFileSync/Build/Languages/hebrew.lng @@ -0,0 +1,1515 @@ +
    + עברית + nitnit + he_IL + flag_israel.png + 2 + n == 1 ? 0 : 1 +
    + +Both sides have changed since last synchronization. +שני הצדדים שונו מאז הסנכרון האחרון. + +Cannot determine sync-direction: +לא מזהה כוון סנכרון: + +No change since last synchronization. +אין שינוי מאז הסנכרון האחרון. + +The database entry is not in sync considering current settings. +בסיס הנתונים אינו מסונכרן בהתאם להגדרות הנוכחיות. + +Setting default synchronization directions: Old files will be overwritten with newer files. +בחר ברירת מחדל של סנכרון: קבצים ישנים ידרסו ע"י קבצים חדשים יותר. + +Checking recycle bin availability for folder %x... +בודק זמינות סל מחזור עבור מחיצה %x... + +Moving file %x to the recycle bin +מעביר קובץ %x לסל המיחזור + +Moving folder %x to the recycle bin +מעביר תיקייה %x לסל המיחזור + +Moving symbolic link %x to the recycle bin +מעביר קישור סימבולי %x לסל המיחזור + +Deleting file %x +מוחק קובץ %x + +Deleting folder %x +מוחק מחיצה %x + +Deleting symbolic link %x +מוחק קישור סימבולי %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +סל המיחזור אינו זמין עבור התיקיות הבאות. הקבצים יימחקו לחלוטין: + +An exception occurred +אירוע חריג + +A directory path is expected after %x. +נתיב מחיצה נדרש אחרי %x. + +Syntax error +שגיאת תחביר + +Cannot open file %x. +לא יכול לפתוח קובץ %x. + +File %x does not contain a valid configuration. +קובץ %x אינו כולל תצורה תקינה. + +Unequal number of left and right directories specified. +מספר בלתי שווה של מחיצות ימין ושמאל צוין. + +The config file must not contain settings at directory pair level when directories are set via command line. +קבצי הקונפיגורציה אינם יכולים לכלול הגדרות של זוגות מחיצות כאשר מחיצות מוגדרות באמצעות שורת הפקודה + +Directories cannot be set for more than one configuration file. +מחיצות אינו יכולות להיות מוגדרות עבור יותר מקובץ קונפיגורציה אחד. + +Command line +שורת פקודות + +Syntax: +תחביר: + +config files +קבצי קונפיגורציה + +directory +מחיצה + +Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. +כל כמות של קבצי FreeFileSync .ffs_gui ו\או קבצי קונפיגורציה .ffs_batch. + +Any number of alternative directories for at most one config file. +כל כמות של מחיצות אלטרנטיביות עבור לפחות קובץ קונפיגורציה אחד. + +A folder input field is empty. +שדה קלט תיקייה ריק. + +The corresponding folder will be considered as empty. +המחיצה המתאימה תחשב כריקה. + +Cannot find the following folders: +לא יכול למצוא את המחיצות הבאות: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +אפשר להתעלם משגיאה זו ולהחשיב כל תיקיה כריקה. התיקיות יווצרו אוטומטית בזמן הסינכרון. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +מסלול המחיצות הבאות מותנה. שים לב כאשר חוקי הסינכרון מוגדרים: + +File %x has an invalid date. +קובץ %x מכיל תאריך שגוי. + +Date: +תאריך: + +Files %x have the same date but a different size. +קובץ %x בעל תאריך זהה אך גודל שונה. + +Size: +גודל: + +Items differ in attributes only +פריטים שונים בתכונות בלבד + +Resolving symbolic link %x +פותר קישור סימבולי %x + +Comparing content of files %x +השווה תכולה של קבצים %x + +Generating file list... +מייצר רשימת קבצים... + +Starting comparison +מתחיל השוואה + +Calculating sync directions... +מחשב כיווני סנכרון... + +Out of memory. +תם הזכרון. + +Item exists on left side only +הפריט קיים בצד ימין בלבד + +Item exists on right side only +הפריט קיים בצד שמאל בלבד + +Left side is newer +צד ימין חדש יותר + +Right side is newer +צד שמאל חדש יותר + +Items have different content +הפריטים הם בעלי תוכן שונה + +Both sides are equal +שני הצדדים שווים + +Conflict/item cannot be categorized +סתירה/פריט אינו ניתן לסיווג + +Copy new item to left +העתק פריט חדש לצד ימין + +Copy new item to right +העתק פריט חדש לצד שמאל + +Delete left item +מחק פריט בצד ימין + +Delete right item +מחק פריט בצד שמאל + +Move file on left +העבר קובץ בצד ימין + +Move file on right +העבר קובץ בצד שמאל + +Overwrite left item +רשום על גבי פריט בצד ימין + +Overwrite right item +רשום על גבי פריט בצד שמאל + +Do nothing +אל תעשה כלום + +Update attributes on left +עדכן תכונות בצד ימין + +Update attributes on right +עדכן תכונות בצד שמאל + +Database file %x is incompatible. +קובץ מסד נתונים %x אינו במבנה מתאים. + +Initial synchronization: +סנכרון ראשוני: + +Database file %x does not yet exist. +קובץ מסד נתונים %x אינו קיים עדיין. + +Database file is corrupt: +קובץ מסד נתונים משובש: + +Cannot write file %x. +לא יכול לכתוב קובץ %x. + +Cannot read file %x. +לא יכול לקרוא קובץ %x. + +Database files do not share a common session. +קבצי הנתונים אינם כוללים מופע פעילות משותף. + +Searching for folder %x... +מחפש את תיקייה %x... + +Cannot read file attributes of %x. +לא יכול לקרוא תכונות של קובץ %x. + +Cannot get process information. +לא יכול לקבל את נתוני התהליך. + +Waiting while directory is locked (%x)... +ממתין כאשר מחיצה נעולה (%x)... + + +1 sec +%x sec + + +1 שנייה +%x שניות + + +Creating file %x +יוצר קובץ %x + +Items processed: +אלמנטים עובדו: + +Items remaining: +אלמנתים נותרו: + +Total time: +זמן כולל: + + +1 byte +%x bytes + + +בית 1 +%x בתים + + +%x MB +%x מגה בייט + +%x KB +%x קילו בייט + +%x GB +%x גיגה בייט + +Error parsing file %x, row %y, column %z. +שגיאה בפענוח קובץ %x, שורה %y, טור %z. + +Cannot set directory lock for %x. +לא ניתן לנעול מחיצה עבור %x. + +Scanning: +סורק: + + +1 thread +%x threads + + +תהליך 1 +%x תהליכים + + +Encoding extended time information: %x +מקודד אינפורמצית זמן מורחבת: %x + +/sec +/שנ + +%x items/sec +%x פריטים לשניה + +Configuration file %x loaded partially only. +קובץ תצורה %x נטען חלקית בלבד. + +Show in Explorer +הראה בסייר הקבצים + +Open with default application +פתח באמצאות האפליקציה המתאימה + +Browse directory +עיין במחיצה + +Cannot access the Volume Shadow Copy Service. +לא ניתן לגשת אל שרות Volume Shadow Copy + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +אנא השתמש בגירסת 64-bit של FreeFileSync על מנת ליצר shadow copies במערכת הפעלה זו. + +Cannot load file %x. +לא יכול לטעון קובץ %x. + +Cannot determine volume name for %x. +לא יכול לקבוע שם אמצעי אחסון %x. + +Volume name %x is not part of file path %y. +כרך בשם %x אינו חלק של נתיב קובץ %y. + +Stop requested: Waiting for current operation to finish... +עצירה התבקשה: מחכה שפעולה נוכחית תסתיים... + +Unable to create timestamp for versioning: +לא ניתן ליצור תג זמן לגרסאות: + +Cannot read the following XML elements: +לא יכול לקרוא את שמות צמתי XML: + +&Open... +&פתח... + +Save &as... +שמור &בשם... + +&Quit +&יציאה + +&Program +&תוכנה + +&View help +&הראה עזרה + +&About +&אודות + +&Help +&עזרה + +Usage: +שימוש: + +1. Select folders to watch. +1. בחר תיקיות לצפייה. + +2. Enter a command line. +2. הקש שורת פקודות. + +3. Press 'Start'. +3. לחץ 'הפעל'. + +To get started just import a .ffs_batch file. +בכדי להתחיל יבא קובץ .ffs_batch + +Folders to watch: +תיקיות לצפייה: + +Add folder +הוסף מחיצה + +Remove folder +הסר מחיצה + +Browse +עיין + +Select a folder +בחר מחיצה + +Idle time (in seconds): +זמן המתנה (בשניות): + +Idle time between last detected change and execution of command +זמן המתנה בין שינויי מאובחן אחרון לבין ביצוע של פקודה + +Command line: +שורת פקודה: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +הפקודה מופעלת כאשר: +- קבצים או תת-תיקיות משתנים +- תיקיות חדשות מופיעות (לדוגמה התקן USB מוכנס) + + +&Start +&התחל + +About +אודות + +Build: %x +מבנה: %x + +All files +כל הקבצים + +Automated Synchronization +סנכרון אוטומטי + +Directory monitoring active +ניטור מחיצות פעיל + +Waiting until all directories are available... +מחכה עד שכל המחיצות יהיו זמינות... + +Error +שגיאה + +&Restore +&טען מחדש + +&Show error +&הראה שגיאה + +&Exit +&יציאה + +Incorrect command line: +שורת פקודה לא תקינה: + +&Retry +&נסה שנית + +File content +תכולת הקובץ + +File time and size +זמן וגודל קובץ + +Two way +דו כווני + +Mirror +מראה + +Update +שדרג + +Custom +מותאם + +Multiple... +הכפל... + +Moving file %x to %y +מעביר קובץ %x אל %y + +Moving folder %x to %y +מעביר מחיצה %x אל %y + +Moving symbolic link %x to %y +מעביר קישור סימבולי %x אל %y + +Removing old versions... +מסיר גרסאות ישנות... + +Creating symbolic link %x +יוצר קישור סימבולי %x + +Creating folder %x +יוצר מחיצה %x + +Overwriting file %x +דורס קובץ %x + +Overwriting symbolic link %x +דורס קישור סימבולי %x + +Verifying file %x +מאמת קובץ %x + +Updating attributes of %x +מעדכן תכונות של %x + +Cannot find %x. +לא מוצא %x. + +Target folder %x already existing. +תיקיית מטרה %x כבר קיימת. + +Target folder input field must not be empty. +קלט תיקיית מטרה אינה יכול להיות ריק. + +Please enter a target folder for versioning. +בבקשה הזן תיקיית יעד עבור גרסאות. + +Source folder %x not found. +תיקיית מקור %x לא נמצאת. + +The following items have unresolved conflicts and will not be synchronized: +לפריטים הבאים יש קונפליקטים בלתי פתורים והם לא יסונכרנו: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +התיקיות הבאות שונות באופן מהותי. ודא את התאמת התיקיות לסינכרון. + +Not enough free disk space available in: +אין מספיק מקום דיסק פנוי ב: + +Required: +נדרש: + +Available: +זמין: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +התיקייה שתשתנה היא חלק מריבוי זוגות תיקיות. אנא סקור מחדש הגדרות סינכרון. + +Synchronizing folder pair: +מסנכרן זוג תיקיות: + +Generating database... +מייצר מסד נתונים... + +Creating a Volume Shadow Copy for %x... +מייצר Volume Shadow Copy עבור %x... + +Data verification error: %x and %y have different content. +שגיאת אימות נתונים. ל %x ו %y יש תוכן שונה. + +job name +שם משימה + +Synchronization stopped +סנכרון הופסק + +Synchronization completed with errors +סנכרון הושלם עם שגיאות + +Synchronization completed with warnings +סנכרון הסתיים עם אזהרות + +Nothing to synchronize +אין מה לסנכרן + +Synchronization completed successfully +סנכרון הסתיים בהצלחה + +Saving log file %x... +שומר קובץ יומן %x... + +You can switch to FreeFileSync's main window to resolve this issue. +ניתן לעבור לחלון הראשי של FreeFileSybc כדי לפתור את הסוגיה הזו. + +&Don't show this warning again +&לא להראות אזהרה זו שוב + +&Ignore +&התעלם + +&Switch +&החלפה + +Switching to FreeFileSync's main window +מעבר אל החלון הראשי של FreeFileSybc + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + + + +&Ignore subsequent errors +&התעלם מאזהרות נוספות + +Retrying operation... + + +Serious Error +שגיאה חמורה + +Check for Program Updates +בדוק קיום עדכוני תוכנה + +A new version of FreeFileSync is available: +גירסה חדשה של FreeFileSync זמינה: + +Download now? +הורד עכשיו? + +&Download +&הורד + +FreeFileSync is up to date. +FreeFileSync מעודכן לגירסה האחרונה. + +Unable to connect to sourceforge.net. +אין תקשורת ל sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +לא מוצא מספר גירסה עדכנית של FreeFileSync באופן מכוון. האם אתה רוצה לבדוק באופן ידני? + +&Check + + +Symlink +קשור סימבולי + +Folder +תיקייה + +Full path +נתיב מלא + +Name +שם + +Relative path +נתיב יחסי + +Base folder +תיקיית בסיס + +Size +גודל + +Date +תארין + +Extension +סיומת + +Category +קטגוריה + +Action +פעולה + +Drag && drop +גרור &והשלך + +Close progress dialog +סגור שיח התקדמות + +Standby +עבור למצב המתנה + +Log off +התנתק כמשתמש + +Shut down +כבה מחשב + +Hibernate +עבור למצב שינה + +Alternate comparison settings +הגדרות השוואה חלופיות + +Alternate synchronization settings +הגדרות סנכרון חלופיות + +Local filter +מסנן מקומי + +Active +פעיל + +None +אין + +Remove alternate settings +הסר הגדרות תצורה חליפיות + +Clear filter settings +נקה בחירת מסנן + +Copy +העתק + +Paste +הדבק + +Alternate Comparison Settings +הגדרות השוואה חלופיות + +Alternate Synchronization Settings +הגדרות סנכרון חלופיות + +Local Filter +מסנן מקומי + +&New +&חדש + +&Save +&שמור + +Save as &batch job... +שמור כעבודת &אצווה + +1. &Compare +1. &השווה + +2. &Synchronize +2. &סנכרן + +&Global settings +&משתנים גלובליים + +&Language +&שפה + +&Find... +&מצא + +&Export file list... +&יצא רשימת קבצים... + +&Tools +&כלים + +&Check now +&בדוק עכשיו + +Check &automatically once a week +בדוק &אוטומטית אחת לשבוע + +&Check for new version +&בדוק קיום גירסה חדשה + +Compare +השוואה + +Cancel +בטל + +Synchronize +סנכרן + +Add folder pair +הוסף זוג מחיצות + +Remove folder pair +הסר זוג מחיצות + +Swap sides +החלף צדדים + +Close search bar +סגור סרגל חיפוש + +Find: +מצא: + +Match case +התאם רישיות + +Save as batch job +שמור כעבודת אצווה + +Hide excluded items +הסתר פריטים שלט נכללו + +Show filtered or temporarily excluded files +הראה קבצים שסוננו או לא נכללו זמנית + +Number of files and folders that will be created +מספר הקבצים והתיקיות שייוצרו + +Number of files that will be overwritten +מספר הקבצים העומדים להיכתב מחדש + +Number of files and folders that will be deleted +מספר הקבצים והתיקיות שימחקו + +Total bytes to copy +סה"כ בתים להעתיק + +Select a variant: +בחר גירסה: + +Identify equal files by comparing modification time and size. +זהה קבצים זהים באמצעות השוואת תג הזמן והגודל. + +Identify equal files by comparing the file content. +זהה קבצים זהים באמצעות השוואת תוכן. + +Symbolic links: +קישורים סימבוליים: + +More information +מידע נוסף + +OK +אשר + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +זהה והפץ שינויים בשני הצדדים. מחיקות העברות וסתירות מתגלים באופן אוטומטי באמצעות מסד נתונים. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +צור גבוי מראה של התיקייה השמאלית אשר יהיה זהה לתיקייה הימנית לאחר הסנכרון. + +Copy new and updated files to the right folder. +העתק קבצים חדשים ומעודכנים לתיקייה הימנית. + +Configure your own synchronization rules. +סדר את כללי הסנכרון שלך. + +Detect moved files +גלה קבצים מועברים + +Requires database files. Not supported by all file systems. +דורש קבצי בסיס נתונים. לא נתמך ע"י כל מערכות הקבצים. + +Delete files: +מחק קבצים: + +Permanent +קבוע + +Delete or overwrite files permanently +מחק או דרוס קבצים לצמיתות + +Recycle bin +סל מיחזור + +Back up deleted and overwritten files in the recycle bin +גבה קבצים שנמחקו או שנדרסו בסל המיחזור + +Versioning +עדכון גרסאות + +Move files to a user-defined folder +העבר קבצים לתיקייה המוגדרת ע"י המפעיל + +Naming convention: +מוסכמות לקביעת שמות: + +Show examples +הראה דוגמאות + +Handle errors: +טפל בשגיאות: + +Ignore +התעלם + +Hide all error and warning messages +הסתר את כל הודעות ההזהרה והשגיאה + +Pop-up +מוקפץ + +Show pop-up on errors or warnings +הראה חלונות מוקפצים עבור שגיאות או אזהרות + +On completion: +עם הסיום: + +Start synchronization now? +האם להתחיל סנכרון עכשיו? + +Variant: +גרסה אחרת: + +Statistics +סטטיסטיקה + +&Don't show this dialog again +&אל תראה דושיח זה שנית + +Items found: +אלמנטים נמצאו: + +Speed: +מהירות: + +Time remaining: +זמן שנשאר: + +Time elapsed: +זמן שעבר: + +Synchronizing... +מסנכרן... + +Minimize to notification area +הקטן לאיזור ההתרעות + +Close +סגור + +&Pause +&עצור + +Stop +עצור + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +צור קובץ אצווה לסינכרון ללא התערבות מפעיל. כדי להפעיל, הקלק הקלקה כפולה על הקובץ או תזמן במתכנן המשימות: %x + +Stop synchronization at first error +עצור סנכרון עם הופעת שגיאה ראשונה + +Show progress dialog +הראה שיח התקדמות + +Save log: +שמור יומן: + +Limit: +מגבלה: + +Limit maximum number of log files +הגבל מספר מכסימלי של קבצי יומן + +How can I schedule a batch job? +כיצד לתזמן משימת אצווה? + +&Recycle bin +&סל מחזור + +Delete on both sides +מחק בשני הצדדים + +Delete on both sides even if the file is selected on one side only +מחק בשני הצדדים אף אם הקובץ נבחר בצד אחד בלבד + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +בחר כללי סינון כדי לא לכלול קבצים מסוימים בסנכרון. הזן את נתיבי הקבצים ביחס לזוג התיקיות בהתאמה. + +Include: +כלול: + +Exclude: +לא לכלול: + +Time span: +טווח זמן: + +File size: +גודל קובץ: + +Minimum: +מינימום: + +Maximum: +מקסימום: + +&Clear +&נקה + +The following settings are used for all synchronization jobs. +ההגדרות הבאות משמשות לכל משימות הסנכרון. + +Fail-safe file copy +כשלון באבטחת העתקת קובץ + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +העתק לקובץ זמני (*.ffs_tmp) לפני כתיבה מחדש על היעד. +זה מבטיח מצב עקבי גם במקרה של שגיאה חמורה. + + +(recommended) +(מומלץ) + +Copy locked files +העתק קבצים נעולים + +Copy shared or locked files using the Volume Shadow Copy Service. +העתק קבצים בשיתוף או בנעילה באמצעות שרות Volume Shadow Copy. + +(requires administrator rights) +(נדרשות זכויות מנהל) + +Copy file access permissions +העתק הרשאות גישה של הקובץ + +Transfer file and folder permissions. +הרשאות העברת קובץ ומחיצה. + +Automatic retry on error: +נסיון חוזר אוטומטי במקרה שגיאה: + +Retry count: +מונה נסיונות חוזרים: + +Delay (in seconds): +השהייה (בשניות): + +Customize context menu: +התאמה אישית של תפריט הקשר: + +Description +תאור + +Restore hidden windows +שחזר חלונות ניסתרים + +&Default +&ברירת מחדל + +Source code written in C++ using: +קוד מקור נכתב ב- C++ באמצעות: + +If you like FreeFileSync +במידה ו-FreeFileSync מוצאת חן בעינכם + +Donate with PayPal +תרום עם פייפל + +Feedback and suggestions are welcome +משוב והצעות יתקבלו בברכה + +Homepage +אתר-הבית: + +Email +דוא"ל: + +Published under the GNU General Public License +מפורסם במסגרת GNU General Public License + +Many thanks for localization: +תודות עבור תרגום שפות: + +Save as Batch Job +שמור כמשימת אצווה + +Delete Items +מחק פריטים + +Global Settings +הגדרות גלובליות + +Select Time Span +בחר טווח זמן + +Folder Pairs +זוגות תיקיות + +Find +חפש + +Overview +מבט כללי + +Configuration +תצורה + +Main Bar +סרגל ראשי + +Filter Files +סנן קבצים + +Select View +בחר מבט + +Open... +פתח... + +Save +שמור + +Compare both sides +השווה בין שני הצדדים + +Comparison settings +הגדרות השוואה + +Synchronization settings +הגדרות סנכרון + +Start synchronization +התחל סנכרון + +Confirm +אשר + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + +האם ברצונך לבצע את הפקודה %y עבור פריט אחד? +האם ברצונל לבצע את הפקודה %y עבור %x פריטים? + + +&Execute +&בצע + + +1 directory +%x directories + + +1 מחיצה +%x מחיצות + + + +1 file +%x files + + +1 קובץ +%x קבצים + + + +%y of 1 row in view +%y of %x rows in view + + +%y של שורה 1 במראה +%y של %x שורות במראה + + +Set direction: +בחר כוון: + +multiple selection +בחירה מרובה + +Include via filter: +כלול באמצעות מסנן: + +Exclude via filter: +אל תכלול בעזרת סנן: + +Exclude temporarily +אל תכלול זמנית + +Include temporarily +כלול זמנית + +Delete +מחק + +Include all +הכלל הכל + +Exclude all +הוצא מן הכלל הכל + +Show icons: +הצג סמלים: + +Small +קטן + +Medium +בינוני + +Large +גדול + +Select time span... +בחר תחום זמן... + +Default view +תצוגה בתצורת ברירת מחדל + +Show "%x" +הראה "%x" + +Last session +פעילות אחרונה + +Folder Comparison and Synchronization +סנכרון קבצים ומחיצות + +Configuration saved +תצורה נשמרה + +FreeFileSync batch +אצוות FreeFileSync + +Do you want to save changes to %x? +האם לשמור שינויים אל %x? + +Never save &changes +לא לשמור שינויים ל&עולם + +Do&n't save +אל &תשמור + +Filter +מסנן + +Show files that exist on left side only +הראה קבצים הנמצאים אך ורק בצד ימין + +Show files that exist on right side only +הראה קבצים הנמצאים אך ורק בצד שמאל + +Show files that are newer on left +הראה קבצים חדשים יותר בצד ימין + +Show files that are newer on right +הראה קבצים חדשים יותר בצד שמאל + +Show files that are equal +הראה קבצים שווים + +Show files that are different +הראה קבצים שונים + +Show conflicts +הראה קונפליקטים + +Show files that will be created on the left side +הראה קבצים שיווצרו בצד ימין + +Show files that will be created on the right side +הראה קבצים שיווצרו בצד שמאל + +Show files that will be deleted on the left side +הראה קבצים שימחקו בצד ימין + +Show files that will be deleted on the right side +הראה קבצים שימחקו בצד שמאל + +Show files that will be overwritten on left side +הראה קבצים שידרסו בצד ימין + +Show files that will be overwritten on right side +הראה קבצים שידרסו בצד שמאל + +Show files that won't be copied +הראה קבצים שלא יועתקו + +Set as default +הגדר כברירת מחדל + +All folders are in sync +כל התיקיות מסונכרנות + +Synchronization Settings +הגדרות סנכרון + +Comparison Settings +הגדרות השוואה + +Cannot find %x +לא מוצא %x + +Comma-separated values +ערכים מופרדים באמצעות פסיק + +File list exported +רשימת קבצים יוצאה + +Searching for program updates... +מחפש עידכוני תוכנה... + +Scanning... +סורק... + +Comparing content... +משווה תכולה... + +Info +מידע + +Warning +אזהרה + +Paused +עצור + +Initializing... +מאתחל ... + +Stopped +נעצר + +Completed +הושלם + +&Continue +&המשך + +Log +יומן + +Today +היום + +This week +בשבוע הנוכחי + +This month +בחודש הנוכחי + +This year +בשנה הנוכחית + +Last x days +x ימים אחרונים + +Byte +בייט + +KB +קילו-בייט + +MB +מגה-בייט + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +האם באמת ברצונך להעביר את הפריט הבא לסל המחזור? +האם באמת ברצונך להעביר את הפריטים הבאים %x לסל המחזור? + + +Move +העבר + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +האם ברצונך למחוק את הפריט הבא? +האם ברצונך למחוק את הפריטים %x הבאים? + + +Exclude +אל תכלול + +Direct +כוון + +Follow +עקוב + +Copy NTFS permissions +העתק הרשאות NTFS + +Integrate external applications into context menu. The following macros are available: +הטמע תוכנות חיצוניות. תפריטי המקרו הבאים זמינים: + +- full file or folder name +- שם מלא של קובץ או תיקייה + +- folder part only +- תיקייה בלבד + +- Other side's counterpart to %item_path% +- הצד השני מקביל ל %item_path% + +- Other side's counterpart to %item_folder% +הצד השני המקביל ל %item_folder% + +Restore all hidden windows and warnings? +האם לשחזר את כל החלונות וההודעות הנסתרות? + +Leave as unresolved conflict +השאר כקונפליקט לא מטופל + +Replace +החלף + +Move files and replace if existing +העבר קבצים והחלף במדה וקיימים + +Time stamp +תג זמן + +Append a timestamp to each file name +הצמד תג זמן לכל שם קובץ + +File +קובץ + +YYYY-MM-DD hhmmss +YYYY-MM-DD hhmmss + +Files +קבצים + +Items +פריטים + +Percentage +אחוז + +Cannot monitor directory %x. +לא יכול לנטר מחיצה %x. + +Conversion error: +שגיאה בהסבה: + +Cannot delete file %x. +לא יכול למחוק קובץ %x. + +The file is locked by another process: +הקובץ נעול ע"י תהליך: + +Cannot move file %x to %y. +לא יכול להעביר קובץ %x אל %y. + +Cannot delete directory %x. +לא יכול למחוק מחיצה %x. + +Cannot write file attributes of %x. +לא יכול לכתוב תכונות קובץ של %x. + +Cannot write modification time of %x. +לא יכול לרשום זמן שינוי של %x. + +Cannot read security context of %x. +לא יכול לקרוא הקשר בטיחות של %x. + +Cannot write security context of %x. +לא יכול לכתוב הקשר בטיחות של %x. + +Cannot read permissions of %x. +לא יכול לקרוא הרשאות של %x. + +Cannot write permissions of %x. +לא יכול לכתוב הרשאות של %x. + +Cannot create directory %x. +לא יכול ליצור מחיצה %x. + +Cannot create symbolic link %x. +לא יכול ליצור קישור סימבולי %x. + +Cannot find system function %x. +לא יכול למצוא פונקצית מערכת %x. + +Cannot copy file %x to %y. +לא יכול להעתיק קובץ %x אל %y. + +Type of item %x is not supported: +סוג של פריט %x אינו נתמך: + +Cannot resolve symbolic link %x. +לא יכול לפענח את הקישור הסימבולי %x. + +Cannot open directory %x. +לא יכול לפתוח מחיצה %x. + +Cannot enumerate directory %x. +לא יכול למספר מחיצה %x. + +%x TB +%x טרה בייט + +%x PB +%x פטה בייט + + +1 min +%x min + + +1 דקה +%x דקות + + + +1 hour +%x hours + + +1 שעה +%x שעות + + + +1 day +%x days + + +1 יום +%x ימים + + +Unable to register to receive system messages. +לא ניתן להרשם לקבלת הודעות מערכת. + +Cannot set privilege %x. +לא יכול להגדיר זבות %x. + +Unable to suspend system sleep mode. +לא ניתן להשעות מצב שינה של המערכת. + +Cannot change process I/O priorities. +לא יכול לשנות קדימויות של תהליך קלט פלט. + +Unable to move %x to the recycle bin. +לא יכול להעביר את %x לסל המחזור + +Cannot determine final path for %x. +לא יכול לקבוע את המסלול הסופי ל %x. + +Error Code %x: +קוד שגיאה %x: + diff --git a/FreeFileSync/Build/Languages/hungarian.lng b/FreeFileSync/Build/Languages/hungarian.lng new file mode 100644 index 00000000..a9447de8 --- /dev/null +++ b/FreeFileSync/Build/Languages/hungarian.lng @@ -0,0 +1,1515 @@ +
    + Magyar + szittner + hu_HU + flag_hungary.png + 2 + n == 1 ? 0 : 1 +
    + +Both sides have changed since last synchronization. +Mindkét oldal megváltozott az utolsó szinkronizálás óta. + +Cannot determine sync-direction: +Nem meghatározható a szinkronizálás iránya: + +No change since last synchronization. +Az utolsó szinkronizálás óta nem történt változás. + +The database entry is not in sync considering current settings. +Az adatbázis-bejegyzés nincs összhangban figyelembe véve a jelenlegi beállításokat. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Alapértelmezett szinkronizálási irányok beállítása: a régebbi fájlokat írja felül az újabbakkal. + +Checking recycle bin availability for folder %x... +A Lomtár elérhetőségének ellenőrzése %x könyvtár vonatkozásában... + +Moving file %x to the recycle bin +%x fájlt a Lomtárba mozgatja + +Moving folder %x to the recycle bin +%x könyvtárat a Lomtárba mozgatja + +Moving symbolic link %x to the recycle bin +%x szimbolikus linket a Lomtárba mozgatja + +Deleting file %x +%x fájl törlése + +Deleting folder %x +%x könyvtár törlése + +Deleting symbolic link %x +%x szimbolikus hivatkozás törlése + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +A Lomtár nem elérhető a következő könyvtárak esetében. A fájlok ezért véglegesen törlődnek: + +An exception occurred +Kivétel lépett fel + +A directory path is expected after %x. +%x után egy könyvtár elérési útvonalának kell következnie. + +Syntax error +Szintaktikai hiba + +Cannot open file %x. +%x fájl nem nyitható. + +File %x does not contain a valid configuration. +%x fájl nem tartalmaz érvényes beállításokat. + +Unequal number of left and right directories specified. +Eltérő számú bal- és jobboldali könyvtárat határozott meg. + +The config file must not contain settings at directory pair level when directories are set via command line. +A konfigurációs fájl nem tartalmazhat beállításokat könyvár-pár szinten, ha a könyvtárakat parancssorban állítjuk be. + +Directories cannot be set for more than one configuration file. +A könyvtárakat nem lehet egynél több konfigurációs fájlra beállítani + +Command line +Parancssor + +Syntax: +Szintaxis: + +config files +konfigurációs fájlok + +directory +könyvtár + +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 fájl készíthető a FreeFileSync-hez + +Any number of alternative directories for at most one config file. +Tetszőleges számú alternatív könyvtár legfeljebb egy konfigurációs fájlhoz + +A folder input field is empty. +A könyvtár beviteli mező üres. + +The corresponding folder will be considered as empty. +A kapcsolódó könyvtárat üresként kezeli. + +Cannot find the following folders: +A következő könyvtárak nem találhatóak: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Figyelmen kívül hagyhatod ezt a hibát, ezzel üresnek tekintve minden egyes könyvtárat. A könytárak ekkor automatikusan létrejönnek a szinkronizálás folyamán. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +A következő könyvtárak egymástól függő útvonalat tartalmaznak. Légy óvatos a szinkronizálás szabályainak meghatározásánál! + +File %x has an invalid date. +%x fájl dátuma érvénytelen. + +Date: +Dátum: + +Files %x have the same date but a different size. +A(z) %x fájlok dátuma megegyezik, de a méretük nem. + +Size: +Méret: + +Items differ in attributes only +Az elemek csak attribútumaikban különböznek + +Resolving symbolic link %x +A %x szimbolikus hivatkozás feloldása + +Comparing content of files %x +%x fájlok tartalmának összehasonlítása + +Generating file list... +Fájllista generálása... + +Starting comparison +Kezdi az összehasonlítást + +Calculating sync directions... +Szinkronizálási irányok számítása... + +Out of memory. +Memória túlcsordulás. + +Item exists on left side only +Az elem csak a bal oldalon létezik + +Item exists on right side only +Az elem csak a jobb oldalon létezik + +Left side is newer +A bal oldal újabb + +Right side is newer +A jobb oldal újabb + +Items have different content +Az elemek tartalma különböző + +Both sides are equal +Mindkét oldal azonos + +Conflict/item cannot be categorized +Az ütközés vagy elem nem kategorizálható + +Copy new item to left +Új elem másolása a bal oldalra + +Copy new item to right +Új elem másolása a jobb oldalra + +Delete left item +Bal oldali elem törlése + +Delete right item +Jobb oldali elem törlése + +Move file on left +Bal oldali fájl mozgatása + +Move file on right +Jobb oldali fájl mozgatása + +Overwrite left item +Bal oldali elem fölülírása + +Overwrite right item +Jobb oldali elem fölülírása + +Do nothing +Nincs mit csinálni + +Update attributes on left +Attribútumok frissítése a bal oldalon + +Update attributes on right +Attribútumok frissítése a jobb oldalon + +Database file %x is incompatible. +%x adatbázisfájl inkompatibilis: . + +Initial synchronization: +Kezdeti szinkronizálás: + +Database file %x does not yet exist. +%x adatbázisfájl még nem létezik. + +Database file is corrupt: +Sérült adatbázisfájl: + +Cannot write file %x. +A következő fájl írása nem sikerült: %x. + +Cannot read file %x. +A következő fájl olvasása nem sikerült: %x. + +Database files do not share a common session. +Az adatbázisfájloknak nincs közös munkafolyamatuk. + +Searching for folder %x... +%x könyvtár keresése... + +Cannot read file attributes of %x. +%x fájl attribútumainak olvasása nem sikerült. + +Cannot get process information. +Nem sikerült lekérdezni processz-információkat. + +Waiting while directory is locked (%x)... +Várakozás a könyvtár zárolásának a feloldására (%x)... + + +1 sec +%x sec + + +1 másodperc +%x másodperc + + +Creating file %x +%x fájl létrehozása + +Items processed: +Feldolgozott elemek száma: + +Items remaining: +Hátralévő elemek száma: + +Total time: +Összes időszükségelet: + + +1 byte +%x bytes + + +1 bájt +%x bájt + + +%x MB +%x MB + +%x KB +%x kB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Hiba történt a feldolgozás közben: %x fájl, %y sor, %z oszlop. + +Cannot set directory lock for %x. +Nem sikerült zárolni a(z) %x könyvtárat. + +Scanning: +Vizsgálat: + + +1 thread +%x threads + + +1 szál +%x szál + + +Encoding extended time information: %x +Kibővített időinformációk kódolása: %x + +/sec +/másodperc + +%x items/sec +%x elem/másodperc + +Configuration file %x loaded partially only. +%x konfigurációs fájl csak részlegesen töltődött be: . + +Show in Explorer +Mutassa az Intézőben + +Open with default application +Nyissa meg az alapértelmezett alkalmazással + +Browse directory +Tallózza a könyvtárat + +Cannot access the Volume Shadow Copy Service. +Nem elérhető a Kötet Árnyék-másolat szolgáltatás. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Kérjük, használd a FreeFileSync 64 bites verzióját az árnyékmásolatok készítéséhez ezen a rendszeren. + +Cannot load file %x. +%x fájl betöltése nem sikerült. + +Cannot determine volume name for %x. +Nem lehet meghatározni a kötet-nevet a(z) %x számára + +Volume name %x is not part of file path %y. +%x kötet név nem része a(z) %y fájl elérési útvonalnak + +Stop requested: Waiting for current operation to finish... +Megállás szükséges: várakozás a jelenlegi művelet befejezésére... + +Unable to create timestamp for versioning: +Nem sikerült időbélyeget generálni a verziókövetéshez + +Cannot read the following XML elements: +Nem sikerült a következő XML elemek olvasása: + +&Open... +&Megnyitás... + +Save &as... +Mentés m&ásként... + +&Quit +&Kilépés + +&Program +&Program + +&View help +&Nézd meg a súgót + +&About +&A programról + +&Help +&Súgó + +Usage: +Használat: + +1. Select folders to watch. +1. Válaszd ki a figyelendő könyvtárat. + +2. Enter a command line. +2. Add meg a parancssort. + +3. Press 'Start'. +3. Nyomd meg a Start gombot. + +To get started just import a .ffs_batch file. +Az induláshoz importálj egy .ffs_batch fájlt. + +Folders to watch: +Figyelendő könyvtárak: + +Add folder +Könyvtárat hozzáad + +Remove folder +Könyvtárat eltávolít + +Browse +Tallóz + +Select a folder +Könyvtárat kiválaszt + +Idle time (in seconds): +Üresjárat időtartama (másodperc): + +Idle time between last detected change and execution of command +Üresjárat időtartama az utolsó változás észlelése és a parancs végrehajtása között + +Command line: +Parancssor: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +A parancs végrehajtódik, ha: +- fájlok vagy alkönyvtárak megváltoznak +- új könyvtárak jelennek meg (pl. USB-kulcs csatlakoztatása) + + +&Start +&Start + +About +A programról + +Build: %x +Build: %x + +All files +Összes fájl + +Automated Synchronization +Automatizált szinkronizálás + +Directory monitoring active +A könyvtár figyelés aktív + +Waiting until all directories are available... +Várakozik, amíg az összes könyvtár elérhetővé válik... + +Error +Hiba + +&Restore +&Visszaállít + +&Show error +&Mutassa a hibákat + +&Exit +&Kilép + +Incorrect command line: +Hibás parancssor: + +&Retry +&Ismét + +File content +Fájl tartalma + +File time and size +Fájl dátuma és mérete + +Two way +Kétirányú + +Mirror +Tükröz + +Update +Frissít + +Custom +Egyedi + +Multiple... +Sokszoroz + +Moving file %x to %y +%x fájl mozgatása ide: %y + +Moving folder %x to %y +%x könyvtár mozgatása ide: %y + +Moving symbolic link %x to %y +%x szimbolikus hivatkozás mozgatása ide: %y + +Removing old versions... +Régi verziók eltávolítása... + +Creating symbolic link %x +%x szimbolikus hivatkozás létrehozása + +Creating folder %x +%x könyvtár létrehozása + +Overwriting file %x +%x fájl felülírása + +Overwriting symbolic link %x +%x szimbolikus hivatkozás felülírása + +Verifying file %x +%x fájl ellenőrzése + +Updating attributes of %x +%x attribútumainak frissítése + +Cannot find %x. +%x nem található. + +Target folder %x already existing. +%x célkönyvtár már létezik. + +Target folder input field must not be empty. +A célkönyvtárat meghatározó beviteli mező nem lehet üres. + +Please enter a target folder for versioning. +Adja meg a célkönyvtárat a verziókövetéshez. + +Source folder %x not found. +%x forráskönyvtár nem található . + +The following items have unresolved conflicts and will not be synchronized: +A következő elemek feloldatlan ütközést tartalmaznak, így nem lesznek szinkronizálva: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +A következő könyvtárak lényegileg különböznek. Győződjön meg róla, hogy a megfelelő könyvtárakat illesztette-e össze szinkronizáláshoz. + +Not enough free disk space available in: +Nincs elég szabad lemezterület: + +Required: +Szükséges: + +Available: +Szabad: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Egy olyan könyvtár kerül módosításra, amely több könyvtár-pár része. Kérjük, ellenőrizze a szinkronizálási beállításokat. + +Synchronizing folder pair: +A szinkronizált könyvtár-pár: + +Generating database... +Adatbázis generálása... + +Creating a Volume Shadow Copy for %x... +Készít egy árnyékmásolat-kötetet %x számára + +Data verification error: %x and %y have different content. +Adat-ellenőrzési hiba: %x és %y tartalma különböző. + +job name +feladat neve + +Synchronization stopped +Szinkronizálás leállítva + +Synchronization completed with errors +A szinkronizálás befejeződött, de történtek hibák + +Synchronization completed with warnings +A szinkronizálás befejeződött, de figyelmeztetésekkel + +Nothing to synchronize +Nincs mit szinkronizálni + +Synchronization completed successfully +A szinkronizálás sikeresen befejeződött + +Saving log file %x... +Naplófájl mentése a következő fájlba: %x. + +You can switch to FreeFileSync's main window to resolve this issue. +E jelenség megoldásához kapcsoljon át a FreeFileSync fő ablakába. + +&Don't show this warning again +&Ne mutassa ismételten ezt a figyelmeztetést + +&Ignore +&Kihagy + +&Switch +&Váltás + +Switching to FreeFileSync's main window +Átkapcsolás a FreeFileSync fő ablakába + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + + + +&Ignore subsequent errors +F&igyelmen kívül hagyja az utána következő hibákat + +Retrying operation... + + +Serious Error +Komoly hiba + +Check for Program Updates +Program-frissítések ellenőrzése + +A new version of FreeFileSync is available: +Elérhető a FreeFileSync új verziója: + +Download now? +Letöltsem most? + +&Download +&Letölt + +FreeFileSync is up to date. +A FreeFileSync naprakész. + +Unable to connect to sourceforge.net. +Nem sikerült a csatlakozás a sourceforge.net-hez. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Nem lehet online megtalálni a jelenlegi FreeFileSync verziószámot. Meg akarod keresni manuálisan? + +&Check + + +Symlink +Symlink + +Folder +Könyvtár + +Full path +Teljes elérési útvonal + +Name +Név + +Relative path +Relatív útvonal + +Base folder +Alapkönyvtár + +Size +Méret + +Date +Dátum + +Extension +Kiterjesztés + +Category +Kategória + +Action +Művelet + +Drag && drop +Húzd && Ejtsd + +Close progress dialog +Zárja be a folyamatjelző párbeszédablakot + +Standby +Készenléti állapot + +Log off +Kijelentkezik + +Shut down +Leállítja a gépet + +Hibernate +Hibernál + +Alternate comparison settings +Megváltoztatja az összehasonlítási beállításokat + +Alternate synchronization settings +Megváltoztatja a szinkronizálási beállításokat + +Local filter +Helyi szűrő + +Active +Aktív + +None +Egyik sem + +Remove alternate settings +Törölje a módosított beállításokat + +Clear filter settings +Törölje a szűrőbeállításokat + +Copy +Másol + +Paste +Beilleszt + +Alternate Comparison Settings +Megváltoztatja az összehasonlítási beállításokat + +Alternate Synchronization Settings +Megváltoztatja a szinkronizálási beállításokat + +Local Filter +Helyi szűrő + +&New +&Új + +&Save +&Ment + +Save as &batch job... +Mentse &kötegelt feladatként... + +1. &Compare +1. &Összehasonlít + +2. &Synchronize +2. &Szinkronizál + +&Global settings +&Globális beállítások + +&Language +&Nyelv + +&Find... +Ke&res... + +&Export file list... +&Exportálja a fájllistát... + +&Tools +Es&zközök + +&Check now +&Ellenőrizze most + +Check &automatically once a week +&Automatikus ellenőrzés hetente + +&Check for new version +&Új verzió keresése + +Compare +Összehasonlít + +Cancel +Mégsem + +Synchronize +Szinkronizál + +Add folder pair +Adjon hozzá könyvtár-párt + +Remove folder pair +Távolítsa el a könyvtár-párt + +Swap sides +Cserélje fel az oldalakat + +Close search bar +Zárja be a keresési sávot + +Find: +Keres: + +Match case +Kis-/nagybetű megkülönböztetése + +Save as batch job +Mentse kötegelt feladatként + +Hide excluded items +Rejtse el a kizárt elemeket + +Show filtered or temporarily excluded files +Mutassa a szűrt vagy ideiglenesen kizárt fájlokat + +Number of files and folders that will be created +A létrehozandó fájlok és könyvtárak száma + +Number of files that will be overwritten +A felülírandó fájlok száma + +Number of files and folders that will be deleted +A törlendő fájlok és könyvtárak száma + +Total bytes to copy +Összesen másolandó bájtok + +Select a variant: +Válasszon egy változatot: + +Identify equal files by comparing modification time and size. +Módosítási idő és méret összehasonlítása alapján azonosítsa az azonos fájlokat. + +Identify equal files by comparing the file content. +Tartalom összehasonlítása alapján azonosítsa az azonos fájlokat. + +Symbolic links: +Szimbolikus linkek: + +More information +További információ + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Változások keresése és bemutatása mindkét oldalon. A törléseket, mozgatásokat és ütközéseket automatikusan ismeri fel egy adatbázis segítségével. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Készítse el a bal oldali könyvtár egy tükör-másolatát, amely pontosan megegyezik a jobb oldali könyvtárral a szinkronizálás után. + +Copy new and updated files to the right folder. +Másolja az új és módosult fájlokat a jobb oldali könyvtárba. + +Configure your own synchronization rules. +Állítsd be a saját szinkronizálási szabályaidat + +Detect moved files +Érzékelje a mozgatott fájlokat + +Requires database files. Not supported by all file systems. +Adatbázis fájlok szükségesek. Nem minden fájlrendszer támogatja. + +Delete files: +Törölje a következő fájlokat: + +Permanent +Állandó + +Delete or overwrite files permanently +Törölje vagy írja felül véglegesen a fájlokat + +Recycle bin +Lomtár + +Back up deleted and overwritten files in the recycle bin +Mentse a törölt és felülírt fájlokat a Lomtárba + +Versioning +Verziókövetés + +Move files to a user-defined folder +Mozgassa a fájlokat egy a felhasználó által meghatározott könyvtárba + +Naming convention: +Elnevezési konvenció: + +Show examples +Mutasson példákat + +Handle errors: +A következő hibák kezelése: + +Ignore +Figyelmen kívül hagy + +Hide all error and warning messages +Rejtse el az összes hibaüzenetet és figyelmeztetést + +Pop-up +Párbeszédablak + +Show pop-up on errors or warnings +Hiba vagy figyelmeztetés esetén mutasson párbeszédablakot + +On completion: +Végrehajtás alatt: + +Start synchronization now? +Megkezdje a szinkronizálást? + +Variant: +Variáns: + +Statistics +Statisztika + +&Don't show this dialog again +&Ne mutassa ismételten ezt a párbeszédablakot + +Items found: +Talált elemek száma: + +Speed: +Sebesség: + +Time remaining: +Hátralévő idő: + +Time elapsed: +Eltelt idő: + +Synchronizing... +Szinkronizálás folyamatban... + +Minimize to notification area +Minimalizálja a figyelmeztetési területre + +Close +Bezár + +&Pause +&Várakozás + +Stop +Leállít + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Készítsen egy parancsfájlt a felügyelet nélküli szinkronizáláshoz. Az indításhoz kattintson kétszer erre a fájlra vagy ütemezze a feladat-tervezőben: %x + +Stop synchronization at first error +Állítsa le a szinkronizálást az első hibánál + +Show progress dialog +Mutassa a folyamatjelző párbeszédablakot + +Save log: +Mentse a következő napló-fájlt: + +Limit: +Határérték: + +Limit maximum number of log files +Naplófájlok maximális számának korlátja + +How can I schedule a batch job? +Hogyan tudok kötegelt feldolgozást ütemezni? + +&Recycle bin +Lomtá&r + +Delete on both sides +Törlés mindkét oldalon + +Delete on both sides even if the file is selected on one side only +Törlés mindkét oldalon, még akkor is, ha a fájl csak az egyik oldalon lett kijelölve + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Meghatározott fájlok szinkronizálásból történő kizárásához válasszon szűrőt. Az útvonalakat a megfelelő könyvtár-párjaikhoz viszonyítva adja meg. + +Include: +Bevonva: + +Exclude: +Kizárva: + +Time span: +Idősáv (időtartam): + +File size: +Fájl méret: + +Minimum: +Minimum: + +Maximum: +Maximum: + +&Clear +&Töröl + +The following settings are used for all synchronization jobs. +A következő beállítások minden szinkronizálási feladatnál használja. + +Fail-safe file copy +Hibamentes fájlmásolás + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Másolja először egy ideiglenes fájlba (*.ffs_tmp) mielőtt felülírja a célállományt. +Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. + + +(recommended) +(ajánlott) + +Copy locked files +Másolja a zárolt fájlokat + +Copy shared or locked files using the Volume Shadow Copy Service. +Megosztott vagy zárolt fájlok másolása a Kötet-árnyékmásolat szolgáltatás használatával. + +(requires administrator rights) +(rendszergazdai jogosultság szükséges) + +Copy file access permissions +Másolja a fájl hozzáférési jogosultságokat + +Transfer file and folder permissions. +Vigye át a fájl és könyvtár jogosultságokat. + +Automatic retry on error: +Automatikus visszatérés a következő hibánál: + +Retry count: +Visszatérések száma: + +Delay (in seconds): +Késedelem (másodpercben): + +Customize context menu: +Környezeti menü testreszabása: + +Description +Leírás + +Restore hidden windows +Állítsa vissza a rejtett ablakokat + +&Default +&Alapértelmezett + +Source code written in C++ using: +A programot C++-ban fejlesztették a következők felhasználásával: + +If you like FreeFileSync +Ha szereted a FreeFileSync-et + +Donate with PayPal +Támogasd a PayPal segítségével + +Feedback and suggestions are welcome +Várjuk a visszajelzéseket és az ötleteket + +Homepage +Honlap + +Email +E-mail + +Published under the GNU General Public License +Közzétéve a GNU General Public License alatt + +Many thanks for localization: +Köszönet a lokalizációért: + +Save as Batch Job +Mentse kötegelt feladatként + +Delete Items +Törölje az elemeket + +Global Settings +Globális beállítások + +Select Time Span +Válassza ki az idősávot (időtartamot) + +Folder Pairs +Könyvtár-párok + +Find +Keres + +Overview +Áttekintés + +Configuration +Beállítás + +Main Bar +Fő sáv + +Filter Files +Szűrje a fájlokat + +Select View +Válasszon nézetet + +Open... +Megnyit... + +Save +Ment + +Compare both sides +Vesse össze mindkét oldalt + +Comparison settings +Összehasonlítási beállítások + +Synchronization settings +Szinkronizálási beállítások + +Start synchronization +Indítja a szinkronizálást + +Confirm +Jóváhagy + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + +Tényleg a végre akarja hajtani a(z) %y parancsot a következő elemen? +Tényleg a végre akarja hajtani a(z) %y parancsot a következő %x elemen? + + +&Execute +Vé&grehajt + + +1 directory +%x directories + + +1 könyvtárat +%x könyvtárat + + + +1 file +%x files + + +1 fájlt +%x fájlt + + + +%y of 1 row in view +%y of %x rows in view + + +1 sorból %y sor látható +%x sorból %y látható + + +Set direction: +Állítsa be az irányt: + +multiple selection +többszörös kijelölés + +Include via filter: +A szűrőt figyelembe véve adja hozzá + +Exclude via filter: +Zárja ki szűrő segítségével: + +Exclude temporarily +Zárja ki ideiglenesen + +Include temporarily +Csatolja ideiglenesen + +Delete +Töröl + +Include all +Csatolja az összest + +Exclude all +Zárja ki az összest + +Show icons: +Mutassa az ikonokat: + +Small +Kicsi + +Medium +Közepes + +Large +Nagy + +Select time span... +Időintervallum kiválasztása... + +Default view +Alapértelmezett nézet + +Show "%x" +"%x" mutatása + +Last session +Utolsó munkamenet + +Folder Comparison and Synchronization +Könyvtár összehasonlítás és szinkronizálás + +Configuration saved +Beállítások elmentve + +FreeFileSync batch +FreeFileSync kötegelt fájl + +Do you want to save changes to %x? +Akarod menteni a(z) %x változtatásait? + +Never save &changes +N&e mentse a változásokat + +Do&n't save +&ne mentse + +Filter +Szűrő + +Show files that exist on left side only +Mutassa a csak a bal oldalon létező fájlokat + +Show files that exist on right side only +Mutassa a csak a jobb oldalon létező fájlokat + +Show files that are newer on left +Mutassa a fájlokat, amelyek a bal oldalon frissebbek + +Show files that are newer on right +Mutassa a fájlokat, amelyek a jobb oldalon frissebbek + +Show files that are equal +Mutassa az egyező fájlokat + +Show files that are different +Mutassa az eltérő fájlokat + +Show conflicts +Mutassa az ütközéseket + +Show files that will be created on the left side +Mutassa a bal oldalon létrehozandó fájlokat + +Show files that will be created on the right side +Mutassa a jobb oldalon létrehozandó fájlokat + +Show files that will be deleted on the left side +Mutassa a bal oldalon törlendő fájlokat + +Show files that will be deleted on the right side +Mutassa a jobb oldalon törlendő fájlokat + +Show files that will be overwritten on left side +Mutassa a bal oldalon felülírandó fájlokat + +Show files that will be overwritten on right side +Mutassa a jobb oldalon felülírandó fájlokat + +Show files that won't be copied +Mutassa a nem másolandó fájlokat + +Set as default +Beállítás alapértelmezettként + +All folders are in sync +Minden könyvtár szinkronban + +Synchronization Settings +Szinkronizálási beállítások + +Comparison Settings +Összehasonlítási beállítások + +Cannot find %x +Nem található: %x + +Comma-separated values +Vesszővel elválasztott értékek (CSV) + +File list exported +A fájllista exportálása befejeződött + +Searching for program updates... +Programfrissítés keresése... + +Scanning... +Vizsgálat folyamatban... + +Comparing content... +Tartalom összehasonlítása... + +Info +Információ + +Warning +Figyelmeztetés + +Paused +Megállítva + +Initializing... +Inicializálás... + +Stopped +Leállítva + +Completed +Befejeződött + +&Continue +&Folytat + +Log +Napló + +Today +Mai + +This week +E heti + +This month +E havi + +This year +Ez évi + +Last x days +Utolsó x nap + +Byte +Bájt + +KB +kB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Tényleg a Lomtárba akarja áthelyezni a következő elemet? +Tényleg a Lomtárba akarja áthelyezni a következő %x elemet? + + +Move +Mozgat + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Valóban törölni akarod a következő elemet? +Valóban törölni akarod a következő %x elemet? + + +Exclude +Kizár + +Direct +Közvetlen + +Follow +Követ + +Copy NTFS permissions +Másolja az NTFS jogosultságokat + +Integrate external applications into context menu. The following macros are available: +Integráljon a helyi menübe külső alkalmazásokat. A következő makrók érhetők el: + +- full file or folder name +- teljes fájl- vagy könyvtárnév + +- folder part only +- csak a könyvtár része + +- Other side's counterpart to %item_path% +- A másik oldali megfelelőjét a következő fájlba: %item_path% + +- Other side's counterpart to %item_folder% +- A másik oldali megfelelőjét a következő könyvtárba: %item_folder% + +Restore all hidden windows and warnings? +Visszaállítsa az összes rejtett ablakot és figyelmeztetést? + +Leave as unresolved conflict +Hagyja feloldatlan ütközésként + +Replace +Felülír + +Move files and replace if existing +Mozgassa a fájlokat és írja felül amennyiben léteztek + +Time stamp +Időbélyeg + +Append a timestamp to each file name +Adjon időbélyeget minden fájlnévhez + +File +Fájl + +YYYY-MM-DD hhmmss +ÉÉÉÉ-HH-NN óóppmm + +Files +Fájlok + +Items +Elemek + +Percentage +Százalék + +Cannot monitor directory %x. +Nem sikerült monitorozni a(z) %x könyvtárat. + +Conversion error: +Konverziós hiba: + +Cannot delete file %x. +Nem sikerült a(z) %x fájl törlése. + +The file is locked by another process: +A fájlt egy másik processz zárolta: + +Cannot move file %x to %y. +Nem sikerült %x fájl mozgatása %y fájlba. + +Cannot delete directory %x. +Nem sikerült a %x könyvtár törlése. + +Cannot write file attributes of %x. +Nem sikerült %x fájl attribútumainak írása. + +Cannot write modification time of %x. +Nem sikerült felírni az utolsó módosítás dátumát a következőnél: %x. + +Cannot read security context of %x. +Nem sikerült a biztonsági környezet olvasása a következőnél: %x. + +Cannot write security context of %x. +Nem sikerült a biztonsági környezet írása a következőnél: %x. + +Cannot read permissions of %x. +Nem sikerült a jogosultságok olvasása a következőnél: %x. + +Cannot write permissions of %x. +Nem sikerült a jogosultságok írása a következőnél: %x. + +Cannot create directory %x. +Nem sikerült a következő könyvtár létrehozása: %x. + +Cannot create symbolic link %x. +Nem lehet létrehozni a(z) %x szimbolikus hivatkozást + +Cannot find system function %x. +Nem található a következő rendszerfunkció: %x. + +Cannot copy file %x to %y. +%x fájl másolása a(z) %y fájlba nem sikerült. + +Type of item %x is not supported: +A(z) %x elem típusa nem támogatott: + +Cannot resolve symbolic link %x. +Nem sikerült a következő szimbolikus hivatkozás feloldása: %x. + +Cannot open directory %x. +A %x könyvtár megnyitása nem sikerült. + +Cannot enumerate directory %x. +Nem sikerült besorolni a(z) %x könyvtárat. . + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +1 perc +%x perc + + + +1 hour +%x hours + + +1 óra +%x óra + + + +1 day +%x days + + +1 nap +%x nap + + +Unable to register to receive system messages. +Nem tud regisztrálni hogy megkapja a rendszerüzeneteket. + +Cannot set privilege %x. +A következő privilégium beállítása sikertelen: %x. + +Unable to suspend system sleep mode. +Nem tudta felfüggeszteni a rendszer alvó üzemmódját. + +Cannot change process I/O priorities. +Nem sikerült a folyamatok I/O prioritását megváltoztatni. + +Unable to move %x to the recycle bin. +%x nem helyezhető a Lomtárba + +Cannot determine final path for %x. +Nem lehet meghatározni a végső útvonalat a(z) %x számára. + +Error Code %x: +Hibakód %x: + diff --git a/FreeFileSync/Build/Languages/italian.lng b/FreeFileSync/Build/Languages/italian.lng new file mode 100644 index 00000000..04b3e5c8 --- /dev/null +++ b/FreeFileSync/Build/Languages/italian.lng @@ -0,0 +1,1517 @@ +
    + Italiano + Luciano Paravella + it_IT + flag_italy.png + 2 + n == 1 ? 0 : 1 +
    + +Both sides have changed since last synchronization. +Entrambi i lati sono cambiati dall'ultima sincronizzazione. + +Cannot determine sync-direction: +Impossibile determinare direzione di sincronia: + +No change since last synchronization. +Nessun cambiamento dall'ultima sincronizzazione. + +The database entry is not in sync considering current settings. +La voce di database non è in sincronia con le impostazioni correnti. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Imposta le direzioni di sincronizzazione predefinite: i vecchi file saranno sovrascritti dai nuovi. + +Checking recycle bin availability for folder %x... +Controllo di disponibilità Cestino per la cartella %x... + +Moving file %x to the recycle bin +Spostamento file %x nel cestino + +Moving folder %x to the recycle bin +Spostamento delle cartelle %x nel cestino + +Moving symbolic link %x to the recycle bin +Spostamento collegamento %x nel cestino + +Deleting file %x +Eliminazione file %x + +Deleting folder %x +Eliminazione cartella %x + +Deleting symbolic link %x +Eliminazione collegamento %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +Il cestino non è disponibile per le seguenti cartelle. I file verranno invece eliminati in modo permanente: + +An exception occurred +Si è verificata una eccezione + +A directory path is expected after %x. +E' previsto un percorso di directory dopo %x. + +Syntax error +Errore di sintassi + +Cannot open file %x. +Impossibile aprire il file %x. + +File %x does not contain a valid configuration. +Il file %x non contiene una configurazione valida. + +Unequal number of left and right directories specified. +E' stato specificato un numero diverso di directory a sinistra e a destra. + +The config file must not contain settings at directory pair level when directories are set via command line. +Il file di configurazione non deve contenere le impostazioni a livello di coppia di directory quando le directory sono impostate tramite linea di comando. + +Directories cannot be set for more than one configuration file. +Le directory non possono essere impostate per più di un file di configurazione. + +Command line +Linea di comando + +Syntax: +Sintassi: + +config files +file di configurazione + +directory +directory + +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 directories for at most one config file. +Qualsiasi numero di directory alternative per al massimo un file di configurazione. + +A folder input field is empty. +Un campo di input cartella è vuoto. + +The corresponding folder will be considered as empty. +La cartella corrispondente sarà considerata come vuota. + +Cannot find the following folders: +Impossibile trovare le seguenti cartelle: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +È possibile ignorare questo errore che considera ogni cartella come vuota. Le cartelle poi verranno create automaticamente durante la sincronizzazione. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +Le seguenti cartelle hanno percorsi dipendenti. Fare attenzione quando si imposta le regole di sincronizzazione: + +File %x has an invalid date. +Il file %x ha una data non valida. + +Date: +Data: + +Files %x have the same date but a different size. +I file %x hanno la stessa data ma dimensione diversa. + +Size: +Dimensione: + +Items differ in attributes only +Gli oggetti differiscono solo negli attributi + +Resolving symbolic link %x +Risoluzione collegamento %x + +Comparing content of files %x +Confronto contenuto del file %x + +Generating file list... +Generazione elenco file... + +Starting comparison +Inizio confronto + +Calculating sync directions... +Calcolo della direzione di sincronizzazione... + +Out of memory. +Memoria insufficiente. + +Item exists on left side only +L'oggetto esiste solo sul lato sinistro + +Item exists on right side only +L'oggetto esiste solo sul lato destro + +Left side is newer +Il più recente è sul lato sinistro + +Right side is newer +Il più recente è sul lato destro + +Items have different content +Gli oggetti hanno contenuto differente + +Both sides are equal +Entrambi i lati sono uguali + +Conflict/item cannot be categorized +Conflitto/oggetto non categorizzabile + +Copy new item to left +Copia nuovo oggetto a sinistra + +Copy new item to right +Copia nuovo oggetto a destra + +Delete left item +Elimina oggetto di sinistra + +Delete right item +Elimina oggetto di destra + +Move file on left +Sposta il file a sinistra + +Move file on right +Sposta il file a destra + +Overwrite left item +Sovrascrivi oggetto di sinistra + +Overwrite right item +Sovrascrivi oggetto di destra + +Do nothing +Non fare nulla + +Update attributes on left +Aggiorna attributi a sinistra + +Update attributes on right +Aggiorna attributi a destra + +Database file %x is incompatible. +Il file del database %x non è compatibile. + +Initial synchronization: +Prima sincronizzazione: + +Database file %x does not yet exist. +Il file del database %x non è ancora stato creato. + +Database file is corrupt: +Il file del database è corrotto: + +Cannot write file %x. +Impossibile scrivere il file %x. + +Cannot read file %x. +Impossibile leggere il file %x. + +Database files do not share a common session. +I file del database non condividono una sessione comune. + +Searching for folder %x... +Ricerca della cartella %x... + +Cannot read file attributes of %x. +Impossibile leggere gli attributi del file %x. + +Cannot get process information. +Impossibile ottenere informazioni sul processo. + +Waiting while directory is locked (%x)... +Attendere, la cartella viene bloccata (%x)... + + +1 sec +%x sec + + +1 sec +%x sec + + +Creating file %x +Creazione file %x + +Items processed: +Oggetti processati: + +Items remaining: +Oggetti rimanenti: + +Total time: +Tempo totale: + + +1 byte +%x bytes + + +1 byte +%x byte + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Errore nel parsing del file %x, riga %y, colonna %z. + +Cannot set directory lock for %x. +Impossibile impostare il blocco cartella per %x. + +Scanning: +Scansione di: + + +1 thread +%x threads + + +1 thread +%x thread + + +Encoding extended time information: %x +Codifica estesa informazioni orario: %x + +/sec +/sec + +%x items/sec +%x elementi/sec + +Configuration file %x loaded partially only. +File di configurazione %x caricato solo parzialmente. + +Show in Explorer +Mostra in Esplora Risorse + +Open with default application +Apri con applicazione predefinita + +Browse directory +Sfoglia cartelle + +Cannot access the Volume Shadow Copy Service. +Impossibile accedere al Volume Shadow Copy Service. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +E' necessario utilizzare FreeFileSync versione 64-bit per creare copie shadow su questo sistema. + +Cannot load file %x. +Impossibile caricare il file %x. + +Cannot determine volume name for %x. +Impossibile determinare nome del volume per %x. + +Volume name %x is not part of file path %y. +Nome volume %x non fa parte del percorso del file %y. + +Stop requested: Waiting for current operation to finish... +Bloccata richiesta: Aspettare la fine dell'operazione in corso ... + +Unable to create timestamp for versioning: +Impossibile creare l'ora per la versione: + +Cannot read the following XML elements: +Impossibile leggere i seguenti elementi XML: + +&Open... +&Apri... + +Save &as... +Salva &come... + +&Quit +&Esci + +&Program +&Programma + +&View help +&Vedere aiuto + +&About +&Informazioni + +&Help +&Aiuto + +Usage: +Uso: + +1. Select folders to watch. +1. Seleziona cartelle da controllare. + +2. Enter a command line. +2. Inserisci linea di comando. + +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 + +Folders to watch: +Cartelle da guardare: + +Add folder +Aggiungi cartella + +Remove folder +Rimuovi cartella + +Browse +Sfoglia + +Select a folder +Seleziona una cartella + +Idle time (in seconds): +Tempo di inattività (in secondi): + +Idle time between last detected change and execution of command +Tempo di attesa tra ultimo cambiamento rilevato ed l'esecuzione del comando + +Command line: +Riga di comando: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +Il comando è attivato se: +- file o sottocartelle cambiano +- nuove cartelle vengono aggiunte (es. inserimento di memoria USB) + + +&Start +&Start + +About +Info su + +Build: %x +Versione: %x + +All files +Tutti i file + +Automated Synchronization +Sincronizzazione automatizzata + +Directory monitoring active +Monitoraggio directory attivo + +Waiting until all directories are available... +Attendere finchè tutte le directory diventano disponibili... + +Error +Errore + +&Restore +&Ripristina + +&Show error +&Mostra errore + +&Exit +&Esci + +Incorrect command line: +Linea di comando non corretta: + +&Retry +&Riprova + +File content +Contenuto del file + +File time and size +Ora e dimensione file + +Two way +Due vie + +Mirror +Specchio + +Update +Aggiorna + +Custom +Personalizza + +Multiple... +Multiplo... + +Moving file %x to %y +Spostamento file %x in %y + +Moving folder %x to %y +Spostamento cartella %x in %y + +Moving symbolic link %x to %y +Spostamento collegamento %x in %y + +Removing old versions... +Rimozione di vecchie versioni... + +Creating symbolic link %x +Creazione collegamento %x + +Creating folder %x +Creazione cartella %x + +Overwriting file %x +Sovrascrittura file %x + +Overwriting symbolic link %x +Sovrascrittura collegamento %x + +Verifying file %x +Verifica file %x + +Updating attributes of %x +Aggiornamento attributi di %x + +Cannot find %x. +Impossibile trovare %x. + +Target folder %x already existing. +La cartella di destinazione %x è già esistente. + +Target folder input field must not be empty. +Il campo per la cartella di destinazione non può essere vuoto. + +Please enter a target folder for versioning. +Inserisci una cartella di destinazione per il controllo delle versioni. + +Source folder %x not found. +Cartella sorgente %x non trovata. + +The following items have unresolved conflicts and will not be synchronized: +I seguenti oggetti hanno conflitti irrisolti e non saranno sincronizzati: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +Le seguenti cartelle sono significativamente differenti. Assicurarsi che si sta confrontando le cartelle corrette per la sincronizzazione. + +Not enough free disk space available in: +Spazio libero su disco insufficiente in: + +Required: +Richiesto: + +Available: +Disponibile: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Verrà modificata una cartella che è parte di molte coppie di cartelle. Controlla le impostazioni di sincronizzazione. + +Synchronizing folder pair: +Sincronizzazione della coppia di cartelle: + +Generating database... +Generazione database... + +Creating a Volume Shadow Copy for %x... +Creazione di un Volume Shadow Copy per %x... + +Data verification error: %x and %y have different content. +Dati di verifica errore: %x e %y hanno un contenuto diverso. + +job name +nome del lavoro + +Synchronization stopped +Sincronizzazione fermata + +Synchronization completed with errors +Sincronizzazione completata con errori + +Synchronization completed with warnings +Sincronizzazione completata con avvisi + +Nothing to synchronize +Non c'è nulla da sincronizzare + +Synchronization completed successfully +Sincronizzazione completata con successo + +Saving log file %x... +Salvataggio file di log %x... + +You can switch to FreeFileSync's main window to resolve this issue. +È possibile passare alla finestra principale di FreeFileSync per risolvere questo problema. + +&Don't show this warning again +&Non mostrare più questo avviso + +&Ignore +&Ignora + +&Switch +&Passa a + +Switching to FreeFileSync's main window +Passaggio alla finestra principale di FreeFileSync + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + +Riprova Automatica in 1 secondo... +Riprova Automatica in %x secondi... + + +&Ignore subsequent errors +&Ignora errori successivi + +Retrying operation... +Riprovare l'Operazione... + +Serious Error +Errore Grave + +Check for Program Updates +Controlla Aggiornamenti del Programma + +A new version of FreeFileSync is available: +E' disponibile una nuova versione di FreeFileSync: + +Download now? +Scaricarla ora? + +&Download +&Scaricare + +FreeFileSync is up to date. +FreeFileSync è aggiornato. + +Unable to connect to sourceforge.net. +Impossibile collegarsi a sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Non riesci a trovare l'attuale numero di versione di FreeFileSync on-line. Vuoi controllare manualmente? + +&Check +&Controllare + +Symlink +Symlink + +Folder +Cartella + +Full path +Percorso completo + +Name +Nome + +Relative path +Percorso relativo + +Base folder +Cartella di partenza + +Size +Dimensione + +Date +Data + +Extension +Estensione + +Category +Categoria + +Action +Azioni + +Drag && drop +Trascina + +Close progress dialog +Chiudi stato di avanzamento + +Standby +Sospendi + +Log off +Termina sessione + +Shut down +Arresta + +Hibernate +Iberna + +Alternate comparison settings +Impostazioni di Confronto Alternativi + +Alternate synchronization settings +Impostazioni di Sincronizzazione Alternativi + +Local filter +Filtro Locale + +Active +Attivo + +None +Nessuno + +Remove alternate settings +Rimuovi impostazioni alternative + +Clear filter settings +Azzera impostazioni filtro + +Copy +Copia + +Paste +Incolla + +Alternate Comparison Settings +Impostazioni di Confronto Alternativi + +Alternate Synchronization Settings +Impostazioni di Sincronizzazione Alternativi + +Local Filter +Filtro Locale + +&New +&Nuovo + +&Save +&Salva + +Save as &batch job... +Salva come &processo batch... + +1. &Compare +1. &Compara + +2. &Synchronize +2. &Sincronizza + +&Global settings +&Preferenze + +&Language +&Lingua + +&Find... +&Trova ... + +&Export file list... +&Esporta l'elenco dei file... + +&Tools +&Strumenti + +&Check now +&Controlla ora + +Check &automatically once a week +Controlla &automaticamente una volta a settimana + +&Check for new version +&Controlla nuova versione + +Compare +Compara + +Cancel +Annulla + +Synchronize +Sincronizza + +Add folder pair +Aggiungi una coppia di cartelle + +Remove folder pair +Elimina la coppia di cartelle + +Swap sides +Inverti i lati + +Close search bar +Chiudi barra di ricerca + +Find: +Trova: + +Match case +Corrispondenza + +Save as batch job +Salva come processo batch + +Hide excluded items +Nascondi oggetti esclusi + +Show filtered or temporarily excluded files +Mostra file filtrati o temporaneamente esclusi + +Number of files and folders that will be created +Numero di file e cartelle che verranno creati + +Number of files that will be overwritten +Numero di file che verranno sovrascritti + +Number of files and folders that will be deleted +Numero di file e cartelle che verranno eliminati + +Total bytes to copy +Bytes totali da copiare + +Select a variant: +Scegli una variante: + +Identify equal files by comparing modification time and size. +Identificare i file uguali confrontando data di modifica e dimensione. + +Identify equal files by comparing the file content. +Identificare i file uguali confrontando il contenuto del file. + +Symbolic links: +Link simbolici: + +More information +Maggiori informazioni + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Identifica e propaga cambiamenti su entrambi i lati. Cancellazioni, spostamenti e conflitti sono identificati automaticamente usando un database. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Creare un backup specchio della cartella di sinistra, che corrisponda esattamente alla cartella destra dopo la sincronizzazione. + +Copy new and updated files to the right folder. +Copiare i file nuovi e aggiornati nella cartella destra. + +Configure your own synchronization rules. +Configura le tue regole di sincronizzazione. + +Detect moved files +Rileva file spostati + +Requires database files. Not supported by all file systems. +Richiede il file di database. Non è supportato da tutti i sistemi di file. + +Delete files: +Eliminare i file: + +Permanent +Permanente + +Delete or overwrite files permanently +Elimina o sovrascrivi i file definitivamente + +Recycle bin +Cestino + +Back up deleted and overwritten files in the recycle bin +Eseguire il back up dei file cancellati o sovrascritti nel cestino + +Versioning +Versione + +Move files to a user-defined folder +Spostare i file in una cartella definita dall'utente + +Naming convention: +Modalità di rinomina: + +Show examples +Mostra esempi + +Handle errors: +Gestire gli errori: + +Ignore +Ignora + +Hide all error and warning messages +Nascondi tutti gli errori e i messaggi d'avviso + +Pop-up +Pop-up + +Show pop-up on errors or warnings +Mostra pop-up di errore o avviso + +On completion: +Al termine: + +Start synchronization now? +Avviare la sincronizzazione ora? + +Variant: +Variante: + +Statistics +Statistiche + +&Don't show this dialog again +&Non mostrare più questo avviso + +Items found: +Oggetti trovati: + +Speed: +Velocita': + +Time remaining: +Tempo rimanente: + +Time elapsed: +Tempo trascorso: + +Synchronizing... +Sincronizzazione... + +Minimize to notification area +Ridurre al minimo l'area di notifica + +Close +Chiudi + +&Pause +&Pausa + +Stop +Arresto + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Creare un file batch per la sincronizzazione automatica. Per iniziare, fare doppio clic su questo file o programmarlo in un pianificatore di compiti: %x + +Stop synchronization at first error +Interrompere la sincronizzazione al primo errore + +Show progress dialog +Mostra stato di avanzamento + +Save log: +Salva log: + +Limit: +Limite: + +Limit maximum number of log files +Limita il numero massimo di file log + +How can I schedule a batch job? +Come posso programmare un lavoro batch? + +&Recycle bin +&Cestino + +Delete on both sides +Elimina su entrambi i lati + +Delete on both sides even if the file is selected on one side only +Elimina su entrambi i lati anche se il file è selezionato su un solo lato. + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Selezionare le regole del filtro per escludere alcuni file dalla sincronizzazione. Immettere i percorsi dei file relativi alla loro corrispondente coppia di cartelle. + +Include: +Includi: + +Exclude: +Escludi: + +Time span: +Intervallo di tempo: + +File size: +Dimensione del file: + +Minimum: +Minimo: + +Maximum: +Massimo: + +&Clear +&Pulisci + +The following settings are used for all synchronization jobs. +Le seguenti impostazioni vengono utilizzate per tutti i processi di sincronizzazione. + +Fail-safe file copy +Copia file a prova di errore + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Copiare in un file temporaneo ( *.ffs_tmp ) prima di sovrascrivere il bersaglio. +Questo garantisce uno stato consistente anche in caso di errore grave. + + +(recommended) +(consigliato) + +Copy locked files +Copia file bloccati + +Copy shared or locked files using the Volume Shadow Copy Service. +Copiare file condivisi o bloccati con il Volume Shadow Copy Service. + +(requires administrator rights) +(richiede i diritti di amministratore) + +Copy file access permissions +Copia permessi di accesso file + +Transfer file and folder permissions. +Trasferimento autorizzazioni di file e cartelle. + +Automatic retry on error: +Tentativo Automatico in caso di errore: + +Retry count: +Riprova conteggio: + +Delay (in seconds): +Ritardo (in secondi): + +Customize context menu: +Personalizzare menu contestuale: + +Description +Descrizione + +Restore hidden windows +Ripristina le finestre nascoste + +&Default +&Predefinito + +Source code written in C++ using: +Codice sorgente scritto in C++ utilizzando: + +If you like FreeFileSync +Se ti piace FreeFileSync + +Donate with PayPal +Fai una donazione con PayPal + +Feedback and suggestions are welcome +Ogni commento o suggerimento è ben accetto + +Homepage +Homepage + +Email +Email + +Published under the GNU General Public License +Pubblicato con licenza GNU General Public + +Many thanks for localization: +Ringraziamenti per la traduzione: + +Save as Batch Job +Salva come Processo Batch + +Delete Items +Elimina Elementi + +Global Settings +Impostazioni Globali + +Select Time Span +Selezionare Intervallo di Tempo + +Folder Pairs +Coppie di Cartelle + +Find +Trova + +Overview +Anteprima + +Configuration +Configurazione + +Main Bar +Barra Principale + +Filter Files +Filtrare i file + +Select View +Selezionare Visualizza + +Open... +Apri... + +Save +Salva + +Compare both sides +Confronta entrambi i lati + +Comparison settings +Impostazioni di comparazione + +Synchronization settings +Parametri di sincronizzazione + +Start synchronization +Avvia sincronizzazione + +Confirm +Confermare + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + +Vuoi veramente eseguire il comando %y per un elemento? +Vuoi veramente eseguire il comando %y per %x elementi? + + +&Execute +&Esecuzione + + +1 directory +%x directories + + +1 directory +%x directory + + + +1 file +%x files + + +1 file +%x file + + + +%y of 1 row in view +%y of %x rows in view + + +%y di 1 riga nella vista +%y di %x righe nella vista + + +Set direction: +Imposta direzione: + +multiple selection +selezione multipla + +Include via filter: +Includi tramite filtro: + +Exclude via filter: +Escludi tramite filtro: + +Exclude temporarily +Escludi temporaneamente + +Include temporarily +Includi temporaneamente + +Delete +Elimina + +Include all +Includi tutto + +Exclude all +Escludi tutto + +Show icons: +Mostra icone: + +Small +Piccole + +Medium +Medie + +Large +Grandi + +Select time span... +Seleziona intervallo di tempo... + +Default view +Vista predefinita + +Show "%x" +Mostra "%x" + +Last session +Ultima sessione + +Folder Comparison and Synchronization +Comparazione delle Cartelle e Sincronizzazione + +Configuration saved +Configurazione salvata + +FreeFileSync batch +FreeFileSync batch + +Do you want to save changes to %x? +Vuoi salvare le modifiche a %x? + +Never save &changes +Non salvare mai &modifiche + +Do&n't save +No&n salvare + +Filter +Filtro + +Show files that exist on left side only +Mostra file esistenti solo a sinistra + +Show files that exist on right side only +Mostra file esistenti solo a destra + +Show files that are newer on left +Mostra i file che sono più recenti sulla sinistra + +Show files that are newer on right +Mostra i file che sono più recenti sulla destra + +Show files that are equal +Mostra i file identici + +Show files that are different +Mostra i file differenti + +Show conflicts +Mostra conflitti + +Show files that will be created on the left side +Mostra i file che verranno creati sul lato sinistro + +Show files that will be created on the right side +Mostra i file che verranno creati sul lato destro + +Show files that will be deleted on the left side +Mostra i file che verranno cancellati sul lato sinistro + +Show files that will be deleted on the right side +Mostra i file che verranno cancellati sul lato destro + +Show files that will be overwritten on left side +Mostra i file che verranno sovrascritti sul lato sinistro + +Show files that will be overwritten on right side +Mostra i file che verranno sovrascritti sul lato destro + +Show files that won't be copied +Mostra i file che non saranno copiati + +Set as default +Imposta come predefinito + +All folders are in sync +Tutte le cartelle risultano sincronizzate + +Synchronization Settings +Impostazioni di Sincronizzazione + +Comparison Settings +Impostazioni di Confronto + +Cannot find %x +Impossibile trovare %x + +Comma-separated values +Valori separati da virgole + +File list exported +Elenco file esportato + +Searching for program updates... +Ricerca di aggiornamenti al programma... + +Scanning... +Scansione... + +Comparing content... +Comparazione del contenuto... + +Info +Info + +Warning +Attenzione + +Paused +In pausa + +Initializing... +Inizializzazione... + +Stopped +Arrestato + +Completed +Completato + +&Continue +&Continuare + +Log +Log + +Today +Oggi + +This week +Questa settimana + +This month +Questo mese + +This year +Quest'anno + +Last x days +Ultimi x giorni + +Byte +Byte + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Vuoi davvero spostare il seguente oggetto nel cestino? +Vuoi davvero spostare i seguenti %x oggetti nel cestino? + + +Move +Spostare + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Vuoi davvero eliminare il seguente oggetto? +Vuoi davvero eliminare i seguenti %x oggetti? + + +Exclude +Escludi + +Direct +Diretto + +Follow +Segui + +Copy NTFS permissions +Copia permessi NTFS + +Integrate external applications into context menu. The following macros are available: +Integra applicazioni esterne nel menu contestuale. Sono disponibili le seguenti macro: + +- full file or folder name +- nome completo file o cartella + +- folder part only +- solo porzione di cartella + +- Other side's counterpart to %item_path% +- Omologo dell'altro lato a %item_path% + +- Other side's counterpart to %item_folder% +- Omologo dell'altro lato a %item_folder% + +Restore all hidden windows and warnings? +Ripristina tutte le finestre nascoste e gli avvisi? + +Leave as unresolved conflict +Lascia come conflitti irrisolti + +Replace +Sostituisci + +Move files and replace if existing +Sposta i file e sostituisci se esistenti + +Time stamp +Data e ora + +Append a timestamp to each file name +Accoda data e ora ad ogni nome di file + +File +File + +YYYY-MM-DD hhmmss +AAAA-MM-GG hhmmss + +Files +File + +Items +Oggetti + +Percentage +Percentuale + +Cannot monitor directory %x. +Impossibile monitorare la cartella %x. + +Conversion error: +Errore di conversione: + +Cannot delete file %x. +Impossibile eliminare il file %x. + +The file is locked by another process: +Il file è bloccato da un altro processo: + +Cannot move file %x to %y. +Impossibile spostare il file %x in %y. + +Cannot delete directory %x. +Impossibile eliminare la cartella %x. + +Cannot write file attributes of %x. +Impossibile scrivere gli attributi del file %x. + +Cannot write modification time of %x. +Impossibile scrivere data e ora di modifica di %x. + +Cannot read security context of %x. +Impossibile leggere il contesto di protezione di %x. + +Cannot write security context of %x. +Impossibile scrivere il contesto di protezione di %x. + +Cannot read permissions of %x. +Impossibile leggere i permessi di %x. + +Cannot write permissions of %x. +Impossibile scrivere i permessi di %x. + +Cannot create directory %x. +Impossibile creare la cartella %x. + +Cannot create symbolic link %x. +Impossibile creare il collegamento %x. + +Cannot find system function %x. +Impossibile trovare la funzione di sistema %x. + +Cannot copy file %x to %y. +Impossibile copiare il file %x in %y. + +Type of item %x is not supported: +Il tipo di oggetto %x non è supportato: + +Cannot resolve symbolic link %x. +Impossibile risolvere collegamento %x. + +Cannot open directory %x. +Impossibile aprire la cartella %x. + +Cannot enumerate directory %x. +Impossibile enumerare la cartella %x. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +1 min +%x min + + + +1 hour +%x hours + + +1 ora +%x ore + + + +1 day +%x days + + +1 giorno +%x giorni + + +Unable to register to receive system messages. +Impossibile registrarsi per ricevere i messaggi di sistema. + +Cannot set privilege %x. +Impossibile impostare privilegi %x. + +Unable to suspend system sleep mode. +Impossibile interrompere la modalità di sospensione del sistema. + +Cannot change process I/O priorities. +Impossibile modificare le priorità I/O del processo. + +Unable to move %x to the recycle bin. +Impossibile spostare %x nel cestino. + +Cannot determine final path for %x. +Impossibile determinare il percorso finale per %x. + +Error Code %x: +Codice Errore %x: + diff --git a/FreeFileSync/Build/Languages/japanese.lng b/FreeFileSync/Build/Languages/japanese.lng new file mode 100644 index 00000000..f32e722b --- /dev/null +++ b/FreeFileSync/Build/Languages/japanese.lng @@ -0,0 +1,1502 @@ +
    + 日本語 + Tilt + ja_JP + flag_japan.png + 1 + 0 +
    + +Both sides have changed since last synchronization. +前回最後の同期処理以降、両側とも変更があります. + +Cannot determine sync-direction: +同期方向が決定されていません: + +No change since last synchronization. +前回の同期以降、変更はありません. + +The database entry is not in sync considering current settings. +現在の設定により、このデータベースエントリの同期は行われていません. + +Setting default synchronization directions: Old files will be overwritten with newer files. +同期方向のデフォルト設定: 古いファイルに新しいファイルを上書き + +Checking recycle bin availability for folder %x... +フォルダ %x をゴミ箱として使用できるかを確認中... + +Moving file %x to the recycle bin +ファイル %x をゴミ箱に移動中 + +Moving folder %x to the recycle bin +フォルダ %x をゴミ箱に移動中 + +Moving symbolic link %x to the recycle bin +シンボリックリンク %x をゴミ箱に移動中 + +Deleting file %x +ファイル %x を削除中 + +Deleting folder %x +フォルダ %x を削除中 + +Deleting symbolic link %x +シンボリックリンク %x を削除中 + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +以下のフォルダではゴミ箱を利用できません。代替手段としてファイルの完全削除を利用することが出来ます: + +An exception occurred +例外が発生しました + +A directory path is expected after %x. +%x の後にはディレクトリ・パスが必要です + +Syntax error +構文エラー + +Cannot open file %x. +ファイル %x を開けません + +File %x does not contain a valid configuration. +ファイル %x には有効な構成が含まれていません. + +Unequal number of left and right directories specified. +誤った数の左右ディレクトリが指定されています. + +The config file must not contain settings at directory pair level when directories are set via command line. +コマンドライン経由でディレクトリが設定されている時は、構成設定にペア・レベルのディレクトリは含められません. + +Directories cannot be set for more than one configuration file. +構成設定ファイルにひとつ以上のディレクトリは設定できません. + +Command line +コマンドライン + +Syntax: +構文: + +config files +構成設定ファイル + +directory +ディレクトリ + +Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. +任意の数の FreeFileSync 構成設定ファイル(.ffs_gui および/または .ffs_batch). + +Any number of alternative directories for at most one config file. +ひとつの構成設定ファイルに対して任意の数の代替ディレクトリ. + +A folder input field is empty. +フォルダ入力欄が空白です. + +The corresponding folder will be considered as empty. +対応するフォルダは空であるとみなされます + +Cannot find the following folders: +以下のフォルダがみつかりません: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +各フォルダを空とみなす場合はこのエラーを無視することができます。このフォルダは、同期処理を実行する際自動的に作成されるものです。 + +The following folders have dependent paths. Be careful when setting up synchronization rules: +以下のフォルダには、依存関係にあるパスが存在します。同期規則を設定する時は慎重に行ってください: + +File %x has an invalid date. +ファイル %x の日付は無効なものです. + +Date: +日時: + +Files %x have the same date but a different size. +ファイル %x は、同じ時間ですがサイズが異なっています. + +Size: +サイズ: + +Items differ in attributes only +属性のみ異なる項目 + +Resolving symbolic link %x +シンボリックリンク %x を解決中 + +Comparing content of files %x +ファイル %x の内容を比較中 + +Generating file list... +ファイル一覧を作成中... + +Starting comparison +比較処理を開始中 + +Calculating sync directions... +同期方向を計算しています... + +Out of memory. +メモリが足りません. + +Item exists on left side only +左側のみに存在する項目 + +Item exists on right side only +右側のみに存在する項目 + +Left side is newer +左側がより新しい + +Right side is newer +右側がより新しい + +Items have different content +内容が異なる項目 + +Both sides are equal +両側とも等しい + +Conflict/item cannot be categorized +競合/分類化できない項目 + +Copy new item to left +新しい項目を左にコピー + +Copy new item to right +新しい項目を右にコピー + +Delete left item +左の項目を削除 + +Delete right item +右の項目を削除 + +Move file on left +ファイルを左に移動 + +Move file on right +ファイルを右に移動 + +Overwrite left item +左の項目に上書き + +Overwrite right item +右の項目に上書き + +Do nothing +何もしない + +Update attributes on left +左の属性を更新 + +Update attributes on right +右の属性を更新 + +Database file %x is incompatible. +データベース %x とは互換性がありません. + +Initial synchronization: +同期処理の初期化: + +Database file %x does not yet exist. +データベース %x は存在しません. + +Database file is corrupt: +破損しているデータベース: + +Cannot write file %x. +ファイル %x に書き込めません. + +Cannot read file %x. +ファイル %x を読み込めません. + +Database files do not share a common session. +データベースは一般セッションで共有できません. + +Searching for folder %x... +フォルダ %x を検索中... + +Cannot read file attributes of %x. +%x のファイル属性を読み込めません. + +Cannot get process information. +プロセス情報を取得できません. + +Waiting while directory is locked (%x)... +待機時間中ディレクトリはロックされます(%x)... + + +1 sec +%x sec + + +%x 秒. + + +Creating file %x +ファイル %x を作成中 + +Items processed: +処理された要素: + +Items remaining: +残りの要素: + +Total time: +合計時間: + + +1 byte +%x bytes + + +%x バイト + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +ファイル %x の構文解析エラー, 行 %y, 列 %z. + +Cannot set directory lock for %x. +%x のディレクトリロックができません. + +Scanning: +スキャン: + + +1 thread +%x threads + + +%x スレッド + + +Encoding extended time information: %x +拡張された時間情報のエンコーディング: %x + +/sec +/秒 + +%x items/sec +%x 項目/秒 + +Configuration file %x loaded partially only. +構成ファイル %x は部分的のみ読み込まれます. + +Show in Explorer +エクスプローラで表示 + +Open with default application +既定アプリケーションで開く + +Browse directory +ディレクトリを参照 + +Cannot access the Volume Shadow Copy Service. +ボリュームシャドウコピーサービスにアクセス出来ません. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +FreeFileSync 64-bit 版を使用してこのシステムにシャドウコピーを作成してください. + +Cannot load file %x. +ファイル %x を読み込めません. + +Cannot determine volume name for %x. +%x のボリューム名が決定されていません + +Volume name %x is not part of file path %y. +ボリューム名 %x は、ファイルパス %y の一部ではありません. + +Stop requested: Waiting for current operation to finish... +停止の要求: 現在の操作が完了するのを待機しています... + +Unable to create timestamp for versioning: +バージョン付けのタイムスタンプを作成出来ません: + +Cannot read the following XML elements: +次の XML要素を読み込めません: + +&Open... +開く(&O)... + +Save &as... +別名保存(&A)... + +&Quit +終了(&Q) + +&Program +プログラム(&P) + +&View help +ヘルプを表示(&V) + +&About +情報(&A) + +&Help +ヘルプ(&H) + +Usage: +使用方法: + +1. Select folders to watch. +1. 監視するフォルダを選択 + +2. Enter a command line. +2. コマンドラインを入力 + +3. Press 'Start'. +3. 'スタート'をクリック + +To get started just import a .ffs_batch file. +一括ファイル(.ffs)からインポートして開始 + +Folders to watch: +監視するフォルダ: + +Add folder +フォルダを追加 + +Remove folder +フォルダ除去 + +Browse +参照 + +Select a folder +フォルダを選択 + +Idle time (in seconds): +待機時間(秒で指定): + +Idle time between last detected change and execution of command +最後にコマンドを実行してから、次に変更を検出するまでの待機時間 + +Command line: +コマンドライン: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +このコマンドのトリガ条件: +- ファイルまたはサブフォルダの変更 +- 新規フォルダの追加 (例. USB の挿入) + + +&Start +開始(&S) + +About +情報 + +Build: %x +ビルド: %x + +All files +すべてのファイル + +Automated Synchronization +自動同期 + +Directory monitoring active +ディレクトリの監視アクティブ + +Waiting until all directories are available... +全ディレクトリが利用可能になるまで待機中... + +Error +エラー + +&Restore +修復(&R) + +&Show error +エラー表示(&S) + +&Exit +終了(&E) + +Incorrect command line: +不正なコマンドライン: + +&Retry +再試行(&R) + +File content +ファイルの内容 + +File time and size +ファイル時刻とサイズ + +Two way +両方向 + +Mirror +ミラー + +Update +更新 + +Custom +カスタム + +Multiple... +複数処理... + +Moving file %x to %y +ファイル %x を %y に移動中 + +Moving folder %x to %y +フォルダ %x を %y に移動中 + +Moving symbolic link %x to %y +シンボリックリンク %x を %y に移動中 + +Removing old versions... +旧バージョンを除去中... + +Creating symbolic link %x +シンボリックリンク %x を作成中 + +Creating folder %x +フォルダ %x を作成中 + +Overwriting file %x +ファイル %x を上書き中 + +Overwriting symbolic link %x +シンボリックリンク %x を上書き中 + +Verifying file %x +ファイル %x の検証中 + +Updating attributes of %x +%x の属性を更新 + +Cannot find %x. +%x が見つかりません + +Target folder %x already existing. +対象フォルダ %x は既に存在します. + +Target folder input field must not be empty. +対象フォルダ入力欄が空白になっています. + +Please enter a target folder for versioning. +バージョン付けをする対象フォルダを入力 + +Source folder %x not found. +ソースフォルダ %x が見つかりません + +The following items have unresolved conflicts and will not be synchronized: +以下の項目は、未解決の競合が存在するため同期処理を実行できませんでした: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +以下のフォルダには大量の差異が存在します。同期フォルダの双方が正確にセットされているかどうかをもう一度慎重にご確認ください. + +Not enough free disk space available in: +利用可能なディスク空き容量が足りません: + +Required: +必須: + +Available: +利用可能: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +複数フォルダペアの一部であるフォルダが変更されています. 同期設定を確認してください. + +Synchronizing folder pair: +フォルダペアを同期処理: + +Generating database... +データベースを作成中... + +Creating a Volume Shadow Copy for %x... +ボリュームシャドウコピーを作成中 %x... + +Data verification error: %x and %y have different content. +データ検証エラー: %x と %y には異なる内容が含まれています. + +job name +ジョブ名 + +Synchronization stopped +同期処理を停止 + +Synchronization completed with errors +同期処理はエラーで終了しています + +Synchronization completed with warnings +同期処理は警告で終了しています + +Nothing to synchronize +同期対象がありません + +Synchronization completed successfully +同期処理はすべてが正常に完了しました + +Saving log file %x... +ログファイル %x を保存中... + +You can switch to FreeFileSync's main window to resolve this issue. +FreeFileSync のメインウィンドウを切り替えることでこの問題を解決 + +&Don't show this warning again +次回からこの警告を表示しない(&D) + +&Ignore +無視(&I) + +&Switch +切り替え(&S) + +Switching to FreeFileSync's main window +FreeFileSync メインウィンドウの切り替え + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + + + +&Ignore subsequent errors +以降のエラーは無視する(&I) + +Retrying operation... + + +Serious Error +重大なエラー + +Check for Program Updates +プログラムのアップデートを確認 + +A new version of FreeFileSync is available: +FreeFileSync の新しいバージョンが利用できます: + +Download now? +ダウンロードしますか? + +&Download +ダウンロード(&D) + +FreeFileSync is up to date. +FreeFileSync は最新です. + +Unable to connect to sourceforge.net. +Sourceforge.net に接続できません. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +現在の FreeFileSync バージョン番号を確認できませんでした、手動で確認しますか? + +&Check + + +Symlink +シンボリックリンク + +Folder +フォルダ + +Full path +フルパス + +Name +名前 + +Relative path +相対パス + +Base folder +基準フォルダ + +Size +サイズ + +Date +日付 + +Extension +拡張子 + +Category +カテゴリ + +Action +操作 + +Drag && drop +ドラッグ && ドロップ + +Close progress dialog +進捗ダイアログを閉じる + +Standby +スタンバイ + +Log off +ログオフ + +Shut down +シャットダウン + +Hibernate +休止状態 + +Alternate comparison settings +代替比較設定 + +Alternate synchronization settings +代替同期設定 + +Local filter +ローカル フィルター + +Active +アクティブ + +None +なし + +Remove alternate settings +代替設定を除去 + +Clear filter settings +フィルター設定をクリア + +Copy +コピー + +Paste +貼り付け + +Alternate Comparison Settings +代替比較設定 + +Alternate Synchronization Settings +代替同期設定 + +Local Filter +ローカル フィルター + +&New +新規(&N) + +&Save +保存(&S) + +Save as &batch job... +一括ジョブで保存(&B)... + +1. &Compare +1. 比較(&C) + +2. &Synchronize +2. 同期処理(&S) + +&Global settings +全般的な設定(&G) + +&Language +使用言語(&L) + +&Find... +検索(&F)... + +&Export file list... +ファイル一覧をエクスポート(&E)... + +&Tools +ツール(&T) + +&Check now +今すぐ確認(&C) + +Check &automatically once a week +週に一回、自動的に確認する(&A) + +&Check for new version +新バージョンの確認(&C) + +Compare +比較 + +Cancel +キャンセル + +Synchronize +同期処理 + +Add folder pair +フォルダのペアを追加 + +Remove folder pair +フォルダペアを除去 + +Swap sides +パネルを入れ替え + +Close search bar +検索バーを閉じる + +Find: +検索: + +Match case +文字種を区別 + +Save as batch job +一括ジョブで保存 + +Hide excluded items +除外対象項目を隠す + +Show filtered or temporarily excluded files +フィルター済、または一時除外ファイルを表示 + +Number of files and folders that will be created +作成されたファイル、およびフォルダの数 + +Number of files that will be overwritten +上書きされたファイル数 + +Number of files and folders that will be deleted +削除されたファイル、およびフォルダの数 + +Total bytes to copy +コピーする合計バイト + +Select a variant: +メソッドを選択: + +Identify equal files by comparing modification time and size. +更新時刻とサイズを比較して、同一ファイルを識別します + +Identify equal files by comparing the file content. +ファイルの内容を比較して、同一ファイルを識別します + +Symbolic links: +シンボリック リンク: + +More information +更に詳細な情報 + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +変更箇所を識別して両側に変更を反映します。データベースを使用することで、削除、移動および競合などが自動的に検出されます。 + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +同意処理後、右側フォルダと厳密に合致する左側フォルダのミラーバックアップを作成 + +Copy new and updated files to the right folder. +新しい、または更新されたファイルを右フォルダにコピー + +Configure your own synchronization rules. +あなたの設定した同期規則を使用します。 + +Detect moved files +移動済みのファイルを検出する + +Requires database files. Not supported by all file systems. +データベースファイルが必要です(すべてのシステムには未対応). + +Delete files: +ファイルの削除: + +Permanent +完全消去 + +Delete or overwrite files permanently +ファイルを上書き、または完全に削除 + +Recycle bin +ゴミ箱 + +Back up deleted and overwritten files in the recycle bin +ファイルを削除/上書きする時、ゴミ箱にバックアップをとる + +Versioning +バージョン付け + +Move files to a user-defined folder +ユーザ定義のフォルダにファイルを移動 + +Naming convention: +命名規則: + +Show examples +入力例を表示 + +Handle errors: +エラーの取り扱い: + +Ignore +無視 + +Hide all error and warning messages +すべてのエラーと警告メッセージを非表示 + +Pop-up +ポップアップ + +Show pop-up on errors or warnings +エラーと警告をポップアップで表示 + +On completion: +完了時の動作: + +Start synchronization now? +今すぐ同期を開始しますか? + +Variant: +メソッド: + +Statistics +統計 + +&Don't show this dialog again +次回以降から表示しない(&D) + +Items found: +見つかった要素: + +Speed: +速度: + +Time remaining: +残り時間: + +Time elapsed: +経過時間 + +Synchronizing... +同期処理中... + +Minimize to notification area +通知領域に最小化 + +Close +閉じる + +&Pause +一時停止(&P) + +Stop +停止 + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +無人で同期を行う為のバッチファイルを作成 - このファイルか、タスクプランナーからスケジュールをダブルクリックすることで開始: %x + +Stop synchronization at first error +最初のエラーで同期処理を停止 + +Show progress dialog +進捗ダイアログを表示 + +Save log: +ログ保存: + +Limit: +制限: + +Limit maximum number of log files +ログファイルの最大数を制限 + +How can I schedule a batch job? +一括ジョブ スケジュールの作成方法 + +&Recycle bin +ゴミ箱(&R) + +Delete on both sides +両方を削除 + +Delete on both sides even if the file is selected on one side only +片側のペインのみ選択されている場合でも両方を削除する + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +同期処理から特定のファイルを除外するフィルター規則を選択します。対応するそれらフォルダ ペアからの相対ファイルパスを入力. + +Include: +含める: + +Exclude: +除外: + +Time span: +タイムスパン: + +File size: +ファイルサイズ: + +Minimum: +最小: + +Maximum: +最大: + +&Clear +クリア(&C) + +The following settings are used for all synchronization jobs. +以下の設定は、すべての同期ジョブで使用されます + +Fail-safe file copy +安全なファイルコピーを実施 + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +対象を上書きする前に一時ファイル (*.ffs_tmp) にコピーします. +これにより、エラーが発生した場合も一貫性ある状態が保証されます. + + +(recommended) +(推奨) + +Copy locked files +ロックされたファイルをコピーする + +Copy shared or locked files using the Volume Shadow Copy Service. +共有、ロックされたファイルをボリュームシャドウコピーサービスを使用してコピー + +(requires administrator rights) +(管理者権限が必要) + +Copy file access permissions +ファイルのアクセスパーミッションをコピーする + +Transfer file and folder permissions. +ファイルとフォルダのパーミッションを転送します + +Automatic retry on error: +エラー時の自動再試行: + +Retry count: +再試行回数: + +Delay (in seconds): +遅延 (秒で指定): + +Customize context menu: +コンテキストメニューのカスタマイズ: + +Description +説明 + +Restore hidden windows +非表示ウィンドウを復帰 + +&Default +デフォルト(&D) + +Source code written in C++ using: +ソースコードは C++ で書かれています + +If you like FreeFileSync +FreeFileSync を気に入ってくれた方へ + +Donate with PayPal +PayPal から寄付 + +Feedback and suggestions are welcome +フィードバック、提案などはこちらから + +Homepage +ホームページ + +Email +E-メール + +Published under the GNU General Public License +GNU 一般共有使用許諾に基づき公開されています + +Many thanks for localization: +ローカライズのご協力に感謝します: + +Save as Batch Job +一括ジョブを保存 + +Delete Items +項目の削除 + +Global Settings +全般的な設定 + +Select Time Span +タイムスパンを選択 + +Folder Pairs +フォルダ ペア + +Find +検索 + +Overview +概要 + +Configuration +構成設定 + +Main Bar +メインバー + +Filter Files +ファイルフィルター + +Select View +表示を選択 + +Open... +開く... + +Save +保存 + +Compare both sides +両方を比較 + +Comparison settings +比較設定 + +Synchronization settings +同期処理設定 + +Start synchronization +同期の開始 + +Confirm +確認 + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + + + +&Execute +実行(&E) + + +1 directory +%x directories + + +%x ディレクトリ + + + +1 file +%x files + + +%x 個のファイル + + + +%y of 1 row in view +%y of %x rows in view + + +%y / %x 行(表示内) + + +Set direction: +方向の設定: + +multiple selection +複数選択 + +Include via filter: +フィルター経由で含める: + +Exclude via filter: +フィルターを経由して除外 + +Exclude temporarily +一時的に除外 + +Include temporarily +一時的に含める + +Delete +削除 + +Include all +すべて含める + +Exclude all +すべて除外 + +Show icons: +アイコン表示: + +Small + + +Medium + + +Large + + +Select time span... +タイムスパンを選択... + +Default view +デフォルト表示 + +Show "%x" +"%x" で表示 + +Last session +最後のセッション + +Folder Comparison and Synchronization +フォルダの比較と同期 + +Configuration saved +構成設定は保存されました + +FreeFileSync batch +FreeFileSync 一括 + +Do you want to save changes to %x? +本当に %x の変更を保存しますか? + +Never save &changes +変更の保存をしない(&C) + +Do&n't save +保存しない(&N) + +Filter +フィルター + +Show files that exist on left side only +左側のみに存在するファイルを表示 + +Show files that exist on right side only +右側のみに存在するファイルを表示 + +Show files that are newer on left +左側の新しいファイルを表示 + +Show files that are newer on right +右側の新しいファイルを表示 + +Show files that are equal +同じ内容のファイルを表示 + +Show files that are different +差異のあるファイルを表示 + +Show conflicts +不一致を表示 + +Show files that will be created on the left side +左側で作成されたファイルを表示 + +Show files that will be created on the right side +右側で作成されたファイルを表示 + +Show files that will be deleted on the left side +左側で削除されたファイルを表示 + +Show files that will be deleted on the right side +右側で削除されたファイルを表示 + +Show files that will be overwritten on left side +左側で上書きされたファイルを表示 + +Show files that will be overwritten on right side +右側で上書きされたファイルを表示 + +Show files that won't be copied +コピーされなかったファイルを表示 + +Set as default +デフォルトにセット + +All folders are in sync +すべてのフォルダを同期 + +Synchronization Settings +同期の設定 + +Comparison Settings +比較の設定 + +Cannot find %x +%x は見つかりません + +Comma-separated values +カンマ区切りの値 + +File list exported +ファイル一覧のエクスポートが完了 + +Searching for program updates... +アップデートを検索しています... + +Scanning... +スキャン中... + +Comparing content... +内容を比較中... + +Info +情報 + +Warning +警告 + +Paused +一時停止中 + +Initializing... +初期化中... + +Stopped +停止 + +Completed +完了しました! + +&Continue +続行(&C) + +Log +ログ + +Today +今日 + +This week +この週 + +This month +この月 + +This year +この年 + +Last x days +最後の x 日 + +Byte +バイト + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +本当に以下 %x 個の項目をゴミ箱に移動しますか? + + +Move +移動 + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +本当に以下の %x 個の項目を削除しますか + + +Exclude +除外 + +Direct +ダイレクト + +Follow +考慮する + +Copy NTFS permissions +NTFS パーミッションをコピーする + +Integrate external applications into context menu. The following macros are available: +外部のアプリケーションをコンテキストメニューに統合、以下のマクロが利用できます: + +- full file or folder name +- ファイル、フォルダの完全な名前 + +- folder part only +- フォルダ部分のみ + +- Other side's counterpart to %item_path% +- %item_path% の反対側の対象 + +- Other side's counterpart to %item_folder% +- %item_folder% の反対側の対象 + +Restore all hidden windows and warnings? +すべての非表示ウィンドウと警告を元に戻しますか? + +Leave as unresolved conflict +未解決の競合はそのまま残す + +Replace +置換 + +Move files and replace if existing +ファイルを移動、存在する場合は上書き + +Time stamp +タイムスタンプ + +Append a timestamp to each file name +各ファイル名にタイムスタンプを追加 + +File +ファイル + +YYYY-MM-DD hhmmss +YYYY-MM-DD hhmmss + +Files +ファイル + +Items +項目 + +Percentage +パーセント + +Cannot monitor directory %x. +ディレクトリ %x を監視できません. + +Conversion error: +変換エラー: + +Cannot delete file %x. +ファイル %x を削除出来ません. + +The file is locked by another process: +次のファイルは別のプロセスで使用中です: + +Cannot move file %x to %y. +ファイル %x を %y に移動できません. + +Cannot delete directory %x. +ディレクトリ %x を削除できません. + +Cannot write file attributes of %x. +%x のファイル属性を書き込めません. + +Cannot write modification time of %x. +%x の更新時刻を書き込めませんでした. + +Cannot read security context of %x. +%x のセキュリティコンテキストを読み込めません. + +Cannot write security context of %x. +>%x のセキュリティコンテキストを書き込めません. + +Cannot read permissions of %x. +>%x のパーミッションを読み込めません. + +Cannot write permissions of %x. +%x のパーミッションを書き込めません. + +Cannot create directory %x. +ディレクトリ %x を作成出来ません. + +Cannot create symbolic link %x. +シンボリックリンク %x を作成出来ません + +Cannot find system function %x. +システム関数 %x がみつかりません. + +Cannot copy file %x to %y. +%x から %y にコピーできません. + +Type of item %x is not supported: +項目 %x には対応していません: + +Cannot resolve symbolic link %x. +シンボリックリンク %x を解決できません. + +Cannot open directory %x. +ディレクトリ %x を開けません. + +Cannot enumerate directory %x. +ディレクトリ %x を列挙できません + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +%x 分. + + + +1 hour +%x hours + + +%x 時間 + + + +1 day +%x days + + +%x 日 + + +Unable to register to receive system messages. +システム受信メッセージに登録できません + +Cannot set privilege %x. +%x の特権をセットできません. + +Unable to suspend system sleep mode. +システム スリープモードを中断できません + +Cannot change process I/O priorities. +プロセスの I/O 優先度を変更できません + +Unable to move %x to the recycle bin. +%x をゴミ箱に移動できません. + +Cannot determine final path for %x. +%x の最終的なパスを決定できません. + +Error Code %x: +エラーコード %x: + diff --git a/FreeFileSync/Build/Languages/korean.lng b/FreeFileSync/Build/Languages/korean.lng new file mode 100644 index 00000000..999dc5b1 --- /dev/null +++ b/FreeFileSync/Build/Languages/korean.lng @@ -0,0 +1,1504 @@ +
    + 한국어 + Simon Park + ko_KR + flag_south_korea.png + 1 + 0 +
    + +Both sides have changed since last synchronization. +마지막 동기화 작업 이후, 양측 모두 변경 되었습니다. + +Cannot determine sync-direction: +동기화 방향을 결정할 수 없습니다: + +No change since last synchronization. +마지막 동기화 이후 변경사항 없음. + +The database entry is not in sync considering current settings. +데이터베이스 항목이 현재 설정을 고려하여 동기화 되지 않았습니다. + +Setting default synchronization directions: Old files will be overwritten with newer files. +기본값 동기화 방향 설정: 이전 파일들은 보다 최신 파일들로 덮어 쓰여집니다. + +Checking recycle bin availability for folder %x... +폴더 %x을(를) 위한 휴지통 가용성 여부 확인 중... + +Moving file %x to the recycle bin +파일 %x을(를) 휴지통으로 이동 중 + +Moving folder %x to the recycle bin +폴더 %x을(를) 휴지통으로 이동 중 + +Moving symbolic link %x to the recycle bin +심볼릭 링크 %x을(를) 휴지통으로 이동 중 + +Deleting file %x +파일 %x 삭제 중 + +Deleting folder %x +폴더 %x 삭제 중 + +Deleting symbolic link %x +심볼릭 링크 %x 삭제 중 + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +다음 폴더들을 위해 휴지통을 사용할 수 없습니다. 대신 파일들을 영구 삭제합니다: + +An exception occurred +예외 발생 + +A directory path is expected after %x. +%x 이후 일반적으로 디렉토리 경로가 하나 나옵니다. + +Syntax error +구문 오류 + +Cannot open file %x. +파일 %x을(를) 열 수 없습니다. + +File %x does not contain a valid configuration. +파일 %x 의 구성이 유효하지 않습니다. + +Unequal number of left and right directories specified. +지정된 좌측 및 우측 디렉터리 개수가 같지 않음. + +The config file must not contain settings at directory pair level when directories are set via command line. +명령줄을 통해 설정된 디렉토리의 경우, 설정 파일은 디렉토리 페어 상의 설정을 포함할 수 없습니다. + +Directories cannot be set for more than one configuration file. +디렉토리는 하나 이상의 설정 파일을 위해 설정될 수 없습니다. + +Command line +명령줄(커맨드라인) + +Syntax: +구문(Syntax): + +config files +설정 파일 + +directory +디렉토리 + +Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. +FreeFileSync .ffs_gui 또는 .ffs_batch 설정파일 개수 + +Any number of alternative directories for at most one config file. +최대 한개 설정파일에 대한 대체 디렉토리 개수 + +A folder input field is empty. +폴더 입력 필드 하나가 비어 있습니다. + +The corresponding folder will be considered as empty. +해당 폴더를 비어 있는 상태로 간주합니다. + +Cannot find the following folders: +다음 폴더를 찾을 수 없습니다: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +각 폴더를 비어있는 상태로 간주할 경우 이 오류는 무시될 수 있습니다. 폴더들은 동기화가 진행되는 동안 자동 생성됩니다. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +다음 폴더들에는 종속 경로가 있습니다. 동기화 규칙을 설정 시 주의하시기 바랍니다: + +File %x has an invalid date. +파일 %x 의 날짜가 유효하지 않습니다. + +Date: +날짜: + +Files %x have the same date but a different size. +파일 %x 의 날짜는 같으나, 크기가 다릅니다. + +Size: +크기: + +Items differ in attributes only +항목들이 속성에서만 차이가 있습니다. + +Resolving symbolic link %x +심볼릭 링크 %x 해결 + +Comparing content of files %x +파일 %x 내용 별 비교 중 + +Generating file list... +파일 리스트 생성 중... + +Starting comparison +비교 시작 + +Calculating sync directions... +동기화 방향을 계산 중... + +Out of memory. +메모리 부족. + +Item exists on left side only +항목이 좌측에만 존재합니다. + +Item exists on right side only +항목이 우측에만 존재합니다. + +Left side is newer +좌측이 최신입니다. + +Right side is newer +우측이 최신입니다. + +Items have different content +항목 내용이 서로 다릅니다. + +Both sides are equal +양측이 같음 + +Conflict/item cannot be categorized +충돌 / 항목을 분류할 수 없음 + +Copy new item to left +좌측에 새 항목 복사 + +Copy new item to right +우측에 새 항목 복사 + +Delete left item +좌측 항목 삭제 + +Delete right item +우측 항목 삭제 + +Move file on left +좌측 파일 이동 + +Move file on right +우측 파일 이동 + +Overwrite left item +좌측 항목 덮어쓰기 + +Overwrite right item +우측 항목 덮어쓰기 + +Do nothing +아무 것도 하지 않음 + +Update attributes on left +좌측 속성 업데이트 + +Update attributes on right +우측 속성 업데이트 + +Database file %x is incompatible. +데이터베이스 파일 %x 은(는) 호환 불가능합니다. + +Initial synchronization: +초기 동기화: + +Database file %x does not yet exist. +데이터베이스 파일 %x 은(는) 아직 존재하지 않습니다. + +Database file is corrupt: +데이터베이스 파일 손상: + +Cannot write file %x. +파일 %x을(를) 쓸 수 없습니다. + +Cannot read file %x. +파일 %x을(를) 읽을 수 없습니다. + +Database files do not share a common session. +데이터베이스 파일이 일반/공동 세션을 공유하지 않습니다. + +Searching for folder %x... +폴더 %x 검색 중... + +Cannot read file attributes of %x. +%x의 파일 속성을 읽을 수 없습니다. + +Cannot get process information. +프로세스 정보를 얻을 수 없습니다. + +Waiting while directory is locked (%x)... +디렉토리 잠금 대기 중 (%x)... + + +1 sec +%x sec + + +%x초 + + +Creating file %x +파일 %x 생성 중 + +Items processed: +처리된 항목: + +Items remaining: +남은 항목: + +Total time: +전체 시간: + + +1 byte +%x bytes + + +%x 바이트 + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +분석 오류 - 파일: %x; 행: %y; 열: %z + +Cannot set directory lock for %x. +%x 에 대한 디렉토리 잠금을 설정할 수 없습니다. + +Scanning: +스캔 중: + + +1 thread +%x threads + + +%x 스레드 + + +Encoding extended time information: %x +인코딩 확장 시간 정보: %x + +/sec +/초 + +%x items/sec +%x 항목 + +Configuration file %x loaded partially only. +구성 파일 %x이(가) 부분적으로만 로드 되었음. + +Show in Explorer +탐색기에 표시 + +Open with default application +기본값 응용 프로그램으로 열기 + +Browse directory +디렉토리 찾아보기 + +Cannot access the Volume Shadow Copy Service. +Volume Shadow Copy Service에 접근할 수 없습니다. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +본 운영체제에서의 Shadow Copy 생성은 FreeFileSync 64-비트 버전을 사용하세요. + +Cannot load file %x. +파일 %x을(를) 로드할 수 없습니다. + +Cannot determine volume name for %x. +%x 에 대한 볼륨 이름을 결정할 수 없습니다. + +Volume name %x is not part of file path %y. +볼륨 이름 %x은(는) 파일 경로 %y의 일부가 아닙니다. + +Stop requested: Waiting for current operation to finish... +사용자에 의한 작업 중단: 현재 작업 종료 대기 중... + +Unable to create timestamp for versioning: +버저닝을 위한 타임 스탬프 생성 불가: + +Cannot read the following XML elements: +다음 XML 요소를 읽을 수 없습니다: + +&Open... +열기(&O) + +Save &as... +다른 이름으로 저장(&a) + +&Quit +종료(&Q) + +&Program +프로그램(&P) + +&View help +도움말 보기(&V) + +&About +상세 정보(&A) + +&Help +도움말(&H) + +Usage: +사용: + +1. Select folders to watch. +1. 열어 볼 폴더를 선택하세요. + +2. Enter a command line. +2. 명령줄을 입력하세요. + +3. Press 'Start'. +3. '시작'을 누르세요. + +To get started just import a .ffs_batch file. +시작하려면 .ffs_batch file (일괄 파일)을 가져오십시오. + +Folders to watch: +열어 볼 폴더: + +Add folder +폴더 추가 + +Remove folder +폴더 제거 + +Browse +찾아보기 + +Select a folder +폴더 선택 + +Idle time (in seconds): +유휴 시간 (초 단위) + +Idle time between last detected change and execution of command +마지막으로 감지된 변화와 명령 실행 간의 유휴 시간 + +Command line: +명령줄(커맨드라인): + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +명령은 다음과 같은 경우에 실행됩니다: +- 파일이나 하위 폴더 변경 시 +- 새 폴더가 생겼을 시 (예: USB 스틱 삽입) + + +&Start +시작(&S) + +About +상세 정보 + +Build: %x +빌드: %x + +All files +모든 파일 + +Automated Synchronization +자동 동기화 + +Directory monitoring active +디렉토리 모니터링 활성화 + +Waiting until all directories are available... +모든 디렉토리가 사용 가능할 때까지 대기 중... + +Error +오류 + +&Restore +복원(&R) + +&Show error +오류 표시(&S) + +&Exit +나가기(&E) + +Incorrect command line: +부정확한 명령줄: + +&Retry +다시 시도(&R) + +File content +파일 내용 + +File time and size +파일 시간 및 크기 + +Two way +양방/양면 (Two Way) + +Mirror +미러 + +Update +업데이트 + +Custom +개인 설정 + +Multiple... +다중처리 (멀티플) 작업... + +Moving file %x to %y +파일 %x을(를) %y(으)로 이동 중 + +Moving folder %x to %y +폴더 %x을(를) %y(으)로 이동 중 + +Moving symbolic link %x to %y +심볼릭 링크 %x을(를) %y(으)로 이동 중 + +Removing old versions... +구 버전 삭제 중... + +Creating symbolic link %x +심볼릭 링크 %x 생성 중 + +Creating folder %x +폴더 %x 생성 중 + +Overwriting file %x +파일 %x 덮어쓰는 중 + +Overwriting symbolic link %x +심볼릭 링크 %x 덮어쓰는 중 + +Verifying file %x +파일 %x 확인 중 + +Updating attributes of %x +%x 속성 업데이트 중 + +Cannot find %x. +%x을(를) 찾을 수 없습니다. + +Target folder %x already existing. +대상 폴더 %x이(가) 이미 존재함. + +Target folder input field must not be empty. +대상 폴더 입력 필드가 비어 있어서는 안 됩니다. + +Please enter a target folder for versioning. +버저닝을 위한 대상 폴더를 입력하세요. + +Source folder %x not found. +소스 폴더 %x을(를) 찾을 수 없음. + +The following items have unresolved conflicts and will not be synchronized: +아래의 항목들은 해결치 못 한 충돌로 인해 동기화할 수 없습니다: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +다음 폴더들은 차이가 상당합니다. 동기화를 위해 올바른 폴더들이 매치되었는지 확인해 보십시오. + +Not enough free disk space available in: +사용 가능한 디스크 여유 공간이 부족합니다: + +Required: +필요 공간(크기): + +Available: +여유 공간(크기): + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +다중 폴더 페어의 일부인 폴더가 변경됩니다. 동기화 설정을 재검토하세요. + +Synchronizing folder pair: +폴더 페어(짝) 동기화 진행 중 + +Generating database... +데이터베이스 생성 중... + +Creating a Volume Shadow Copy for %x... +%x을(를) 위한 Volume Shadow Copy 생성 중... + +Data verification error: %x and %y have different content. +데이터 확인 오류: %x 및 %y 의 내용이 서로 다릅니다. + +job name +작업 이름 + +Synchronization stopped +동기화 중단 + +Synchronization completed with errors +동기화가 완료되긴 했으나, 오류가 있습니다 + +Synchronization completed with warnings +경고 메세지와 함께 동기화 완료 + +Nothing to synchronize +동기화 할 항목이 없습니다 + +Synchronization completed successfully +동기화가 성공적으로 완료 됐습니다 + +Saving log file %x... +로그 파일 %x 저장 중... + +You can switch to FreeFileSync's main window to resolve this issue. +이 문제는 FreeFileSync 기본 창으로 전환해서 해결 가능합니다. + +&Don't show this warning again +이 경고를 다시 표시하지 않음(&D) + +&Ignore +무시(&I) + +&Switch +전환(&S) + +Switching to FreeFileSync's main window +FreeFileSync 기본 창으로 전환 중 + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + +%x초 이내에 자동 재시도... + + +&Ignore subsequent errors +이후 오류 무시(&I) + +Retrying operation... + + +Serious Error +심각한 오류 + +Check for Program Updates +프로그램 업데이트 확인 + +A new version of FreeFileSync is available: +새로운 버전의 FreeFileSync가 나왔습니다: + +Download now? +지금 다운로드 하시겠습니까? + +&Download +다운로드(&D) + +FreeFileSync is up to date. +FreeFileSync는 현재 최신버전 상태입니다. + +Unable to connect to sourceforge.net. +Sourceforge.net에 접속할 수 없습니다. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +현재 사용 중인 FreeFileSync 버전 번호를 온라인에서 찾을 수 없습니다. 수동으로 확인해 보시겠습니까? + +&Check + + +Symlink +심링크 + +Folder +폴더 + +Full path +전체 경로 + +Name +이름 + +Relative path +대상 경로 + +Base folder +기본 폴더 + +Size +크기 + +Date +날짜 + +Extension +확장자 + +Category +카테고리 + +Action +실행 + +Drag && drop +드래그 앤 드랍(&&) [마우스로 파일 끌어다 놓기] + +Close progress dialog +진행 표시 창 닫기 + +Standby +대기 + +Log off +로그오프 + +Shut down +종료 + +Hibernate +최대절전모드 + +Alternate comparison settings +비교 설정 변경 + +Alternate synchronization settings +동기화 설정 변경 + +Local filter +로컬 필터 + +Active +활성화 + +None +아무것도 안 함 + +Remove alternate settings +대체설정 제거 + +Clear filter settings +필터 설정 지우기 + +Copy +복사 + +Paste +붙여넣기 + +Alternate Comparison Settings +비교 설정 변경 + +Alternate Synchronization Settings +동기화 설정 변경 + +Local Filter +로컬 필터 + +&New +신규 작업(&N) + +&Save +저장(&S) + +Save as &batch job... +일괄 작업으로 저장(&b) + +1. &Compare +1. 비교(&C) + +2. &Synchronize +2. 동기화(&S) + +&Global settings +전체 설정(&G) + +&Language +언어 선택(&L) + +&Find... +찾기(&F) + +&Export file list... +파일 리스트 내보내기(&E) + +&Tools +도구(&T) + +&Check now +지금 확인(&C) + +Check &automatically once a week +자동으로 주당 1회 확인(&a) + +&Check for new version +신규 버전 확인(&C) + +Compare +비 교 + +Cancel +취소 + +Synchronize +동 기 화 + +Add folder pair +폴더 페어(짝) 추가 + +Remove folder pair +폴더 페어(짝) 제거 + +Swap sides +양측 위치 바꾸기 + +Close search bar +검색 창 닫기 + +Find: +찾기: + +Match case +대문자/소문자 구분 + +Save as batch job +일괄 작업으로 저장 + +Hide excluded items +제외 항목 숨기기 + +Show filtered or temporarily excluded files +필터링 또는 일시적으로 제외된 파일 보이기 + +Number of files and folders that will be created +생성될 파일 및 폴더 개수 + +Number of files that will be overwritten +덮어 씌어질 파일 개수 + +Number of files and folders that will be deleted +삭제될 파일 및 폴더 개수 + +Total bytes to copy +복사할 전체 바이트 크기 + +Select a variant: +베어리언트 선택: + +Identify equal files by comparing modification time and size. +수정 시간 및 크기를 비교하여 동일한 파일을 식별 + +Identify equal files by comparing the file content. +파일 내용을 비교하여 동일한 파일을 식별 + +Symbolic links: +심볼링 링크: + +More information +상세 정보 + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +양측 변경사항을 확인하고 전달. 삭제, 이동 및 충돌은 데이터베이스를 통해 자동으로 감지됩니다. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +좌측 폴더 백업 미러 생성: 동기화 이후 좌측 및 우측 폴더는 완전히 똑같이 매치 됩니다. + +Copy new and updated files to the right folder. +신규 또는 업데이트 된 파일을 우측 폴더로 복사 + +Configure your own synchronization rules. +개인 동기화 규칙 설정 + +Detect moved files +이동 파일 탐지 + +Requires database files. Not supported by all file systems. +데이터베이스 파일이 필요합니다. 모든 파일 시스템에서 지원되지는 않습니다. + +Delete files: +파일 삭제: + +Permanent +영구 + +Delete or overwrite files permanently +파일 영구 삭제 또는 덮어쓰기 + +Recycle bin + + +Back up deleted and overwritten files in the recycle bin +휴지통에 삭제되고 덮어 씌어진 파일 백업 + +Versioning +버저닝 + +Move files to a user-defined folder +사용자 정의 폴더로 파일 이동 + +Naming convention: +이름 지정: + +Show examples +예 보이기 + +Handle errors: +오류 발생시: + +Ignore +무시 + +Hide all error and warning messages +모든 오류/경고 메세지 숨기기 + +Pop-up +팝업 + +Show pop-up on errors or warnings +오류 또는 경고에 대한 팝업 보이기 + +On completion: +완료 시: + +Start synchronization now? +지금 동기화를 시작하시겠습니까? + +Variant: +베어리언트: + +Statistics +통 계 + +&Don't show this dialog again +이 대화 창을 다시 표시 안 함(&D) + +Items found: +발견된 항목: + +Speed: +속도: + +Time remaining: +남은 시간: + +Time elapsed: +경과 시간: + +Synchronizing... +동기화 작업 중... + +Minimize to notification area +알림 영역으로 최소화 + +Close +닫기 + +&Pause +일시정지(&P) + +Stop +중지 + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +직접 지켜보지 않는 자동 동기화의 경우, 배치 파일을 만듭니다. 시작하려면 파일을 더블 클릭하거나 작업 플래너에서 일정을 만듭니다: %x + +Stop synchronization at first error +첫 오류 발생 시 동기화 중지 + +Show progress dialog +진행 표시 창 보기 + +Save log: +로그 저장: + +Limit: +제한: + +Limit maximum number of log files +로그 파일의 최대 개수 제한 + +How can I schedule a batch job? +일괄 작업 예약 방법은? + +&Recycle bin +휴지통(&R) + +Delete on both sides +양측 모두 삭제 + +Delete on both sides even if the file is selected on one side only +어느 한쪽의 파일만 선택하더라도 양측 모두 삭제 + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +동기화에서 특정 파일을 제외시키는 필터 규칙을 선택합니다. 해당 폴더 페어(짝)에 대한 상대 파일 경로를 입력하세요. + +Include: +포함: + +Exclude: +제외: + +Time span: +시간 간격: + +File size: +파일 크기: + +Minimum: +최소: + +Maximum: +최대: + +&Clear +지우기(&C) + +The following settings are used for all synchronization jobs. +다음 설정은 모든 동기화 작업에 사용됩니다. + +Fail-safe file copy +실패 - 안전 파일 복사 + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +대상을 덮어쓰기 전에 임시파일(*.ffs_tmp)로 복사. +이는 심각한 오류 발생시에도 일관된 상태 유지를 보장합니다. + + +(recommended) +(권장) + +Copy locked files +락 걸린 파일 복사 + +Copy shared or locked files using the Volume Shadow Copy Service. +Volume Shadow Copy Service를 사용하여 공유 또는 잠긴 파일 복사 + +(requires administrator rights) +(관리자 권한 필요) + +Copy file access permissions +파일 접근 권한 복사 + +Transfer file and folder permissions. +파일 및 폴더 권한 전송 + +Automatic retry on error: +오류 발생 시 자동 재시도: + +Retry count: +재시도 횟수: + +Delay (in seconds): +지연 (초 단위): + +Customize context menu: +컨텍스트 메뉴 커스터마이즈 (사용자 정의): + +Description +설명 + +Restore hidden windows +숨겨진 창 복원 + +&Default +기본 설정/값(&D) + +Source code written in C++ using: +소스코드는 C++ 언어로 아래 툴을 사용하여 작성되었습니다: + +If you like FreeFileSync +FreeFileSync를 위한 기부 + +Donate with PayPal +PayPal로 기부하기 + +Feedback and suggestions are welcome +모든 의견 및 건의/제안을 환영합니다 + +Homepage +홈페이지 + +Email +이메일 + +Published under the GNU General Public License +GNU 일반 공용 라이센스에 의한 출시 + +Many thanks for localization: +현지화 작업에 깊은 감사 드립니다: + +Save as Batch Job +일괄 작업으로 저장 + +Delete Items +항목 삭제 + +Global Settings +전체 설정 + +Select Time Span +시간간격(타임스팬) 선택 + +Folder Pairs +폴더 페어(짝) + +Find +검색 + +Overview +개요 + +Configuration +구성 설정 + +Main Bar +메인 바 + +Filter Files +파일 필터 + +Select View +보기 선택 + +Open... +열기 + +Save +저장 + +Compare both sides +양측 비교 + +Comparison settings +비교 설정 + +Synchronization settings +동기화 설정 + +Start synchronization +동기화 시작 + +Confirm +확인 + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + +정말로 %x 항목을 위해 %y 명령을 실행하기를 원하십니까? + + +&Execute +실행(&E) + + +1 directory +%x directories + + +%x개 디렉토리 + + + +1 file +%x files + + +%x개 파일 + + + +%y of 1 row in view +%y of %x rows in view + + +보기에 나타난 %x 행의 %y + + +Set direction: +방향 설정: + +multiple selection +복수 선택 + +Include via filter: +필터를 통해 다음을 포함: + +Exclude via filter: +필터를 통해 다음을 제외: + +Exclude temporarily +임시 제외 + +Include temporarily +임시 포함 + +Delete +삭제 + +Include all +모두 포함 + +Exclude all +모두 제외 + +Show icons: +아이콘 표시: + +Small +작게 + +Medium +중간 + +Large +크게 + +Select time span... +시간간격(타임스팬) 선택... + +Default view +기본 보기 + +Show "%x" +"%x" 표시 + +Last session +마지막 세션 + +Folder Comparison and Synchronization +폴더 비교 및 동기화 + +Configuration saved +설정 저장 완료 + +FreeFileSync batch +FreeFileSync 일괄처리(배치) + +Do you want to save changes to %x? +%x의 변경사항을 저장하시겠습니까? + +Never save &changes +변경사항을 절대 저장하기 않기(&c) + +Do&n't save +저장하기 않기(&n) + +Filter +필터 + +Show files that exist on left side only +좌측에만 존재하는 파일 표시 + +Show files that exist on right side only +우측에만 존재하는 파일 표시 + +Show files that are newer on left +좌측이 보다 최신인 파일 표시 + +Show files that are newer on right +우측이 보다 최신인 파일 표시 + +Show files that are equal +내용이 같은 파일 표시 + +Show files that are different +내용이 다른 파일 표시 + +Show conflicts +충돌 표시 + +Show files that will be created on the left side +좌측에 생성될 파일 표시 + +Show files that will be created on the right side +우측에 생성될 파일 표시 + +Show files that will be deleted on the left side +좌측에서 삭제될 파일 표시 + +Show files that will be deleted on the right side +우측에서 삭제될 파일 표시 + +Show files that will be overwritten on left side +좌측에 덮어쓰여질 파일 표시 + +Show files that will be overwritten on right side +우측에 덮어쓰여질 파일 표시 + +Show files that won't be copied +복사되지 않을 파일 표시 + +Set as default +기본 값으로 설정 + +All folders are in sync +모든 폴더가 동기화 되었음 + +Synchronization Settings +동기화 설정 + +Comparison Settings +비교 설정 + +Cannot find %x +%x을(를) 찾을 수 없습니다. + +Comma-separated values +쉼표로 구분된 값 + +File list exported +파일 리스트 내보내기 완료 + +Searching for program updates... +프로그램 업데이트 검색 중... + +Scanning... +스캔 중... + +Comparing content... +내용 비교 중... + +Info +정보 + +Warning +경고 + +Paused +일시정지 중 + +Initializing... +초기화 작업 중... + +Stopped +중단 + +Completed +완료 + +&Continue +계속(&C) + +Log +로그 + +Today +오늘 + +This week +이번 주 + +This month +이번 달 + +This year +올해 + +Last x days +최근 x일 + +Byte +바이트 + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +다음 %x 항목을 정말로 휴지통으로 이동하기를 원하십니까? + + +Move +이동 + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +정말로 다음 %x 항목(들)을 삭제하시겠습니까? + + +Exclude +제외 + +Direct +다이렉트 + +Follow +팔로우 + +Copy NTFS permissions +NTFS 권한 복사 + +Integrate external applications into context menu. The following macros are available: +외부 응용 프로그램을 Context Menu에 통합. 다음 매크로가 사용 가능합니다: + +- full file or folder name +- 전체 파일 또는 폴더 이름 + +- folder part only +- 폴더 부분만 + +- Other side's counterpart to %item_path% +% 항목_경로 %의 반대편 대응부(카운터파트) + +- Other side's counterpart to %item_folder% +% 항목_폴더 %의 반대편 대응부(카운터파트) + +Restore all hidden windows and warnings? +모든 숨겨진 창 및 경고를 복원하시겠습니까? + +Leave as unresolved conflict +미해결 충돌로 놔두기 + +Replace +대체 + +Move files and replace if existing +파일 이동 및 기존 파일 존재 시에는 대체 + +Time stamp +타임 스탬프 + +Append a timestamp to each file name +각 파일 이름마다 타임스탬프 추가 + +File +파일 + +YYYY-MM-DD hhmmss +YYYY-MM-DD hhmmss + +Files +파일 + +Items +항목 + +Percentage +백분율(%) + +Cannot monitor directory %x. +디렉토리 %x을(를) 모니터링 할 수 없습니다. + +Conversion error: +변환 오류: + +Cannot delete file %x. +파일 %x을(를) 삭제할 수 없습니다. + +The file is locked by another process: +파일이 다른 프로세스에 의해 잠겨 있습니다: + +Cannot move file %x to %y. +파일 %x을(를) %y(으)로 이동할 수 없습니다. + +Cannot delete directory %x. +디렉토리 %x을(를) 삭제할 수 없습니다. + +Cannot write file attributes of %x. +%x의 파일 속성을 쓸 수 없습니다. + +Cannot write modification time of %x. +%x의 수정 시간을 쓸 수 없습니다. + +Cannot read security context of %x. +%x의 보안 컨텍스트를 읽을 수 없습니다. + +Cannot write security context of %x. +%x의 보안 컨텍스트를 쓸 수 없습니다. + +Cannot read permissions of %x. +%x의 권한을 읽을 수 없습니다. + +Cannot write permissions of %x. +%x의 권한을 쓸 수 없습니다. + +Cannot create directory %x. +디렉토리 %x을(를) 생성할 수 없습니다. + +Cannot create symbolic link %x. +심볼릭 링크 %x을(를) 생성할 수 없습니다. + +Cannot find system function %x. +시스템 함수 %x을(를) 찾을 수 없습니다. + +Cannot copy file %x to %y. +파일 %x을(를) %y(으)로 복사할 수 없습니다. + +Type of item %x is not supported: +항목 %x의 형식은 지원되지 않습니다: + +Cannot resolve symbolic link %x. +심볼릭 링크 %x을(를) 해결할 수 없습니다. + +Cannot open directory %x. +디렉토리 %x을(를) 열 수 없습니다. + +Cannot enumerate directory %x. +디렉토리 %x을(를) 열거할 수 없습니다. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +%x분 + + + +1 hour +%x hours + + +%x시간 + + + +1 day +%x days + + +%x일 + + +Unable to register to receive system messages. +시스템 메시지 수신을 위한 등록을 할 수 없습니다. + +Cannot set privilege %x. +권한 %x을(를) 설정할 수 없습니다. + +Unable to suspend system sleep mode. +시스템 절전모드 중지를 할 수 없습니다. + +Cannot change process I/O priorities. +프로세스 I/O 우선순위 변경을 할 수 없습니다. + +Unable to move %x to the recycle bin. +휴지통으로 %x을(를) 이동할 수 없습니다. + +Cannot determine final path for %x. +%x 에 대한 최종 경로를 결정할 수 없습니다. + +Error Code %x: +오류 코드 %x + diff --git a/FreeFileSync/Build/Languages/outdated/lithuanian.lng b/FreeFileSync/Build/Languages/outdated/lithuanian.lng new file mode 100644 index 00000000..f4c49c2a --- /dev/null +++ b/FreeFileSync/Build/Languages/outdated/lithuanian.lng @@ -0,0 +1,1520 @@ +
    + Lietuvių + xxx + lt_LT + flag_lithuania.png + 4 + n==1 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : n%10==0 || (n%100>10 && n%100<20) ? 2 : 3 +
    + +Both sides have changed since last synchronization. +Abi pusės buvo pakeistos nuo paskutinio sinchronizavimo. + +Cannot determine sync-direction: +Nepavyksta nustatyti sinchronizavimo krypties: + +No change since last synchronization. +Nėra pakitimo nuo pakutinio sinchronizavimo. + +The database entry is not in sync considering current settings. +Duomenų bazės įrašas nesinchronizuotas remiantis šiais nustatymais. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Nustatomos numatytos sinchronizavimo kryptys: Seni failai bus perrašyti naujesniais failais. + +Checking recycle bin availability for folder %x... +Tikrinamas šiukšliadėžės prieinamumas aplankui %x... + +Moving file %x to the recycle bin +%x failas perkeliamas į šiukšliadėžę + +Moving folder %x to the recycle bin +%x katalogas perkeliamas į šiukšliadėžę + +Moving symbolic link %x to the recycle bin +Simbolinė nuoroda %x perkeliama į šiukšliadėžę + +Deleting file %x +Trinamas failas %x + +Deleting folder %x +Trinamas aplankas %x + +Deleting symbolic link %x +Tinama simbolinė nuoroda %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +Šiuklšlių dėžė šiems katalogams yra nepasiekiama. Failai bus negrįžtamai ištrinti: + +An exception occurred +Atsirado išimtis + +A directory path is expected after %x. +Reikalingas katalogo kelias po %x + +Syntax error +Sintaksės klaida + +Cannot open file %x. +%x failo nepavyko atidaryti. + +File %x does not contain a valid configuration. +Failas %x neturi tinkamų nustatymų. + +Unequal number of left and right directories specified. +Nustatytas nevienodas skaičius katalogų kairėje ir dešinėje. + +The config file must not contain settings at directory pair level when directories are set via command line. +Config failas negali turėti katalogų poros lygio nustatymų kai katalogai nustatomi per komandinę eilutę. + +Directories cannot be set for more than one configuration file. +Katalogai negali būti nustatyti daugiau nei viename konfigūracijos faile. + +Command line +Komandinė eilutė + +Syntax: +Sintaksė: + +config files +config failai + +directory +katalogas + +Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. +Keletas FreeFileSync .ffs_gui ir/arba .ffs_batch configūracijos failų + +Any number of alternative directories for at most one config file. +Daugiausia vienam config failui, n alternatyvių katalogų + +A folder input field is empty. +Aplanko įvesties laukas yra tuščias. + +The corresponding folder will be considered as empty. +Atitinkamas aplankas bus laikomas tuščiu. + +Cannot find the following folders: +Nepavyksta rasti šių aplankų: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Jei žinote, kad kiekvienas katalogas yra tuščias, šią klaidą galite ignoruoti. Katalogai bus automatiškai sukurti sinchronizacijos metu. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +Šie katalogai turi tarpusavyje susijusių kelių. Būkite atsargūs nustatydami sinchronizacijos taisykles: + +File %x has an invalid date. +Failas %x turi netinkamą datą. + +Date: +Data: + +Files %x have the same date but a different size. +Failai %x turi tokią pačią datą bet skirtingą dydį. + +Size: +Dydis: + +Items differ in attributes only +Elementai skiriasi tik atributais + +Resolving symbolic link %x +Ieškoma simbolinės nuorodos %x + +Comparing content of files %x +Sulyginamas failų turinys %x + +Generating file list... +Generuojamas failų sąrašas... + +Starting comparison +Pradedamas palyginimas + +Calculating sync directions... +Apskaičiuojamos sinchrinizacijos kryptys... + +Out of memory. +Trūksta atminties. + +Item exists on left side only +Elementas egzistuoja tik kairėje pusėje + +Item exists on right side only +Elementas egzistuoja tik dešinėje pusėje + +Left side is newer +Kairė pusė yra naujesnė + +Right side is newer +Dešinė pusė yra naujesnė + +Items have different content +Elementai turi skirtingą turinį + +Both sides are equal +Abi pusės yra lygios + +Conflict/item cannot be categorized +Konfliktas/elementas negali būti kategorizuojamas + +Copy new item to left +Kopijuoti naują elementą į kairę + +Copy new item to right +Kopijuoti naują elementą į dešinę + +Delete left item +Ištrinti kairįjį elementą + +Delete right item +Ištrinti dešinįjį elementą + +Move file on left +Perkelti failą į kairę + +Move file on right +Perkelti failą į dešinę + +Overwrite left item +Perrašyti kairįjį elementą + +Overwrite right item +Perrašyti dešinįjį elementą + +Do nothing +Nieko nedaryti + +Update attributes on left +Atnaujinti atributus kairėje + +Update attributes on right +Atnaujinti atributus dešinėje + +Database file %x is incompatible. +Duomenų bazė %x yra netinkama. + +Initial synchronization: +Pirminis sinchronizavimas: + +Database file %x does not yet exist. +Duomenų bazės failo %x dar nėra. + +Database file is corrupt: +Duomenų bazės failas sugadintas + +Cannot write file %x. +Nepavyksta įrašyti failo %x. + +Cannot read file %x. +Nepavyksta nuskaityti failo %x. + +Database files do not share a common session. +Duomenų bazės failai nesidalina bendros sesijos. + +Searching for folder %x... +Ieškoma aplanko %x... + +Cannot read file attributes of %x. +Nepavyko perskaityti failo %x atributų + +Cannot get process information. +Nepavyksta gauti eigos informacijos. + +Waiting while directory is locked (%x)... +Laikiama kol katalogas bus užrakintas (%x)... + + +1 sec +%x sec + + +1 sek +%x sek +%x sek +%x sek + + +Creating file %x +Kuriamas failas %x + +Items processed: +Elementų apdorota: + +Items remaining: +Likę elementai: + +Total time: +Visas laikas: + + +1 byte +%x bytes + + + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Klaida trinant failą %x, eilė %y, stulpelis %z. + +Cannot set directory lock for %x. +Nepavyksta nustatyti katalogo užrakto %x. + +Scanning: +Skenuojama: + + +1 thread +%x threads + + + + +Encoding extended time information: %x +Koduojama išplėstinė laiko informacija: %x + +/sec +/sek. + +%x items/sec + + +Configuration file %x loaded partially only. +Nustatymų failas %x įkeltas tik dalinai. + +Show in Explorer +Rodyti naršyklėje + +Open with default application +Atverti su numatyta programa + +Browse directory +Naršyti katalogą + +Cannot access the Volume Shadow Copy Service. +Volume Shadow Copy paslauga nepasiekiama. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Prašome naudoti 64-bit FreeFileSync versiją, kad sukurti šėšėlines kopijas šioje sistemoje. + +Cannot load file %x. +Nepavyksta įkelti failo %x. + +Cannot determine volume name for %x. +Vietos vardo %x nustatyti nepavyko + +Volume name %x is not part of file path %y. +Vietos vardas %x nėra failo kelio %y dalis. + +Stop requested: Waiting for current operation to finish... + + +Unable to create timestamp for versioning: + + +Cannot read the following XML elements: +Nepavyksta perskaityti sekančių XML elementų: + +&Open... +&Atverti... + +Save &as... +Išsaugoti &kaip... + +&Quit +&Išeiti + +&Program +&Programa + +&View help + + +&About +&Apie + +&Help +&Pagalba + +Usage: +Naudojimas: + +1. Select folders to watch. +1. Pasirinkite stebimus aplankus. + +2. Enter a command line. +2. Įvesti komandinę eilutę. + +3. Press 'Start'. +3. Spauskite „Pradėti“'. + +To get started just import a .ffs_batch file. +Kad pradėti tiesiog importuokite .ffs_batch failą. + +Folders to watch: + + +Add folder +Pridėti aplanką + +Remove folder +Pašalinti aplanką + +Browse +Naršyti + +Select a folder +Pažymėti aplanką + +Idle time (in seconds): + + +Idle time between last detected change and execution of command +Neveiklus laikas tarp paskutinio aptikto pokyčio ir komandos įvykdymo + +Command line: + + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +Komanda inicijuojama jei: +- failai arba poaplankiai keičiasi +- atsiranda nauji aplankai (pvz.: įkišamas USB raktas) + + +&Start + + +About +Apie + +Build: %x +Versija: %x + +All files +Visi failai + +Automated Synchronization + + +Directory monitoring active +Katalogų stebėjimas yra aktyvus + +Waiting until all directories are available... +Laukiama kol katalogai bus prieinami... + +Error +Klaida + +&Restore +&Atstatyti + +&Show error + + +&Exit +&Išeiti + +Incorrect command line: + + +&Retry +&Bandyti vėl + +File content +Failo turinį + +File time and size +Failo laiką ir dydį + +Two way +Dvipusis + +Mirror +Veidrodis + +Update +Atnaujinti + +Custom +Savitas + +Multiple... +Keletas... + +Moving file %x to %y +Perkeliamas failas %x į %y + +Moving folder %x to %y +Perkeliamas aplankas %x į %y + +Moving symbolic link %x to %y +Perkeliama simbolinė nuoroda %x į %y + +Removing old versions... +Šalinamos senos versijos... + +Creating symbolic link %x +Kuriama simbolinė nuoroda %x + +Creating folder %x +Kuriamas aplankas %x + +Overwriting file %x +Perrašomas failas %x + +Overwriting symbolic link %x +Perrašoma simbolinė nuoroda %x + +Verifying file %x +Tikrinamas failas %x + +Updating attributes of %x +Atnaujinami atributai %x + +Cannot find %x. +Nepavyksta rasti %x. + +Target folder %x already existing. +Tikslo aplankas %x jau yra. + +Target folder input field must not be empty. +Tikslo aplanko įvesties laukas negali būti tuščias. + +Please enter a target folder for versioning. + + +Source folder %x not found. +Šaltinio aplankas %x nerastas. + +The following items have unresolved conflicts and will not be synchronized: +Šie elementai turi neišspręstų konfliktų ir nebus sinchronizuoti: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +Šie katalogai yra labai skirtingi. Įsitikinkite, kad sinchronizacijai pasirinkote teisingus katalogus. + +Not enough free disk space available in: +Nepakanka laisvos disko vietos: + +Required: +Reikia: + +Available: +Pasiekiama: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Aplankas, kuris yra dalis keletos aplankų porų, bus pakeistas. Prašome peržiūrėti sinchronizavimo nustatymus. + +Synchronizing folder pair: +Sinchrinizuojama aplankų pora: + +Generating database... +Generuojama duomenų bazė... + +Creating a Volume Shadow Copy for %x... +%x kuriamas Volume Shadow Copy... + +Data verification error: %x and %y have different content. +Duomenų patikros klaida: %x ir %y turinys yra skirtingas. + +job name +Užduoties pavadinimas + +Synchronization stopped + + +Synchronization completed with errors +Synchronizavimas baigtas su klaidomis + +Synchronization completed with warnings +Synchronizavimas baigtas su perspėjimais + +Nothing to synchronize +Nėra ko sinchronizuoti + +Synchronization completed successfully +Synchronizavimas sėkmingai baigtas + +Saving log file %x... +Saugmas žurnalo failas %x... + +You can switch to FreeFileSync's main window to resolve this issue. + + +&Don't show this warning again + + +&Ignore +&Ignoruoti + +&Switch +&Perjungti + +Switching to FreeFileSync's main window + + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + + + +&Ignore subsequent errors + + +Retrying operation... + + +Serious Error + + +Check for Program Updates + + +A new version of FreeFileSync is available: +Yra nauja FreeFileSync versija: + +Download now? +Atsiųsti dabar? + +&Download + + +FreeFileSync is up to date. +FreeFileSync yra naujausia. + +Unable to connect to sourceforge.net. +Nepavyksta prisijungti prie sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Dabartinės FreeFileSync versijos numeris internete nerastas. Ar norėtumėte patikrinti rankiniu būdu? + +&Check + + +Symlink +Simbolinė nuoroda + +Folder +Katalogas + +Full path +Pilnas kelias + +Name +Pavadinimas + +Relative path +Elemento kelias + +Base folder +Bazinis katalogas + +Size +Dydis + +Date +Data + +Extension +Plėtinys + +Category +Kategorija + +Action +Veiksmas + +Drag && drop +Vilkti ir numesti + +Close progress dialog +Uždaryti eigos langą + +Standby +Stabdyti į diską + +Log off +Atsijungti + +Shut down +Išjungti kompiuterį + +Hibernate +Stabdyti į operatyviąją atmintį + +Alternate comparison settings + + +Alternate synchronization settings + + +Local filter + + +Active + + +None + + +Remove alternate settings +Pašalinti alternatyvius nustatymus + +Clear filter settings +Išvalyti filtro nustatymus + +Copy +Kopijuoti + +Paste +Įklijuoti + +Alternate Comparison Settings + + +Alternate Synchronization Settings + + +Local Filter + + +&New +&Naujas + +&Save +&Išsaugoti + +Save as &batch job... +Išsaugoti kaip &paketinę užduotį... + +1. &Compare +1. &Sulyginti + +2. &Synchronize +2. &Sinchronizuoti + +&Global settings +&Bendri nustatymai + +&Language +&Kalba + +&Find... + + +&Export file list... +&Eksportuoti failų sąrašą... + +&Tools + + +&Check now +&Tikrinti dabar + +Check &automatically once a week +Tikrinti &automatiškai kartą per savaitę + +&Check for new version + + +Compare +Sulyginti + +Cancel +Atšaukti + +Synchronize +Sinchronizuoti + +Add folder pair +Pridėti aplankų porą + +Remove folder pair +Pašalinti aplankų porą + +Swap sides +Sukeisti puses + +Close search bar + + +Find: + + +Match case +Atitikti atveją + +Save as batch job +Išsaugoti kaip paketinį darbą + +Hide excluded items +Slėpti išskirtus elementus + +Show filtered or temporarily excluded files +Rodyti išfiltruotus ar laikinai išskirtus failus + +Number of files and folders that will be created +Failų ir aplankų, kurie bus sukurti, skaičius + +Number of files that will be overwritten +Skaičius failų, kurie bus perrašyti + +Number of files and folders that will be deleted +Failų ir aplankų, kurie bus ištrinti, skaičius + +Total bytes to copy +Viso baitų kopijuoti + +Select a variant: + + +Identify equal files by comparing modification time and size. + + +Identify equal files by comparing the file content. + + +Symbolic links: + + +More information + + +OK +Gerai + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Nustatyti ir skatinti pokyčius apbiejose pusėse. Trinimai, perkėlimai ir konfliktai yra aptinkami automatiškai naudojant duomenų bazę. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. + + +Copy new and updated files to the right folder. + + +Configure your own synchronization rules. +Nustatyti Jūsų pačių sinchronizavimo taisykles. + +Detect moved files +Rasti perkeltus failus + +Requires database files. Not supported by all file systems. +Reikalauja duomenų bazės failų. Visų failų sistemų nėra palaikoma. + +Delete files: + + +Permanent +Visiškai + +Delete or overwrite files permanently +Trinti ar perrašyti failus visam laikui + +Recycle bin +Šiukšlių dėžė + +Back up deleted and overwritten files in the recycle bin +Padaryti šiukšlių dėžėje esančių ištrintų ar perrašytų failų atsarginę kopiją + +Versioning +Versijavimas + +Move files to a user-defined folder +Perkelti failus į vartotojo nustatytą katalogą + +Naming convention: +Pavadinimų taisyklės: + +Show examples + + +Handle errors: + + +Ignore +Ignoruoti + +Hide all error and warning messages +Slėpti visus klaidų ir perspėjimų pranešimus + +Pop-up +Iššokti + +Show pop-up on errors or warnings +Rodyti pranešimą esant klaidoms ar perspėjimams + +On completion: + + +Start synchronization now? + + +Variant: + + +Statistics +Statistika + +&Don't show this dialog again + + +Items found: +Rasta elementų: + +Speed: +Greitis: + +Time remaining: +Likęs laikas: + +Time elapsed: +Praėjęs laikas: + +Synchronizing... +Sinchronizuojama... + +Minimize to notification area +Nuleisti į apačią dešinėje + +Close +Uždaryti + +&Pause +&Pauzė + +Stop + + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Sukurti paleidimo failą sinchronizacijai be priežiūros. Norint jį paleisti reikia paspausti and failo du kartus pele arba nustatyti su užduočių planuotoju: %x + +Stop synchronization at first error + + +Show progress dialog +Rodyti eigos langą + +Save log: + + +Limit: + + +Limit maximum number of log files +Apriboti ataskaitų failų skaičių + +How can I schedule a batch job? + + +&Recycle bin + + +Delete on both sides +Ištrinti abiejose pusėse + +Delete on both sides even if the file is selected on one side only +Ištrinti abiejose pusėse net jei failas yra pažymėtas tik vienoje pusėje + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. + + +Include: + + +Exclude: + + +Time span: + + +File size: + + +Minimum: + + +Maximum: + + +&Clear +&Išvalyti + +The following settings are used for all synchronization jobs. + + +Fail-safe file copy +Apsauginė failo kopija + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + + +(recommended) + + +Copy locked files +Kopijuoti užrakintus failus + +Copy shared or locked files using the Volume Shadow Copy Service. + + +(requires administrator rights) + + +Copy file access permissions +Kopijuoti failo leidimus + +Transfer file and folder permissions. + + +Automatic retry on error: + + +Retry count: + + +Delay (in seconds): + + +Customize context menu: + + +Description +Apibūdinimas + +Restore hidden windows + + +&Default +&Numatyta + +Source code written in C++ using: +Šaltinio kodas parašytas su C++ naudojant: + +If you like FreeFileSync +Jei Jums patinka FreeFileSync + +Donate with PayPal +Paremkite per PayPal + +Feedback and suggestions are welcome +Nuomonė ir patarimai laukiami + +Homepage +Namų puslapis + +Email +El. paštas + +Published under the GNU General Public License +Platinama su GNU General Public licenzija + +Many thanks for localization: +Labai dėkojame už vertimą: + +Save as Batch Job + + +Delete Items + + +Global Settings + + +Select Time Span + + +Folder Pairs + + +Find +Rasti + +Overview +Apžvalga + +Configuration +Nustatymai + +Main Bar + + +Filter Files + + +Select View + + +Open... +Atverti... + +Save +Išsaugoti + +Compare both sides +Sulyginti abi puses + +Comparison settings +Sulyginimo nustatymai + +Synchronization settings +Sinchronizavimo nustatymai + +Start synchronization +Pradėti sinchronizavimą + +Confirm +Patvirtinti + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + + + +&Execute + + + +1 directory +%x directories + + +1 katalogas +%x katalogai +%x katalogų +%x katalogas + + + +1 file +%x files + + +1 failas +%x failai +%x failų +%x failas + + + +%y of 1 row in view +%y of %x rows in view + + + + +Set direction: +Nustatyti kryptį: + +multiple selection +keletos pažymėjimas + +Include via filter: +Įtraukti naudojant filtrą: + +Exclude via filter: +Neįtraukti per filtrą: + +Exclude temporarily +Neįtraukti laikinai + +Include temporarily +Įtraukti laikinai + +Delete +Trinti + +Include all +Įtraukti visus + +Exclude all +Neįtraukti visų + +Show icons: +Rodyti ženkliukus: + +Small +Maži + +Medium +Vidutiniai + +Large +Dideli + +Select time span... +Pasirinkti laiko tarpą... + +Default view +Numatytas rodmuo + +Show "%x" +Rodyti "%x" + +Last session +Paskutinė sesija + +Folder Comparison and Synchronization +Aplankų sulyginimas ir sinchronizavimas + +Configuration saved +Nustatymai išsaugoti + +FreeFileSync batch +FreeFileSync paketinė užduotis + +Do you want to save changes to %x? +Ar norite išsaugoti %x pakeitimus? + +Never save &changes + + +Do&n't save +&Nesaugoti + +Filter +Filtras + +Show files that exist on left side only +Rodyti failus, kurie egzistuoja tik kairėje pusėje + +Show files that exist on right side only +Rodyti failus, kurie egzistuoja tik dešinėje pusėje + +Show files that are newer on left +Rodyti failus, kurie yra naujesni kairėje + +Show files that are newer on right +Rodyti failus, kurie yra naujesni dešinėje + +Show files that are equal +Rodyti failus, kurie yra lygūs + +Show files that are different +Rodyti failus, kurie yra skirtingi + +Show conflicts +Rodyti konfliktus + +Show files that will be created on the left side +Rodyti failus, kurie bus sukurti kairėje pusėje + +Show files that will be created on the right side +Rodyti failus, kurie bus sukurti dešinėje pusėje + +Show files that will be deleted on the left side +Rodyti failus, kurie bus ištrinti kairėje pusėje + +Show files that will be deleted on the right side +Rodyti failus, kurie bus ištrinti dešinėje pusėje + +Show files that will be overwritten on left side +Rodyti failus, kurie bus perrašyti kairėje pusėje + +Show files that will be overwritten on right side +Rodyti failus, kurie bus perrašyti dešinėje pusėje + +Show files that won't be copied +Rodyti failus, kurie ne bus kopijuojami + +Set as default +Nustatyti kaip numatytą + +All folders are in sync +Visi aplankai susinchronizuoti + +Synchronization Settings + + +Comparison Settings + + +Cannot find %x +Nepavyksta rasti %x + +Comma-separated values +Kableliu atskirtos reikšmės + +File list exported +Failų sąrašas eksportuotas + +Searching for program updates... +Ieškoma programos atnaujinimų... + +Scanning... +Skenuojama... + +Comparing content... +Sulyginamas turinys... + +Info +Informacija + +Warning +Perspėjimas + +Paused +Pristabdyta + +Initializing... +Pradedama... + +Stopped + + +Completed +Baigta + +&Continue + + +Log +Archyvas + +Today +Šiandien + +This week +Ši savaitė + +This month +Šis mėnuo + +This year +Šie metai + +Last x days +Paskutinės x dienos + +Byte +Baitai + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Ar tikrai norite perkelti poziciją į šiukšklių dėžę? +Ar tikrai norite perkelti šias %x pozicijas į šiukšklių dėžę? +Ar tikrai norite perkelti šias %x pozicijų į šiukšklių dėžę? +Ar tikrai norite perkelti poziciją %x į šiukšklių dėžę? + + +Move + + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Ar tikrai norite ištrinti šį elementą? +Ar tikrai norite ištrinti šiuos %x elementus? +Ar tikrai norite ištrinti šiuos %x elementų? +Ar tikrai norite ištrinti šį %x elementą? + + +Exclude +Neįtraukti + +Direct +Tiesiogiai + +Follow +Sekti + +Copy NTFS permissions +Kopijuoti NTFS leidimus + +Integrate external applications into context menu. The following macros are available: +Integruoti išorines programas į kontekstinį meniu. Sekantys makro galimi: + +- full file or folder name +- pilnas failo ar aplanko pavadinimas + +- folder part only +- tik aplanko dalis + +- Other side's counterpart to %item_path% +- Kitos pusės atitikmuo %item_path% + +- Other side's counterpart to %item_folder% +- Kitos pusės atitikmuo %item_folder% + +Restore all hidden windows and warnings? + + +Leave as unresolved conflict +Palikti kaip neišpręstą konfliktą + +Replace +Pakeisti + +Move files and replace if existing +Perkelti failus ir pakeisti jei egzistuoja + +Time stamp +Laiko žymė + +Append a timestamp to each file name +Pridėti laiko žymę kiekvieno failo pavadinime + +File +Failas + +YYYY-MM-DD hhmmss +YYYY-MM-DD hhmmss + +Files +Failai + +Items +Pozicijos + +Percentage +Procentai + +Cannot monitor directory %x. +Nepavyko stebėti katalogo %x. + +Conversion error: +Konvertavimo klaida: + +Cannot delete file %x. +Nepavyksta ištrinti failo %x. + +The file is locked by another process: +Failas yra užrakintas kito procceso: + +Cannot move file %x to %y. +Nepavyksta perkelti failo %x į %y. + +Cannot delete directory %x. +Nepavyksta ištrinti katalogo %x. + +Cannot write file attributes of %x. +Nepavyksta įrašyti atributų failui %x. + +Cannot write modification time of %x. +Nepavyksta šrašyti pakeitimo datos %x. + +Cannot read security context of %x. +Nepavyksta perskaityti %x saugumo konteksto. + +Cannot write security context of %x. +Nepavyksta įrašyti %x saugumo konteksto. + +Cannot read permissions of %x. +Nepavyksta perskaityti %x leidimų. + +Cannot write permissions of %x. +Nepavyksta įrašyti leidimų %x. + +Cannot create directory %x. +Nepavyksta sukurti katalogo %x. + +Cannot create symbolic link %x. +Nepavyko sukurti simbolinės nuorodos %x + +Cannot find system function %x. +Nepavyksta rasti sistemos funkcijos %x. + +Cannot copy file %x to %y. +Nepavyksta nukopijuoti failo %x į %y. + +Type of item %x is not supported: +Element tipas %x nepalaikomas: + +Cannot resolve symbolic link %x. +Nepavyko rasti simbolinės nuorodos %x reikšmės + +Cannot open directory %x. +Nepavyksta atverti direktorijos %x. + +Cannot enumerate directory %x. +Nepavyksta sunumeruoti direktorijos %x. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +1 min +%x min +%x min +%x min + + + +1 hour +%x hours + + +1 valanda +%x valandos +%x valandų +%x valanda + + + +1 day +%x days + + +1 diena +%x dienos +%x dienų +%x diena + + +Unable to register to receive system messages. + + +Cannot set privilege %x. +Nepavyksta nustatyti privilegijos %x. + +Unable to suspend system sleep mode. + + +Cannot change process I/O priorities. +Nepavyksta pakeisti proceso I/O prioritetų. + +Unable to move %x to the recycle bin. +%x į šiukšlinę perkelti nepavyko + +Cannot determine final path for %x. +Galutinio %x kelio rasti nepavyko + +Error Code %x: +Klaidos kodas %x: + diff --git a/FreeFileSync/Build/Languages/outdated/norwegian.lng b/FreeFileSync/Build/Languages/outdated/norwegian.lng new file mode 100644 index 00000000..86192770 --- /dev/null +++ b/FreeFileSync/Build/Languages/outdated/norwegian.lng @@ -0,0 +1,1500 @@ +
    + Norsk + xxx + nb_NO + flag_norway.png + 2 + n == 1 ? 0 : 1 +
    + +Both sides have changed since last synchronization. +Begge sider er endret siden siste synkronisering. + +Cannot determine sync-direction: +Kan ikke bestemme synkroniseringsretning: + +No change since last synchronization. +Ingen endringer siden siste synkronisering. + +The database entry is not in sync considering current settings. + + +Setting default synchronization directions: Old files will be overwritten with newer files. +Stiller inn standard synkroniseringsretning: Gamle filer blir overskrevet med nyere filer. + +Checking recycle bin availability for folder %x... +Sjekker søppelbøtte for tilgjengelig mappe %x... + +Moving file %x to the recycle bin + + +Moving folder %x to the recycle bin + + +Moving symbolic link %x to the recycle bin + + +Deleting file %x +Sletter fil %x + +Deleting folder %x +Sletter mappe %x + +Deleting symbolic link %x +Sletter symbolsk lenke %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: + + +An exception occurred +Et avvik har oppstått + +A directory path is expected after %x. + + +Syntax error + + +Cannot open file %x. + + +File %x does not contain a valid configuration. +Filen %x inneholder en ugyldig innstilling. + +Unequal number of left and right directories specified. + + +The config file must not contain settings at directory pair level when directories are set via command line. + + +Directories cannot be set for more than one configuration file. + + +Command line +Kommandolinje + +Syntax: + + +config files + + +directory + + +Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. + + +Any number of alternative directories for at most one config file. + + +A folder input field is empty. +Et mappefelt er tomt. + +The corresponding folder will be considered as empty. + + +Cannot find the following folders: +Kan ikke finne følgende mapper: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. + + +The following folders have dependent paths. Be careful when setting up synchronization rules: + + +File %x has an invalid date. +Filen %x har en ugyldig dato. + +Date: +Dato: + +Files %x have the same date but a different size. +Filer %x har den samme datoen, men forskjellig størrelse. + +Size: +Størrelse: + +Items differ in attributes only +Elementer har kun forskjellige attributter + +Resolving symbolic link %x + + +Comparing content of files %x +Sammenligner innhold til filer %x + +Generating file list... +Lager filliste... + +Starting comparison + + +Calculating sync directions... + + +Out of memory. +For lite minne. + +Item exists on left side only +Element eksisterer kun på venstre side + +Item exists on right side only +Element eksisterer kun på høyre side + +Left side is newer +Venstre side er nyere + +Right side is newer +Høyre side er nyere + +Items have different content +Elementer har forskjellig innhold + +Both sides are equal +Begge sider er like + +Conflict/item cannot be categorized +Konflikt/element kan ikke kategoriseres + +Copy new item to left +Kopier nytt element til venstre + +Copy new item to right +Kopier nytt element til høyre + +Delete left item +Slett venstre element + +Delete right item +Slett høyre element + +Move file on left +Flytt venstre fil + +Move file on right +Flytt høyre fil + +Overwrite left item +Skriv over venstre element + +Overwrite right item +Skriv over høyre element + +Do nothing +Ikke gjør noe + +Update attributes on left +Oppdater attributter til venstre + +Update attributes on right +Oppdater attributter til høyre + +Database file %x is incompatible. +Databasefil %x er inkompatibel. + +Initial synchronization: +Førstegangs synkronisering: + +Database file %x does not yet exist. +Databasefil %x finnes ikke ennå. + +Database file is corrupt: +Databasefilen er korrupt + +Cannot write file %x. +Kan ikke skrive fil %x. + +Cannot read file %x. +Kan ikke lese fil %x. + +Database files do not share a common session. +Databasefiler deler ikke en felles synkroniseringsøkt. + +Searching for folder %x... +Søker etter mappen %x... + +Cannot read file attributes of %x. +Kan ikke lese filattributter fra %x. + +Cannot get process information. +Kan ikke hente prosessinformasjon. + +Waiting while directory is locked (%x)... +Venter mens mappe er låst (%x)... + + +1 sec +%x sec + + +1 sekund +%x sekunder + + +Creating file %x +Oppretter fil %x + +Items processed: +Elementer behandlet: + +Items remaining: +Gjenværende elementer: + +Total time: +Total tid: + + +1 byte +%x bytes + + + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Feil ved analysering av fil %x, rekke %y, kolonne %z. + +Cannot set directory lock for %x. + + +Scanning: +Skanner: + + +1 thread +%x threads + + + + +Encoding extended time information: %x +Koder utvided tidsinformasjon: %x + +/sec +/sekund + +%x items/sec + + +Configuration file %x loaded partially only. +Innstillingsfilen %x bare delvis lastet. + +Show in Explorer +Vis i utforsker + +Open with default application +Åpne med standardprogram + +Browse directory +Utforsk mappe + +Cannot access the Volume Shadow Copy Service. + + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Bruk FreeFileSync 64-bit-versjon til å opprette skyggekopier på dette systemet. + +Cannot load file %x. +Kan ikke laste filen %x. + +Cannot determine volume name for %x. + + +Volume name %x is not part of file path %y. + + +Stop requested: Waiting for current operation to finish... + + +Unable to create timestamp for versioning: + + +Cannot read the following XML elements: +Kan ikke lese følgende XML-elementer: + +&Open... +&Åpne... + +Save &as... +Lagre &som... + +&Quit +&Avslutt + +&Program +&Program + +&View help + + +&About +&Om programmet + +&Help +&Hjelp + +Usage: +Bruk: + +1. Select folders to watch. +1. Velg mapper å overvåke. + +2. Enter a command line. +2. Skriv en kommandolinje. + +3. Press 'Start'. +3. Trykk 'Start'. + +To get started just import a .ffs_batch file. +Importer en .ffs_batch-fil for å komme i gang + +Folders to watch: + + +Add folder +Legg til mappe + +Remove folder +Fjern mappe + +Browse +Bla gjennom + +Select a folder +Velg en mappe + +Idle time (in seconds): + + +Idle time between last detected change and execution of command +Inaktiv tid mellom forrige endring og utførelse av kommando + +Command line: + + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +Kommandoen utløses hvis: +- filer eller mapper endres +- nye mapper legges til (f.eks. en USB-pinne kobles til) + + +&Start + + +About +Om + +Build: %x +Build: %x + +All files +Alle filer + +Automated Synchronization + + +Directory monitoring active + + +Waiting until all directories are available... + + +Error +Feil + +&Restore +&Gjenopprett + +&Show error + + +&Exit +&Avslutt + +Incorrect command line: + + +&Retry +&Prøv igjen + +File content +Filinnhold + +File time and size +Filtid og størrelse + +Two way +Toveis + +Mirror +Speile + +Update +Oppdatere + +Custom +Brukerdefinert + +Multiple... +Flere... + +Moving file %x to %y +Flytter fil %x til %y + +Moving folder %x to %y +Flytter mappe %x til %y + +Moving symbolic link %x to %y +Flytter symbolsk lenke %x til %y + +Removing old versions... +Fjerner gamle versjoner... + +Creating symbolic link %x +Oppretter symbolsk lenke %x + +Creating folder %x +Oppretter mappe %x + +Overwriting file %x +Overskriver fil %x + +Overwriting symbolic link %x +Overskriver symbolsk lenke %x + +Verifying file %x +Verifiserer fil %x + +Updating attributes of %x +Oppdaterer attributter til %x + +Cannot find %x. +Kan ikke finne %x. + +Target folder %x already existing. +Målmappe %x eksisterer allerede. + +Target folder input field must not be empty. +Feltet for målmappe kan ikke være tomt. + +Please enter a target folder for versioning. + + +Source folder %x not found. +Kildemappe %x finnes ikke. + +The following items have unresolved conflicts and will not be synchronized: +De følgende elementene har uoppklarte konflikter og vil ikke bli synkroniserte: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. + + +Not enough free disk space available in: +Ikke nok ledig diskplass tilgjengelig på: + +Required: +Nødvendig: + +Available: +Tilgjengelig: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +En mappe vil endres som er en del av flere mappepar. Se over innstillingene for synkronisering. + +Synchronizing folder pair: +Synkroniserer mappepar: + +Generating database... +Oppretter database... + +Creating a Volume Shadow Copy for %x... + + +Data verification error: %x and %y have different content. + + +job name + + +Synchronization stopped + + +Synchronization completed with errors +Synkronisering fullført med feil + +Synchronization completed with warnings +Synkronisering fullført med advarsler + +Nothing to synchronize +Ikke noe å synkronisere + +Synchronization completed successfully +Synkdonisering fullført + +Saving log file %x... +Lagrer loggfil %x... + +You can switch to FreeFileSync's main window to resolve this issue. + + +&Don't show this warning again + + +&Ignore +&Ignorer + +&Switch +&Skift + +Switching to FreeFileSync's main window + + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + + + +&Ignore subsequent errors + + +Retrying operation... + + +Serious Error + + +Check for Program Updates + + +A new version of FreeFileSync is available: +En ny versjon av FreeFileSync er tilgjengelig + +Download now? +Laste ned nå? + +&Download + + +FreeFileSync is up to date. +FreeFileSync er oppdatert. + +Unable to connect to sourceforge.net. +Kan ikke koble til sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? + + +&Check + + +Symlink +Symbolsk lenke + +Folder +Mappe + +Full path +Full bane + +Name +Navn + +Relative path +Relativ bane + +Base folder +Grunnmappe + +Size +Størrelse + +Date +Dato + +Extension +Filendelse + +Category +Kategori + +Action +Handling + +Drag && drop +Dra && slipp + +Close progress dialog +Lukk status vindu + +Standby +Standby + +Log off +Logg av + +Shut down +Slå av maskinen + +Hibernate +Sett i dvalemodus + +Alternate comparison settings + + +Alternate synchronization settings + + +Local filter + + +Active + + +None + + +Remove alternate settings +Fjern alternative innstillinger + +Clear filter settings +Fjern filterinnstillinger + +Copy +Kopier + +Paste + + +Alternate Comparison Settings + + +Alternate Synchronization Settings + + +Local Filter + + +&New +&Ny + +&Save +&Lagre + +Save as &batch job... +Lagre som &sammensatt jobb... + +1. &Compare +1. Sammen&lign + +2. &Synchronize +2. &Synkroniser + +&Global settings +&Felles innstillinger + +&Language +&Språk + +&Find... + + +&Export file list... +&Eksporter filliste... + +&Tools + + +&Check now + + +Check &automatically once a week + + +&Check for new version + + +Compare +Sammenlign + +Cancel +Avbryt + +Synchronize +Synkroniser + +Add folder pair +Legg til mappepar + +Remove folder pair +Fjern mappepar + +Swap sides +Bytt sider + +Close search bar + + +Find: + + +Match case +Skill mellom store og små bokstaver + +Save as batch job +Lagre som sammensatt oppgave + +Hide excluded items +Skjul ekskluderte elementer + +Show filtered or temporarily excluded files +Vis filtrerte eller midlertidig ekskluderte filer + +Number of files and folders that will be created +Antall filer og mapper som vil opprettes + +Number of files that will be overwritten +Antall filer som blir overskrevet + +Number of files and folders that will be deleted +Antall filer og mapper som vil slettes + +Total bytes to copy +Mengde bytes å kopiere + +Select a variant: + + +Identify equal files by comparing modification time and size. + + +Identify equal files by comparing the file content. + + +Symbolic links: + + +More information + + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. + + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. + + +Copy new and updated files to the right folder. + + +Configure your own synchronization rules. +Still inn dine egne synkroniseringsregler. + +Detect moved files + + +Requires database files. Not supported by all file systems. + + +Delete files: + + +Permanent +Permanent + +Delete or overwrite files permanently +Slett eller overskriv filer permanent + +Recycle bin + + +Back up deleted and overwritten files in the recycle bin + + +Versioning +Versjonshåndtering + +Move files to a user-defined folder + + +Naming convention: +Navnekonvensjon: + +Show examples + + +Handle errors: + + +Ignore +Ignorer + +Hide all error and warning messages +Skjul feilmeldinger og advarsler + +Pop-up +Pop-up + +Show pop-up on errors or warnings +Vis pop-upvindu ved feil eller advarsler + +On completion: + + +Start synchronization now? + + +Variant: + + +Statistics +Statistikk + +&Don't show this dialog again + + +Items found: +Elementer funnet: + +Speed: +Hastighet: + +Time remaining: +Gjenstående tid: + +Time elapsed: +Tid gått: + +Synchronizing... +Synkroniserer... + +Minimize to notification area + + +Close +Lukk + +&Pause +&Pause + +Stop + + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x + + +Stop synchronization at first error + + +Show progress dialog +Vis statuslinje + +Save log: + + +Limit: + + +Limit maximum number of log files +Begrens maks antall loggfiler + +How can I schedule a batch job? + + +&Recycle bin + + +Delete on both sides +Slett på begge sider + +Delete on both sides even if the file is selected on one side only +Slett på begge sider selv om filen bare er valgt på en side + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. + + +Include: + + +Exclude: + + +Time span: + + +File size: + + +Minimum: + + +Maximum: + + +&Clear + + +The following settings are used for all synchronization jobs. + + +Fail-safe file copy +Trygg filkopi + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + + +(recommended) + + +Copy locked files +Kopier låste filer + +Copy shared or locked files using the Volume Shadow Copy Service. + + +(requires administrator rights) + + +Copy file access permissions +Kopier filadgangstillatelser + +Transfer file and folder permissions. + + +Automatic retry on error: + + +Retry count: + + +Delay (in seconds): + + +Customize context menu: + + +Description +Beskrivelse + +Restore hidden windows + + +&Default +&Standard + +Source code written in C++ using: +Kildekode skrevet i C++ med hjelp fra: + +If you like FreeFileSync +Hvis du liker FreeFileSync + +Donate with PayPal +Doner med PayPal + +Feedback and suggestions are welcome +Tilbakemelding og forslag er velkomne + +Homepage +Hjemmeside + +Email +E-post + +Published under the GNU General Public License +Utgitt under GNU General Public Licence + +Many thanks for localization: +Mange takk for lokalisering: + +Save as Batch Job + + +Delete Items + + +Global Settings + + +Select Time Span + + +Folder Pairs + + +Find +Søk + +Overview +Oversikt + +Configuration +Innstilling + +Main Bar + + +Filter Files + + +Select View + + +Open... +Åpne... + +Save +Lagre + +Compare both sides +Sammenlign begge sider + +Comparison settings +Innstillinger for sammenligning + +Synchronization settings +Innstillinger for synkronisering + +Start synchronization +Start synkronisering + +Confirm + + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + + + +&Execute + + + +1 directory +%x directories + + +1 mappe +%x mapper + + + +1 file +%x files + + +1 fil +%x filer + + + +%y of 1 row in view +%y of %x rows in view + + + + +Set direction: +Still inn retningen: + +multiple selection +flervalg + +Include via filter: + + +Exclude via filter: +Ekskluder via filter: + +Exclude temporarily +Ekskluder midlertidig + +Include temporarily +Inkluder midlertidig + +Delete +Slett + +Include all +Inkluder alle + +Exclude all +Ekskluder alle + +Show icons: +Vis ikoner: + +Small +Små + +Medium +Middels + +Large +Store + +Select time span... +Velg tidsområde... + +Default view +Standardvisning + +Show "%x" +Vis "%x" + +Last session +Siste økt + +Folder Comparison and Synchronization +Mappesammenligning og synkronisering + +Configuration saved +Innstilling lagret + +FreeFileSync batch +FreeFileSync batch + +Do you want to save changes to %x? +Vil du lagre endringene til %x? + +Never save &changes + + +Do&n't save +&Ikke lagre + +Filter +Filter + +Show files that exist on left side only +Vis filer som bare finnes på venstre side + +Show files that exist on right side only +Vis filer som bare finnes på høyre side + +Show files that are newer on left +Vis filer som er nyere til venstre + +Show files that are newer on right +Vis filer som er nyere til høyre + +Show files that are equal +Vis filer som er like + +Show files that are different +Vis filer som er forskjellige + +Show conflicts +Vis konflikter + +Show files that will be created on the left side +Vis filer som blir opprettet på venstre side + +Show files that will be created on the right side +Vis filer som blir opprettet på høyre side + +Show files that will be deleted on the left side +Vis filer som blir slettet på venstre side + +Show files that will be deleted on the right side +Vis filer som blir slettet på høyre side + +Show files that will be overwritten on left side +Vis filer som blir overskrevet på venstre side + +Show files that will be overwritten on right side +Vis filer som blir overskrevet på høyre side + +Show files that won't be copied +Vis filer som ikke blir kopiert + +Set as default +Set som standard + +All folders are in sync +Alle mapper er synkroniserte + +Synchronization Settings + + +Comparison Settings + + +Cannot find %x +Kan ikke finne %x + +Comma-separated values + + +File list exported +Filliste eksportert + +Searching for program updates... + + +Scanning... +Skanner... + +Comparing content... +Sammenligner innhold... + +Info +Info + +Warning +Advarsel + +Paused +Pauset + +Initializing... +Initialiserer... + +Stopped + + +Completed +Fullført + +&Continue + + +Log + + +Today +Idag + +This week +Denne uke + +This month +Denne måned + +This year +Dette år + +Last x days +Siste x dager + +Byte +Byte + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + + + +Move + + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + + + +Exclude +Ekskluder + +Direct +Direkte + +Follow +Følg + +Copy NTFS permissions +Kopier NTFS-tillatelser + +Integrate external applications into context menu. The following macros are available: +Integrer eksterne programmer i høyreklikkmeny. Følgende makroer er tilgjengelige: + +- full file or folder name +- full fil- eller mappenavn + +- folder part only +- kun mapper + +- Other side's counterpart to %item_path% +- Den andre sidens motpart til %item_path% + +- Other side's counterpart to %item_folder% +- Den andre sidens motpart til %item_folder% + +Restore all hidden windows and warnings? + + +Leave as unresolved conflict +Etterlat som uløste konflikter + +Replace +Erstatt + +Move files and replace if existing +Flytt filer og erstatt hvis eksisterer + +Time stamp + + +Append a timestamp to each file name +Legg til et tidsstempel til hvert filnavn + +File +Fil + +YYYY-MM-DD hhmmss +ÅÅÅÅ-MM-DD ttmmss + +Files +Filer + +Items + + +Percentage +Prosent + +Cannot monitor directory %x. +Kan ikke overvåke mappen %x. + +Conversion error: +Konverteringsfeil: + +Cannot delete file %x. +Kan ikke slette filen %x. + +The file is locked by another process: +Filen er låst av en annen prosess: + +Cannot move file %x to %y. +Kan ikke flytte filen %x til %y. + +Cannot delete directory %x. +Kan ikke slette mappen %x. + +Cannot write file attributes of %x. +Kan ikke skrive filattributter til %x. + +Cannot write modification time of %x. +Kan ikke skrive endringstid til %x. + +Cannot read security context of %x. +Kan ikke lese sikkerhetskontekst til %x. + +Cannot write security context of %x. +Kan ikke skrive sikkerhetskontekst til %x. + +Cannot read permissions of %x. +Kan ikke lese tillatelser til %x. + +Cannot write permissions of %x. +Kan ikke skrive tillatelser til %x. + +Cannot create directory %x. +Kan ikke opprette mappen %x. + +Cannot create symbolic link %x. + + +Cannot find system function %x. +Kan ikke finne systemfunksjonen %x. + +Cannot copy file %x to %y. +Kan ikke kopiere filen %x til %y. + +Type of item %x is not supported: +Type element %x er ikke støttet: + +Cannot resolve symbolic link %x. +Kan ikke følge symbolsk lenke %x. + +Cannot open directory %x. +Kan ikke åpne mappe %x. + +Cannot enumerate directory %x. +Kan ikke gjennomgå mappe %x. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +1 min +%x min + + + +1 hour +%x hours + + +1 time +%x timer + + + +1 day +%x days + + +1 dag +%x dager + + +Unable to register to receive system messages. + + +Cannot set privilege %x. +Kan ikke sette privilegie %x. + +Unable to suspend system sleep mode. + + +Cannot change process I/O priorities. +Kan ikke endre I/O prioriet på prosess + +Unable to move %x to the recycle bin. + + +Cannot determine final path for %x. + + +Error Code %x: +Feil kode %x: + diff --git a/FreeFileSync/Build/Languages/outdated/turkish.lng b/FreeFileSync/Build/Languages/outdated/turkish.lng new file mode 100644 index 00000000..99823c18 --- /dev/null +++ b/FreeFileSync/Build/Languages/outdated/turkish.lng @@ -0,0 +1,1510 @@ +
    + Türkçe + Kaya Zeren + tr_TR + flag_turkey.png + 2 + n == 1 ? 0 : 1 +
    + +Both sides have changed since last synchronization. +Son eşleştirmeden bu yana iki tarafın da içeriği değişmiş. + +Cannot determine sync-direction: +Eşleştirme yönü belirlenemedi: + +No change since last synchronization. +Son eşleştirmeden bu yana bir değişiklik olmamış. + +The database entry is not in sync considering current settings. +Geçerli kayıtlar ile veritabanı kaydı aynı değil. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Varsayılan eşleştirme yönleri ayarlanıyor: Yeni dosyalar eski dosyaların üzerine yazılacak. + +Checking recycle bin availability for folder %x... +%x klasörü için Geri Dönüşüm Kutusu kullanılabilir mi diye bakılıyor... + +Moving file %x to the recycle bin +%x dosyası Geri Dönüşüm Kutusuna atılıyor + +Moving folder %x to the recycle bin +%x klasörü Geri Dönüşüm Kutusuna atılıyor + +Moving symbolic link %x to the recycle bin +%x simgesel bağlantısı Geri Dönüşüm Kutusuna atılıyor + +Deleting file %x +%x dosyası siliniyor + +Deleting folder %x +%x klasörü siliniyor + +Deleting symbolic link %x +%x sembolik bağlantısı siliniyor + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +Şu klasörler için Geri Dönüşüm Kutusu kullanılamaz. Dosyalar kalıcı olarak silinecek: + +An exception occurred +Olağan dışı bir durumla karşılaşıldı + +A directory path is expected after %x. +%x ardından bir klasör yolu gelmelidir. + +Syntax error +Yazım hatası + +Cannot open file %x. +%x dosyası açılamıyor. + +File %x does not contain a valid configuration. +%x dosyası geçerli ayar bilgilerini içermiyor. + +Unequal number of left and right directories specified. +Sağdan ve soldan seçilen klasör sayısı aynı değil. + +The config file must not contain settings at directory pair level when directories are set via command line. +Klasörler komut satırından seçildiğinde, ayar dosyasında klasör çifti düzeyinde ayar bulunmamalıdır. + +Directories cannot be set for more than one configuration file. +Klasörler bir ayar dosyasından fazlasında kullanılamaz. + +Command line +Komut satırı + +Syntax: +Yazım: + +config files +ayar dosyaları + +directory +klasör + +Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. +FreeFileSync .ffs_gui ya da .ffs_batch ayar dosyalarının sayısı. + +Any number of alternative directories for at most one config file. +En çok bir ayar dosyası için alternatif klasörlerin sayısı. + +A folder input field is empty. +Bir klasör giriş alanı boş. + +The corresponding folder will be considered as empty. +Karşıdaki klasör boş olarak kabul edilecek. + +Cannot find the following folders: +Aşağıdaki klasörler bulunamadı: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Bu hatayı yok sayarak karşıdaki klasörleri boş kabul edebilirsiniz. Bu klasörler eşleştirme sırasında kendiliğinden oluşturulur. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +Şu klasörlerin bağlı yolları var. Eşleştirme kurallarını ayarlarken dikkatli olun: + +File %x has an invalid date. +%x dosyasının tarihi geçersiz. + +Date: +Tarih: + +Files %x have the same date but a different size. +%x dosyalarının tarihleri aynı fakat boyutları farklı. + +Size: +Boyut: + +Items differ in attributes only +Yalnız öznitelikleri farklı ögeler + +Resolving symbolic link %x +%x sembolik bağlantısı çözümleniyor + +Comparing content of files %x +%x dosyalarının içeriği karşılaştırılıyor + +Generating file list... +Dosya listesi oluşturuluyor... + +Starting comparison +Karşılaştırmaya başlanıyor + +Calculating sync directions... +Eşleştirme yönleri hesaplanıyor... + +Out of memory. +Bellek yetersiz. + +Item exists on left side only +Yalnız solda bulunan ögeler + +Item exists on right side only +Yalnız sağda bulunan ögeler + +Left side is newer +Soldaki daha yeni ögeler + +Right side is newer +Sağdaki daha yeni ögeler + +Items have different content +İçeriği farklı ögeler + +Both sides are equal +Öge iki taraftada aynı + +Conflict/item cannot be categorized +Uyuşmayan/sınıflanamayan ögeler + +Copy new item to left +Yeni öge sola kopyalansın + +Copy new item to right +Yeni öge sağa kopyalansın + +Delete left item +Soldaki öge silinsin + +Delete right item +Sağdaki öge silinsin + +Move file on left +Soldaki dosya taşınsın + +Move file on right +Sağdaki dosya taşınsın + +Overwrite left item +Soldaki ögenin üzerine yazılsın + +Overwrite right item +Sağdaki ögenin üzerine yazılsın + +Do nothing +Hiçbir işlem yapılmasın + +Update attributes on left +Soldaki öznitelikleri güncelle + +Update attributes on right +Sağdaki öznitelikler güncellensin + +Database file %x is incompatible. +%x veritabanı dosyası uyumsuz + +Initial synchronization: +Başlangıç eşleştirmesi: + +Database file %x does not yet exist. +%x veritabanı dosyası henüz yok. + +Database file is corrupt: +Veritabanı dosyası bozulmuş: + +Cannot write file %x. +%x dosyası yazılamadı. + +Cannot read file %x. +%x dosyası okunamadı. + +Database files do not share a common session. +Veritabanı dosyaları ortak bir oturumu paylaşamaz. + +Searching for folder %x... +%x klasörü aranıyor... + +Cannot read file attributes of %x. +%x dosyasının özellikleri okunamadı. + +Cannot get process information. +İşlem bilgisi alınamadı. + +Waiting while directory is locked (%x)... +Klasör kilitli olduğundan bekleniyor (%x)... + + +1 sec +%x sec + + +1 saniye +%x saniye + + +Creating file %x +%x dosyası ekleniyor + +Items processed: +İşlenen öge: + +Items remaining: +Kalan öge: + +Total time: +Toplam süre: + + +1 byte +%x bytes + + +1 bayt +%x bayt + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +%x dosyası işlenirken hata, satır %y, sütun %z. + +Cannot set directory lock for %x. +%x için klasör kilidi uygulanamıyor. + +Scanning: +Taranıyor: + + +1 thread +%x threads + + +1 iş parçacığı +%x iş parçacığı + + +Encoding extended time information: %x +Uzatılmış zaman bilgisi kodlanıyor: %x + +/sec +/saniye + +%x items/sec + + +Configuration file %x loaded partially only. +%x ayarlar dosyası kısmen yüklendi. + +Show in Explorer +Tarayıcıda Görüntüleyin + +Open with default application +Varsayılan uygulama ile açın + +Browse directory +Klasöre gözatın + +Cannot access the Volume Shadow Copy Service. +Birim Gölge Hizmetine erişilemiyor. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Bu sistem üzerinde gölge kopyalar oluşturmak için FreeFileSync 64-bit sürümünü kullanın. + +Cannot load file %x. +%x dosyası yüklenemedi. + +Cannot determine volume name for %x. +%x için birim adı belirlenemedi. + +Volume name %x is not part of file path %y. +%x birim adı %y dosya yolunun bir parçası değil. + +Stop requested: Waiting for current operation to finish... + + +Unable to create timestamp for versioning: + + +Cannot read the following XML elements: +Şu XML elemanları okunamadı: + +&Open... +&Açın... + +Save &as... +F&arklı kaydedin... + +&Quit +Çı&kın + +&Program +&Dosya + +&View help + + +&About +H&akkında + +&Help +&Yardım + +Usage: +Kullanım: + +1. Select folders to watch. +1. İzlenecek klasörleri seçin + +2. Enter a command line. +2. Bir komut satırı yazın. + +3. Press 'Start'. +3. 'Başlat'a Tıklayın. + +To get started just import a .ffs_batch file. +.ffs_batch dosyasını yükleyerek başlayabilirsiniz. + +Folders to watch: + + +Add folder +Klasör ekleyin + +Remove folder +Klasörü silin + +Browse +Gözatın + +Select a folder +Bir klasör seçin + +Idle time (in seconds): + + +Idle time between last detected change and execution of command +Son algılanan değişiklik ile komutun yürütülmesi arasında beklenecek süre + +Command line: + + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +Komut şu durumlarda yürütülür: +- dosya ya alt klasörler değiştiğinde +- yeni klasörler algılandığında (örneğin bir USB bellek takıldığında) + + +&Start + + +About +Hakkında + +Build: %x +Yapım: %x + +All files +Tüm dosyalar + +Automated Synchronization + + +Directory monitoring active +Klasör izleme kullanılıyor + +Waiting until all directories are available... +Tüm klasörlerin uygun olması bekleniyor... + +Error +Hata + +&Restore +Gö&rüntülensin + +&Show error +Hataya &bakın + +&Exit +Çı&kın + +Incorrect command line: + + +&Retry +&Yeniden deneyin + +File content +İçeriğe göre + +File time and size +Tarih ve saate göre + +Two way +Çift yönlü + +Mirror +Yansıtma + +Update +Güncelleme + +Custom +Özel + +Multiple... +Çoklu... + +Moving file %x to %y +%x dosyası %y içine taşınıyor + +Moving folder %x to %y +%x klasörü %y içine taşınıyor + +Moving symbolic link %x to %y +%x sembolik bağlantısı %y içine taşınıyor + +Removing old versions... +Eski sürümler siliniyor... + +Creating symbolic link %x +%x sembolik bağlantısı ekleniyor + +Creating folder %x +%x klasörü ekleniyor + +Overwriting file %x +%x dosyasının üzerine yazılıyor + +Overwriting symbolic link %x +%x sembolik bağlantısının üzerine yazılıyor + +Verifying file %x +%x dosyası doğrulanıyor + +Updating attributes of %x +%x öznitelikleri güncelleniyor + +Cannot find %x. +%x bulunamadı. + +Target folder %x already existing. +%x hedef klasörü zaten var. + +Target folder input field must not be empty. +Hedef klasör giriş alanı boş olmamalı. + +Please enter a target folder for versioning. + + +Source folder %x not found. +%x kaynak klasörü bulunamadı. + +The following items have unresolved conflicts and will not be synchronized: +Uyuşmazlığı çözülmemiş şu ögeler eşleştirilmeyecek: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +Şu klasörler arasında oldukça büyük farklar var. Eşleştirme için doğru klasörleri seçtiğinize emin olun. + +Not enough free disk space available in: +Şurada yeterli disk alanı yok : + +Required: +Zorunlu: + +Available: +Kullanılabilir: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Çoklu klasör çiftlerinin parçası olan bir klasör değiştirilecek. Lütfen eşleştirme ayarlarını gözden geçirin. + +Synchronizing folder pair: +Eşleştirilen klasör çifti: + +Generating database... +Veri tabanı oluşturuluyor... + +Creating a Volume Shadow Copy for %x... +%x için Birim Gölge Hizmeti oluşturuluyor... + +Data verification error: %x and %y have different content. +Veri doğrulama hatası: %x ve %y farklı içeriklere sahip. + +job name +iş adı + +Synchronization stopped + + +Synchronization completed with errors +Eşleştirme bazı hatalarla tamamlandı + +Synchronization completed with warnings +Eşleştirme işlemi bazı uyarılarla tamamlandı + +Nothing to synchronize +Eşleştirilecek bir şey yok + +Synchronization completed successfully +Eşleştirme tamamlandı + +Saving log file %x... +%x günlük dosyası kaydediliyor... + +You can switch to FreeFileSync's main window to resolve this issue. + + +&Don't show this warning again +Bu uyarı bir daha &görüntülenmesin + +&Ignore +&Yoksayın + +&Switch +&Değiştirin + +Switching to FreeFileSync's main window + + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + + + +&Ignore subsequent errors +Sonraki &hatalar yoksayılsın + +Retrying operation... + + +Serious Error + + +Check for Program Updates + + +A new version of FreeFileSync is available: +Yeni bir FreeFileSync sürümü yayınlanmış: + +Download now? +İndirilsin mi? + +&Download +İn&dirin + +FreeFileSync is up to date. +FreeFileSync güncel. + +Unable to connect to sourceforge.net. +sourceforge.net sitesine bağlanılamıyor. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Geçerli FreeFileSync sürüm numarası çevrimiçi olarak bulunamıyor. El ile denetlemek ister misiniz? + +&Check + + +Symlink +Smblkbağlantı + +Folder +Klasör + +Full path +Tam yol + +Name +Ad + +Relative path +Bağıl klasör yolu + +Base folder +Başlangıç klasörü + +Size +Boyut + +Date +Tarih + +Extension +Uzantı + +Category +Öge tipi + +Action +İşlem + +Drag && drop +Klasör ya da dosyaları sürükleyip buraya bırakabilirsiniz + +Close progress dialog +İşlem penceresi kapatılsın + +Standby +Uykuya dalınsın + +Log off +Oturum kapatılsın + +Shut down +Bilgisayar kapatılsın + +Hibernate +Hazırda bekletilsin + +Alternate comparison settings + + +Alternate synchronization settings + + +Local filter + + +Active + + +None + + +Remove alternate settings +Alternatif ayarları silin + +Clear filter settings +Süzgeç ayarlarını temizleyin + +Copy +Kopyalayın + +Paste +Yapıştırın + +Alternate Comparison Settings + + +Alternate Synchronization Settings + + +Local Filter + + +&New +Ye&ni + +&Save +&Kaydedin + +Save as &batch job... +&Toplu iş olarak kaydedin... + +1. &Compare +1. &Karşılaştırın + +2. &Synchronize +2. &Eşleştirin + +&Global settings +&Genel ayarlar + +&Language +Di&l + +&Find... + + +&Export file list... +Dosya list&esini verin... + +&Tools +A&raçlar + +&Check now +&Sürümü Denetleyin + +Check &automatically once a week +&Haftada bir kendiliğinden denetlensin + +&Check for new version + + +Compare +Karşılaştırın + +Cancel +İptal + +Synchronize +Eşleştirin + +Add folder pair +Klasör çifti ekleyin + +Remove folder pair +Klasör çiftini silin + +Swap sides +Sağ ve sol tarafları değiştirin + +Close search bar + + +Find: + + +Match case +Büyük/küçük harfe uyulsun + +Save as batch job +Toplu iş olarak kaydedin + +Hide excluded items +Katılmayan ögeler görüntülensin + +Show filtered or temporarily excluded files +Süzülmüş ya da geçici olarak katılmayan dosyalar görüntülenir + +Number of files and folders that will be created +Eklenecek dosya ve klasör sayısı + +Number of files that will be overwritten +Üzerine yazılacak dosya sayısı + +Number of files and folders that will be deleted +Silinecek dosya ve klasör sayısı + +Total bytes to copy +Toplam kopyalanacak bayt + +Select a variant: + + +Identify equal files by comparing modification time and size. + + +Identify equal files by comparing the file content. + + +Symbolic links: + + +More information + + +OK +Tamam + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +İki taraftaki değişiklikler belirlenir ve kopyalanır. Silme, taşıma ve çakışmalar, veritabanı kullanılarak kendiliğinden algılanır. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. + + +Copy new and updated files to the right folder. + + +Configure your own synchronization rules. +Eşleştirme kurallarını istediğiniz şekilde ayarlayabilirsiniz. + +Detect moved files +Taşınmış dosyalar algılansın + +Requires database files. Not supported by all file systems. +Veritabanı dosyalarına gerek duyar. Tüm dosya sistemleri tarafından desteklenmez. + +Delete files: + + +Permanent +Kalıcı olarak silinsin + +Delete or overwrite files permanently +Dosyalar kalıcı olarak silinir ya da üzerine yazılır + +Recycle bin +Geri Dönüşüm Kutusuna atılsın + +Back up deleted and overwritten files in the recycle bin +Geri Dönüşüm Kutusundaki silinmiş ya da üzerine yazılmış dosyalar yedeklensin + +Versioning +Eski sürüm olarak saklansın + +Move files to a user-defined folder +Dosyalar kullanıcı tarafından belirtilen bir klasöre taşınsın + +Naming convention: +Adlandırma kuralı: + +Show examples + + +Handle errors: + + +Ignore +Yoksayılsın + +Hide all error and warning messages +Tüm hata ve uyarı iletileri gizlenir + +Pop-up +Görüntülensin + +Show pop-up on errors or warnings +Hata ya da uyarılar açılır pencerede görüntülenir + +On completion: + + +Start synchronization now? + + +Variant: + + +Statistics +İstatistikler + +&Don't show this dialog again +Bu &pencere bir daha görüntülenmesin + +Items found: +Bulunan öge: + +Speed: +Hız: + +Time remaining: +Kalan süre: + +Time elapsed: +Geçen süre: + +Synchronizing... +Eşleştiriliyor... + +Minimize to notification area +Bildirim alanına küçültün + +Close +Kapatın + +&Pause +&Duraklatılsın + +Stop + + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Katılımsız eşleştirme için bir toplu iş dosyası oluşturun. İşlemi başlatmak için bu dosyaya çift tıklayın ya da görev zamanlayıcı ile programlayın: %x + +Stop synchronization at first error + + +Show progress dialog +İşlem penceresi görüntülensin + +Save log: + + +Limit: + + +Limit maximum number of log files +Tutulacak en fazla günlük dosyası sayısı + +How can I schedule a batch job? + + +&Recycle bin + + +Delete on both sides +Her iki taraftaki de silinsin + +Delete on both sides even if the file is selected on one side only +Dosya yalnız bir tarafta seçili olsa bile her iki taraftaki de silinir + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. + + +Include: + + +Exclude: + + +Time span: + + +File size: + + +Minimum: + + +Maximum: + + +&Clear +&Temizleyin + +The following settings are used for all synchronization jobs. + + +Fail-safe file copy +Dosyalar hatasız kopyalansın + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + + +(recommended) + + +Copy locked files +Kilitli dosyalar da kopyalansın + +Copy shared or locked files using the Volume Shadow Copy Service. + + +(requires administrator rights) + + +Copy file access permissions +Dosya erişim izinleri de kopyalansın + +Transfer file and folder permissions. + + +Automatic retry on error: + + +Retry count: + + +Delay (in seconds): + + +Customize context menu: + + +Description +Açıklama + +Restore hidden windows + + +&Default +&Varsayılan + +Source code written in C++ using: +Kaynak kodu C++ kullanılarak yazılmıştır: + +If you like FreeFileSync +FreeFileSync’i beğendiyseniz + +Donate with PayPal +PayPal üzerinden bağış yapın + +Feedback and suggestions are welcome +Öneri ve geri bildirimlerinizi bekleriz + +Homepage +Web sitesi + +Email +E-posta + +Published under the GNU General Public License +GNU Genel Kamu Lisansı koşulları altında yayınlanmıştır + +Many thanks for localization: +Çeviriler için çok teşekkürler: + +Save as Batch Job + + +Delete Items + + +Global Settings + + +Select Time Span + + +Folder Pairs + + +Find +Arayın + +Overview +Genel + +Configuration +İşlemler + +Main Bar + + +Filter Files + + +Select View + + +Open... +Açın... + +Save +Kaydedin + +Compare both sides +İki tarafı karşılaştırır + +Comparison settings +Karşılaştırma ayarları + +Synchronization settings +Eşleştirme ayarları + +Start synchronization +Eşleştirmeyi başlatın + +Confirm +Onaylayın + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + + + +&Execute +Çalış&tırın + + +1 directory +%x directories + + +1 klasör +%x klasör + + + +1 file +%x files + + +1 dosya +%x dosya + + + +%y of 1 row in view +%y of %x rows in view + + +%y / 1 satır görüntüleniyor +%y / %x satır görüntüleniyor + + +Set direction: +Yönü seçin: + +multiple selection +çoklu seçim + +Include via filter: +Şu süzgeçle katılsın: + +Exclude via filter: +Süzerek dışarda bırakılsın: + +Exclude temporarily +Geçici olarak katılmasın + +Include temporarily +Geçici olarak katılsın + +Delete +Silin + +Include all +Tümü katılsın + +Exclude all +Hiçbiri katılmasın + +Show icons: +Simgeler görüntülensin: + +Small +Küçük + +Medium +Orta + +Large +Büyük + +Select time span... +Zaman aralığını seçin... + +Default view +Varsayılan görünüm + +Show "%x" +"%x" panelini görüntülensin + +Last session +Önceki oturum + +Folder Comparison and Synchronization +Klasör Karşılaştırma ve Eşleştirme + +Configuration saved +Ayarlar kaydedildi + +FreeFileSync batch +FreeFileSync toplu işi + +Do you want to save changes to %x? +Değişiklikleri %x dosyasına kaydetmek istiyor musunuz? + +Never save &changes +Değişiklikler asla &kaydedilmesin + +Do&n't save +Kaydedilmesi&n + +Filter +Süzgeç + +Show files that exist on left side only +Yalnız sol tarafta bulunan dosyaları görüntüler ya da gizler + +Show files that exist on right side only +Yalnız sağ tarafta bulunan dosyaları görüntüler ya da gizler + +Show files that are newer on left +Solda daha yeni olan dosyaları görüntüler ya da gizler + +Show files that are newer on right +Sağda daha yeni olan dosyaları görüntüler ya da gizler + +Show files that are equal +Aynı olan dosyaları görüntüler ya da gizler + +Show files that are different +Farklı olan dosyaları görüntüler ya da gizler + +Show conflicts +Uyuşmazlıkları görüntüler ya da gizler + +Show files that will be created on the left side +Sol tarafa eklenecek dosyalar görüntülensin + +Show files that will be created on the right side +Sağ tarafa eklenecek dosyalar görüntülensin + +Show files that will be deleted on the left side +Sol tarafta silinecek dosyalar görüntülensin + +Show files that will be deleted on the right side +Sağ tarafta silinecek dosyalar görüntülensin + +Show files that will be overwritten on left side +Sol tarafta üzerine yazılacak dosyalar görüntülensin + +Show files that will be overwritten on right side +Sağ tarafta üzerine yazılacak dosyalar görüntülensin + +Show files that won't be copied +Kopyalanmayacak dosyalar görüntülensin + +Set as default +Varsayılan olarak belirleyin + +All folders are in sync +Tüm klasörler eşleştirildi + +Synchronization Settings + + +Comparison Settings + + +Cannot find %x +%x bulunamadı + +Comma-separated values +Virgül ile ayrılmış değerler + +File list exported +Dosya listesi verildi + +Searching for program updates... +Yazılım güncellemesine bakılıyor... + +Scanning... +Taranıyor... + +Comparing content... +İçerik karşılaştırılıyor... + +Info +Bilgi + +Warning +Uyarı + +Paused +Duraklatıldı + +Initializing... +Başlatılıyor... + +Stopped + + +Completed +Tamamlandı + +&Continue +&Devam + +Log +Günlük + +Today +Bugün + +This week +Bu hafta + +This month +Bu ay + +This year +Bu yıl + +Last x days +Son x günde + +Byte +Bayt + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Aşağıdaki ögeyi Geri Dönüşüm Kutusuna atmak istediğinize emin misiniz? +Aşağıdaki %x ögeyi Geri Dönüşüm Kutusuna atmak istediğinize emin misiniz? + + +Move + + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Aşağıdaki 1 ögeyi silmek istediğinize emin misiniz? +Aşağıdaki %x ögeyi silmek istediğinize emin misiniz? + + +Exclude +Katılmayacak ögeler + +Direct +Doğrudan katılsın + +Follow +Hedefleri katılsın + +Copy NTFS permissions +NTFS izinleri de kopyalansın + +Integrate external applications into context menu. The following macros are available: +Sağ tık menüsüne dış uygulamalar ekler. Şu etiketler kullanılabilir: + +- full file or folder name +- tam dosya ya da klasör adı + +- folder part only +- yalnız klasör bölümü + +- Other side's counterpart to %item_path% +%item_path% ögesinin diğer taraftaki karşılığı + +- Other side's counterpart to %item_folder% +%item_folder% ögesinin diğer taraftaki karşılığı + +Restore all hidden windows and warnings? + + +Leave as unresolved conflict +Uyuşmazlık çözülmeden bırakılsın + +Replace +Değiştirin + +Move files and replace if existing +Dosyalar taşınsın ve varsa üzerine yazılsın + +Time stamp +Zaman Damgası + +Append a timestamp to each file name +Dosya adlarına zaman damgası eklensin + +File +Dosya + +YYYY-MM-DD hhmmss +YYYY-AA-GG SSddss + +Files +Dosyalar + +Items +Ögeler + +Percentage +Yüzde + +Cannot monitor directory %x. +%x klasörü izlenemiyor. + +Conversion error: +Dönüştürme hatası: + +Cannot delete file %x. +%x dosyası silinemiyor. + +The file is locked by another process: +Dosya başka bir işlem tarafından kilitlenmiş: + +Cannot move file %x to %y. +%x dosyası %y olarak taşınamadı. + +Cannot delete directory %x. +%x klasörü silinemedi. + +Cannot write file attributes of %x. +%x dosya öznitelikleri yazılamadı. + +Cannot write modification time of %x. +%x son değişiklik zamanı yazılamadı. + +Cannot read security context of %x. +%x için güvenlik bağlamı okunamadı. + +Cannot write security context of %x. +%x için güvenlik bağlamı yazılamadı. + +Cannot read permissions of %x. +%x izinleri okunamadı. + +Cannot write permissions of %x. +%x izinleri yazılamadı. + +Cannot create directory %x. +%x klasörü eklenemedi. + +Cannot create symbolic link %x. +%x sembolik bağlantısı oluşturulamadı. + +Cannot find system function %x. +%x sistem işlevi bulunamadı. + +Cannot copy file %x to %y. +%x dosyası %y olarak kopyalanamadı. + +Type of item %x is not supported: +%x ögesi tipi desteklenmiyor: + +Cannot resolve symbolic link %x. +%x simge bağlantısı çözümlenemedi + +Cannot open directory %x. +%x klasörü açılamadı. + +Cannot enumerate directory %x. +%x klasörü sayılamadı. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +1 dakika +%x dakika + + + +1 hour +%x hours + + +1 saat +%x saat + + + +1 day +%x days + + +1 gün +%x gün + + +Unable to register to receive system messages. + + +Cannot set privilege %x. +%x izni verilemedi. + +Unable to suspend system sleep mode. + + +Cannot change process I/O priorities. +Giriş/Çıkış işlemi öncelikleri değiştirilemedi + +Unable to move %x to the recycle bin. +%x Geri Dönüşüm Kutusuna atılamıyor. + +Cannot determine final path for %x. +Son %x yolu belirlenemedi. + +Error Code %x: +Hata Kodu %x: + diff --git a/FreeFileSync/Build/Languages/polish.lng b/FreeFileSync/Build/Languages/polish.lng new file mode 100644 index 00000000..63fff3f2 --- /dev/null +++ b/FreeFileSync/Build/Languages/polish.lng @@ -0,0 +1,1524 @@ +
    + Polski + Wojciech Pietruszewski + pl_PL + flag_poland.png + 3 + n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2 +
    + +Both sides have changed since last synchronization. +Obie strony uległy zmianie od ostatniej synchronizacji. + +Cannot determine sync-direction: +Nie można określić kierunku synchronizacji: + +No change since last synchronization. +Brak zmian od ostatniej synchronizacji. + +The database entry is not in sync considering current settings. +Baza danych nie jest spójna z aktualnymi ustawieniami. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Ustawianie domyślnego kierunku synchronizacji: Stare pliki zostaną nadpisane nowszymi. + +Checking recycle bin availability for folder %x... +Sprawdzanie dostępności kosza dla katalogu %x... + +Moving file %x to the recycle bin +Przenoszenie pliku %x do kosza + +Moving folder %x to the recycle bin +Przenoszenie katalogu %x do kosza + +Moving symbolic link %x to the recycle bin +Przenoszenie dowiązania symbolicznego %x do kosza + +Deleting file %x +Usuwanie pliku %x + +Deleting folder %x +Usuwanie folderu %x + +Deleting symbolic link %x +Usuwanie dowiązania symbolicznego %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +Dla następujących katalogów kosz nie jest dostępny. Pliki zostaną usunięte bez możliwości ich przywrócenia: + +An exception occurred +Wystąpił wyjątek + +A directory path is expected after %x. +Za %x oczekiwana jest ścieżka katalogu. + +Syntax error +Błąd składni + +Cannot open file %x. +Nie można otworzyć pliku %x. + +File %x does not contain a valid configuration. +Plik %x nie zawiera prawidłowej konfiguracji + +Unequal number of left and right directories specified. +Wprowadzona liczba katalogów do synchronizacji jest nierówna. + +The config file must not contain settings at directory pair level when directories are set via command line. +Plik konfiguracyjny nie może zawierać informacji o synchronizowanych katalogach gdy nazwy ścieżek przekazywane są w linii poleceń. + +Directories cannot be set for more than one configuration file. +Dla więcej niż jednego pliku konfiguracyjnego, nie można ustawić wielu katalogów. + +Command line +Linia komend + +Syntax: +Składnia: + +config files +pliki konfiguracyjne + +directory +katalog + +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 directories for at most one config file. +Dowolna liczba katalogów w przypadku użycia jednego pliku konfiguracyjnego. + +A folder input field is empty. +Pole katalog źródłowy jest puste. + +The corresponding folder will be considered as empty. +Katalog będzie oznaczony jako pusty. + +Cannot find the following folders: +Nie można znaleźć następujących katalogów: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Możesz zignorować ten błąd aby uznając katalogi jako puste. Katalog zostanie utworzony automatycznie podczas synchronizacji. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +Podane katalogi mają zależne ścieżki. Bądź ostrożny podczas ustawiania reguł synchronizacji: + +File %x has an invalid date. +Plik %x ma nieprawidłową datę. + +Date: +Data: + +Files %x have the same date but a different size. +Pliki %x mają tą samą datę lecz różne rozmiary. + +Size: +Rozmiar: + +Items differ in attributes only +Elementy różnią się wyłącznie atrybutami + +Resolving symbolic link %x +Rozwiązywanie dowiązania symbolicznego %x + +Comparing content of files %x +Porównywanie zawartości plików %x + +Generating file list... +Generowanie listy plików... + +Starting comparison +Kompresowanie + +Calculating sync directions... +Obliczanie kierunków synchronizacji... + +Out of memory. +Brak wolnej pamięci. + +Item exists on left side only +Element istnieje tylko po lewej stronie + +Item exists on right side only +Element istnieje tylko po prawej stronie + +Left side is newer +Lewa strona jest nowsza + +Right side is newer +Prawa strona jest nowsza + +Items have different content +Elementy różnią się zawartością + +Both sides are equal +Obie strony są równe + +Conflict/item cannot be categorized +Konflikt/element nie może zostać skategoryzowany + +Copy new item to left +Kopiuj nowy element na lewą stronę + +Copy new item to right +Kopiuj nowy element na prawą stronę + +Delete left item +Usuń lewy element + +Delete right item +Usuń prawy element + +Move file on left +Przenieś plik na lewą stronę + +Move file on right +Przenieś plik na prawą stronę + +Overwrite left item +Nadpisz lewy element + +Overwrite right item +Nadpisz prawy element + +Do nothing +Nie rób nic + +Update attributes on left +Aktualizuj atrybuty po lewej stronie + +Update attributes on right +Aktualizuj atrybuty po prawej stronie + +Database file %x is incompatible. +Plik bazy danych %x nie jest kompatybilny. + +Initial synchronization: +Wstępna synchronizacja: + +Database file %x does not yet exist. +Plik bazy danych %x nie istnieje. + +Database file is corrupt: +Plik bazy danych jest uszkodzony: + +Cannot write file %x. +Nie można zapisać pliku %x. + +Cannot read file %x. +Nie można odczytać pliku %x. + +Database files do not share a common session. +Pliki bazy danych nie współdzielą sesji. + +Searching for folder %x... +Wyszukiwanie katalogu %x... + +Cannot read file attributes of %x. +Nie można odczytać atrybutów pliku %x. + +Cannot get process information. +Nie można uzyskać informacji dla procesu. + +Waiting while directory is locked (%x)... +Blokada katalogu (%x), oczekiwanie... + + +1 sec +%x sec + + +1 sekunda +%x sekundy +%x sekund + + +Creating file %x +Tworzenie pliku %x + +Items processed: +Przetworzone elementy: + +Items remaining: +Pozostałe elementy: + +Total time: +Całkowity czas: + + +1 byte +%x bytes + + +1 bajt +%x bajty +%x bajtów + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Błąd podczas parsowania pliku %x, rząd %y, kolumna %z. + +Cannot set directory lock for %x. +Nie można utworzyć blokady dla katalogu %x. + +Scanning: +Skanowanie: + + +1 thread +%x threads + + +1 wątek +%x wątki +%x wątków + + +Encoding extended time information: %x +Odkodowywanie rozszerzonych informacji o czasie: %x + +/sec +/sekundę + +%x items/sec +%x elementów/sek + +Configuration file %x loaded partially only. +Plik konfiguracyjny %x został wczytany tylko częściowo. + +Show in Explorer +Wyświetl w Eksploratorze + +Open with default application +Otwórz za pomocą domyślnej aplikacji + +Browse directory +Przeglądaj katalog + +Cannot access the Volume Shadow Copy Service. +Nie można uzyskać dostępu do usługi Volume Shadow Copy. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Usługa shadow copy jest aktywna tylko w wersji 64 bitowej programu FreeFileSync. + +Cannot load file %x. +Nie można wczytać pliku %x. + +Cannot determine volume name for %x. +Nie można określić nazwy dysku dla %x. + +Volume name %x is not part of file path %y. +Nazwa zasobu %x ni jest częścią ścieżki %y. + +Stop requested: Waiting for current operation to finish... +Przerwanie: Oczekiwanie na zakończenie aktualnej operacji... + +Unable to create timestamp for versioning: +Nie można utowrzyć znacznika czasu dla wersjonowania: + +Cannot read the following XML elements: +Nie można odczytać elementu XML: + +&Open... +&Otwórz... + +Save &as... +&Zapisz jako... + +&Quit +Zam&knij + +&Program +&Program + +&View help +&Pomoc + +&About +O Program&ie + +&Help +Pomo&c + +Usage: +Użycie: + +1. Select folders to watch. +1. Określ obserwowane katalogi. + +2. Enter a command line. +2. Wprowadź komendę. + +3. Press 'Start'. +3. Wciśnij 'Start'. + +To get started just import a .ffs_batch file. +Aby rozpocząć po zaimportuj plik .ffs_batch. + +Folders to watch: +Katalogi do obserwowania: + +Add folder +Dodaj katalog + +Remove folder +Usuń katalog + +Browse +Przeglądaj + +Select a folder +Wybierz katalog + +Idle time (in seconds): +Czas bezczynności (w sekundach): + +Idle time between last detected change and execution of command +Czas pomiędzy ostatnią wykrytą zmianą, a uruchomieniem komendy + +Command line: +Linia komend: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +Komenda jest wykonywana gdy: +- pliki lub podkatalogi ulegną zmianie +- zostaną utworzony nowe katalogi (n.p włożenie pamięci USB) + + +&Start +&Rozpocznij + +About +O Programie + +Build: %x +Zbudowano: %x + +All files +Wszystkie pliki + +Automated Synchronization +Automatyczna synchronizacja + +Directory monitoring active +Monitorowanie katalogów aktywne + +Waiting until all directories are available... +Oczekiwanie na dostępność wszystkich katalogów... + +Error +Błąd + +&Restore +&Przywróć + +&Show error +Pokaż &błędy + +&Exit +&Wyjście + +Incorrect command line: +Niepoprawne polecenie: + +&Retry +&Powtórz + +File content +Zawartość pliku + +File time and size +Czas modyfikacji i rozmiar + +Two way +Obustronna + +Mirror +Lustrzana + +Update +Uaktualnij + +Custom +Własne + +Multiple... +Wiele... + +Moving file %x to %y +Przenoszenie pliku %x do %y + +Moving folder %x to %y +Przenoszenie katalogu %x do %y + +Moving symbolic link %x to %y +Przenoszenie dowiązania symbolicznego %x do %y + +Removing old versions... +Usuwanie starszych wersji... + +Creating symbolic link %x +Tworzenie dowiązania symbolicznego %x + +Creating folder %x +Tworzenie folderu %x + +Overwriting file %x +Nadpisywanie pliku %x + +Overwriting symbolic link %x +Nadpisywanie dowiązania symbolicznego %x + +Verifying file %x +Weryfikowanie pliku %x + +Updating attributes of %x +Aktualizowanie atrybutów %x + +Cannot find %x. +Nie można znaleźć %x. + +Target folder %x already existing. +Katalog docelowy %x już istnieje. + +Target folder input field must not be empty. +Pole katalog docelowy nie może być puste. + +Please enter a target folder for versioning. +Określ katalog do wersjonowania. + +Source folder %x not found. +Nie znaleziono katalogu docelowego %x. + +The following items have unresolved conflicts and will not be synchronized: +Te elementy znajdują się w konflikcie, którego nie można rozwiązać. Pliki nie zostaną zsynchronizowane: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +Poniższe katalogi znacznie się różnią. Upewnij się, że określone zostały prawiłowe katalogi. + +Not enough free disk space available in: +Brak wystarczającej przestrzeni dyskowej na: + +Required: +Wymagane: + +Available: +Dostępne: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Katalog zostanie zmodyfikowany w związku z synchronizacją wielu katalogów. Zweryfikuj ustawienia synchronizacji. + +Synchronizing folder pair: +Synchronizacja katalgów: + +Generating database... +Generowanie bazy danych... + +Creating a Volume Shadow Copy for %x... +Tworzenie Volume Shadow Copy dla %x... + +Data verification error: %x and %y have different content. +Nastąpił błąd weryfikacji: %x oraz %y różnią się zawartością. + +job name +nazwa zadania + +Synchronization stopped +Synchronizacja zatrzymana + +Synchronization completed with errors +Synchronizacja zakończona z błędami + +Synchronization completed with warnings +Synchronizacja zakończona z ostrzeżeniami + +Nothing to synchronize +Brak plików do synchronizacji + +Synchronization completed successfully +Synchronizacja zakończona pomyślnie + +Saving log file %x... +Zapisywanie pliku logów %x... + +You can switch to FreeFileSync's main window to resolve this issue. +Możesz przejść do głównego okna FreeFileSync abe rozwiązać ten problem. + +&Don't show this warning again +&Nie pokazuj ponownie tego ostrzeżenia + +&Ignore +&Ignoruj + +&Switch +&Zamień + +Switching to FreeFileSync's main window +Przejdź do głównego okna FreeFileSync. + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + + + +&Ignore subsequent errors +&Ignoruj kolejne błędy + +Retrying operation... + + +Serious Error +Poważny błąd + +Check for Program Updates +Sprawdź dostępne aktualizacje. + +A new version of FreeFileSync is available: +Dostępna jest nowa wersja FreeFileSync: + +Download now? +Pobrać teraz? + +&Download +&Pobierz + +FreeFileSync is up to date. +Posiadasz aktualną wersję FreeFileSync. + +Unable to connect to sourceforge.net. +Nie można się połączyć z sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Nie można znaleźć obecnej wersji FreeFileSync. Czy chcesz sprawdzić ręcznie? + +&Check + + +Symlink +Dowiązanie symboliczne + +Folder +Katalog + +Full path +Pełna ścieżka + +Name +Nazwa + +Relative path +Relatywna ścieżka + +Base folder +Katalog bazowy + +Size +Rozmiar + +Date +Data + +Extension +Rozszerzenie + +Category +Kategoria + +Action +Akcja + +Drag && drop +Drag && Drop + +Close progress dialog +Zamknij okno postępu + +Standby +Przejdź w stan uśpienia + +Log off +Wyloguj użytkownika + +Shut down +Wyłącz komputer + +Hibernate +Przejdź w stan hibernacji + +Alternate comparison settings +Alternatywne ustawienia porównywania + +Alternate synchronization settings +Alternatywne ustawienia synchronizacji + +Local filter +Filtr lokalny + +Active +Aktywny + +None +Żaden + +Remove alternate settings +Usuń alternatywne ustawienia + +Clear filter settings +Wyczyść ustawienia filtra + +Copy +Kopiuj + +Paste +Wklej + +Alternate Comparison Settings +Alternatywne ustawienia porównywania + +Alternate Synchronization Settings +Alternatywne ustawienia synchronizacji + +Local Filter +Filtr lokalny + +&New +&Nowy + +&Save +&Zapisz + +Save as &batch job... +Zapisz w trybie &wsadowym... + +1. &Compare +1. &Porównaj + +2. &Synchronize +2. &Synchronizuj + +&Global settings +&Ustawienia programu + +&Language +&Język + +&Find... +&Szukaj... + +&Export file list... +&Eksportuj listę plików... + +&Tools +&Narzędzia + +&Check now +Spra&wdź teraz + +Check &automatically once a week +Sprawdzaj &automatycznie raz w tygodniu + +&Check for new version +Sprawdź &nową wersję + +Compare +Porównaj + +Cancel +Anuluj + +Synchronize +Synchronizuj + +Add folder pair +Dodaj katalogi do porównania + +Remove folder pair +Usuń katalogi + +Swap sides +Zamień stronami + +Close search bar +Zamknij pasek wyszukiwania + +Find: +Szukaj: + +Match case +Uwzględnij wielkość liter + +Save as batch job +Zapisz w trybie wsadowym + +Hide excluded items +Ukryj wykluczone elementy + +Show filtered or temporarily excluded files +Pokaż pliki wykluczone tymczasowo lub pliki wykluczone tymczasowo + +Number of files and folders that will be created +Liczba plików i katalogów, które zostaną utworzone + +Number of files that will be overwritten +Liczba plików, które zostaną nadpisane + +Number of files and folders that will be deleted +Liczba plików i katalogów, które zostaną usunięte + +Total bytes to copy +Całkowity rozmiar do skopiowania + +Select a variant: +Określ wariant: + +Identify equal files by comparing modification time and size. +Określ różnice w plikach na podstawie czasu modyfikacji i rozmiaru. + +Identify equal files by comparing the file content. +Określ różnice w plikach na podstawie ich zawartości. + +Symbolic links: +Dowiązania symboliczne: + +More information +Więcej informacji + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Wyszukaj oraz zastosuj zmiany po obu stronach. Wszystkie operacje na plikach takie jak usunięcia, zmiany oraz konflikty wykrywane są automatycznie przy użyciu bazy danych. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Kopia lustrzana lewego katalogu. Po synchronizacji, zawartość prawego katalogu będzie będzie identyczna jak w lewym katalogu. + +Copy new and updated files to the right folder. +Kopiuj nowe i zmodyfikowane pliki do prawego katalogu. + +Configure your own synchronization rules. +Skonfiguruj swoje własne reguły synchronizacji. + +Detect moved files +Wykryj przeniesione pliki. + +Requires database files. Not supported by all file systems. +Wymaga plików bazy danych. Nie wspierane przez wszystkie systemy plików. + +Delete files: +Usuwanie plików: + +Permanent +Trwale + +Delete or overwrite files permanently +Usuń lub nadpisz pliki na stałe + +Recycle bin +Kosz + +Back up deleted and overwritten files in the recycle bin +Przechowuj pliki, które zostały usunięte lub nadpisane w koszu. + +Versioning +Wersjonowanie + +Move files to a user-defined folder +Przenieś pliki do katalogu zdefiniowanego przez użytkownika + +Naming convention: +Konwencja nazewnictwa: + +Show examples +Przykład + +Handle errors: +Obsługa błędów: + +Ignore +Ignoruj + +Hide all error and warning messages +Ukryj wszystkie informacje błędach i ostrzeżeniach + +Pop-up +Pop-up + +Show pop-up on errors or warnings +Pokazuj okna pop-up dla błędów i ostrzeżeń + +On completion: +Po zakończeniu: + +Start synchronization now? +Rozpocząć teraz synchronizację? + +Variant: +Wariant: + +Statistics +Statystyki + +&Don't show this dialog again +&Nie pokazuj więcej tego okna + +Items found: +Znalezione elementy: + +Speed: +Prędkość: + +Time remaining: +Do ukończenia: + +Time elapsed: +Szacowany czas: + +Synchronizing... +Synchronizuję... + +Minimize to notification area +Minimalizuj do obszaru powiadomiń + +Close +Zamknij + +&Pause +&Pauza + +Stop +Przerwij + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Utwórz plik wsadowy do zautomatyzowania procesu synchronizacji. Aby rozpocząć, klknij dwa razy plik lub zaplanuj zadanie w: %x + +Stop synchronization at first error +Przerwij synchronizację przy pierwszym błędzie + +Show progress dialog +Pokaż okno postępu + +Save log: +Zapisz logi: + +Limit: +Limit: + +Limit maximum number of log files +Określ maksymalną liczbę plików z logami + +How can I schedule a batch job? +Jak zaplanować zadanie w trybie wsadowym? + +&Recycle bin +&Kosz systemowy + +Delete on both sides +Usuń po obu stronach + +Delete on both sides even if the file is selected on one side only +Usuń po obu stronach nawet jeżeli plik zaznaczony jest tylko po jednej stronie + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Określ reguły filtrowania w celu wykluczenia niektórych plików z synchronizacji. Ścieżki plików muszą być relatywne do podanych par katalogów. + +Include: +Dołącz: + +Exclude: +Wyklucz: + +Time span: +Przedział czasu: + +File size: +Rozmiar pliku: + +Minimum: +Minimalny: + +Maximum: +Maksymalny: + +&Clear +W&yczyść + +The following settings are used for all synchronization jobs. +Następujące ustawienia stosowane są do wszystkich zadań synchronizacji. + +Fail-safe file copy +Bezpieczne kopiowanie + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Aby zagwarantować spójność synchronizacji nawet podczas błędu, +program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadpisuje plik docelowy. + + +(recommended) +(rekomendowane) + +Copy locked files +Kopiuj zablokowane pliki + +Copy shared or locked files using the Volume Shadow Copy Service. +Kopiuj pliki współdzielone lub zablokowane używając usługi Volume Shadow Copy. + +(requires administrator rights) +(wymagane uprawnienia administratora) + +Copy file access permissions +Kopiuj uprawnienia plików + +Transfer file and folder permissions. +kopiuj uprawnienia plików i katalogów. + +Automatic retry on error: +Automatyczne ponowienie operacji podczas błędu: + +Retry count: +Liczba prób: + +Delay (in seconds): +Opóźnienie (w sekundach): + +Customize context menu: +Dostosuj menu kontekstowe: + +Description +Opis + +Restore hidden windows +Przywróć ukryte dialogi + +&Default +&Domyślne + +Source code written in C++ using: +Kod stworzony w C++ z wykorzystaniem: + +If you like FreeFileSync +Podoba Ci się FreeFileSync? + +Donate with PayPal +Wesprzyj z PayPal + +Feedback and suggestions are welcome +Wszelkie opinie i sugestie mile widziane + +Homepage +Strona domowa + +Email +Poczta + +Published under the GNU General Public License +Udostępnione na zasadach licencji GNU General Public License + +Many thanks for localization: +Podziękowania za tłumaczenia: + +Save as Batch Job +Zapisz w trybie wsadowym + +Delete Items +Usuń elementy + +Global Settings +Ustawienia ogólne + +Select Time Span +Określ + +Folder Pairs +Pary katalogów + +Find +Znajdź + +Overview +Przegląd + +Configuration +Konfiguracja + +Main Bar +Główny pasek + +Filter Files +Filtr + +Select View +Widok + +Open... +Otwórz... + +Save +Zapisz + +Compare both sides +Porównaj foldery + +Comparison settings +Ustawienia porównywania + +Synchronization settings +Ustawienia synchronizacji + +Start synchronization +Rozpocznij synchronizację + +Confirm +Potwierdź + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + + + +&Execute +&Wykonaj + + +1 directory +%x directories + + +1 katalog +%x katalogi +%x katalogów + + + +1 file +%x files + + +1 plik +%x pliki +%x plików + + + +%y of 1 row in view +%y of %x rows in view + + +Widok %y z 1 lini +Widok %y z %x lini +Widok %y z %x lini + + +Set direction: +Kierunek synchronizacji: + +multiple selection +zaznaczone elementy + +Include via filter: +Dodaj pliki: + +Exclude via filter: +Dodaj filtr: + +Exclude temporarily +Wyklucz tymczasowo + +Include temporarily +Dołącz tymczasowo + +Delete +Usuń + +Include all +Zaznacz wszystko + +Exclude all +Odznacz wszystko + +Show icons: +Pokaż ikony: + +Small +Mały + +Medium +Średni + +Large +Duży + +Select time span... +Określ przedział czasowy... + +Default view +Widok domyślny + +Show "%x" +Pokaż "%x" + +Last session +Ostatnia sesja + +Folder Comparison and Synchronization +Porównywanie i Synchronizacja folderów + +Configuration saved +Konfiguracja zapisana + +FreeFileSync batch +Plik wsadowy FreeFileSync + +Do you want to save changes to %x? +Czy chcesz zapisać zmiany w %x? + +Never save &changes +Nigdy nie zapisuj &zmian + +Do&n't save +&Nie zapisuj + +Filter +Filtr + +Show files that exist on left side only +Pokaż pliki istniejące tylko po lewej stronie + +Show files that exist on right side only +Pokaż pliki istniejące tylko po prawej stronie + +Show files that are newer on left +Pokaż pliki nowsze po lewej stronie + +Show files that are newer on right +Pokaż pliki nowsze po prawej stronie + +Show files that are equal +Pokaż pliki, które są równe + +Show files that are different +Pokaż pliki, które się różnią + +Show conflicts +Pokaż konflikty + +Show files that will be created on the left side +Pokaż pliki, które będą utworzone po lewej stronie + +Show files that will be created on the right side +Pokaż pliki, które będą utworzone po prawej stronie + +Show files that will be deleted on the left side +Pokaż pliki, które będą usunięte po lewej stronie + +Show files that will be deleted on the right side +Pokaż pliki, które będą usunięte po prawej stronie + +Show files that will be overwritten on left side +Pokaż pliki, które zostaną nadpisane po lewej stronie + +Show files that will be overwritten on right side +Pokaż pliki, które zostaną nadpisane po prawej stronie + +Show files that won't be copied +Pokaż pliki, które nie będą kopiowane + +Set as default +Zapisz jako domyślne + +All folders are in sync +Wszystkie katalogi są zsynchronizowane + +Synchronization Settings +Ustawienia synchronizacji + +Comparison Settings +Ustawienia porównywania + +Cannot find %x +Nie można znaleźć %x + +Comma-separated values +Wartości rozdzielone przecinkiem + +File list exported +Lista plików wyeksportowana + +Searching for program updates... +Wyszukiwanie aktualizacji... + +Scanning... +Skanowanie... + +Comparing content... +Porównywanie zawartości... + +Info +Info + +Warning +Ostrzeżenie + +Paused +Pauza + +Initializing... +Inicjalizacja... + +Stopped +Zatrzymana + +Completed +Zakończono + +&Continue +&Kontynuuj + +Log +Log + +Today +Dzisiaj + +This week +W tym tygodniu + +This month +W tym miesiącu + +This year +W tym roku + +Last x days +Ostatnie x dni + +Byte +Bajtów + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Czy na pewno chcesz przenieść ten element do kosza? +Czy na pewno chcesz przenieść te %x elementy do kosza? +Czy na pewno chcesz przenieść te %x elemntów do kosza? + + +Move +Przenieś + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Czy na pewno chcesz usunąć następujący element? +Czy na pewno chcesz usunąć %x następujące elementy? +Czy na pewno chcesz usunąć %x następujących elementów? + + +Exclude +Wyklucz + +Direct +Bezpośrednio + +Follow +Podążaj + +Copy NTFS permissions +Kopiuj uprawnienia NTFS + +Integrate external applications into context menu. The following macros are available: +Dołącz zewnętrzną aplikację do menu kontekstowego. Dostępne makra: + +- full file or folder name +- pełna nazwa pliku lub katalogu + +- folder part only +- tylko część katalogu + +- Other side's counterpart to %item_path% +- Odpowiednik %item_path% po drugiej stronie + +- Other side's counterpart to %item_folder% +- Odpowiednik %item_folder% po drugiej stronie + +Restore all hidden windows and warnings? +Przywrócić wszystkie ukryte dialogi? + +Leave as unresolved conflict +Zostaw jako nierozwiązany konflikt + +Replace +Zamień + +Move files and replace if existing +Przenieś pliki i nadpisz jeżeli już istnieją + +Time stamp +Znacznik czasu + +Append a timestamp to each file name +Dołącz znacznik czasu do nazwy każdego pliku + +File +Plik + +YYYY-MM-DD hhmmss +YYYY-MM-DD hhmmss + +Files +Pliki + +Items +Elementy + +Percentage +Procentowo + +Cannot monitor directory %x. +Nie można monitorować katalogu %x. + +Conversion error: +Błąd konwersji: + +Cannot delete file %x. +Nie można usunąć pliku %x. + +The file is locked by another process: +Plik jest zablokowany przez inny proces: + +Cannot move file %x to %y. +Nie można przenieść pliku %x do %y. + +Cannot delete directory %x. +Nie można usunąć katalogu %x. + +Cannot write file attributes of %x. +Nie można zapisać atrybutów %x. + +Cannot write modification time of %x. +Nie można zapisać czasu modyfikacji %x. + +Cannot read security context of %x. +Nie można odczytać ustawień bezpieczeństwa %x. + +Cannot write security context of %x. +Nie można zapisać ustawień bezpieczeństwa %x. + +Cannot read permissions of %x. +Nie można odczytać uprawnień %x. + +Cannot write permissions of %x. +Nie można zapisać uprawnień %x. + +Cannot create directory %x. +Nie można utworzyć katalogu %x. + +Cannot create symbolic link %x. +Nie można utworzyć dowiązania symbolicznego dla %x. + +Cannot find system function %x. +Nie można odnaleźć funkcji systemowej %x. + +Cannot copy file %x to %y. +Nie można skopiować pliku %x do %y. + +Type of item %x is not supported: +Element typu %x nie jest wspierany: + +Cannot resolve symbolic link %x. +Nie można określić położenia dowiązania symbolicznego %x. + +Cannot open directory %x. +Nie można otworzyć katalogu %x. + +Cannot enumerate directory %x. +Nie można wyliczyć katalogu %x. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +1 minuta +%x minuty +%x minut + + + +1 hour +%x hours + + +1 godzina +%x godziny +%x godzin + + + +1 day +%x days + + +1 dzień +%x dni +%x dni + + +Unable to register to receive system messages. +Błąd podczas rejestrowania do odbioru komunikatów systemowych. + +Cannot set privilege %x. +Nie można ustawić uprawnień %x. + +Unable to suspend system sleep mode. +Nie można przerwać trybu uśpienia. + +Cannot change process I/O priorities. +Nie można zmienić priorytetu I/O procesu. + +Unable to move %x to the recycle bin. +Nie można przenieść %x do kosza. + +Cannot determine final path for %x. +Nie można określić ostatecznej ścieżki dla %x. + +Error Code %x: +Kod błędu %x: + diff --git a/FreeFileSync/Build/Languages/portuguese.lng b/FreeFileSync/Build/Languages/portuguese.lng new file mode 100644 index 00000000..0c26ef2f --- /dev/null +++ b/FreeFileSync/Build/Languages/portuguese.lng @@ -0,0 +1,1517 @@ +
    + Português + Carlos Balseiro + pt_PT + flag_portugal.png + 2 + n == 1 ? 0 : 1 +
    + +Both sides have changed since last synchronization. +Ambos os lados tiveram alterações desde a última sincronização. + +Cannot determine sync-direction: +Não é possível saber a direção de sincronização: + +No change since last synchronization. +Não há alterações desde a última sincronização. + +The database entry is not in sync considering current settings. +A base de dados não está sincronizada com as definições correntes. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Escolher direcção de sincronização por defeito: Os ficheiros antigos serão substituídos pelos novos. + +Checking recycle bin availability for folder %x... +A verificar a disponibilidade da reciclagem para a pasta %x... + +Moving file %x to the recycle bin +Mover ficheiro %x para a reciclagem + +Moving folder %x to the recycle bin +Mover pasta %x para a reciclagem + +Moving symbolic link %x to the recycle bin +Mover link simbólico %x para a reciclagem + +Deleting file %x +Eliminar ficheiro %x + +Deleting folder %x +Eliminar pasta %x + +Deleting symbolic link %x +Eliminar link simbólico %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +A reciclagem não está disponível para as seguintes pastas. Os ficheiros serão, em alternativa, eliminados permanentemente: + +An exception occurred +Ocorreu uma excepção + +A directory path is expected after %x. +É esperado um caminho de directório após %x. + +Syntax error +Erro de sintaxe + +Cannot open file %x. +Não é possível abrir o ficheiro %x. + +File %x does not contain a valid configuration. +Ficheiro %x não tem uma configuração válida. + +Unequal number of left and right directories specified. +Número desigual de directórios à esquerda e à direita. + +The config file must not contain settings at directory pair level when directories are set via command line. +O ficheiro de configuração não deve conter configurações ao nível de par-directório quando os directórios são definidos pela linha de comandos. + +Directories cannot be set for more than one configuration file. +Os directórios não podem ser definidos em mais do que um ficheiro de configuração. + +Command line +Linha de comandos + +Syntax: +Sintaxe: + +config files +ficheiros de configuração + +directory +directório + +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 directories for at most one config file. +Qualquer número de directórios alternativos para apenas um ficheiro de configuração. + +A folder input field is empty. +Um dos campos de directório para comparar está vazio. + +The corresponding folder will be considered as empty. +A pasta correspondente será considerada como vazia. + +Cannot find the following folders: +Não é possível encontrar as seguintes pastas: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Pode ignorar este erro para considerar cada pasta como vazia. As pastas necessárias serão criadas automaticamente durante a sincronização. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +As seguintes pastas têm caminhos dependentes. Tenha atenção ao definir as regras de sincronizaçção: + +File %x has an invalid date. +Ficheiro %x tem data inválida. + +Date: +Data: + +Files %x have the same date but a different size. +Os ficheiros %x têm a mesma data, mas tamanho diferente. + +Size: +Tamanho: + +Items differ in attributes only +Itens diferem apenas nos atributos + +Resolving symbolic link %x +Resolver link simbólico %x + +Comparing content of files %x +A comparar o conteúdo do ficheiro %x + +Generating file list... +A gerar lista ficheiros... + +Starting comparison +A iniciar a comparação + +Calculating sync directions... +A calcular a direcção de sincronização... + +Out of memory. +Sem memória disponível. + +Item exists on left side only +Item existe apenas à esquerda + +Item exists on right side only +Item existe apenas à direita + +Left side is newer +Esquerda é mais recente + +Right side is newer +Direita é mais recente + +Items have different content +Itens tem conteúdo diferente + +Both sides are equal +Ambos os lados iguais + +Conflict/item cannot be categorized +Conflito/item não pode ser categorizado + +Copy new item to left +Copiar novo item para a esquerda + +Copy new item to right +Copiar novo item para a direita + +Delete left item +Apagar item esquerdo + +Delete right item +Apagar item direito + +Move file on left +Mover ficheiro à esquerda + +Move file on right +Mover ficheiro à direita + +Overwrite left item +Substituir item da esquerda + +Overwrite right item +Substituir item da direita + +Do nothing +Não fazer nada + +Update attributes on left +Actualizar atributos à esquerda + +Update attributes on right +Actualizar atributos à direita + +Database file %x is incompatible. +Base de dados %x não é compatível. + +Initial synchronization: +Sincronização inicial: + +Database file %x does not yet exist. +Base de dados %x não existe. + +Database file is corrupt: +Ficheiro de base de dados está corrompido: + +Cannot write file %x. +Não é possível escrever o ficheiro %x. + +Cannot read file %x. +Não é possível ler o ficheiro %x. + +Database files do not share a common session. +As bases de dados são de sessões diferentes. + +Searching for folder %x... +À procura da pasta %x... + +Cannot read file attributes of %x. +Não é possível ler os atributos do ficheiro %x. + +Cannot get process information. +Não é possível obter informação sobre o processo. + +Waiting while directory is locked (%x)... +Aguardar enquanto o directório é bloqueado (%x)... + + +1 sec +%x sec + + +1 seg +%x segs + + +Creating file %x +Criar ficheiro %x + +Items processed: +Elementos processados: + +Items remaining: +Elementos restantes: + +Total time: +Tempo total: + + +1 byte +%x bytes + + +1 byte +%x bytes + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Erro ao analisar ficheiro %x, linha %y, coluna %z. + +Cannot set directory lock for %x. +Não é possível colocar o bloqueio de directório para %x. + +Scanning: +A pesquisar: + + +1 thread +%x threads + + +1 thread +%x threads + + +Encoding extended time information: %x +A codificar dados temporais extendidos: %x + +/sec +/seg + +%x items/sec +%x itens/seg + +Configuration file %x loaded partially only. +Ficheiro de configuração %x carregado parcialmente. + +Show in Explorer +Mostrar no Explorer + +Open with default application +Abrir com a aplicação associada + +Browse directory +Procurar directório + +Cannot access the Volume Shadow Copy Service. +Não é possível aceder ao serviço Volume Shadow Copy. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Utilize a versão 64-bit do FreeFileSync para criar cópias sombra neste sistema. + +Cannot load file %x. +Não é possível carregar o ficheiro %x. + +Cannot determine volume name for %x. +Não é possível determinar o nome do volume para %x. + +Volume name %x is not part of file path %y. +Nome de volume %x não faz parte do caminho do ficheiro %y. + +Stop requested: Waiting for current operation to finish... +Paragem solicitada: À espera que termine a operação actual... + +Unable to create timestamp for versioning: +Não é possível criar timestamp para controle de versões: + +Cannot read the following XML elements: +Não é possível ler os elementos XML: + +&Open... +&Abrir... + +Save &as... +Guardar &como... + +&Quit +&Sair + +&Program +&Programa + +&View help +&Ver ajuda + +&About +So&bre + +&Help +A&juda + +Usage: +Uso: + +1. Select folders to watch. +1. Selecionar pastas para observar. + +2. Enter a command line. +2. Inserir a linha de comando. + +3. Press 'Start'. +3. Pressionar 'Iniciar'. + +To get started just import a .ffs_batch file. +Para começar basta importar um ficheiro .ffs_batch. + +Folders to watch: +Pastas a verificar: + +Add folder +Adicionar pasta + +Remove folder +Remover pasta(s) + +Browse +Procurar + +Select a folder +Selecione uma pasta + +Idle time (in seconds): +Tempo de espera (em segundos): + +Idle time between last detected change and execution of command +Tempo de espera entre a última alteração detetada e a execução do comando + +Command line: +Linha de comandos: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +O comando é executado se: +- ficheiros ou subpastas forem alterados +- forem detetadas novas pastas (p.e. pen USB) + + +&Start +&Início + +About +Sobre + +Build: %x +Build: %x + +All files +Todos os ficheiros + +Automated Synchronization +Sincronização Automática + +Directory monitoring active +Monitorização de directório activa + +Waiting until all directories are available... +A aguardar que todos os directórios fiquem disponíveis... + +Error +Erro + +&Restore +&Restaurar + +&Show error +Mostrar &erro + +&Exit +&Sair + +Incorrect command line: +Linha de comandos incorrecta: + +&Retry +&Tentar de Novo + +File content +Conteúdo do ficheiro + +File time and size +Data e tamanho do ficheiro + +Two way +Duas vias + +Mirror +Espelhar + +Update +Actualizar + +Custom +Personalizado + +Multiple... +Multiplo... + +Moving file %x to %y +Mover ficheiro %x para %y + +Moving folder %x to %y +Mover pasta %x para %y + +Moving symbolic link %x to %y +Mover link simbólico %x para %y + +Removing old versions... +A remover versões antigas... + +Creating symbolic link %x +Criar link simbólico %x + +Creating folder %x +Criar pasta %x + +Overwriting file %x +Substituir ficheiro %x + +Overwriting symbolic link %x +Substituir link simbólico %x + +Verifying file %x +A verificar ficheiro %x + +Updating attributes of %x +Actualizar atributos de %x + +Cannot find %x. +Não é possível encontrar %x. + +Target folder %x already existing. +Directório de destino %x já existe. + +Target folder input field must not be empty. +Campo de directório de destino não deve estar vazio. + +Please enter a target folder for versioning. +Introduza uma pasta de destino para o controlo de versões. + +Source folder %x not found. +Directório %x não encontrado. + +The following items have unresolved conflicts and will not be synchronized: +Os seguintes itens têm conflitos não resolvidos, e não serão sincronizados: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +As seguintes pastas são significativamente diferentes. Verifique se está a usar as pastas correctas na sincronização. + +Not enough free disk space available in: +Não há espaço livre suficiente em: + +Required: +Requirido: + +Available: +Disponível: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Uma pasta que faz parte de vários pares vai ser modificada. Por favor, reveja os parametros de sincronização. + +Synchronizing folder pair: +Sincronizar o par de pastas: + +Generating database... +A gerar base de dados... + +Creating a Volume Shadow Copy for %x... +A criar Volume Shadow Copy para %x... + +Data verification error: %x and %y have different content. +Erro de verificação de dados: %x e %y têm conteúdo diferente. + +job name +nome da tarefa + +Synchronization stopped +Sincronização parada + +Synchronization completed with errors +Sincronização completa com erros + +Synchronization completed with warnings +Sincronização completa com avisos + +Nothing to synchronize +Nada a sincronizar + +Synchronization completed successfully +Sincronização completa com sucesso + +Saving log file %x... +A guardar ficheiro log %x... + +You can switch to FreeFileSync's main window to resolve this issue. +Pode mudar para a janela principal do FreeFileSync para resovler este problema. + +&Don't show this warning again +&Não mostrar este aviso novamente + +&Ignore +&Ignorar + +&Switch +&Trocar + +Switching to FreeFileSync's main window +A mudar para a janela principal do FreeFileSync + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + +Repetir automaticamente dentro de 1 segundo... +Repetir automaticamente dentro de %x segundos... + + +&Ignore subsequent errors +&Ignorar erros subsequentes + +Retrying operation... + + +Serious Error +Erro Grave + +Check for Program Updates +Procurar actualizações do programa + +A new version of FreeFileSync is available: +Uma nova versão do FreeFileSync está disponível: + +Download now? +Fazer download agora? + +&Download +&Download + +FreeFileSync is up to date. +FreeFileSync está actualizado. + +Unable to connect to sourceforge.net. +Não é possível ligar a sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Não é possível encontrar a última versão do FreeFileSync online. Deseja verificar manualmente? + +&Check + + +Symlink +Link Simbólico + +Folder +Pasta + +Full path +Caminho completo + +Name +Nome + +Relative path +Caminho + +Base folder +Pasta principal + +Size +Tamanho + +Date +Data + +Extension +Extensão + +Category +Categoria + +Action +Ação + +Drag && drop +Arrastar && Largar + +Close progress dialog +Fechar diálogo de progresso + +Standby +Standby + +Log off +Terminar sessão + +Shut down +Desligar + +Hibernate +Hibernar + +Alternate comparison settings +Definições de comparação alternativas + +Alternate synchronization settings +Definições de sincronização alternativas + +Local filter +Filtro local + +Active +Activo + +None +Nenhum + +Remove alternate settings +Remover opções alternativas + +Clear filter settings +Limpar opções do filtro + +Copy +Copiar + +Paste +Colar + +Alternate Comparison Settings +Definições de comparação alternativas + +Alternate Synchronization Settings +Definições de sincronização alternativas + +Local Filter +Filtro local + +&New +&Novo + +&Save +G&uardar + +Save as &batch job... +Guardar como &batch... + +1. &Compare +1. &Comparar + +2. &Synchronize +2. &Sincronizar + +&Global settings +&Opções + +&Language +&Língua + +&Find... +&Procurar... + +&Export file list... +&Exportar lista de ficheiros... + +&Tools +&Ferramentas + +&Check now +&Verificar agora + +Check &automatically once a week +Verificar &automaticamente uma vez por semana + +&Check for new version +&Procurar por actualizações + +Compare +Comparar + +Cancel +Cancelar + +Synchronize +Sincronizar + +Add folder pair +Adicionar um par de pastas + +Remove folder pair +Remover o par de pastas + +Swap sides +Trocar lados + +Close search bar +Fechar barra de pesquisa + +Find: +Procurar: + +Match case +Correspondência + +Save as batch job +Guardar como batch + +Hide excluded items +Esconder itens excluídos + +Show filtered or temporarily excluded files +Mostrar ficheiros filtrados ou temporariamente excluidos + +Number of files and folders that will be created +Número de ficheiros e pastas a ser criados + +Number of files that will be overwritten +Número de ficheiros substituidos + +Number of files and folders that will be deleted +Número de ficheiros e pastas a ser eliminados + +Total bytes to copy +Total em bytes a copiar + +Select a variant: +Seleccionar uma variante: + +Identify equal files by comparing modification time and size. +Identificar ficheiros iguais ao comparar hora de modificação e tamanho. + +Identify equal files by comparing the file content. +Identificar ficheiros iguais ao comparar o conteúdo. + +Symbolic links: +Links simbólicos: + +More information +Mais informação + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Identificar e propagar mudanças nos dois lados. Eliminações, cópias e conflitos são detectados automaticamente usando base de dados. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Criar uma cópia exacta da pasta à esquerda, que vai corresponder à pasta da direita após a sincronização. + +Copy new and updated files to the right folder. +Copiar ficheiros novos e actualizados para a pasta à direita. + +Configure your own synchronization rules. +Configure as suas regras de sincronização. + +Detect moved files +Detectar ficheiros movidos + +Requires database files. Not supported by all file systems. +Requer base de dados. Não é suportado por todos os sistemas de ficheiros. + +Delete files: +Eliminar ficheiros: + +Permanent +Permanente + +Delete or overwrite files permanently +Apagar ou substituir ficheiros permanentemente + +Recycle bin +Reciclagem + +Back up deleted and overwritten files in the recycle bin +Fazer backup dos ficheiros apagados e substituidos na reciclagem + +Versioning +Manter versões anteriores + +Move files to a user-defined folder +Mover ficheiros para uma pasta definida pelo utilizador + +Naming convention: +Convenção de nomes: + +Show examples +Mostrar exemplos + +Handle errors: +Tratar erros: + +Ignore +Ignorar + +Hide all error and warning messages +Ocultar todas as mensagens de erro/aviso + +Pop-up +Pop-up + +Show pop-up on errors or warnings +Mostrar popup em caso de erros ou avisos + +On completion: +Ao completar: + +Start synchronization now? +Iniciar sincronização agora? + +Variant: +Variante: + +Statistics +Estatísticas + +&Don't show this dialog again +&Não mostrar este diálogo novamente + +Items found: +Elementos encontrados: + +Speed: +Velocidade: + +Time remaining: +Tempo restante: + +Time elapsed: +Tempo decorrido + +Synchronizing... +A sincronizar... + +Minimize to notification area +Minimizar para a área de notificação + +Close +Fechar + +&Pause +&Pausa + +Stop +Parar + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Criar ficheiro batch para sincronização automática. Para iniciar, fazer duplo clique no ficheiro ou acrescentar ao planeador de tarefas: %x + +Stop synchronization at first error +Para sincronização ao primeiro erro + +Show progress dialog +Mostrar diálogo de progresso + +Save log: +Guardar registo: + +Limit: +Limitar: + +Limit maximum number of log files +Limitar o número máximo de ficheiros log + +How can I schedule a batch job? +Como posso agendar um trabalho batch? + +&Recycle bin +&Reciclagem + +Delete on both sides +Eliminar em ambos os lados + +Delete on both sides even if the file is selected on one side only +Eliminar em ambos os lados mesmo se o ficheiro só está seleccionado num lado + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Seleccionar regras de filtro para excluir certos ficheiros da sincronização. Insira os caminhos relativos ao par de directórios correspondente. + +Include: +Incluir: + +Exclude: +Excluir: + +Time span: +Intervalo de tempo: + +File size: +Tamanho ficheiro: + +Minimum: +Mínimo: + +Maximum: +Máximo + +&Clear +&Limpar + +The following settings are used for all synchronization jobs. +As seguintes definições são usadas para todas as sincronizações. + +Fail-safe file copy +Cópia à prova de falhas + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Copiar para um ficheiro temporário (*.ffs_tmp) antes de substituir. +Isto garante um estado consistente mesmo em caso de falha grave. + + +(recommended) +(recomendado) + +Copy locked files +Copiar ficheiros bloqueados + +Copy shared or locked files using the Volume Shadow Copy Service. +Copiar ficheiros partilhados ou bloqueados usando o serviço Volume Shadow Copy. + +(requires administrator rights) +(requer permissões de administrador) + +Copy file access permissions +Copiar permissões de acesso do ficheiro + +Transfer file and folder permissions. +Transferir permissões de pasta e ficheiro. + +Automatic retry on error: +Retentar automaticamente em caso de erro: + +Retry count: +Nº de tentativas: + +Delay (in seconds): +Atraso (em segundos): + +Customize context menu: +Personalizar menu de contexto: + +Description +Descrição + +Restore hidden windows +Restaurar janelas escondidas + +&Default +&Config. Iniciais + +Source code written in C++ using: +Código fonte escrito em C++ utilizando: + +If you like FreeFileSync +Se gosta do FreeFileSync + +Donate with PayPal +Doar usando PayPal + +Feedback and suggestions are welcome +Comentários e sugestões são apreciados + +Homepage +Site + +Email +Email + +Published under the GNU General Public License +Publicado sobre GNU General Public License + +Many thanks for localization: +Muito obrigado pela localização: + +Save as Batch Job +Guardar como Ficheiro Batch + +Delete Items +Eliminar Itens + +Global Settings +Opções Globais + +Select Time Span +Seleccionar Intervalo de Tempo + +Folder Pairs +Par de Pastas + +Find +Procurar + +Overview +Vista + +Configuration +Configuração + +Main Bar +Barra principal + +Filter Files +Filtrar Ficheiros + +Select View +Seleccionar Vista + +Open... +Abrir... + +Save +Guardar + +Compare both sides +Comparar listas + +Comparison settings +Opções de comparação + +Synchronization settings +Parametros de sincronização + +Start synchronization +Iniciar a sincronização + +Confirm +Confirmar + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + +Deseja mesmo executar o comando %y para um item? +Deseja mesmo executar o comando %y para %x itens? + + +&Execute +&Executar + + +1 directory +%x directories + + +1 directório +%x directórios + + + +1 file +%x files + + +1 ficheiro +%x ficheiros + + + +%y of 1 row in view +%y of %x rows in view + + +%y de 1 linha em vista +%y de %x linhas em vista + + +Set direction: +Escolher direcção: + +multiple selection +seleção múltipla + +Include via filter: +Incluir via filtro: + +Exclude via filter: +Excluir por filtro: + +Exclude temporarily +Excluir temporariamente + +Include temporarily +Incluir temporariamente + +Delete +Eliminar + +Include all +Incluir tudo + +Exclude all +Excluir tudo + +Show icons: +Mostrar ícones: + +Small +Pequeno + +Medium +Médio + +Large +Grande + +Select time span... +Selecione intervalo de tempo... + +Default view +Vista normal + +Show "%x" +Mostrar "%x" + +Last session +Última Sessão + +Folder Comparison and Synchronization +Comparação e Sincronização de Pastas + +Configuration saved +Configuração guardada + +FreeFileSync batch +FreeFileSync batch + +Do you want to save changes to %x? +Deseja guardar as alterações a %x? + +Never save &changes +Nunca guardar &alterações + +Do&n't save +Não g&uardar + +Filter +Filtro + +Show files that exist on left side only +Mostrar ficheiros existentes somente à esquerda + +Show files that exist on right side only +Mostrar ficheiros existentes somente à direita + +Show files that are newer on left +Mostrar ficheiros mais recentes à esquerda + +Show files that are newer on right +Mostrar ficheiros mais recentes à direita + +Show files that are equal +Mostrar ficheiros iguais + +Show files that are different +Mostrar ficheiros diferentes + +Show conflicts +Mostrar conflitos + +Show files that will be created on the left side +Mostrar ficheiros a ser criados à esquerda + +Show files that will be created on the right side +Mostrar ficheiros a ser criados à direita + +Show files that will be deleted on the left side +Mostrar ficheiros a ser apagados à esquerda + +Show files that will be deleted on the right side +Mostrar ficheiros a ser apagados à direita + +Show files that will be overwritten on left side +Mostrar ficheiros a ser substituidos do lado esquerdo + +Show files that will be overwritten on right side +Mostrar ficheiros a ser substituidos do lado direito + +Show files that won't be copied +Mostrar ficheiros que não serão copiados + +Set as default +Definir como padrão + +All folders are in sync +Todas as pastas estão sincronizadas + +Synchronization Settings +Definições de Sincronização + +Comparison Settings +Definições de Comparação + +Cannot find %x +Não é possível descobrir %x + +Comma-separated values +Valores separados por virgula + +File list exported +Lista dos ficheiros exportada + +Searching for program updates... +A procurar actualizações do programa... + +Scanning... +A pesquisar... + +Comparing content... +A comparar... + +Info +Info + +Warning +Atenção + +Paused +Em pausa + +Initializing... +A iniciar... + +Stopped +Parado + +Completed +Terminado + +&Continue +&Continuar + +Log +Registo + +Today +Hoje + +This week +Esta semana + +This month +Este mês + +This year +Este ano + +Last x days +Últimos x dias + +Byte +Byte + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Deseja mesmo mover o seguinte item para a reciclagem? +Deseja mesmo mover os seguintes %x itens para a reciclagem? + + +Move +Mover + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Deseja mesmo eliminar o seguinte item? +Deseja mesmo eliminar os seguintes %x itens? + + +Exclude +Excluir + +Direct +Direto + +Follow +Seguir + +Copy NTFS permissions +Copiar permissões NTFS + +Integrate external applications into context menu. The following macros are available: +Integrar aplicações externas no menu de contexto. As seguintes macros estão disponíveis: + +- full file or folder name +- nome completo de ficheiro ou pasta + +- folder part only +- nome da pasta parcial + +- Other side's counterpart to %item_path% +- Contrapartida de %item_path% + +- Other side's counterpart to %item_folder% +- Contrapartida de %item_folder% + +Restore all hidden windows and warnings? +Restaurar todas as janelas e avisos escondidos? + +Leave as unresolved conflict +Deixar como conflito + +Replace +Substituir + +Move files and replace if existing +Mover ficheiros e substituir se existirem + +Time stamp +Selo temporal + +Append a timestamp to each file name +Juntar selo temporal a cada nome de ficheiro + +File +Ficheiro + +YYYY-MM-DD hhmmss +AAAA-MM-DD hhmmss + +Files +Ficheiros + +Items +Itens + +Percentage +Percentagem + +Cannot monitor directory %x. +Não é possível monitorizar o directório %x. + +Conversion error: +Erro de conversão: + +Cannot delete file %x. +Não é possível eliminar o ficheiro %x. + +The file is locked by another process: +O ficheiro está bloqueado por outro processo: + +Cannot move file %x to %y. +Não é possível mover o ficheiro %x para %y. + +Cannot delete directory %x. +Não é possível eliminar o directório %x. + +Cannot write file attributes of %x. +Não é possível escrever os atributos de %x. + +Cannot write modification time of %x. +Não é possível alterar a data de modificação a %x. + +Cannot read security context of %x. +Não é possível ler o contexto de segurança %x. + +Cannot write security context of %x. +Não é possível escrever o contexto de segurança %x. + +Cannot read permissions of %x. +Não é possível ler as permissões de %x. + +Cannot write permissions of %x. +Não é possível escrever as permissões de %x. + +Cannot create directory %x. +Não é possível criar o directório %x. + +Cannot create symbolic link %x. +Não é possível criar link simbólico %x. + +Cannot find system function %x. +Não é possível encontrar a função do sistema %x. + +Cannot copy file %x to %y. +Não é possível copiar o ficheiro %x para %y. + +Type of item %x is not supported: +Tipo de item %x não é suportado: + +Cannot resolve symbolic link %x. +Não é possível resolver o link simbólico %x. + +Cannot open directory %x. +Não é possível abrir o directório %x. + +Cannot enumerate directory %x. +Não é possível enumerar o directório %x. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +1 min +%x mins + + + +1 hour +%x hours + + +1 hora +%x horas + + + +1 day +%x days + + +1 dia +%x dias + + +Unable to register to receive system messages. +Não foi possível registar para receber mensagens do sistema. + +Cannot set privilege %x. +Não é possível definir o privilégio %x. + +Unable to suspend system sleep mode. +Não é possível retornar do modo de suspensão do sistema. + +Cannot change process I/O priorities. +Não é possível alterar as prioridades de E / S do processo. + +Unable to move %x to the recycle bin. +Não é possível mover %x para a reciclagem. + +Cannot determine final path for %x. +Não é possível determinar o caminho final de %x. + +Error Code %x: +Código de erro %x: + diff --git a/FreeFileSync/Build/Languages/portuguese_br.lng b/FreeFileSync/Build/Languages/portuguese_br.lng new file mode 100644 index 00000000..f58606f8 --- /dev/null +++ b/FreeFileSync/Build/Languages/portuguese_br.lng @@ -0,0 +1,1517 @@ +
    + Português (BR) + Edison Aranha + pt_BR + flag_brazil.png + 2 + n == 1 ? 0 : 1 +
    + +Both sides have changed since last synchronization. +Ambos os lados foram alterados desde a última sincronização. + +Cannot determine sync-direction: +Não foi possível determinar a direção de sincronização: + +No change since last synchronization. +Nenhuma mudança desde a última sincronização. + +The database entry is not in sync considering current settings. +A entrada de banco de dados não está em sincronia considerando as configurações atuais. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Configurando direções padrões de sincronização: Arquivos antigos serão substituídos por arquivos mais novos. + +Checking recycle bin availability for folder %x... +Verificando a disponibilidade da lixeira para a pasta %x... + +Moving file %x to the recycle bin +Movendo arquivo %x para a Lixeira + +Moving folder %x to the recycle bin +Movendo pasta %x para a Lixeira + +Moving symbolic link %x to the recycle bin +Movendo link simbólico %x para a Lixeira + +Deleting file %x +Apagando arquivo %x + +Deleting folder %x +Apagando pasta %x + +Deleting symbolic link %x +Apagando link simbólico %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +A Lixeira não está disponível para as seguintes pastas. Os arquivos serão apagados permanentemente: + +An exception occurred +Ocorreu uma exceção + +A directory path is expected after %x. +Um caminho de diretório é esperado após %x. + +Syntax error +Erro de sintaxe + +Cannot open file %x. +Não foi possível abrir o arquivo %x. + +File %x does not contain a valid configuration. +O arquivo %x não contém uma configuração válida. + +Unequal number of left and right directories specified. +Número de diretórios especificados na esquerda e na direita são diferentes. + +The config file must not contain settings at directory pair level when directories are set via command line. +O arquivo de configuração não deve conter configurações de diretórios quando os diretórios são definidos via linha de comando. + +Directories cannot be set for more than one configuration file. +Os diretórios não podem ser definidos para mais de um arquivo de configuração. + +Command line +Linha de comando + +Syntax: +Sintaxe: + +config files +arquivos de configuração + +directory +diretório + +Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. +Qualquer número de arquivos FreeFileSync .ffs e/ou de tarefa em lote .ffs_batch. + +Any number of alternative directories for at most one config file. +Qualquer número de alternativas de diretórios para pelo menos um arquivo de configuração. + +A folder input field is empty. +Um campo de entrada de pasta está vazio. + +The corresponding folder will be considered as empty. +A pasta correspondente será considerada como vazia. + +Cannot find the following folders: +Não foi possível localizar as seguintes pastas: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Você pode ignorar este erro para considerar cada pasta como vazia. As pastas serão criadas automaticamentes durante a sincronização. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +As seguintes pastas tem caminhos dependentes. Cuidado na hora de estabelecer as regras de sincronização: + +File %x has an invalid date. +O arquivo %x tem uma data inválida. + +Date: +Data: + +Files %x have the same date but a different size. +Os arquivos %x têm a mesma data mas tamanhos diferentes. + +Size: +Tamanho: + +Items differ in attributes only +Os itens diferem apenas nos atributos + +Resolving symbolic link %x +Resolvendo link simbólico %x + +Comparing content of files %x +Comparando conteúdo do arquivo %x + +Generating file list... +Gerando lista de arquivos... + +Starting comparison +Iniciando comparação + +Calculating sync directions... +Calculando as direções de sincronização... + +Out of memory. +Falta de memória. + +Item exists on left side only +Item existe apenas no lado esquerdo + +Item exists on right side only +Item existe apenas no lado direito + +Left side is newer +Lado esquerdo é novo + +Right side is newer +Lado direito é novo + +Items have different content +Itens têm conteúdos diferentes + +Both sides are equal +Ambos os lados são iguais + +Conflict/item cannot be categorized +Conflito/item não pode ser categorizado + +Copy new item to left +Copiar novo item para a esquerda + +Copy new item to right +Copiar novo item para a direita + +Delete left item +Apagar item à esquerda + +Delete right item +Apagar item à direita + +Move file on left +Mover arquivo à esquerda + +Move file on right +Mover arquivo à direita + +Overwrite left item +Sobrescrever item à esquerda + +Overwrite right item +Sobrescrever item à direita + +Do nothing +Não fazer nada + +Update attributes on left +Atualizar atributos à esquerda + +Update attributes on right +Atualizar atributos à direita + +Database file %x is incompatible. +O arquivo de banco de dados %x é incompatível. + +Initial synchronization: +Sincronização inicial: + +Database file %x does not yet exist. +O arquivo de banco de dados %x ainda não existe. + +Database file is corrupt: +O arquivo de banco de dados está corrompido + +Cannot write file %x. +Não foi possível escrever o arquivo %x. + +Cannot read file %x. +Não foi possível ler o arquivo %x. + +Database files do not share a common session. +Os arquivos de banco de dados são de sessões diferentes. + +Searching for folder %x... +Buscando pela pasta %x... + +Cannot read file attributes of %x. +Não foi possível ler os atributos do arquivo %x. + +Cannot get process information. +Não foi possível obter as informações do processo. + +Waiting while directory is locked (%x)... +Esperando enquanto o diretório é bloqueado (%x)... + + +1 sec +%x sec + + +1 seg +%x segs + + +Creating file %x +Criando arquivo %x + +Items processed: +Elementos processados: + +Items remaining: +Elementos restantes: + +Total time: +Tempo total: + + +1 byte +%x bytes + + +1 byte +%x bytes + + +%x MB +%x MB + +%x KB +%x kB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Erro analisando o arquivo %x, linha %y, coluna %z. + +Cannot set directory lock for %x. +Não foi possível travar o diretório %x. + +Scanning: +Pesquisando: + + +1 thread +%x threads + + +1 thread +%x threads + + +Encoding extended time information: %x +Codificando informações adicionais de tempo: %x + +/sec +/seg + +%x items/sec +%x itens/seg + +Configuration file %x loaded partially only. +O arquivo de configuração %x foi carregado parcialmente. + +Show in Explorer +Mostrar no Explorer + +Open with default application +Abrir com aplicativo padrão + +Browse directory +Procurar diretório + +Cannot access the Volume Shadow Copy Service. +Não foi possível acessar o Serviço de Cópia de Sombra de Volume. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Por favor, utilize a versão 64 bits do FreeFileSync para criar cópias de sombra neste sistema. + +Cannot load file %x. +Não foi possível carregar o aquivo %x. + +Cannot determine volume name for %x. +Não foi possível determinar o nome do volume para %x. + +Volume name %x is not part of file path %y. +O nome do volume %x não faz parte do caminho do arquivo %y. + +Stop requested: Waiting for current operation to finish... +Interromper solicitado: Aguardando operação ser finalizada... + +Unable to create timestamp for versioning: +Erro ao criar estampa de tempo para controle de versão: + +Cannot read the following XML elements: +Não foi possível ler os seguintes elementos XML: + +&Open... +&Abrir... + +Save &as... +Salvar &como... + +&Quit +Sai&r + +&Program +&Programa + +&View help +&Ver ajuda + +&About +&Sobre + +&Help +A&juda + +Usage: +Uso: + +1. Select folders to watch. +1. Selecione as pastas para monitorar. + +2. Enter a command line. +2. Entre uma linha de comando. + +3. Press 'Start'. +3. Pressione 'Iniciar'. + +To get started just import a .ffs_batch file. +Para iniciar importe um arquivo .ffs_batch. + +Folders to watch: +Pastas para comparar + +Add folder +Adicionar pasta + +Remove folder +Remover pasta + +Browse +Procurar + +Select a folder +Selecionar uma pasta + +Idle time (in seconds): +Tempo de espera (em segundos): + +Idle time between last detected change and execution of command +Tempo de espera entre última mudança detectada e execução do comando + +Command line: +Linha de comando: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +O comando é disparado se: +- arquivos ou subpastas são alterados +- novas pastas aparecem (p. ex. Uma pendrive é inserida) + + +&Start +&Iniciar + +About +Sobre + +Build: %x +Versão: %x + +All files +Todos os arquivos + +Automated Synchronization +Sincronização Automatizada + +Directory monitoring active +Monitoramento de diretórios ativado + +Waiting until all directories are available... +Aguardando até todos os diretórios estarem disponíveis... + +Error +Erro + +&Restore +&Restaurar + +&Show error +&Mostrar erro + +&Exit +&Sair + +Incorrect command line: +Linha de comando incorreta: + +&Retry +&Tentar Novamente + +File content +Conteúdo do arquivo + +File time and size +Data e tamanho do arquivo + +Two way +Dois sentidos + +Mirror +Espelhar + +Update +Atualizar + +Custom +Personalizado + +Multiple... +Múltiplo... + +Moving file %x to %y +Movendo arquivo %x para %y + +Moving folder %x to %y +Movendo pasta %x para %y + +Moving symbolic link %x to %y +Movendo link simbólico %x para %y + +Removing old versions... +Removendo versões antigas... + +Creating symbolic link %x +Criando link simbólico %x + +Creating folder %x +Criando pasta %x + +Overwriting file %x +Substituindo arquivo %x + +Overwriting symbolic link %x +Substituindo link simbólico %x + +Verifying file %x +Verificando arquivo %x + +Updating attributes of %x +Atualizando atributos de %x + +Cannot find %x. +Não foi possível localizar %x. + +Target folder %x already existing. +Pasta de destino %x já existe. + +Target folder input field must not be empty. +Campo de entrada da pasta de destino não pode ficar vazio. + +Please enter a target folder for versioning. +Por favor, entre com uma pasta destino para controle de versões. + +Source folder %x not found. +Pasta de origem %x não foi encontrada. + +The following items have unresolved conflicts and will not be synchronized: +Os seguintes itens possuem conflitos não resolvidos e não serão sincronizados: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +As seguintes pastas são significativamente diferentes. Tenha certeza que está comparando as pastas corretas para sincronização. + +Not enough free disk space available in: +Espaço em disco insuficiente em: + +Required: +Necessário: + +Available: +Disponível: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Uma pasta que é parte de múltiplos pares de pasta será modificada. Por favor, revise as configurações de sincronização. + +Synchronizing folder pair: +Sincronizando par de pastas: + +Generating database... +Gerando banco de dados... + +Creating a Volume Shadow Copy for %x... +Criando uma Cópia de Sombra de Volume para %x... + +Data verification error: %x and %y have different content. +Erro na verificação dos dados: %x e %y têm conteúdo diferente. + +job name +nome da tarefa + +Synchronization stopped +Sincronização interrompida + +Synchronization completed with errors +Sincronização finalizada com erros + +Synchronization completed with warnings +Sincronização finalizada com avisos + +Nothing to synchronize +Nada para sincronizar + +Synchronization completed successfully +Sincronização finalizada com sucesso + +Saving log file %x... +Salvando arquivo de log %x... + +You can switch to FreeFileSync's main window to resolve this issue. +Você pode alternar para a janela principal do FreeFileSync para resolver este problema. + +&Don't show this warning again +&Não mostrar este aviso novamente + +&Ignore +&Ignorar + +&Switch +&Alterar + +Switching to FreeFileSync's main window +Mudando para a janela principal do FreeFileSync + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + +Nova tentativa autompatica em 1 segundo... +Nova tentativa autompatica em %x segundos... + + +&Ignore subsequent errors +&Ignorar erros subsequentes + +Retrying operation... + + +Serious Error +Erro Grave + +Check for Program Updates +Verificar se existem atualizações + +A new version of FreeFileSync is available: +Uma nova versão do FreeFileSync está disponível: + +Download now? +Baixar agora? + +&Download +&Baixar + +FreeFileSync is up to date. +FreeFileSync está atualizado. + +Unable to connect to sourceforge.net. +Não foi possível conectar a sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Não foi possível encontrar a versão atual do FreeFileSync online. Deseja verificar manualmente? + +&Check + + +Symlink +Link Simbólico + +Folder +Pasta + +Full path +Caminho completo + +Name +Nome + +Relative path +Caminho relativo + +Base folder +Pasta base + +Size +Tamanho + +Date +Data + +Extension +Extensão + +Category +Categoria + +Action +Ação + +Drag && drop +Arrastar && Soltar + +Close progress dialog +Fechar indicador de progresso + +Standby +Em espera + +Log off +Fazer logoff + +Shut down +Desligar + +Hibernate +Hibernar + +Alternate comparison settings +Configurações alternativas de comparação + +Alternate synchronization settings +Configurações alternativas de sincronização + +Local filter +Filtro local + +Active +Ativo + +None +Nenhum + +Remove alternate settings +Remover configurações alternativas + +Clear filter settings +Limpar configurações do filtro + +Copy +Copiar + +Paste +Colar + +Alternate Comparison Settings +Configurações Alternativas de Comparação + +Alternate Synchronization Settings +Configurações Alternativas de Sincronização + +Local Filter +Filtro Local + +&New +&Novo + +&Save +&Salvar + +Save as &batch job... +Salvar como &tarefa em lote... + +1. &Compare +1. C&omparar + +2. &Synchronize +2. S&incronizar + +&Global settings +&Configurações + +&Language +&Idioma + +&Find... +&Localizar... + +&Export file list... +&Exportar lista de arquivos... + +&Tools +&Ferramentas + +&Check now +&Verificar agora + +Check &automatically once a week +Verificar &automaticamente uma vez por semana + +&Check for new version +&Verificar se existe nova versão + +Compare +Comparar + +Cancel +Cancelar + +Synchronize +Sincronizar + +Add folder pair +Adicionar um par de pastas + +Remove folder pair +Remover o par de pastas + +Swap sides +Inverter lados + +Close search bar +Fechar barra de localização + +Find: +Localizar: + +Match case +Diferenciar maiúsculas e minúsculas + +Save as batch job +Salvar como tarefa em lote + +Hide excluded items +Ocultar itens excluídos + +Show filtered or temporarily excluded files +Mostra arquivos que foram filtrados ou excluídos temporariamente + +Number of files and folders that will be created +Número de arquivos e pastas que serão criados + +Number of files that will be overwritten +Número de arquivos que serão substituídos + +Number of files and folders that will be deleted +Número de arquivos e pastas que serão apagados + +Total bytes to copy +Bytes totais a serem copiados + +Select a variant: +Selecione uma variante: + +Identify equal files by comparing modification time and size. +Identifica arquivos iguais comparando modificações na data e no tamanho. + +Identify equal files by comparing the file content. +Identifica arquivos iguais comparando o conteúdo do arquivo. + +Symbolic links: +Links simbólicos: + +More information +Mais informação + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Identifica e propaga as mudanças em ambos os lados. Arquivos e pastas apagados e/ou movidos e conflitos são detectados automaticamente usando um banco de dados. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Cria uma cópia espelho da pasta da esquerda que é exatamente igual à pasta da esquerda após a sincronização. + +Copy new and updated files to the right folder. +Copia arquivos novos ou atualizados para a pasta da direita. + +Configure your own synchronization rules. +Configure as suas próprias regras de sincronização. + +Detect moved files +Detectar arquivos movidos + +Requires database files. Not supported by all file systems. +Requer um arquivo de banco de dados. Não é suportado por todos os sistemas de arquivos. + +Delete files: +Apagar arquivos: + +Permanent +Permanente + +Delete or overwrite files permanently +Apaga ou substitui os arquivos permanentemente + +Recycle bin +Lixeira + +Back up deleted and overwritten files in the recycle bin +Faz uma cópia dos arquivos apagados ou substituídos na Lixeira + +Versioning +Controle de versões + +Move files to a user-defined folder +Move os arquivos para uma pasta definida pelo usuário + +Naming convention: +Convenção de nomenclatura: + +Show examples +Mostrar exemplos + +Handle errors: +Tratamento de erros: + +Ignore +Ignorar + +Hide all error and warning messages +Oculta todas as mensagens de erros e avisos + +Pop-up +Pop-up + +Show pop-up on errors or warnings +Mostra pop-up em caso de erros ou avisos + +On completion: +Ao finalizar: + +Start synchronization now? +Iniciar sincronização agora? + +Variant: +Variante: + +Statistics +Estatísticas + +&Don't show this dialog again +&Não mostrar este diálogo novamente + +Items found: +Elementos encontrados: + +Speed: +Velocidade: + +Time remaining: +Tempo restante: + +Time elapsed: +Tempo decorrido: + +Synchronizing... +Sincronizando... + +Minimize to notification area +Minimizar para a área de notificação + +Close +Fechar + +&Pause +&Pausar + +Stop +Interromper + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Cria um arquivo de tarefa em lote para sincronização desatendida. Para iniciar, dê um clique duplo neste arquivo ou agende no agendador de tarefas: %x + +Stop synchronization at first error +Interromper sincronização ao primeiro erro + +Show progress dialog +Mostrar indicador de progresso + +Save log: +Salvar arquivo de log: + +Limit: +Limite: + +Limit maximum number of log files +Limita número máximo de arquivos de log + +How can I schedule a batch job? +Como posso agendar uma tarefa em lote? + +&Recycle bin +&Lixeira + +Delete on both sides +Apagar em ambos os lados + +Delete on both sides even if the file is selected on one side only +Apaga em ambos os lados mesmo se o arquivo está selecionado só em um lado + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Selecione regras de filtro para exlcuir certos arquivos da sincrinização. Entre com os caminhos relativos dos pares de pastas correspondentes. + +Include: +Incluir: + +Exclude: +Excluir: + +Time span: +Intervalo de tempo: + +File size: +Tamanho do arquivo: + +Minimum: +Mínimo: + +Maximum: +Máximo: + +&Clear +&Limpar + +The following settings are used for all synchronization jobs. +As seguintes configurações são utilizadas para todas as tarefas de sincronização. + +Fail-safe file copy +Cópia de arquivos a prova de falhas + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Copia para um arquivo temporário (*.ffs_tmp) antes de substituir o destino. +Isto garante um estado consistente mesmo em caso de erro grave. + + +(recommended) +(recomendado) + +Copy locked files +Copiar arquivos bloqueados (em uso) + +Copy shared or locked files using the Volume Shadow Copy Service. +Copia arquivos compartilhados ou bloqueados usando o Serviço de Cópia de Sombra de Volume. + +(requires administrator rights) +(requer direitos de administrador) + +Copy file access permissions +Copiar permissões de acesso aos arquivos + +Transfer file and folder permissions. +Transfere permissões de arquivos e pastas. + +Automatic retry on error: +Nova tentativa automática em caso de erro: + +Retry count: +Número de tentativas: + +Delay (in seconds): +Atraso (em segundos): + +Customize context menu: +Personalizar menu de contexto: + +Description +Descrição + +Restore hidden windows +Restaurar janelas ocultadas + +&Default +&Config. Padrão + +Source code written in C++ using: +Código-fonte escrito em C++ utilizando: + +If you like FreeFileSync +Se você gosta do FreeFileSync + +Donate with PayPal +Doar usando PayPal + +Feedback and suggestions are welcome +Críticas e sugestões são bem-vindas + +Homepage +Homepage + +Email +E-mail + +Published under the GNU General Public License +Publicado sobre a GNU General Public License + +Many thanks for localization: +Pela tradução, um agradecimento a: + +Save as Batch Job +Salvar como Tarefa em Lote + +Delete Items +Apagar Itens + +Global Settings +Configurações + +Select Time Span +Selecionar Intervalo de Tempo + +Folder Pairs +Pares de Pastas + +Find +Localizar + +Overview +Parâmetros + +Configuration +Configuração + +Main Bar +Barra Principal + +Filter Files +Filtrar Arquivos + +Select View +Selecionar Visualização + +Open... +Abrir... + +Save +Salvar + +Compare both sides +Comparar os dois lados + +Comparison settings +Parâmetros de comparação + +Synchronization settings +Parâmetros de sincronização + +Start synchronization +Iniciar a sincronização + +Confirm +Confirmar + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + +Você realmente deseja executar o comando %y para um item? +Você realmente deseja executar o comando %y para %x itens? + + +&Execute +&Executar + + +1 directory +%x directories + + +1 diretório +%x diretórios + + + +1 file +%x files + + +1 arquivo +%x arquivos + + + +%y of 1 row in view +%y of %x rows in view + + +%y de 1 linha +%y de %x linhas + + +Set direction: +Configurar direção + +multiple selection +seleção múltipla + +Include via filter: +Incluir via filtro: + +Exclude via filter: +Excluir via filtro: + +Exclude temporarily +Excluir temporariamente + +Include temporarily +Incluir temporariamente + +Delete +Apagar + +Include all +Incluir todos + +Exclude all +Excluir todos + +Show icons: +Mostrar ícones: + +Small +Pequeno + +Medium +Médio + +Large +Grande + +Select time span... +Selecionar intervalo de tempo... + +Default view +Visualização padrão + +Show "%x" +Mostrar "%x" + +Last session +Última sessão + +Folder Comparison and Synchronization +Comparação e Sincronização de Pastas + +Configuration saved +Configuração salva + +FreeFileSync batch +Tarefa em lote do FreeFileSync + +Do you want to save changes to %x? +Gostaria de salvar as alterações para %x? + +Never save &changes +Nunca salvar as &modificações + +Do&n't save +&Não salvar + +Filter +Filtro + +Show files that exist on left side only +Mostrar arquivos que existem somente à esquerda + +Show files that exist on right side only +Mostrar arquivos que existem somente à direita + +Show files that are newer on left +Mostrar arquivos que são mais recentes à esquerda + +Show files that are newer on right +Mostrar arquivos que são mais recentes à direita + +Show files that are equal +Mostrar arquivos que são iguais + +Show files that are different +Mostrar arquivos que são diferentes + +Show conflicts +Mostrar conflitos + +Show files that will be created on the left side +Mostrar arquivos que serão criados no lado esquerdo + +Show files that will be created on the right side +Mostrar arquivos que serão criados no lado direito + +Show files that will be deleted on the left side +Mostrar arquivos que serão apagados no lado esquerdo + +Show files that will be deleted on the right side +Mostrar arquivos que serão apagados no lado direito + +Show files that will be overwritten on left side +Mostrar arquivos que serão substituídos no lado esquerdo + +Show files that will be overwritten on right side +Mostrar arquivos que serão substituídos no lado direito + +Show files that won't be copied +Mostrar arquivos que não serão copiados + +Set as default +Definir como padrão + +All folders are in sync +Todas as pastas estão sincronizadas + +Synchronization Settings +Configurações de Sincronização + +Comparison Settings +Configurações de Comparação + +Cannot find %x +Não foi possível localizar %x + +Comma-separated values +Valores separados por vírgula + +File list exported +Lista de arquivos exportada + +Searching for program updates... +Procurando atualizações do programa... + +Scanning... +Pesquisando... + +Comparing content... +Comparando conteúdo... + +Info +Informações + +Warning +Avisos + +Paused +Pausado + +Initializing... +Inicializando... + +Stopped +Interrompido + +Completed +Finalizado + +&Continue +&Continuar + +Log +Log + +Today +Hoje + +This week +Esta semana + +This month +Este mês + +This year +Este ano + +Last x days +Últimos x dias + +Byte +Byte + +KB +kB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Você realmente deseja mover o seguinte item para a Lixeira? +Você realmente deseja mover os seguintes %x itens para a Lixeira? + + +Move +Mover + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Você realmente deseja apagar o seguinte item? +Você realmente deseja apagar os seguintes %x itens? + + +Exclude +Excluir + +Direct +Direto + +Follow +Seguir + +Copy NTFS permissions +Copiar permissões NTFS + +Integrate external applications into context menu. The following macros are available: +Integrar aplicações externas no menu de contexto. As seguintes macros estão disponíveis: + +- full file or folder name +- nome completo do arquivo ou pasta + +- folder part only +- apenas parte da pasta + +- Other side's counterpart to %item_path% +- Contrapartida do outro lado para %item_path% + +- Other side's counterpart to %item_folder% +- Contrapartida do outro lado para %item_folder% + +Restore all hidden windows and warnings? +Restaurar todos as janelas e avisos ocultados? + +Leave as unresolved conflict +Deixar como conflito não resolvido + +Replace +Substituir + +Move files and replace if existing +Move arquivos e substitui se existente + +Time stamp +Estampa de tempo + +Append a timestamp to each file name +Atribuir uma estampa de tempo para cada nome de arquivo + +File +Arquivo + +YYYY-MM-DD hhmmss +AAAA-MM-DD hhmmss + +Files +Arquivos + +Items +Itens + +Percentage +Percentual + +Cannot monitor directory %x. +Não foi possível monitorar o diretório %x. + +Conversion error: +Erro de conversão: + +Cannot delete file %x. +Não foi possível apagar o arquivo %x. + +The file is locked by another process: +O arquivo está bloqueado por outro processo: + +Cannot move file %x to %y. +Não foi possível mover o arquivo %x para %y. + +Cannot delete directory %x. +Não foi possível apagar o diretório %x. + +Cannot write file attributes of %x. +Não foi possível escrever os atributos de arquivo de %x. + +Cannot write modification time of %x. +Não foi possível escrever a data de modificação de %x. + +Cannot read security context of %x. +Não foi possível ler o contexto de segurança de %x. + +Cannot write security context of %x. +Não foi possível escrever o contexto de segurança de %x. + +Cannot read permissions of %x. +Não foi possível ler as permissões de %x. + +Cannot write permissions of %x. +Não foi possível escrever as permissões de %x. + +Cannot create directory %x. +Não foi possível criar o diretório %x. + +Cannot create symbolic link %x. +Não foi possível criar o link simbólico %x. + +Cannot find system function %x. +Não foi possível localizar a função de sistema %x. + +Cannot copy file %x to %y. +Não foi possível copiar o arquivo %x para %y. + +Type of item %x is not supported: +Tipo de item %x não é suportado: + +Cannot resolve symbolic link %x. +Não foi possível resolver o link simbólico %x. + +Cannot open directory %x. +Não foi possível abrir o diretório %x. + +Cannot enumerate directory %x. +Não foi possível enumerar o diretório %x. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +1 min +%x min + + + +1 hour +%x hours + + +1 hora +%x horas + + + +1 day +%x days + + +1 dia +%x dias + + +Unable to register to receive system messages. +Não foi possível registrar para receber mensagens do sistema. + +Cannot set privilege %x. +Não foi possível estabelecer o privilégio %x. + +Unable to suspend system sleep mode. +Não foi possível suspender o modo de espera do sistema. + +Cannot change process I/O priorities. +Não foi possível mudar a prioridade de E/S do processo. + +Unable to move %x to the recycle bin. +Não foi possível mover %x para a Lixeira. + +Cannot determine final path for %x. +Não foi possível determinar o caminho final para %x. + +Error Code %x: +Código do Erro %x: + diff --git a/FreeFileSync/Build/Languages/romanian.lng b/FreeFileSync/Build/Languages/romanian.lng new file mode 100644 index 00000000..10ca0b53 --- /dev/null +++ b/FreeFileSync/Build/Languages/romanian.lng @@ -0,0 +1,1524 @@ +
    + Română + Alexandru Bogdan Munteanu + ro_RO + flag_romania.png + 3 + n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2 +
    + +Both sides have changed since last synchronization. +Ambele părți s-au modificat de la ultima sincronizare. + +Cannot determine sync-direction: +Nu pot determina sensul de sincronizare: + +No change since last synchronization. +Nu sînt schimbări de la ultima sincronizare. + +The database entry is not in sync considering current settings. +Intrarea în baza de date nu e sincronizată, ținînd cont de setările curente. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Baza de date existentă va fi făcută compatibilă cu versiunea softului și apoi va fi setat sensul implicit de sincronizare: Filele vechi vor fi suprascrise de cele noi. + +Checking recycle bin availability for folder %x... +Verific dacă Reciclatorul e disponibil pentru dosarul %x... + +Moving file %x to the recycle bin +Mut fila %x în Reciclator + +Moving folder %x to the recycle bin +Mut dosarul %x în Reciclator + +Moving symbolic link %x to the recycle bin +Mut legătura simbolică %x în Reciclator + +Deleting file %x +Șterg fila %x + +Deleting folder %x +Șterg dosarul %x + +Deleting symbolic link %x +Șterg legătura simbolică %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +Reciclatorul nu este disponibil pentru dosarele următoare. Filele șterse de acolo vor dispărea în mod definitiv: + +An exception occurred +A apărut o excepție + +A directory path is expected after %x. +Este așteptată o cale de dosar după %x. + +Syntax error +Eroare de sintaxă + +Cannot open file %x. +Nu pot deschide fila %x. + +File %x does not contain a valid configuration. +Fila %x nu conține o configurație validă. + +Unequal number of left and right directories specified. +Numărul de dosare specificat pentru părțile stîngă și dreaptă este inegal. + +The config file must not contain settings at directory pair level when directories are set via command line. +Fila de configurare nu trebuie să conțină setări la nivelul perechii de dosare, cînd dosarele sînt setate prin linia de comandă. + +Directories cannot be set for more than one configuration file. +Dosarele nu pot fi setate pentru mai mult de o singură filă de configurare. + +Command line +Linie de comandă + +Syntax: +Sintaxă: + +config files +file de configurare + +directory +dosar + +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 directories for at most one config file. +Orice număr de dosare alternative pentru cel mult o filă de configurare. + +A folder input field is empty. +Un cîmp de introducere a dosarului este gol. + +The corresponding folder will be considered as empty. +Dosarul corespondent va fi considerat ca fiind gol. + +Cannot find the following folders: +Nu pot găsi dosarele următoare: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Poți ignora această eroare dacă vrei ca ambele dosare să fie considerate goale. Dosarele vor fi apoi create automat în timpul sincronizării. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +Dosarele următoare au căi dependente. Atenție la setarea regulilor de sincronizare: + +File %x has an invalid date. +Fila %x are o dată nevalidă. + +Date: +Dată: + +Files %x have the same date but a different size. +Filele %x au dată identică, dar mărime diferită. + +Size: +Mărime: + +Items differ in attributes only +Elementele diferă doar prin atributele lor + +Resolving symbolic link %x +Rezolv legătura simbolică %x + +Comparing content of files %x +Compar conținutul filelor %x + +Generating file list... +Generez lista de file... + +Starting comparison +Pornesc compararea + +Calculating sync directions... +Calculez acțiunile de sincronizare... + +Out of memory. +Memorie epuizată. + +Item exists on left side only +Elementul există doar în partea stîngă + +Item exists on right side only +Elementul există doar în partea dreaptă + +Left side is newer +Elementul din partea stîngă e mai nou + +Right side is newer +Elementul din partea dreaptă e mai nou + +Items have different content +Elementele au conținut diferit + +Both sides are equal +Ambele părți sînt identice + +Conflict/item cannot be categorized +Conflictul/elementul nu poate fi inclus într-o anumită categorie + +Copy new item to left +Copiază elementul nou în stînga + +Copy new item to right +Copiază elementul nou în dreapta + +Delete left item +Șterge elementul stîng + +Delete right item +Șterge elementul drept + +Move file on left +Mută fila în stînga + +Move file on right +Mută fila în dreapta + +Overwrite left item +Suprascrie elementul stîng cu cel drept + +Overwrite right item +Suprascrie elementul drept cu cel stîng + +Do nothing +Nici o acțiune + +Update attributes on left +Actualizează atributele în partea stîngă + +Update attributes on right +Actualizează atributele în partea dreaptă + +Database file %x is incompatible. +Fila cu baza de date %x este incompatibilă. + +Initial synchronization: +Sincronizare inițială: + +Database file %x does not yet exist. +Fila cu baza de date %x nu există încă. + +Database file is corrupt: +Fila bazei de date este stricată (coruptă) + +Cannot write file %x. +Nu pot scrie fila %x. + +Cannot read file %x. +Nu pot citi fila %x. + +Database files do not share a common session. +Filele cu baze de date nu partajează o sesiune comună. + +Searching for folder %x... +Caut dosarul %x... + +Cannot read file attributes of %x. +Nu pot citi atributele filei %x. + +Cannot get process information. +Nu pot obține informații despre proces. + +Waiting while directory is locked (%x)... +Aștept ca dosarul să fie zăvorît (%x)... + + +1 sec +%x sec + + +1 sec +%x sec +%x de sec + + +Creating file %x +Creez fila %x + +Items processed: +Elemente Procesate: + +Items remaining: +Elemente Rămase: + +Total time: +Timp Total: + + +1 byte +%x bytes + + +1 bait +%x baiți +%x de baiți + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Eroare la parsarea filei %x, rîndul %y, coloana %z. + +Cannot set directory lock for %x. +Nu pot face zăvorîrea dosarului %x. + +Scanning: +Scanez: + + +1 thread +%x threads + + +1 fir +%x fire +%x de fire + + +Encoding extended time information: %x +Codez informațiile extinse despre timp: %x + +/sec +/sec + +%x items/sec +%x itemuri/sec + +Configuration file %x loaded partially only. +Fila de configurație %x a fost deschisă doar parțial. + +Show in Explorer +Arată în Exploratorul de File + +Open with default application +Deschide cu Aplicația Implicită + +Browse directory +Explorează Dosarul + +Cannot access the Volume Shadow Copy Service. +Nu pot accesa Serviciul de Conservare a Volumelor [Volume Shadow Copy]. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Folosește versiunea pe 64-biți a FreeFileSync pentru a crea conservări (copii de rezervă) pe acest sistem. + +Cannot load file %x. +Nu pot deschide fila %x. + +Cannot determine volume name for %x. +Nu pot determina numele volumului pentru %x. + +Volume name %x is not part of file path %y. +Numele volumului %x nu face parte din calea filei %y. + +Stop requested: Waiting for current operation to finish... +Oprire solicitată: Aștept terminarea operației în curs... + +Unable to create timestamp for versioning: +Nu pot crea marcajul de timp pentru versionare: + +Cannot read the following XML elements: +Nu pot citi următoarele elemente XML: + +&Open... +&Deschide... + +Save &as... +Salvează c&a... + +&Quit +&Ieși + +&Program +&Program + +&View help +&Cuprins + +&About +&Despre + +&Help +&Ajutor + +Usage: +Utilizare: + +1. Select folders to watch. +1. Selectează dosarele de monitorizat. + +2. Enter a command line. +2. Scrie calea. + +3. Press 'Start'. +3. Apasă 'Pornește'. + +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: + +Add folder +Adaugă Dosar + +Remove folder +Înlătură Dosarul + +Browse +Explorează + +Select a folder +Selectează un Dosar + +Idle time (in seconds): +Timp de inactivitate (în secunde): + +Idle time between last detected change and execution of command +Timpul de inactivitate între ultima schimbare detectată și executarea comenzii + +Command line: +Linie de comandă: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +Comanda este declanșată dacă: +- sînt modificate filele sau subdosarele +- apar dosare noi (de ex. prin atașarea unui dispozitiv USB) + + +&Start +&Pornește + +About +Despre + +Build: %x +Compilația: %x + +All files +Toate Filele + +Automated Synchronization +Sincronizare Automată + +Directory monitoring active +Monitorizarea dosarelor este activă + +Waiting until all directories are available... +Aștept pînă cînd toate dosarele vor fi disponibile... + +Error +Eroare + +&Restore +&Restaurează + +&Show error +A&rată eroarea + +&Exit +&Ieși + +Incorrect command line: +Linie de comandă incorectă: + +&Retry +&Reîncearcă + +File content +Conținutul Filelor + +File time and size +Timpul și Mărimea Filelor + +Two way +Ambele Sensuri + +Mirror +Clonare + +Update +Actualizare + +Custom +Personalizat + +Multiple... +Multiplu... + +Moving file %x to %y +Mut fila %x în %y + +Moving folder %x to %y +Mut dosarul %x în %y + +Moving symbolic link %x to %y +Mut legătura simbolică %x în %y + +Removing old versions... +Înlătur versiunile vechi... + +Creating symbolic link %x +Creez legătura simbolică %x + +Creating folder %x +Creez dosarul %x + +Overwriting file %x +Suprascriu fila %x + +Overwriting symbolic link %x +Suprascriu legătura simbolică %x + +Verifying file %x +Verific fila %x + +Updating attributes of %x +Actualizez atributele lui %x + +Cannot find %x. +Nu pot găsi %x. + +Target folder %x already existing. +Dosarul țintă %x există deja. + +Target folder input field must not be empty. +Cîmpul de introducere a dosarului țintă nu trebuie să fie gol. + +Please enter a target folder for versioning. +Introdu dosarul țintă pentru versionare. + +Source folder %x not found. +Dosarul sursă %x nu a fost găsit. + +The following items have unresolved conflicts and will not be synchronized: +Există conflicte nerezolvate la elementele listate mai jos, deci ele nu vor fi sincronizate: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +Dosarele următoare diferă foarte mult. Asigură-te că ai potrivit pentru sincronizare dosarele corecte. + +Not enough free disk space available in: +Spațiu de stocare insuficient pe: + +Required: +Necesar: + +Available: +Disponibil: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Va fi modificat un dosar care face parte din mai multe perechi de dosare. Reverifică setările de sincronizare. + +Synchronizing folder pair: +Sincronizez perechea de dosare: + +Generating database... +Generez baza de date... + +Creating a Volume Shadow Copy for %x... +Creez o Conservare a Volumului [Volume Shadow Copy] pentru %x... + +Data verification error: %x and %y have different content. +Eroare la verificarea datelor: %x și %y au conținut diferit. + +job name +numele sarcinii + +Synchronization stopped +Sincronizare oprită + +Synchronization completed with errors +Sincronizare terminată cu erori + +Synchronization completed with warnings +Sincronizare realizată, dar cu avertismente + +Nothing to synchronize +Nu e nimic de sincronizat + +Synchronization completed successfully +Sincronizare realizată cu succes + +Saving log file %x... +Salvez fila jurnal %x... + +You can switch to FreeFileSync's main window to resolve this issue. +Poți comuta la fereastra principală FreeFileSync pentru a rezolva această problemă. + +&Don't show this warning again +&Nu arăta din nou această atenționare + +&Ignore +&OK + +&Switch +&Comută + +Switching to FreeFileSync's main window +Comut la fereastra principală FreeFileSync + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + + + +&Ignore subsequent errors +&Ignoră erorile ulterioare + +Retrying operation... + + +Serious Error +Eroare Serioasă + +Check for Program Updates +Caută Actualizări ale Programului + +A new version of FreeFileSync is available: +Este disponibilă o versiune nouă a softului: + +Download now? +Vrei s-o descarci acum ? + +&Download +&Descarcă + +FreeFileSync is up to date. +Ai deja ultima versiune a softului. + +Unable to connect to sourceforge.net. +Conectarea la situl sourceforge.net nu poate fi realizată. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Nu pot afla numărul versiunii FreeFileSync disponibile acum pe internet. Vrei să-l cauți manual? + +&Check + + +Symlink +Simlegătură + +Folder +Dosar + +Full path +Cale Completă + +Name +Nume + +Relative path +Cale Relativă + +Base folder +Dosar Bază (Rădăcină) + +Size +Mărime + +Date +Dată + +Extension +Extensie + +Category +Categorie + +Action +Acțiune + +Drag && drop +Trage și pune un dosar mai jos sau explorează către el + +Close progress dialog +Închide Fereastra cu Progresul Sincronizării + +Standby +Pune Calculatorul în Stare de Veghe [Stand-by] + +Log off +Deloghează Utilizatorul [Log off] + +Shut down +Închide Calculatorul [Shut down] + +Hibernate +Pune Calculatorul în Hibernare + +Alternate comparison settings +Setări alternative de comparare + +Alternate synchronization settings +Setări alternative de sincronizare + +Local filter +Filtru local + +Active +Activ + +None +Nimic + +Remove alternate settings +Înlătură setările alternative + +Clear filter settings +Curăță Setările Filtrului + +Copy +Copiază + +Paste +Lipește + +Alternate Comparison Settings +Setări Alternative de Comparare + +Alternate Synchronization Settings +Setări Alternative de Sincronizare + +Local Filter +Filtru Local + +&New +Configurație &Nouă + +&Save +&Salvează + +Save as &batch job... +Salvea&ză ca Sarcină Set... + +1. &Compare +1. &Compară + +2. &Synchronize +2. &Sincronizează + +&Global settings +&Setări Globale + +&Language +&Limbă + +&Find... +&Găsește... + +&Export file list... +&Exportă Lista de File... + +&Tools +&Unelte + +&Check now +&Caută Acum + +Check &automatically once a week +Caută &Automat în Fiecare Săptămînă + +&Check for new version +Caută &Versiune Nouă + +Compare +Compară + +Cancel +Anulează + +Synchronize +Sincronizează + +Add folder pair +Adaugă Pereche Nouă de Dosare + +Remove folder pair +Înlătură Perechea de Dosare + +Swap sides +Schimbă compartimentele stîng și drept între ele + +Close search bar +Închide bara de căutare + +Find: +Găsește: + +Match case +Potrivește cu caseta literelor (MAJ/min) + +Save as batch job +Salvează ca Sarcină Set + +Hide excluded items +Ascunde elementele excluse + +Show filtered or temporarily excluded files +Arată filele filtrate sau excluse temporar + +Number of files and folders that will be created +Numărul de file și dosare care vor fi create + +Number of files that will be overwritten +Numărul de file care vor fi suprascrise + +Number of files and folders that will be deleted +Numărul de file și dosare care vor fi șterse + +Total bytes to copy +Numărul total de baiți copiați + +Select a variant: +Selectează o variantă: + +Identify equal files by comparing modification time and size. +Filele identice sînt descoperite prin compararea timpului modificării și a mărimii. + +Identify equal files by comparing the file content. +Filele identice sînt descoperite prin compararea conținutului filelor. + +Symbolic links: +Legături Simbolice: + +More information +Mai multe informații + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Identificare și propagare a modificărilor din ambele părți. Ștergerile, renumirile și conflictele sînt detectate automat, folosind o bază de date. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Creează o conservare [backup] în oglindă a dosarului stîng care se potrivește exact cu dosarul drept după sincronizare. + +Copy new and updated files to the right folder. +Copiază filele noi și cele actualizate în dosarul drept. + +Configure your own synchronization rules. +Reguli de sincronizare definite de utilizator pentru fiecare situație. + +Detect moved files +Detectează filele mutate + +Requires database files. Not supported by all file systems. +Necesită file cu baze de date. Nu e suportat de toate sistemele de file. + +Delete files: +Ștergerea Filelor: + +Permanent +Definitivă + +Delete or overwrite files permanently +Filele sînt șterse sau suprascrise în mod definitiv, fără a mai putea fi recuperate + +Recycle bin + + +Back up deleted and overwritten files in the recycle bin +Conservă [back up] în Reciclator filele șterse sau suprascrise + +Versioning +Versionare + +Move files to a user-defined folder +Mută filele într-un dosar stabilit de utilizator + +Naming convention: +Convenție de numire: + +Show examples +Arată exemple + +Handle errors: +Gestionarea Erorilor: + +Ignore +Ignorare + +Hide all error and warning messages +Mesajele de eroare și de avertizare nu sînt arătate + +Pop-up +Popîc + +Show pop-up on errors or warnings +În caz de erori sau avertizări este arătată o fereastră popîc [popup] + +On completion: +La terminare: + +Start synchronization now? +Pornesc sincronizarea acum? + +Variant: +Variantă: + +Statistics +Statistici + +&Don't show this dialog again +Nu arăta acest &dialog din nou + +Items found: +Elemente Găsite: + +Speed: +Viteză: + +Time remaining: +Timp Rămas: + +Time elapsed: +Timp Scurs: + +Synchronizing... +Sincronizare Aflată în Curs... + +Minimize to notification area +Minimizează în aria de notificare (sertar) + +Close +Închide + +&Pause +&Pauzează + +Stop +Oprește + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Creează o filă set [batch file] pentru sincronizarea neasistată. Pentru efectuarea sincronizării, dublu-clichează această filă sau programează rularea ei folosind un Planificator de Sarcini [Task Scheduler]: %x + +Stop synchronization at first error +Oprește sincronizarea la prima eroare întîlnită + +Show progress dialog +Arată progresul sincronizării + +Save log: +Salvează jurnalul: + +Limit: +Limitare: + +Limit maximum number of log files +Limitează numărul maxim de file jurnal + +How can I schedule a batch job? +Cum pot planifica o sarcină set? + +&Recycle bin +&Reciclator + +Delete on both sides +Șterge din ambele părți + +Delete on both sides even if the file is selected on one side only +Șterge din ambele părți, chiar dacă fila e selecționată într-o singură parte + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Selectează regulile filtrului pentru a exclude anumite file de la sincronizare. Introdu căile filelor relative la perechea lor de dosare corespondentă. + +Include: +Incluse: + +Exclude: +Excluse: + +Time span: +Intervalul de Timp: + +File size: +Mărimea Filei: + +Minimum: +Minim: + +Maximum: +Maxim: + +&Clear +&Curăță + +The following settings are used for all synchronization jobs. +Setările următoare sînt folosite pentru toate sarcinile de sincronizare. + +Fail-safe file copy +Copiază filele în modul protejat la eșec [fail-safe] + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Copiază într-o filă temporară (*.ffs_tmp) înainte de suprascrierea filei țintă. +Aceasta garantează consecvența stării filelor chiar și în cazul apariției de erori serioase. + + +(recommended) +(recomandat) + +Copy locked files +Copiază filele partajate [shared] sau zăvorîte [locked] + +Copy shared or locked files using the Volume Shadow Copy Service. +Filele partajate sau zăvorîte sînt copiate folosind Serviciul de Conservare a Volumelor din Windows [Volume Shadow Copy]. + +(requires administrator rights) +(necesită drepturi de Administrator) + +Copy file access permissions +Copiază permisiunile de acces ale filelor + +Transfer file and folder permissions. +Permisiunile filelor și dosarelor sînt și ele transferate. + +Automatic retry on error: +Reîncearcă automat în caz de eroare: + +Retry count: +Numărul reîncercărilor: + +Delay (in seconds): +Întîrziere (în secunde): + +Customize context menu: +Personalizează meniul contextual: + +Description +Descriere + +Restore hidden windows +Restaurează ferestrele ascunse + +&Default +Coloanele &Implicite + +Source code written in C++ using: +Cod sursă scris în C++ folosind: + +If you like FreeFileSync +Donează pentru FreeFileSync + +Donate with PayPal +Donează prin PayPal + +Feedback and suggestions are welcome +Opiniile și sugestiile despre program sînt binevenite. + +Homepage +Situl Softului + +Email +Adresa Autorului + +Published under the GNU General Public License +Publicat sub licența GNU GPL + +Many thanks for localization: +Multe mulțumiri pentru localizare: + +Save as Batch Job +Salvează ca Sarcină Set + +Delete Items +Șterge Elementele + +Global Settings +Setări Globale + +Select Time Span +Selectează Intervalul de Timp + +Folder Pairs +Perechi de Dosare + +Find +Găsește + +Overview +Panoramă + +Configuration +Configurație + +Main Bar +Bara Principală + +Filter Files +Filtrează Filele + +Select View +Selectează Vederea + +Open... +Deschide... + +Save +Salvează + +Compare both sides +Compară Părțile Stîngă și Dreaptă + +Comparison settings +Setările Comparării + +Synchronization settings +Setările Sincronizării + +Start synchronization +Pornește Sincronizarea + +Confirm +Confirmă + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + + + +&Execute +&Execută + + +1 directory +%x directories + + +1 dosar +%x dosare +%x de dosare + + + +1 file +%x files + + +1 filă +%x file +%x de file + + + +%y of 1 row in view +%y of %x rows in view + + +%y din 1 rînd afișat +%y din %x rînduri afișate +%y din %x de rînduri afișate + + +Set direction: +Setează Acțiunea ca în Icoana Alăturată: + +multiple selection +selectare multiplă + +Include via filter: +Include prin Filtrul: + +Exclude via filter: +Exclude prin Filtrul: + +Exclude temporarily +Exclude Temporar + +Include temporarily +Include Temporar + +Delete +Șterge + +Include all +Include Tot + +Exclude all +Exclude Tot + +Show icons: +Arată Icoane: + +Small +Mici + +Medium +Medii + +Large +Mari + +Select time span... +Selectează intervalul de timp... + +Default view +Vedere Implicită + +Show "%x" +Arată "%x" + +Last session +Ultima Sesiune + +Folder Comparison and Synchronization +Comparare și Sincronizare de Dosare + +Configuration saved +Configurație salvată + +FreeFileSync batch +Set FreeFileSync + +Do you want to save changes to %x? +Vrei să salvezi modificările făcute la %x? + +Never save &changes +Nu salva ni&ciodată modificările + +Do&n't save +&Nu salva + +Filter +Filtru + +Show files that exist on left side only +Arată filele care există doar în stînga + +Show files that exist on right side only +Arată filele care există doar în dreapta + +Show files that are newer on left +Arată filele din stînga mai noi decît cele din dreapta + +Show files that are newer on right +Arată filele din dreapta mai noi decît cele din stînga + +Show files that are equal +Arată elementele (file/dosare) identice + +Show files that are different +Arată elementele (file/dosare) diferite + +Show conflicts +Arată conflictele + +Show files that will be created on the left side +Arată elementele (file/dosare) care vor fi create în stînga + +Show files that will be created on the right side +Arată elementele (file/dosare) care vor fi create în dreapta + +Show files that will be deleted on the left side +Arată elementele (file/dosare) care vor fi șterse în stînga + +Show files that will be deleted on the right side +Arată elementele (file/dosare) care vor fi șterse în dreapta + +Show files that will be overwritten on left side +Arată elementele (file/dosare) care vor fi suprascrise în stînga + +Show files that will be overwritten on right side +Arată elementele (file/dosare) care vor fi suprascrise în dreapta + +Show files that won't be copied +Arată elementele (file/dosare) care nu vor fi copiate + +Set as default +Setează ca implicit + +All folders are in sync +Toate dosarele sînt sincronizate + +Synchronization Settings +Setările Sincronizării + +Comparison Settings +Setările Comparării + +Cannot find %x +Nu pot găsi %x + +Comma-separated values +Valori separate prin virgulă + +File list exported +Lista de file a fost exportată + +Searching for program updates... +Caut actualizări ale programului... + +Scanning... +Scanez... + +Comparing content... +Compar conținutul... + +Info +Informații + +Warning +Atenție + +Paused +Sincronizare Pauzată + +Initializing... +Inițializez... + +Stopped +Oprită + +Completed +Sincronizare Terminată + +&Continue +&Continuă + +Log +Jurnal + +Today +Azi + +This week +Săptămîna asta + +This month +Luna asta + +This year +Anul ăsta + +Last x days +Ultimele x zile + +Byte +Baiți + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Sigur vrei să muți în Reciclator elementul următor? +Sigur vrei să muți în Reciclator următoarele %x elemente? +Sigur vrei să muți în Reciclator următoarele %x de elemente? + + +Move +Mută + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Sigur vrei să ștergi definitiv elementul următor? +Sigur vrei să ștergi definitiv următoarele %x elemente? +Sigur vrei să ștergi definitiv următoarele %x de elemente? + + +Exclude +Exclude + +Direct +Direcționează + +Follow +Urmărește + +Copy NTFS permissions +Copiază permisiunile NTFS + +Integrate external applications into context menu. The following macros are available: +Comenzi pentru aplicații externe incluse în meniul contextual al softului. Sînt disponibile următoarele macrocomenzi: + +- full file or folder name +- numele complet al filei sau dosarului + +- folder part only +- doar pentru partea dosarului + +- Other side's counterpart to %item_path% +- corespondentul din partea opusă al lui %item_path% + +- Other side's counterpart to %item_folder% +- corespondentul din partea opusă al lui %item_folder% + +Restore all hidden windows and warnings? +Restaurezi toate ferestrele și avertismentele ascunse? + +Leave as unresolved conflict +Lasă ca conflict nerezolvat + +Replace +Înlocuiește + +Move files and replace if existing +Mută filele și înlocuiește-le pe cele existente + +Time stamp +Marcaj Temporal + +Append a timestamp to each file name +Adaugă un marcaj temporal la numele fiecărei file + +File +Filă + +YYYY-MM-DD hhmmss +AAAA-LL-ZZ hhmmss + +Files +File + +Items +Elemente + +Percentage +Procent + +Cannot monitor directory %x. +Nu pot monitoriza dosarul %x. + +Conversion error: +Eroare de convertire: + +Cannot delete file %x. +Nu pot șterge fila %x. + +The file is locked by another process: +Fila este zăvorîtă [locked] de alt proces: + +Cannot move file %x to %y. +Nu pot muta fila %x în %y. + +Cannot delete directory %x. +Nu pot șterge dosarul %x. + +Cannot write file attributes of %x. +Nu pot scrie atributele de filă ale lui %x. + +Cannot write modification time of %x. +Nu pot scrie modificarea timpului pentru %x. + +Cannot read security context of %x. +Nu pot citi contextul de securitate pentru %x. + +Cannot write security context of %x. +Nu pot scrie contextul de securitate pentru %x. + +Cannot read permissions of %x. +Nu pot citi permisiunile lui %x. + +Cannot write permissions of %x. +Nu pot scrie permisiunile lui %x. + +Cannot create directory %x. +Nu pot crea dosarul %x. + +Cannot create symbolic link %x. +Nu pot crea legătura simbolică %x. + +Cannot find system function %x. +Nu pot găsi funcția de sistem %x. + +Cannot copy file %x to %y. +Nu pot copia fila %x în %y. + +Type of item %x is not supported: +Tipul de element %x nu e suportat: + +Cannot resolve symbolic link %x. +Nu pot rezolva legătura simbolică %x. + +Cannot open directory %x. +Nu pot deschide dosarul %x. + +Cannot enumerate directory %x. +Nu pot enumera dosarul %x. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +1 min +%x min +%x de min + + + +1 hour +%x hours + + +1 oră +%x ore +%x de ore + + + +1 day +%x days + + +1 zi +%x zile +%x de zile + + +Unable to register to receive system messages. +Nu pot înregistra softul pentru a primi mesaje de la sistemul de operare. + +Cannot set privilege %x. +Nu pot seta privilegiul %x. + +Unable to suspend system sleep mode. +Nu pot suspenda modul de intrare în repaus [sleep] al sistemului. + +Cannot change process I/O priorities. +Nu pot schimba prioritățile I/O ale procesului. + +Unable to move %x to the recycle bin. +Nu pot muta %x în Reciclator. + +Cannot determine final path for %x. +Nu pot determina calea finală pentru %x. + +Error Code %x: +Cod de Eroare %x: + diff --git a/FreeFileSync/Build/Languages/russian.lng b/FreeFileSync/Build/Languages/russian.lng new file mode 100644 index 00000000..5c138a10 --- /dev/null +++ b/FreeFileSync/Build/Languages/russian.lng @@ -0,0 +1,1537 @@ +
    + Pусский + Fayzullin T.N. aka Svobodniy + ru_RU + flag_russia.png + 3 + n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<11 || n%100>14) ? 1 : 2 +
    + +Both sides have changed since last synchronization. +Со времени последней синхронизации с обеих сторон произошли изменения. + +Cannot determine sync-direction: +Невозможно определить направление синхронизации: + +No change since last synchronization. +Никаких изменений с последней синхронизации. + +The database entry is not in sync considering current settings. +Запись в базе данных не находится в состоянии синхронизации, учитывая текущие настройки. + +Setting default synchronization directions: Old files will be overwritten with newer files. + +Настройка направления синхронизации по умолчанию: +Старые файлы будут заменены более новыми файлами. + + +Checking recycle bin availability for folder %x... +Проверка доступности "Корзины" для папки %x... + +Moving file %x to the recycle bin +Перемещение файла %x в "Корзину" + +Moving folder %x to the recycle bin +Перемещение папки %x в "Корзину" + +Moving symbolic link %x to the recycle bin +Перемещение символьной ссылки %x в "Корзину" + +Deleting file %x +Удаление файла %x + +Deleting folder %x +Удаление папки %x + +Deleting symbolic link %x +Удаление символьной ссылки %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +Для этого пути "Корзина" недоступна! Файлы будут удалены безвозвратно: + +An exception occurred +Исключение произошло + +A directory path is expected after %x. +Ожидается путь папки после %x. + +Syntax error +Синтаксическая ошибка + +Cannot open file %x. +Невозможно открыть файл %x. + +File %x does not contain a valid configuration. +Файл %x не содержит действительной конфигурации. + +Unequal number of left and right directories specified. +Указано неравное число папок слева и справа. + +The config file must not contain settings at directory pair level when directories are set via command line. +Конфигурационный файл не должен содержать настройки на уровне пар папок, когда папки задаются с помощью командной строки. + +Directories cannot be set for more than one configuration file. +Папки не могут быть установлены более чем в одном конфигурационном файле. + +Command line +Командная строка + +Syntax: +Синтаксис: + +config files +конфигурационные файлы + +directory +папка + +Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. +Любое количество FreeFileSync .ffs_gui и/или .ffs_batch конфигурационных файлов. + +Any number of alternative directories for at most one config file. +Любое количество альтернативных папок для одного конфигурационного файла. + +A folder input field is empty. +Поле ввода папки пустое. + +The corresponding folder will be considered as empty. +Соответствующая папка будет считаться пустой. + +Cannot find the following folders: +Невозможно найти следующие папки: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Вы можете проигнорировать эту ошибку, приняв каждую папку за пустую. При этом папки будут созданы автоматически во время синхронизации. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +Следующие папки имеют зависимые пути. Будьте осторожны при настройке правил синхронизации: + +File %x has an invalid date. +Файл %x имеет недействительную дату. + +Date: +Дата: + +Files %x have the same date but a different size. +Файлы %x имеют одинаковую дату, но различаются по размеру. + +Size: +Размер: + +Items differ in attributes only +Элементы различаются только атрибутами + +Resolving symbolic link %x +Разрешение символьной ссылки %x + +Comparing content of files %x +Сравнение содержания файлов %x + +Generating file list... +Создание списка файлов... + +Starting comparison +Начать сравнение + +Calculating sync directions... +Расчет направлений синхронизации... + +Out of memory. +Недостаточно памяти. + +Item exists on left side only +Элемент существует только на левой стороне + +Item exists on right side only +Элемент существует только на правой стороне + +Left side is newer +На левой стороне новее + +Right side is newer +На правой стороне новее + +Items have different content +Элемент имеют различное содержание + +Both sides are equal +Обе стороны равны + +Conflict/item cannot be categorized +Конфликт/элемент невозможно отнести к какой-либо категории + +Copy new item to left +Скопировать новый элемент налево + +Copy new item to right +Скопировать новый элемент направо + +Delete left item +Удалить элемент слева + +Delete right item +Удалить элемент справа + +Move file on left +Переместить файл налево + +Move file on right +Переместить файл направо + +Overwrite left item +Перезаписать элемент слева + +Overwrite right item +Перезаписать элемент справа + +Do nothing +Ничего не делать + +Update attributes on left +Обновление атрибутов слева + +Update attributes on right +Обновление атрибутов справа + +Database file %x is incompatible. +Файл базы данных %x несовместим. + +Initial synchronization: +Первоначальная синхронизация: + +Database file %x does not yet exist. +Файл базы данных %x еще не существует. + +Database file is corrupt: +Файл базы данных поврежден: + +Cannot write file %x. +Невозможно записать файл %x. + +Cannot read file %x. +Невозможно прочитать файл %x. + +Database files do not share a common session. +Файлы баз данных не имеют общей сессии. + +Searching for folder %x... +Поиск папки %x... + +Cannot read file attributes of %x. +Невозможно прочитать атрибуты файла %x. + +Cannot get process information. +Невозможно получить информацию о процессе. + +Waiting while directory is locked (%x)... +Ожидание снятия блокировки с папки %x... + + +1 sec +%x sec + + +%x секунда +%x секунды +%x секунд + + +Creating file %x +Создание файла %x + +Items processed: +Элементов обработано: + +Items remaining: +Элементов осталось: + +Total time: +Общее время: + + +1 byte +%x bytes + + +%x байт +%x байта +%x байт + + +%x MB +%x МБ + +%x KB +%x КБ + +%x GB +%x ГБ + +Error parsing file %x, row %y, column %z. +Ошибка при разборе файла %x, строка %y, колонка %z. + +Cannot set directory lock for %x. +Невозможно установить блокировку папки для %x. + +Scanning: +Сканирую: + + +1 thread +%x threads + + +%x поток +%x потока +%x потоков + + +Encoding extended time information: %x +Кодирование расширенной информации о времени: %x + +/sec + + +%x items/sec +%x элементов/с + +Configuration file %x loaded partially only. +Файл конфигурации %x загрузился частично. + +Show in Explorer +Показать в Проводнике + +Open with default application +Открыть с помощью приложения по умолчанию + +Browse directory +Обзор папок + +Cannot access the Volume Shadow Copy Service. +Невозможно получить доступ к службе Теневого Копирования Тома. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Пожалуйста, используйте 64-разрядную версию FreeFileSync для создания теневых копий на этой системе. + +Cannot load file %x. +Невозможно загрузить файл %x. + +Cannot determine volume name for %x. +Невозможно определить имя тома для %x. + +Volume name %x is not part of file path %y. +Имя тома %x не является частью имени файла %y. + +Stop requested: Waiting for current operation to finish... +Запрошена остановка: Ожидайте, пока текущая операция завершится... + +Unable to create timestamp for versioning: +Неспособность создать отметку времени для архивации файлов: + +Cannot read the following XML elements: +Невозможно прочитать следующие XML элементы: + +&Open... +&Открыть... + +Save &as... +Сохранить &как... + +&Quit +&Выход + +&Program +&Файл + +&View help +&Справка + +&About +&О программе + +&Help +&Помощь + +Usage: +Инструкция: + +1. Select folders to watch. +1. Выберите папки для наблюдения; + +2. Enter a command line. +2. Введите командную строку; + +3. Press 'Start'. +3. Нажмите 'Старт'. + +To get started just import a .ffs_batch file. +Для запуска просто импортируйте файл .ffs_batch. + +Folders to watch: +Папки для наблюдения: + +Add folder +Добавить папку + +Remove folder +Удалить папку + +Browse +Обзор + +Select a folder +Выбрать папку + +Idle time (in seconds): +Время ожидания (в секундах): + +Idle time between last detected change and execution of command +Время ожидания между последним обнаруженным изменением и выполнением командной строки в секундах + +Command line: +Командная строка: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +Команда выполняется, если: +- файлы или подпапки изменены +- появились новые папки (например, подключение переносного носителя) + + +&Start +&Старт + +About +О программе + +Build: %x +сборка %x + +All files +Все файлы + +Automated Synchronization +Автоматическая синхронизация + +Directory monitoring active +Слежение за папками активировано + +Waiting until all directories are available... +Ожидать, пока все папки станут доступны... + +Error +Ошибка + +&Restore +&Восстановить + +&Show error +&Показать ошибку + +&Exit +&Выход + +Incorrect command line: +Неверная командная строка: + +&Retry +&Повторить + +File content +Содержимое файла + +File time and size +Дата и размер файла + +Two way +В обе стороны + +Mirror +Зеркало + +Update +Обновить + +Custom +Выборочно + +Multiple... +Различные варианты синхронизации + +Moving file %x to %y +Перемещение файла %x в %y + +Moving folder %x to %y +Перемещение папки %x в %y + +Moving symbolic link %x to %y +Перемещение символьной ссылки %x в %y + +Removing old versions... +Удаление старых версий... + +Creating symbolic link %x +Создание символьной ссылки %x + +Creating folder %x +Создание папки %x + +Overwriting file %x +Перезапись файла %x + +Overwriting symbolic link %x +Перезапись символьной ссылки %x + +Verifying file %x +Проверка файла %x + +Updating attributes of %x +Обновление атрибутов %x + +Cannot find %x. +Невозможно найти %x. + +Target folder %x already existing. +Целевая папка %x уже существует. + +Target folder input field must not be empty. +Поле ввода целевой папки не должно быть пустым. + +Please enter a target folder for versioning. +Пожалуйста, введите целевую папку для архивации файлов. + +Source folder %x not found. +Исходная папка %x не найдена. + +The following items have unresolved conflicts and will not be synchronized: +Следующие элементы имеют неурегулированные конфликты и не будут синхронизированы: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. + +Следующие папки существенно различаются. +Убедитесь, что вы указали соответствующие папки для синхронизации. + + +Not enough free disk space available in: +Не достаточно свободного места в: + +Required: +Требуется: + +Available: +Доступно: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Папка, входящая в несколько пар папок, будет изменена. Пожалуйста, проверьте настройки синхронизации. + +Synchronizing folder pair: +Синхронизация пары папок: + +Generating database... +Создание базы данных... + +Creating a Volume Shadow Copy for %x... +Создание Тома Теневого Копирования для %x... + +Data verification error: %x and %y have different content. +Ошибка проверки данных: %x и %y имеют разное содержание! + +job name +название + +Synchronization stopped +Синхронизация остановлена + +Synchronization completed with errors +Синхронизация завершена. В процессе синхронизации возникли ошибки + +Synchronization completed with warnings +Синхронизация завершена. В процессе синхронизации возникли проблемы + +Nothing to synchronize +Ничего нет для синхронизации + +Synchronization completed successfully +Синхронизация завершена успешно + +Saving log file %x... +Сохранение лог-файла %x... + +You can switch to FreeFileSync's main window to resolve this issue. +Вы можете переключиться на главное окно FreeFileSync для решения этой проблемы. + +&Don't show this warning again +Больше &не показывать это предупреждение + +&Ignore +&Игнорировать + +&Switch +&Переключить + +Switching to FreeFileSync's main window +Переключение на главное окно FreeFileSync + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + + + +&Ignore subsequent errors +И&гнорировать последующие ошибки + +Retrying operation... + + +Serious Error +Серьезная ошибка + +Check for Program Updates +Проверка обновления программы + +A new version of FreeFileSync is available: +Доступна новая версия FreeFileSync: + +Download now? +Загрузить сейчас? + +&Download +&Загрузить + +FreeFileSync is up to date. +У Вас самая последняя версия FreeFileSync. + +Unable to connect to sourceforge.net. +Невозможно соединиться с sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Невозможно найти номер текущей версии FreeFileSync онлайн! Вы хотите проверить вручную? + +&Check + + +Symlink +Символьная ссылка + +Folder +Папка + +Full path +Полный путь + +Name +Имя + +Relative path +Относительный путь + +Base folder +Основная папка + +Size +Размер + +Date +Дата + +Extension +Расширение + +Category +Категория + +Action +Действие + +Drag && drop +Drag && drop + +Close progress dialog +Закрыть окно процесса + +Standby +Перейти в ожидании + +Log off +Выйти из системы (разлогиниться) + +Shut down +Выключить компьютер + +Hibernate +Гибернация + +Alternate comparison settings +Альтернативные настройки сравнения + +Alternate synchronization settings +Альтернативные настройки синхронизации + +Local filter +Локальный фильтр + +Active +активный + +None +неактивный + +Remove alternate settings +Удалить альтернативные настройки + +Clear filter settings +Очистить настройки фильтра + +Copy +Копировать + +Paste +Вставить + +Alternate Comparison Settings +Альтернативные настройки сравнения + +Alternate Synchronization Settings +Альтернативные настройки синхронизации + +Local Filter +Локальный фильтр + +&New +&Новая + +&Save +&Сохранить + +Save as &batch job... +Сохранить как &пакетное задание + +1. &Compare +1. С&равнить + +2. &Synchronize +2. С&инхронизировать + +&Global settings +&Глобальные настройки + +&Language +&Язык + +&Find... +&Найти... + +&Export file list... +&Экспортировать список файлов... + +&Tools +&Инструменты + +&Check now +&Проверить сейчас + +Check &automatically once a week +Проверять &автоматически раз в неделю + +&Check for new version +&Проверка обновлений + +Compare +Сравнить + +Cancel +Отмена + +Synchronize +Синхронизировать + +Add folder pair +Добавить пару папок + +Remove folder pair +Удалить пару папок + +Swap sides +Поменять направление + +Close search bar +Закрыть строку поиска + +Find: +Найти: + +Match case +Учитывать регистр + +Save as batch job +Сохранить как пакетное задание + +Hide excluded items +Скрыть исключенные элементы + +Show filtered or temporarily excluded files +Показать отфильтрованные или временно исключенные файлы + +Number of files and folders that will be created +Количество файлов и папок, которые будут созданы + +Number of files that will be overwritten +Количество файлов, которые будут перезаписаны + +Number of files and folders that will be deleted +Количество файлов и папок, которые будут удалены + +Total bytes to copy +Всего байт для копирования + +Select a variant: +Выберите вариант: + +Identify equal files by comparing modification time and size. +Определять одинаковые файлы путем сравнения времени изменения и размера. + +Identify equal files by comparing the file content. +Определять одинаковые файлы путем сравнения содержания файла. + +Symbolic links: +Символьные ссылки: + +More information +Подробнее + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Выявять и распространять изменения на обе стороны. Удаленные, перемещенные и конфликтующие файлы определяются автоматически с использованием базы данных. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Создавать зеркальную копию левой части. После синхронизации правая часть будет полностью соответствовать левой. + +Copy new and updated files to the right folder. +Копировать новые или обновленные файлы на правую сторону. + +Configure your own synchronization rules. +Настроить свои собственные правила синхронизации. + +Detect moved files +Обнаруживать перемещенные файлы + +Requires database files. Not supported by all file systems. +Требуются файлы баз данных. Поддерживается не всеми файловыми системами. + +Delete files: +Удаление файлов: + +Permanent +Удалять безвозвратно + +Delete or overwrite files permanently +Удалять или перезаписывать файлы, не помещая в "Корзину" + +Recycle bin + + +Back up deleted and overwritten files in the recycle bin +Делать резервную копию удаленных и перезаписанных файлов в "Корзине" + +Versioning +Архивировать + +Move files to a user-defined folder +Перемещать файлы в пользовательскую папку + +Naming convention: +Условие переименования: + +Show examples +Подробнее + +Handle errors: +Обработка ошибкок: + +Ignore +Игнорировать + +Hide all error and warning messages +Скрывать все ошибки и сообщения с предупреждениями + +Pop-up +Показывать всплывающие окна + +Show pop-up on errors or warnings +Показывать всплывающие окна при ошибках и предупреждениях + +On completion: +По завершению: + +Start synchronization now? +Начать синхронизацию сейчас? + +Variant: +Вариант: + +Statistics +Статистика + +&Don't show this dialog again +Больше &не показывать это окно + +Items found: +Элементов найдено: + +Speed: +Скорость: + +Time remaining: +Времени осталось: + +Time elapsed: +Времени прошло: + +Synchronizing... +Синхронизация... + +Minimize to notification area +Свернуть в область уведомлений + +Close +Закрыть + +&Pause +&Пауза + +Stop +Остановить + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x + +Создать файл пакетного задания для автоматической синхронизации. +Для запуска дважды кликните этот файл или запланируйте +в планировщике задач: %x + + +Stop synchronization at first error +Остановить синхронизацию при первой ошибке + +Show progress dialog +Показать окно прогресса + +Save log: +Сохранить лог-файл: + +Limit: +Ограничение: + +Limit maximum number of log files +Ограничить максимальное количество лог-файлов + +How can I schedule a batch job? +Как запланировать пакетное задание? + +&Recycle bin +В "&Корзину" + +Delete on both sides +Удалить с обеих сторон + +Delete on both sides even if the file is selected on one side only +Удалить с обеих сторон, даже если файл выделен только на одной стороне + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Выберите правила фильтрации для исключения определенных файлов из синхронизации. Введите пути файлов относящиеся к соответствующим парам папок. + +Include: +Включить: + +Exclude: +Исключить: + +Time span: +Промежуток времени: + +File size: +Размер файла: + +Minimum: +минимум: + +Maximum: +максимум: + +&Clear +&Очистить + +The following settings are used for all synchronization jobs. +Следующие настройки используются для всех заданий синхронизации. + +Fail-safe file copy +Отказоустойчивое копирование файла + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Копирование во временный файл (*.ffs_tmp) перед перезаписью целевого файла. +Это гарантирует целостность заменяемых файлов даже в случае возникновения серьезной ошибки. + + +(recommended) +(рекомендовано) + +Copy locked files +Копирование заблокированных файлов + +Copy shared or locked files using the Volume Shadow Copy Service. +Копирование общих или заблокированных файлов с использованием службы Теневого Копирования Тома + +(requires administrator rights) +(требуются права Администратора) + +Copy file access permissions +Копирование прав доступа к файлам + +Transfer file and folder permissions. +Передача прав доступа к файлам/папкам + +Automatic retry on error: +Автоматическое повторение при ошибках: + +Retry count: +Число повторений: + +Delay (in seconds): +Задержка (в секундах): + +Customize context menu: +Кастомизация контекстного меню: + +Description +Описание + +Restore hidden windows +Восстановить скрытые окна + +&Default +&По умолчанию + +Source code written in C++ using: +Исходный код написан на C++ с использованием: + +If you like FreeFileSync +Если Вам понравился FreeFileSync + +Donate with PayPal +Отправить деньги через PayPal + +Feedback and suggestions are welcome +Замечания и предложения приветствуются + +Homepage +Оф.сайт + +Email +Почта + +Published under the GNU General Public License +Издается под лицензией GNU General Public License + +Many thanks for localization: +Большое спасибо за перевод: + +Save as Batch Job +Сохранение пакетного задания + +Delete Items +Удаление элементов + +Global Settings +Глобальные настройки + +Select Time Span +Выбор промежутка времени + +Folder Pairs +Пары папок для синхронизации + +Find +Поиск + +Overview +Главная + +Configuration +Настройки + +Main Bar +Главная панель + +Filter Files +Фильтр + +Select View +Вид списка файлов + +Open... +Открыть... + +Save +Сохранить + +Compare both sides +Сравнить обе стороны + +Comparison settings +Настройки сравнения + +Synchronization settings +Настройки синхронизации + +Start synchronization +Начать синхронизацию + +Confirm +Подтвердить + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + + + +&Execute +&Выполнить + + +1 directory +%x directories + + +%x папка +%x папки +%x папок + + + +1 file +%x files + + +%x файл +%x файла +%x файлов + + + +%y of 1 row in view +%y of %x rows in view + + +%y из %x строки показано +%y из %x строк показано +%y из %x строк показано + + +Set direction: +Выберите направление: + +multiple selection +групповое выделение + +Include via filter: +Включить с помощью фильтра: + +Exclude via filter: +Исключить с помощью фильтра: + +Exclude temporarily +Временно исключить + +Include temporarily +Временно включить + +Delete +Удалить + +Include all +Включить все + +Exclude all +Исключить все + +Show icons: +Отображать иконки: + +Small +- маленькие + +Medium +- средние + +Large +- большие + +Select time span... +Выберите промежуток времени... + +Default view +Стандартный вид + +Show "%x" +Показать "%x" + +Last session +Последняя сессия + +Folder Comparison and Synchronization +Сравнение и синхронизация + +Configuration saved +Настройки синхронизации сохранены + +FreeFileSync batch +Пакетное задание FreeFileSync + +Do you want to save changes to %x? +Вы хотите сохранить изменения в %x? + +Never save &changes +Никогда не сохранять &изменения + +Do&n't save +&Не сохранять + +Filter +Фильтр + +Show files that exist on left side only +Показать файлы, существующие только слева + +Show files that exist on right side only +Показать файлы, существующие только справа + +Show files that are newer on left +Показать файлы, которые новее слева + +Show files that are newer on right +Показать файлы, которые новее справа + +Show files that are equal +Показать одинаковые файлы + +Show files that are different +Показать различающиеся файлы + +Show conflicts +Показать конфликтующие файлы + +Show files that will be created on the left side +Показать файлы, которые будут созданы на левой стороне + +Show files that will be created on the right side +Показать файлы, которые будут созданы на правой стороне + +Show files that will be deleted on the left side +Показать файлы, которые будут удалены на левой стороне + +Show files that will be deleted on the right side +Показать файлы, которые будут удалены на правой стороне + +Show files that will be overwritten on left side +Показать файлы, которые будут перезаписаны на левой стороне + +Show files that will be overwritten on right side +Показать файлы, которые будут перезаписаны на правой стороне + +Show files that won't be copied +Показать файлы, которые не будут скопированы + +Set as default +Установить по умолчанию + +All folders are in sync +Все папки синхронизированы + +Synchronization Settings +Настройки синхронизации + +Comparison Settings +Настройки сравнения + +Cannot find %x +Невозможно найти %x + +Comma-separated values +Значения, разделенные запятыми + +File list exported +Список файлов экспортирован + +Searching for program updates... +Проверка обновлений программы... + +Scanning... +Сканирование... + +Comparing content... +Сравнение содержания... + +Info +Информация + +Warning +Внимание + +Paused +Пауза + +Initializing... +Инициализация... + +Stopped +Остановлено + +Completed +Завершено + +&Continue +&Продолжить + +Log +Лог (журнал) + +Today +сегодня + +This week +на этой неделе + +This month +последний месяц + +This year +последний год + +Last x days +последние X дня(ей) + +Byte +Байт + +KB +КБ + +MB +МБ + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Вы точно хотите переместить следующий %x элемент в "Корзину"? +Вы точно хотите переместить следующие %x элемента в "Корзину"? +Вы точно хотите переместить следующие %x элементов в "Корзину"? + + +Move +Переместить + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Вы точно хотите удалить следующий %x элемент? +Вы точно хотите удалить следующие %x элемента? +Вы точно хотите удалить следующие %x элементов? + + +Exclude +Исключить + +Direct +Прямое + +Follow +Последовательное + +Copy NTFS permissions +Копирование NTFS прав доступа + +Integrate external applications into context menu. The following macros are available: + +Интегрируйте внешние приложения в контекстное меню. +Доступны следующие команды: + + +- full file or folder name +- полный путь файла или папки + +- folder part only +- часть пути папки + +- Other side's counterpart to %item_path% +- аналог %item_path% с другой стороны + +- Other side's counterpart to %item_folder% +- аналог %item_folder% с другой стороны + +Restore all hidden windows and warnings? +Восстановить все скрытые окна и предупреждения? + +Leave as unresolved conflict +Оставить как нерешенный конфликт + +Replace +Без переименования + +Move files and replace if existing +Переместить файлы и заменить, если существуют + +Time stamp +Добавить отметку времени + +Append a timestamp to each file name +Добавить отметку времени для каждого имени файла + +File +Файл + +YYYY-MM-DD hhmmss +ГГГГ-ММ-ДД ччммсс + +Files +Файлы + +Items +Элементы + +Percentage +Проценты + +Cannot monitor directory %x. +Невозможно наблюдать папку %x. + +Conversion error: +Ошибка преобразования: + +Cannot delete file %x. +Невозможно удалить файл %x. + +The file is locked by another process: +Файл заблокирован другим процессом: + +Cannot move file %x to %y. +Невозможно перенести файл %x в %y. + +Cannot delete directory %x. +Невозможно удалить папку %x. + +Cannot write file attributes of %x. +Невозможно записать атрибуты файла %x. + +Cannot write modification time of %x. +Невозможно записать время изменения файла %x. + +Cannot read security context of %x. +Невозможно прочитать контекст безобасности %x. + +Cannot write security context of %x. +Невозможно записать контекст безобасности %x. + +Cannot read permissions of %x. +Невозможно прочитать права доступа %x. + +Cannot write permissions of %x. +Невозможно записать права доступа %x. + +Cannot create directory %x. +Невозможно создать папку %x. + +Cannot create symbolic link %x. +Невозможно создать символьную ссылку %x. + +Cannot find system function %x. +Невозможно найти системную функцию %x. + +Cannot copy file %x to %y. +Невозможно скопировать файл %x в %y. + +Type of item %x is not supported: +Тип элемента %x не поддерживается: + +Cannot resolve symbolic link %x. +Невозможно разрешить символьную ссылку %x. + +Cannot open directory %x. +Невозможно открыть папку %x. + +Cannot enumerate directory %x. +Невозможно прочесть папку %x. + +%x TB +%x ТБ + +%x PB +%x ПБ + + +1 min +%x min + + +%x минута +%x минуты +%x минут + + + +1 hour +%x hours + + +%x час +%x часа +%x часов + + + +1 day +%x days + + +%x день +%x дня +%x дней + + +Unable to register to receive system messages. +Невозможно зарегистрироваться для получения системных сообщений. + +Cannot set privilege %x. +Невозможно установить привелегии %x. + +Unable to suspend system sleep mode. +Невозможно приостановить режим сна системы. + +Cannot change process I/O priorities. +Невозможно изменить приоритет процесса. + +Unable to move %x to the recycle bin. +Невозможно переместить %x в "Корзину". + +Cannot determine final path for %x. +Невозможно определить конечный путь для %x. + +Error Code %x: +Код ошибки %x: + diff --git a/FreeFileSync/Build/Languages/scottish_gaelic.lng b/FreeFileSync/Build/Languages/scottish_gaelic.lng new file mode 100644 index 00000000..906969cb --- /dev/null +++ b/FreeFileSync/Build/Languages/scottish_gaelic.lng @@ -0,0 +1,1543 @@ +
    + Gàidhlig + Michael Bauer aka Akerbeltz + gd + flag_scotland.png + 4 + (n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3 +
    + +Both sides have changed since last synchronization. +Chaidh an dà thaobh atharrachadh on t-sioncronachadh mu dheireadh. + +Cannot determine sync-direction: +Cha ghabh comhair an t-sioncronachaidh aithneachadh: + +No change since last synchronization. +Cha deach dad atharrachadh on t-sioncronachadh mu dheireadh. + +The database entry is not in sync considering current settings. +Chan eil innteart an stòir-dhàta sioncronaicht a-rèir nan roghainnean làithreach + +Setting default synchronization directions: Old files will be overwritten with newer files. +A' suidheachadh comhair bhunaiteach an t-sioncronachaidh: Thèid faidhlichean nas ùire a sgrìobhadh thairis air seann-fhaidhlichean. + +Checking recycle bin availability for folder %x... +A' toirt sùil a bheil am biona ri fhaighin airson a' phasgain %x... + +Moving file %x to the recycle bin +A' gluasad an fhaidhle %x dhan bhiona + +Moving folder %x to the recycle bin +A' gluasad a' phasgain %x dhan bhiona + +Moving symbolic link %x to the recycle bin +A' gluasad an symbolic link %x dhan bhiona + +Deleting file %x +A' sguabadh às an fhaidhle %x + +Deleting folder %x +A' sguabadh às a' phasgain %x + +Deleting symbolic link %x +A' sguabadh às an symbolic link %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +Chan eil am biona ri làimh airson nam pasganan a leanas. Thèid an sguabadh às gu buan an àite sin: + +An exception occurred +Thachair eisgeachd + +A directory path is expected after %x. +Tha dùil ri slighe pasgain an dèidh %x + +Syntax error +Mearachd co-chàraidh + +Cannot open file %x. +Cha ghabh am faidhle %x fhosgladh. + +File %x does not contain a valid configuration. +Chan eil rèiteachadh dligheach san fhaidhle %x. + +Unequal number of left and right directories specified. +Chan eil àireamh nam pasganan air an taobh chlì 's an taobh deas co-ionnann + +The config file must not contain settings at directory pair level when directories are set via command line. +Chan fhaod roghainnean aig ìre paidhrichean nam pasganan a bhith san fhaidhle rèiteachaidh nuair a thathar a' cur pasganan slighe na loidhne-àithne. + +Directories cannot be set for more than one configuration file. +Cha ghabh pasganan a shuidheachadh airson barrachd air aon fhaidhle rèiteachaidh. + +Command line +Loidhne-àithne + +Syntax: +Co-chàradh: + +config files +faidhlichean rèiteachaidh + +directory +pasgan + +Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. +Àireamh sam bith de dh'fhaidhlichean rèiteachaidh FreeFileSync .ffs_gui agus/no .ffs_batch + +Any number of alternative directories for at most one config file. +Àireamh sam bith de phasganan eile airson aon fhaidhle rèiteachaidh air a' char as fhaide. + +A folder input field is empty. +Tha co-dhiù aon raon pasgain ann a tha falamh. + +The corresponding folder will be considered as empty. +Thèid am pasgan a leanas a làimhseachadh mar phasgan falamh. + +Cannot find the following folders: +Chan urrainn dhuinn na pasgain a leanas a lorg: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +'S urrainn dhut a' mhearachd seo a leigeil seachad ma tha thu airson 's gun dèilig sinn ri gach pasgan mar gum biodh iad falamh. Thèid na pasgain a chruthachadh gu fèin-obrachail an uairsin rè an t-sioncronachaidh + +The following folders have dependent paths. Be careful when setting up synchronization rules: +Tha slighean eisimeileachdach aig na pasganan a leanas. Bi faiceallach nuair a chruthaicheas tu riaghailtean sioncronachaidh: + +File %x has an invalid date. +Tha ceann-là mì-dhligheach aig an fhaidhle %x. + +Date: +Ceann-là: + +Files %x have the same date but a different size. +Tha an dearbh cheann-là aig na faidhlichean %x ach chan eil am meud co-ionnann. + +Size: +Meud: + +Items differ in attributes only +Chan eil diofar eatarra ach a thaobh an cuid bhuadhan + +Resolving symbolic link %x +Duilgheadas a' rèiteachadh a' symbolic link %x + +Comparing content of files %x +A' dèanamh coimheas eadar na faidhlichean %x + +Generating file list... +A' gintinn liosta nam faidhle... + +Starting comparison +A' tòiseachadh air a' choimeas + +Calculating sync directions... +Ag àireamhachadh comhairean an t-sioncronachaidh... + +Out of memory. +Chan eil cuimhne gu leòr ann. + +Item exists on left side only +Chan eil an nì seo ann ach air an taobh chlì + +Item exists on right side only +Chan eil an nì seo ann ach air an taobh deas + +Left side is newer +Tha an taobh clì nas ùire + +Right side is newer +Tha an taobh deas nas ùire + +Items have different content +Tha diofar susbaint sna nithean + +Both sides are equal +Tha an dà thaobh co-ionnann + +Conflict/item cannot be categorized +Tha còmhstri/nì ann nach urrainn dhuinn aithneachadh + +Copy new item to left +Cuir lethbhreac dhen nì ùr dhan taobh chlì + +Copy new item to right +Cuir lethbhreac dhen nì ùr dhan taobh deas + +Delete left item +Sguab às an nì air an taobh chlì + +Delete right item +Sguab às an nì air an taobh deas + +Move file on left +Gluais am faidhle a tha air an taobh chlì + +Move file on right +Gluais am faidhle a tha air an taobh deas + +Overwrite left item +Sgrìobh thairis air an nì chlì + +Overwrite right item +Sgrìobh thairis air an nì deas + +Do nothing +Na dèan dad + +Update attributes on left +Ùraich na buadhan air an taobh chlì + +Update attributes on right +Ùraich na buadhan air an taobh deas + +Database file %x is incompatible. +Chan eil am faidhle stòir-dhàta %x co-chòrdail. + +Initial synchronization: +A' chiad sioncronachadh: + +Database file %x does not yet exist. +Chan eil am faidhle stòir-dhàta %x ann fhathast. + +Database file is corrupt: +Tha am faidhle stòir-dhàta coirbte: + +Cannot write file %x. +Cha ghabh am faidhle %x a sgrìobhadh. + +Cannot read file %x. +Cha ghabh am faidhle %x a leughadh. + +Database files do not share a common session. +Chan eil seisean an cumantas aig na faidhlichean stòir-dhàta. + +Searching for folder %x... +A' lorg a' phasgain %x... + +Cannot read file attributes of %x. +Cha ghabh buadhan an fhaidhle %x a leughadh. + +Cannot get process information. +Chan urrainn dhuinn greim fhaighinn air fiosrachadh a' phròiseis. + +Waiting while directory is locked (%x)... +A' feitheamh fhad 's a tha am pasgan glaiste (%x)... + + +1 sec +%x sec + + +%x diog +%x dhiog +%x diogan +%x diog + + +Creating file %x +A' cruthachadh an fhaidhle %x + +Items processed: +Nithean a tha deiseil: + +Items remaining: +Nithean a tha ri dhèanamh: + +Total time: +An ùine gu lèir: + + +1 byte +%x bytes + + +%x byte +%x bytes +%x bytes +%x bytes + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Mearachd le parsadh an fhaidhle %x, loidhne %y, colbh %z. + +Cannot set directory lock for %x. +Cha ghabh glas a' phasgain airson %x a shuidheachadh. + +Scanning: +'Ga sganadh: + + +1 thread +%x threads + + +%x shnàithlean +%x shnàithlean +%x snàithleanan +%x snàithlean + + +Encoding extended time information: %x +A' còdachadh fiosrachadh leudaichte an ama: %x + +/sec +/diog + +%x items/sec +%x nithean/diog + +Configuration file %x loaded partially only. +Cha deach faidhle an rèiteachaidh %x a luchdadh gu tur. + +Show in Explorer +Seall san taisgealaiche + +Open with default application +Fosgail leis an aplacaid bhunaiteach + +Browse directory +Rùraich am pasgan + +Cannot access the Volume Shadow Copy Service. +Chan fhaigh sinn cothrom air seirbheise lethbhreacan sgàil an draibh. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Nach cleachd sibh an tionndadh 64 biot de FreeFileSync gus lethbhreacan-sgàile a chruthachadh air an t-siostam seo? + +Cannot load file %x. +Cha ghabh am faidhle %x a lorg. + +Cannot determine volume name for %x. +Chan urrainn dhuinn ainm an draibh airson %x a dhearbhadh. + +Volume name %x is not part of file path %y. +Chan eil ainm an draibh %x 'na phàirt de shlighe an fhaodhle %y. + +Stop requested: Waiting for current operation to finish... +Dh'iarr thu oirnn sgur: A' feitheamh ri crìoch na h-obrach làithreach... + +Unable to create timestamp for versioning: +Chan urrainn dhuinn stampa-ama a chruthachadh airson versioning: + +Cannot read the following XML elements: +Chan urrainn dhuinn na h-eileamaidean XML a leanas a leughadh: + +&Open... +F&osgail... + +Save &as... +Sàbhail &mar... + +&Quit +&Fàg + +&Program +&Prògram + +&View help +&Faic a' chobhair + +&About +&Mu dheidhinn + +&Help +&Cobhair + +Usage: +Cleachdadh: + +1. Select folders to watch. +1. Tagh na pasgain air an cumar sùil. + +2. Enter a command line. +2. Cuir a-steach àithne. + +3. Press 'Start'. +3. Briog air "Tòisich". + +To get started just import a .ffs_batch file. +Cha leig thu leas ach faidhle .ffs_batch ion-phortadh airson toiseach tòiseachaidh. + +Folders to watch: +Pasganan air an cumar sùil: + +Add folder +Cuir pasgan ris + +Remove folder +Thoir am pasgan air falbh + +Browse +Rùraich + +Select a folder +Tagh pasgan + +Idle time (in seconds): +Ùine 'na thàmh (ann an diogan): + +Idle time between last detected change and execution of command +An tàmh eadar an t-atharrachadh mu dheireadh agus gnìomhachadh na h-àithne + +Command line: +Loidhne-àithne: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +Thèid an loidhne-àithne a chur gu dol: +- ma dh'atharraicheas faidhlichean no fo-phasgain +- ma nochdas pasgain ùra (m.e. ma chuireas tu a-steach bioran USB) + + +&Start +Tòi&sich + +About +Mu dheidhinn + +Build: %x +Build: %x + +All files +Gach faidhle + +Automated Synchronization +Sioncronachadh fèin-obrachail + +Directory monitoring active +Tha na pasganan 'gam marasgladh + +Waiting until all directories are available... +A' feitheamh gus am bi gach pasgan ri làimh... + +Error +Mearachd + +&Restore +&Aisig + +&Show error +&Seall a' mhearachd + +&Exit +&Fàg an-seo + +Incorrect command line: +Loidhne-àithne chearr: + +&Retry +&Feuch ris a-rithist + +File content +Susbaint an fhaidhle + +File time and size +Ceann-là is meud + +Two way +An dà chomhair + +Mirror +Sgàthanaich + +Update +Ùraich + +Custom +Gnàthaichte + +Multiple... +Iomadh fear... + +Moving file %x to %y +A' gluasad an fhaidhle %x gu %y + +Moving folder %x to %y +A' gluasad a' phasgain %x gu %y + +Moving symbolic link %x to %y +A' gluasad an symbolic link %x gu %y + +Removing old versions... +A' toirt air falbh nan seann tionndaidhean... + +Creating symbolic link %x +A' cruthachadh an symbolic link %x + +Creating folder %x +A' cruthachadh a' phasgain %x + +Overwriting file %x +A' sgrìobhadh thairis air an fhaidhle %x + +Overwriting symbolic link %x +A' sgrìobhadh thairis air an symbolic link %x + +Verifying file %x +A' dearbhadh an fhaidhle %x + +Updating attributes of %x +Ag ùrachadh buadhan %x + +Cannot find %x. +Cha ghabh %x a lorg. + +Target folder %x already existing. +Tha am pasgan-uidhe %x ann mu thràth. + +Target folder input field must not be empty. +Chan fhaod raon a' phasgain a bhith falamh. + +Please enter a target folder for versioning. +Cuir a-steach pasgan-targaide a chum versioning. + +Source folder %x not found. +Cha deach am pasgan tùsail %x a lorg. + +The following items have unresolved conflicts and will not be synchronized: +Tha còmstrithean aig na nithean a leanas fhathast is cha dèid an sioncronachadh: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +Tha diofar mòr eadar na pasganan a leanas. Dèan cinnteach gu bheil thu a' maidseadh nam pasganan ceart airson sioncronachadh. + +Not enough free disk space available in: +Chan eil rùm saor gu leòr air an diosga: + +Required: +Na tha feum air: + +Available: +Na tha ri làimh: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Thèid pasgan atharrachadh a tha 'na phàirt de dh'iomadh paidhir de phasgain. Nach doir thu sùil air roghainnean an t-sioncronachaidh? + +Synchronizing folder pair: +A' sioncronachadh paidhir nam pasgan: + +Generating database... +A' gintinn an stòir-dhàta... + +Creating a Volume Shadow Copy for %x... +A' cruthachadh lethbhreac sgàil draibh airson %x... + +Data verification error: %x and %y have different content. +Mearachd le dearbhadh an dàta: tha susbaint eadar-dhealaichte ann an %x agus %y. + +job name +ainm na h-obrach + +Synchronization stopped +Sguireadh dhen t-sioncronachadh + +Synchronization completed with errors +Chaidh an sioncronachadh a choileanadh ach bha mearachdan ann + +Synchronization completed with warnings +Chaidh a shioncronachadh ach bha rabhaidhean ann + +Nothing to synchronize +Chan eil dad ri shioncronachadh + +Synchronization completed successfully +Chaidh a shioncronachadh + +Saving log file %x... +A' sàbhaladh faidhle an loga %x... + +You can switch to FreeFileSync's main window to resolve this issue. +'S urrainn dhut leum a ghearradh gu prìomh-uinneag FreeFileSync gus an duilgheadas seo fhuasgladh. + +&Don't show this warning again +Na seall an rabha&dh seo a-rithist + +&Ignore +&Leig seachad + +&Switch +&Dèan suids + +Switching to FreeFileSync's main window +A' gearradh leum gu prìomh-uinneag FreeFileSync + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + +A' feuchainn ris às ùr gu fèin-obrachail ann an %x diog... +A' feuchainn ris às ùr gu fèin-obrachail ann an %x dhiog... +A' feuchainn ris às ùr gu fèin-obrachail ann an %x diogan... +A' feuchainn ris às ùr gu fèin-obrachail ann an %x diog... + + +&Ignore subsequent errors +Le&ig seachad mearachdan às a dhèidh seo + +Retrying operation... + + +Serious Error +Mearachd mhòr + +Check for Program Updates +Thoir sùil ach a bheil ùrachadh ann airson a' phrògraim + +A new version of FreeFileSync is available: +Tha tionndadh ùr de FreeFileSync ann: + +Download now? +A bheil thu airson a luchdadh a-nuas an-dràsta? + +&Download +&Luchdaich a-nuas + +FreeFileSync is up to date. +Tha FreeFileSync cho ùr 's a ghabhas. + +Unable to connect to sourceforge.net. +Cha b' urrainn dhuinn ceangal a dhèanamh ri Sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Chan urrainn dhuinn àireamh an tionndaidh làithrich aig FreeFileSync a lorg air loidhne. A bheil thu airson sùil a thoirt thu fhèin? + +&Check + + +Symlink +Symlink + +Folder +Pasgan + +Full path +Slighe shlan + +Name +Ainm + +Relative path +An t-slighe dhàimheach + +Base folder +Bun-phasgan + +Size +Meud + +Date +Ceann-là + +Extension +Leudachan + +Category +Roinn seòrsa + +Action +Gnìomh + +Drag && drop +Slaod ┐ leig às + +Close progress dialog +Dùin còmhradh an adhartais + +Standby +Cuir 'na fhuireachas + +Log off +Clàraich a-mach + +Shut down +Dùin sìos an siostam + +Hibernate +Geamhraich + +Alternate comparison settings +Roghainnean eile airson coimeasadh + +Alternate synchronization settings +Roghainnean eile airson sioncronachadh + +Local filter +Criathrag ionadail + +Active +Gnìomhach + +None +Chan eil gin + +Remove alternate settings +Thoir air falbh na roghainnean eile + +Clear filter settings +Falamhaich roghainnean na criathraige + +Copy +Dèan lethbhreac + +Paste +Cuir ann + +Alternate Comparison Settings +Roghainnean eile airson coimeasadh + +Alternate Synchronization Settings +Roghainnean eile airson sioncronachadh + +Local Filter +Criathrag ionadail + +&New +Ù&r + +&Save +&Sàbhail + +Save as &batch job... +Sàbhail mar obair &baidse... + +1. &Compare +1. &Dèan coimeas + +2. &Synchronize +2. &Dèan sioncronachadh + +&Global settings +&Na roghainnean uile-choitcheann + +&Language +&Cànan + +&Find... +L&org... + +&Export file list... +Às-p&hortaich liosta nam faidhle... + +&Tools +&Innealan + +&Check now +&Thoir sùil an-dràsta + +Check &automatically once a week +Thoir sùil gu &fèin-obrachail turas san t-seachdain + +&Check for new version +Thoir sùil a&ch a bheil tionndadh ùr ann + +Compare +Dèan coimeas + +Cancel +Sguir dheth + +Synchronize +Dèan sioncronachadh + +Add folder pair +Cuir paidhir de phasgain ris + +Remove folder pair +Thoir air falbh am paidhir seo de phasgain + +Swap sides +Cuir an dà thaobh an àite a chèile + +Close search bar +Dùin bàr an luirg + +Find: +Lorg: + +Match case +An aire do litrichean mòra 's beaga + +Save as batch job +Sàbhail mar obair baidse + +Hide excluded items +Falaich nithean a chaidh a dhùnadh às + +Show filtered or temporarily excluded files +Seall faidhlichean a tha 'gan dùnadh a-mach no air an criathradh a-mach an-dràsta fhèin + +Number of files and folders that will be created +Àireamh nam faidhle 's nam pasgan a thèid a chruthachadh + +Number of files that will be overwritten +Àireamh nam faidhle a thèid sgrìobhadh thairis orra + +Number of files and folders that will be deleted +Àireamh nam faidhle 's nam pasgan a thèid a sguabadh às + +Total bytes to copy +Co mheud baidht a thèid lethbhreac a dhèanamh dhiubh + +Select a variant: +Tagh eug-samhail: + +Identify equal files by comparing modification time and size. +Lorg faidhlichean a tha co-ionnann le bhith a' dèanamh coimeas eadar an cuid meud is ama. + +Identify equal files by comparing the file content. +Lorg faidhlichean a tha co-ionnann le bhith a' dèanamh coimeas eadar susbaint nam faidhlichean. + +Symbolic links: +Ceanglaichean samhlachail: + +More information +Barrachd fiosrachaidh + +OK +Ceart ma-thà + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Lorg is sìolaich na h-atharraichean air an dà thaobh. Mothaichidh sinn do rudan a chaidh a sguabadh às, a ghluasad no còmhstrithean gu fèin-obrachail le stòr-dàta. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Cruthaich lethbhreac-glèidhidh sgàthanaichte dhen phasgan air an taobh chlì a bhios gu tur co-ionnann ris a' phasgan air an taobh deas an dèidh sioncronachaidh. + +Copy new and updated files to the right folder. +Cuir lethbhreac de dh'fhaidhlichean ùra 's ùraichte dhan phasgan air an taobh deas. + +Configure your own synchronization rules. +Sònraich riaghailtean sioncronachaidh thu fhèin. + +Detect moved files +Mothaich do dh'fhaidhlichean a chaidh a ghluasad + +Requires database files. Not supported by all file systems. +Feumaidh seo faidhlichean stòir-dhàta. Chan eil gach siostam fhaidhlichean a' cur taic ri seo. + +Delete files: +Sguab às na faidhlichean: + +Permanent +Buan + +Delete or overwrite files permanently +Sguab às no sgrìobh thairis air faidhlichean gu buan + +Recycle bin +Am biona + +Back up deleted and overwritten files in the recycle bin +Dèan lethbhreac-glèidhidh de dh'fhaidhlichean sa bhiona a chaidh a sguabadh às no a chaidh sgrìobhadh thairis orra + +Versioning +Versioning + +Move files to a user-defined folder +Gluais na faidhlichean gu àite a shònraich an cleachdaiche + +Naming convention: +Gnàthas nan ainmean: + +Show examples +Seall buill-eisimpleir + +Handle errors: +Mearachdan làimhseachaidh: + +Ignore +Leig seachad + +Hide all error and warning messages +Falaich gach teachdaireachd mu mhearachdan no rabhaidhean + +Pop-up +Priob-uinneag + +Show pop-up on errors or warnings +Seall priob-uinneagan a thaobh mhearachdan no rabhaidhean + +On completion: +An dèidh coileanaidh: + +Start synchronization now? +A bheil thu airson tòiseachadh air an t-sioncronachadh an-dràsta? + +Variant: +Eug-samhail: + +Statistics +Stats + +&Don't show this dialog again +&Na seall an còmhradh seo a-rithist + +Items found: +Rudan a chaidh a lorg: + +Speed: +Astar: + +Time remaining: +An ùine a tha air fhàgail: + +Time elapsed: +An ùine a dh'fhalbh: + +Synchronizing... +A' sioncronachadh... + +Minimize to notification area +Lùghdach 's gluais gu raon nam brathan + +Close +Dùin + +&Pause +&Cuir 'na stad + +Stop +Sguir dheth + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Cruthaich faidhle batch airson sioncronachadh a dh'obraicheas gun thusa a bhith an làthair. Gus a thòiseachadh, dèan briogadh dùbailte air an fhaidhle seo ann am planadair shaothraichean: %x + +Stop synchronization at first error +Sguir dhen t-sioncronachadh aig a' chiad mhearachd + +Show progress dialog +Seall còmhradh an adhartais + +Save log: +Sàbhail an loga: + +Limit: +Crìoch: + +Limit maximum number of log files +Cuingich an àireamh as motha de dh'fhaidhlichean an loga + +How can I schedule a batch job? +Ciamar a chuireas mi obair baidse air an sgeideal? + +&Recycle bin +A&m biona + +Delete on both sides +Sguab às air an dà thaobh + +Delete on both sides even if the file is selected on one side only +Sguab às air an dà thaobh fiù mur an deach am faidhle a thaghadh ach air aon taobh + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Tagh riaghailteach criathraidh gus seòrsachan àraidh de dh'fhaidhlichean fhàgail às an t-sioncronachadh. Cuir a-steach slighean fhaidhlichean a-rèir paidhir an dà phasgan aca. + +Include: +Gabh a-staigh: + +Exclude: +Dùin a-mach: + +Time span: +An rainse ama: + +File size: +Meud an fhaidhle: + +Minimum: +Air a' char as lugha: + +Maximum: +Air a' char as motha: + +&Clear +Fala&mhaich + +The following settings are used for all synchronization jobs. +Thèid na roghainnean a leanas a chleachdadh airson a h-uile obair sioncronachaidh. + +Fail-safe file copy +Dèan lethbhreac nach gabh fàilligeadh + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Cuir lethbhreac dheth ann am faidhle sealach (*.ffs_tmp) mus sgrìobhar thairis air an targaid. +Nì seo cinnteach gum bi fuasgladh ann ma thachras mearachd mhòr. + + +(recommended) +(mholamaid seo) + +Copy locked files +Dèan lethbhreac de dh'fhaidhlichean glaiste + +Copy shared or locked files using the Volume Shadow Copy Service. +Dèan lethbhreac de dh'fhaidhlichean co-roinnte no glaiste le seirbheis lethbhreacan sgàil an draibh. + +(requires administrator rights) +(feumaidh seo còraichean rianadair) + +Copy file access permissions +Dèan lethbhreac de cheadan-inntrigidh nam faidhle + +Transfer file and folder permissions. +Tar-chuir am faidhle 's ceadan a' phasgain + +Automatic retry on error: +Feuch ris a-rithist gu fèin-obrachail ri linn mearachd: + +Retry count: +Co mheud turas a dh'fheudadh ris a-rithist: + +Delay (in seconds): +An dàil (ann an diogan): + +Customize context menu: +Gnàthaich an clàr-taice co-theacsail + +Description +Tuairisgeul + +Restore hidden windows +Aisig na h-uinneagan falaichte + +&Default +&Bun-roghainn + +Source code written in C++ using: +Chaidh an còd tùsail a sgrìobhadh ann an C++ le taic: + +If you like FreeFileSync +Ma tha FreeFileSync a' còrdadh riut + +Donate with PayPal +Nach doir sibh tabhartas le PayPal? + +Feedback and suggestions are welcome +Tha sinn a' cur fàilte mhòr air beachd is moladh sam bith + +Homepage +An duilleag-dhachaigh + +Email +Post-d + +Published under the GNU General Public License +Air fhoillseachadh fo GNU General Public License + +Many thanks for localization: +Tha sinn fada an comain nan daoine a leanas airson eadar-theangachadh: + +Save as Batch Job +Sàbhail mar obair baidse + +Delete Items +Sguab às na nithean + +Global Settings +Na roghainnean uile-choitcheann + +Select Time Span +Tagh an rainse ama + +Folder Pairs +Paidhrichean nam pasganan + +Find +Lorg + +Overview +Foir-shealladh + +Configuration +Rèiteachadh + +Main Bar +Am prìomh-bhàr + +Filter Files +Criathraich na faidhlichean + +Select View +Tagh sealladh + +Open... +Fosgail... + +Save +Sàbhail + +Compare both sides +Dèan coimeas air an dà thaobh + +Comparison settings +Roghainnean a' choimeasaidh + +Synchronization settings +Roghainnean an t-sioncronachaidh + +Start synchronization +Tòisich air an t-sioncronachadh + +Confirm +Dearbh + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + +A bheil thu cinnteach gu bheil thu airson an àithne seo (%y) a ruith air %x nì? +A bheil thu cinnteach gu bheil thu airson an àithne seo (%y) a ruith air %x nì? +A bheil thu cinnteach gu bheil thu airson an àithne seo (%y) a ruith air %x nithean? +A bheil thu cinnteach gu bheil thu airson an àithne seo (%y) a ruith air %x nì? + + +&Execute +&Cuir an gnìomh + + +1 directory +%x directories + + +%x phasgan +%x phasgan +%x pasgain +%x pasgan + + + +1 file +%x files + + +%x fhaidhle +%x fhaidhle +%x faidhlichean +%x faidhle + + + +%y of 1 row in view +%y of %x rows in view + + +%y de %x ràgh san t-sealladh +%y de %x ràgh san t-sealladh +%y de %x ràghan san t-sealladh +%y de %x ràgh san t-sealladh + + +Set direction: +Suidhich a' chomhair: + +multiple selection +Ioma-thaghadh + +Include via filter: +Gach a-steach slighe na criathraige: + +Exclude via filter: +Dùin a-mach le criathrag: + +Exclude temporarily +Dùin a-mach gu sealach + +Include temporarily +Gabh a-steach gu sealach + +Delete +Sguab às + +Include all +Gabh a-steach na h-uile + +Exclude all +Dùin a-mach na h-uile + +Show icons: +Meud nan ìomhaigheagan: + +Small +Beag + +Medium +Meadhanach + +Large +Mòr + +Select time span... +Tagh an raon-ama... + +Default view +An sealladh bunaiteach + +Show "%x" +Seall "%x" + +Last session +An seisean mu dheireadh + +Folder Comparison and Synchronization +Coimeas eadar na pasgain is sioncronachadh + +Configuration saved +Chaidh an rèiteachadh a shàbhaladh + +FreeFileSync batch +FreeFileSync batch + +Do you want to save changes to %x? +An sàbhail sinn dhut na h-atharraichean air %x? + +Never save &changes +Na sàbhail atharrai&chean idir + +Do&n't save +&Na sàbhail + +Filter +Criathrag + +Show files that exist on left side only +Na seall ach faidhlichean a tha air an taobh chlì a-mhàin + +Show files that exist on right side only +Na seall ach faidhlichean a tha air an taobh deas a-mhàin + +Show files that are newer on left +Seall faidhlichean a tha nas ùire air an taobh chlì + +Show files that are newer on right +Seall faidhlichean a tha nas ùire air an taobh deas + +Show files that are equal +Seall faidhlichean a tha co-ionnann + +Show files that are different +Seall faidhlichean a tha eadar-dhealaichte + +Show conflicts +Seall còmhstrithean + +Show files that will be created on the left side +Seall faidhlichean a thèid a chruthachadh air an taobh chlì + +Show files that will be created on the right side +Seall faidhlichean a thèid a chruthachadh air an taobh deas + +Show files that will be deleted on the left side +Seall faidhlichean a thèid a sguabadh às air an taobh chlì + +Show files that will be deleted on the right side +Seall faidhlichean a thèid a sguabadh às air an taobh deas + +Show files that will be overwritten on left side +Seall faidhlichean a thèid a thar-sgrìobhadh air an taobh chlì + +Show files that will be overwritten on right side +Seall faidhlichean a thèid a thar-sgrìobhadh air an taobh deas + +Show files that won't be copied +Seall faidhlichean nach dèid lethbhreac a dhèanamh dhiubh + +Set as default +Suidhich mar a' bhun-roghainn + +All folders are in sync +Tha gach pasgan air a shioncronachadh + +Synchronization Settings +Roghainnean an t-sioncronachaidh + +Comparison Settings +Roghainnean a' choimeasaidh + +Cannot find %x +Chan urrainn dhuinn %x a lorg. + +Comma-separated values +Luachan le cromagan eatarra + +File list exported +Chaidh liosta nam faidhle às-phortadh + +Searching for program updates... +A' lorg ùrachaidhean a' phrògraim... + +Scanning... +'Ga sganadh... + +Comparing content... +A' dèanamh coimeas eadar an cuid susbaint... + +Info +Fiosrachadh + +Warning +Rabhadh + +Paused +'Na stad + +Initializing... +A' tòiseachadh... + +Stopped +Chaidh stad a chur air + +Completed +Deiseil + +&Continue +Lean air adhar&t + +Log +Logaich + +Today +An-diugh + +This week +An t-seachdain seo + +This month +Am mìos seo + +This year +Am bliadhna + +Last x days +Na x làithean seo chaidh + +Byte +Baidht + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +A bheil thu cinnteach gu bheil thu airson an %x nì seo a chur dhan bhiona? +A bheil thu cinnteach gu bheil thu airson an %x nì seo a chur dhan bhiona? +A bheil thu cinnteach gu bheil thu airson na %x nithean seo a chur dhan bhiona? +A bheil thu cinnteach gu bheil thu airson an %x nì seo a chur dhan bhiona? + + +Move +Gluais + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +A bheil thu cinnteach gu bheil thu airson an %x nì seo a sguabadh às? +A bheil thu cinnteach gu bheil thu airson na %x nì seo a sguabadh às? +A bheil thu cinnteach gu bheil thu airson na %x nithean seo a sguabadh às? +A bheil thu cinnteach gu bheil thu airson na %x nì seo a sguabadh às? + + +Exclude +Dùin a-mach + +Direct +Dìreach + +Follow +Lean + +Copy NTFS permissions +Dèan lethbhreac de cheadan NTFS + +Integrate external applications into context menu. The following macros are available: +Amalaichidh seo aplacaidean air an taobh a-muigh dhan chlàr-taice cho-theacsail. Tha na macrothan a leanas ri làimh: + +- full file or folder name +- ainm slàn dhen fhaidhle no dhen phasgan + +- folder part only +- cuid a' phasgain a-mhàin + +- Other side's counterpart to %item_path% +- Seise %item_folder% aig a' choimpiutair eile + +- Other side's counterpart to %item_folder% +- Seise %item_folder% aig a' choimpiutair eile + +Restore all hidden windows and warnings? +A bheil thu airson gach uinneag is rabhadh falaichte aiseaga? + +Leave as unresolved conflict +Fàg mar còmhstri gun rèiteachadh + +Replace +Cuir 'na àite + +Move files and replace if existing +Gluais na faidhlichean 's cuir iad an àite na feadhainn làithreach ma tha gin ann + +Time stamp +Stampa ama + +Append a timestamp to each file name +Cuir stampa-ama ris ainm gach faidhle + +File +Faidhle + +YYYY-MM-DD hhmmss +BBBB-MM-LL uummdd + +Files +Faidhlichean + +Items +Nithean + +Percentage +Ceudad + +Cannot monitor directory %x. +Chan urrainn dhuinn sùil a chumail air %x. + +Conversion error: +Mearachd iompachaidh: + +Cannot delete file %x. +Cha ghabh am faidhle %x a sguabadh às. + +The file is locked by another process: +Tha am faidhle glaiste aig pròiseas eile: + +Cannot move file %x to %y. +Cha ghabh am faidhle %x a ghluasad dha %y. + +Cannot delete directory %x. +Cha ghabh am pasgan %x a sguabadh às. + +Cannot write file attributes of %x. +Chan urrainn dhuinn buadhan an fhaidhle %x a sgrìobhadh. + +Cannot write modification time of %x. +Cha ghabh àm atharrachaidh %x a sgrìobhadh. + +Cannot read security context of %x. +Cha ghabh susbaint tèarainteachd %x a leughadh. + +Cannot write security context of %x. +Cha ghabh susbaint tèarainteachd %x a sgrìobhadh. + +Cannot read permissions of %x. +Cha ghabh ceadan %x a leughadh. + +Cannot write permissions of %x. +Cha ghabh ceadan %x a sgrìobhadh. + +Cannot create directory %x. +Cha ghabh am pasgan %x a chruthachadh. + +Cannot create symbolic link %x. +Cha ghabh an symbolic link %x a chruthachadh + +Cannot find system function %x. +Chan urrainn dhuinn foincsean an t-siostaim %x a lorg. + +Cannot copy file %x to %y. +Cha ghabh lethbhreac an fhaidhle %x a chur gu %y. + +Type of item %x is not supported: +Chan eil taic ri nì dhen t-seòrsa %x: + +Cannot resolve symbolic link %x. +Cha ghabh an symbolic link %x fhuasgladh. + +Cannot open directory %x. +Cha ghabh am pasgan %x fhosgladh. + +Cannot enumerate directory %x. +Cha ghabh am pasgan %x àireamhachadh. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +%x mhionaid +%x mhionaid +%x mionaidean +%x mionaid + + + +1 hour +%x hours + + +%x uair a thìde +%x uair a thìde +%x uairean a thìde +%x uair a thìde + + + +1 day +%x days + + +%x latha +%x latha +%x làithean +%x latha + + +Unable to register to receive system messages. +Chan urrainn dhuinn clàradh gus teachdaireachdan an t-siostaim fhaighinn. + +Cannot set privilege %x. +Cha ghabh a' phribhleid %x a shuidheachadh. + +Unable to suspend system sleep mode. +Chan urrainn dhuinn modh cadal an t-siostaim a chur dheth. + +Cannot change process I/O priorities. +Chan urrainn dhuinn na prìomhachasan I/O atharrachadh. + +Unable to move %x to the recycle bin. +Cha ghabh %x a ghluasad dhan bhiona. + +Cannot determine final path for %x. +Chan urrainn dhuinn an t-slighe dheireannach airson %x a dhearbhadh. + +Error Code %x: +Còd na mearachd %x: + diff --git a/FreeFileSync/Build/Languages/serbian.lng b/FreeFileSync/Build/Languages/serbian.lng new file mode 100644 index 00000000..952cce1c --- /dev/null +++ b/FreeFileSync/Build/Languages/serbian.lng @@ -0,0 +1,1530 @@ +
    + Cрпски + Балкански Шпијун + sr_RS + flag_serbia.png + 3 + n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2 +
    + +Both sides have changed since last synchronization. +Обе су стране промењене од последње синхронизације. + +Cannot determine sync-direction: +Не могу одредити смер синхронизације: + +No change since last synchronization. +Нема промена од задње синхронизације. + +The database entry is not in sync considering current settings. +Унос у бази података није синхронизован у односу на тренутна подешавања. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Постављам подразумеване синхронизацијске смерове: Старе датотеке биће замењене новијим датотекама. + +Checking recycle bin availability for folder %x... +Проверавам доступност Корпе за смеће за фолдер %x... + +Moving file %x to the recycle bin +Премештам датотеку %x у Корпи за смеће + +Moving folder %x to the recycle bin +Премештам фолдер %x у Корпи за смеће + +Moving symbolic link %x to the recycle bin +Премештам симболичну везу %x у Корпи за смеће + +Deleting file %x +Брисање датотеке %x + +Deleting folder %x +Брисање фолдера %x + +Deleting symbolic link %x +Брисање симболичне везе %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +Корпа за смеће није доступна за следеће фолдере. Уместо тога датотеке биће обрисане трајно: + +An exception occurred +Догодило се изузеће + +A directory path is expected after %x. +Путања фолдера се очекује после %x. + +Syntax error +Синтаксичка грешка + +Cannot open file %x. +Не могу отворити датотеку %x. + +File %x does not contain a valid configuration. +Датотека %x не садржи валидну конфигурацију. + +Unequal number of left and right directories specified. +Неједнак број левих и десних фолдера је дефинисан. + +The config file must not contain settings at directory pair level when directories are set via command line. +Конфигурациона датотека не сме садржавати подешавања на нивоу фолдерског пара када су фолдери подешени по командној линији. + +Directories cannot be set for more than one configuration file. +Фолдери не могу бити подешени за више од једне конфигурационе датотеке. + +Command line +Командна линија + +Syntax: +Синтакса: + +config files +конфигурационе датотеке + +directory +фолдер + +Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. +Било који број FreeFileSync .ffs_gui и/или .ffs_batch конфигурационих датотека. + +Any number of alternative directories for at most one config file. +Било који број алтернативних фолдера за најмање једну конфигурациону датотеку. + +A folder input field is empty. +Поље за одабир фолдера је празно. + +The corresponding folder will be considered as empty. +Одговарајући фолдер сматраће се празним. + +Cannot find the following folders: +Не могу пронаћи следеће фолдере: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Можете игнорисати ову грешку да би сматрали сваки фолдер празним. Фолдери ће бити креирани аутоматски током синхронизације. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +Следећи фолдери имају зависне путање. Будите пажљиви када подешавате синхронизациона правила: + +File %x has an invalid date. +Датотека %x има невалидни датум. + +Date: +Датум: + +Files %x have the same date but a different size. +Датотеке %x имају исти датум али другачију величину. + +Size: +Величина: + +Items differ in attributes only +Ставке се разликују само у атрибутима + +Resolving symbolic link %x +Разрешавање симболичне везе %x + +Comparing content of files %x +Упоређујем садржај датотека %x + +Generating file list... +Стварам листу датотека... + +Starting comparison +Покретање упоређивања + +Calculating sync directions... +Одређујем смерове синхронизације... + +Out of memory. +Недостатак меморије. + +Item exists on left side only +Ставка постоји само на левој страни + +Item exists on right side only +Ставка постоји само на десној страни + +Left side is newer +Лева страна је новија + +Right side is newer +Десна страна је новија + +Items have different content +Ставке имају различит садржај + +Both sides are equal +Обе стране су једнаке + +Conflict/item cannot be categorized +Конфликт/ставка не може бити разврстана + +Copy new item to left +Копирај нову ставку лево + +Copy new item to right +Копирај нову ставку десно + +Delete left item +Избриши леву ставку + +Delete right item +Избриши десну ставку + +Move file on left +Премести датотеку лево + +Move file on right +Премести датотеку десно + +Overwrite left item +Замени леву ставку + +Overwrite right item +Замени десну ставку + +Do nothing +Не ради ништа + +Update attributes on left +Освежи атрибуте лево + +Update attributes on right +Освежи атрибуте десно + +Database file %x is incompatible. +Датотека базе %x је некомпатибилна. + +Initial synchronization: +Почетна синхронизација: + +Database file %x does not yet exist. +Датотека базе %x још не постоји. + +Database file is corrupt: +Датотека базе је оштећена: + +Cannot write file %x. +Не могу уписати датотеку %x. + +Cannot read file %x. +Не могу читати датотеку %x. + +Database files do not share a common session. +Датотеке базе не деле заједничку сесију. + +Searching for folder %x... +Тражим фолдер %x... + +Cannot read file attributes of %x. +Не могу прочитати атрибуте од %x. + +Cannot get process information. +Не могу добити информације о процесу. + +Waiting while directory is locked (%x)... +Чека се док се фолдер не закључа (%x)... + + +1 sec +%x sec + + +%x сек +%x сек +%x сек + + +Creating file %x +Правим датотеку %x + +Items processed: +Обрађени елементи: + +Items remaining: +Преостали елементи: + +Total time: +Укупно време: + + +1 byte +%x bytes + + +%x бајт +%x бајта +%x бајтова + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Грешка у анализи датотеке %x, ред %y, колона %z. + +Cannot set directory lock for %x. +Не могу закључати фолдер %x. + +Scanning: +Претражујем: + + +1 thread +%x threads + + +%x нит +%x нити +%x нити + + +Encoding extended time information: %x +Кодирам проширене информације о времену: %x + +/sec +/сек + +%x items/sec +%x ставки/секунди + +Configuration file %x loaded partially only. +Датотека подешавања %x учитана само делимично. + +Show in Explorer +Прикажи у Експлореру + +Open with default application +Отвори са подразумеваном апликацијом + +Browse directory +Одабери фолдер + +Cannot access the Volume Shadow Copy Service. +Не могу приступити Volume Shadow Copy сервису. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Молимо користите FreeFileSync 64-битну верзију за израду shadow копија на овом систему. + +Cannot load file %x. +Не могу учитати датотеку %x. + +Cannot determine volume name for %x. +Не могу утврдити назив партиције за %x. + +Volume name %x is not part of file path %y. +Назив партиције %x није део путање датотеке %y. + +Stop requested: Waiting for current operation to finish... +Заустављање захтевано: Чека се да се тренутна акција заврши... + +Unable to create timestamp for versioning: +Није могуће стварање временске ознаке за верзију: + +Cannot read the following XML elements: +Не могу прочитати следеће XML елементе: + +&Open... +&Отвори... + +Save &as... +Сачувај &као... + +&Quit +&Излаз + +&Program +&Програм + +&View help +&Погледај помоћ + +&About +&О програму + +&Help +&Помоћ + +Usage: +Употреба: + +1. Select folders to watch. +1. Одаберите фолдере за надгледање. + +2. Enter a command line. +2. Унесите командну линију. + +3. Press 'Start'. +3. Притисните 'Старт'. + +To get started just import a .ffs_batch file. +Да би почели увезите .ffs_batch датотеку. + +Folders to watch: +Фолдери за надгледање: + +Add folder +Додај фолдер + +Remove folder +Уклони фолдер + +Browse +Одабери + +Select a folder +Одаберите фолдер + +Idle time (in seconds): +Време мировања (у секундама): + +Idle time between last detected change and execution of command +Време мировања између задње препознате промене и извршења наредбе + +Command line: +Командна линија: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +Наредба ће бити покренута ако се: +- датотеке или подфолдери промене +- нови фолдери појаве (нпр. укључење USB стика) + + +&Start +&Покрени + +About +О програму + +Build: %x +Подверзија: %x + +All files +Све датотеке + +Automated Synchronization +Аутоматска синхронизација + +Directory monitoring active +Активно надгледање фолдера + +Waiting until all directories are available... +Чекање да сви фолдери буду доступни... + +Error +Грешка + +&Restore +&Врати + +&Show error +&Прикажи грешку + +&Exit +&Излаз + +Incorrect command line: +Нетачна командна линија: + +&Retry +&Понови + +File content +Садржај датотеке + +File time and size +Време и величина датотеке + +Two way +Двосмерно + +Mirror +Огледално + +Update +Ажурирарно + +Custom +Специфично + +Multiple... +Многоструко... + +Moving file %x to %y +Премештам датотеку %x у %y + +Moving folder %x to %y +Премештам фолдер %x у %y + +Moving symbolic link %x to %y +Премештам симболичну везу %x у %y + +Removing old versions... +Уклањам старије верзије... + +Creating symbolic link %x +Креирам симболичну везу %x + +Creating folder %x +Креирам фолдер %x + +Overwriting file %x +Замењујем датотеку %x + +Overwriting symbolic link %x +Замењујем симболичну везу %x + +Verifying file %x +Проверавам датотеку %x + +Updating attributes of %x +Обнављам атрибуте од %x + +Cannot find %x. +Не могу пронаћи %x. + +Target folder %x already existing. +Одредишни фолдер %x већ постоји. + +Target folder input field must not be empty. +Поље за одабир одредишног фолдера не може бити празно. + +Please enter a target folder for versioning. +Молим унесите одредишни фолдер за верзионирање. + +Source folder %x not found. +Изворни фолдер %x није пронађен. + +The following items have unresolved conflicts and will not be synchronized: +Следеће ставке имају неразрешених конфликата и неће бити синхронизоване: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +Следећи фолдери су значајно различити. Обезбедите да сигурно упарујете праве фолдере за синхронизацију. + +Not enough free disk space available in: +Недовољно простора на диску у: + +Required: +Потребно: + +Available: +Доступно: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Фолдер ће бити промењен који је део више парова фолдера. Молимо проверите синхронизацијска подешавања. + +Synchronizing folder pair: +Синхронизовање фолдерског пара: + +Generating database... +Генерисање базе података... + +Creating a Volume Shadow Copy for %x... +Креирање Volume Shadow Copy за %x... + +Data verification error: %x and %y have different content. +Грешка при провери података: %x и %y имају различит садржај. + +job name +име задатка + +Synchronization stopped +Синхронизација заустављена + +Synchronization completed with errors +Синхронизација завршена с грешкама + +Synchronization completed with warnings +Синхронизација завршена с упозорењима + +Nothing to synchronize +Нема ничега за синхронизацију + +Synchronization completed successfully +Синхронизација успешно завршена + +Saving log file %x... +Уписујем лог датотеку %x... + +You can switch to FreeFileSync's main window to resolve this issue. +Можете се пребацити на главни прозор FreeFileSync-а да би разрешили ову ствар. + +&Don't show this warning again +&Не приказуј ово упозорење поновно + +&Ignore +&Игнориши + +&Switch +&Замени + +Switching to FreeFileSync's main window +Пребацујем се на главни прозор FreeFileSync-а + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + +Аутоматски покушај за %x секунду... +Аутоматски покушај за %x секунде... +Аутоматски покушај за %x секунди... + + +&Ignore subsequent errors +&Занемари грешке које даље следе + +Retrying operation... + + +Serious Error +Озбиљна грешка + +Check for Program Updates +Провери постојање надоградње програма + +A new version of FreeFileSync is available: +Нова верзија FreeFileSync је доступна: + +Download now? +Преузети сада? + +&Download +&Преузми + +FreeFileSync is up to date. +FreeFileSync је ажуриран. + +Unable to connect to sourceforge.net. +Не могу се повезати на sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Не могу на мрежи пронаћи тренутни број верзије FreeFileSync-а. Да ли желите да проверите ручно? + +&Check + + +Symlink +Сим-веза + +Folder +Фолдер + +Full path +Пуна путања + +Name +Име + +Relative path +Релативна путања + +Base folder +Основни фолдер + +Size +Величина + +Date +Датум + +Extension +Екстензија + +Category +Категорија + +Action +Акција + +Drag && drop +Вуци && пусти + +Close progress dialog +Затвори дијалог прогреса + +Standby +Пређи у стање приправности + +Log off +Одјави се + +Shut down +Искључи рачунар + +Hibernate +Хибернација + +Alternate comparison settings +Алтернативна подешавања упоређивања + +Alternate synchronization settings +Алтернативна подешавања синхронизације + +Local filter +Локални филтер + +Active +Активан + +None +Ниједан + +Remove alternate settings +Уклони алтернативна подешавања + +Clear filter settings +Уклони подешавања филтера + +Copy +Копирај + +Paste +Залепи + +Alternate Comparison Settings +Алтернативна подешавања упоређивања + +Alternate Synchronization Settings +Алтернативна подешавања синхронизације + +Local Filter +Локални филтер + +&New +&Ново + +&Save +&Сачувај + +Save as &batch job... +Сачувај као &беч задатак... + +1. &Compare +1. &Упореди + +2. &Synchronize +2. &Синхронизуј + +&Global settings +&Глобална подешавања + +&Language +&Језик + +&Find... +&Нађи... + +&Export file list... +&Извоз листе датотека... + +&Tools +&Алати + +&Check now +&Провери сада + +Check &automatically once a week +Провери &аутоматски једном недељно + +&Check for new version +&Провери постојање нове верзије + +Compare +Упореди + +Cancel +Одустани + +Synchronize +Синхронизуј + +Add folder pair +Додај фолдер пар + +Remove folder pair +Уклони фолдер пар + +Swap sides +Замени стране + +Close search bar +Затвори траку за претраживање + +Find: +Нађи: + +Match case +По величини слова + +Save as batch job +Сачувај као беч задатак + +Hide excluded items +Сакриј искључене ставке + +Show filtered or temporarily excluded files +Прикажи филтриране или привремено искључене датотеке + +Number of files and folders that will be created +Број датотека и фолдера који ће бити креирани + +Number of files that will be overwritten +Број датотека које ће бити замењене у садржају + +Number of files and folders that will be deleted +Број датотека и фолдера који ће бити избрисани + +Total bytes to copy +Укупно бајтова за копирање + +Select a variant: +Одаберите варијанту: + +Identify equal files by comparing modification time and size. +Идентификуј једнаке датотеке упоређивањем времена промена и величина. + +Identify equal files by comparing the file content. +Идентификуј једнаке датотеке упоређивањем садржаја датотека. + +Symbolic links: +Симболичне везе: + +More information +Више информација + +OK +У реду + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Пронађи и изврши промене на обе стране. Брисања, премештања и конфликти се откривају аутоматски употребом базе података. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Креирај огледални бекап левог фолдера који ће се тачно подударати са десним фолдером након синхронизације. + +Copy new and updated files to the right folder. +Копирај нове и ажуриране датотеке у десни фолдер. + +Configure your own synchronization rules. +Конфигуришите ваша властита синхронизациона правила. + +Detect moved files +Уочи премештене датотеке + +Requires database files. Not supported by all file systems. +Потребне датотеке базе. Није подржано за све системе датотека. + +Delete files: +Обриши датотеке: + +Permanent +Трајно + +Delete or overwrite files permanently +Трајно избриши или замени датотеке + +Recycle bin +Корпа за смеће + +Back up deleted and overwritten files in the recycle bin +Направи резервне копије обрисаних и замењених датотека у Корпи за смеће + +Versioning +Верзионирање + +Move files to a user-defined folder +Премести датотеке у кориснички одабран фолдер + +Naming convention: +Правило именовања: + +Show examples +Прикажи примере + +Handle errors: +Обрада грешака: + +Ignore +Игнориши + +Hide all error and warning messages +Сакриј све грешке и упозорења + +Pop-up +Искачући прозор + +Show pop-up on errors or warnings +Прикажи искачући прозор при грешкама и упозорењима + +On completion: +При завршетку: + +Start synchronization now? +Почни синхронизацију сада? + +Variant: +Варијанта: + +Statistics +Статистика + +&Don't show this dialog again +&Не приказуј овај диалог поновно + +Items found: +Пронађене ставке: + +Speed: +Брзина: + +Time remaining: +Преостало време: + +Time elapsed: +Протекло време: + +Synchronizing... +Синхронизујем... + +Minimize to notification area +Минимизирај у области за обавештења + +Close +Затвори + +&Pause +&Пауза + +Stop +Заустави + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Креирај беч датотеку за ненадзирану синхронизацију. Да би почели, кликните дуплим кликом ову датотеку или додајте задатак у таск менаџеру: %x + +Stop synchronization at first error +Заустави синхронизацију при првој грешци + +Show progress dialog +Прикажи дијалог прогреса + +Save log: +Сачувај лог: + +Limit: +Ограничи: + +Limit maximum number of log files +Ограничи максималан број лог датотека + +How can I schedule a batch job? +Како могу заказати беч задатак? + +&Recycle bin +&Корпа за смеће + +Delete on both sides +Избриши на обе стране + +Delete on both sides even if the file is selected on one side only +Избриши на обе стране чак иако је датотека селектована само на једној страни + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Одаберите филтерска подешавања да би искључили одређене датотеке из синхронизације. Унесите путање датотека релативно према њиховим одговарајућим фолдерским паровима. + +Include: +Укључи: + +Exclude: +Искључи: + +Time span: +Временско ограничење: + +File size: +Величина датотеке: + +Minimum: +Минимум: + +Maximum: +Максимум: + +&Clear +&Уклони + +The following settings are used for all synchronization jobs. +Следећа подешавања се користе за све синхронизацијске задатке. + +Fail-safe file copy +Копирање заштићено од грешака + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Копирај у привремену датотеку (*.ffs_tmp) пре уклањања циљане ставке. +Ово гарантује непромењивост чак и у случају озбиљне грешке. + + +(recommended) +(препоручено) + +Copy locked files +Копирај закључане датотеке + +Copy shared or locked files using the Volume Shadow Copy Service. +Копирај дељене или закључане датотеке користећи Volume Shadow Copy сервис. + +(requires administrator rights) +(потребна администраторска права) + +Copy file access permissions +Копирај овлашћења приступа датотекама + +Transfer file and folder permissions. +Премести овлашћења приступа датотека и фолдера. + +Automatic retry on error: +Аутоматски покушај при грешци: + +Retry count: +Број покушаја: + +Delay (in seconds): +Паузирање (у секундама): + +Customize context menu: +Прилагоди контекстни мени: + +Description +Опис + +Restore hidden windows +Прикажи скривене прозоре + +&Default +&Подразумевано + +Source code written in C++ using: +Изворни код написан у C++ уз коришћење: + +If you like FreeFileSync +Ако вам се свиђа FreeFileSync + +Donate with PayPal +Донација са PayPal-ом + +Feedback and suggestions are welcome +Повратне информације и предлози су добродошли + +Homepage +Веб страница + +Email +И-меил + +Published under the GNU General Public License +Објављено под ГНУ Општом јавном лиценцом + +Many thanks for localization: +Велике похвале за локализацију: + +Save as Batch Job +Сачувај као беч задатак + +Delete Items +Избриши ставке + +Global Settings +Глобална подешавања + +Select Time Span +Изаберите временски распон + +Folder Pairs +Фолдерски парови + +Find +Пронађи + +Overview +Преглед + +Configuration +Подешавања + +Main Bar +Главна трака + +Filter Files +Филтрирање датотека + +Select View +Изаберите приказ + +Open... +Отвори... + +Save +Сачувај + +Compare both sides +Упореди обе стране + +Comparison settings +Подешавања упоређивања + +Synchronization settings +Подешавања синхронизације + +Start synchronization +Почни синхронизацију + +Confirm +Потврди + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + +Да ли стварно желите да извршите команду %y за %x ставку? +Да ли стварно желите да извршите команду %y за %x ставке? +Да ли стварно желите да извршите команду %y за %x ставки? + + +&Execute +&Изврши + + +1 directory +%x directories + + +%x фолдер +%x фолдера +%x фолдера + + + +1 file +%x files + + +%x датотека +%x датотеке +%x датотека + + + +%y of 1 row in view +%y of %x rows in view + + +%y од %x реда у приказу +%y од %x реда у приказу +%y од %x редова у приказу + + +Set direction: +Одабери смер: + +multiple selection +вишеструки одабир + +Include via filter: +Укључи по филтеру: + +Exclude via filter: +Искључи преко филтера: + +Exclude temporarily +Тренутно искључи + +Include temporarily +Тренутно укључи + +Delete +Избриши + +Include all +Укључи све + +Exclude all +Искључи све + +Show icons: +Прикажи иконе: + +Small +Мале + +Medium +Средње + +Large +Велике + +Select time span... +Одаберите временски распон... + +Default view +Подразумевани приказ + +Show "%x" +Прикажи "%x" + +Last session +Задња сесија + +Folder Comparison and Synchronization +Упоређивање и синхронизација фолдера + +Configuration saved +Подешавања сачувана + +FreeFileSync batch +FreeFileSync беч задатак + +Do you want to save changes to %x? +Да ли желите сачувати промене за %x? + +Never save &changes +Никад не сачувај &промене + +Do&n't save +Не&мој сачувати + +Filter +Филтрирање + +Show files that exist on left side only +Прикажи датотеке које постоје само на левој страни + +Show files that exist on right side only +Прикажи датотеке које постоје само на десној страни + +Show files that are newer on left +Прикажи датотеке које су новије лево + +Show files that are newer on right +Прикажи датотеке које су новије десно + +Show files that are equal +Прикажи једнаке датотеке + +Show files that are different +Прикажи датотеке које су различите + +Show conflicts +Прикажи конфликте + +Show files that will be created on the left side +Прикажи датотеке које ће бити креиране на левој страни + +Show files that will be created on the right side +Прикажи датотеке које ће бити креиране на десној страни + +Show files that will be deleted on the left side +Прикажи датотеке које ће бити избрисане на левој страни + +Show files that will be deleted on the right side +Прикажи датотеке које ће бити избрисане на десној страни + +Show files that will be overwritten on left side +Прикажи датотеке које ће бити замењене на левој страни + +Show files that will be overwritten on right side +Прикажи датотеке које ће бити замењене на десној страни + +Show files that won't be copied +Прикажи датотеке које неће бити копиране + +Set as default +Постави као подразумевано + +All folders are in sync +Сви фолдери су синхронизовани + +Synchronization Settings +Подешавања синхронизације + +Comparison Settings +Подешавања упоређивања + +Cannot find %x +Немогу пронаћи %x + +Comma-separated values +Зарезом одвојене вредности + +File list exported +Листа датотека експортована + +Searching for program updates... +Претражујем ажурирање за програм... + +Scanning... +Прегледавање... + +Comparing content... +Упоређујем садржај... + +Info +Инфо + +Warning +Упозорење + +Paused +Паузирано + +Initializing... +Покретање... + +Stopped +Заустављено + +Completed +Завршено + +&Continue +&Настави + +Log +Лог + +Today +Данас + +This week +Ове недеље + +This month +Овог месеца + +This year +Ове године + +Last x days +Задњих x дана + +Byte +Бајт + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Да ли стварно желите да преместите следећу %x ставку у Корпи за смеће? +Да ли стварно желите да преместите следеће %x ставке у Корпи за смеће? +Да ли стварно желите да преместите следећих %x ставки у Корпи за смеће? + + +Move +Премести + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Да ли стварно желите да обришете следећу %x ставку? +Да ли стварно желите да обришете следеће %x ставке? +Да ли стварно желите да обришете следећих %x ставки? + + +Exclude +Искључи + +Direct +Непосредно + +Follow +Следи + +Copy NTFS permissions +Копирај NTFS овлашћења + +Integrate external applications into context menu. The following macros are available: +Интегриши спољне апликације у контекстни мени. Следећи макрои су доступни: + +- full file or folder name +- цело име датотеке или фолдера + +- folder part only +- само фолдерски део + +- Other side's counterpart to %item_path% +- Дупликат с друге стране у %item_path% + +- Other side's counterpart to %item_folder% +- Дупликат с друге стране у %item_folder% + +Restore all hidden windows and warnings? +Прикажи све скривене прозоре и упозорења? + +Leave as unresolved conflict +Остави као неразрешени конфликт + +Replace +Замени + +Move files and replace if existing +Премести датотеке и замени их ако већ постоје + +Time stamp +Временска ознака + +Append a timestamp to each file name +Додај временску ознаку сваком имену датотеке + +File +Датотека + +YYYY-MM-DD hhmmss +ГГГГ-ММ-ДД ччммсс + +Files +Датотеке + +Items +Ставке + +Percentage +Проценат + +Cannot monitor directory %x. +Не могу надгледати фолдер %x. + +Conversion error: +Грешка при претварању: + +Cannot delete file %x. +Не могу избрисати датотеку %x. + +The file is locked by another process: +Датотека је блокирана другим процесом: + +Cannot move file %x to %y. +Не могу преместити датотеку %x у %y. + +Cannot delete directory %x. +Не могу избрисати фолдер %x. + +Cannot write file attributes of %x. +Не могу уписати својства од %x. + +Cannot write modification time of %x. +Не могу уписати време промене %x. + +Cannot read security context of %x. +Не могу читати безбедносни садржај %x. + +Cannot write security context of %x. +Не могу уписати безбедносни садржај %x. + +Cannot read permissions of %x. +Не могу читати овлашћења од %x. + +Cannot write permissions of %x. +Не могу уписати овлашћења за %x. + +Cannot create directory %x. +Не могу креирати фолдер %x. + +Cannot create symbolic link %x. +Не могу креирати симболичну везу %x. + +Cannot find system function %x. +Не могу пронаћи системску функцију %x. + +Cannot copy file %x to %y. +Не могу копирати датотеку %x на %y. + +Type of item %x is not supported: +Тип ставке %x није подржан: + +Cannot resolve symbolic link %x. +Не могу разрешити симболичну везу %x. + +Cannot open directory %x. +Не могу отворити фолдер %x. + +Cannot enumerate directory %x. +Не могу излистати фолдер %x. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +%x мин +%x мин +%x мин + + + +1 hour +%x hours + + +%x сат +%x сата +%x сати + + + +1 day +%x days + + +%x дан +%x дана +%x дана + + +Unable to register to receive system messages. +Није могућа регистрација примања системских порука. + +Cannot set privilege %x. +Не могу поставити права за %x. + +Unable to suspend system sleep mode. +Није могуће суспендовање мода спавања система. + +Cannot change process I/O priorities. +Не може се променити процес I/O приоритета + +Unable to move %x to the recycle bin. +Није могуће преместити %x у Корпи за смеће. + +Cannot determine final path for %x. +Не могу утврдити коначну путању за %x. + +Error Code %x: +Грешка број %x: + diff --git a/FreeFileSync/Build/Languages/slovenian.lng b/FreeFileSync/Build/Languages/slovenian.lng new file mode 100644 index 00000000..92f38be4 --- /dev/null +++ b/FreeFileSync/Build/Languages/slovenian.lng @@ -0,0 +1,1539 @@ +
    + Slovenščina + Matej Badalič, Tine Mlakar + sl_SI + flag_slovenia.png + 4 + n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3 +
    + +Both sides have changed since last synchronization. +Obe strani sta se spremenili od zadnje sinhronizacije. + +Cannot determine sync-direction: +Ne morem določiti sinhronizacijske smeri. + +No change since last synchronization. +Ni sprememb od zadnje sinhronizacije. + +The database entry is not in sync considering current settings. +Glede na trenutne nastavitve vnos v podatkovni bazi ni sinhroniziran. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Nastavljanje privzetih smeri sinhronizacije: Stare datoteke bodo prepisane z novimi datotekami. + +Checking recycle bin availability for folder %x... +Preverjam razpoložljivost koša za mapo %x... + +Moving file %x to the recycle bin +Premikam datoteko %x koš + +Moving folder %x to the recycle bin +Premikam imenik %x v koš + +Moving symbolic link %x to the recycle bin +Premikam simbolično povezavo %x v koš + +Deleting file %x +Brisanje datoteke %x + +Deleting folder %x +Brisanje mape %x + +Deleting symbolic link %x +Brisanje simboličnih povezav %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +Za sledeče imenike koš ni na voljo. Datoteke bodo namesto tega dokončno izbrisane: + +An exception occurred +Zgodila se je napaka + +A directory path is expected after %x. +Za %x se pričakuje pot do imenika. + +Syntax error +Sintaktična napaka + +Cannot open file %x. +Ne morem odpreti datoteke %x. + +File %x does not contain a valid configuration. +Datoteka %x ne vsebuje veljavnih nastavitev + +Unequal number of left and right directories specified. +Vnešeno je neenako število levih in desnih imenikov. + +The config file must not contain settings at directory pair level when directories are set via command line. +Konfiguracijska datoteka ne sme vsebovati nastavitev na ravni imeniških parov, če so imeniki nastavljeni prek ukazne vrstice. + +Directories cannot be set for more than one configuration file. +Imeniki ne morejo biti nastavljeni za eč kot eno konfiguracijsko datoteko. + +Command line +Ukazna vrstica + +Syntax: +Sintaksa: + +config files +konfiguracijske datoteke + +directory +imenik + +Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. +Poljubno število FreeFileSync .ffs_gui in/ali .ffs_batch konfigracijskih datotek. + +Any number of alternative directories for at most one config file. +Poljubno število alternatvnih imenikov za največ eno konfiguracijsko datoteko. + +A folder input field is empty. +Vnosno polje za mapo je prazno. + +The corresponding folder will be considered as empty. +Ustrezajoča mapa bo smatrana kot prazna. + +Cannot find the following folders: +Ne morem najti naslednjih map: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +To napako, ki smatra vsako mapo kot prazno, lahko ignorirate. Mape bodo potem samodejno ustvarjene med sinhronizacijo. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +Naslednje mape imajo odvisne poti. Bodite previdni pri nastavitvi sinhronizacijskih pravil: + +File %x has an invalid date. +Datoteka %x ima neveljaven datum. + +Date: +Datum: + +Files %x have the same date but a different size. +Datoteki %x imata enak datum ampak različno velikost. + +Size: +Velikost: + +Items differ in attributes only +Elementi se razlikujejo samo v atributih + +Resolving symbolic link %x +Razrešujem simbolično povezavo %x + +Comparing content of files %x +Primerjam vsebino datotek %x + +Generating file list... +Ustvarjam seznam datotek... + +Starting comparison +Začenjam primerjavo + +Calculating sync directions... +Preračunavam sinhronizacijske smeri... + +Out of memory. +Zmanjkalo pomnilnika. + +Item exists on left side only +Element obstaja samo na levi strani + +Item exists on right side only +Element obstaja samo na desni strani + +Left side is newer +Leva stran je novejša + +Right side is newer +Desna stran je novejša + +Items have different content +Elementi imajo različno vsebino + +Both sides are equal +Obe strani sta enaki + +Conflict/item cannot be categorized +Spor/element ne more biti kategoriziran + +Copy new item to left +Kopiraj nov element na levo + +Copy new item to right +Kopiraj nov element na desno + +Delete left item +Izbriši levi element + +Delete right item +Izbriše desni element + +Move file on left +Premakni datoteko na levo + +Move file on right +Premakni datoteko na desno + +Overwrite left item +Prepiši levi element + +Overwrite right item +Prepiši desni element + +Do nothing +Ne naredi ničesar + +Update attributes on left +Posodobi atribute na levi + +Update attributes on right +Posodobi atribute na desni + +Database file %x is incompatible. +Datoteka podatkovne baze %x je nekompatibilna. + +Initial synchronization: +Začetna sinhronizacija: + +Database file %x does not yet exist. +Datoteka podatkovne baze %x še ne obstaja. + +Database file is corrupt: +Datoteka podatkovne baze je poškodovana: + +Cannot write file %x. +Ne morem zapisati datoteke %x. + +Cannot read file %x. +Ne morem prebrati datoteke %x. + +Database files do not share a common session. +Datoteke podatkovne baze si ne delijo skupne seje. + +Searching for folder %x... +Iskanje mape %x... + +Cannot read file attributes of %x. +Ne morem brati datotečnih atributov od %x. + +Cannot get process information. +Ne morem pridobiti informacij o procesu. + +Waiting while directory is locked (%x)... +Čakam, medtem ko se zaklepa imenik (%x)... + + +1 sec +%x sec + + +%x sek +%x sek +%x sek +%x sek + + +Creating file %x +Ustvarjam datoteko %x + +Items processed: +Obdelanih elementov: + +Items remaining: +Preostalih elementov: + +Total time: +Celoten čas: + + +1 byte +%x bytes + + +%x bajt +%x bajta +%x bajti +%x bajtov + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Napaka pri razčlenjevanju datoteke %x, vrstica %y, stolpec %z. + +Cannot set directory lock for %x. +Ne morem nastaviti zaklepanja imenikov za %x. + +Scanning: +Pregledujem: + + +1 thread +%x threads + + +%x nit +%x niti +%x niti +%x niti + + +Encoding extended time information: %x +Podrobne informacije o času enkodiranja: %x + +/sec +/sek + +%x items/sec +%x elementov/s + +Configuration file %x loaded partially only. +Nastavitvena datoteka %x naložena samo delno. + +Show in Explorer +Prikaži v Raziskovalcu + +Open with default application +Odpri s privzeto aplikacijo + +Browse directory +Brskaj po imeniku + +Cannot access the Volume Shadow Copy Service. +Ne morem dostopati do Volume Shadov Copy servisa. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Prosimo uporabite 64-bitno različico FreeFileSync za ustvarjanje senčnih kopij na tem sistemu. + +Cannot load file %x. +Ne morem naložiti datoteko %x. + +Cannot determine volume name for %x. +Ne morem določiti ime nosilca za %x. + +Volume name %x is not part of file path %y. +Ime nosilca %x ni del poti datoteke %y. + +Stop requested: Waiting for current operation to finish... +Zahteva za ustavitev: Čakam da se trenutni proces zaključi... + +Unable to create timestamp for versioning: +Ne morem ustvariti časovnega žiga za verzioniranje: + +Cannot read the following XML elements: +Ne morem brati naslednje XML elemente: + +&Open... +&Odpri... + +Save &as... +Shr&ani kot... + +&Quit +&Zapri + +&Program +&Program + +&View help +&Prikaži pomoč + +&About +&O programu + +&Help +&Pomoč + +Usage: +Uporaba: + +1. Select folders to watch. +1. Izberite imenike za opazovanje + +2. Enter a command line. +2. Vnesite ukazno-vrstico. + +3. Press 'Start'. +3. Pritisnite 'Začni'. + +To get started just import a .ffs_batch file. +Da začnete uvozite datoteko .ffs_batch + +Folders to watch: +Imeniki za pregled: + +Add folder +Dodaj imenik + +Remove folder +Odstrani v imenik + +Browse +Brskaj + +Select a folder +Izberite imenik + +Idle time (in seconds): +Nedejavni čas (v sekundah): + +Idle time between last detected change and execution of command +Čas nedejavnosti med zadnjo zaznano spremembo in izvršitvijo ukaza + +Command line: +Ukazna vrstica: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +Ukaz se sproži če: +- se spremenijo datoteke ali podmape +- pridejo nove mape (npr. ob vstavitvi USB ključka) + + +&Start +&Zaženi + +About +O programu(1) + +Build: %x +Izgradnja: %x + +All files +Vse datoteke + +Automated Synchronization +Avtomatska sinhnorizacija + +Directory monitoring active +Nadzor imenikov je aktven + +Waiting until all directories are available... +Čakam da so vsi imeniki dostopni... + +Error +Napaka + +&Restore +&Obnovi + +&Show error +&Pokaži napako + +&Exit +&Izhod + +Incorrect command line: +Napačna ukazna vrstica: + +&Retry +&Ponovi + +File content +Vsebina datoteke + +File time and size +Čas in velikost datoteke + +Two way +Obojesmerno + +Mirror +Zrcalno + +Update +Posodobi + +Custom +Po meri + +Multiple... +Večkratno... + +Moving file %x to %y +Premikam datoteko %x v %y + +Moving folder %x to %y +Premikam mapo %x v %y + +Moving symbolic link %x to %y +Premikam simbolično povezavo %x v %y + +Removing old versions... +Odstranjujem stare različice... + +Creating symbolic link %x +Ustvarjam simbolično povezavo %x + +Creating folder %x +Ustvarjam mapo %x + +Overwriting file %x +Prepisujem datoteko %x + +Overwriting symbolic link %x +Prepisujem simbolično povezavo %x + +Verifying file %x +Preverjam datoteko %x + +Updating attributes of %x +Posodabljam atribute od %x + +Cannot find %x. +Ne morem najti %x. + +Target folder %x already existing. +Ciljna mapa %x že obstaja. + +Target folder input field must not be empty. +Vnosno polje za ciljno mapo ne sme biti prazno. + +Please enter a target folder for versioning. +Prosimo vnesite ciljno mapo za verzioniranje. + +Source folder %x not found. +Izvorna mapa %x se ne najde. + +The following items have unresolved conflicts and will not be synchronized: +Naslednji elementi imajo nerešene konflikte in ne bodo sinhronizirani: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +Naslednji imeniki so občutno različni. Preverite, če primerjate pravilne imenike za sinhnorizacijo. + +Not enough free disk space available in: +Na voljo ni dovolj prostega prostora na disku v: + +Required: +Zahtevano: + +Available: +Na voljo: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Spremenjena bo mapa, ki je del večkratnih parov map. Prosim preglejte nastavitve sinhronizacije. + +Synchronizing folder pair: +Sinhroniziram par map: + +Generating database... +Ustvarjam podatkovno bazo... + +Creating a Volume Shadow Copy for %x... +Ustvarjam Volume Shadow Copy za %x... + +Data verification error: %x and %y have different content. +Napaka pri preverjanju podatkov: %x in %y imata drugačno vsebino. + +job name +naziv opravila + +Synchronization stopped +Sinhnorizacija ustavljena + +Synchronization completed with errors +Sinhronizacija se je končala z napakami + +Synchronization completed with warnings +Sinhronizacija se je končala z opozorili + +Nothing to synchronize +Nič za sinhronizirati + +Synchronization completed successfully +Sinhronizacija se je uspešno končala + +Saving log file %x... +Shranjujem datoteko beleženja %x... + +You can switch to FreeFileSync's main window to resolve this issue. +Preklopite na FreeFileSync glavno okno za odpravo težave. + +&Don't show this warning again +&Ne pokaži več tega opozorila + +&Ignore +&Ignoriraj + +&Switch +&Preklopi + +Switching to FreeFileSync's main window +Preklopi na FreeFileSync glavno okno + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + +Ponovni poskus čez %x sekundo... +Ponovni poskus čez %x sekundi... +Ponovni poskus čez %x sekunde... +Ponovni poskus čez %x sekund... + + +&Ignore subsequent errors +&Ignoriraj nadaljnje napake + +Retrying operation... + + +Serious Error +Resna napaka + +Check for Program Updates +Prevri obstoj nadgradnje programa + +A new version of FreeFileSync is available: +Nova različica FreeFileSync je na voljo: + +Download now? +Prenesem sedaj? + +&Download +&Prenesi + +FreeFileSync is up to date. +FreeFileSync je posodobljen. + +Unable to connect to sourceforge.net. +Ne morem se povezati na sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Na omrežju ne najdem obstoječe verzije FreeFileSync-a. Ali želite preveriti lastnoročno? + +&Check + + +Symlink +Simbolična povezava + +Folder +Mapa + +Full path +Polna pot + +Name +Ime + +Relative path +Relativna pot + +Base folder +Osnovna mapa + +Size +Velikost + +Date +Datum + +Extension +Razširitev + +Category +Kategorija + +Action +Ukrep + +Drag && drop +Povleci && spusti + +Close progress dialog +Zapri pogovorno okno z napredkom + +Standby +V pripravljenost + +Log off +Odjavi + +Shut down +Ugasni + +Hibernate +Hibernacija + +Alternate comparison settings +Alternativne nastavitve primerjave + +Alternate synchronization settings +Alternativne nastavitve sinhnorizacije + +Local filter +Lokalni filter + +Active +Aktivno + +None +Nič + +Remove alternate settings +Odstrani nadomestne nastavitve + +Clear filter settings +Počisti nastavitve filtra + +Copy +Kopiraj + +Paste +Prilepi + +Alternate Comparison Settings +Alternativne nastavitve primerjave + +Alternate Synchronization Settings +Alternativne nastavitve sinhnorizacije + +Local Filter +Lokalni filter + +&New +&Novo + +&Save +&Shrani + +Save as &batch job... +Shrani kot serijsko op&ravilo... + +1. &Compare +1. &Primerjaj + +2. &Synchronize +2. &Sinhroniziraj + +&Global settings +&Skupne nastavitve + +&Language +&Jezik + +&Find... +&Išči... + +&Export file list... +&Izvozi seznam datotek... + +&Tools +&Orodja + +&Check now +P&reveri zdaj + +Check &automatically once a week +S&amodejno preveri enkrat tedensko + +&Check for new version +&Preveri, če obstaja nova verzija + +Compare +Primerjaj + +Cancel +Prekliči + +Synchronize +Sinhroniziraj + +Add folder pair +Dodaj par imenikov + +Remove folder pair +Odstrani par imenikov + +Swap sides +Zamenjaj strani + +Close search bar +Zapri iskalno vrstico + +Find: +Išči: + +Match case +Ujemaj se s primerom + +Save as batch job +Shrani kot serijsko opravilo + +Hide excluded items +Skrij izključene elemente + +Show filtered or temporarily excluded files +Pokaži filtrirane ali začasno izključene datoteke + +Number of files and folders that will be created +Število datotek in map, ki bodo ustvarjene + +Number of files that will be overwritten +Število datotek, ki bodo prepisane + +Number of files and folders that will be deleted +Število datotek in map, ki bodo izbrisane + +Total bytes to copy +Skupno bajtov za kopiranje + +Select a variant: +Izberi možnost: + +Identify equal files by comparing modification time and size. +Določi enake datoteke s primerjavo datuma spremembe in velikosti. + +Identify equal files by comparing the file content. +Določi enake datoteke s primerjavo vsebine. + +Symbolic links: +Simbolične povezave: + +More information +Več informacij + +OK +V redu + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Identificiraj in razširjaj spremembe na obeh straneh. Izbrisi, premiki in spori so samodejno zaznani z uporabo podatkovne baze. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Ustvari zrcalno kopijo levega imenika, tako da se bo desni imenik po sinhnorizaciji popolnoma ujemal. + +Copy new and updated files to the right folder. +Kopiraj nove in posodobljene datoteke v desni imenik. + +Configure your own synchronization rules. +Konfigurirajte vaša lastna sinhronizacijska pravila. + +Detect moved files +Zaznaj premaknjene datoteke + +Requires database files. Not supported by all file systems. +Zahteva podatkovno bazo. Ni podprt s strani vseh datotečnih sistemov. + +Delete files: +Izbriši datoteke: + +Permanent +Trajno + +Delete or overwrite files permanently +Trajno izbriši ali prepiši datoteke + +Recycle bin +Koš + +Back up deleted and overwritten files in the recycle bin +Naredi varnostno kopijo izbrisanih in prepisanih datotek v koš + +Versioning +Ustvarjanje različič + +Move files to a user-defined folder +Premakni datoteke v izbran imenik + +Naming convention: +Konvencija poimenovanja: + +Show examples +Pokaži primere + +Handle errors: +Upravljanje napak: + +Ignore +Ignoriraj + +Hide all error and warning messages +Skrij vsa obvestila o napakah in opozorilih + +Pop-up +Pogovorno okno + +Show pop-up on errors or warnings +Prikaži pojavne napaka ali opozorila + +On completion: +Ob zaključku: + +Start synchronization now? +Zaženem sinhnorizacijo takoj? + +Variant: +Možnost: + +Statistics +Statistika + +&Don't show this dialog again +&Ne pokaži več tega sporočila + +Items found: +Najdenih elementov: + +Speed: +Hitrost: + +Time remaining: +Preostali čas: + +Time elapsed: +Pretečeni čas: + +Synchronizing... +Sinhroniziram... + +Minimize to notification area +Pomanjšaj v območje obvestil + +Close +Zapri + +&Pause +&Premor + +Stop +Ustavi + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Ustvari skriptno datoteko za samodejno sinhnorizacijo. Za zagon dvojno kliknite to datoteko ali pa jo umestite v razporejevalnik opravil: %x + +Stop synchronization at first error +Ustavi sinhnorizacojo ob prvi napaki + +Show progress dialog +Prikazuj pogovorno okno z napredkom + +Save log: +Shrani dnevnik: + +Limit: +Omejitev: + +Limit maximum number of log files +Omeji maksimalno število datotek beleženja + +How can I schedule a batch job? +Kako nastavim urnik za serijsko opravilo? + +&Recycle bin +&Koš + +Delete on both sides +Izbriši na obeh straneh + +Delete on both sides even if the file is selected on one side only +Izbriši na obeh straneh, četudi je datoteka izbrana na samo eni strani + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Izberi filtrirna pravila za izključitev določenih datotek iz sinhnorizacije. Vpiši pot datotek relativno na imenik v obdelavi + +Include: +Vključi: + +Exclude: +Izključi: + +Time span: +Časovno obdobje + +File size: +Velikost datoteke: + +Minimum: +Minimum: + +Maximum: +Maksimum: + +&Clear +P&očisti + +The following settings are used for all synchronization jobs. +Naslednje nastavitve se uporabljajo pri vseh sinhronizacijskih opravilih. + +Fail-safe file copy +Kopiranje datotek varno pred odpovedjo + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Kopiraj v začasno datoteko (*.ffs_tmp) preden prepišeš cilj. +To zagotavlja konsistenco podatkov v primeru napake. + + +(recommended) +(priporočeno) + +Copy locked files +Kopiraj zaklenjene datoteke + +Copy shared or locked files using the Volume Shadow Copy Service. +Kopiraj zaklenjene in datoteke v skupni rabi s pomočjo Shadow Copy Service. + +(requires administrator rights) +(zahteva pravice administratorja) + +Copy file access permissions +Kopiraj dovoljenja dostopov datoteke + +Transfer file and folder permissions. +Prenesi pravice datotek in map. + +Automatic retry on error: +Ob napaki avtomatsko poskusi znova: + +Retry count: +Število poiskusov: + +Delay (in seconds): +Zakasnitev (v sekundah): + +Customize context menu: +Prilagodi vsebinski meni: + +Description +Opis + +Restore hidden windows +Obnovi skrita okna + +&Default +&Privzeto + +Source code written in C++ using: +Izvorna koda napisana v C++ z uporabo: + +If you like FreeFileSync +Če vam je FreeFileSync všeč + +Donate with PayPal +Doniraj s PayPal + +Feedback and suggestions are welcome +Povratne informacije in predlogi so dobrodošli + +Homepage +Domača stran + +Email +Email + +Published under the GNU General Public License +Objavljeno pod licenco GNU General Public + +Many thanks for localization: +Zahvale za lokalizacijo: + +Save as Batch Job +Shrani kot serijsko opravilo + +Delete Items +Izbriši elemente + +Global Settings +Skupne nastavitve + +Select Time Span +Izberi časovno obdobje + +Folder Pairs +Pari imenikov + +Find +Najdi + +Overview +Pregled + +Configuration +Konfiguracija + +Main Bar +Glavna vrstica + +Filter Files +Filtriraj datoteke + +Select View +Izberi pogled + +Open... +Odpri... + +Save +Shrani + +Compare both sides +Primerjaj obe strani + +Comparison settings +Nastavitve primerjanja + +Synchronization settings +Nastavitve sinhronizacije + +Start synchronization +Začni sinhronizacijo + +Confirm +Potrdi + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + + + +&Execute +&Izvedi + + +1 directory +%x directories + + +%x imenik +%x imenika +%x imeniki +%x imenikov + + + +1 file +%x files + + +%x datoteka +%x datoteki +%x datoteke +%x datotek + + + +%y of 1 row in view +%y of %x rows in view + + +%y od %x vrstice prikazana +%y od %x vrstic prikazanih +%y od %x vrstic prikazanih +%y od %x vrstic prikazanih + + +Set direction: +Nastavi smer: + +multiple selection +mnogokratna izbira + +Include via filter: +Vključi preko filtra: + +Exclude via filter: +Izključi preko filtra: + +Exclude temporarily +Začasno izključi + +Include temporarily +Trenutno vključi + +Delete +Izbriši + +Include all +Vključi vse + +Exclude all +Izključi vse + +Show icons: +Prikaži ikone: + +Small +Majhna + +Medium +Srednja + +Large +Velika + +Select time span... +Izberite časovni okvir... + +Default view +Privzeti pogled + +Show "%x" +Prikaži "%x" + +Last session +Zadnja seja + +Folder Comparison and Synchronization +Primerjava in sinhronizacija imenika + +Configuration saved +Konfiguracija shranjena + +FreeFileSync batch +FreeFileSync paket + +Do you want to save changes to %x? +Ali želite shraniti spremembe v %x? + +Never save &changes +Nikoli ne shrani &sprememb + +Do&n't save +Ne shra&ni + +Filter +Filter + +Show files that exist on left side only +Prikaži datoteke, ki obstajajo samo na levi + +Show files that exist on right side only +Prikaži datoteke, ki obstajajo samo na desni + +Show files that are newer on left +Prikaži datoteke, ki so novejše na levi + +Show files that are newer on right +Prikaži datoteke, ki so novejše na desni + +Show files that are equal +Prikaži datoteke, ki so enake + +Show files that are different +Prikaži datoteke, ki so različne + +Show conflicts +Prikaži spore + +Show files that will be created on the left side +Prikaži datoteke, ki bodo ustvarjene na levi strani + +Show files that will be created on the right side +Prikaži datoteke, ki bodo ustvarjene na desni strani + +Show files that will be deleted on the left side +Prikaži datoteke, ki bodo izbrisane na levi strani + +Show files that will be deleted on the right side +Prikaži datoteke, ki bodo izbrisane na desni strani + +Show files that will be overwritten on left side +Prikaži datoteke, ki bodo prepisane na levi strani + +Show files that will be overwritten on right side +Prikaži datoteke, ki bodo prepisane na desni strani + +Show files that won't be copied +Prikaži datoteke, ki ne bodo kopirane + +Set as default +Nastavi kot privzeto + +All folders are in sync +Vse mape so sinhronizirane + +Synchronization Settings +Nastavitve sinhnorizacije + +Comparison Settings +Nastavitve primerjave + +Cannot find %x +Ne najdem %x + +Comma-separated values +Vrednosti ločene z vejico + +File list exported +Seznam datotek je bil izvožen + +Searching for program updates... +Iščem posodobitve programa... + +Scanning... +Pregledujem... + +Comparing content... +Primerjam vsebino... + +Info +Info + +Warning +Pozor + +Paused +Na premoru + +Initializing... +Inicializiram... + +Stopped +Ustavljen + +Completed +Zaključeno + +&Continue +&Nadaljuj + +Log +Dnevnik + +Today +Danes + +This week +Ta teden + +This month +Ta mesec + +This year +To leto + +Last x days +Zadnjih x dni + +Byte +Bajt + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Ali res želite premakniti sledeč %x element v koš? +Ali res želite premakniti sledeča %x elementa v koš? +Ali res želite premakniti sledeče %x elemente v koš? +Ali res želite premakniti sledečih %x elementov v koš? + + +Move +Premakni + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Ali resnično želite izbrisati sledeči %x element? +Ali resnično želite izbrisati naslednja %x elementa? +Ali resnično želite izbrisati naslednje %x elemente? +Ali resnično želite izbrisati naslednjih %x elementov? + + +Exclude +Izključi + +Direct +Neposredno + +Follow +Sledi + +Copy NTFS permissions +Kopiraj NTFS dovoljenja + +Integrate external applications into context menu. The following macros are available: +Integriraj zunanje aplikacije v kontekstni menu. Na voljo so naslednji makri: + +- full file or folder name +- polno ime datoteke ali mape + +- folder part only +- samo del glede mape + +- Other side's counterpart to %item_path% +- Na drugi strani nasprotno v %item_path% + +- Other side's counterpart to %item_folder% +- Na drugi strani nasprotno v %item_folder% + +Restore all hidden windows and warnings? +Obnovim vsa skrita okna in opozorila? + +Leave as unresolved conflict +Pusti kot nerešeni spor + +Replace +Zamenjaj + +Move files and replace if existing +Premakne datoteke in jih zamenja, če obstajajo + +Time stamp +Časovna oznaka + +Append a timestamp to each file name +Dodaj časovno oznako k vsakemu imenu datoteke + +File +Datoteka + +YYYY-MM-DD hhmmss +LLLL-MM-DD hhmmss + +Files +Datoteke + +Items +Elementi + +Percentage +Odstotek + +Cannot monitor directory %x. +Ne morem nadzorovati imenika %x. + +Conversion error: +Napaka pri pretvorbi: + +Cannot delete file %x. +Ne morem izbrisati datoteke %x. + +The file is locked by another process: +Datoteka je zaklenjena s strani drugega procesa: + +Cannot move file %x to %y. +Ne morem premakniti datoteko %x v %y. + +Cannot delete directory %x. +Ne morem izbrisati imenika %x. + +Cannot write file attributes of %x. +Ne morem zapisati datotečnih atributov od %x. + +Cannot write modification time of %x. +Ne morem zapisati časa spremembe od %x. + +Cannot read security context of %x. +Ne morem prebrati varnostnega konteksta od %x. + +Cannot write security context of %x. +Ne morem zapisati varnostni kontekst od %x + +Cannot read permissions of %x. +Ne morem prebrati dovoljenja od %x. + +Cannot write permissions of %x. +Ne morem zapisati dovoljenj od %x. + +Cannot create directory %x. +Ne morem ustvariti imenika %x. + +Cannot create symbolic link %x. +Ne morem ustvariti simbolične povezave %x. + +Cannot find system function %x. +Ne morem najti sistemske funkcije %x. + +Cannot copy file %x to %y. +Ne morem kopirati datoteke %x v %y. + +Type of item %x is not supported: +Element tipa %x ni podprt: + +Cannot resolve symbolic link %x. +Ne morem razrešiti simbolične povezave %x. + +Cannot open directory %x. +Ne morem odpreti imenika %x. + +Cannot enumerate directory %x. +Ne morem oštevilčiti imenika %x. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +%x min +%x min +%x min +%x min + + + +1 hour +%x hours + + +%x ura +%x uri +%x ure +%x ur + + + +1 day +%x days + + +%x dan +%x dni +%x dni +%x dni + + +Unable to register to receive system messages. +Ne morem se registriratiza prejem sistemskih sporočil. + +Cannot set privilege %x. +Ne morem nastaviti privilegija %x. + +Unable to suspend system sleep mode. +Ne morem preprečiti mirovanja sistema. + +Cannot change process I/O priorities. +Ne morem spremeniti V/I prioritet procesa. + +Unable to move %x to the recycle bin. +Ne morem premakniti %x v koš- + +Cannot determine final path for %x. +Ne morem določiti končne poti za %x. + +Error Code %x: +Koda napake %x: + diff --git a/FreeFileSync/Build/Languages/spanish.lng b/FreeFileSync/Build/Languages/spanish.lng new file mode 100644 index 00000000..0ad717bf --- /dev/null +++ b/FreeFileSync/Build/Languages/spanish.lng @@ -0,0 +1,1517 @@ +
    + Español + I.R.Maturana (irmlab.com) + es_ES + flag_spain.png + 2 + n == 1 ? 0 : 1 +
    + +Both sides have changed since last synchronization. +Ambos lados han cambiado desde la última sincronización. + +Cannot determine sync-direction: +No se puede determinar la dirección de sincronización: + +No change since last synchronization. +Ningún cambio desde la última sincronización. + +The database entry is not in sync considering current settings. +La entrada de la base de datos no está sincronizada, de acuerdo con la configuración actual. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Fijando direcciones de sincronización predeterminadas: los archivos nuevos sobrescribirán los archivos antiguos. + +Checking recycle bin availability for folder %x... +Comprobando disponibilidad de la papelera de reciclaje para la carpeta %x… + +Moving file %x to the recycle bin +Mover archivo %x a la papelera de reciclaje + +Moving folder %x to the recycle bin +Mover carpeta %x a la papelera de reciclaje + +Moving symbolic link %x to the recycle bin +Mover vínculo simbólico %x a la papelera de reciclaje + +Deleting file %x +Borrar archivo %x + +Deleting folder %x +Borrar carpeta %x + +Deleting symbolic link %x +Borrar vínculo simbólico %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +La papelera de reciclaje no está disponible para las carpetas siguientes. Los archivos se borrarán de forma permanente: + +An exception occurred +Ha ocurrido una excepción + +A directory path is expected after %x. +Se esperaba una ruta de directorio después de %x. + +Syntax error +Error de sintaxis + +Cannot open file %x. +No se puede abrir el archivo %x. + +File %x does not contain a valid configuration. +El archivo %x no contiene una configuración válida. + +Unequal number of left and right directories specified. +Desigualdad en el número de directorios especificados a izquierda y derecha. + +The config file must not contain settings at directory pair level when directories are set via command line. +El archivo de configuración no debe incluir parámetros al nivel de un par de directorios cuando éstos se especifican desde la línea de comandos. + +Directories cannot be set for more than one configuration file. +No se pueden definir directorios para más de un archivo de configuración. + +Command line +Línea de comandos + +Syntax: +Sintaxis: + +config files +archivos de configuración + +directory +directorio + +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 directories for at most one config file. +Cualquier número de directorios alternativos para un archivo de configuración como máximo. + +A folder input field is empty. +Un campo de entrada de la carpeta está vacío. + +The corresponding folder will be considered as empty. +La siguiente carpeta será considerada como vacía. + +Cannot find the following folders: +No se pudieron encontrar las siguiente carpetas: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Ignore este error si desea tratar cada carpeta como vacía. En tal caso, se crearán estas carpetas automáticamente durante la sincronización. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +Las carpetas siguientes usan rutas dependientes. Tenga cuidado al definir reglas de sincronización: + +File %x has an invalid date. +El archivo %x tiene una fecha inválida. + +Date: +Fecha: + +Files %x have the same date but a different size. +Los archivos %x tienen la misma fecha pero un tamaño diferente. + +Size: +Tamaño: + +Items differ in attributes only +Los elementos sólo se diferencian en los atributos + +Resolving symbolic link %x +Resolviendo el vínculo simbólico %x + +Comparing content of files %x +Comparación del contenido de los archivos %x + +Generating file list... +Generando lista de archivos… + +Starting comparison +Iniciando la comparación + +Calculating sync directions... +Calculando direcciones de sincronización… + +Out of memory. +Sin memoria. + +Item exists on left side only +El elemento existe sólo en el lado izquierdo + +Item exists on right side only +El elemento existe sólo en el lado derecho + +Left side is newer +El lado izquierdo es más reciente + +Right side is newer +El lado derecho es más reciente + +Items have different content +Los elementos tienen contenido distinto + +Both sides are equal +Ambos lados son iguales + +Conflict/item cannot be categorized +No se puede categorizar el conflicto o elemento + +Copy new item to left +Copiar nuevo elemento a la izquierda + +Copy new item to right +Copiar nuevo elemento a la derecha + +Delete left item +Eliminar elemento izquierdo + +Delete right item +Eliminar elemento derecho + +Move file on left +Mover archivo a la izquierda + +Move file on right +Mover archivo a la derecha + +Overwrite left item +Sobrescribir elemento izquierdo + +Overwrite right item +Sobrescribir elemento derecho + +Do nothing +No hacer nada + +Update attributes on left +Actualizar atributos en la izquierda + +Update attributes on right +Actualizar atributos en la derecha + +Database file %x is incompatible. +El archivo de base de datos %x es incompatible. + +Initial synchronization: +Sincronización inicial: + +Database file %x does not yet exist. +El archivo de base de datos %x aún no existe. + +Database file is corrupt: +El archivo de base de datos está dañado: + +Cannot write file %x. +No se puede escribir el archivo %x. + +Cannot read file %x. +No se puede leer el archivo %x. + +Database files do not share a common session. +Los archivos de base de datos no comparten una sesión común. + +Searching for folder %x... +Buscando carpeta %x… + +Cannot read file attributes of %x. +No se puede leer archivo de atributos de %x. + +Cannot get process information. +No se puede obtener información del proceso. + +Waiting while directory is locked (%x)... +Esperando mientras el directorio se encuentre bloqueado (%x)… + + +1 sec +%x sec + + +1 seg. +%x seg. + + +Creating file %x +Creando archivo %x + +Items processed: +Elementos procesados: + +Items remaining: +Elementos restantes: + +Total time: +Tiempo total: + + +1 byte +%x bytes + + +1 byte +%x bytes + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Error analizando archivo %x, fila %y, columna %z. + +Cannot set directory lock for %x. +No se pudo bloquear el directorio %x. + +Scanning: +Escanear: + + +1 thread +%x threads + + +1 hilo +%x hilos + + +Encoding extended time information: %x +Codificando información de hora extendida: %x + +/sec +/seg + +%x items/sec +%x elementos/seg + +Configuration file %x loaded partially only. +Archivo de configuración %x cargado sólo parcialmente. + +Show in Explorer +Mostrar en el Explorador + +Open with default application +Abrir con la aplicación predeterminada + +Browse directory +Examinar directorio + +Cannot access the Volume Shadow Copy Service. +No se puede acceder al servicio de Instantánea de volumen. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Por favor, use la versión de 64 bits de FreeFileSync para crear copias de Shadow en el sistema. + +Cannot load file %x. +No se puede cargar el archivo %x. + +Cannot determine volume name for %x. +No se puede determinar nombre del volumen de %x. + +Volume name %x is not part of file path %y. +El nombre de volumen %x no es parte de la ruta de archivo %y. + +Stop requested: Waiting for current operation to finish... +Detención solicitada: esperando a que la operación actual finalice… + +Unable to create timestamp for versioning: +No es posible crear fecha y hora para la versión: + +Cannot read the following XML elements: +No se pueden leer los siguientes elementos XML: + +&Open... +&Abrir… + +Save &as... +Guardar &como… + +&Quit +&Salir + +&Program +&Programa + +&View help +&Ver ayuda + +&About +&Acerca de + +&Help +&Ayuda + +Usage: +Uso: + +1. Select folders to watch. +1. Seleccionar carpetas para mostrar. + +2. Enter a command line. +2. Introduzca una línea de comandos. + +3. Press 'Start'. +3. Presione 'Inicio'. + +To get started just import a .ffs_batch file. +Para comenzar, importe un archivo .ffs_batch. + +Folders to watch: +Carpetas para examinar: + +Add folder +Añadir carpeta + +Remove folder +Eliminar carpeta + +Browse +Examinar + +Select a folder +Seleccione una carpeta + +Idle time (in seconds): +Tiempo de inactividad (en segundos): + +Idle time between last detected change and execution of command +Tiempo ocioso entre el último cambio detectado y la ejecución del comando + +Command line: +Línea de comandos: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +El comando es disparado si: +- hay cambios en los archivos o subcarpetas +- aparecen nuevas carpetas (en una memoria USB, por ejemplo) + + +&Start +&Iniciar + +About +Acerca de + +Build: %x +Completado: %x + +All files +Todos los archivos + +Automated Synchronization +Sincronización Automática + +Directory monitoring active +Supervisión de directorios activada + +Waiting until all directories are available... +Esperando que todos los directorios estén disponibles… + +Error +Error + +&Restore +&Restaurar + +&Show error +Mo&strar error + +&Exit +&Salir + +Incorrect command line: +Línea de comandos incorrecta: + +&Retry +&Reintentar + +File content +Contenido del archivo + +File time and size +Fecha y tamaño del archivo + +Two way +Bidireccional + +Mirror +Espejo + +Update +Actualizar + +Custom +Personalizado + +Multiple... +Múltiple… + +Moving file %x to %y +Mover archivo de %x a %y + +Moving folder %x to %y +Mover carpeta de %x a %y + +Moving symbolic link %x to %y +Mover vínculo simbólico de %x a %y + +Removing old versions... +Eliminando versiones antiguas… + +Creating symbolic link %x +Creando vínculo simbólico %x + +Creating folder %x +Creando carpeta %x + +Overwriting file %x +Sobrescribir archivo %x + +Overwriting symbolic link %x +Sobrescribir vínculo simbólico %x + +Verifying file %x +Verificación del archivo %x + +Updating attributes of %x +Actualizar atributos de %x + +Cannot find %x. +No se pudo encontrar %x. + +Target folder %x already existing. +La carpeta de destino %x ya existe. + +Target folder input field must not be empty. +El campo de entrada de la carpeta de destino no debe estar vacío. + +Please enter a target folder for versioning. +Indique una carpeta de destino para la versión. + +Source folder %x not found. +El archivo de origen %x no ha sido encontrado. + +The following items have unresolved conflicts and will not be synchronized: +Los siguientes elementos tienen conflictos sin resolver y no serán sincronizados: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +Las carpetas siguientes muestran diferencias significativas. Compruebe que hace coincidir las carpetas correctas para su sincronización. + +Not enough free disk space available in: +Espacio en disco insuficiente en: + +Required: +Requerido: + +Available: +Disponible: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Una carpeta, que es parte de múltiple pares de carpetas, será modificada. Por favor, revise las opciones de sincronización. + +Synchronizing folder pair: +Sincronizando par de carpetas: + +Generating database... +Generando base de datos… + +Creating a Volume Shadow Copy for %x... +Creando una Instantánea de volumen para %x… + +Data verification error: %x and %y have different content. +Error al comprobar los datos: %x y %y tienen contenidos diferentes. + +job name +nombre de tarea + +Synchronization stopped +Sincronización detenida + +Synchronization completed with errors +Sincronización completada con errores + +Synchronization completed with warnings +Sincronización completada con avisos + +Nothing to synchronize +Nada que sincronizar + +Synchronization completed successfully +Sincronización completada satisfactoriamente + +Saving log file %x... +Guardando registro %x… + +You can switch to FreeFileSync's main window to resolve this issue. +Puede cambiar a la ventana principal de FreeFileSync para resolver este problema. + +&Don't show this warning again +&No volver a mostrar este aviso + +&Ignore +&Ignorar + +&Switch +&Cambiar + +Switching to FreeFileSync's main window +Cambiar a la ventana principal de FreeFileSync + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + +Reintento automático en 1 segundo… +Reintento automático en %x segundos… + + +&Ignore subsequent errors +&Ignorar errores posteriores + +Retrying operation... + + +Serious Error +Error grave + +Check for Program Updates +Buscar actualizaciones del programa + +A new version of FreeFileSync is available: +Una nueva versión de FreeFileSync está disponible: + +Download now? +¿Descargar ahora? + +&Download +&Descargar + +FreeFileSync is up to date. +FreeFileSync está actualizado. + +Unable to connect to sourceforge.net. +No se puede conectar con sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +No se encuentra el número de versión actual de FreeFileSync en línea. ¿Desea comprobarla manualmente? + +&Check + + +Symlink +Enlace simbólico + +Folder +Carpeta + +Full path +Ruta completa + +Name +Nombre + +Relative path +Ruta relativa + +Base folder +Carpeta base + +Size +Tamaño + +Date +Fecha + +Extension +Extensión + +Category +Categoría + +Action +Acción + +Drag && drop +Arrastrar y soltar + +Close progress dialog +Cerrar diálogo de progreso + +Standby +Suspender + +Log off +Finalizar sesión + +Shut down +Apagar + +Hibernate +Hibernar + +Alternate comparison settings +Opciones avanzadas de comparación + +Alternate synchronization settings +Opciones avanzadas de sincronización + +Local filter +Filtro local + +Active +Activa + +None +Ninguna + +Remove alternate settings +Eliminar opciones avanzadas + +Clear filter settings +Limpiar opciones del filtrado + +Copy +Copiar + +Paste +Pegar + +Alternate Comparison Settings +Opciones avanzadas de comparación + +Alternate Synchronization Settings +Opciones avanzadas de sincronización + +Local Filter +Filtro local + +&New +&Nuevo + +&Save +&Guardar + +Save as &batch job... +Guardar como tarea por &lotes… + +1. &Compare +1. &Comparar + +2. &Synchronize +2. &Sincronizar + +&Global settings +&Opciones globales + +&Language +&Idioma + +&Find... +&Buscar… + +&Export file list... +&Exportar lista de archivos… + +&Tools +Herramien&tas + +&Check now +&Comprobar ahora + +Check &automatically once a week +Comprobar &automáticamente una vez por semana + +&Check for new version +&Comprobar si hay una nueva versión + +Compare +Comparar + +Cancel +Cancelar + +Synchronize +Sincronizar + +Add folder pair +Añadir un par de carpetas + +Remove folder pair +Eliminar un par de carpetas + +Swap sides +Intercambiar lados + +Close search bar +Cerrar la barra de búsqueda + +Find: +Find: + +Match case +Distinción entre mayúsculas y minúsculas + +Save as batch job +Salvar como tarea por lotes + +Hide excluded items +Ocultar elementos excluidos + +Show filtered or temporarily excluded files +Mostrar archivos excluidos temporalmente o filtrados + +Number of files and folders that will be created +Número de archivos y carpetas que serán creados + +Number of files that will be overwritten +Número de archivos que serán sobrescritos + +Number of files and folders that will be deleted +Número de archivos y carpetas que serán eliminados + +Total bytes to copy +Total de bytes a copiar + +Select a variant: +Seleccione una variante: + +Identify equal files by comparing modification time and size. +Comparar archivos iguales por la hora de modificación y el tamaño. + +Identify equal files by comparing the file content. +Comparar archivos iguales por el contenido. + +Symbolic links: +Vínculos simbólicos: + +More information +Más información + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Identificar y propagar cambios en ambos lados. Eliminaciones, movimientos y conflictos serán detectados automáticamente usando una base de datos. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Crear una copia de respaldo reflejo de la carpeta izquierda que corresponda exactamente a la carpeta derecha después de la sincronización. + +Copy new and updated files to the right folder. +Copiar archivos nuevos y actualizados a la carpeta de la derecha. + +Configure your own synchronization rules. +Configuración de sus propias reglas de sincronización. + +Detect moved files +Detectar archivos movidos + +Requires database files. Not supported by all file systems. +Archivos de base de datos requeridos. No son compatibles en todos los sistemas de archivos. + +Delete files: +Eliminar archivos: + +Permanent +Permanente + +Delete or overwrite files permanently +Borrar o Sobrescribir archivos permanentemente + +Recycle bin +Papelera de reciclaje + +Back up deleted and overwritten files in the recycle bin +Se eliminó la copia de respaldo y se reemplazaron archivos en la papelera de reciclaje + +Versioning +Control de versiones + +Move files to a user-defined folder +Mover archivos a una carpeta del usuario + +Naming convention: +Convención de nombrado: + +Show examples +Mostrar ejemplos + +Handle errors: +Tratar errores: + +Ignore +Ignorar + +Hide all error and warning messages +Ocultar todos los mensajes de error y aviso + +Pop-up +Automático + +Show pop-up on errors or warnings +Mostrar ventana emergente de errores o avisos + +On completion: +Al completar: + +Start synchronization now? +¿Iniciar la sincronización ahora? + +Variant: +Variante: + +Statistics +Estadísticas + +&Don't show this dialog again +&No volver a mostrar este diálogo + +Items found: +Elementos encontrados: + +Speed: +Velocidad: + +Time remaining: +Tiempo restante: + +Time elapsed: +Tiempo transcurrido: + +Synchronizing... +Sincronizando… + +Minimize to notification area +Minimizar en el área de notificación + +Close +Cerrar + +&Pause +&Pausa + +Stop +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 + +Stop synchronization at first error +Detener la sincronización con el primer error + +Show progress dialog +Mostrar diálogo de progreso + +Save log: +Guardar registro: + +Limit: +Limite: + +Limit maximum number of log files +Limitar el número máximo de archivos de registro + +How can I schedule a batch job? +Cómo puedo programar una tarea por lotes? + +&Recycle bin +Papelera de &reciclaje + +Delete on both sides +Borrar en ambos lados + +Delete on both sides even if the file is selected on one side only +Borrar en ambos lados incluso si el archivo está seleccionado en un solo lado + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Seleccione reglas para excluir archivos durante la sincronización. Indique rutas relativas a la carpeta emparejada correspondiente. + +Include: +Incluir: + +Exclude: +Excluir: + +Time span: +Espacio de tiempo: + +File size: +Tamaño de archivo: + +Minimum: +Mínimo: + +Maximum: +Máximo: + +&Clear +&Borrar + +The following settings are used for all synchronization jobs. +Las opciones siguientes se utilizan para todas las tareas de sincronización. + +Fail-safe file copy +Copia de archivo a prueba de fallos + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Copiar a archivo temporal (*.ffs_tmp) antes de sobrescribir en destino. +Se garantiza un estado coherente incluso en caso de error grave. + + +(recommended) +(recomendado) + +Copy locked files +Copiar archivos bloqueados + +Copy shared or locked files using the Volume Shadow Copy Service. +Copiar archivos compartidos o bloqueados usando el servicio de Instantánea de volumen. + +(requires administrator rights) +(requiere derechos de administrador) + +Copy file access permissions +Copiar permisos de acceso al archivo + +Transfer file and folder permissions. +Transferir permisos de archivos y carpetas. + +Automatic retry on error: +Retardo automático en caso de error : + +Retry count: +Cuenta de reintentos: + +Delay (in seconds): +Retardo (en segundos): + +Customize context menu: +Personalizar menú contextual : + +Description +Descripción + +Restore hidden windows +Restaurar ventanas ocultas + +&Default +&Configuración predeterminada + +Source code written in C++ using: +Código fuente original en C++ con apoyo de: + +If you like FreeFileSync +¿Te gusta FreeFileSync? : + +Donate with PayPal +Haz una donación por PayPal + +Feedback and suggestions are welcome +Comentarios y sugerencias bienvenidos : + +Homepage +Página de inicio + +Email +Correo electrónico + +Published under the GNU General Public License +Publicado con derechos GNU General Public License : + +Many thanks for localization: +Agradecimientos por las traducciones a: + +Save as Batch Job +Guardar como tarea por lotes + +Delete Items +Eliminar elementos + +Global Settings +Opciones globales + +Select Time Span +Seleccionar duración + +Folder Pairs +Pares de carpetas + +Find +Buscar + +Overview +Visión global + +Configuration +Configuración + +Main Bar +Barra principal + +Filter Files +Filtrar archivos + +Select View +Seleccione vista + +Open... +Abrir… + +Save +Guardar + +Compare both sides +Comparar ambos lados + +Comparison settings +Opciones de comparación + +Synchronization settings +Opciones de sincronización + +Start synchronization +Iniciar sincronización + +Confirm +Confirmar + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + +¿Realmente desea ejecutar el comando %y para un elemento? +¿Realmente desea ejecutar el comando %y para %x elementos? + + +&Execute +&Ejecutar + + +1 directory +%x directories + + +1 directorio +%x directorios + + + +1 file +%x files + + +1 archivo +%x archivos + + + +%y of 1 row in view +%y of %x rows in view + + +%y de 1 fila en la vista +%y de %x filas en vista + + +Set direction: +Indicar dirección: + +multiple selection +selección múltiple + +Include via filter: +Incluir a través del filtro: + +Exclude via filter: +Excluir a través del filtro: + +Exclude temporarily +Excluir temporalmente + +Include temporarily +Incluir temporalmente + +Delete +Eliminar + +Include all +Incluir todo + +Exclude all +Excluir todo + +Show icons: +Mostrar iconos: + +Small +Pequeño + +Medium +Medio + +Large +Grande + +Select time span... +Seleccionar duración… + +Default view +Vista predeterminada + +Show "%x" +Mostrar "%x" + +Last session +Última sesión + +Folder Comparison and Synchronization +Comparación y sincronización de carpetas + +Configuration saved +Configuración guardada + +FreeFileSync batch +Tarea por lotes de FreeFileSync + +Do you want to save changes to %x? +¿Quiere guardar los cambios de %x? + +Never save &changes +Nunca guardar &cambios + +Do&n't save +&No guardar + +Filter +Filtro + +Show files that exist on left side only +Mostrar sólo archivos existentes en la izquierda + +Show files that exist on right side only +Mostrar sólo archivos existentes en la derecha + +Show files that are newer on left +Mostrar archivos más recientes a la izquierda + +Show files that are newer on right +Mostrar archivos más recientes a la derecha + +Show files that are equal +Mostrar archivos iguales + +Show files that are different +Mostrar archivos diferentes + +Show conflicts +Mostrar conflictos + +Show files that will be created on the left side +Mostrar archivos que serán creados en el lado izquierdo + +Show files that will be created on the right side +Mostrar archivos que serán creados en el lado derecho + +Show files that will be deleted on the left side +Mostrar archivos que serán eliminados en el lado izquierdo + +Show files that will be deleted on the right side +Mostrar archivos que serán eliminados en el lado derecho + +Show files that will be overwritten on left side +Mostrar archivos que serán sobrescritos en el lado izquierdo + +Show files that will be overwritten on right side +Mostrar archivos que serán sobrescritos en el lado derecho + +Show files that won't be copied +Mostrar archivos que no serán copiados + +Set as default +Predeterminado + +All folders are in sync +Todas las carpetas están sincronizadas + +Synchronization Settings +Opciones de sincronización + +Comparison Settings +Opciones de comparación + +Cannot find %x +No se puede encontrar %x + +Comma-separated values +Valores separados por comas + +File list exported +Lista de archivos exportada + +Searching for program updates... +Buscando actualizaciones del programa… + +Scanning... +Escaneando… + +Comparing content... +Comparando contenido… + +Info +Info + +Warning +Atención + +Paused +Pausado + +Initializing... +Inicializando… + +Stopped +Detenido + +Completed +Terminado + +&Continue +&Continuar + +Log +Registro + +Today +Hoy + +This week +Esta semana + +This month +Este mes + +This year +Este año + +Last x days +Últimos x días + +Byte +Byte + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +¿Realmente desea mover este elemento a la papelera de reciclaje? +¿Realmente desea mover estos %x elementos a la papelera de reciclaje? + + +Move +Mover + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +¿Realmente desea eliminar este elemento? +¿Realmente desea eliminar estos %x elementos? + + +Exclude +Excluir + +Direct +Enviar + +Follow +Seguir + +Copy NTFS permissions +Copiar permisos NTFS + +Integrate external applications into context menu. The following macros are available: +Integrar aplicaciones externas en el menú de contexto. Los siguientes macros están disponibles: + +- full file or folder name +- nombre completo de archivo o carpeta + +- folder part only +- sólo parte de la carpeta + +- Other side's counterpart to %item_path% +- El otro lado de %item_path% + +- Other side's counterpart to %item_folder% +- El otro lado de %item_folder% + +Restore all hidden windows and warnings? +Restaurar todas las ventanas y mensajes de advertencia ocultados ? + +Leave as unresolved conflict +Dejar como conflicto sin resolver + +Replace +Reemplazar + +Move files and replace if existing +Mover archivos y reemplazar si ya existen + +Time stamp +Intervalo de tiempo + +Append a timestamp to each file name +Incluir fecha y hora a cada nombre de archivo + +File +Archivo + +YYYY-MM-DD hhmmss +YYYY-MM-DD hhmmss + +Files +Archivos + +Items +Elementos + +Percentage +Porcentaje + +Cannot monitor directory %x. +No se puede monitorizar el directorio %x. + +Conversion error: +Error de conversión: + +Cannot delete file %x. +No se puede eliminar el archivo %x. + +The file is locked by another process: +El archivo está bloqueado por otro proceso: + +Cannot move file %x to %y. +No se puede mover el archivo %x a %y. + +Cannot delete directory %x. +No se puede eliminar el directorio %x. + +Cannot write file attributes of %x. +No se pueden escribir los atributos de archivo de %x. + +Cannot write modification time of %x. +No se puede escribir el tiempo de modificación de %x. + +Cannot read security context of %x. +No se puede leer el contexto de seguridad de %x. + +Cannot write security context of %x. +No se puede escribir el contexto de seguridad de %x. + +Cannot read permissions of %x. +No se pueden leer los permisos de %x. + +Cannot write permissions of %x. +No se pueden escribir los permisos de %x. + +Cannot create directory %x. +No se puede crear el directorio %x. + +Cannot create symbolic link %x. +No se puede crear el vínculo simbólico %x. + +Cannot find system function %x. +No se puede encontrar la función del sistema %x. + +Cannot copy file %x to %y. +No se puede copiar el archivo %x a %y. + +Type of item %x is not supported: +El tipo de objeto %x no esta soportado: + +Cannot resolve symbolic link %x. +No se puede resolver el vínculo simbólico %x. + +Cannot open directory %x. +No se puede abrir el directorio %x. + +Cannot enumerate directory %x. +No se puede enumerar el directorio %x. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +1 minuto +%x minutos + + + +1 hour +%x hours + + +1 hora +%x horas + + + +1 day +%x days + + +1 día +%x días + + +Unable to register to receive system messages. +No es posible registrar la recepción de mensajes sistema. + +Cannot set privilege %x. +No se puede asignar el privilegio %x. + +Unable to suspend system sleep mode. +No es posible suspender el sistema. + +Cannot change process I/O priorities. +No se pudieron cambiar las prioridades de E/S del proceso. + +Unable to move %x to the recycle bin. +Incapaz de mover %x a la papelera de reciclaje. + +Cannot determine final path for %x. +No se puede determinar la ruta final de %x. + +Error Code %x: +Error: %x: + diff --git a/FreeFileSync/Build/Languages/swedish.lng b/FreeFileSync/Build/Languages/swedish.lng new file mode 100644 index 00000000..f6cebeab --- /dev/null +++ b/FreeFileSync/Build/Languages/swedish.lng @@ -0,0 +1,1513 @@ +
    + Svenska + Åke Engelbrektson + sv_SE + flag_sweden.png + 2 + n == 1 ? 0 : 1 +
    + +Both sides have changed since last synchronization. +Bägge sidor har ändrats sedan senaste synkroniseringen. + +Cannot determine sync-direction: +Kan inte bestämma synkroniseringsriktning: + +No change since last synchronization. +Inga ändringar sedan senaste synkronisering. + +The database entry is not in sync considering current settings. +Databasposten är inte synkroniserad, i förhållande till aktuella inställningar + +Setting default synchronization directions: Old files will be overwritten with newer files. +Standardsynkronisering: Gamla filer kommer att skrivas över av nyare versioner. + +Checking recycle bin availability for folder %x... +Kontrollerar papperskorgens tillgänglighet för %x... + +Moving file %x to the recycle bin +Flyttar %x till papperskorgen + +Moving folder %x to the recycle bin +Flyttar mappen %x till papperskorgen + +Moving symbolic link %x to the recycle bin +Flyttar den symboliska länken %x till papperskorgen + +Deleting file %x +Tar bort filen %x + +Deleting folder %x +Tar bort mappen %x + +Deleting symbolic link %x +Tar bort den symboliska länken %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +Papperskorgen är inte tillgänglig för följande mappar. Filerna kommer istället att tas bort permanent: + +An exception occurred +Ett undantag inträffade + +A directory path is expected after %x. +En sökväg förväntas efter %x. + +Syntax error +Syntaxfel + +Cannot open file %x. +Kan inte öppna %x + +File %x does not contain a valid configuration. +Filen %x innehåller ingen giltig konfiguration. + +Unequal number of left and right directories specified. +Ett ojämnt antal vänster- och högermappar har specificerats. + +The config file must not contain settings at directory pair level when directories are set via command line. +Konfigurationsfilen kan inte innehålla inställningar på katalogparnivå när mappar anges via kommandorad. + +Directories cannot be set for more than one configuration file. +Mappar kan inte anges för mer än en konfigurationsfil + +Command line +Kommandofält + +Syntax: +Syntax: + +config files +konfigurationsfiler + +directory +mapp + +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 directories for at most one config file. +Valfritt antal alternativa mappar för max en konfigurationsfil. + +A folder input field is empty. +Ett inmatningsfält är tomt + +The corresponding folder will be considered as empty. +Motsvarande mapp kommer att betraktas som tom. + +Cannot find the following folders: +Kan inte hitta följande mappar: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Du kan bortse från detta fel, och betrakta varje mapp som tom. Mapparna kommer då att skapas automatiskt, under synkroniseringen + +The following folders have dependent paths. Be careful when setting up synchronization rules: +Följande mappar har beroende sökvägar. Var försiktig vid konfigurering av synkroniseringsregler: + +File %x has an invalid date. +Filen %x har ett ogiltigt datum. + +Date: +Datum: + +Files %x have the same date but a different size. +Filerna %x har samma datum men olika storlek. + +Size: +Storlek: + +Items differ in attributes only +Endast attribut skiljer objekten åt + +Resolving symbolic link %x +Översätter den symboliska länken %x + +Comparing content of files %x +Jämför filinnehåll för %x + +Generating file list... +Skapar fillista... + +Starting comparison +Startar jämföelse + +Calculating sync directions... +Beräknar synkroniseringsmappar... + +Out of memory. +Minnesbrist. + +Item exists on left side only +Objektet finns bara på vänster sida + +Item exists on right side only +Objektet finns bara på höger sida + +Left side is newer +Vänster sida är nyare + +Right side is newer +Höger sida är nyare + +Items have different content +Objekten har olika innehåll + +Both sides are equal +Bägge sidor är lika + +Conflict/item cannot be categorized +Konflikten/Objektet kan inte kategoriseras + +Copy new item to left +Kopiera nytt objekt åt vänster + +Copy new item to right +Kopiera nytt objekt åt höger + +Delete left item +Ta bort vänster objekt + +Delete right item +Ta bort höger objekt + +Move file on left +Flytta fil på vänster sida + +Move file on right +Flytta fil på höger sida + +Overwrite left item +Skriv över vänster objekt + +Overwrite right item +Skriv över höger objekt + +Do nothing +Gör ingenting + +Update attributes on left +Uppdatera attribut på vänster sida + +Update attributes on right +Uppdatera attribut på höger sida + +Database file %x is incompatible. +Databasfilen %x är inkompatibel + +Initial synchronization: +Initial synkronisering: + +Database file %x does not yet exist. +Databasfilen %x finns ännu inte + +Database file is corrupt: +Databafilen är korrupt + +Cannot write file %x. +Filen %x kan inte skrivas + +Cannot read file %x. +Filen %x kan inte läsas in + +Database files do not share a common session. +Databasfilerna har ingen gemensam session + +Searching for folder %x... +Söker efter mappen %x... + +Cannot read file attributes of %x. +Kan inte läsa filattribut för %x + +Cannot get process information. +Processinformation kan inte inhämtas + +Waiting while directory is locked (%x)... +Väntar medan mappen låses (%x)... + + +1 sec +%x sec + + +1 sek +%x sek + + +Creating file %x +Skapar fil %x + +Items processed: +Processade poster + +Items remaining: +Återstående poster: + +Total time: +Total tid: + + +1 byte +%x bytes + + +1 byte +%x byte + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Error parsing file %x, row %y, column %z. +Tolkningsfel på filen %x, rad %y, kolumn %z. + +Cannot set directory lock for %x. +Kan inte låsa %x. + +Scanning: +Skannar: + + +1 thread +%x threads + + +1 tråd +%x trådar + + +Encoding extended time information: %x +Kodar utökad tidsinformation: %x + +/sec +/s + +%x items/sec +%x objekt/sek. + +Configuration file %x loaded partially only. +Konfigurationsfilen %x lästes bara delvis in. + +Show in Explorer +Visa i Utforskaren + +Open with default application +Öppna med standardprogram + +Browse directory +Sök upp mapp + +Cannot access the Volume Shadow Copy Service. +Kan inte komma åt tjänsten 'Volume Shadow Copy' + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Använd FreeFileSync 64-bitarsversion för att skapa skuggkopior på detta system. + +Cannot load file %x. +Kan inte läsa in %x + +Cannot determine volume name for %x. +Kan inte utläsa volymnamn för %x + +Volume name %x is not part of file path %y. +Volymnamnet %x är inte en del av sökvägen %y. + +Stop requested: Waiting for current operation to finish... +Stopp begärt: Väntar på att aktuell åtgärd skall slutföras... + +Unable to create timestamp for versioning: +Kunde inte skapa tidsstämpel för versionshantering: + +Cannot read the following XML elements: +Kan inte läsa följande XML-element: + +&Open... +&Öppna... + +Save &as... +S¶ som... + +&Quit +&Avsluta + +&Program +&Program + +&View help +&Visa hjälpen + +&About +&Om + +&Help +&Hjälp + +Usage: +Användning: + +1. Select folders to watch. +1. Välj mapp att bevaka. + +2. Enter a command line. +2. Mata in ett kommando. + +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 + +Folders to watch: +Mappar att övervaka: + +Add folder +Lägg till mapp + +Remove folder +Ta bort mapp + +Browse +Bläddra + +Select a folder +Välj en mapp + +Idle time (in seconds): +Vilotid (i sekunder) + +Idle time between last detected change and execution of command +Väntetid mellan senast upptäckta förändring och kommandoexekvering + +Command line: +Kommandorad: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +Kommandot triggas om: +- filer eller undermappar förändras +- nya mappar upptäcks (ex. USB-minne ansluts) + + +&Start +&Start + +About +Om + +Build: %x +Bygge: %x + +All files +Alla filer + +Automated Synchronization +Automatiserad synkronisering + +Directory monitoring active +Mappövervakning aktiv + +Waiting until all directories are available... +Väntar på att samtliga mappar skall bli tillgängliga... + +Error +Fel + +&Restore +&Återställ + +&Show error +&Visa fel + +&Exit +&Avsluta + +Incorrect command line: +Felaktig kommandorad: + +&Retry +&Försök igen + +File content +Filinnehåll + +File time and size +Tidsstämpling och storlek + +Two way +Tvåvägs + +Mirror +Spegla + +Update +Uppdatera + +Custom +Anpassat + +Multiple... +Flera... + +Moving file %x to %y +Flyttar filen %x till %y + +Moving folder %x to %y +Flyttar mappen %x till %y + +Moving symbolic link %x to %y +Flyttar den symboliska länken %x till %y + +Removing old versions... +Tar bort gamla versioner... + +Creating symbolic link %x +Skapar den symboliska länken %x + +Creating folder %x +Skapar mappen %x + +Overwriting file %x +Skriver över filen %x + +Overwriting symbolic link %x +Skriver över den symboliska länken %x + +Verifying file %x +Verifierar %x + +Updating attributes of %x +Uppdaterar attribut för %x + +Cannot find %x. +Kan inte hitta %x. + +Target folder %x already existing. +Målmappen %x finns redan. + +Target folder input field must not be empty. +Indatafältet för målmapp får inte vara tomt. + +Please enter a target folder for versioning. +Ange en målmapp för versionshantering. + +Source folder %x not found. +Källmappen %x kan inte hittas. + +The following items have unresolved conflicts and will not be synchronized: +Följande objekt har olösta konflikter, och kommer inte att synkroniseras: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +Följande mappar är signifikant olika. Kontrollera att du jämför rätt mappar för synkronisering. + +Not enough free disk space available in: +Ej tillräckligt ledigt diskutrymme på: + +Required: +Utrymmeskrav: + +Available: +Tillgängligt: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +En mapp som tillhör flera mapp-par är på väg att ändras. Kontrollera synkroniseringsinställningarna + +Synchronizing folder pair: +Synkroniserar mapp-par: + +Generating database... +Skapar databas... + +Creating a Volume Shadow Copy for %x... +Skapar en 'Volume Shadow Copy' för %x... + +Data verification error: %x and %y have different content. +Dataverifieringsfel: %x och %y har olika innehåll. + +job name +åtgärdsnamn + +Synchronization stopped +Synkroniseringen stoppad + +Synchronization completed with errors +Synkronisering slutförd med fel + +Synchronization completed with warnings +Synkronisering slutförd med varningar + +Nothing to synchronize +Det finns inget att synkronisera + +Synchronization completed successfully +Synkronisering slutförd + +Saving log file %x... +Sparar loggfil %x... + +You can switch to FreeFileSync's main window to resolve this issue. +Du kan växla till FreeFileSyncs programfönster för att lösa problemet. + +&Don't show this warning again +&Visa inte den här varningen igen + +&Ignore +&Ignorera + +&Switch +&Växla + +Switching to FreeFileSync's main window +Växla till FreeFileSyncs programfönster + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + + + +&Ignore subsequent errors +&Ignorera efterföljande fel + +Retrying operation... + + +Serious Error +Allvarligt fel + +Check for Program Updates +Sök efter programuppdateringar + +A new version of FreeFileSync is available: +Det finns en ny version av FreeFileSync + +Download now? +Ladda ner nu? + +&Download +&Ladda ner + +FreeFileSync is up to date. +FreeFileSync är uppdaterad. + +Unable to connect to sourceforge.net. +Kan inte ansluta sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Kan inte hitta aktuellt versionsnummer online. Vill du kontrollera manuellt? + +&Check + + +Symlink +Symboliska länkar + +Folder +Mapp + +Full path +Fullständig sökväg + +Name +Namn + +Relative path +Sökväg + +Base folder +Basmapp + +Size +Storlek + +Date +Datum + +Extension +Filformat + +Category +Kategori + +Action +Aktivitet + +Drag && drop +Dra && släpp + +Close progress dialog +Stäng förloppsindikator + +Standby +Strömsparläge + +Log off +Logga ut + +Shut down +Stäng av datorn + +Hibernate +Viloläge + +Alternate comparison settings +Alternativa jämförelseinställningar + +Alternate synchronization settings +Alternativa synkroniseringsinställningar + +Local filter +Lokalt filter + +Active +Aktiv + +None +Inget + +Remove alternate settings +Ta bort alternativa inställningar + +Clear filter settings +Rensa filterinställningar + +Copy +Kopiera + +Paste +Klistra in + +Alternate Comparison Settings +Alternativa jämförelseinställningar + +Alternate Synchronization Settings +Alternativa synkroniseringsinställningar + +Local Filter +Lokalt filter + +&New +&Nytt + +&Save +&Spara + +Save as &batch job... +Spara som &batch-fil... + +1. &Compare +1. &Jämför + +2. &Synchronize +2. &Synkronisera + +&Global settings +&Allmäna inställningar + +&Language +&Språk + +&Find... +&Sök... + +&Export file list... +&Exportera fillista... + +&Tools +&Verktyg + +&Check now +&Sök nu + +Check &automatically once a week +Sök &automatiskt en gång per vecka + +&Check for new version +&Sök efter uppdateringar + +Compare +Jämför + +Cancel +Avbryt + +Synchronize +Synkronisera + +Add folder pair +Lägg till mapp-par + +Remove folder pair +Ta bort mapp-par + +Swap sides +Byt sida + +Close search bar +Stäng sökfältet + +Find: +Sök: + +Match case +Matcha gemener/VERSALER + +Save as batch job +Spara som batch-fil + +Hide excluded items +Dölj undantagna objekt + +Show filtered or temporarily excluded files +Dölj filtrerade eller temporärt undantagna filer + +Number of files and folders that will be created +Antal filer och mappar som kommer att skapas + +Number of files that will be overwritten +Antal filer som kommer att skrivas över + +Number of files and folders that will be deleted +Antal filer och mappar som kommer att tas bort + +Total bytes to copy +Byte att kopiera + +Select a variant: +Välj ett alternativ: + +Identify equal files by comparing modification time and size. +Identifiera likadana filer genom att jämföra tidsstämpling och filstorlek. + +Identify equal files by comparing the file content. +Identifiera likadana filer genom att jämföra filinnehåll. + +Symbolic links: +Symboliska länkar: + +More information +Mer information + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Identifierar och sprider förändringar på båda sidor. Borttagningar, förflyttningar och konflikter detekteras automatiskt med hjälp av en databas. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Skapar en speglad kopia av vänster mapp, som exakt matchas av höger mapp efter synkronisering. + +Copy new and updated files to the right folder. +Kopierar nya och uppdaterade filer till höger mapp. + +Configure your own synchronization rules. +Konfigurera dina egna synkroniseringsregler. + +Detect moved files +Hitta flyttade filer + +Requires database files. Not supported by all file systems. +Kräver databasfiler som inte stöds av alla filsystem + +Delete files: +Ta bort filer: + +Permanent +Permanent + +Delete or overwrite files permanently +Ta bort eller skriv över permanent + +Recycle bin +Papperskorgen + +Back up deleted and overwritten files in the recycle bin +Kopiera borttagna och överskrivna filer till papperskorgen + +Versioning +Versionshantering + +Move files to a user-defined folder +Flytta filer till en fördefinierad mapp + +Naming convention: +Regler för namngivning + +Show examples +Visa exempel + +Handle errors: +Felhantering: + +Ignore +Ignorera + +Hide all error and warning messages +Visa inte fel- och varningsmeddelanden + +Pop-up +Popup + +Show pop-up on errors or warnings +Visa popup vid fel och varningar + +On completion: +Vid slutfört: + +Start synchronization now? +Vill du starta synkroniseringen nu? + +Variant: +Alternativ: + +Statistics +Statistik + +&Don't show this dialog again +&Visa inte den här dialogen igen + +Items found: +Funna poster: + +Speed: +Hastighet: + +Time remaining: +Återstående tid: + +Time elapsed: +Förfluten tid: + +Synchronizing... +Synkroniserar... + +Minimize to notification area +Minimera till meddelandefältet + +Close +Stäng + +&Pause +&Paus + +Stop +Stoppa + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Skapa en batch-fil för obevakad synkronisering. Dubbelklicka på filen för att starta den, eller schemalägg i en åtgärdshanterare: %x + +Stop synchronization at first error +Stoppa synkroniseringen vid första fel som uppstår + +Show progress dialog +Visa förloppsindikator + +Save log: +Spara logg: + +Limit: +Gräns: + +Limit maximum number of log files +Begränsa antalet loggfiler + +How can I schedule a batch job? +Hur schemalägger jag en batch-fil? + +&Recycle bin +&Papperskorgen + +Delete on both sides +Ta bort på båda sidor + +Delete on both sides even if the file is selected on one side only +Ta bort på båda sidor, även om filen är markerad på endast en sida + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Skapa filtreringsregler för att undanta vissa filer från synkronisering. Sökvägar relateras till motsvarande mapp-par + +Include: +Inkludera: + +Exclude: +Undanta: + +Time span: +Tidsrymd: + +File size: +Filstorlek: + +Minimum: +Min: + +Maximum: +Max: + +&Clear +&Rensa + +The following settings are used for all synchronization jobs. +Följande inställningar används för all synkronisering + +Fail-safe file copy +Felsäker filkopiering + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Kopierar till en temporär fil (*.ffs_tmp), före överskrivning av målet. +Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. + + +(recommended) +(rekommenderas) + +Copy locked files +Kopiera låsta filer + +Copy shared or locked files using the Volume Shadow Copy Service. +Kopierar delade eller låsta filer med hjälp av tjänsten 'Volume Shadow Copy' + +(requires administrator rights) +(kräver administratörsbehörighet) + +Copy file access permissions +Kopiera filåtkomstbehörigheter + +Transfer file and folder permissions. +Överför behörighetsinställningar + +Automatic retry on error: +Automatiska återförsök vid fel: + +Retry count: +Antal försök: + +Delay (in seconds): +Fördröjning (i sekunder) + +Customize context menu: +Anpassad kontextmeny: + +Description +Beskrivning + +Restore hidden windows +Återställ dolda vyer + +&Default +&Standard + +Source code written in C++ using: +Källkod skriven i C++ med hjälp av: + +If you like FreeFileSync +Om du gillar FreeFileSync + +Donate with PayPal +Donera via PayPal + +Feedback and suggestions are welcome +Återkoppling och förslag är välkommna + +Homepage +Hemsida + +Email +E-post + +Published under the GNU General Public License +Publiserad under GNU General Public License + +Many thanks for localization: +Tack för översättning: + +Save as Batch Job +Spara som batch-fil + +Delete Items +Ta bort objekt + +Global Settings +Globala inställningar + +Select Time Span +Välj tidsrymd + +Folder Pairs +Mapp-par + +Find +Sök + +Overview +Översikt + +Configuration +Inställningar + +Main Bar +Primärt verktygsfält + +Filter Files +Filtrera filer + +Select View +Välj vy + +Open... +Öppna... + +Save +Spara + +Compare both sides +Jämför båda sidor + +Comparison settings +Jämförelseinställningar + +Synchronization settings +Synkroniseringsinställningar + +Start synchronization +Starta synkronisering + +Confirm +Bekräfta + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + + + +&Execute +&Kör + + +1 directory +%x directories + + +1 mapp +%x mappar + + + +1 file +%x files + + +1 fil +%x filer + + + +%y of 1 row in view +%y of %x rows in view + + +%y av 1 rad i vyn +%y av %x rader i vyn + + +Set direction: +Ange riktning: + +multiple selection +flerval + +Include via filter: +Inkludera via filter: + +Exclude via filter: +Lägg till i undantag: + +Exclude temporarily +Undanta tillfälligt + +Include temporarily +Inkludera tillfälligt + +Delete +Ta bort + +Include all +Inkludera alla + +Exclude all +Exkludera alla + +Show icons: +Visa ikoner + +Small +Liten + +Medium +Normal + +Large +Stor + +Select time span... +Välj tidsintervall... + +Default view +Standardvy + +Show "%x" +Visa "%x" + +Last session +Senaste session + +Folder Comparison and Synchronization +Mappjämförelse och synkronisering + +Configuration saved +Inställningar sparade + +FreeFileSync batch +FreeFileSync batch + +Do you want to save changes to %x? +Vill du spara ändringar till %x? + +Never save &changes +Spara aldrig &ändringar + +Do&n't save +Spara &inte + +Filter +Filter + +Show files that exist on left side only +Visa filer som endast finns till vänster + +Show files that exist on right side only +Visa filer som endast finns till höger + +Show files that are newer on left +Visa filer som är nyare till vänster + +Show files that are newer on right +Visa filer som är nyare till höger + +Show files that are equal +Visa filer som är lika + +Show files that are different +Visa filer som är olika + +Show conflicts +Visa konflikter + +Show files that will be created on the left side +Visa filer som kommer att skapas till vänster + +Show files that will be created on the right side +Visa filer som kommer att skapas till höger + +Show files that will be deleted on the left side +Visa filer som kommer att tas bort till vänster + +Show files that will be deleted on the right side +Visa filer som kommer att tas bort till höger + +Show files that will be overwritten on left side +Visa filer som skrivas över till vänster + +Show files that will be overwritten on right side +Visa filer som skrivas över till höger + +Show files that won't be copied +Visa filer som inte kommer att kopieras + +Set as default +Ange som standard + +All folders are in sync +Alla mappar är synkroniserade + +Synchronization Settings +Synkroniseringsinställningar + +Comparison Settings +Jämförelseinställningar + +Cannot find %x +Kan inte hitta %x + +Comma-separated values +Kommaseparerade värden + +File list exported +Fillista exporterad + +Searching for program updates... +Söker efter programuppdateringar... + +Scanning... +Skannar... + +Comparing content... +Jämför innehåll... + +Info +Info + +Warning +Varning + +Paused +Pausad + +Initializing... +Initierar... + +Stopped +Stoppad + +Completed +Slutförd + +&Continue +&Fortsätt + +Log +Logg + +Today +Idag + +This week +Denna veckan + +This month +Denna månaden + +This year +I år + +Last x days +Senaste x dagarna + +Byte +Byte + +KB +KB + +MB +MB + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Vill du verkligen flytta följande objekt till papperskorgen? +Vill du verkligen flytta följande %x objekt till papperskorgen? + + +Move +Flytta + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Vill du verkligen ta bort följande objekt? +Vill du verkligen ta bort följande %x objekt? + + +Exclude +Undanta + +Direct +Direkt + +Follow +Följ + +Copy NTFS permissions +Kopiera NTFS-behörigheter + +Integrate external applications into context menu. The following macros are available: +Integrera externa program i högerklicksmeny. Följande variabler finns tillgängliga: + +- full file or folder name +- fullständigt fil- eller mappnamn + +- folder part only +- endast mappdelen + +- Other side's counterpart to %item_path% +- Andra sidans motsvarighet till %item_path% + +- Other side's counterpart to %item_folder% +- Andra sidans motsvarighet till %item_folder% + +Restore all hidden windows and warnings? +Vill du återställa alla dolda vyer och varningar + +Leave as unresolved conflict +Ignorera konflikt + +Replace +Byt ut + +Move files and replace if existing +Flytta filer och byt ut om de redan finns + +Time stamp +Tidsstämpel + +Append a timestamp to each file name +Lägg till en tidsstämpel till varje filnamn + +File +Fil + +YYYY-MM-DD hhmmss +YYYY-MM-DD hhmmss + +Files +Filer + +Items +Objekt + +Percentage +Procent + +Cannot monitor directory %x. +Mappen %x kan inte övervakas. + +Conversion error: +Konversionsfel: + +Cannot delete file %x. +Filen %x kan inte tas bort. + +The file is locked by another process: +Filen är låst av en annan process: + +Cannot move file %x to %y. +Kan inte flytta filen %x till %y. + +Cannot delete directory %x. +Kan inte ta bort mappen %x. + +Cannot write file attributes of %x. +Kan inte skriva filattribut för %x. + +Cannot write modification time of %x. +Kan inte ändra tidsangivelsen för %x. + +Cannot read security context of %x. +Kan inte läsa säkerhetskontext för %x. + +Cannot write security context of %x. +Kan inte skriva säkerhetskontext för %x. + +Cannot read permissions of %x. +Kan inte läsa behörigheter för %x. + +Cannot write permissions of %x. +Kan inte skriva behörigheter för %x. + +Cannot create directory %x. +Kan inte skapa mappen %x. + +Cannot create symbolic link %x. +Kan inte skapa den symboliska länken, %x + +Cannot find system function %x. +Kan inte hitta systemfunktion %x + +Cannot copy file %x to %y. +Kan inte kopiera filen %x till %y. + +Type of item %x is not supported: +Objekttyp %x stöds ej: + +Cannot resolve symbolic link %x. +Den symboliska länken %x kan inte matchas. + +Cannot open directory %x. +Kan inte öppna %x. + +Cannot enumerate directory %x. +Kan inte räkna upp %x. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +1 min +%x min + + + +1 hour +%x hours + + +1 timma +%x timmar + + + +1 day +%x days + + +1 dag +%x dagar + + +Unable to register to receive system messages. +Det gick inte att registrera mottagning av systemmeddelanden + +Cannot set privilege %x. +Kan inte att ange behörigheten %x. + +Unable to suspend system sleep mode. +Det går inte att avbryta systemets viloläge + +Cannot change process I/O priorities. +Kan inte ändra process I/O-prioritet + +Unable to move %x to the recycle bin. +Kan inte att flytta %x till papperskorgen + +Cannot determine final path for %x. +Kan inte utläsa slutlig sökväg för %x. + +Error Code %x: +Felkod %x: + diff --git a/FreeFileSync/Build/Languages/ukrainian.lng b/FreeFileSync/Build/Languages/ukrainian.lng new file mode 100644 index 00000000..af819d76 --- /dev/null +++ b/FreeFileSync/Build/Languages/ukrainian.lng @@ -0,0 +1,1527 @@ +
    + Українська + Roman Ardan + uk_UA + flag_ukraine.png + 3 + n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2 +
    + +Both sides have changed since last synchronization. +З моменту останньої синхронізації з обох сторін відбулися зміни. + +Cannot determine sync-direction: +Не можна визначити напрям синхронізації: + +No change since last synchronization. +Жодних змін з останньої синхронізації. + +The database entry is not in sync considering current settings. +Запис бази даних не синхронізований з урахуванням поточних налаштувань. + +Setting default synchronization directions: Old files will be overwritten with newer files. + +Налаштування напряму синхронізації за замовчуванням: +Старі файли будуть замінені новішими файлами. + + +Checking recycle bin availability for folder %x... +Перевірка доступності Корзини для папки %x... + +Moving file %x to the recycle bin +Переміщення файлу %x до Корзини + +Moving folder %x to the recycle bin +Переміщення папки %x до Корзини + +Moving symbolic link %x to the recycle bin +Переміщення символічного посилання %x до Корзини + +Deleting file %x +Вилучення файлу %x + +Deleting folder %x +Вилучення папки %x + +Deleting symbolic link %x +Вилучення символьного посилання %x + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +Корзина не доступна для таких папок. Файли замість цього будуть видалені назавжди: + +An exception occurred +Відбулось виключення + +A directory path is expected after %x. +Після %x очікується шлях до каталогу. + +Syntax error +Синтаксична помилка + +Cannot open file %x. +Не вдається фідкрити файл %x. + +File %x does not contain a valid configuration. +Файл %x не містить правильної конфігурації. + +Unequal number of left and right directories specified. +Вказано різну кількість лівих і правих каталогів + +The config file must not contain settings at directory pair level when directories are set via command line. +Конфігураційний файл не повинен містити налаштувань на рівні пар каталогів, якщо каталоги задаються командним рядком. + +Directories cannot be set for more than one configuration file. +Каталоги не можуть бути призначені більш ніж одному файлу конфігурації. + +Command line +Командний рядок + +Syntax: +Синтаксис: + +config files +файли конфігурації + +directory +каталог + +Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. +Будь-яка кількість FreeFileSync .ffs_gui та/або .ffs_batch файлів конфігурації. + +Any number of alternative directories for at most one config file. +Будь-яка кількість альтернативних каталогів для щонайбільше одного конфігураційного файлу. + +A folder input field is empty. +Порожнє поле папки. + +The corresponding folder will be considered as empty. +Відповідна папка буде вважатися порожньою. + +Cannot find the following folders: +Не вдається знайти такі папки: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Ви можете ігнорувати цю помилку, вважаючи кожну папку порожньою. Папки будуть автоматично створені під час синхронізації. + +The following folders have dependent paths. Be careful when setting up synchronization rules: +Наступні папки мають залежні шляхи. Будьте обережні при налаштуванні правил синхронізації: + +File %x has an invalid date. +Файл %x має неіснуючу дату. + +Date: +Дата: + +Files %x have the same date but a different size. +Файли %x мають однакову дату, але різні за розміром. + +Size: +Розмір: + +Items differ in attributes only +Елементи відрізняються тільки атрибутами + +Resolving symbolic link %x +Вирішення символьного посилання %x + +Comparing content of files %x +Порівнювання вмісту файлів %x + +Generating file list... +Створення списку файлів... + +Starting comparison +Початок порівняння + +Calculating sync directions... +Встановлення напрямку синхронізації... + +Out of memory. +Бракує пам'яті. + +Item exists on left side only +Елемент існує тільки ліворуч + +Item exists on right side only +Елемент існує тільки праворуч + +Left side is newer +Ліва сторона новіша + +Right side is newer +Права сторона новіша + +Items have different content +Елементи мають різний вміст + +Both sides are equal +Сторони ідентичні + +Conflict/item cannot be categorized +Не вдається категоризувати конфлікт/елемент + +Copy new item to left +Копіювати нові елементи ліворуч + +Copy new item to right +Копіювати нові елементи праворуч + +Delete left item +Вилучити елемент ліворуч + +Delete right item +Вилучити елемент праворуч + +Move file on left +Перемістити файли ліворуч + +Move file on right +Перемістити файли праворуч + +Overwrite left item +Перезаписати елемент ліворуч + +Overwrite right item +Перезаписати елемент праворуч + +Do nothing +Нічого не робити + +Update attributes on left +Оновити атрибути ліворуч + +Update attributes on right +Оновити атрибути праворуч + +Database file %x is incompatible. +Несумісний файл бази даних %x. + +Initial synchronization: +Вступна синхронізація: + +Database file %x does not yet exist. +Файл бази даних %x ще не існує. + +Database file is corrupt: +Файл бази даних пошкоджений: + +Cannot write file %x. +Не вдається записати файл %x. + +Cannot read file %x. +Не вдається прочитати файл %x. + +Database files do not share a common session. +Файли баз даних не поділяють спільну сесію. + +Searching for folder %x... +Пошук каталогу %x... + +Cannot read file attributes of %x. +Не вдається прочитати атрибути файла %x. + +Cannot get process information. +Не вдається отримати інформацію процесу + +Waiting while directory is locked (%x)... +Очікування зняття блокування з каталогу (%x)... + + +1 sec +%x sec + + +%x сек +%x сек +%x сек + + +Creating file %x +Створення файлу %x + +Items processed: +Елементів оброблено: + +Items remaining: +Елементів залишилось: + +Total time: +Загальний час: + + +1 byte +%x bytes + + +%x байт +%x байти +%x байтів + + +%x MB +%x МБ + +%x KB +%x КБ + +%x GB +%x ГБ + +Error parsing file %x, row %y, column %z. +Помилка розбору файла %x, рядок %y, колонка %z. + +Cannot set directory lock for %x. +Не вдається замкнути каталога %x. + +Scanning: +Сканую: + + +1 thread +%x threads + + +%x нить виконання +%x ниті виконання +%x нитей виконання + + +Encoding extended time information: %x +Кодування розширеної інформації про час: %x + +/sec +/сек + +%x items/sec +%x елемента/сек + +Configuration file %x loaded partially only. +Файл конфігурації %x завантажено лише частково. + +Show in Explorer +Показати у Провіднику + +Open with default application +Відкрити за допомогою програми за замовчуванням + +Browse directory +Переглянути каталог + +Cannot access the Volume Shadow Copy Service. +Не вдається отримати доступ до послуги Тіньового Копіювання Тому. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Будь ласка, використовуйте 64-розрядну версію FreeFileSync для створення тіньових копій у цій системі. + +Cannot load file %x. +Не вдається завантажити файл %x. + +Cannot determine volume name for %x. +Не вдалося встановити ім'я тому для %x. + +Volume name %x is not part of file path %y. +Ім'я тому %x не є частиною файловго шляху %y. + +Stop requested: Waiting for current operation to finish... +Запит зупинки: очікування завершення поточної операції... + +Unable to create timestamp for versioning: +Не вдається створити часової мітки для версій: + +Cannot read the following XML elements: +Не вдається прочитати такі елементи XML: + +&Open... +&Відкрити... + +Save &as... +Зберегти &як... + +&Quit +&Вихід + +&Program +&Програма + +&View help +&Перегляд довідки + +&About +&Про програму + +&Help +&Допомога + +Usage: +Використання: + +1. Select folders to watch. +1. Виберіть папки для моніторингу; + +2. Enter a command line. +2. Уведіть рядок команди; + +3. Press 'Start'. +3. Натисніть 'Старт'. + +To get started just import a .ffs_batch file. +Щоб розпочати імпортуйте .ffs_batch файл. + +Folders to watch: +Папки для спостеження + +Add folder +Додати папку + +Remove folder +Вилучити папку + +Browse +Переглянути + +Select a folder +Вибрати папку + +Idle time (in seconds): +Час очікування (секунд): + +Idle time between last detected change and execution of command +Час простою між виявленням останньої зміни та виконанням команди + +Command line: +Командний рядок: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +Команда спрацьовує, якщо: +- змінилися файли або вкладені папки +- появилися нові папки (наприклад, підключена USB-пам'ять) + + +&Start +&Старт + +About +Про + +Build: %x +компіляція %x + +All files +Всі файли + +Automated Synchronization +Автоматична Синхронізація + +Directory monitoring active +Моніторинг каталогів активний + +Waiting until all directories are available... +Очікування доступності всіх каталогів... + +Error +Помилка + +&Restore +&Відновити + +&Show error +&Показати помилку + +&Exit +&Вихід + +Incorrect command line: +Неправильний командний рядок: + +&Retry +&Повторити + +File content +Вміст файлу + +File time and size +Дата та розмір файлу + +Two way +Обидва напрямки + +Mirror +Дзеркало + +Update +Оновити + +Custom +Вибірково + +Multiple... +Різні варіанти... + +Moving file %x to %y +Переміщення файлу %x до %y + +Moving folder %x to %y +Переміщення папки %x до %y + +Moving symbolic link %x to %y +Переміщення символьного посилання %x до %y + +Removing old versions... +Видалення старих версій... + +Creating symbolic link %x +Створення символьного посилання %x + +Creating folder %x +Створення папки %x + +Overwriting file %x +Перезапис файлу %x + +Overwriting symbolic link %x +Перезапис символьного посилання %x + +Verifying file %x +Перевірка файлу %x + +Updating attributes of %x +Оновлення атрибутів %x + +Cannot find %x. +Не вдається знайти %x. + +Target folder %x already existing. +Цільова папка %x вже існує. + +Target folder input field must not be empty. +Поле цільової папки не повинно бути порожнім. + +Please enter a target folder for versioning. +Будь ласка, введіть цільову папку для версій. + +Source folder %x not found. +Вихідний каталог %x не знайдено. + +The following items have unresolved conflicts and will not be synchronized: +Наступні елементи мають невирішені конфлікти і не будуть синхронізовані: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +Ці папки істотно відрізняються. Переконайтеся, що ви вказали відповідні папки для синхронізації. + +Not enough free disk space available in: +Не достатньо вільного місця в: + +Required: +Потрібно: + +Available: +Доступно: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +Буде змінена папка, яка є частиною кількох пар папок. Будь ласка, перегляньте налаштування синхронізації. + +Synchronizing folder pair: +Синхронізація пари папок: + +Generating database... +Створення бази даних... + +Creating a Volume Shadow Copy for %x... +Створення Тіньової Копії для %x... + +Data verification error: %x and %y have different content. +Помилка перевірки даних: %x та %y мають різний вміст. + +job name +назва завдання + +Synchronization stopped +Синхронізацію зупинено + +Synchronization completed with errors +Синхронізація закінчилася з помилками + +Synchronization completed with warnings +Синхронізація завершена з попередженнями + +Nothing to synchronize +Нічого синхронізувати + +Synchronization completed successfully +Синхронізація успішно завершена + +Saving log file %x... +Збереження файла журналу %x... + +You can switch to FreeFileSync's main window to resolve this issue. +Ви можете перейти до головного вікна FreeFileSync щоб вирішити це питання. + +&Don't show this warning again +&Надалі не показувати це попередження + +&Ignore +&Ігнорувати + +&Switch +&Змінити + +Switching to FreeFileSync's main window +Перехід до головного вікна FreeFileSync + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + + + +&Ignore subsequent errors +&Ігнорувати наступні помилки + +Retrying operation... + + +Serious Error +Серйозна помилка + +Check for Program Updates +Перевірка Оновлень Програми + +A new version of FreeFileSync is available: +Доступна нова версія FreeFileSync: + +Download now? +Завантажити зараз? + +&Download +&Завантажити + +FreeFileSync is up to date. +У Вас найновіша версія FreeFileSync. + +Unable to connect to sourceforge.net. +Не можна з’єднатися з sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Не вдається знайти номер поточної версії FreeFileSync онлайн. Бажаєте перевірити вручну? + +&Check + + +Symlink +Символьне посилання + +Folder +Папка + +Full path +Повний шлях + +Name +Назва + +Relative path +Відносний шлях + +Base folder +Базова папка + +Size +Розмір + +Date +Дата + +Extension +Розширення + +Category +Категорія + +Action +Дія + +Drag && drop +Drag && drop + +Close progress dialog +Закрити вікно прогресу + +Standby +Сплячий режим + +Log off +Вилогувати + +Shut down +Вимкнути комп'ютер + +Hibernate +Гібернація + +Alternate comparison settings +Альтернативні налаштування порівняння + +Alternate synchronization settings +Альтернативні налаштування синхронізації + +Local filter +Локальний фільтр + +Active +Активні + +None +Відсутні + +Remove alternate settings +Вилучити альтернативні налаштування + +Clear filter settings +Очистити налаштування фільтра + +Copy +Копіювати + +Paste +Вклеїти + +Alternate Comparison Settings +Альтернативні Налаштування Порівняння + +Alternate Synchronization Settings +Альтернативні Налаштування Синхронізації + +Local Filter +Локальний Фільтр + +&New +&Нова + +&Save +&Зберегти + +Save as &batch job... +Зберегти як &пакетне завдання + +1. &Compare +1. &Порівняти + +2. &Synchronize +2. &Синхронізувати + +&Global settings +&Глобальні налаштування + +&Language +&Мова + +&Find... +&Знайти... + +&Export file list... +&Експортувати список файлів... + +&Tools +&Інструменти + +&Check now +&Перевірити тепер + +Check &automatically once a week +Перевіряти &автоматично щотижня + +&Check for new version +&Перевірка наявності нової версії + +Compare +Порівняти + +Cancel +Відмінити + +Synchronize +Синхронізувати + +Add folder pair +Додати пару папок + +Remove folder pair +Вилучити пару папок + +Swap sides +Поміняти місцями + +Close search bar +Закрити панель пошуку + +Find: +Знайти: + +Match case +Враховувати регістр + +Save as batch job +Зберегти як пакетне завдання + +Hide excluded items +Приховати виключені елементи + +Show filtered or temporarily excluded files +Показати відфільтровані чи тимчасово виключені елементи + +Number of files and folders that will be created +Кількість файлів і папок, які будуть створені + +Number of files that will be overwritten +Кількість файлів, які будуть перезаписані + +Number of files and folders that will be deleted +Кількість файлів і папок, які будуть вилучені + +Total bytes to copy +Всього зкопіювати байтів + +Select a variant: +Виберіть варіант: + +Identify equal files by comparing modification time and size. +Визначити однакові файли порівнюючи час модифікації та розмір. + +Identify equal files by comparing the file content. +Визначити однакові файли порівнюючи їх вміст. + +Symbolic links: +Символьні посилання: + +More information +Додаткова інформація + +OK +OK + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Виявити та поширити зміни на обидві сторони. Видалення, перейменування та конфлікти визначаються автоматично використовуючи базу даних. + +Create a mirror backup of the left folder which exactly matches the right folder after synchronization. +Створити дзеркальну копію лівої частини, після синхронізації права папка повністю дорівнюватиме лівій. + +Copy new and updated files to the right folder. +Скопіювати нові та оновлені файли в праву папку. + +Configure your own synchronization rules. +Налаштувати власні правила синхронізації. + +Detect moved files +Виявляти переміщені файли + +Requires database files. Not supported by all file systems. +Потрібні файли бази даних. Підтримується не всіма файловими системами. + +Delete files: +Вилучити файли: + +Permanent +Назавжди + +Delete or overwrite files permanently +Вилучати чи перезаписати файли назавжди + +Recycle bin +Корзина + +Back up deleted and overwritten files in the recycle bin +Резервно зберегти вилучені та перезаисані файли в Корзині + +Versioning +Запис версій + +Move files to a user-defined folder +Перемістити файли у визначену користувачем папку + +Naming convention: +Метод іменування: + +Show examples +Показати приклади + +Handle errors: +Обробка помилок: + +Ignore +Ігнорувати + +Hide all error and warning messages +Приховати всі помилки і повідомлення з попередженнями + +Pop-up +Виринаючі вікна + +Show pop-up on errors or warnings +Показувати виринаючі вікна при помилках та попередженнях + +On completion: +Після завершення: + +Start synchronization now? +Розпочати синхронізацію? + +Variant: +Варіант: + +Statistics +Статистика + +&Don't show this dialog again +Більше &не показувати цей діалог + +Items found: +Елементів знайдено: + +Speed: +Швидкість: + +Time remaining: +Залишилось часу: + +Time elapsed: +Пройшло часу: + +Synchronizing... +Синхронізація... + +Minimize to notification area +Згорнути в область повідомлень + +Close +Замкнути + +&Pause +&Пауза + +Stop +Зупинити + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Створити пакетний файл для автоматичної синхронізації. Щоб розпочати двічі клацніть цей файл або заплануйте в планувальнику завдань: %x + +Stop synchronization at first error +Зупинити синхронізацію при першій помилці + +Show progress dialog +Показувати вікно прогресу + +Save log: +Зберегти журнал: + +Limit: +Обмеження: + +Limit maximum number of log files +Обмежити максимальну кількість файлів журналу + +How can I schedule a batch job? +Як можна запланувати пакетне завдання? + +&Recycle bin +&Корзина + +Delete on both sides +Вилучити з обох сторін + +Delete on both sides even if the file is selected on one side only +Вилучити з обох сторін, навіть якщо файл виділений тільки з однієї сторони + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Виберіть правила фільтрації для виключення деяких файлів із синхронізації. Введіть шляхи файлів відносно відповідної пари папок. + +Include: +Включити + +Exclude: +Виключити + +Time span: +Часовий інтервал + +File size: +Розмір Файла + +Minimum: +Мінімум: + +Maximum: +Максимум: + +&Clear +&Очистити + +The following settings are used for all synchronization jobs. +Наступні налаштування використовуються для всіх завдань синхронізації. + +Fail-safe file copy +Безпечне копіювання файлів + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Скопіювати в тимчасовий файл (*.ffs_tmp) перед перезаписом цілі. +Це гарантує узгоджений стан навіть у випадку серйозної помилки. + + +(recommended) +(рекомендовано) + +Copy locked files +Копіювати заблоковані файли + +Copy shared or locked files using the Volume Shadow Copy Service. +Копіювати спільні та заблоковані файли за допомогою сервісу Тіньового Копіювання Тому. + +(requires administrator rights) +(потрібні права адміністратора) + +Copy file access permissions +Копіювати права доступу до файлу + +Transfer file and folder permissions. +Перенести права файлів і папок. + +Automatic retry on error: +Автоматичний повтор при помилці: + +Retry count: +Кількість спроб: + +Delay (in seconds): +Затримка (секунд): + +Customize context menu: +Налаштування контекстного меню: + +Description +Опис + +Restore hidden windows +Відновити приховані вікна + +&Default +&За замовчуванням + +Source code written in C++ using: +Код програми написаний на C++ з використанням: + +If you like FreeFileSync +Якщо Вам сподобався FreeFileSync + +Donate with PayPal +Пожертвувати через PayPal + +Feedback and suggestions are welcome +Відгуки та пропозиції вітаються + +Homepage +Оф.сайт + +Email +Почта + +Published under the GNU General Public License +Видано за ліцензією GNU General Public License + +Many thanks for localization: +Подяка за локалізацію: + +Save as Batch Job +Зберегти як пакетне завдання + +Delete Items +Вилучити Елементи + +Global Settings +Глобальні Налаштування + +Select Time Span +Виберіть Інтервал Часу + +Folder Pairs +Пари Папок + +Find +Знайти + +Overview +Головна + +Configuration +Налаштування + +Main Bar +Головна панель + +Filter Files +Фільтрування файлів + +Select View +Вибрати перегляд + +Open... +Відкрити... + +Save +Зберегти + +Compare both sides +Порівняти обидві сторони + +Comparison settings +Налаштування порівнювання + +Synchronization settings +Налаштування синхронізації + +Start synchronization +Розпочати синхронізацію + +Confirm +Підтвердити + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + + + +&Execute +&Виконати + + +1 directory +%x directories + + +%x каталог +%x каталоги +%x каталогів + + + +1 file +%x files + + +%x файл +%x файли +%x файлів + + + +%y of 1 row in view +%y of %x rows in view + + +%y of %x рядка +%y of %x рядків +%y of %x рядків + + +Set direction: +Виберіть напрям: + +multiple selection +груповий вибір + +Include via filter: +Включити згідно фільтру: + +Exclude via filter: +Виключити через фільтр: + +Exclude temporarily +Виключити тимчасово + +Include temporarily +Включити + +Delete +Видалити + +Include all +Включити все + +Exclude all +Виключити все + +Show icons: +Показати іконки: + +Small +Малий + +Medium +середній + +Large +великий + +Select time span... +Виберіть інтервал часу... + +Default view +Вигляд за замовчуванням + +Show "%x" +Показати "%x" + +Last session +Остання сесія + +Folder Comparison and Synchronization +Порівнювання та Синхронізація папок + +Configuration saved +Налаштування синхронізації збережено + +FreeFileSync batch +Командний файл FreeFileSync + +Do you want to save changes to %x? +Зберегти зміни в %x? + +Never save &changes +Ніколи не зберігати &зміни + +Do&n't save +&Не зберігати + +Filter +Фільтр + +Show files that exist on left side only +Показати файли, які є тільки ліворуч + +Show files that exist on right side only +Показати файли, які є тільки праворуч + +Show files that are newer on left +Показати файли, які новіші ліворуч + +Show files that are newer on right +Показати файли, які новіші праворуч + +Show files that are equal +Показати однакові файли + +Show files that are different +Показати різні файли + +Show conflicts +Показати конфлікти + +Show files that will be created on the left side +Показати файли, які будуть створені ліворуч + +Show files that will be created on the right side +Показати файли, які будуть створені праворуч + +Show files that will be deleted on the left side +Показати файли, які будуть вилучені ліворуч + +Show files that will be deleted on the right side +Показати файли, які будуть вилучені праворуч + +Show files that will be overwritten on left side +Показати файли, які будуть перезаписані ліворуч + +Show files that will be overwritten on right side +Показати файли, які будуть перезаписані праворуч + +Show files that won't be copied +Показати файли, які не будуть зкопійовані + +Set as default +Встановити за замовчуванням + +All folders are in sync +Всі папки синхронізовано + +Synchronization Settings +Налаштування Синхронізації + +Comparison Settings +Параметри Порівняння + +Cannot find %x +Не можна знайти %x + +Comma-separated values +Значення розділені комою + +File list exported +Список файлів експортовано + +Searching for program updates... +Пошук оновлень програми ... + +Scanning... +Сканування... + +Comparing content... +Порівнювання вмісту... + +Info +Інформація + +Warning +Увага + +Paused +Призупинено + +Initializing... +Ініціалізація... + +Stopped +Зупинено + +Completed +Завершено + +&Continue +&Продовжити + +Log +Лог + +Today +Сьогодні + +This week +Цього тижня + +This month +Цього місяця + +This year +Цього року + +Last x days +Останні x днів + +Byte +Байт + +KB +КБ + +MB +МБ + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Ви дійсно хочете перемістити цей %x елемент у Корзину? +Ви дійсно хочете перемістити ці %x елементи у Корзину? +Ви дійсно хочете перемістити ці %x елементів у Корзину? + + +Move +Перемістити + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Ви дійсно хочете вилучити цей %x елемент? +Ви дійсно хочете вилучити ці %x елементи? +Ви дійсно хочете вилучити ці %x елементів? + + +Exclude +Виключити + +Direct +Прямо + +Follow +Послідовно + +Copy NTFS permissions +Копіювати права доступу NTFS + +Integrate external applications into context menu. The following macros are available: +Інтеграція зовнішніх додатків в контекстному меню. Доступні макроси: + +- full file or folder name +- повна назва файлу або папки + +- folder part only +- тільки папка + +- Other side's counterpart to %item_path% +- Елемент з протилежної сторони до %item_path% + +- Other side's counterpart to %item_folder% +- Елемент з протилежної сторони до %item_folder% + +Restore all hidden windows and warnings? +Відновити всі приховані вікна та попередження? + +Leave as unresolved conflict +Залишити як невирішений конфлікт + +Replace +Замінити + +Move files and replace if existing +Перемістити файли замінюючи існуючі + +Time stamp +Часова мітка + +Append a timestamp to each file name +Додати часову мітку до кожного імені файлу + +File +Файл + +YYYY-MM-DD hhmmss +YYYY-MM-DD hhmmss + +Files +Файли + +Items +Елементи + +Percentage +Проценти + +Cannot monitor directory %x. +Не вдається контролювати каталог %x. + +Conversion error: +Помилка перетворення: + +Cannot delete file %x. +Не вдається видалити файл %x. + +The file is locked by another process: +Файл заблоковано іншим процесом: + +Cannot move file %x to %y. +Не вдається перемістити файл %x до %y. + +Cannot delete directory %x. +Не вдається видалити каталог %x. + +Cannot write file attributes of %x. +Не вдається записати атрибути файла %x. + +Cannot write modification time of %x. +Не вдається записати часу модифікації %x. + +Cannot read security context of %x. +Не вдається прочитати контексту безпеки %x. + +Cannot write security context of %x. +Не вдається записати контексту безпеки %x. + +Cannot read permissions of %x. +Не вдається прочитати дозволів %x. + +Cannot write permissions of %x. +Не вдається записати дозволів %x. + +Cannot create directory %x. +Не вдається створити каталогу %x. + +Cannot create symbolic link %x. +Не вдається створити символьного посилання %x. + +Cannot find system function %x. +Не вдається знайти системної функції %x. + +Cannot copy file %x to %y. +Не вдається зкопіювати файл %x до %y. + +Type of item %x is not supported: +Тип елемента %x не підтримується: + +Cannot resolve symbolic link %x. +Не вдається вирішити символьне посилання %x. + +Cannot open directory %x. +Не вдається відкрити каталогу %x. + +Cannot enumerate directory %x. +Не вдається вчитати каталог %x. + +%x TB +%x ТБ + +%x PB +%x ПБ + + +1 min +%x min + + +%x хв +%x хв +%x хв + + + +1 hour +%x hours + + +%x година +%x години +%x годин + + + +1 day +%x days + + +%x день +%x дні +%x днів + + +Unable to register to receive system messages. +Не вдається зареєструватися для отримання системних повідомлень. + +Cannot set privilege %x. +Не вдається встановити привілеї %x. + +Unable to suspend system sleep mode. +Не вдається призупинbnb режим сну системи. + +Cannot change process I/O priorities. +Не вдалося змінити пріоритетів Вх/Вих процесу. + +Unable to move %x to the recycle bin. +Не вдається перемістити %x до Корзини. + +Cannot determine final path for %x. +Не вдається визначити кінцевого шляху для %x. + +Error Code %x: +Код помилки %x: + diff --git a/FreeFileSync/Build/Resources.zip b/FreeFileSync/Build/Resources.zip new file mode 100644 index 00000000..ee52f047 Binary files /dev/null and b/FreeFileSync/Build/Resources.zip differ diff --git a/FreeFileSync/Build/Sync_Complete.wav b/FreeFileSync/Build/Sync_Complete.wav new file mode 100644 index 00000000..96dd2a15 Binary files /dev/null and b/FreeFileSync/Build/Sync_Complete.wav differ diff --git a/FreeFileSync/Build/styles.gtk_rc b/FreeFileSync/Build/styles.gtk_rc new file mode 100644 index 00000000..c6b1b5ab --- /dev/null +++ b/FreeFileSync/Build/styles.gtk_rc @@ -0,0 +1,8 @@ +style "no-inner-border" +{ + GtkButton::focus-padding = 0 + GtkButton::focus-line-width = 0 + GtkButton::inner-border = {0, 0, 0, 0} +} + +class "GtkButton" style "no-inner-border" diff --git a/FreeFileSync/Source/FreeFileSync.vcxproj b/FreeFileSync/Source/FreeFileSync.vcxproj new file mode 100644 index 00000000..cba18010 --- /dev/null +++ b/FreeFileSync/Source/FreeFileSync.vcxproj @@ -0,0 +1,282 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {86C36CC7-9418-4253-9928-828486F59A00} + Win32Proj + FreeFileSync + $(VCTargetsPath11) + + + + Application + true + Unicode + v120_xp + + + Application + true + Unicode + v120_xp + + + Application + false + true + Unicode + v120_xp + + + Application + false + true + Unicode + v120_xp + + + + + + + + + + + + + + + + ..\Build\Bin\ + ..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + false + $(ProjectName)_$(PlatformName) + + + ..\Build\Bin\ + ..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + false + $(ProjectName)_$(PlatformName) + + + false + ..\Build\Bin\ + ..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + false + $(ProjectName)_$(PlatformName) + + + ..\Build\Bin\ + ..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + false + $(ProjectName)_$(PlatformName) + false + + + + Use + Level4 + Disabled + _SCL_SECURE_NO_WARNINGS;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;__WXDEBUG__;WXUSINGDLL;_DEBUG;%(PreprocessorDefinitions) + C:\Data\Projects;C:\Data\Projects\zenXml;C:\Data\C++\Boost;C:\Data\C++\wxWidgets\include;C:\Data\C++\wxWidgets\lib\vc12_x86_debug_dll\mswud + wx+/pch.h + 4100;4512 + true + $(IntDir)pch.obj + zen/warn_static.h;wx+/pch.h + true + NoExtensions + false + + + Windows + true + $(OutDir)$(TargetName)$(TargetExt) + Rpcrt4.lib;winmm.lib;comctl32.lib;Wininet.lib;wxmsw29ud_aui.lib;wxmsw29ud_adv.lib;wxmsw29ud_core.lib;wxbase29ud_net.lib;wxbase29ud.lib;wxpngd.lib;wxzlibd.lib;ws2_32.lib;%(AdditionalDependencies) + C:\Data\C++\Boost\stage\lib;C:\Data\C++\wxWidgets\lib\vc12_x86_debug_dll + + + C:\Data\C++\wxWidgets\include + %(PreprocessorDefinitions);ZEN_ARCHITECTURE_X86 + + + + + Use + Level4 + Disabled + _SCL_SECURE_NO_WARNINGS;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;__WXDEBUG__;WXUSINGDLL;_DEBUG;%(PreprocessorDefinitions) + C:\Data\Projects;C:\Data\Projects\zenXml;C:\Data\C++\Boost;C:\Data\C++\wxWidgets\include;C:\Data\C++\wxWidgets\lib\vc12_x64_debug_dll\mswud + wx+/pch.h + 4100;4512 + true + $(IntDir)pch.obj + zen/warn_static.h;wx+/pch.h + true + false + + + Windows + true + $(OutDir)$(TargetName)$(TargetExt) + Rpcrt4.lib;winmm.lib;comctl32.lib;Wininet.lib;wxmsw29ud_aui.lib;wxmsw29ud_adv.lib;wxmsw29ud_core.lib;wxbase29ud_net.lib;wxbase29ud.lib;wxpngd.lib;wxzlibd.lib;ws2_32.lib;%(AdditionalDependencies) + C:\Data\C++\Boost\stage_x64\lib;C:\Data\C++\wxWidgets\lib\vc12_x64_debug_dll + + + C:\Data\C++\wxWidgets\include + %(PreprocessorDefinitions);ZEN_ARCHITECTURE_X64 + + + + + Level4 + MaxSpeed + _SCL_SECURE_NO_WARNINGS;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;%(PreprocessorDefinitions) + C:\Data\Projects;C:\Data\Projects\zenXml;C:\Data\C++\Boost;C:\Data\C++\wxWidgets\include;C:\Data\C++\wxWidgets\lib\vc12_x86_release_lib\mswu + Speed + 4100;4512;4996 + MultiThreaded + true + zen/warn_static.h + NoExtensions + + + Windows + Rpcrt4.lib;winmm.lib;comctl32.lib;Wininet.lib;wxmsw29u_aui.lib;wxmsw29u_adv.lib;wxmsw29u_core.lib;wxbase29u.lib;wxpng.lib;wxzlib.lib;wxbase29u_net.lib;ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + C:\Data\C++\Boost\stage\lib;C:\Data\C++\wxWidgets\lib\vc12_x86_release_lib + + + C:\Data\C++\wxWidgets\include + %(PreprocessorDefinitions);ZEN_ARCHITECTURE_X86 + + + "C:\Data\C++\CodeSigning\SignCode.cmd" "$(TargetPath)" + + + + + Level4 + MaxSpeed + _SCL_SECURE_NO_WARNINGS;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;%(PreprocessorDefinitions) + C:\Data\Projects;C:\Data\Projects\zenXml;C:\Data\C++\Boost;C:\Data\C++\wxWidgets\include;C:\Data\C++\wxWidgets\lib\vc12_x64_release_lib\mswu + Speed + 4100;4512;4996 + MultiThreaded + true + zen/warn_static.h + + + Windows + true + Rpcrt4.lib;winmm.lib;comctl32.lib;Wininet.lib;wxmsw29u_aui.lib;wxmsw29u_adv.lib;wxmsw29u_core.lib;wxbase29u.lib;wxpng.lib;wxzlib.lib;wxbase29u_net.lib;ws2_32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + C:\Data\C++\Boost\stage_x64\lib;C:\Data\C++\wxWidgets\lib\vc12_x64_release_lib + + + C:\Data\C++\wxWidgets\include + %(PreprocessorDefinitions);ZEN_ARCHITECTURE_X64 + + + "C:\Data\C++\CodeSigning\SignCode.cmd" "$(TargetPath)" + + + + + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + Use + Use + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + + + + \ No newline at end of file diff --git a/FreeFileSync/Source/LICENSE b/FreeFileSync/Source/LICENSE new file mode 100644 index 00000000..94a04532 --- /dev/null +++ b/FreeFileSync/Source/LICENSE @@ -0,0 +1,621 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS diff --git a/FreeFileSync/Source/Makefile b/FreeFileSync/Source/Makefile new file mode 100644 index 00000000..919328f2 --- /dev/null +++ b/FreeFileSync/Source/Makefile @@ -0,0 +1,172 @@ +BLAH_BLUBB_123= +#for some buggy reason the first row in the make file has no effect on Suse Linux! => make sure there's no important command +APPNAME = FreeFileSync +prefix = /usr +BINDIR = $(DESTDIR)$(prefix)/bin +SHAREDIR = $(DESTDIR)$(prefix)/share +APPSHAREDIR = $(SHAREDIR)/$(APPNAME) +DOCSHAREDIR = $(SHAREDIR)/doc/$(APPNAME) + +CXXFLAGS = -std=c++11 -Wall -pipe -O3 -DNDEBUG -DWXINTL_NO_GETTEXT_MACRO -I../.. -I../../zenXml -include "zen/i18n.h" -include "zen/warn_static.h" +LINKFLAGS = + +#distinguish Linux/OSX builds +OPERATING_SYSTEM_NAME := $(shell uname) + +#################### Linux ############################ +ifeq ($(OPERATING_SYSTEM_NAME), Linux) +COMPILER_BIN=g++ -pthread +CXXFLAGS += -DZEN_LINUX + +#Gtk - support recycler/icon loading/no button border/grid scrolling +CXXFLAGS += `pkg-config --cflags gtk+-2.0` +LINKFLAGS += `pkg-config --libs gtk+-2.0` + +#support for SELinux (optional) +SELINUX_EXISTING=$(shell pkg-config --exists libselinux && echo YES) +ifeq ($(SELINUX_EXISTING),YES) +CXXFLAGS += `pkg-config --cflags libselinux` -DHAVE_SELINUX +LINKFLAGS += `pkg-config --libs libselinux` +endif + +#support for Ubuntu Unity (optional) +UNITY_EXISTING=$(shell pkg-config --exists unity && echo YES) +ifeq ($(UNITY_EXISTING),YES) +CXXFLAGS += `pkg-config --cflags unity` -DHAVE_UBUNTU_UNITY +LINKFLAGS += `pkg-config --libs unity` +endif + +ifeq ($(BUILD),Launchpad) +#default build/Launchpad +CXXFLAGS += `wx-config --cxxflags --debug=no` +LINKFLAGS += `wx-config --libs std, aui --debug=no` -lboost_thread -lboost_system -lz +else +#static wxWidgets and boost library linkage for precompiled release +WX_CONFIG_BIN =$(HOME)/Desktop/wxWidgets-2.9.5/lib/release/bin/wx-config +CXXFLAGS += -I$(HOME)/Desktop/boost_1_54_0 +BOOST_LIB_DIR =$(HOME)/Desktop/boost_1_54_0/stage/lib + +CXXFLAGS += `$(WX_CONFIG_BIN) --cxxflags --debug=no --static=yes` +LINKFLAGS += `$(WX_CONFIG_BIN) --libs std, aui --debug=no --static=yes` $(BOOST_LIB_DIR)/libboost_thread.a $(BOOST_LIB_DIR)/libboost_system.a -lX11 +endif + +endif +#################### OS X ############################ +ifeq ($(OPERATING_SYSTEM_NAME), Darwin) +COMPILER_BIN=clang++ -stdlib=libc++ +CXXFLAGS += -DZEN_MAC + +WX_CONFIG_BIN =$(HOME)/Desktop/wxWidgets-2.9.5/lib/release/bin/wx-config +CXXFLAGS += -I$(HOME)/Desktop/boost_1_54_0 +BOOST_LIB_DIR =$(HOME)/Desktop/boost_1_54_0/stage/lib +MACOS_SDK =-mmacosx-version-min=10.7 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk + +#-Wl,-Bstatic not supported on OSX! + +# link wxWidgets and boost statically -> check dependencies with: otool -L FreeFileSync +CXXFLAGS += $(MACOS_SDK) `$(WX_CONFIG_BIN) --cxxflags --debug=no --static=yes` +LINKFLAGS += $(MACOS_SDK) `$(WX_CONFIG_BIN) --libs std, aui --debug=no --static=yes` $(BOOST_LIB_DIR)/libboost_thread.a $(BOOST_LIB_DIR)/libboost_system.a + +endif +###################################################### + +CPP_LIST= #internal list of all *.cpp files needed for compilation +CPP_LIST+=algorithm.cpp +CPP_LIST+=application.cpp +CPP_LIST+=comparison.cpp +CPP_LIST+=structures.cpp +CPP_LIST+=synchronization.cpp +CPP_LIST+=file_hierarchy.cpp +CPP_LIST+=ui/custom_grid.cpp +CPP_LIST+=ui/folder_history_box.cpp +CPP_LIST+=ui/exec_finished_box.cpp +CPP_LIST+=ui/dir_name.cpp +CPP_LIST+=ui/batch_config.cpp +CPP_LIST+=ui/batch_status_handler.cpp +CPP_LIST+=ui/check_version.cpp +CPP_LIST+=ui/grid_view.cpp +CPP_LIST+=ui/tree_view.cpp +CPP_LIST+=ui/gui_generated.cpp +CPP_LIST+=ui/gui_status_handler.cpp +CPP_LIST+=ui/main_dlg.cpp +CPP_LIST+=ui/progress_indicator.cpp +CPP_LIST+=ui/search.cpp +CPP_LIST+=ui/small_dlgs.cpp +CPP_LIST+=ui/sync_cfg.cpp +CPP_LIST+=ui/taskbar.cpp +CPP_LIST+=ui/triple_splitter.cpp +CPP_LIST+=ui/tray_icon.cpp +CPP_LIST+=lib/binary.cpp +CPP_LIST+=lib/db_file.cpp +CPP_LIST+=lib/dir_lock.cpp +CPP_LIST+=lib/hard_filter.cpp +CPP_LIST+=lib/icon_buffer.cpp +CPP_LIST+=lib/localization.cpp +CPP_LIST+=lib/parallel_scan.cpp +CPP_LIST+=lib/process_xml.cpp +CPP_LIST+=lib/resolve_path.cpp +CPP_LIST+=lib/perf_check.cpp +CPP_LIST+=lib/status_handler.cpp +CPP_LIST+=lib/versioning.cpp +CPP_LIST+=lib/ffs_paths.cpp +CPP_LIST+=lib/xml_base.cpp +CPP_LIST+=../../zen/recycler.cpp +CPP_LIST+=../../zen/file_handling.cpp +CPP_LIST+=../../zen/file_io.cpp +CPP_LIST+=../../zen/file_traverser.cpp +CPP_LIST+=../../zen/zstring.cpp +CPP_LIST+=../../zen/format_unit.cpp +CPP_LIST+=../../zen/process_priority.cpp +CPP_LIST+=../../wx+/grid.cpp +CPP_LIST+=../../wx+/image_tools.cpp +CPP_LIST+=../../wx+/graph.cpp +CPP_LIST+=../../wx+/tooltip.cpp +CPP_LIST+=../../wx+/image_resources.cpp +CPP_LIST+=../../wx+/popup_dlg.cpp +CPP_LIST+=../../wx+/popup_dlg_generated.cpp +CPP_LIST+=../../wx+/zlib_wrap.cpp + +# OS X +ifeq ($(OPERATING_SYSTEM_NAME), Darwin) +MM_LIST= #objective C files +MM_LIST+=ui/osx_dock.mm +MM_LIST+=lib/osx_file_icon.mm +endif + +#list of all *.o files +OBJECT_LIST = $(CPP_LIST:%.cpp=../Obj/FFS_GCC_Make_Release/ffs/src/%.o) +OBJECT_LIST += $(MM_LIST:%.mm=../Obj/FFS_GCC_Make_Release/ffs/src/%.mm.o) + +all: FreeFileSync + +../Obj/FFS_GCC_Make_Release/ffs/src/%.mm.o : %.mm + mkdir -p $(dir $@) + $(COMPILER_BIN) $(CXXFLAGS) -c $< -o $@ + +../Obj/FFS_GCC_Make_Release/ffs/src/%.o : %.cpp + mkdir -p $(dir $@) + $(COMPILER_BIN) $(CXXFLAGS) -c $< -o $@ + +FreeFileSync: $(OBJECT_LIST) + $(COMPILER_BIN) -o ../Build/$(APPNAME) $(OBJECT_LIST) $(LINKFLAGS) + +clean: + rm -rf ../Obj/FFS_GCC_Make_Release + rm -f ../Build/$(APPNAME) + rm -f ../../wx+/pch.h.gch + +install: + mkdir -p $(BINDIR) + cp ../Build/$(APPNAME) $(BINDIR) + + mkdir -p $(APPSHAREDIR) + cp -R ../Build/Languages/ \ + ../Build/Help/ \ + ../Build/Sync_Complete.wav \ + ../Build/Resources.zip \ + ../Build/styles.gtk_rc \ + $(APPSHAREDIR) + + mkdir -p $(DOCSHAREDIR) + cp ../Build/Changelog.txt $(DOCSHAREDIR)/changelog + gzip $(DOCSHAREDIR)/changelog diff --git a/FreeFileSync/Source/RealtimeSync/Makefile b/FreeFileSync/Source/RealtimeSync/Makefile new file mode 100644 index 00000000..3ce0a764 --- /dev/null +++ b/FreeFileSync/Source/RealtimeSync/Makefile @@ -0,0 +1,101 @@ +BLAH_BLUBB_123= +#for some buggy reason the first row in the make file has no effect on Suse Linux! => make sure there's no important command +APPNAME = RealtimeSync +prefix = /usr +BINDIR = $(DESTDIR)$(prefix)/bin + +CXXFLAGS = -std=c++11 -Wall -pipe -O3 -DNDEBUG -DWXINTL_NO_GETTEXT_MACRO -I../../.. -I../../../zenXml -include "zen/i18n.h" -include "zen/warn_static.h" +LINKFLAGS = + +#distinguish Linux/Mac builds +OPERATING_SYSTEM_NAME := $(shell uname) + +#################### Linux ############################ +ifeq ($(OPERATING_SYSTEM_NAME), Linux) +COMPILER_BIN=g++ -pthread +CXXFLAGS += -DZEN_LINUX + +#Gtk - support "no button border" +CXXFLAGS += `pkg-config --cflags gtk+-2.0` +LINKFLAGS += `pkg-config --libs gtk+-2.0` + +ifeq ($(BUILD),Launchpad) +#default build/Launchpad +CXXFLAGS += `wx-config --cxxflags --debug=no` +LINKFLAGS += `wx-config --libs --debug=no` -lboost_thread -lboost_system -lz +else +#static wxWidgets and boost library linkage for precompiled release +WX_CONFIG_BIN =$(HOME)/Desktop/wxWidgets-2.9.5/lib/release/bin/wx-config +CXXFLAGS += -I$(HOME)/Desktop/boost_1_54_0 +BOOST_LIB_DIR =$(HOME)/Desktop/boost_1_54_0/stage/lib + +CXXFLAGS += `$(WX_CONFIG_BIN) --cxxflags --debug=no --static=yes` +LINKFLAGS += `$(WX_CONFIG_BIN) --libs --debug=no --static=yes` $(BOOST_LIB_DIR)/libboost_thread.a $(BOOST_LIB_DIR)/libboost_system.a -lX11 +endif + +endif +#################### OS X ############################ +ifeq ($(OPERATING_SYSTEM_NAME), Darwin) +COMPILER_BIN=clang++ -stdlib=libc++ +CXXFLAGS += -DZEN_MAC + +WX_CONFIG_BIN =$(HOME)/Desktop/wxWidgets-2.9.5/lib/release/bin/wx-config +CXXFLAGS += -I$(HOME)/Desktop/boost_1_54_0 +BOOST_LIB_DIR =$(HOME)/Desktop/boost_1_54_0/stage/lib +MACOS_SDK =-mmacosx-version-min=10.7 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk +#-Wl,-Bstatic not supported on OSX! + +# link wxWidgets and boost statically -> check dependencies with: otool -L RealtimeSync +CXXFLAGS += $(MACOS_SDK) `$(WX_CONFIG_BIN) --cxxflags --debug=no --static=yes` +LINKFLAGS += $(MACOS_SDK) `$(WX_CONFIG_BIN) --libs --debug=no --static=yes` $(BOOST_LIB_DIR)/libboost_thread.a $(BOOST_LIB_DIR)/libboost_system.a + +endif +###################################################### + +CPP_LIST= #internal list of all *.cpp files needed for compilation +CPP_LIST+=application.cpp +CPP_LIST+=gui_generated.cpp +CPP_LIST+=main_dlg.cpp +CPP_LIST+=tray_menu.cpp +CPP_LIST+=monitor.cpp +CPP_LIST+=xml_ffs.cpp +CPP_LIST+=xml_proc.cpp +CPP_LIST+=../structures.cpp +CPP_LIST+=../ui/dir_name.cpp +CPP_LIST+=../ui/folder_history_box.cpp +CPP_LIST+=../lib/localization.cpp +CPP_LIST+=../lib/process_xml.cpp +CPP_LIST+=../lib/resolve_path.cpp +CPP_LIST+=../lib/xml_base.cpp +CPP_LIST+=../lib/ffs_paths.cpp +CPP_LIST+=../../../zen/dir_watcher.cpp +CPP_LIST+=../../../zen/file_handling.cpp +CPP_LIST+=../../../zen/file_io.cpp +CPP_LIST+=../../../zen/file_traverser.cpp +CPP_LIST+=../../../zen/zstring.cpp +CPP_LIST+=../../../zen/format_unit.cpp +CPP_LIST+=../../../wx+/image_tools.cpp +CPP_LIST+=../../../wx+/image_resources.cpp +CPP_LIST+=../../../wx+/popup_dlg.cpp +CPP_LIST+=../../../wx+/popup_dlg_generated.cpp + +#list of all *.o files (we need the "RTS" subdirectory to handle "../*.cpp" files +OBJECT_LIST=$(CPP_LIST:%.cpp=../../Obj/RTS_GCC_Make_Release/ffs/src/rts/%.o) + +all: RealtimeSync + +../../Obj/RTS_GCC_Make_Release/ffs/src/rts/%.o : %.cpp + mkdir -p $(dir $@) + $(COMPILER_BIN) $(CXXFLAGS) -c $< -o $@ + +RealtimeSync: $(OBJECT_LIST) + $(COMPILER_BIN) -o ../../Build/$(APPNAME) $(OBJECT_LIST) $(LINKFLAGS) + +clean: + rm -rf ../../Obj/RTS_GCC_Make_Release + rm -f ../../Build/$(APPNAME) + rm -f ../../../wx+/pch.h.gch + +install: + mkdir -p $(BINDIR) + cp ../../Build/$(APPNAME) $(BINDIR) diff --git a/FreeFileSync/Source/RealtimeSync/RealtimeSync.ico b/FreeFileSync/Source/RealtimeSync/RealtimeSync.ico new file mode 100644 index 00000000..f9012b12 Binary files /dev/null and b/FreeFileSync/Source/RealtimeSync/RealtimeSync.ico differ diff --git a/FreeFileSync/Source/RealtimeSync/RealtimeSync.vcxproj b/FreeFileSync/Source/RealtimeSync/RealtimeSync.vcxproj new file mode 100644 index 00000000..9344fb4b --- /dev/null +++ b/FreeFileSync/Source/RealtimeSync/RealtimeSync.vcxproj @@ -0,0 +1,250 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {A80B97E9-807C-47A1-803A-27565A1BD526} + Win32Proj + RealtimeSync + $(VCTargetsPath11) + + + + Application + true + Unicode + v120_xp + + + Application + true + Unicode + v120_xp + + + Application + false + true + Unicode + v120_xp + + + Application + false + true + Unicode + v120_xp + + + + + + + + + + + + + + + + ..\..\Build\Bin\ + ..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + false + $(ProjectName)_$(PlatformName) + + + ..\..\Build\Bin\ + ..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + false + $(ProjectName)_$(PlatformName) + + + false + ..\..\Build\Bin\ + ..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + false + $(ProjectName)_$(PlatformName) + + + false + ..\..\Build\Bin\ + ..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + false + $(ProjectName)_$(PlatformName) + + + + Use + Level4 + Disabled + _SCL_SECURE_NO_WARNINGS;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;__WXDEBUG__;WXUSINGDLL;_DEBUG;%(PreprocessorDefinitions) + C:\Data\Projects;C:\Data\Projects\zenXml;C:\Data\C++\Boost;C:\Data\C++\wxWidgets\include;C:\Data\C++\wxWidgets\lib\vc12_x86_debug_dll\mswud + wx+/pch.h + 4100;4512 + true + $(IntDir)pch.obj + zen/warn_static.h;wx+/pch.h + NoExtensions + true + false + + + Windows + true + $(OutDir)$(TargetName)$(TargetExt) + comctl32.lib;wxmsw29ud_adv.lib;wxmsw29ud_core.lib;wxbase29ud.lib;wxpngd.lib;wxzlibd.lib;wxbase29ud_net.lib;comctl32.lib;ws2_32.lib;Rpcrt4.lib;winmm.lib;%(AdditionalDependencies) + C:\Data\C++\Boost\stage\lib;C:\Data\C++\wxWidgets\lib\vc12_x86_debug_dll + + + C:\Data\C++\wxWidgets\include + %(PreprocessorDefinitions);ZEN_ARCHITECTURE_X86 + + + + + Use + Level4 + Disabled + _SCL_SECURE_NO_WARNINGS;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;__WXDEBUG__;WXUSINGDLL;_DEBUG;%(PreprocessorDefinitions) + C:\Data\Projects;C:\Data\Projects\zenXml;C:\Data\C++\Boost;C:\Data\C++\wxWidgets\include;C:\Data\C++\wxWidgets\lib\vc12_x64_debug_dll\mswud + wx+/pch.h + 4100;4512 + true + $(IntDir)pch.obj + zen/warn_static.h;wx+/pch.h + false + true + false + + + Windows + true + $(OutDir)$(TargetName)$(TargetExt) + comctl32.lib;wxmsw29ud_adv.lib;wxmsw29ud_core.lib;wxbase29ud.lib;wxpngd.lib;wxzlibd.lib;wxbase29ud_net.lib;comctl32.lib;ws2_32.lib;Rpcrt4.lib;winmm.lib;%(AdditionalDependencies) + C:\Data\C++\Boost\stage_x64\lib;C:\Data\C++\wxWidgets\lib\vc12_x64_debug_dll + + + C:\Data\C++\wxWidgets\include + %(PreprocessorDefinitions);ZEN_ARCHITECTURE_X64 + + + + + Level4 + NotUsing + MaxSpeed + true + _SCL_SECURE_NO_WARNINGS;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;%(PreprocessorDefinitions) + C:\Data\Projects;C:\Data\Projects\zenXml;C:\Data\C++\Boost;C:\Data\C++\wxWidgets\include;C:\Data\C++\wxWidgets\lib\vc12_x86_release_lib\mswu + Speed + 4100;4512;4996 + MultiThreaded + true + zen/warn_static.h + NoExtensions + + + Windows + comctl32.lib;wxmsw29u_adv.lib;wxbase29u_net.lib;wxbase29u.lib;wxmsw29u_core.lib;wxpng.lib;wxzlib.lib;ws2_32.lib;winmm.lib;Rpcrt4.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + C:\Data\C++\Boost\stage\lib;C:\Data\C++\wxWidgets\lib\vc12_x86_release_lib + + + C:\Data\C++\wxWidgets\include + %(PreprocessorDefinitions);ZEN_ARCHITECTURE_X86 + + + "C:\Data\C++\CodeSigning\SignCode.cmd" "$(TargetPath)" + + + + + Level4 + NotUsing + MaxSpeed + true + _SCL_SECURE_NO_WARNINGS;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;%(PreprocessorDefinitions) + C:\Data\Projects;C:\Data\Projects\zenXml;C:\Data\C++\Boost;C:\Data\C++\wxWidgets\include;C:\Data\C++\wxWidgets\lib\vc12_x64_release_lib\mswu + Speed + 4100;4512;4996 + MultiThreaded + true + zen/warn_static.h + + + Windows + comctl32.lib;wxmsw29u_adv.lib;wxbase29u_net.lib;wxbase29u.lib;wxmsw29u_core.lib;wxpng.lib;wxzlib.lib;ws2_32.lib;winmm.lib;Rpcrt4.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + C:\Data\C++\Boost\stage_x64\lib;C:\Data\C++\wxWidgets\lib\vc12_x64_release_lib + + + C:\Data\C++\wxWidgets\include + %(PreprocessorDefinitions);ZEN_ARCHITECTURE_X64 + + + "C:\Data\C++\CodeSigning\SignCode.cmd" "$(TargetPath)" + + + + + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/FreeFileSync/Source/RealtimeSync/app_icon.h b/FreeFileSync/Source/RealtimeSync/app_icon.h new file mode 100644 index 00000000..fcd2b548 --- /dev/null +++ b/FreeFileSync/Source/RealtimeSync/app_icon.h @@ -0,0 +1,40 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef RTS_APP_ICON_8914578394545 +#define RTS_APP_ICON_8914578394545 + +#include +#include + +namespace zen +{ +inline +wxIcon getRtsIcon() +{ + //wxWidgets' bitmap to icon conversion on OS X can only deal with very specific sizes => check on all platforms! + assert(getResourceImage(L"RealtimeSync").GetWidth () == getResourceImage(L"RealtimeSync").GetHeight() && + getResourceImage(L"RealtimeSync").GetWidth() % 128 == 0); +#ifdef ZEN_WIN + //for compatibility it seems we need to stick with a "real" icon + return wxIcon(L"A_RTS_ICON"); + +#elif defined ZEN_LINUX + //attention: make sure to not implicitly call "instance()" again => deadlock on Linux + wxIcon icon; + icon.CopyFromBitmap(getResourceImage(L"RealtimeSync")); //use big logo bitmap for better quality + return icon; + +#elif defined ZEN_MAC + wxIcon icon; + icon.CopyFromBitmap(getResourceImage(L"RealtimeSync").ConvertToImage().Scale(128, 128, wxIMAGE_QUALITY_HIGH)); //"von hinten durch die Brust ins Auge" + return icon; +#endif +} +} + + +#endif //RTS_APP_ICON_8914578394545 diff --git a/FreeFileSync/Source/RealtimeSync/application.cpp b/FreeFileSync/Source/RealtimeSync/application.cpp new file mode 100644 index 00000000..e5bed7f3 --- /dev/null +++ b/FreeFileSync/Source/RealtimeSync/application.cpp @@ -0,0 +1,186 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "application.h" +#include "main_dlg.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "xml_ffs.h" +#include "../lib/localization.h" +#include "../lib/ffs_paths.h" +#include "../lib/return_codes.h" +#include "../lib/error_log.h" + +#ifdef ZEN_WIN +#include + +#elif defined ZEN_LINUX +#include +#endif + +using namespace zen; + + +IMPLEMENT_APP(Application); + +namespace +{ +/* +boost::thread::id mainThreadId = boost::this_thread::get_id(); + +void onTerminationRequested() +{ +std::wstring msg = boost::this_thread::get_id() == mainThreadId ? + L"Termination requested in main thread!\n\n" : + L"Termination requested in worker thread!\n\n"; +msg += L"Please file a bug report at: http://sourceforge.net/projects/freefilesync"; + +wxSafeShowMessage(_("An exception occurred"), msg); +std::abort(); +} +*/ +#ifdef _MSC_VER +void crtInvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved) { assert(false); } +#endif + +const wxEventType EVENT_ENTER_EVENT_LOOP = wxNewEventType(); +} + + +bool Application::OnInit() +{ + //std::set_terminate(onTerminationRequested); //unlike wxWidgets uncaught exception handling, this works for all worker threads + +#ifdef ZEN_WIN +#ifdef _MSC_VER + _set_invalid_parameter_handler(crtInvalidParameterHandler); //see comment in +#endif + //Quote: "Best practice is that all applications call the process-wide SetErrorMode function with a parameter of + //SEM_FAILCRITICALERRORS at startup. This is to prevent error mode dialogs from hanging the application." + ::SetErrorMode(SEM_FAILCRITICALERRORS); + +#elif defined ZEN_LINUX + ::gtk_rc_parse((zen::getResourceDir() + "styles.gtk_rc").c_str()); //remove inner border from bitmap buttons +#endif + +#ifdef ZEN_WIN + wxToolTip::SetMaxWidth(-1); //disable tooltip wrapping -> Windows only +#endif + //Windows User Experience Interaction Guidelines: tool tips should have 5s timeout, info tips no timeout => compromise: + wxToolTip::SetAutoPop(7000); //http://msdn.microsoft.com/en-us/library/windows/desktop/aa511495.aspx + + SetAppName(L"RealtimeSync"); + + initResourceImages(getResourceDir() + Zstr("Resources.zip")); + + Connect(wxEVT_QUERY_END_SESSION, wxEventHandler(Application::onQueryEndSession), nullptr, this); + Connect(wxEVT_END_SESSION, wxEventHandler(Application::onQueryEndSession), nullptr, this); + + //do not call wxApp::OnInit() to avoid using default commandline parser + + //Note: app start is deferred: -> see FreeFileSync + Connect(EVENT_ENTER_EVENT_LOOP, wxEventHandler(Application::onEnterEventLoop), nullptr, this); + wxCommandEvent scrollEvent(EVENT_ENTER_EVENT_LOOP); + AddPendingEvent(scrollEvent); + + return true; //true: continue processing; false: exit immediately. +} + + +int Application::OnExit() +{ + releaseWxLocale(); + return wxApp::OnExit(); +} + + +void Application::onEnterEventLoop(wxEvent& event) +{ + Disconnect(EVENT_ENTER_EVENT_LOOP, wxEventHandler(Application::onEnterEventLoop), nullptr, this); + + try + { + setLanguage(rts::getProgramLanguage()); //throw FileError + } + catch (const FileError& e) + { + showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); + //continue! + } + + //try to set config/batch-filename set by %1 parameter + std::vector commandArgs; + for (int i = 1; i < argc; ++i) + { + Zstring filename = toZ(argv[i]); + + if (!fileExists(filename)) //be a little tolerant + { + if (fileExists(filename + Zstr(".ffs_real"))) + filename += Zstr(".ffs_real"); + else if (fileExists(filename + Zstr(".ffs_batch"))) + filename += Zstr(".ffs_batch"); + else + { + showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setMainInstructions(replaceCpy(_("Cannot open file %x."), L"%x", fmtFileName(filename)))); + return; + } + } + commandArgs.push_back(filename); + } + + Zstring cfgFilename; + if (!commandArgs.empty()) + cfgFilename = commandArgs[0]; + + MainDialog::create(cfgFilename); +} + + +int Application::OnRun() +{ + + auto processException = [](const std::wstring& msg) + { + //it's not always possible to display a message box, e.g. corrupted stack, however low-level file output works! + logError(utfCvrtTo(msg)); + wxSafeShowMessage(_("An exception occurred"), msg); + }; + + try + { + wxApp::OnRun(); + } + catch (const std::exception& e) //catch all STL exceptions + { + processException(utfCvrtTo(e.what())); + return FFS_RC_EXCEPTION; + } + catch (...) //catch the rest + { + processException(L"Unknown error."); + return FFS_RC_EXCEPTION; + } + + return FFS_RC_SUCCESS; //program's return code +} + + + +void Application::onQueryEndSession(wxEvent& event) +{ + if (auto mainWin = dynamic_cast(GetTopWindow())) + mainWin->onQueryEndSession(); + OnExit(); //wxWidgets screws up again: http://trac.wxwidgets.org/ticket/3069 + //wxEntryCleanup(); -> gives popup "dll init failed" on XP + std::exit(FFS_RC_SUCCESS); //Windows will terminate anyway: destruct global objects +} \ No newline at end of file diff --git a/FreeFileSync/Source/RealtimeSync/application.h b/FreeFileSync/Source/RealtimeSync/application.h new file mode 100644 index 00000000..3752f824 --- /dev/null +++ b/FreeFileSync/Source/RealtimeSync/application.h @@ -0,0 +1,26 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef REALTIMESYNCAPP_H +#define REALTIMESYNCAPP_H + +#include + +class Application : public wxApp +{ +public: + virtual bool OnInit(); + virtual int OnExit(); + virtual int OnRun(); + virtual bool OnExceptionInMainLoop() { throw; } //just re-throw and avoid display of additional messagebox: it will be caught in OnRun() + void onQueryEndSession(wxEvent& event); + +private: + void onEnterEventLoop(wxEvent& event); + //virtual wxLayoutDirection GetLayoutDirection() const { return wxLayout_LeftToRight; } +}; + +#endif // REALTIMESYNCAPP_H diff --git a/FreeFileSync/Source/RealtimeSync/gui_generated.cpp b/FreeFileSync/Source/RealtimeSync/gui_generated.cpp new file mode 100644 index 00000000..3b3ad3fe --- /dev/null +++ b/FreeFileSync/Source/RealtimeSync/gui_generated.cpp @@ -0,0 +1,283 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Oct 8 2012) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "../wx+/bitmap_button.h" + +#include "gui_generated.h" + +/////////////////////////////////////////////////////////////////////////// + +MainDlgGenerated::MainDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + m_menubar1 = new wxMenuBar( 0 ); + m_menuFile = new wxMenu(); + wxMenuItem* m_menuItem13; + m_menuItem13 = new wxMenuItem( m_menuFile, wxID_OPEN, wxString( _("&Open...") ) + wxT('\t') + wxT("CTRL+O"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItem13 ); + + wxMenuItem* m_menuItem14; + m_menuItem14 = new wxMenuItem( m_menuFile, wxID_SAVEAS, wxString( _("Save &as...") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItem14 ); + + m_menuFile->AppendSeparator(); + + wxMenuItem* m_menuItem4; + m_menuItem4 = new wxMenuItem( m_menuFile, wxID_EXIT, wxString( _("&Quit") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItem4 ); + + m_menubar1->Append( m_menuFile, _("&Program") ); + + m_menuHelp = new wxMenu(); + wxMenuItem* m_menuItemContent; + m_menuItemContent = new wxMenuItem( m_menuHelp, wxID_HELP, wxString( _("&View help") ) + wxT('\t') + wxT("F1"), wxEmptyString, wxITEM_NORMAL ); + m_menuHelp->Append( m_menuItemContent ); + + m_menuHelp->AppendSeparator(); + + m_menuItemAbout = new wxMenuItem( m_menuHelp, wxID_ABOUT, wxString( _("&About") ) + wxT('\t') + wxT("SHIFT+F1"), wxEmptyString, wxITEM_NORMAL ); + m_menuHelp->Append( m_menuItemAbout ); + + m_menubar1->Append( m_menuHelp, _("&Help") ); + + this->SetMenuBar( m_menubar1 ); + + bSizerMain = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer161; + bSizer161 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer16; + bSizer16 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText9 = new wxStaticText( this, wxID_ANY, _("Usage:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText9->Wrap( -1 ); + m_staticText9->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer16->Add( m_staticText9, 0, wxALL, 5 ); + + wxBoxSizer* bSizer15; + bSizer15 = new wxBoxSizer( wxVERTICAL ); + + m_staticText3 = new wxStaticText( this, wxID_ANY, _("1. Select folders to watch."), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText3->Wrap( -1 ); + bSizer15->Add( m_staticText3, 0, 0, 5 ); + + m_staticText4 = new wxStaticText( this, wxID_ANY, _("2. Enter a command line."), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText4->Wrap( -1 ); + bSizer15->Add( m_staticText4, 0, 0, 5 ); + + m_staticText5 = new wxStaticText( this, wxID_ANY, _("3. Press 'Start'."), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText5->Wrap( -1 ); + bSizer15->Add( m_staticText5, 0, 0, 5 ); + + + bSizer16->Add( bSizer15, 0, wxALL, 5 ); + + + 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->Wrap( -1 ); + m_staticText811->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); + + bSizer161->Add( m_staticText811, 0, wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizerMain->Add( bSizer161, 0, wxALL|wxEXPAND, 5 ); + + m_staticline2 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizerMain->Add( m_staticline2, 0, wxEXPAND, 5 ); + + m_panelMain = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelMain->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer1; + bSizer1 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer151; + bSizer151 = new wxBoxSizer( wxVERTICAL ); + + m_staticText7 = new wxStaticText( m_panelMain, wxID_ANY, _("Folders to watch:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText7->Wrap( -1 ); + bSizer151->Add( m_staticText7, 0, wxALL, 5 ); + + m_panelMainFolder = new wxPanel( m_panelMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelMainFolder->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxFlexGridSizer* fgSizer1; + fgSizer1 = new wxFlexGridSizer( 0, 2, 0, 0 ); + fgSizer1->AddGrowableCol( 1 ); + fgSizer1->SetFlexibleDirection( wxBOTH ); + fgSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_ALL ); + + + fgSizer1->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextFinalPath = new wxStaticText( m_panelMainFolder, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextFinalPath->Wrap( -1 ); + fgSizer1->Add( m_staticTextFinalPath, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); + + wxBoxSizer* bSizer20; + bSizer20 = new wxBoxSizer( wxHORIZONTAL ); + + m_bpButtonAddFolder = new wxBitmapButton( m_panelMainFolder, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonAddFolder->SetToolTip( _("Add folder") ); + + bSizer20->Add( m_bpButtonAddFolder, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonRemoveTopFolder = new wxBitmapButton( m_panelMainFolder, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonRemoveTopFolder->SetToolTip( _("Remove folder") ); + + bSizer20->Add( m_bpButtonRemoveTopFolder, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + fgSizer1->Add( bSizer20, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + wxBoxSizer* bSizer19; + bSizer19 = new wxBoxSizer( wxHORIZONTAL ); + + m_txtCtrlDirectoryMain = new wxTextCtrl( m_panelMainFolder, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 300,-1 ), 0 ); + m_txtCtrlDirectoryMain->SetMaxLength( 0 ); + bSizer19->Add( m_txtCtrlDirectoryMain, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonSelectDirMain = new wxButton( m_panelMainFolder, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonSelectDirMain->SetToolTip( _("Select a folder") ); + + bSizer19->Add( m_buttonSelectDirMain, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + fgSizer1->Add( bSizer19, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + m_panelMainFolder->SetSizer( fgSizer1 ); + m_panelMainFolder->Layout(); + fgSizer1->Fit( m_panelMainFolder ); + bSizer151->Add( m_panelMainFolder, 0, wxRIGHT|wxLEFT|wxEXPAND, 5 ); + + m_scrolledWinFolders = new wxScrolledWindow( m_panelMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); + m_scrolledWinFolders->SetScrollRate( 10, 10 ); + m_scrolledWinFolders->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + bSizerFolders = new wxBoxSizer( wxVERTICAL ); + + + m_scrolledWinFolders->SetSizer( bSizerFolders ); + m_scrolledWinFolders->Layout(); + bSizerFolders->Fit( m_scrolledWinFolders ); + bSizer151->Add( m_scrolledWinFolders, 1, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer1->Add( bSizer151, 1, wxALL|wxEXPAND, 5 ); + + m_staticline212 = new wxStaticLine( m_panelMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer1->Add( m_staticline212, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer14; + bSizer14 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText8 = new wxStaticText( m_panelMain, wxID_ANY, _("Idle time (in seconds):"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText8->Wrap( -1 ); + bSizer14->Add( m_staticText8, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); + + m_spinCtrlDelay = new wxSpinCtrl( m_panelMain, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 2000000000, 0 ); + m_spinCtrlDelay->SetToolTip( _("Idle time between last detected change and execution of command") ); + + bSizer14->Add( m_spinCtrlDelay, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + + bSizer1->Add( bSizer14, 0, wxALIGN_RIGHT|wxEXPAND|wxALL, 5 ); + + m_staticline211 = new wxStaticLine( m_panelMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer1->Add( m_staticline211, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer141; + bSizer141 = new wxBoxSizer( wxVERTICAL ); + + m_staticText6 = new wxStaticText( m_panelMain, wxID_ANY, _("Command line:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText6->Wrap( -1 ); + bSizer141->Add( m_staticText6, 0, wxALL, 5 ); + + m_textCtrlCommand = new wxTextCtrl( m_panelMain, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_textCtrlCommand->SetMaxLength( 0 ); + m_textCtrlCommand->SetToolTip( _("The command is triggered if:\n- files or subfolders change\n- new folders arrive (e.g. USB stick insert)") ); + + bSizer141->Add( m_textCtrlCommand, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 ); + + + bSizer1->Add( bSizer141, 0, wxALL|wxEXPAND, 5 ); + + + m_panelMain->SetSizer( bSizer1 ); + m_panelMain->Layout(); + bSizer1->Fit( m_panelMain ); + bSizerMain->Add( m_panelMain, 1, wxEXPAND, 5 ); + + m_staticline5 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizerMain->Add( m_staticline5, 0, wxEXPAND, 5 ); + + m_buttonStart = new zen::BitmapTextButton( this, wxID_OK, _("&Start"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonStart->SetDefault(); + m_buttonStart->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizerMain->Add( m_buttonStart, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); + + + this->SetSizer( bSizerMain ); + this->Layout(); + bSizerMain->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( MainDlgGenerated::OnClose ) ); + this->Connect( m_menuItem13->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnConfigLoad ) ); + this->Connect( m_menuItem14->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnConfigSave ) ); + this->Connect( m_menuItem4->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnMenuQuit ) ); + this->Connect( m_menuItemContent->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnShowHelp ) ); + this->Connect( m_menuItemAbout->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnMenuAbout ) ); + m_bpButtonAddFolder->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDlgGenerated::OnAddFolder ), NULL, this ); + m_bpButtonRemoveTopFolder->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDlgGenerated::OnRemoveTopFolder ), NULL, this ); + m_buttonStart->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDlgGenerated::OnStart ), NULL, this ); +} + +MainDlgGenerated::~MainDlgGenerated() +{ +} + +FolderGenerated::FolderGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) +{ + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer114; + bSizer114 = new wxBoxSizer( wxHORIZONTAL ); + + m_bpButtonRemoveFolder = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonRemoveFolder->SetToolTip( _("Remove folder") ); + + bSizer114->Add( m_bpButtonRemoveFolder, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_txtCtrlDirectory = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_txtCtrlDirectory->SetMaxLength( 0 ); + bSizer114->Add( m_txtCtrlDirectory, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonSelectDir = new wxButton( this, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonSelectDir->SetToolTip( _("Select a folder") ); + + bSizer114->Add( m_buttonSelectDir, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + this->SetSizer( bSizer114 ); + this->Layout(); + bSizer114->Fit( this ); +} + +FolderGenerated::~FolderGenerated() +{ +} diff --git a/FreeFileSync/Source/RealtimeSync/gui_generated.h b/FreeFileSync/Source/RealtimeSync/gui_generated.h new file mode 100644 index 00000000..a816e10a --- /dev/null +++ b/FreeFileSync/Source/RealtimeSync/gui_generated.h @@ -0,0 +1,117 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Oct 8 2012) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __GUI_GENERATED_H__ +#define __GUI_GENERATED_H__ + +#include +#include +#include +namespace zen { class BitmapTextButton; } + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zen/i18n.h" + +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/// Class MainDlgGenerated +/////////////////////////////////////////////////////////////////////////////// +class MainDlgGenerated : public wxFrame +{ +private: + +protected: + wxMenuBar* m_menubar1; + wxMenu* m_menuFile; + wxMenu* m_menuHelp; + wxMenuItem* m_menuItemAbout; + wxBoxSizer* bSizerMain; + wxStaticText* m_staticText9; + wxStaticText* m_staticText3; + wxStaticText* m_staticText4; + wxStaticText* m_staticText5; + wxStaticText* m_staticText811; + wxStaticLine* m_staticline2; + wxPanel* m_panelMain; + wxStaticText* m_staticText7; + wxPanel* m_panelMainFolder; + wxStaticText* m_staticTextFinalPath; + wxBitmapButton* m_bpButtonAddFolder; + wxBitmapButton* m_bpButtonRemoveTopFolder; + wxTextCtrl* m_txtCtrlDirectoryMain; + wxButton* m_buttonSelectDirMain; + wxScrolledWindow* m_scrolledWinFolders; + wxBoxSizer* bSizerFolders; + wxStaticLine* m_staticline212; + wxStaticText* m_staticText8; + wxSpinCtrl* m_spinCtrlDelay; + wxStaticLine* m_staticline211; + wxStaticText* m_staticText6; + wxTextCtrl* m_textCtrlCommand; + wxStaticLine* m_staticline5; + zen::BitmapTextButton* m_buttonStart; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnConfigLoad( wxCommandEvent& event ) { event.Skip(); } + virtual void OnConfigSave( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMenuQuit( wxCommandEvent& event ) { event.Skip(); } + virtual void OnShowHelp( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMenuAbout( wxCommandEvent& event ) { event.Skip(); } + virtual void OnAddFolder( wxCommandEvent& event ) { event.Skip(); } + virtual void OnRemoveTopFolder( wxCommandEvent& event ) { event.Skip(); } + virtual void OnStart( wxCommandEvent& event ) { event.Skip(); } + + +public: + + MainDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL ); + + ~MainDlgGenerated(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class FolderGenerated +/////////////////////////////////////////////////////////////////////////////// +class FolderGenerated : public wxPanel +{ +private: + +protected: + wxButton* m_buttonSelectDir; + +public: + wxBitmapButton* m_bpButtonRemoveFolder; + wxTextCtrl* m_txtCtrlDirectory; + + FolderGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = 0 ); + ~FolderGenerated(); + +}; + +#endif //__GUI_GENERATED_H__ diff --git a/FreeFileSync/Source/RealtimeSync/main_dlg.cpp b/FreeFileSync/Source/RealtimeSync/main_dlg.cpp new file mode 100644 index 00000000..ee3e7dda --- /dev/null +++ b/FreeFileSync/Source/RealtimeSync/main_dlg.cpp @@ -0,0 +1,532 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "main_dlg.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xml_proc.h" +#include "tray_menu.h" +#include "xml_ffs.h" +#include "app_icon.h" +#include "../lib/help_provider.h" +#include "../lib/process_xml.h" +#include "../lib/ffs_paths.h" +#ifdef ZEN_LINUX +#include +#elif defined ZEN_MAC +#include +#endif + +using namespace zen; + + +class DirectoryPanel : public FolderGenerated +{ +public: + DirectoryPanel(wxWindow* parent) : + FolderGenerated(parent), + dirName(*this, *m_buttonSelectDir, *m_txtCtrlDirectory) + { +#ifdef ZEN_LINUX + //file drag and drop directly into the text control unhelpfully inserts in format "file://.."; see folder_history_box.cpp + if (GtkWidget* widget = m_txtCtrlDirectory->GetConnectWidget()) + ::gtk_drag_dest_unset(widget); +#endif + } + + void setName(const wxString& dirname) { dirName.setName(dirname); } + wxString getName() const { return dirName.getName(); } + +private: + zen::DirectoryName dirName; +}; + + +void MainDialog::create(const Zstring& cfgFile) +{ + /*MainDialog* frame = */ new MainDialog(nullptr, cfgFile); +} + + +MainDialog::MainDialog(wxDialog* dlg, const Zstring& cfgFileName) + : MainDlgGenerated(dlg) +{ +#ifdef ZEN_WIN + new MouseMoveWindow(*this); //ownership passed to "this" + wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! +#endif + +#ifdef ZEN_LINUX + //file drag and drop directly into the text control unhelpfully inserts in format "file://.."; see folder_history_box.cpp + if (GtkWidget* widget = m_txtCtrlDirectoryMain->GetConnectWidget()) + ::gtk_drag_dest_unset(widget); +#endif + + SetIcon(getRtsIcon()); //set application icon + + setRelativeFontSize(*m_buttonStart, 1.5); + + m_bpButtonRemoveTopFolder->Hide(); + m_panelMainFolder->Layout(); + + m_bpButtonAddFolder ->SetBitmapLabel(getResourceImage(L"item_add")); + m_bpButtonRemoveTopFolder->SetBitmapLabel(getResourceImage(L"item_remove")); + setBitmapTextLabel(*m_buttonStart, getResourceImage(L"startRts").ConvertToImage(), m_buttonStart->GetLabel(), 5, 8); + + + //register key event + Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::OnKeyPressed), nullptr, this); + + //prepare drag & drop + dirNameFirst.reset(new DirectoryName(*m_panelMainFolder, *m_buttonSelectDirMain, *m_txtCtrlDirectoryMain, m_staticTextFinalPath)); + + //--------------------------- load config values ------------------------------------ + xmlAccess::XmlRealConfig newConfig; + + const Zstring currentConfigFile = cfgFileName.empty() ? lastConfigFileName() : cfgFileName; + bool loadCfgSuccess = false; + if (!cfgFileName.empty() || fileExists(lastConfigFileName())) + try + { + rts::readRealOrBatchConfig(currentConfigFile, newConfig); //throw FfsXmlError + loadCfgSuccess = true; + } + catch (const xmlAccess::FfsXmlError& e) + { + if (e.getSeverity() == xmlAccess::FfsXmlError::WARNING) + showNotificationDialog(this, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(e.toString())); + else + showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); + } + + const bool startWatchingImmediately = loadCfgSuccess && !cfgFileName.empty(); + + setConfiguration(newConfig); + setLastUsedConfig(currentConfigFile); + //----------------------------------------------------------------------------------------- + + if (startWatchingImmediately) //start watch mode directly + { + wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); + this->OnStart(dummy2); + //don't Show()! + } + else + { + m_buttonStart->SetFocus(); //don't "steal" focus if program is running from sys-tray" + Show(); +#ifdef ZEN_MAC + ProcessSerialNumber psn = { 0, kCurrentProcess }; + ::TransformProcessType(&psn, kProcessTransformToForegroundApplication); //show dock icon, even if we're not an application bundle + ::SetFrontProcess(&psn); //call before TransformProcessType() so that OSX menu is updated correctly + //if the executable is not yet in a bundle or if it is called through a launcher, we need to set focus manually: +#endif + } + + //drag and drop .ffs_real and .ffs_batch on main dialog + setupFileDrop(*m_panelMain); + m_panelMain->Connect(EVENT_DROP_FILE, FileDropEventHandler(MainDialog::onFilesDropped), nullptr, this); + + timerForAsyncTasks.Connect(wxEVT_TIMER, wxEventHandler(MainDialog::onProcessAsyncTasks), nullptr, this); +} + + +MainDialog::~MainDialog() +{ + //save current configuration + const xmlAccess::XmlRealConfig currentCfg = getConfiguration(); + + try //write config to XML + { + writeRealConfig(currentCfg, lastConfigFileName()); //throw FfsXmlError + } + catch (const xmlAccess::FfsXmlError& e) + { + showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); + } +} + + +void MainDialog::onQueryEndSession() +{ + try { writeRealConfig(getConfiguration(), lastConfigFileName()); } //throw FfsXmlError + catch (const xmlAccess::FfsXmlError&) {} //we try our best do to something useful in this extreme situation - no reason to notify or even log errors here! +} + + +void MainDialog::onProcessAsyncTasks(wxEvent& event) +{ + //schedule and run long-running tasks asynchronously + asyncTasks.evalResults(); //process results on GUI queue + if (asyncTasks.empty()) + timerForAsyncTasks.Stop(); +} + + +const Zstring& MainDialog::lastConfigFileName() +{ + static Zstring instance = zen::getConfigDir() + Zstr("LastRun.ffs_real"); + return instance; +} + + +void MainDialog::OnShowHelp(wxCommandEvent& event) +{ + zen::displayHelpEntry(L"html/RealtimeSync.html", this); +} + + +void MainDialog::OnMenuAbout(wxCommandEvent& event) +{ + wxString build = __TDATE__; + build += L" - Unicode"; +#ifndef wxUSE_UNICODE +#error what is going on? +#endif + + build += zen::is64BitBuild ? L" x64" : L" x86"; + assert_static(zen::is32BitBuild || zen::is64BitBuild); + + showNotificationDialog(this, DialogInfoType::INFO, PopupDialogCfg(). + setTitle(_("About")). + setMainInstructions(L"RealtimeSync" L"\n\n" + replaceCpy(_("Build: %x"), L"%x", build))); +} + + +void MainDialog::OnKeyPressed(wxKeyEvent& event) +{ + const int keyCode = event.GetKeyCode(); + if (keyCode == WXK_ESCAPE) + { + Close(); + return; + } + event.Skip(); +} + + +void MainDialog::OnStart(wxCommandEvent& event) +{ + xmlAccess::XmlRealConfig currentCfg = getConfiguration(); + + Hide(); +#ifdef ZEN_MAC + //hide dock icon: else user is able to forcefully show the hidden main dialog by clicking on the icon!! + ProcessSerialNumber psn = { 0, kCurrentProcess }; + ::TransformProcessType(&psn, kProcessTransformToUIElementApplication); +#endif + + switch (rts::startDirectoryMonitor(currentCfg, xmlAccess::extractJobName(utfCvrtTo(currentConfigFileName)))) + { + case rts::EXIT_APP: + Close(); + return; + + case rts::SHOW_GUI: + break; + } + Show(); //don't show for EXIT_APP +#ifdef ZEN_MAC + //why isn't this covered by wxWindows::Raise()?? + ::TransformProcessType(&psn, kProcessTransformToForegroundApplication); //show dock icon again + ::SetFrontProcess(&psn); //call before TransformProcessType() so that OSX menu is updated correctly +#endif + Raise(); +} + + +void MainDialog::OnConfigSave(wxCommandEvent& event) +{ + Zstring defaultFileName = currentConfigFileName.empty() ? Zstr("Realtime.ffs_real") : currentConfigFileName; + //attention: currentConfigFileName may be an imported *.ffs_batch file! We don't want to overwrite it with a GUI config! + if (endsWith(defaultFileName, Zstr(".ffs_batch"))) + replace(defaultFileName, Zstr(".ffs_batch"), Zstr(".ffs_real"), false); + + + wxFileDialog filePicker(this, + wxEmptyString, + //OS X really needs dir/file separated like this: + utfCvrtTo(beforeLast(defaultFileName, FILE_NAME_SEPARATOR)), //default dir; empty string if / not found + utfCvrtTo(afterLast (defaultFileName, FILE_NAME_SEPARATOR)), //default file; whole string if / not found + wxString(L"RealtimeSync (*.ffs_real)|*.ffs_real") + L"|" +_("All files") + L" (*.*)|*", + wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + if (filePicker.ShowModal() != wxID_OK) + return; + + const Zstring newFileName = utfCvrtTo(filePicker.GetPath()); + + //write config to XML + const xmlAccess::XmlRealConfig currentCfg = getConfiguration(); + try + { + writeRealConfig(currentCfg, newFileName); //throw FfsXmlError + setLastUsedConfig(newFileName); + } + catch (const xmlAccess::FfsXmlError& e) + { + showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); + } +} + + +void MainDialog::loadConfig(const Zstring& filename) +{ + xmlAccess::XmlRealConfig newConfig; + + try + { + rts::readRealOrBatchConfig(filename, newConfig); + } + catch (const xmlAccess::FfsXmlError& e) + { + if (e.getSeverity() == xmlAccess::FfsXmlError::WARNING) + showNotificationDialog(this, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(e.toString())); + else + { + showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); + return; + } + } + + setConfiguration(newConfig); + setLastUsedConfig(filename); +} + + +void MainDialog::setLastUsedConfig(const Zstring& filename) +{ + //set title + if (filename == lastConfigFileName()) + { + SetTitle(L"RealtimeSync - " + _("Automated Synchronization")); + currentConfigFileName.clear(); + } + else + { + SetTitle(utfCvrtTo(filename)); + currentConfigFileName = filename; + } +} + + +void MainDialog::OnConfigLoad(wxCommandEvent& event) +{ + wxFileDialog filePicker(this, + wxEmptyString, + utfCvrtTo(beforeLast(currentConfigFileName, FILE_NAME_SEPARATOR)), //default dir; empty string if / not found + wxEmptyString, + wxString(L"RealtimeSync (*.ffs_real; *.ffs_batch)|*.ffs_real;*.ffs_batch") + L"|" +_("All files") + L" (*.*)|*", + wxFD_OPEN); + if (filePicker.ShowModal() == wxID_OK) + loadConfig(utfCvrtTo(filePicker.GetPath())); +} + + +void MainDialog::onFilesDropped(FileDropEvent& event) +{ + const auto& files = event.getFiles(); + if (!files.empty()) + loadConfig(utfCvrtTo(files[0])); +} + + +void MainDialog::setConfiguration(const xmlAccess::XmlRealConfig& cfg) +{ + //clear existing folders + dirNameFirst->setName(wxString()); + clearAddFolders(); + + if (!cfg.directories.empty()) + { + //fill top folder + dirNameFirst->setName(utfCvrtTo(*cfg.directories.begin())); + + //fill additional folders + addFolder(std::vector(cfg.directories.begin() + 1, cfg.directories.end())); + } + + //fill commandline + m_textCtrlCommand->SetValue(utfCvrtTo(cfg.commandline)); + + //set delay + m_spinCtrlDelay->SetValue(static_cast(cfg.delay)); +} + + +xmlAccess::XmlRealConfig MainDialog::getConfiguration() +{ + xmlAccess::XmlRealConfig output; + + output.directories.push_back(utfCvrtTo(dirNameFirst->getName())); + for (const DirectoryPanel* dne : dirNamesExtra) + output.directories.push_back(utfCvrtTo(dne->getName())); + + output.commandline = utfCvrtTo(m_textCtrlCommand->GetValue()); + output.delay = m_spinCtrlDelay->GetValue(); + + return output; +} + + +void MainDialog::OnAddFolder(wxCommandEvent& event) +{ + const Zstring topFolder = utfCvrtTo(dirNameFirst->getName()); + + //clear existing top folder first + dirNameFirst->setName(wxString()); + + std::vector newFolders; + newFolders.push_back(topFolder); + + addFolder(newFolders, true); //add pair in front of additonal pairs +} + + +void MainDialog::OnRemoveFolder(wxCommandEvent& event) +{ + //find folder pair originating the event + const wxObject* const eventObj = event.GetEventObject(); + for (auto it = dirNamesExtra.begin(); it != dirNamesExtra.end(); ++it) + if (eventObj == static_cast((*it)->m_bpButtonRemoveFolder)) + { + removeAddFolder(it - dirNamesExtra.begin()); + return; + } +} + + +void MainDialog::OnRemoveTopFolder(wxCommandEvent& event) +{ + if (dirNamesExtra.size() > 0) + { + dirNameFirst->setName(dirNamesExtra[0]->getName()); + removeAddFolder(0); //remove first of additional folders + } +} + + +#ifdef ZEN_WIN +static const size_t MAX_ADD_FOLDERS = 8; +#elif defined ZEN_LINUX || defined ZEN_MAC +static const size_t MAX_ADD_FOLDERS = 6; +#endif + + +void MainDialog::addFolder(const std::vector& newFolders, bool addFront) +{ + if (newFolders.size() == 0) + return; + +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! +#endif + + int folderHeight = 0; + for (const Zstring& dirname : newFolders) + { + //add new folder pair + DirectoryPanel* newFolder = new DirectoryPanel(m_scrolledWinFolders); + newFolder->m_bpButtonRemoveFolder->SetBitmapLabel(getResourceImage(L"item_remove")); + + //get size of scrolled window + folderHeight = newFolder->GetSize().GetHeight(); + + if (addFront) + { + bSizerFolders->Insert(0, newFolder, 0, wxEXPAND, 5); + dirNamesExtra.insert(dirNamesExtra.begin(), newFolder); + } + else + { + bSizerFolders->Add(newFolder, 0, wxEXPAND, 5); + dirNamesExtra.push_back(newFolder); + } + + //register events + newFolder->m_bpButtonRemoveFolder->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MainDialog::OnRemoveFolder), nullptr, this ); + + //insert directory name + newFolder->setName(utfCvrtTo(dirname)); + } + + //set size of scrolled window + const size_t additionalRows = std::min(dirNamesExtra.size(), MAX_ADD_FOLDERS); //up to MAX_ADD_FOLDERS additional folders shall be shown + m_scrolledWinFolders->SetMinSize(wxSize( -1, folderHeight * static_cast(additionalRows))); + + //adapt delete top folder pair button + m_bpButtonRemoveTopFolder->Show(); + + GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() + Layout(); + Refresh(); //remove a little flicker near the start button +} + + +void MainDialog::removeAddFolder(size_t pos) +{ +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! +#endif + + if (pos < dirNamesExtra.size()) + { + //remove folder pairs from window + DirectoryPanel* pairToDelete = dirNamesExtra[pos]; + const int folderHeight = pairToDelete->GetSize().GetHeight(); + + bSizerFolders->Detach(pairToDelete); //Remove() does not work on Window*, so do it manually + dirNamesExtra.erase(dirNamesExtra.begin() + pos); //remove last element in vector + //more (non-portable) wxWidgets bullshit: on OS X wxWindow::Destroy() screws up and calls "operator delete" directly rather than + //the deferred deletion it is expected to do (and which is implemented correctly on Windows and Linux) + //http://bb10.com/python-wxpython-devel/2012-09/msg00004.html + //=> since we're in a mouse button callback of a sub-component of "pairToDelete" we need to delay deletion ourselves: + processAsync2([] {}, [pairToDelete] { pairToDelete->Destroy(); }); + + //set size of scrolled window + const size_t additionalRows = std::min(dirNamesExtra.size(), MAX_ADD_FOLDERS); //up to MAX_ADD_FOLDERS additional folders shall be shown + m_scrolledWinFolders->SetMinSize(wxSize( -1, folderHeight * static_cast(additionalRows))); + + //adapt delete top folder pair button + if (dirNamesExtra.size() == 0) + { + m_bpButtonRemoveTopFolder->Hide(); + m_panelMainFolder->Layout(); + } + + GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() + Layout(); + Refresh(); //remove a little flicker near the start button + } +} + + +void MainDialog::clearAddFolders() +{ +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! +#endif + + bSizerFolders->Clear(true); + dirNamesExtra.clear(); + + m_scrolledWinFolders->SetMinSize(wxSize(-1, 0)); + + m_bpButtonRemoveTopFolder->Hide(); + m_panelMainFolder->Layout(); + + GetSizer()->SetSizeHints(this); //~=Fit() + Layout(); + Refresh(); //remove a little flicker near the start button +} diff --git a/FreeFileSync/Source/RealtimeSync/main_dlg.h b/FreeFileSync/Source/RealtimeSync/main_dlg.h new file mode 100644 index 00000000..4e6f71b7 --- /dev/null +++ b/FreeFileSync/Source/RealtimeSync/main_dlg.h @@ -0,0 +1,82 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef REALTIMESYNCMAIN_H +#define REALTIMESYNCMAIN_H + +#include "gui_generated.h" +#include +#include +#include +#include +#include +#include +#include "../ui/dir_name.h" + +namespace xmlAccess +{ +struct XmlRealConfig; +} +class DirectoryPanel; + + +class MainDialog: public MainDlgGenerated +{ +public: + static void create(const Zstring& cfgFile); + + void onQueryEndSession(); //last chance to do something useful before killing the application! + +private: + MainDialog(wxDialog* dlg, const Zstring& cfgFileName); + ~MainDialog(); + + void loadConfig(const Zstring& filename); + + virtual void OnClose (wxCloseEvent& event) { Destroy(); } + virtual void OnShowHelp (wxCommandEvent& event); + virtual void OnMenuAbout (wxCommandEvent& event); + virtual void OnAddFolder (wxCommandEvent& event); + virtual void OnRemoveFolder (wxCommandEvent& event); + virtual void OnRemoveTopFolder(wxCommandEvent& event); + virtual void OnKeyPressed (wxKeyEvent& event); + virtual void OnStart (wxCommandEvent& event); + virtual void OnConfigSave (wxCommandEvent& event); + virtual void OnConfigLoad (wxCommandEvent& event); + virtual void OnMenuQuit (wxCommandEvent& event) { Close(); } + void onFilesDropped(zen::FileDropEvent& event); + + void setConfiguration(const xmlAccess::XmlRealConfig& cfg); + xmlAccess::XmlRealConfig getConfiguration(); + void setLastUsedConfig(const Zstring& filename); + + void layoutAsync(); //call Layout() asynchronously + + //void addFolder(const Zstring& dirname, bool addFront = false); + void addFolder(const std::vector& newFolders, bool addFront = false); + void removeAddFolder(size_t pos); + void clearAddFolders(); + + static const Zstring& lastConfigFileName(); + + std::unique_ptr> dirNameFirst; + std::vector dirNamesExtra; //additional pairs to the standard pair + + Zstring currentConfigFileName; + + void onProcessAsyncTasks(wxEvent& event); + + template + void processAsync(Fun doAsync, Fun2 evalOnGui) { asyncTasks.add(doAsync, evalOnGui); timerForAsyncTasks.Start(50); /*timer interval in [ms] */ } + template + void processAsync2(Fun doAsync, Fun2 evalOnGui) { asyncTasks.add2(doAsync, evalOnGui); timerForAsyncTasks.Start(50); /*timer interval in [ms] */ } + + //schedule and run long-running tasks asynchronously, but process results on GUI queue + zen::AsyncTasks asyncTasks; + wxTimer timerForAsyncTasks; //don't use wxWidgets idle handling => repeated idle requests/consumption hogs 100% cpu! +}; + +#endif // REALTIMESYNCMAIN_H diff --git a/FreeFileSync/Source/RealtimeSync/monitor.cpp b/FreeFileSync/Source/RealtimeSync/monitor.cpp new file mode 100644 index 00000000..23473857 --- /dev/null +++ b/FreeFileSync/Source/RealtimeSync/monitor.cpp @@ -0,0 +1,277 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "monitor.h" +#include +#include +#include +#include +#include +#include +#include +#include "../lib/resolve_path.h" +//#include "../library/db_file.h" //SYNC_DB_FILE_ENDING -> complete file too much of a dependency; file ending too little to decouple into single header +//#include "../library/lock_holder.h" //LOCK_FILE_ENDING + +using namespace zen; + + +namespace +{ +const int CHECK_DIR_INTERVAL = 1; //unit: [s] + + +std::vector getFormattedDirs(const std::vector& dirnamePhrases) //throw FileError +{ + std::set dirnames; //make unique + for (const Zstring& phrase : std::set(dirnamePhrases.begin(), dirnamePhrases.end())) + //make unique: no need to resolve duplicate phrases more than once! (consider "[volume name]" syntax) -> shouldn't this be already buffered by OS? + dirnames.insert(getFormattedDirectoryName(phrase)); + + return std::vector(dirnames.begin(), dirnames.end()); +} + + +//wait until changes are detected or if a directory is not available (anymore) +struct WaitResult +{ + enum ChangeType + { + CHANGE_DETECTED, + CHANGE_DIR_MISSING + }; + + WaitResult(const zen::DirWatcher::Entry& changedItem) : type(CHANGE_DETECTED), changedItem_(changedItem) {} + WaitResult(const Zstring& dirname) : type(CHANGE_DIR_MISSING), dirname_(dirname) {} + + ChangeType type; + zen::DirWatcher::Entry changedItem_; //for type == CHANGE_DETECTED: file or directory + Zstring dirname_; //for type == CHANGE_DIR_MISSING +}; + + +WaitResult waitForChanges(const std::vector& dirnamePhrases, //throw FileError + const std::function& onRefreshGui) //bool: readyForSync +{ + const std::vector dirNamesFmt = getFormattedDirs(dirnamePhrases); //throw FileError + if (dirNamesFmt.empty()) //pathological case, but we have to check else this function will wait endlessly + throw zen::FileError(_("A folder input field is empty.")); //should have been checked by caller! + + //detect when volumes are removed/are not available anymore + std::vector>> watches; + + for (const Zstring& dirnameFmt : dirNamesFmt) + { + try + { + //a non-existent network path may block, so check existence asynchronously! + auto ftDirExists = async([=] { return zen::dirExists(dirnameFmt); }); + //we need to check dirExists(), not somethingExists(): it's not clear if DirWatcher detects a type clash (file instead of directory!) + while (!ftDirExists.timed_wait(boost::posix_time::milliseconds(rts::UI_UPDATE_INTERVAL / 2))) + onRefreshGui(false); //may throw! + if (!ftDirExists.get()) + return WaitResult(dirnameFmt); + + watches.push_back(std::make_pair(dirnameFmt, std::make_shared(dirnameFmt))); //throw FileError + } + catch (FileError&) + { + if (!somethingExists(dirnameFmt)) //a benign(?) race condition with FileError + return WaitResult(dirnameFmt); + throw; + } + } + + const std::int64_t TICKS_DIR_CHECK_INTERVAL = CHECK_DIR_INTERVAL * ticksPerSec(); //0 on error + TickVal lastCheck = getTicks(); //0 on error + while (true) + { + const bool checkDirExistNow = [&]() -> bool //checking once per sec should suffice + { + const TickVal now = getTicks(); //0 on error + if (dist(lastCheck, now) >= TICKS_DIR_CHECK_INTERVAL) + { + lastCheck = now; + return true; + } + return false; + }(); + + + for (auto it = watches.begin(); it != watches.end(); ++it) + { + const Zstring& dirname = it->first; + DirWatcher& watcher = *(it->second); + + //IMPORTANT CHECK: dirwatcher has problems detecting removal of top watched directories! + if (checkDirExistNow) + if (!dirExists(dirname)) //catch errors related to directory removal, e.g. ERROR_NETNAME_DELETED -> somethingExists() is NOT sufficient here! + return WaitResult(dirname); + try + { + std::vector changedItems = watcher.getChanges([&] { onRefreshGui(false); /*may throw!*/ }); //throw FileError + + //remove to be ignored changes + vector_remove_if(changedItems, [](const DirWatcher::Entry& e) + { + return endsWith(e.filename_, Zstr(".ffs_lock")) || //sync.ffs_lock, sync.Del.ffs_lock + endsWith(e.filename_, Zstr(".ffs_db")); //sync.ffs_db, .sync.tmp.ffs_db + //no need to ignore temporal recycle bin directory: this must be caused by a file deletion anyway + }); + + if (!changedItems.empty()) + { + /* + std::for_each(changedItems.begin(), changedItems.end(), + [](const Zstring& fn) { wxMessageBox(toWx(fn));}); + */ + return WaitResult(changedItems[0]); //directory change detected + } + + } + catch (FileError&) + { + if (!somethingExists(dirname)) //a benign(?) race condition with FileError + return WaitResult(dirname); + throw; + } + } + + boost::this_thread::sleep(boost::posix_time::milliseconds(rts::UI_UPDATE_INTERVAL / 2)); + onRefreshGui(true); //throw ?: may start sync at this presumably idle time + } +} + + +//wait until all directories become available (again) + logs in network share +void waitForMissingDirs(const std::vector& dirnamePhrases, //throw FileError + const std::function& onRefreshGui) //Zstring: the directory that is currently being waited for +{ + while (true) + { + bool allExisting = true; + //support specifying volume by name => call getFormattedDirectoryName() repeatedly + for (const Zstring& dirnameFmt : getFormattedDirs(dirnamePhrases)) //throw FileError + { + auto ftDirExisting = async([=]() -> bool + { +#ifdef ZEN_WIN + //1. login to network share, if necessary -> we probably do NOT want multiple concurrent runs: GUI!? + loginNetworkShare(dirnameFmt, false); //login networks shares, no PW prompt -> is this really RTS's job? +#endif + //2. check dir existence + return zen::dirExists(dirnameFmt); + }); + while (!ftDirExisting.timed_wait(boost::posix_time::milliseconds(rts::UI_UPDATE_INTERVAL / 2))) + onRefreshGui(dirnameFmt); //may throw! + + if (!ftDirExisting.get()) + { + allExisting = false; + //wait some time... + const int refreshInterval = rts::UI_UPDATE_INTERVAL / 2; + assert_static(CHECK_DIR_INTERVAL * 1000 % refreshInterval == 0); + for (int i = 0; i < CHECK_DIR_INTERVAL * 1000 / refreshInterval; ++i) + { + onRefreshGui(dirnameFmt); //may throw! + boost::this_thread::sleep(boost::posix_time::milliseconds(refreshInterval)); + } + break; + } + } + if (allExisting) + return; + } +} + + +inline +wxString toString(DirWatcher::ActionType type) +{ + switch (type) + { + case DirWatcher::ACTION_CREATE: + return L"CREATE"; + case DirWatcher::ACTION_UPDATE: + return L"UPDATE"; + case DirWatcher::ACTION_DELETE: + return L"DELETE"; + } + return L"ERROR"; +} + +struct ExecCommandNowException {}; +} + + +void rts::monitorDirectories(const std::vector& dirnamePhrases, unsigned int delay, rts::MonitorCallback& callback) +{ + if (dirnamePhrases.empty()) + { + assert(false); + return; + } + + auto execMonitoring = [&] //throw FileError + { + callback.setPhase(MonitorCallback::MONITOR_PHASE_WAITING); + waitForMissingDirs(dirnamePhrases, [&](const Zstring& dirname) { callback.requestUiRefresh(); }); //throw FileError + callback.setPhase(MonitorCallback::MONITOR_PHASE_ACTIVE); + + //schedule initial execution (*after* all directories have arrived, which could take some time which we don't want to include) + time_t nextExecDate = std::time(nullptr) + delay; + + while (true) //loop over command invocations + { + DirWatcher::Entry lastChangeDetected; + try + { + while (true) //loop over detected changes + { + //wait for changes (and for all directories to become available) + WaitResult res = waitForChanges(dirnamePhrases, [&](bool readyForSync) //throw FileError, ExecCommandNowException + { + if (readyForSync) + if (nextExecDate <= std::time(nullptr)) + throw ExecCommandNowException(); //abort wait and start sync + callback.requestUiRefresh(); + }); + switch (res.type) + { + case WaitResult::CHANGE_DIR_MISSING: //don't execute the command before all directories are available! + callback.setPhase(MonitorCallback::MONITOR_PHASE_WAITING); + waitForMissingDirs(dirnamePhrases, [&](const Zstring& dirname) { callback.requestUiRefresh(); }); //throw FileError + callback.setPhase(MonitorCallback::MONITOR_PHASE_ACTIVE); + break; + + case WaitResult::CHANGE_DETECTED: + lastChangeDetected = res.changedItem_; + break; + } + nextExecDate = std::time(nullptr) + delay; + } + } + catch (ExecCommandNowException&) {} + + ::wxSetEnv(L"change_path", utfCvrtTo(lastChangeDetected.filename_)); //some way to output what file changed to the user + ::wxSetEnv(L"change_action", toString(lastChangeDetected.action_)); // + + //execute command + callback.executeExternalCommand(); + nextExecDate = std::numeric_limits::max(); + } + }; + + while (true) + try + { + execMonitoring(); //throw FileError + } + catch (const zen::FileError& e) + { + callback.reportError(e.toString()); + } +} diff --git a/FreeFileSync/Source/RealtimeSync/monitor.h b/FreeFileSync/Source/RealtimeSync/monitor.h new file mode 100644 index 00000000..81569239 --- /dev/null +++ b/FreeFileSync/Source/RealtimeSync/monitor.h @@ -0,0 +1,38 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef MONITOR_HEADER_345087425834253425 +#define MONITOR_HEADER_345087425834253425 + +#include +#include + +namespace rts +{ +const int UI_UPDATE_INTERVAL = 100; //unit: [ms]; perform ui updates not more often than necessary, 100 seems to be a good value with only a minimal performance loss + + +struct MonitorCallback +{ + virtual ~MonitorCallback() {} + + enum WatchPhase + { + MONITOR_PHASE_ACTIVE, + MONITOR_PHASE_WAITING, + }; + virtual void setPhase(WatchPhase mode) = 0; + virtual void executeExternalCommand() = 0; + virtual void requestUiRefresh() = 0; + virtual void reportError(const std::wstring& msg) = 0; //automatically retries after return! +}; +void monitorDirectories(const std::vector& dirnamePhrases, + //non-formatted dirnames that yet require call to getFormattedDirectoryName(); empty directories must be checked by caller! + unsigned int delay, + MonitorCallback& callback); +} + +#endif //MONITOR_HEADER_345087425834253425 diff --git a/FreeFileSync/Source/RealtimeSync/tray_menu.cpp b/FreeFileSync/Source/RealtimeSync/tray_menu.cpp new file mode 100644 index 00000000..6e67b5ec --- /dev/null +++ b/FreeFileSync/Source/RealtimeSync/tray_menu.cpp @@ -0,0 +1,342 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "tray_menu.h" +#include +#include +#include +#include //Linux needs this +#include +#include +#include +#include +#include +#include +#include +#include "monitor.h" +#include "../lib/resolve_path.h" + +using namespace rts; +using namespace zen; + + +namespace +{ +const std::int64_t TICKS_UPDATE_INTERVAL = rts::UI_UPDATE_INTERVAL* ticksPerSec() / 1000; +TickVal lastExec = getTicks(); + +bool updateUiIsAllowed() +{ + const TickVal now = getTicks(); //0 on error + if (dist(lastExec, now) >= TICKS_UPDATE_INTERVAL) //perform ui updates not more often than necessary + { + lastExec = now; + return true; + } + return false; +} + + +enum TrayMode +{ + TRAY_MODE_ACTIVE, + TRAY_MODE_WAITING, + TRAY_MODE_ERROR, +}; + + +class TrayIconObject : public wxTaskBarIcon +{ +public: + TrayIconObject(const wxString& jobname) : + resumeRequested(false), + abortRequested(false), + showErrorMsgRequested(false), + mode(TRAY_MODE_ACTIVE), + iconFlashStatusLast(false), + jobName_(jobname), +#if defined ZEN_WIN || defined ZEN_MAC //16x16 seems to be the only size that is shown correctly on OS X + trayBmp(getResourceImage(L"RTS_tray_16x16")) //use a 16x16 bitmap +#elif defined ZEN_LINUX + trayBmp(getResourceImage(L"RTS_tray_24x24")) //use a 24x24 bitmap for perfect fit +#endif + { + Connect(wxEVT_TASKBAR_LEFT_DCLICK, wxEventHandler(TrayIconObject::OnDoubleClick), nullptr, this); + setMode(mode); + } + + //require polling: + bool resumeIsRequested() const { return resumeRequested; } + bool abortIsRequested () const { return abortRequested; } + + //during TRAY_MODE_ERROR those two functions are available: + void clearShowErrorRequested() { assert(mode == TRAY_MODE_ERROR); showErrorMsgRequested = false; } + bool getShowErrorRequested() const { assert(mode == TRAY_MODE_ERROR); return showErrorMsgRequested; } + + void setMode(TrayMode m) + { + mode = m; + timer.Stop(); + timer.Disconnect(wxEVT_TIMER, wxEventHandler(TrayIconObject::OnErrorFlashIcon), nullptr, this); + switch (m) + { + case TRAY_MODE_ACTIVE: + setTrayIcon(trayBmp, _("Directory monitoring active")); + break; + + case TRAY_MODE_WAITING: + setTrayIcon(greyScale(trayBmp), _("Waiting until all directories are available...")); + break; + + case TRAY_MODE_ERROR: + timer.Connect(wxEVT_TIMER, wxEventHandler(TrayIconObject::OnErrorFlashIcon), nullptr, this); + timer.Start(500); //timer interval in [ms] + break; + } + } + +private: + void OnErrorFlashIcon(wxEvent& event) + { + iconFlashStatusLast = !iconFlashStatusLast; + setTrayIcon(iconFlashStatusLast ? trayBmp : greyScale(trayBmp), _("Error")); + } + + void setTrayIcon(const wxBitmap& bmp, const wxString& statusTxt) + { + wxIcon realtimeIcon; + realtimeIcon.CopyFromBitmap(bmp); + wxString tooltip = L"RealtimeSync\n" + statusTxt; + if (!jobName_.empty()) + tooltip += L"\n\"" + jobName_ + L"\""; + SetIcon(realtimeIcon, tooltip); + } + + enum Selection + { + CONTEXT_RESTORE = 1, //wxWidgets: "A MenuItem ID of zero does not work under Mac" + CONTEXT_SHOW_ERROR, + CONTEXT_ABORT = wxID_EXIT + }; + + virtual wxMenu* CreatePopupMenu() + { + wxMenu* contextMenu = new wxMenu; + + wxMenuItem* defaultItem = nullptr; + switch (mode) + { + case TRAY_MODE_ACTIVE: + case TRAY_MODE_WAITING: + defaultItem = new wxMenuItem(contextMenu, CONTEXT_RESTORE, _("&Restore")); + break; + case TRAY_MODE_ERROR: + defaultItem = new wxMenuItem(contextMenu, CONTEXT_SHOW_ERROR, _("&Show error")); + break; + } +#ifdef ZEN_WIN //no wxMenuItem::SetFont() on Linux and OS X: wasn't wxWidgets supposed to be *portable* at some point in time????? + defaultItem->SetFont(wxNORMAL_FONT->Bold()); +#endif + contextMenu->Append(defaultItem); + + contextMenu->AppendSeparator(); + contextMenu->Append(CONTEXT_ABORT, _("&Exit")); + //event handling + contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TrayIconObject::OnContextMenuSelection), nullptr, this); + + return contextMenu; //ownership transferred to caller + } + + void OnContextMenuSelection(wxCommandEvent& event) + { + switch (static_cast(event.GetId())) + { + case CONTEXT_ABORT: + abortRequested = true; + break; + + case CONTEXT_RESTORE: + resumeRequested = true; + break; + + case CONTEXT_SHOW_ERROR: + showErrorMsgRequested = true; + break; + } + } + + void OnDoubleClick(wxEvent& event) + { + switch (mode) + { + case TRAY_MODE_ACTIVE: + case TRAY_MODE_WAITING: + resumeRequested = true; //never throw exceptions through a C-Layer call stack (GUI)! + break; + case TRAY_MODE_ERROR: + showErrorMsgRequested = true; + break; + } + } + + bool resumeRequested; + bool abortRequested; + bool showErrorMsgRequested; + + TrayMode mode; + + bool iconFlashStatusLast; //flash try icon for TRAY_MODE_ERROR + wxTimer timer; // + + const wxString jobName_; //RTS job name, may be empty + const wxBitmap trayBmp; +}; + + +struct AbortMonitoring //exception class +{ + AbortMonitoring(AbortReason reasonCode) : reasonCode_(reasonCode) {} + AbortReason reasonCode_; +}; + + +//=> don't derive from wxEvtHandler or any other wxWidgets object unless instance is safely deleted (deferred) during idle event!!tray_icon.h +class TrayIconHolder +{ +public: + TrayIconHolder(const wxString& jobname) : + trayObj(new TrayIconObject(jobname)) {} + + ~TrayIconHolder() + { + //harmonize with tray_icon.cpp!!! + trayObj->RemoveIcon(); + //use wxWidgets delayed destruction: delete during next idle loop iteration (handle late window messages, e.g. when double-clicking) + wxPendingDelete.Append(trayObj); + } + + void doUiRefreshNow() //throw AbortMonitoring + { + wxTheApp->Yield(); //yield is UI-layer which is represented by this tray icon + + //advantage of polling vs callbacks: we can throw exceptions! + if (trayObj->resumeIsRequested()) + throw AbortMonitoring(SHOW_GUI); + + if (trayObj->abortIsRequested()) + throw AbortMonitoring(EXIT_APP); + } + + void setMode(TrayMode m) { trayObj->setMode(m); } + + bool getShowErrorRequested() const { return trayObj->getShowErrorRequested(); } + void clearShowErrorRequested() { trayObj->clearShowErrorRequested(); } + +private: + TrayIconObject* trayObj; +}; + +//############################################################################################################## +} + + +rts::AbortReason rts::startDirectoryMonitor(const xmlAccess::XmlRealConfig& config, const wxString& jobname) +{ + std::vector dirNamesNonFmt = config.directories; + vector_remove_if(dirNamesNonFmt, [](Zstring str) -> bool { trim(str); return str.empty(); }); //remove empty entries WITHOUT formatting paths yet! + + if (dirNamesNonFmt.empty()) + { + showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setMainInstructions(_("A folder input field is empty."))); + return SHOW_GUI; + } + + Zstring cmdLine = config.commandline; + trim(cmdLine); + + if (cmdLine.empty()) + { + showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setMainInstructions(_("Incorrect command line:") + L" \"\"")); + return SHOW_GUI; + } + + struct MonitorCallbackImpl : public MonitorCallback + { + MonitorCallbackImpl(const wxString& jobname, + const Zstring& cmdLine) : trayIcon(jobname), cmdLine_(cmdLine) {} + + virtual void setPhase(WatchPhase mode) + { + switch (mode) + { + case MONITOR_PHASE_ACTIVE: + trayIcon.setMode(TRAY_MODE_ACTIVE); + break; + case MONITOR_PHASE_WAITING: + trayIcon.setMode(TRAY_MODE_WAITING); + break; + } + } + + virtual void executeExternalCommand() + { + auto cmdLineExp = expandMacros(cmdLine_); + try + { + shellExecute2(cmdLineExp, EXEC_TYPE_SYNC); //throw FileError + } + catch (const FileError& e) + { + warn_static("fix dialog hiding on OSX !!") + showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); + } + } + + virtual void requestUiRefresh() + { + if (updateUiIsAllowed()) + trayIcon.doUiRefreshNow(); //throw AbortMonitoring + } + + virtual void reportError(const std::wstring& msg) + { + trayIcon.setMode(TRAY_MODE_ERROR); + trayIcon.clearShowErrorRequested(); + + //wait for some time, then return to retry + assert_static(15 * 1000 % UI_UPDATE_INTERVAL == 0); + for (int i = 0; i < 15 * 1000 / UI_UPDATE_INTERVAL; ++i) + { + trayIcon.doUiRefreshNow(); //throw AbortMonitoring + + if (trayIcon.getShowErrorRequested()) + switch (showConfirmationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg(). + setDetailInstructions(msg), _("&Retry"))) + { + case ConfirmationButton::DO_IT: //retry + return; + case ConfirmationButton::CANCEL: + throw AbortMonitoring(SHOW_GUI); + } + boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL)); + } + } + + TrayIconHolder trayIcon; + const Zstring cmdLine_; + } cb(jobname, cmdLine); + + try + { + monitorDirectories(dirNamesNonFmt, config.delay, cb); //cb: throw AbortMonitoring + assert(false); + return SHOW_GUI; + } + catch (const AbortMonitoring& ab) + { + return ab.reasonCode_; + } +} diff --git a/FreeFileSync/Source/RealtimeSync/tray_menu.h b/FreeFileSync/Source/RealtimeSync/tray_menu.h new file mode 100644 index 00000000..1f71a017 --- /dev/null +++ b/FreeFileSync/Source/RealtimeSync/tray_menu.h @@ -0,0 +1,23 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef TRAY_583967857420987534253245 +#define TRAY_583967857420987534253245 + +#include +#include "xml_proc.h" + +namespace rts +{ +enum AbortReason +{ + SHOW_GUI, + EXIT_APP +}; +AbortReason startDirectoryMonitor(const xmlAccess::XmlRealConfig& config, const wxString& jobname); //jobname may be empty +} + +#endif //TRAY_583967857420987534253245 diff --git a/FreeFileSync/Source/RealtimeSync/xml_ffs.cpp b/FreeFileSync/Source/RealtimeSync/xml_ffs.cpp new file mode 100644 index 00000000..224337ad --- /dev/null +++ b/FreeFileSync/Source/RealtimeSync/xml_ffs.cpp @@ -0,0 +1,77 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "xml_ffs.h" +#include "../lib/ffs_paths.h" +#include +//#include + +//include FreeFileSync xml headers +#include "../lib/process_xml.h" + +using namespace zen; + + +xmlAccess::XmlRealConfig convertBatchToReal(const xmlAccess::XmlBatchConfig& batchCfg, const Zstring& filename) +{ + std::set uniqueFolders; + + //add main folders + uniqueFolders.insert(batchCfg.mainCfg.firstPair.dirnamePhraseLeft); + uniqueFolders.insert(batchCfg.mainCfg.firstPair.dirnamePhraseRight); + + //additional folders + for (const FolderPairEnh& fp : batchCfg.mainCfg.additionalPairs) + { + uniqueFolders.insert(fp.dirnamePhraseLeft); + uniqueFolders.insert(fp.dirnamePhraseRight); + } + + uniqueFolders.erase(Zstring()); + + xmlAccess::XmlRealConfig output; + output.directories.assign(uniqueFolders.begin(), uniqueFolders.end()); + output.commandline = Zstr("\"") + zen::getFreeFileSyncLauncher() + Zstr("\" \"") + filename + Zstr("\""); + return output; +} + + +void rts::readRealOrBatchConfig(const Zstring& filename, xmlAccess::XmlRealConfig& config) //throw xmlAccess::FfsXmlError; +{ + using namespace xmlAccess; + + if (getXmlType(filename) != XML_TYPE_BATCH) //throw FfsXmlError + return readRealConfig(filename, config); //throw FfsXmlError + + //convert batch config to RealtimeSync config + XmlBatchConfig batchCfg; + try + { + readConfig(filename, batchCfg); //throw FfsXmlError; + } + catch (const FfsXmlError& e) + { + if (e.getSeverity() == FfsXmlError::WARNING) + config = convertBatchToReal(batchCfg, filename); //do work despite parsing errors, then re-throw + + throw; // + } + config = convertBatchToReal(batchCfg, filename); +} + + +int rts::getProgramLanguage() +{ + xmlAccess::XmlGlobalSettings settings; + + try + { + xmlAccess::readConfig(settings); + } + catch (const xmlAccess::FfsXmlError&) {} //user default language if error occurred + + return settings.programLanguage; +} diff --git a/FreeFileSync/Source/RealtimeSync/xml_ffs.h b/FreeFileSync/Source/RealtimeSync/xml_ffs.h new file mode 100644 index 00000000..90c1c6ca --- /dev/null +++ b/FreeFileSync/Source/RealtimeSync/xml_ffs.h @@ -0,0 +1,23 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef XMLFREEFILESYNC_H_INCLUDED +#define XMLFREEFILESYNC_H_INCLUDED + +#include "xml_proc.h" +#include + + +//reuse (some of) FreeFileSync's xml files + +namespace rts +{ +void readRealOrBatchConfig(const Zstring& filename, xmlAccess::XmlRealConfig& config); //throw FfsXmlError + +int getProgramLanguage(); +} + +#endif // XMLFREEFILESYNC_H_INCLUDED diff --git a/FreeFileSync/Source/RealtimeSync/xml_proc.cpp b/FreeFileSync/Source/RealtimeSync/xml_proc.cpp new file mode 100644 index 00000000..db235ecb --- /dev/null +++ b/FreeFileSync/Source/RealtimeSync/xml_proc.cpp @@ -0,0 +1,75 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "xml_proc.h" +#include +#include +#include + +using namespace zen; +using namespace xmlAccess; + + +namespace +{ +void readConfig(const XmlIn& in, XmlRealConfig& config) +{ + in["Directories"](config.directories); + in["Delay" ](config.delay); + in["Commandline"](config.commandline); +} + + +bool isXmlTypeRTS(const XmlDoc& doc) //throw() +{ + if (doc.root().getNameAs() == "FreeFileSync") + { + std::string type; + if (doc.root().getAttribute("XmlType", type)) + return type == "REAL"; + } + return false; +} +} + + +void xmlAccess::readRealConfig(const Zstring& filename, XmlRealConfig& config) +{ + XmlDoc doc = loadXmlDocument(filename); //throw FfsXmlError + + if (!isXmlTypeRTS(doc)) + throw FfsXmlError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename))); + + XmlIn in(doc); + ::readConfig(in, config); + + if (in.errorsOccured()) + throw FfsXmlError(replaceCpy(_("Configuration file %x loaded partially only."), L"%x", fmtFileName(filename)) + L"\n\n" + + getErrorMessageFormatted(in.getErrorsAs()), FfsXmlError::WARNING); +} + + +namespace +{ +void writeConfig(const XmlRealConfig& config, XmlOut& out) +{ + out["Directories"](config.directories); + out["Delay" ](config.delay); + out["Commandline"](config.commandline); +} +} + + +void xmlAccess::writeRealConfig(const XmlRealConfig& config, const Zstring& filename) +{ + XmlDoc doc("FreeFileSync"); + doc.root().setAttribute("XmlType", "REAL"); + + XmlOut out(doc); + writeConfig(config, out); + + saveXmlDocument(doc, filename); //throw FfsXmlError +} diff --git a/FreeFileSync/Source/RealtimeSync/xml_proc.h b/FreeFileSync/Source/RealtimeSync/xml_proc.h new file mode 100644 index 00000000..671a237f --- /dev/null +++ b/FreeFileSync/Source/RealtimeSync/xml_proc.h @@ -0,0 +1,30 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef XMLPROCESSING_H_INCLUDED +#define XMLPROCESSING_H_INCLUDED + +#include +//#include +#include +#include "../lib/xml_base.h" + + +namespace xmlAccess +{ +struct XmlRealConfig +{ + XmlRealConfig() : delay(10) {} + std::vector directories; + Zstring commandline; + unsigned int delay; +}; + +void readRealConfig(const Zstring& filename, XmlRealConfig& config); //throw FfsXmlError +void writeRealConfig(const XmlRealConfig& config, const Zstring& filename); //throw FfsXmlError +} + +#endif // XMLPROCESSING_H_INCLUDED diff --git a/FreeFileSync/Source/algorithm.cpp b/FreeFileSync/Source/algorithm.cpp new file mode 100644 index 00000000..04d56490 --- /dev/null +++ b/FreeFileSync/Source/algorithm.cpp @@ -0,0 +1,1427 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "algorithm.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "lib/norm_filter.h" +#include "lib/db_file.h" +#include "lib/cmp_filetime.h" +#include "lib/norm_filter.h" +#include "process_callback.h" //for UI_UPDATE_INTERVAL + +using namespace zen; +using namespace std::rel_ops; + + +void zen::swapGrids(const MainConfiguration& config, FolderComparison& folderCmp) +{ + std::for_each(begin(folderCmp), end(folderCmp), [](BaseDirPair& baseObj) { baseObj.flip(); }); + redetermineSyncDirection(config, folderCmp, [](const std::wstring&) {}); +} + +//---------------------------------------------------------------------------------------------- + +namespace +{ +class Redetermine +{ +public: + static void execute(const DirectionSet& dirCfgIn, HierarchyObject& hierObj) { Redetermine(dirCfgIn).recurse(hierObj); } + +private: + Redetermine(const DirectionSet& dirCfgIn) : dirCfg(dirCfgIn) {} + + void recurse(HierarchyObject& hierObj) const + { + for (FilePair& fileObj : hierObj.refSubFiles()) + processFile(fileObj); + for (SymlinkPair& linkObj : hierObj.refSubLinks()) + processLink(linkObj); + for (DirPair& dirObj : hierObj.refSubDirs()) + processDir(dirObj); + } + + void processFile(FilePair& fileObj) const + { + const CompareFilesResult cat = fileObj.getCategory(); + + //##################### schedule old temporary files for deletion #################### + if (cat == FILE_LEFT_SIDE_ONLY && endsWith(fileObj.getShortName(), TEMP_FILE_ENDING)) + return fileObj.setSyncDir(SyncDirection::LEFT); + else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(fileObj.getShortName(), TEMP_FILE_ENDING)) + return fileObj.setSyncDir(SyncDirection::RIGHT); + //#################################################################################### + + switch (cat) + { + case FILE_LEFT_SIDE_ONLY: + fileObj.setSyncDir(dirCfg.exLeftSideOnly); + break; + case FILE_RIGHT_SIDE_ONLY: + fileObj.setSyncDir(dirCfg.exRightSideOnly); + break; + case FILE_RIGHT_NEWER: + fileObj.setSyncDir(dirCfg.rightNewer); + break; + case FILE_LEFT_NEWER: + fileObj.setSyncDir(dirCfg.leftNewer); + break; + case FILE_DIFFERENT: + fileObj.setSyncDir(dirCfg.different); + break; + case FILE_CONFLICT: + case FILE_DIFFERENT_METADATA: //use setting from "conflict/cannot categorize" + if (dirCfg.conflict == SyncDirection::NONE) + fileObj.setSyncDirConflict(fileObj.getCatExtraDescription()); //take over category conflict + else + fileObj.setSyncDir(dirCfg.conflict); + break; + case FILE_EQUAL: + fileObj.setSyncDir(SyncDirection::NONE); + break; + } + } + + void processLink(SymlinkPair& linkObj) const + { + switch (linkObj.getLinkCategory()) + { + case SYMLINK_LEFT_SIDE_ONLY: + linkObj.setSyncDir(dirCfg.exLeftSideOnly); + break; + case SYMLINK_RIGHT_SIDE_ONLY: + linkObj.setSyncDir(dirCfg.exRightSideOnly); + break; + case SYMLINK_LEFT_NEWER: + linkObj.setSyncDir(dirCfg.leftNewer); + break; + case SYMLINK_RIGHT_NEWER: + linkObj.setSyncDir(dirCfg.rightNewer); + break; + case SYMLINK_CONFLICT: + case SYMLINK_DIFFERENT_METADATA: //use setting from "conflict/cannot categorize" + if (dirCfg.conflict == SyncDirection::NONE) + linkObj.setSyncDirConflict(linkObj.getCatExtraDescription()); //take over category conflict + else + linkObj.setSyncDir(dirCfg.conflict); + break; + case SYMLINK_DIFFERENT: + linkObj.setSyncDir(dirCfg.different); + break; + case SYMLINK_EQUAL: + linkObj.setSyncDir(SyncDirection::NONE); + break; + } + } + + void processDir(DirPair& dirObj) const + { + const CompareDirResult cat = dirObj.getDirCategory(); + + //########### schedule abandoned temporary recycle bin directory for deletion ########## + if (cat == DIR_LEFT_SIDE_ONLY && endsWith(dirObj.getShortName(), TEMP_FILE_ENDING)) + return setSyncDirectionRec(SyncDirection::LEFT, dirObj); // + else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(dirObj.getShortName(), TEMP_FILE_ENDING)) + return setSyncDirectionRec(SyncDirection::RIGHT, dirObj); //don't recurse below! + //####################################################################################### + + switch (cat) + { + case DIR_LEFT_SIDE_ONLY: + dirObj.setSyncDir(dirCfg.exLeftSideOnly); + break; + case DIR_RIGHT_SIDE_ONLY: + dirObj.setSyncDir(dirCfg.exRightSideOnly); + break; + case DIR_EQUAL: + dirObj.setSyncDir(SyncDirection::NONE); + break; + case DIR_DIFFERENT_METADATA: //use setting from "conflict/cannot categorize" + if (dirCfg.conflict == SyncDirection::NONE) + dirObj.setSyncDirConflict(dirObj.getCatExtraDescription()); //take over category conflict + else + dirObj.setSyncDir(dirCfg.conflict); + break; + } + + recurse(dirObj); + } + + const DirectionSet dirCfg; +}; + +//--------------------------------------------------------------------------------------------------------------- + +//test if non-equal items exist in scanned data +bool allItemsCategoryEqual(const HierarchyObject& hierObj) +{ + return std::all_of(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), + [](const FilePair& fileObj) { return fileObj.getCategory() == FILE_EQUAL; })&& //files + + std::all_of(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(), + [](const SymlinkPair& linkObj) { return linkObj.getLinkCategory() == SYMLINK_EQUAL; })&& //symlinks + + std::all_of(hierObj.refSubDirs(). begin(), hierObj.refSubDirs().end(), + [](const DirPair& dirObj) + { + return dirObj.getDirCategory() == DIR_EQUAL && allItemsCategoryEqual(dirObj); //short circuit-behavior! + }); //directories +} +} + +bool zen::allElementsEqual(const FolderComparison& folderCmp) +{ + return std::all_of(begin(folderCmp), end(folderCmp), [](const BaseDirPair& baseObj) { return allItemsCategoryEqual(baseObj); }); +} + +//--------------------------------------------------------------------------------------------------------------- + +namespace +{ +template inline +const InSyncDescrFile& getDescriptor(const InSyncFile& dbFile) { return dbFile.left; } + +template <> inline +const InSyncDescrFile& getDescriptor(const InSyncFile& dbFile) { return dbFile.right; } + + +//check whether database entry and current item match: *irrespective* of current comparison settings +template inline +bool isEqual(const FilePair& fileObj, const InSyncDir::FileList::value_type* dbFile) +{ + if (fileObj.isEmpty()) + return !dbFile; + else if (!dbFile) + return false; + + const Zstring& shortNameDb = dbFile->first; + const InSyncDescrFile& descrDb = getDescriptor(dbFile->second); + + return fileObj.getShortName() == shortNameDb && //detect changes in case (windows) + //respect 2 second FAT/FAT32 precision! copying a file to a FAT32 drive changes it's modification date by up to 2 seconds + sameFileTime(fileObj.getLastWriteTime(), descrDb.lastWriteTimeRaw, 2) && + fileObj.getFileSize() == dbFile->second.fileSize; + //note: we do *not* consider FileId here, but are only interested in *visual* changes. Consider user moving data to some other medium, this is not a change! +} + + +//check whether database entry is in sync considering *current* comparison settings +inline +bool stillInSync(const InSyncFile& dbFile, CompareVariant compareVar, size_t fileTimeTolerance) +{ + switch (compareVar) + { + case CMP_BY_TIME_SIZE: + if (dbFile.cmpVar == CMP_BY_CONTENT) return true; //special rule: this is already "good enough" for CMP_BY_TIME_SIZE! + + return //case-sensitive short name match is a database invariant! + CmpFileTime::getResult(dbFile.left.lastWriteTimeRaw, dbFile.right.lastWriteTimeRaw, fileTimeTolerance) == CmpFileTime::TIME_EQUAL; + //dbFile.left.fileSize == dbFile.right.fileSize; + + case CMP_BY_CONTENT: + //case-sensitive short name match is a database invariant! + return dbFile.cmpVar == CMP_BY_CONTENT; + //in contrast to comparison, we don't care about modification time here! + } + assert(false); + return false; +} + +//-------------------------------------------------------------------- + +template inline +const InSyncDescrLink& getDescriptor(const InSyncSymlink& dbLink) { return dbLink.left; } + +template <> inline +const InSyncDescrLink& getDescriptor(const InSyncSymlink& dbLink) { return dbLink.right; } + + +//check whether database entry and current item match: *irrespective* of current comparison settings +template inline +bool isEqual(const SymlinkPair& linkObj, const InSyncDir::LinkList::value_type* dbLink) +{ + if (linkObj.isEmpty()) + return !dbLink; + else if (!dbLink) + return false; + + const Zstring& shortNameDb = dbLink->first; + const InSyncDescrLink& descrDb = getDescriptor(dbLink->second); + + return linkObj.getShortName() == shortNameDb && + //respect 2 second FAT/FAT32 precision! copying a file to a FAT32 drive changes its modification date by up to 2 seconds + sameFileTime(linkObj.getLastWriteTime(), descrDb.lastWriteTimeRaw, 2); +} + + +//check whether database entry is in sync considering *current* comparison settings +inline +bool stillInSync(const InSyncSymlink& dbLink, CompareVariant compareVar, size_t fileTimeTolerance) +{ + switch (compareVar) + { + case CMP_BY_TIME_SIZE: + if (dbLink.cmpVar == CMP_BY_CONTENT) return true; //special rule: this is already "good enough" for CMP_BY_TIME_SIZE! + + return //case-sensitive short name match is a database invariant! + CmpFileTime::getResult(dbLink.left.lastWriteTimeRaw, dbLink.right.lastWriteTimeRaw, fileTimeTolerance) == CmpFileTime::TIME_EQUAL; + + case CMP_BY_CONTENT: + //case-sensitive short name match is a database invariant! + return dbLink.cmpVar == CMP_BY_CONTENT; + //in contrast to comparison, we don't care about modification time here! + } + assert(false); + return false; +} + +//-------------------------------------------------------------------- + +//check whether database entry and current item match: *irrespective* of current comparison settings +template inline +bool isEqual(const DirPair& dirObj, const InSyncDir::DirList::value_type* dbDir) +{ + if (dirObj.isEmpty()) + return !dbDir || dbDir->second.status == InSyncDir::DIR_STATUS_STRAW_MAN; + else if (!dbDir || dbDir->second.status == InSyncDir::DIR_STATUS_STRAW_MAN) + return false; + + const Zstring& shortNameDb = dbDir->first; + + return dirObj.getShortName() == shortNameDb; +} + + +inline +bool stillInSync(const InSyncDir& dbDir) +{ + //case-sensitive short name match is a database invariant! + //InSyncDir::DIR_STATUS_STRAW_MAN considered + return true; +} + +//---------------------------------------------------------------------------------------------- + +class DetectMovedFiles +{ +public: + static void execute(BaseDirPair& baseDirectory, const InSyncDir& dbContainer) { DetectMovedFiles(baseDirectory, dbContainer); } + +private: + DetectMovedFiles(BaseDirPair& baseDirectory, const InSyncDir& dbContainer) : + cmpVar(baseDirectory.getCompVariant()), + fileTimeTolerance(baseDirectory.getFileTimeTolerance()) + { + recurse(baseDirectory); + + if (!exLeftOnly.empty() && !exRightOnly.empty()) + detectFilePairs(dbContainer); + } + + void recurse(HierarchyObject& hierObj) + { + for (FilePair& fileObj : hierObj.refSubFiles()) + { + const CompareFilesResult cat = fileObj.getCategory(); + + if (cat == FILE_LEFT_SIDE_ONLY) + { + if (fileObj.getFileId() != FileId()) + { + auto rv = exLeftOnly.insert(std::make_pair(fileObj.getFileId(), &fileObj)); + assert(rv.second); + if (!rv.second) //duplicate file ID! + rv.first->second = nullptr; + } + } + else if (cat == FILE_RIGHT_SIDE_ONLY) + { + if (fileObj.getFileId() != FileId()) + { + auto rv = exRightOnly.insert(std::make_pair(fileObj.getFileId(), &fileObj)); + assert(rv.second); + if (!rv.second) //duplicate file ID! + rv.first->second = nullptr; + } + } + } + for (DirPair& dirObj : hierObj.refSubDirs()) + recurse(dirObj); + } + + void detectFilePairs(const InSyncDir& container) const + { + for (auto& dbFile : container.files) + findAndSetMovePair(dbFile.second); + + for (auto& dbDir : container.dirs) + detectFilePairs(dbDir.second); + } + + static bool sameSizeAndDateLeft(const FilePair& fsObj, const InSyncFile& dbEntry) + { + return fsObj.getFileSize() == dbEntry.fileSize && + sameFileTime(fsObj.getLastWriteTime(), dbEntry.left.lastWriteTimeRaw, 2); //respect 2 second FAT/FAT32 precision! + //PS: *never* allow 2 sec tolerance as container predicate!! + // => no strict weak ordering relation! reason: no transitivity of equivalence! + } + static bool sameSizeAndDateRight(const FilePair& fsObj, const InSyncFile& dbEntry) + { + return fsObj.getFileSize() == dbEntry.fileSize && + sameFileTime(fsObj.getLastWriteTime(), dbEntry.right.lastWriteTimeRaw, 2); + } + + void findAndSetMovePair(const InSyncFile& dbEntry) const + { + const FileId idLeft = dbEntry.left .fileId; + const FileId idRight = dbEntry.right.fileId; + + if (idLeft != FileId() && + idRight != FileId() && + stillInSync(dbEntry, cmpVar, fileTimeTolerance)) + { + auto itL = exLeftOnly.find(idLeft); + if (itL != exLeftOnly.end()) + if (FilePair* fileLeftOnly = itL->second) //= nullptr, if duplicate ID! + if (sameSizeAndDateLeft(*fileLeftOnly, dbEntry)) + { + auto itR = exRightOnly.find(idRight); + if (itR != exRightOnly.end()) + if (FilePair* fileRightOnly = itR->second) //= nullptr, if duplicate ID! + if (sameSizeAndDateRight(*fileRightOnly, dbEntry)) + if (fileLeftOnly ->getMoveRef() == nullptr && //the db may contain duplicate file ids on left or right side: e.g. consider aliasing through symlinks + fileRightOnly->getMoveRef() == nullptr) //=> should not be a problem (same id, size, date => alias!) but don't let a row participate in two move pairs! + { + fileLeftOnly ->setMoveRef(fileRightOnly->getId()); //found a pair, mark it! + fileRightOnly->setMoveRef(fileLeftOnly ->getId()); // + } + } + } + } + + const CompareVariant cmpVar; + const size_t fileTimeTolerance; + + std::map exLeftOnly; //FilePair* == nullptr for duplicate ids! => consider aliasing through symlinks! + std::map exRightOnly; //=> avoid ambiguity for mixtures of files/symlinks on one side and allow 1-1 mapping only! + + /* + detect renamed files: + + X -> |_| Create right + |_| -> Y Delete right + + is detected as: + + Rename Y to X on right + + Algorithm: + ---------- + DB-file left <--- (name, size, date) ---> DB-file right + | | + | (file ID, size, date) | (file ID, size, date) + \|/ \|/ + file left only file right only + + FAT caveat: File Ids are generally not stable when file is either moved or renamed! + => 1. Move/rename operations on FAT cannot be detected reliably. + => 2. database generally contains wrong file ID on FAT after renaming from .ffs_tmp files => correct file Ids in database only after next sync + => 3. even exFAT screws up (but less than FAT) and changes IDs after file move. Did they learn nothing from the past? + + Possible refinement + ------------------- + If the file ID is wrong (FAT) or not available, we could at least allow direct association by name, instead of breaking the chain completely: support NTFS -> FAT + */ +}; + +//---------------------------------------------------------------------------------------------- + +class RedetermineTwoWay +{ +public: + static void execute(BaseDirPair& baseDirectory, const InSyncDir& dbContainer) { RedetermineTwoWay(baseDirectory, dbContainer); } + +private: + RedetermineTwoWay(BaseDirPair& baseDirectory, const InSyncDir& dbContainer) : + txtBothSidesChanged(_("Both sides have changed since last synchronization.")), + txtNoSideChanged(_("Cannot determine sync-direction:") + L" \n" + _("No change since last synchronization.")), + txtDbNotInSync(_("Cannot determine sync-direction:") + L" \n" + _("The database entry is not in sync considering current settings.")), + cmpVar(baseDirectory.getCompVariant()), + fileTimeTolerance(baseDirectory.getFileTimeTolerance()) + { + //-> considering filter not relevant: + //if narrowing filter: all ok; if widening filter (if file ex on both sides -> conflict, fine; if file ex. on one side: copy to other side: fine) + + recurse(baseDirectory, &dbContainer); + } + + void recurse(HierarchyObject& hierObj, const InSyncDir* dbContainer) const + { + for (FilePair& fileObj : hierObj.refSubFiles()) + processFile(fileObj, dbContainer); + for (SymlinkPair& linkObj : hierObj.refSubLinks()) + processSymlink(linkObj, dbContainer); + for (DirPair& dirObj : hierObj.refSubDirs()) + processDir(dirObj, dbContainer); + } + + void processFile(FilePair& fileObj, const InSyncDir* dbContainer) const + { + const CompareFilesResult cat = fileObj.getCategory(); + if (cat == FILE_EQUAL) + return; + + //##################### schedule old temporary files for deletion #################### + if (cat == FILE_LEFT_SIDE_ONLY && endsWith(fileObj.getShortName(), TEMP_FILE_ENDING)) + return fileObj.setSyncDir(SyncDirection::LEFT); + else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(fileObj.getShortName(), TEMP_FILE_ENDING)) + return fileObj.setSyncDir(SyncDirection::RIGHT); + //#################################################################################### + + //try to find corresponding database entry + const InSyncDir::FileList::value_type* dbEntry = nullptr; + if (dbContainer) + { + auto it = dbContainer->files.find(fileObj.getObjShortName()); + if (it != dbContainer->files.end()) + dbEntry = &*it; + } + + //evaluation + const bool changeOnLeft = !isEqual(fileObj, dbEntry); + const bool changeOnRight = !isEqual(fileObj, dbEntry); + + if (changeOnLeft != changeOnRight) + { + //if database entry not in sync according to current settings! -> do not set direction based on async status! + if (dbEntry && !stillInSync(dbEntry->second, cmpVar, fileTimeTolerance)) + fileObj.setSyncDirConflict(txtDbNotInSync); + else + fileObj.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT); + } + else + { + if (changeOnLeft) + fileObj.setSyncDirConflict(txtBothSidesChanged); + else + fileObj.setSyncDirConflict(txtNoSideChanged); + } + } + + void processSymlink(SymlinkPair& linkObj, const InSyncDir* dbContainer) const + { + const CompareSymlinkResult cat = linkObj.getLinkCategory(); + if (cat == SYMLINK_EQUAL) + return; + + //try to find corresponding database entry + const InSyncDir::LinkList::value_type* dbEntry = nullptr; + if (dbContainer) + { + auto it = dbContainer->symlinks.find(linkObj.getObjShortName()); + if (it != dbContainer->symlinks.end()) + dbEntry = &*it; + } + + //evaluation + const bool changeOnLeft = !isEqual(linkObj, dbEntry); + const bool changeOnRight = !isEqual(linkObj, dbEntry); + + if (changeOnLeft != changeOnRight) + { + //if database entry not in sync according to current settings! -> do not set direction based on async status! + if (dbEntry && !stillInSync(dbEntry->second, cmpVar, fileTimeTolerance)) + linkObj.setSyncDirConflict(txtDbNotInSync); + else + linkObj.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT); + } + else + { + if (changeOnLeft) + linkObj.setSyncDirConflict(txtBothSidesChanged); + else + linkObj.setSyncDirConflict(txtNoSideChanged); + } + } + + void processDir(DirPair& dirObj, const InSyncDir* dbContainer) const + { + const CompareDirResult cat = dirObj.getDirCategory(); + + //########### schedule abandoned temporary recycle bin directory for deletion ########## + if (cat == DIR_LEFT_SIDE_ONLY && endsWith(dirObj.getShortName(), TEMP_FILE_ENDING)) + return setSyncDirectionRec(SyncDirection::LEFT, dirObj); // + else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(dirObj.getShortName(), TEMP_FILE_ENDING)) + return setSyncDirectionRec(SyncDirection::RIGHT, dirObj); //don't recurse below! + //####################################################################################### + + //try to find corresponding database entry + const InSyncDir::DirList::value_type* dbEntry = nullptr; + if (dbContainer) + { + auto it = dbContainer->dirs.find(dirObj.getObjShortName()); + if (it != dbContainer->dirs.end()) + dbEntry = &*it; + } + + if (cat != DIR_EQUAL) + { + //evaluation + const bool changeOnLeft = !isEqual(dirObj, dbEntry); + const bool changeOnRight = !isEqual(dirObj, dbEntry); + + if (changeOnLeft != changeOnRight) + { + //if database entry not in sync according to current settings! -> do not set direction based on async status! + if (dbEntry && !stillInSync(dbEntry->second)) + dirObj.setSyncDirConflict(txtDbNotInSync); + else + dirObj.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT); + } + else + { + if (changeOnLeft) + dirObj.setSyncDirConflict(txtBothSidesChanged); + else + dirObj.setSyncDirConflict(txtNoSideChanged); + } + } + + recurse(dirObj, dbEntry ? &dbEntry->second : nullptr); + } + + const std::wstring txtBothSidesChanged; + const std::wstring txtNoSideChanged; + const std::wstring txtDbNotInSync; + + const CompareVariant cmpVar; + const size_t fileTimeTolerance; +}; +} + +//--------------------------------------------------------------------------------------------------------------- + +std::vector zen::extractDirectionCfg(const MainConfiguration& mainCfg) +{ + //merge first and additional pairs + std::vector allPairs; + allPairs.push_back(mainCfg.firstPair); + allPairs.insert(allPairs.end(), + mainCfg.additionalPairs.begin(), //add additional pairs + mainCfg.additionalPairs.end()); + + std::vector output; + std::for_each(allPairs.begin(), allPairs.end(), + [&](const FolderPairEnh& fp) + { + output.push_back(fp.altSyncConfig.get() ? fp.altSyncConfig->directionCfg : mainCfg.syncCfg.directionCfg); + }); + + return output; +} + + +void zen::redetermineSyncDirection(const DirectionConfig& dirCfg, BaseDirPair& baseDirectory, std::function reportWarning) +{ + //try to load sync-database files + std::shared_ptr lastSyncState; + if (dirCfg.var == DirectionConfig::TWOWAY || detectMovedFilesEnabled(dirCfg)) + try + { + if (allItemsCategoryEqual(baseDirectory)) + return; //nothing to do: abort and don't even try to open db files + + lastSyncState = loadLastSynchronousState(baseDirectory); //throw FileError, FileErrorDatabaseNotExisting + } + catch (FileErrorDatabaseNotExisting&) {} //let's ignore this error, there's no value in reporting it other than confuse users + catch (FileError& error) //e.g. incompatible database version + { + reportWarning(error.toString() + + (dirCfg.var == DirectionConfig::TWOWAY ? + L" \n\n" + _("Setting default synchronization directions: Old files will be overwritten with newer files.") : std::wstring())); + } + + //set sync directions + if (dirCfg.var == DirectionConfig::TWOWAY) + { + if (lastSyncState) + RedetermineTwoWay::execute(baseDirectory, *lastSyncState); + else //default fallback + Redetermine::execute(getTwoWayUpdateSet(), baseDirectory); + } + else + Redetermine::execute(extractDirections(dirCfg), baseDirectory); + + //detect renamed files + if (lastSyncState) + DetectMovedFiles::execute(baseDirectory, *lastSyncState); +} + + +void zen::redetermineSyncDirection(const MainConfiguration& mainCfg, FolderComparison& folderCmp, std::function reportWarning) +{ + if (folderCmp.empty()) + return; + + std::vector directCfgs = extractDirectionCfg(mainCfg); + + if (folderCmp.size() != directCfgs.size()) + throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo(__LINE__)); + + for (auto it = folderCmp.begin(); it != folderCmp.end(); ++it) + { + const DirectionConfig& cfg = directCfgs[it - folderCmp.begin()]; + redetermineSyncDirection(cfg, **it, reportWarning); + } +} + +//--------------------------------------------------------------------------------------------------------------- + +struct SetNewDirection +{ + static void execute(FilePair& fileObj, SyncDirection newDirection) + { + if (fileObj.getCategory() != FILE_EQUAL) + fileObj.setSyncDir(newDirection); + } + + static void execute(SymlinkPair& linkObj, SyncDirection newDirection) + { + if (linkObj.getLinkCategory() != SYMLINK_EQUAL) + linkObj.setSyncDir(newDirection); + } + + static void execute(DirPair& dirObj, SyncDirection newDirection) + { + if (dirObj.getDirCategory() != DIR_EQUAL) + dirObj.setSyncDir(newDirection); + + //recurse: + for (FilePair& fileObj : dirObj.refSubFiles()) + execute(fileObj, newDirection); + for (SymlinkPair& linkObj : dirObj.refSubLinks()) + execute(linkObj, newDirection); + for (DirPair& dirObj2 : dirObj.refSubDirs()) + execute(dirObj2, newDirection); + } +}; + + +void zen::setSyncDirectionRec(SyncDirection newDirection, FileSystemObject& fsObj) +{ + //process subdirectories also! + struct Recurse: public FSObjectVisitor + { + Recurse(SyncDirection newDir) : newDir_(newDir) {} + virtual void visit(const FilePair& fileObj) + { + SetNewDirection::execute(const_cast(fileObj), newDir_); //phyiscal object is not const in this method anyway + } + virtual void visit(const SymlinkPair& linkObj) + { + SetNewDirection::execute(const_cast(linkObj), newDir_); // + } + virtual void visit(const DirPair& dirObj) + { + SetNewDirection::execute(const_cast(dirObj), newDir_); // + } + private: + SyncDirection newDir_; + } setDirVisitor(newDirection); + fsObj.accept(setDirVisitor); +} + +//--------------- functions related to filtering ------------------------------------------------------------------------------------ + +namespace +{ +template +void inOrExcludeAllRows(zen::HierarchyObject& hierObj) +{ + for (FilePair& fileObj : hierObj.refSubFiles()) + fileObj.setActive(include); + for (SymlinkPair& linkObj : hierObj.refSubLinks()) + linkObj.setActive(include); + for (DirPair& dirObj : hierObj.refSubDirs()) + { + dirObj.setActive(include); + inOrExcludeAllRows(dirObj); //recurse + } +} +} + + +void zen::setActiveStatus(bool newStatus, zen::FolderComparison& folderCmp) +{ + if (newStatus) + std::for_each(begin(folderCmp), end(folderCmp), [](BaseDirPair& baseDirObj) { inOrExcludeAllRows(baseDirObj); }); //include all rows + else + std::for_each(begin(folderCmp), end(folderCmp), [](BaseDirPair& baseDirObj) { inOrExcludeAllRows(baseDirObj); }); //exclude all rows +} + + +void zen::setActiveStatus(bool newStatus, zen::FileSystemObject& fsObj) +{ + fsObj.setActive(newStatus); + + //process subdirectories also! + struct Recurse: public FSObjectVisitor + { + Recurse(bool newStat) : newStatus_(newStat) {} + virtual void visit(const FilePair& fileObj) {} + virtual void visit(const SymlinkPair& linkObj) {} + virtual void visit(const DirPair& dirObj) + { + if (newStatus_) + inOrExcludeAllRows(const_cast(dirObj)); //object is not physically const here anyway + else + inOrExcludeAllRows(const_cast(dirObj)); // + } + private: + const bool newStatus_; + } recurse(newStatus); + fsObj.accept(recurse); +} + +namespace +{ +enum FilterStrategy +{ + STRATEGY_SET, + STRATEGY_AND, + STRATEGY_OR +}; + +template struct Eval; + +template <> +struct Eval //process all elements +{ + template + bool process(const T& obj) const { return true; } +}; + +template <> +struct Eval +{ + template + bool process(const T& obj) const { return obj.isActive(); } +}; + +template <> +struct Eval +{ + template + bool process(const T& obj) const { return !obj.isActive(); } +}; + + +template +class ApplyHardFilter +{ +public: + static void execute(HierarchyObject& hierObj, const HardFilter& filterProcIn) { ApplyHardFilter(hierObj, filterProcIn); } + +private: + ApplyHardFilter(HierarchyObject& hierObj, const HardFilter& filterProcIn) : filterProc(filterProcIn) { recurse(hierObj); } + + void recurse(HierarchyObject& hierObj) const + { + for (FilePair& fileObj : hierObj.refSubFiles()) + processFile(fileObj); + for (SymlinkPair& linkObj : hierObj.refSubLinks()) + processLink(linkObj); + for (DirPair& dirObj : hierObj.refSubDirs()) + processDir(dirObj); + }; + + void processFile(FilePair& fileObj) const + { + if (Eval().process(fileObj)) + fileObj.setActive(filterProc.passFileFilter(fileObj.getObjRelativeName())); + } + + void processLink(SymlinkPair& linkObj) const + { + if (Eval().process(linkObj)) + linkObj.setActive(filterProc.passFileFilter(linkObj.getObjRelativeName())); + } + + void processDir(DirPair& dirObj) const + { + bool subObjMightMatch = true; + const bool filterPassed = filterProc.passDirFilter(dirObj.getObjRelativeName(), &subObjMightMatch); + + if (Eval().process(dirObj)) + dirObj.setActive(filterPassed); + + if (!subObjMightMatch) //use same logic like directory traversing here: evaluate filter in subdirs only if objects could match + { + inOrExcludeAllRows(dirObj); //exclude all files dirs in subfolders + return; + } + + recurse(dirObj); + } + + const HardFilter& filterProc; +}; + +template <> +class ApplyHardFilter; //usage of InOrExcludeAllRows doesn't allow for strategy "or" + + +template +class ApplySoftFilter //falsify only! -> can run directly after "hard/base filter" +{ +public: + static void execute(HierarchyObject& hierObj, const SoftFilter& timeSizeFilter) { ApplySoftFilter(hierObj, timeSizeFilter); } + +private: + ApplySoftFilter(HierarchyObject& hierObj, const SoftFilter& timeSizeFilter) : timeSizeFilter_(timeSizeFilter) { recurse(hierObj); } + + void recurse(zen::HierarchyObject& hierObj) const + { + for (FilePair& fileObj : hierObj.refSubFiles()) + processFile(fileObj); + for (SymlinkPair& linkObj : hierObj.refSubLinks()) + processLink(linkObj); + for (DirPair& dirObj : hierObj.refSubDirs()) + processDir(dirObj); + }; + + void processFile(FilePair& fileObj) const + { + if (Eval().process(fileObj)) + { + if (fileObj.isEmpty()) + fileObj.setActive(matchSize(fileObj) && + matchTime(fileObj)); + else if (fileObj.isEmpty()) + fileObj.setActive(matchSize(fileObj) && + matchTime(fileObj)); + else + { + //the only case with partially unclear semantics: + //file and time filters may match or not match on each side, leaving a total of 16 combinations for both sides! + /* + ST S T - ST := match size and time + --------- S := match size only + ST |X|X|X|X| T := match time only + ------------ - := no match + S |X|O|?|O| + ------------ X := include row + T |X|?|O|O| O := exclude row + ------------ ? := unclear + - |X|O|O|O| + ------------ + */ + //let's set ? := O + fileObj.setActive((matchSize(fileObj) && + matchTime(fileObj)) || + (matchSize(fileObj) && + matchTime(fileObj))); + } + } + } + + void processLink(SymlinkPair& linkObj) const + { + if (Eval().process(linkObj)) + { + if (linkObj.isEmpty()) + linkObj.setActive(matchTime(linkObj)); + else if (linkObj.isEmpty()) + linkObj.setActive(matchTime(linkObj)); + else + linkObj.setActive(matchTime(linkObj) || + matchTime (linkObj)); + } + } + + void processDir(DirPair& dirObj) const + { + if (Eval().process(dirObj)) + dirObj.setActive(timeSizeFilter_.matchFolder()); //if date filter is active we deactivate all folders: effectively gets rid of empty folders! + + recurse(dirObj); + } + + template + bool matchTime(const T& obj) const + { + return timeSizeFilter_.matchTime(obj.template getLastWriteTime()); + } + + template + bool matchSize(const T& obj) const + { + return timeSizeFilter_.matchSize(obj.template getFileSize()); + } + + const SoftFilter timeSizeFilter_; +}; +} + + +void zen::addHardFiltering(BaseDirPair& baseDirObj, const Zstring& excludeFilter) +{ + ApplyHardFilter::execute(baseDirObj, NameFilter(FilterConfig().includeFilter, excludeFilter)); +} + + +void zen::addSoftFiltering(BaseDirPair& baseDirObj, const SoftFilter& timeSizeFilter) +{ + if (!timeSizeFilter.isNull()) //since we use STRATEGY_AND, we may skip a "null" filter + ApplySoftFilter::execute(baseDirObj, timeSizeFilter); +} + + +void zen::applyFiltering(FolderComparison& folderCmp, const MainConfiguration& mainCfg) +{ + if (folderCmp.empty()) + return; + else if (folderCmp.size() != mainCfg.additionalPairs.size() + 1) + throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo(__LINE__)); + + //merge first and additional pairs + std::vector allPairs; + allPairs.push_back(mainCfg.firstPair); + allPairs.insert(allPairs.end(), + mainCfg.additionalPairs.begin(), //add additional pairs + mainCfg.additionalPairs.end()); + + for (auto it = allPairs.begin(); it != allPairs.end(); ++it) + { + BaseDirPair& baseDirectory = *folderCmp[it - allPairs.begin()]; + + const NormalizedFilter normFilter = normalizeFilters(mainCfg.globalFilter, it->localFilter); + + //"set" hard filter + ApplyHardFilter::execute(baseDirectory, *normFilter.nameFilter); + + //"and" soft filter + addSoftFiltering(baseDirectory, normFilter.timeSizeFilter); + } +} + + +class FilterByTimeSpan +{ +public: + static void execute(HierarchyObject& hierObj, const Int64& timeFrom, const Int64& timeTo) { FilterByTimeSpan(hierObj, timeFrom, timeTo); } + +private: + FilterByTimeSpan(HierarchyObject& hierObj, + const Int64& timeFrom, + const Int64& timeTo) : + timeFrom_(timeFrom), + timeTo_(timeTo) { recurse(hierObj); } + + void recurse(HierarchyObject& hierObj) const + { + for (FilePair& fileObj : hierObj.refSubFiles()) + processFile(fileObj); + for (SymlinkPair& linkObj : hierObj.refSubLinks()) + processLink(linkObj); + for (DirPair& dirObj : hierObj.refSubDirs()) + processDir(dirObj); + }; + + void processFile(FilePair& fileObj) const + { + if (fileObj.isEmpty()) + fileObj.setActive(matchTime(fileObj)); + else if (fileObj.isEmpty()) + fileObj.setActive(matchTime(fileObj)); + else + fileObj.setActive(matchTime(fileObj) || + matchTime(fileObj)); + } + + void processLink(SymlinkPair& linkObj) const + { + if (linkObj.isEmpty()) + linkObj.setActive(matchTime(linkObj)); + else if (linkObj.isEmpty()) + linkObj.setActive(matchTime(linkObj)); + else + linkObj.setActive(matchTime(linkObj) || + matchTime (linkObj)); + } + + void processDir(DirPair& dirObj) const + { + dirObj.setActive(false); + recurse(dirObj); + } + + template + bool matchTime(const T& obj) const + { + return timeFrom_ <= obj.template getLastWriteTime() && + obj.template getLastWriteTime() <= timeTo_; + } + + const Int64 timeFrom_; + const Int64 timeTo_; +}; + + +void zen::applyTimeSpanFilter(FolderComparison& folderCmp, const Int64& timeFrom, const Int64& timeTo) +{ + std::for_each(begin(folderCmp), end(folderCmp), [&](BaseDirPair& baseDirObj) { FilterByTimeSpan::execute(baseDirObj, timeFrom, timeTo); }); +} + + +//############################################################################################################ +std::pair zen::deleteFromGridAndHDPreview(const std::vector& selectionLeft, + const std::vector& selectionRight, + bool deleteOnBothSides) +{ + //don't use wxString here, it's linear allocation strategy would bring perf down to a crawl; Zstring: exponential growth! + Zstring fileList; + int totalDelCount = 0; + + if (deleteOnBothSides) + { + //mix selected rows from left and right (without changing order) + std::vector selection; + { + hash_set objectsUsed; + std::copy_if(selectionLeft .begin(), selectionLeft .end(), std::back_inserter(selection), [&](FileSystemObject* fsObj) { return objectsUsed.insert(fsObj).second; }); + std::copy_if(selectionRight.begin(), selectionRight.end(), std::back_inserter(selection), [&](FileSystemObject* fsObj) { return objectsUsed.insert(fsObj).second; }); + } + + std::for_each(selection.begin(), selection.end(), + [&](const FileSystemObject* fsObj) + { + if (!fsObj->isEmpty()) + { + fileList += fsObj->getFullName() + Zstr('\n'); + ++totalDelCount; + } + + if (!fsObj->isEmpty()) + { + fileList += fsObj->getFullName() + Zstr('\n'); + ++totalDelCount; + } + + fileList += Zstr('\n'); + }); + } + else //delete selected files only + { + std::for_each(selectionLeft.begin(), selectionLeft.end(), + [&](const FileSystemObject* fsObj) + { + if (!fsObj->isEmpty()) + { + fileList += fsObj->getFullName() + Zstr('\n'); + ++totalDelCount; + } + }); + + std::for_each(selectionRight.begin(), selectionRight.end(), + [&](const FileSystemObject* fsObj) + { + if (!fsObj->isEmpty()) + { + fileList += fsObj->getFullName() + Zstr('\n'); + ++totalDelCount; + } + }); + } + + return std::make_pair(fileList, totalDelCount); +} + + +namespace +{ +template inline +bool tryReportingError(Function cmd, DeleteFilesHandler& handler) //return "true" on success, "false" if error was ignored +{ + for (;;) + try + { + cmd(); //throw FileError + return true; + } + catch (FileError& error) + { + switch (handler.reportError(error.toString())) //may throw! + { + case DeleteFilesHandler::IGNORE_ERROR: + return false; + case DeleteFilesHandler::RETRY: + break; //continue with loop + default: + assert(false); + break; + } + } +} + +#ifdef ZEN_WIN +//recycleBinStatus() blocks seriously if recycle bin is really full and drive is slow +StatusRecycler recycleBinStatusUpdating(const Zstring& dirname, DeleteFilesHandler& callback) +{ + const std::wstring msg = replaceCpy(_("Checking recycle bin availability for folder %x..."), L"%x", fmtFileName(dirname), false); + + auto ft = async([=] { return recycleBinStatus(dirname); }); + while (!ft.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL / 2))) + callback.reportStatus(msg); //may throw! + return ft.get(); +} +#endif + + +template +void categorize(const std::set& rowsIn, + std::vector& deletePermanent, + std::vector& deleteRecyler, + bool useRecycleBin, + std::map& hasRecyclerBuffer, + DeleteFilesHandler& callback) +{ + auto hasRecycler = [&](const FileSystemObject& fsObj) -> bool + { +#ifdef ZEN_WIN + const Zstring& baseDirPf = fsObj.root().getBaseDirPf(); + + auto it = hasRecyclerBuffer.find(baseDirPf); + if (it != hasRecyclerBuffer.end()) + return it->second; + return hasRecyclerBuffer.insert(std::make_pair(baseDirPf, recycleBinStatusUpdating(baseDirPf, callback) == STATUS_REC_EXISTS)).first->second; +#elif defined ZEN_LINUX || defined ZEN_MAC + return true; +#endif + }; + + for (auto it = rowsIn.begin(); it != rowsIn.end(); ++it) + if (!(*it)->isEmpty()) + { + if (useRecycleBin && hasRecycler(**it)) //Windows' ::SHFileOperation() will delete permanently anyway, but we have a superior deletion routine + deleteRecyler.push_back(*it); + else + deletePermanent.push_back(*it); + } +} + + +template +struct ItemDeleter : public FSObjectVisitor //throw FileError, but nothrow constructor!!! +{ + ItemDeleter(bool useRecycleBin, DeleteFilesHandler& handler) : + handler_(handler), useRecycleBin_(useRecycleBin), remCallback(*this) + { + if (useRecycleBin_) + { + txtRemovingFile = _("Moving file %x to the recycle bin" ); + txtRemovingDirectory = _("Moving folder %x to the recycle bin" ); + txtRemovingSymlink = _("Moving symbolic link %x to the recycle bin"); + } + else + { + txtRemovingFile = _("Deleting file %x" ); + txtRemovingDirectory = _("Deleting folder %x" ); + txtRemovingSymlink = _("Deleting symbolic link %x"); + } + } + + virtual void visit(const FilePair& fileObj) + { + notifyFileDeletion(fileObj.getFullName()); + + if (useRecycleBin_) + zen::recycleOrDelete(fileObj.getFullName()); //throw FileError + else + zen::removeFile(fileObj.getFullName()); //throw FileError + } + + virtual void visit(const SymlinkPair& linkObj) + { + notifySymlinkDeletion(linkObj.getFullName()); + + if (useRecycleBin_) + zen::recycleOrDelete(linkObj.getFullName()); //throw FileError + else + { + if (dirExists(linkObj.getFullName())) //dir symlink + zen::removeDirectory(linkObj.getFullName()); //throw FileError + else //file symlink, broken symlink + zen::removeFile(linkObj.getFullName()); //throw FileError + } + } + + virtual void visit(const DirPair& dirObj) + { + notifyDirectoryDeletion(dirObj.getFullName()); //notfied twice! see RemoveCallbackImpl -> no big deal + + if (useRecycleBin_) + zen::recycleOrDelete(dirObj.getFullName()); //throw FileError + else + zen::removeDirectory(dirObj.getFullName(), &remCallback); //throw FileError + } + +private: + struct RemoveCallbackImpl : public zen::CallbackRemoveDir + { + RemoveCallbackImpl(ItemDeleter& itemDeleter) : itemDeleter_(itemDeleter) {} + + virtual void onBeforeFileDeletion(const Zstring& filename) { itemDeleter_.notifyFileDeletion (filename); } + virtual void onBeforeDirDeletion (const Zstring& dirname) { itemDeleter_.notifyDirectoryDeletion(dirname ); } + + private: + ItemDeleter& itemDeleter_; + }; + + void notifyFileDeletion (const Zstring& objName) { notifyItemDeletion(txtRemovingFile , objName); } + void notifyDirectoryDeletion(const Zstring& objName) { notifyItemDeletion(txtRemovingDirectory, objName); } + void notifySymlinkDeletion (const Zstring& objName) { notifyItemDeletion(txtRemovingSymlink , objName); } + + void notifyItemDeletion(const std::wstring& statusText, const Zstring& objName) + { + handler_.reportStatus(replaceCpy(statusText, L"%x", fmtFileName(objName))); + } + + DeleteFilesHandler& handler_; + const bool useRecycleBin_; + RemoveCallbackImpl remCallback; + + std::wstring txtRemovingFile; + std::wstring txtRemovingDirectory; + std::wstring txtRemovingSymlink; +}; + + +template +void deleteFromGridAndHDOneSide(std::vector& ptrList, + bool useRecycleBin, + DeleteFilesHandler& handler) +{ + ItemDeleter deleter(useRecycleBin, handler); + + for (auto it = ptrList.begin(); it != ptrList.end(); ++it) //VS 2010 bug prevents replacing this by std::for_each + lamba + { + FileSystemObject& fsObj = **it; //all pointers are required(!) to be bound + + if (!fsObj.isEmpty()) //element may be implicitly deleted, e.g. if parent folder was deleted first + tryReportingError([&] + { + fsObj.accept(deleter); //throw FileError + fsObj.removeObject(); //if directory: removes recursively! + }, handler); + } +} +} + +void zen::deleteFromGridAndHD(const std::vector& rowsToDeleteOnLeft, //refresh GUI grid after deletion to remove invalid rows + const std::vector& rowsToDeleteOnRight, //all pointers need to be bound! + FolderComparison& folderCmp, //attention: rows will be physically deleted! + const std::vector& directCfgs, + bool deleteOnBothSides, + bool useRecycleBin, + DeleteFilesHandler& statusHandler, + bool& warningRecyclerMissing) +{ + if (folderCmp.empty()) + return; + else if (folderCmp.size() != directCfgs.size()) + throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo(__LINE__)); + + //build up mapping from base directory to corresponding direction config + hash_map baseDirCfgs; + for (auto it = folderCmp.begin(); it != folderCmp.end(); ++it) + baseDirCfgs[&** it] = directCfgs[it - folderCmp.begin()]; + + std::set deleteLeft (rowsToDeleteOnLeft .begin(), rowsToDeleteOnLeft .end()); + std::set deleteRight(rowsToDeleteOnRight.begin(), rowsToDeleteOnRight.end()); + if (deleteOnBothSides) + { + deleteLeft.insert(deleteRight.begin(), deleteRight.end()); + deleteRight = deleteLeft; + } + + set_remove_if(deleteLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty(); }); //still needed? + set_remove_if(deleteRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty(); }); // + + //ensure cleanup: redetermination of sync-directions and removal of invalid rows + auto updateDirection = [&]() + { + //update sync direction: we cannot do a full redetermination since the user may already have entered manual changes + std::set deletedTotal = deleteLeft; + deletedTotal.insert(deleteRight.begin(), deleteRight.end()); + + for (auto it = deletedTotal.begin(); it != deletedTotal.end(); ++it) + { + FileSystemObject& fsObj = **it; //all pointers are required(!) to be bound + + if (fsObj.isEmpty() != fsObj.isEmpty()) //make sure objects exists on one side only + { + auto cfgIter = baseDirCfgs.find(&fsObj.root()); + if (cfgIter != baseDirCfgs.end()) + { + SyncDirection newDir = SyncDirection::NONE; + + if (cfgIter->second.var == DirectionConfig::TWOWAY) + newDir = fsObj.isEmpty() ? SyncDirection::RIGHT : SyncDirection::LEFT; + else + { + const DirectionSet& dirCfg = extractDirections(cfgIter->second); + newDir = fsObj.isEmpty() ? dirCfg.exRightSideOnly : dirCfg.exLeftSideOnly; + } + + setSyncDirectionRec(newDir, fsObj); //set new direction (recursively) + } + else + assert(!"this should not happen!"); + } + } + + //last step: cleanup empty rows: this one invalidates all pointers! + std::for_each(begin(folderCmp), end(folderCmp), BaseDirPair::removeEmpty); + }; + ZEN_ON_SCOPE_EXIT(updateDirection()); //MSVC: assert is a macro and it doesn't play nice with ZEN_ON_SCOPE_EXIT, surprise... wasn't there something about macros being "evil"? + + //categorize rows into permanent deletion and recycle bin + std::vector deletePermanentLeft; + std::vector deletePermanentRight; + std::vector deleteRecylerLeft; + std::vector deleteRecylerRight; + + std::map hasRecyclerBuffer; + categorize(deleteLeft, deletePermanentLeft, deleteRecylerLeft, useRecycleBin, hasRecyclerBuffer, statusHandler); + categorize(deleteRight, deletePermanentRight, deleteRecylerRight, useRecycleBin, hasRecyclerBuffer, statusHandler); + + //windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong + if (useRecycleBin && + std::any_of(hasRecyclerBuffer.begin(), hasRecyclerBuffer.end(), [](std::pair item) { return !item.second; })) + { + std::wstring msg = _("The recycle bin is not available for the following folders. Files will be deleted permanently instead:") + L"\n"; + + for (auto it = hasRecyclerBuffer.begin(); it != hasRecyclerBuffer.end(); ++it) + if (!it->second) + msg += std::wstring(L"\n") + it->first; + + statusHandler.reportWarning(msg, warningRecyclerMissing); + } + + deleteFromGridAndHDOneSide(deleteRecylerLeft, true, statusHandler); + deleteFromGridAndHDOneSide(deletePermanentLeft, false, statusHandler); + + deleteFromGridAndHDOneSide(deleteRecylerRight, true, statusHandler); + deleteFromGridAndHDOneSide(deletePermanentRight, false, statusHandler); +} diff --git a/FreeFileSync/Source/algorithm.h b/FreeFileSync/Source/algorithm.h new file mode 100644 index 00000000..09adb5ec --- /dev/null +++ b/FreeFileSync/Source/algorithm.h @@ -0,0 +1,68 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef ALGORITHM_H_INCLUDED +#define ALGORITHM_H_INCLUDED + +#include +#include "file_hierarchy.h" +#include "lib/soft_filter.h" + +namespace zen +{ +void swapGrids(const MainConfiguration& config, FolderComparison& folderCmp); + +std::vector extractDirectionCfg(const MainConfiguration& mainCfg); + +void redetermineSyncDirection(const DirectionConfig& directConfig, BaseDirPair& baseDirectory, std::function reportWarning); +void redetermineSyncDirection(const MainConfiguration& mainCfg, FolderComparison& folderCmp, std::function reportWarning); + +void setSyncDirectionRec(SyncDirection newDirection, FileSystemObject& fsObj); //set new direction (recursively) + +bool allElementsEqual(const FolderComparison& folderCmp); + +//filtering +void applyFiltering (FolderComparison& folderCmp, const MainConfiguration& mainCfg); //full filter apply +void addHardFiltering(BaseDirPair& baseDirObj, const Zstring& excludeFilter); //exclude additional entries only +void addSoftFiltering(BaseDirPair& baseDirObj, const SoftFilter& timeSizeFilter); //exclude additional entries only + +void applyTimeSpanFilter(FolderComparison& folderCmp, const Int64& timeFrom, const Int64& timeTo); //overwrite current active/inactive settings + +void setActiveStatus(bool newStatus, FolderComparison& folderCmp); //activate or deactivate all rows +void setActiveStatus(bool newStatus, FileSystemObject& fsObj); //activate or deactivate row: (not recursively anymore) + + +//manual deletion of files on main grid +std::pair deleteFromGridAndHDPreview( //returns string with elements to be deleted and total count of selected(!) objects, NOT total files/dirs! + const std::vector& selectionLeft, //all pointers need to be bound! + const std::vector& selectionRight, // + bool deleteOnBothSides); + +struct DeleteFilesHandler +{ + virtual ~DeleteFilesHandler() {} + + enum Response + { + IGNORE_ERROR = 10, + RETRY + }; + virtual Response reportError (const std::wstring& msg) = 0; + virtual void reportWarning(const std::wstring& msg, bool& warningActive) = 0; + virtual void reportStatus (const std::wstring& msg) = 0; +}; +void deleteFromGridAndHD(const std::vector& rowsToDeleteOnLeft, //refresh GUI grid after deletion to remove invalid rows + const std::vector& rowsToDeleteOnRight, //all pointers need to be bound! + FolderComparison& folderCmp, //attention: rows will be physically deleted! + const std::vector& directCfgs, + bool deleteOnBothSides, + bool useRecycleBin, + DeleteFilesHandler& statusHandler, + //global warnings: + bool& warningRecyclerMissing); +} + +#endif //ALGORITHM_H_INCLUDED diff --git a/FreeFileSync/Source/application.cpp b/FreeFileSync/Source/application.cpp new file mode 100644 index 00000000..b474d05f --- /dev/null +++ b/FreeFileSync/Source/application.cpp @@ -0,0 +1,627 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "application.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "comparison.h" +#include "algorithm.h" +#include "synchronization.h" +#include "ui/batch_status_handler.h" +#include "ui/check_version.h" +#include "ui/main_dlg.h" +#include "ui/switch_to_gui.h" +#include "lib/process_xml.h" +#include "lib/error_log.h" + +#ifdef ZEN_WIN +#include + +#elif defined ZEN_LINUX +#include +#endif + +using namespace zen; +using namespace xmlAccess; + + +IMPLEMENT_APP(Application) + +namespace +{ +/* +boost::thread::id mainThreadId = boost::this_thread::get_id(); + +void onTerminationRequested() +{ +std::wstring msg = boost::this_thread::get_id() == mainThreadId ? + L"Termination requested in main thread!\n\n" : + L"Termination requested in worker thread!\n\n"; +msg += L"Please file a bug report at: http://sourceforge.net/projects/freefilesync"; + +wxSafeShowMessage(_("An exception occurred"), msg); +std::abort(); +} +*/ + +#ifdef _MSC_VER +void crtInvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved) { assert(false); } +#endif + + +std::vector getCommandlineArgs(const wxApp& app) +{ + std::vector args; +#ifdef ZEN_WIN + //we do the job ourselves! both wxWidgets and ::CommandLineToArgvW() parse "C:\" "D:\" as single line C:\" D:\" + //-> "solution": we just don't support protected quotation mark! + Zstring cmdLine = ::GetCommandLine(); //only way to get a unicode commandline + while (endsWith(cmdLine, L' ')) //may end with space + cmdLine.resize(cmdLine.size() - 1); + + auto iterStart = cmdLine.end(); //end() means: no token + for (auto it = cmdLine.begin(); it != cmdLine.end(); ++it) + if (*it == L' ') //space commits token + { + if (iterStart != cmdLine.end()) + { + args.push_back(Zstring(iterStart, it)); + iterStart = cmdLine.end(); //expect consecutive blanks! + } + } + else + { + //start new token + if (iterStart == cmdLine.end()) + iterStart = it; + + if (*it == L'\"') + { + it = std::find(it + 1, cmdLine.end(), L'\"'); + if (it == cmdLine.end()) + break; + } + } + if (iterStart != cmdLine.end()) + args.push_back(Zstring(iterStart, cmdLine.end())); + + if (!args.empty()) + args.erase(args.begin()); //remove first argument which is exe path by convention: http://blogs.msdn.com/b/oldnewthing/archive/2006/05/15/597984.aspx + + std::for_each(args.begin(), args.end(), + [](Zstring& str) + { + if (str.size() >= 2 && startsWith(str, L'\"') && endsWith(str, L'\"')) + str = Zstring(str.c_str() + 1, str.size() - 2); + }); + +#else + for (int i = 1; i < app.argc; ++i) //wxWidgets screws up once again making "argv implicitly convertible to a wxChar**" in 2.9.3, + args.push_back(toZ(wxString(app.argv[i]))); //so we are forced to use this pitiful excuse for a range construction!! +#endif + return args; +} + +const wxEventType EVENT_ENTER_EVENT_LOOP = wxNewEventType(); +} + +//################################################################################################################## + +bool Application::OnInit() +{ + //-> this seems rather useless: + //std::set_terminate(onTerminationRequested); //unlike wxWidgets uncaught exception handling, this works for all worker threads + +#ifdef ZEN_WIN +#ifdef _MSC_VER + _set_invalid_parameter_handler(crtInvalidParameterHandler); //see comment in +#endif + //Quote: "Best practice is that all applications call the process-wide ::SetErrorMode() function with a parameter of + //SEM_FAILCRITICALERRORS at startup. This is to prevent error mode dialogs from hanging the application." + ::SetErrorMode(SEM_FAILCRITICALERRORS); + +#elif defined ZEN_LINUX + ::gtk_init(nullptr, nullptr); + ::gtk_rc_parse((getResourceDir() + "styles.gtk_rc").c_str()); //remove inner border from bitmap buttons +#endif + +#ifdef ZEN_WIN + wxToolTip::SetMaxWidth(-1); //disable tooltip wrapping -> Windows only +#endif + //Windows User Experience Interaction Guidelines: tool tips should have 5s timeout, info tips no timeout => compromise: + wxToolTip::SetAutoPop(7000); //http://msdn.microsoft.com/en-us/library/windows/desktop/aa511495.aspx + + SetAppName(L"FreeFileSync"); //if not set, the default is the executable's name! + + initResourceImages(getResourceDir() + Zstr("Resources.zip")); + + Connect(wxEVT_QUERY_END_SESSION, wxEventHandler(Application::onQueryEndSession), nullptr, this); + Connect(wxEVT_END_SESSION, wxEventHandler(Application::onQueryEndSession), nullptr, this); + + //do not call wxApp::OnInit() to avoid using wxWidgets command line parser + + //Note: app start is deferred: batch mode requires the wxApp eventhandler to be established for UI update events. This is not the case at the time of OnInit()! + Connect(EVENT_ENTER_EVENT_LOOP, wxEventHandler(Application::onEnterEventLoop), nullptr, this); + wxCommandEvent scrollEvent(EVENT_ENTER_EVENT_LOOP); + AddPendingEvent(scrollEvent); + + return true; //true: continue processing; false: exit immediately. +} + + +int Application::OnExit() +{ + releaseWxLocale(); + return wxApp::OnExit(); +} + + +void Application::onEnterEventLoop(wxEvent& event) +{ + Disconnect(EVENT_ENTER_EVENT_LOOP, wxEventHandler(Application::onEnterEventLoop), nullptr, this); + + //determine FFS mode of operation + std::vector commandArgs = getCommandlineArgs(*this); + launch(commandArgs); +} + +#ifdef ZEN_MAC +/* +wxWidgets initialization sequence on OS X is a mess: +---------------------------------------------------- +1. double click FFS app bundle or execute from command line without arguments + OnInit() + OnRun() + onEnterEventLoop() + MacNewFile() + +2. double-click .ffs_gui file + OnInit() + OnRun() + onEnterEventLoop() + MacOpenFiles() + +3. start from command line with .ffs_gui file as first argument + OnInit() + OnRun() + MacOpenFiles() -> WTF!? + onEnterEventLoop() + MacNewFile() -> yes, wxWidgets screws up once again: http://trac.wxwidgets.org/ticket/14558 + +=> solution: map Apple events to regular command line via launcher +*/ +#endif + + +int Application::OnRun() +{ + auto processException = [](const std::wstring& msg) + { + //it's not always possible to display a message box, e.g. corrupted stack, however low-level file output works! + logError(utfCvrtTo(msg)); + wxSafeShowMessage(L"FreeFileSync - " + _("An exception occurred"), msg); + }; + + try + { + wxApp::OnRun(); + } + catch (const std::exception& e) //catch all STL exceptions + { + processException(utfCvrtTo(e.what())); + return FFS_RC_EXCEPTION; + } + catch (...) //catch the rest + { + processException(L"Unknown error."); + return FFS_RC_EXCEPTION; + } + + return returnCode; +} + + +void Application::onQueryEndSession(wxEvent& event) +{ + if (auto mainWin = dynamic_cast(GetTopWindow())) + mainWin->onQueryEndSession(); + OnExit(); //wxWidgets screws up again: http://trac.wxwidgets.org/ticket/3069 + //wxEntryCleanup(); -> gives popup "dll init failed" on XP + std::exit(returnCode); //Windows will terminate anyway: destruct global objects +} + + +void runGuiMode(); +void runGuiMode(const XmlGuiConfig& guiCfg, const std::vector& referenceFiles); +void runBatchMode(const XmlBatchConfig& batchCfg, const Zstring& referenceFile, FfsReturnCode& returnCode); +void showSyntaxHelp(); + + +void Application::launch(const std::vector& commandArgs) +{ + //wxWidgets app exit handling is weird... we want the app to exit only if the logical main window is closed + wxTheApp->SetExitOnFrameDelete(false); //avoid popup-windows from becoming temporary top windows leading to program exit after closure + ZEN_ON_SCOPE_EXIT(if (!mainWindowWasSet()) wxTheApp->ExitMainLoop();); //quit application, if no main window was set (batch silent mode) + + try + { + //tentatively set program language to OS default until GlobalSettings.xml is read later + setLanguage(xmlAccess::XmlGlobalSettings().programLanguage); //throw FileError + } + catch (const FileError&) { assert(false); } //no messagebox: consider batch job! + + auto notifyError = [&](const std::wstring& msg, const std::wstring& title) + { + showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setTitle(title).setDetailInstructions(msg)); + raiseReturnCode(returnCode, FFS_RC_ABORTED); + }; + + //parse command line arguments + std::vector leftDirs; + std::vector rightDirs; + std::vector> configFiles; //XmlType: batch or GUI files only + { + const Zchar optionLeftDir [] = Zstr("-leftdir"); + const Zchar optionRightDir[] = Zstr("-rightdir"); + + auto syntaxHelpRequested = [](const Zstring& arg) + { + auto it = std::find_if(arg.begin(), arg.end(), [](Zchar c) { return c != Zchar('/') && c != Zchar('-'); }); + const Zstring argTmp(it, arg.end()); + return argTmp == Zstr("help") || + argTmp == Zstr("h") || + argTmp == Zstr("?"); + }; + + for (auto it = commandArgs.begin(); it != commandArgs.end(); ++it) + if (syntaxHelpRequested(*it)) + return showSyntaxHelp(); + else if (*it == optionLeftDir) + { + if (++it == commandArgs.end()) + { + notifyError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfCvrtTo(optionLeftDir)), _("Syntax error")); + return; + } + leftDirs.push_back(*it); + } + else if (*it == optionRightDir) + { + if (++it == commandArgs.end()) + { + notifyError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfCvrtTo(optionRightDir)), _("Syntax error")); + return; + } + rightDirs.push_back(*it); + } + else + { + Zstring filename = *it; + if (!fileExists(filename)) //...be a little tolerant + { + if (fileExists(filename + Zstr(".ffs_batch"))) + filename += Zstr(".ffs_batch"); + else if (fileExists(filename + Zstr(".ffs_gui"))) + filename += Zstr(".ffs_gui"); + else + { + notifyError(replaceCpy(_("Cannot open file %x."), L"%x", fmtFileName(filename)), std::wstring()); + return; + } + } + + try + { + switch (getXmlType(filename)) //throw FfsXmlError + { + case XML_TYPE_GUI: + configFiles.push_back(std::make_pair(filename, XML_TYPE_GUI)); + break; + case XML_TYPE_BATCH: + configFiles.push_back(std::make_pair(filename, XML_TYPE_BATCH)); + break; + + case XML_TYPE_GLOBAL: + case XML_TYPE_OTHER: + notifyError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename)), std::wstring()); + return; + } + } + catch (const FfsXmlError& e) + { + notifyError(e.toString(), std::wstring()); + return; + } + } + } + + if (leftDirs.size() != rightDirs.size()) + { + notifyError(_("Unequal number of left and right directories specified."), _("Syntax error")); + return; + } + + auto hasNonDefaultConfig = [](const FolderPairEnh& fp) + { + return !(fp == FolderPairEnh(fp.dirnamePhraseLeft, + fp.dirnamePhraseRight, + nullptr, nullptr, FilterConfig())); + }; + + auto replaceDirectories = [&](MainConfiguration& mainCfg) + { + if (!leftDirs.empty()) + { + //check if config at folder-pair level is present: this probably doesn't make sense when replacing/adding the user-specified directories + if (hasNonDefaultConfig(mainCfg.firstPair) || std::any_of(mainCfg.additionalPairs.begin(), mainCfg.additionalPairs.end(), hasNonDefaultConfig)) + { + notifyError(_("The config file must not contain settings at directory pair level when directories are set via command line."), _("Syntax error")); + return false; + } + + mainCfg.additionalPairs.clear(); + for (size_t i = 0; i < leftDirs.size(); ++i) + if (i == 0) + { + mainCfg.firstPair.dirnamePhraseLeft = leftDirs [0]; + mainCfg.firstPair.dirnamePhraseRight = rightDirs[0]; + } + else + mainCfg.additionalPairs.push_back(FolderPairEnh(leftDirs [i], + rightDirs[i], + nullptr, nullptr, FilterConfig())); + } + return true; + }; + + //distinguish sync scenarios: + //--------------------------- + if (configFiles.empty()) + { + //gui mode: default startup + if (leftDirs.empty()) + runGuiMode(); + //gui mode: default config with given directories + else + { + XmlGuiConfig guiCfg; + guiCfg.mainCfg.syncCfg.directionCfg.var = DirectionConfig::MIRROR; + + if (!replaceDirectories(guiCfg.mainCfg)) return; + runGuiMode(guiCfg, std::vector()); + } + } + else if (configFiles.size() == 1) + { + const Zstring filename = configFiles[0].first; + + //batch mode + if (configFiles[0].second == XML_TYPE_BATCH) + { + XmlBatchConfig batchCfg; + try + { + readConfig(filename, batchCfg); + } + catch (const xmlAccess::FfsXmlError& e) + { + //batch mode: break on errors AND even warnings! + notifyError(e.toString(), std::wstring()); + return; + } + if (!replaceDirectories(batchCfg.mainCfg)) return; + runBatchMode(batchCfg, filename, returnCode); + } + //GUI mode: single config + else + { + XmlGuiConfig guiCfg; + try + { + readConfig(filename, guiCfg); + } + catch (const xmlAccess::FfsXmlError& e) + { + if (e.getSeverity() == FfsXmlError::WARNING) + showNotificationDialog(nullptr, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(e.toString())); + //what about simulating changed config on parsing errors???? + else + { + notifyError(e.toString(), std::wstring()); + return; + } + } + if (!replaceDirectories(guiCfg.mainCfg)) return; + //what about simulating changed config due to directory replacement? + //-> propably fine to not show as changed on GUI and not ask user to save on exit! + + runGuiMode(guiCfg, { filename }); //caveat: guiCfg and filename do not match if directories were set/replaced via command line! + } + } + //gui mode: merged configs + else + { + if (!leftDirs.empty()) + { + notifyError(_("Directories cannot be set for more than one configuration file."), _("Syntax error")); + return; + } + + std::vector filenames; + for (const auto& item : configFiles) + filenames.push_back(item.first); + + XmlGuiConfig guiCfg; //structure to receive gui settings with default values + try + { + readAnyConfig(filenames, guiCfg); //throw FfsXmlError + } + catch (const FfsXmlError& e) + { + if (e.getSeverity() == FfsXmlError::WARNING) + showNotificationDialog(nullptr, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(e.toString())); + //what about simulating changed config on parsing errors???? + else + { + notifyError(e.toString(), std::wstring()); + return; + } + } + runGuiMode(guiCfg, filenames); + } +} + + +void runGuiMode() { MainDialog::create(); } + + +void runGuiMode(const xmlAccess::XmlGuiConfig& guiCfg, + const std::vector& referenceFiles) +{ + MainDialog::create(guiCfg, referenceFiles, nullptr, true); //startComparison == true! +} + + +void showSyntaxHelp() +{ + showNotificationDialog(nullptr, DialogInfoType::INFO, PopupDialogCfg(). + setTitle(_("Command line")). + setDetailInstructions(_("Syntax:") + L"\n" + + L"FreeFileSync [" + _("config files") + L"]\n[-leftdir " + _("directory") + L"] [-rightdir " + _("directory") + L"]" + L"\n" + + L"\n" + + _("config files") + L"\n" + + _("Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.") + L"\n\n" + + L"-leftdir " + _("directory") + L" -rightdir " + _("directory") + L"\n" + + _("Any number of alternative directories for at most one config file."))); +} + + +void runBatchMode(const XmlBatchConfig& batchCfg, const Zstring& referenceFile, FfsReturnCode& returnCode) +{ + auto notifyError = [&](const std::wstring& msg, FfsReturnCode rc) + { + if (batchCfg.handleError == ON_ERROR_POPUP) + showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(msg)); + else //"exit" or "ignore" + logError(utfCvrtTo(msg)); + + raiseReturnCode(returnCode, rc); + }; + + XmlGlobalSettings globalCfg; + try + { + if (fileExists(getGlobalConfigFile())) + readConfig(globalCfg); //throw FfsXmlError + //else: globalCfg already has default values + } + catch (const xmlAccess::FfsXmlError& e) + { + assert(false); + if (e.getSeverity() != FfsXmlError::WARNING) //ignore parsing errors: should be migration problems only *cross-fingers* + return notifyError(e.toString(), FFS_RC_ABORTED); //abort sync! + } + + try + { + setLanguage(globalCfg.programLanguage); //throw FileError + } + catch (const FileError& e) + { + notifyError(e.toString(), FFS_RC_FINISHED_WITH_WARNINGS); + //continue! + } + + //all settings have been read successfully... + + //regular check for program updates -> disabled for batch + //if (batchCfg.showProgress && manualProgramUpdateRequired()) + // checkForUpdatePeriodically(globalCfg.lastUpdateCheck); + + try //begin of synchronization process (all in one try-catch block) + { + + const TimeComp timeStamp = localTime(); + + const SwitchToGui switchBatchToGui(referenceFile, batchCfg, globalCfg); //prepare potential operational switch + + //class handling status updates and error messages + BatchStatusHandler statusHandler(batchCfg.showProgress, //throw BatchAbortProcess + extractJobName(referenceFile), + timeStamp, + batchCfg.logFileDirectory, + batchCfg.logfilesCountLimit, + globalCfg.lastSyncsLogFileSizeMax, + batchCfg.handleError, + globalCfg.automaticRetryCount, + globalCfg.automaticRetryDelay, + switchBatchToGui, + returnCode, + batchCfg.mainCfg.onCompletion, + globalCfg.gui.onCompletionHistory); + + const std::vector cmpConfig = extractCompareCfg(batchCfg.mainCfg); + + bool allowPwPrompt = false; + switch (batchCfg.handleError) + { + case ON_ERROR_POPUP: + allowPwPrompt = true; + break; + case ON_ERROR_IGNORE: + case ON_ERROR_STOP: + break; + } + + //batch mode: place directory locks on directories during both comparison AND synchronization + std::unique_ptr dirLocks; + + //COMPARE DIRECTORIES + FolderComparison folderCmp; + compare(globalCfg.fileTimeTolerance, + globalCfg.optDialogs, + allowPwPrompt, + globalCfg.runWithBackgroundPriority, + globalCfg.createLockFile, + dirLocks, + cmpConfig, + folderCmp, + statusHandler); + + //START SYNCHRONIZATION + const std::vector syncProcessCfg = extractSyncCfg(batchCfg.mainCfg); + if (syncProcessCfg.size() != folderCmp.size()) + throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo(__LINE__)); + + synchronize(timeStamp, + globalCfg.optDialogs, + globalCfg.verifyFileCopy, + globalCfg.copyLockedFiles, + globalCfg.copyFilePermissions, + globalCfg.failsafeFileCopy, + globalCfg.runWithBackgroundPriority, + syncProcessCfg, + folderCmp, + statusHandler); + } + catch (BatchAbortProcess&) {} //exit used by statusHandler + + try //save global settings to XML: e.g. ignored warnings + { + xmlAccess::writeConfig(globalCfg); //FfsXmlError + } + catch (const xmlAccess::FfsXmlError& e) + { + notifyError(e.toString(), FFS_RC_FINISHED_WITH_WARNINGS); + } +} diff --git a/FreeFileSync/Source/application.h b/FreeFileSync/Source/application.h new file mode 100644 index 00000000..defc1e17 --- /dev/null +++ b/FreeFileSync/Source/application.h @@ -0,0 +1,34 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef FREEFILESYNCAPP_H +#define FREEFILESYNCAPP_H + +#include +#include +#include +#include "lib/return_codes.h" + + +class Application : public wxApp +{ +public: + Application() : returnCode(zen::FFS_RC_SUCCESS) {} + +private: + virtual bool OnInit(); + virtual int OnExit(); + virtual int OnRun(); + virtual bool OnExceptionInMainLoop() { throw; } //just re-throw and avoid display of additional messagebox: it will be caught in OnRun() + + void onEnterEventLoop(wxEvent& event); + void onQueryEndSession(wxEvent& event); + void launch(const std::vector& commandArgs); + + zen::FfsReturnCode returnCode; +}; + +#endif // FREEFILESYNCAPP_H diff --git a/FreeFileSync/Source/comparison.cpp b/FreeFileSync/Source/comparison.cpp new file mode 100644 index 00000000..2b3a37b0 --- /dev/null +++ b/FreeFileSync/Source/comparison.cpp @@ -0,0 +1,907 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "comparison.h" +#include +#include +#include +#include +#include +#include +#include +#include "algorithm.h" +#include "lib/parallel_scan.h" +#include "lib/resolve_path.h" +#include "lib/dir_exist_async.h" +#include "lib/binary.h" +#include "lib/cmp_filetime.h" +#include "lib/status_handler_impl.h" +#include "lib/parallel_scan.h" + +using namespace zen; + + +std::vector zen::extractCompareCfg(const MainConfiguration& mainCfg) +{ + //merge first and additional pairs + std::vector allPairs; + allPairs.push_back(mainCfg.firstPair); + allPairs.insert(allPairs.end(), + mainCfg.additionalPairs.begin(), //add additional pairs + mainCfg.additionalPairs.end()); + + std::vector output; + std::transform(allPairs.begin(), allPairs.end(), std::back_inserter(output), + [&](const FolderPairEnh& enhPair) -> FolderPairCfg + { + return FolderPairCfg(enhPair.dirnamePhraseLeft, enhPair.dirnamePhraseRight, + enhPair.altCmpConfig.get() ? enhPair.altCmpConfig->compareVar : mainCfg.cmpConfig.compareVar, + enhPair.altCmpConfig.get() ? enhPair.altCmpConfig->handleSymlinks : mainCfg.cmpConfig.handleSymlinks, + + normalizeFilters(mainCfg.globalFilter, enhPair.localFilter), + + enhPair.altSyncConfig.get() ? enhPair.altSyncConfig->directionCfg : mainCfg.syncCfg.directionCfg); + }); + return output; +} + +//------------------------------------------------------------------------------------------ +namespace +{ +struct ResolvedFolderPair +{ + ResolvedFolderPair(const Zstring& left, const Zstring& right) : + dirnameLeft(left), + dirnameRight(right) {} + + Zstring dirnameLeft; //resolved directory names + Zstring dirnameRight; // +}; + + +void determineExistentDirs(const std::vector& cfgList, //in + bool allowUserInteraction, ProcessCallback& callback, + std::vector& resolvedPairs, //out + std::set& existingDirs) //out +{ + tryReportingError([&] + { + //support retry for environment variable and and variable driver letter resolution! + resolvedPairs.clear(); + for (const FolderPairCfg& fpCfg : cfgList) + resolvedPairs.push_back(ResolvedFolderPair(getFormattedDirectoryName(fpCfg.dirnamePhraseLeft), + getFormattedDirectoryName(fpCfg.dirnamePhraseRight))); + + std::set dirnames; + for (const ResolvedFolderPair& fp : resolvedPairs) + { + dirnames.insert(fp.dirnameLeft); + dirnames.insert(fp.dirnameRight); + } + + std::set missing; + existingDirs = getExistingDirsUpdating(dirnames, missing, allowUserInteraction, callback); //check *all* directories on each try! + if (!missing.empty()) + { + std::wstring msg = _("Cannot find the following folders:") + L"\n"; + for (const Zstring& dirname : missing) + msg += std::wstring(L"\n") + dirname; + throw FileError(msg, _("You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.")); + } + }, callback); + + assert(resolvedPairs.size() == cfgList.size()); //postcondition! +} + + +void checkForIncompleteInput(const std::vector& folderPairs, bool& warningInputFieldEmpty, ProcessCallback& callback) +{ + bool havePartialPair = false; + bool haveFullPair = false; + + for (const ResolvedFolderPair& fp : folderPairs) + if (fp.dirnameLeft.empty() != fp.dirnameRight.empty()) + havePartialPair = true; + else if (!fp.dirnameLeft.empty()) + haveFullPair = true; + + if (havePartialPair == haveFullPair) //error if: all empty or exist both full and partial pairs -> support single-dir scenario + callback.reportWarning(_("A folder input field is empty.") + L" \n\n" + + _("The corresponding folder will be considered as empty."), warningInputFieldEmpty); +} + + +//check whether one side is subdirectory of other side (folder pair wise!) +//similar check if one directory is read/written by multiple pairs not before beginning of synchronization +void checkFolderDependency(const std::vector& folderPairs, bool& warningDependentFolders, ProcessCallback& callback) //returns warning message, empty if all ok +{ + std::vector> dependentDirs; + + auto dependentDir = [](const Zstring& lhs, const Zstring& rhs) + { + return EqualFilename()(Zstring(lhs.c_str(), std::min(lhs.length(), rhs.length())), //note: this is NOT an equivalence relation! + Zstring(rhs.c_str(), std::min(lhs.length(), rhs.length()))); + }; + + for (const ResolvedFolderPair& fp : folderPairs) + if (!fp.dirnameLeft.empty() && !fp.dirnameRight.empty()) //empty folders names may be accepted by user + { + if (dependentDir(fp.dirnameLeft, fp.dirnameRight)) //test wheter leftDirectory begins with rightDirectory or the other way round + dependentDirs.push_back(std::make_pair(fp.dirnameLeft, fp.dirnameRight)); + } + + if (!dependentDirs.empty()) + { + std::wstring warningMsg = _("The following folders have dependent paths. Be careful when setting up synchronization rules:"); + for (auto it = dependentDirs.begin(); it != dependentDirs.end(); ++it) + warningMsg += std::wstring(L"\n\n") + + it->first + L"\n" + + it->second; + + callback.reportWarning(warningMsg, warningDependentFolders); + } +} + + +class CmpCallbackImpl : public CompareCallback +{ +public: + CmpCallbackImpl(ProcessCallback& pc, Int64& bytesReported) : + pc_(pc), + bytesReported_(bytesReported) {} + + virtual void updateCompareStatus(Int64 bytesDelta) + { + //inform about the (differential) processed amount of data + pc_.updateProcessedData(0, bytesDelta); //throw()! -> ensure client and service provider are in sync! + bytesReported_ += bytesDelta; // + + pc_.requestUiRefresh(); //may throw + } + +private: + ProcessCallback& pc_; + Int64& bytesReported_; +}; + + +bool filesHaveSameContentUpdating(const Zstring& filename1, const Zstring& filename2, Int64 expectedBytesToCmp, ProcessCallback& pc) //throw FileError +{ + Int64 bytesReported; //amount of bytes that have been compared and communicated to status handler + + //error = unexpected increase of total workload + zen::ScopeGuard guardStatistics = zen::makeGuard([&] { pc.updateTotalData(0, bytesReported); }); + + CmpCallbackImpl callback(pc, bytesReported); + bool sameContent = filesHaveSameContent(filename1, filename2, callback); //throw FileError + + guardStatistics.dismiss(); + + //update statistics to consider the real amount of data processed: consider short-cut behavior if first bytes differ! + if (bytesReported != expectedBytesToCmp) + pc.updateTotalData(0, bytesReported - expectedBytesToCmp); + + return sameContent; +} + +//############################################################################################################################# + +class ComparisonBuffer +{ +public: + ComparisonBuffer(const std::set& keysToRead, size_t fileTimeTol, ProcessCallback& callback); + + //create comparison result table and fill category except for files existing on both sides: undefinedFiles and undefinedLinks are appended! + std::shared_ptr compareByTimeSize(const ResolvedFolderPair& fp, const FolderPairCfg& fpConfig) const; + std::list> compareByContent(const std::vector>& workLoad) const; + +private: + ComparisonBuffer(const ComparisonBuffer&); //=delete + ComparisonBuffer& operator=(const ComparisonBuffer&); //=delete + + std::shared_ptr performComparison(const ResolvedFolderPair& fp, + const FolderPairCfg& fpCfg, + std::vector& undefinedFiles, + std::vector& undefinedLinks) const; + + std::map directoryBuffer; //contains only *existing* directories + const size_t fileTimeTolerance; + ProcessCallback& callback_; +}; + + +ComparisonBuffer::ComparisonBuffer(const std::set& keysToRead, + size_t fileTimeTol, + ProcessCallback& callback) : + fileTimeTolerance(fileTimeTol), + callback_(callback) +{ + class CbImpl : public FillBufferCallback + { + public: + CbImpl(ProcessCallback& pcb) : + callback_(pcb), + itemsReported(0) {} + + virtual void reportStatus(const std::wstring& statusMsg, int itemsTotal) + { + callback_.updateProcessedData(itemsTotal - itemsReported, 0); //processed bytes are reported in subfunctions! + itemsReported = itemsTotal; + + callback_.reportStatus(statusMsg); //may throw + //callback_.requestUiRefresh(); //already called by reportStatus() + } + + virtual HandleError reportError(const std::wstring& msg, size_t retryNumber) + { + switch (callback_.reportError(msg, retryNumber)) + { + case ProcessCallback::IGNORE_ERROR: + return ON_ERROR_IGNORE; + + case ProcessCallback::RETRY: + return ON_ERROR_RETRY; + } + + assert(false); + return ON_ERROR_IGNORE; + } + + private: + ProcessCallback& callback_; + int itemsReported; + } cb(callback); + + fillBuffer(keysToRead, //in + directoryBuffer, //out + cb, + UI_UPDATE_INTERVAL / 2); //every ~50 ms +} + + +//--------------------assemble conflict descriptions--------------------------- + +//const wchar_t arrowLeft [] = L"\u2190"; +//const wchar_t arrowRight[] = L"\u2192"; unicode arrows -> too small +const wchar_t arrowLeft [] = L"<--"; +const wchar_t arrowRight[] = L"-->"; + + +//check for very old dates or date2s in the future +std::wstring getConflictInvalidDate(const Zstring& fileNameFull, Int64 utcTime) +{ + return replaceCpy(_("File %x has an invalid date."), L"%x", fmtFileName(fileNameFull)) + L"\n" + + _("Date:") + L" " + utcToLocalTimeString(utcTime); +} + + +//check for changed files with same modification date +std::wstring getConflictSameDateDiffSize(const FilePair& fileObj) +{ + return replaceCpy(_("Files %x have the same date but a different size."), L"%x", fmtFileName(fileObj.getObjRelativeName())) + L"\n" + + L" " + arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.getLastWriteTime()) + L" " + _("Size:") + L" " + toGuiString(fileObj.getFileSize()) + L"\n" + + L" " + arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.getLastWriteTime()) + L" " + _("Size:") + L" " + toGuiString(fileObj.getFileSize()); +} + + +std::wstring getDescrDiffMetaShortnameCase(const FileSystemObject& fsObj) +{ + return _("Items differ in attributes only") + L"\n" + + L" " + arrowLeft + L" " + fmtFileName(fsObj.getShortName()) + L"\n" + + L" " + arrowRight + L" " + fmtFileName(fsObj.getShortName()); +} + + +template +std::wstring getDescrDiffMetaDate(const FileOrLinkPair& fileObj) +{ + return _("Items differ in attributes only") + L"\n" + + L" " + arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.template getLastWriteTime()) + L"\n" + + L" " + arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.template getLastWriteTime()); +} + +//----------------------------------------------------------------------------- + +void categorizeSymlinkByTime(SymlinkPair& linkObj, size_t fileTimeTolerance) +{ + //categorize symlinks that exist on both sides + switch (CmpFileTime::getResult(linkObj.getLastWriteTime(), + linkObj.getLastWriteTime(), fileTimeTolerance)) + { + case CmpFileTime::TIME_EQUAL: + //Caveat: + //1. SYMLINK_EQUAL may only be set if short names match in case: InSyncDir's mapping tables use short name as a key! see db_file.cpp + //2. harmonize with "bool stillInSync()" in algorithm.cpp + + if (linkObj.getShortName() == linkObj.getShortName()) + linkObj.setCategory(); + else + linkObj.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(linkObj)); + break; + + case CmpFileTime::TIME_LEFT_NEWER: + linkObj.setCategory(); + break; + + case CmpFileTime::TIME_RIGHT_NEWER: + linkObj.setCategory(); + break; + + case CmpFileTime::TIME_LEFT_INVALID: + linkObj.setCategoryConflict(getConflictInvalidDate(linkObj.getFullName(), linkObj.getLastWriteTime())); + break; + + case CmpFileTime::TIME_RIGHT_INVALID: + linkObj.setCategoryConflict(getConflictInvalidDate(linkObj.getFullName(), linkObj.getLastWriteTime())); + break; + } +} + + +std::shared_ptr ComparisonBuffer::compareByTimeSize(const ResolvedFolderPair& fp, const FolderPairCfg& fpConfig) const +{ + //do basis scan and retrieve files existing on both sides as "compareCandidates" + std::vector uncategorizedFiles; + std::vector uncategorizedLinks; + std::shared_ptr output = performComparison(fp, fpConfig, uncategorizedFiles, uncategorizedLinks); + + //finish symlink categorization + for (SymlinkPair* linkObj : uncategorizedLinks) + categorizeSymlinkByTime(*linkObj, fileTimeTolerance); + + //categorize files that exist on both sides + for (FilePair* fileObj : uncategorizedFiles) + { + switch (CmpFileTime::getResult(fileObj->getLastWriteTime(), + fileObj->getLastWriteTime(), fileTimeTolerance)) + { + case CmpFileTime::TIME_EQUAL: + //Caveat: + //1. FILE_EQUAL may only be set if short names match in case: InSyncDir's mapping tables use short name as a key! see db_file.cpp + //2. FILE_EQUAL is expected to mean identical file sizes! See InSyncFile + //3. harmonize with "bool stillInSync()" in algorithm.cpp, FilePair::syncTo() in file_hierarchy.cpp + if (fileObj->getFileSize() == fileObj->getFileSize()) + { + if (fileObj->getShortName() == fileObj->getShortName()) + fileObj->setCategory(); + else + fileObj->setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(*fileObj)); + } + else + fileObj->setCategoryConflict(getConflictSameDateDiffSize(*fileObj)); //same date, different filesize + break; + + case CmpFileTime::TIME_LEFT_NEWER: + fileObj->setCategory(); + break; + + case CmpFileTime::TIME_RIGHT_NEWER: + fileObj->setCategory(); + break; + + case CmpFileTime::TIME_LEFT_INVALID: + fileObj->setCategoryConflict(getConflictInvalidDate(fileObj->getFullName(), fileObj->getLastWriteTime())); + break; + + case CmpFileTime::TIME_RIGHT_INVALID: + fileObj->setCategoryConflict(getConflictInvalidDate(fileObj->getFullName(), fileObj->getLastWriteTime())); + break; + } + } + return output; +} + + +void categorizeSymlinkByContent(SymlinkPair& linkObj, size_t fileTimeTolerance, ProcessCallback& callback) +{ + //categorize symlinks that exist on both sides + Zstring targetPathRawL; + Zstring targetPathRawR; + Opt errMsg = tryReportingError([&] + { + callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtFileName(linkObj.getFullName()))); + targetPathRawL = getSymlinkTargetRaw(linkObj.getFullName()); //throw FileError + + callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtFileName(linkObj.getFullName()))); + targetPathRawR = getSymlinkTargetRaw(linkObj.getFullName()); //throw FileError + }, callback); + + if (errMsg) + linkObj.setCategoryConflict(*errMsg); + else + { + if (targetPathRawL == targetPathRawR +#ifdef ZEN_WIN //type of symbolic link is relevant for Windows only + && + dirExists(linkObj.getFullName()) == //check if dir-symlink + dirExists(linkObj.getFullName()) // +#endif + ) + { + //Caveat: + //1. SYMLINK_EQUAL may only be set if short names match in case: InSyncDir's mapping tables use short name as a key! see db_file.cpp + //2. harmonize with "bool stillInSync()" in algorithm.cpp, FilePair::syncTo() in file_hierarchy.cpp + + //symlinks have same "content" + if (linkObj.getShortName() != linkObj.getShortName()) + linkObj.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(linkObj)); + else if (CmpFileTime::getResult(linkObj.getLastWriteTime(), + linkObj.getLastWriteTime(), fileTimeTolerance) != CmpFileTime::TIME_EQUAL) + linkObj.setCategoryDiffMetadata(getDescrDiffMetaDate(linkObj)); + else + linkObj.setCategory(); + } + else + linkObj.setCategory(); + } +} + + +std::list> ComparisonBuffer::compareByContent(const std::vector>& workLoad) const +{ + std::list> output; + if (workLoad.empty()) + return output; + + //PERF_START; + std::vector undefinedFiles; + + //process folder pairs one after another + for (const auto& w : workLoad) + { + std::vector uncategorizedLinks; + //do basis scan and retrieve candidates for binary comparison (files existing on both sides) + + output.push_back(performComparison(w.first, w.second, undefinedFiles, uncategorizedLinks)); + + //finish symlink categorization + for (SymlinkPair* linkObj : uncategorizedLinks) + categorizeSymlinkByContent(*linkObj, fileTimeTolerance, callback_); + } + + //finish categorization... + std::vector filesToCompareBytewise; + + //content comparison of file content happens AFTER finding corresponding files + //in order to separate into two processes (scanning and comparing) + + for (FilePair* fileObj : undefinedFiles) + //pre-check: files have different content if they have a different filesize (must not be FILE_EQUAL: see InSyncFile) + if (fileObj->getFileSize() != fileObj->getFileSize()) + fileObj->setCategory(); + else + filesToCompareBytewise.push_back(fileObj); + + const size_t objectsTotal = filesToCompareBytewise.size(); + + UInt64 bytesTotal; //left and right filesizes are equal + for (FilePair* fileObj : filesToCompareBytewise) + bytesTotal += fileObj->getFileSize(); + + callback_.initNewPhase(static_cast(objectsTotal), + to(bytesTotal), + ProcessCallback::PHASE_COMPARING_CONTENT); + + const std::wstring txtComparingContentOfFiles = _("Comparing content of files %x"); + + //compare files (that have same size) bytewise... + for (FilePair* fileObj : filesToCompareBytewise) + { + callback_.reportStatus(replaceCpy(txtComparingContentOfFiles, L"%x", fmtFileName(fileObj->getObjRelativeName()), false)); + + //check files that exist in left and right model but have different content + + bool haveSameContent = false; + Opt errMsg = tryReportingError([&] + { + haveSameContent = filesHaveSameContentUpdating(fileObj->getFullName(), //throw FileError + fileObj->getFullName(), + to(fileObj->getFileSize()), + callback_); + + callback_.updateProcessedData(1, 0); //processed bytes are reported in subfunctions! + callback_.requestUiRefresh(); //may throw + }, callback_); + + if (errMsg) + fileObj->setCategoryConflict(*errMsg); + else + { + if (haveSameContent) + { + //Caveat: + //1. FILE_EQUAL may only be set if short names match in case: InSyncDir's mapping tables use short name as a key! see db_file.cpp + //2. FILE_EQUAL is expected to mean identical file sizes! See InSyncFile + //3. harmonize with "bool stillInSync()" in algorithm.cpp, FilePair::syncTo() in file_hierarchy.cpp + if (fileObj->getShortName() != fileObj->getShortName()) + fileObj->setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(*fileObj)); + else if (CmpFileTime::getResult(fileObj->getLastWriteTime(), + fileObj->getLastWriteTime(), fileTimeTolerance) != CmpFileTime::TIME_EQUAL) + fileObj->setCategoryDiffMetadata(getDescrDiffMetaDate(*fileObj)); + else + fileObj->setCategory(); + } + else + fileObj->setCategory(); + } + } + return output; +} + + +class MergeSides +{ +public: + MergeSides(std::vector& appendUndefinedFileOut, + std::vector& appendUndefinedLinkOut) : + appendUndefinedFile(appendUndefinedFileOut), + appendUndefinedLink(appendUndefinedLinkOut) {} + + void execute(const DirContainer& leftSide, const DirContainer& rightSide, HierarchyObject& output); + +private: + template + void fillOneSide(const DirContainer& dirCont, HierarchyObject& output); + + std::vector& appendUndefinedFile; + std::vector& appendUndefinedLink; +}; + + +template +void MergeSides::fillOneSide(const DirContainer& dirCont, HierarchyObject& output) +{ + for (const auto& file : dirCont.files) + output.addSubFile(file.first, file.second); + + for (const auto& link : dirCont.links) + output.addSubLink(link.first, link.second); + + for (const auto& dir : dirCont.dirs) + { + DirPair& newDirMap = output.addSubDir(dir.first); + fillOneSide(dir.second, newDirMap); //recurse + } +} + + +//improve merge-perf by over 70% + more natural default sequence +template inline +void linearMerge(const MapType& mapLeft, const MapType& mapRight, ProcessLeftOnly lo, ProcessRightOnly ro, ProcessBoth bo) +{ + const auto lessVal = mapLeft.value_comp(); + + auto iterLeft = mapLeft .begin(); + auto iterRight = mapRight.begin(); + + auto finishLeft = [&] { std::for_each(iterLeft, mapLeft .end(), lo); }; + auto finishRight = [&] { std::for_each(iterRight, mapRight.end(), ro); }; + + if (iterLeft == mapLeft .end()) return finishRight(); + if (iterRight == mapRight.end()) return finishLeft(); + + for (;;) + if (lessVal(*iterLeft, *iterRight)) + { + lo(*iterLeft); + if (++iterLeft == mapLeft.end()) + return finishRight(); + } + else if (lessVal(*iterRight, *iterLeft)) + { + ro(*iterRight); + if (++iterRight == mapRight.end()) + return finishLeft(); + } + else + { + bo(*iterLeft, *iterRight); + ++iterLeft; // + ++iterRight; //increment BOTH before checking for end of range! + if (iterLeft == mapLeft .end()) return finishRight(); + if (iterRight == mapRight.end()) return finishLeft(); + } +} + + +void MergeSides::execute(const DirContainer& leftSide, const DirContainer& rightSide, HierarchyObject& output) +{ + //HierarchyObject::addSubFile() must NOT invalidate references used in "appendUndefined"! + + typedef const DirContainer::FileList::value_type FileData; + + linearMerge(leftSide.files, rightSide.files, + [&](const FileData& fileLeft ) { output.addSubFile(fileLeft .first, fileLeft .second); }, //left only + [&](const FileData& fileRight) { output.addSubFile(fileRight.first, fileRight.second); }, //right only + + [&](const FileData& fileLeft, const FileData& fileRight) //both sides + { + FilePair& newEntry = output.addSubFile(fileLeft.first, + fileLeft.second, + FILE_EQUAL, //FILE_EQUAL is just a dummy-value here + fileRight.first, + fileRight.second); + appendUndefinedFile.push_back(&newEntry); + }); + + //----------------------------------------------------------------------------------------------- + typedef const DirContainer::LinkList::value_type LinkData; + + linearMerge(leftSide.links, rightSide.links, + [&](const LinkData& linkLeft) { output.addSubLink(linkLeft.first, linkLeft.second); }, //left only + [&](const LinkData& linkRight) { output.addSubLink(linkRight.first, linkRight.second); }, //right only + + [&](const LinkData& linkLeft, const LinkData& linkRight) //both sides + { + SymlinkPair& newEntry = output.addSubLink(linkLeft.first, + linkLeft.second, + SYMLINK_EQUAL, //SYMLINK_EQUAL is just a dummy-value here + linkRight.first, + linkRight.second); + appendUndefinedLink.push_back(&newEntry); + }); + + //----------------------------------------------------------------------------------------------- + typedef const DirContainer::DirList::value_type DirData; + + linearMerge(leftSide.dirs, rightSide.dirs, + [&](const DirData& dirLeft) //left only + { + DirPair& newDirMap = output.addSubDir(dirLeft.first); + this->fillOneSide(dirLeft.second, newDirMap); //recurse into subdirectories + }, + [&](const DirData& dirRight) //right only + { + DirPair& newDirMap = output.addSubDir(dirRight.first); + this->fillOneSide(dirRight.second, newDirMap); //recurse into subdirectories + }, + + [&](const DirData& dirLeft, const DirData& dirRight) //both sides + { + DirPair& newDirMap = output.addSubDir(dirLeft.first, dirRight.first, DIR_EQUAL); + if (dirLeft.first != dirRight.first) + newDirMap.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(newDirMap)); + + execute(dirLeft.second, dirRight.second, newDirMap); //recurse into subdirectories + }); +} + +//mark excluded directories (see fillBuffer()) + remove superfluous excluded subdirectories +void removeFilteredDirs(HierarchyObject& hierObj, const HardFilter& filterProc) +{ + //process subdirs recursively + for (DirPair& dirObj : hierObj.refSubDirs()) + { + dirObj.setActive(filterProc.passDirFilter(dirObj.getObjRelativeName(), nullptr)); //subObjMightMatch is always true in this context! + removeFilteredDirs(dirObj, filterProc); + } + + //remove superfluous directories -> note: this does not invalidate "std::vector& undefinedFiles", since we delete folders only + //and there is no side-effect for memory positions of FilePair and SymlinkPair thanks to zen::FixedList! + hierObj.refSubDirs().remove_if([](DirPair& dirObj) + { + return !dirObj.isActive() && + dirObj.refSubDirs ().empty() && + dirObj.refSubLinks().empty() && + dirObj.refSubFiles().empty(); + }); +} + + +//create comparison result table and fill category except for files existing on both sides: undefinedFiles and undefinedLinks are appended! +std::shared_ptr ComparisonBuffer::performComparison(const ResolvedFolderPair& fp, + const FolderPairCfg& fpCfg, + std::vector& undefinedFiles, + std::vector& undefinedLinks) const +{ + callback_.reportStatus(_("Generating file list...")); + callback_.forceUiRefresh(); + + auto getDirValue = [&](const Zstring& dirnameFmt) -> const DirectoryValue* + { + auto it = directoryBuffer.find(DirectoryKey(dirnameFmt, fpCfg.filter.nameFilter, fpCfg.handleSymlinks)); + return it != directoryBuffer.end() ? &it->second : nullptr; + }; + + const DirectoryValue* bufValueLeft = getDirValue(fp.dirnameLeft); + const DirectoryValue* bufValueRight = getDirValue(fp.dirnameRight); + + Zstring filterFailedRead; + auto filterAddFailedDirReads = [&filterFailedRead](const std::set& failedDirReads) //exclude directory child items only! + { + //note: relDirPf is empty for base dir, otherwise postfixed! e.g. "subdir\" + //an empty relDirPf is a pathological case at this point, since determineExistentDirs() already filtered missing base directories! + for (const Zstring& relDirPf : failedDirReads) + filterFailedRead += relDirPf + Zstr("?*\n"); + }; + auto filterAddFailedItemReads = [&filterFailedRead](const std::set& failedItemReads) //exclude item AND (potential) child items! + { + for (const Zstring& relItem : failedItemReads) + filterFailedRead += relItem + Zstr("\n"); + }; + + if (bufValueLeft ) filterAddFailedDirReads(bufValueLeft ->failedDirReads); + if (bufValueRight) filterAddFailedDirReads(bufValueRight->failedDirReads); + + if (bufValueLeft ) filterAddFailedItemReads(bufValueLeft ->failedItemReads); + if (bufValueRight) filterAddFailedItemReads(bufValueRight->failedItemReads); + + std::shared_ptr output = std::make_shared(fp.dirnameLeft, + bufValueLeft != nullptr, //dir existence must be checked only once: available iff buffer entry exists! + fp.dirnameRight, + bufValueRight != nullptr, + fpCfg.filter.nameFilter, + fpCfg.compareVar, + fileTimeTolerance); + //PERF_START; + MergeSides(undefinedFiles, undefinedLinks).execute(bufValueLeft ? bufValueLeft ->dirCont : DirContainer(), + bufValueRight ? bufValueRight->dirCont : DirContainer(), *output); + //PERF_STOP; + + //##################### in/exclude rows according to filtering ##################### + + //attention: some excluded directories are still in the comparison result! (see include filter handling!) + if (!fpCfg.filter.nameFilter->isNull()) + removeFilteredDirs(*output, *fpCfg.filter.nameFilter); //mark excluded directories (see fillBuffer()) + remove superfluous excluded subdirectories + + //apply soft filtering (hard filter already applied during traversal!) + addSoftFiltering(*output, fpCfg.filter.timeSizeFilter); + + //handle (user-ignored) traversing errors: just uncheck them, no need to physically delete them from both sides + if (!filterFailedRead.empty()) + addHardFiltering(*output, filterFailedRead); + + //################################################################################## + return output; +} +} + + +void zen::compare(size_t fileTimeTolerance, + xmlAccess::OptionalDialogs& warnings, + bool allowUserInteraction, + bool runWithBackgroundPriority, + bool createDirLocks, + std::unique_ptr& dirLocks, + const std::vector& cfgList, + FolderComparison& output, + ProcessCallback& callback) +{ + //specify process and resource handling priorities + std::unique_ptr backgroundPrio; + if (runWithBackgroundPriority) + try + { + backgroundPrio = make_unique(); //throw FileError + } + catch (const FileError& e) //not an error in this context + { + callback.reportInfo(e.toString()); //may throw! + } + + //prevent operating system going into sleep state + std::unique_ptr noStandby; + try + { + noStandby = make_unique(); //throw FileError + } + catch (const FileError& e) //not an error in this context + { + callback.reportInfo(e.toString()); //may throw! + } + + //PERF_START; + + callback.reportInfo(_("Starting comparison")); //indicator at the very beginning of the log to make sense of "total time" + + //init process: keep at beginning so that all gui elements are initialized properly + callback.initNewPhase(-1, 0, ProcessCallback::PHASE_SCANNING); //it's not known how many files will be scanned => -1 objects + + //-------------------some basic checks:------------------------------------------ + + std::vector resolvedPairs; + std::set existingDirs; + determineExistentDirs(cfgList, //in + allowUserInteraction, callback, + resolvedPairs, //out + existingDirs); //out + //directory existence only checked *once* to avoid race conditions! + if (resolvedPairs.size() != cfgList.size()) + throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo(__LINE__)); + + checkForIncompleteInput(resolvedPairs, warnings.warningInputFieldEmpty, callback); + checkFolderDependency (resolvedPairs, warnings.warningDependentFolders, callback); + + //list of directories that are *expected* to be existent (and need to be scanned)! + + //-------------------end of basic checks------------------------------------------ + + auto dirAvailable = [&](const Zstring& dirnameFmt) { return existingDirs.find(dirnameFmt) != existingDirs.end(); }; + + std::vector> totalWorkLoad; + for (size_t i = 0; i < cfgList.size(); ++i) + totalWorkLoad.push_back(std::make_pair(resolvedPairs[i], cfgList[i])); + + //lock (existing) directories before comparison + if (createDirLocks) + dirLocks = make_unique(existingDirs, warnings.warningDirectoryLockFailed, callback); + + try + { + //------------------- fill directory buffer --------------------------------------------------- + std::set dirsToRead; + + for (const auto& w : totalWorkLoad) + { + if (dirAvailable(w.first.dirnameLeft)) //only request *currently existing* directories: at this point user is aware that non-ex + empty string are seen as empty folder! + dirsToRead.insert(DirectoryKey(w.first.dirnameLeft, w.second.filter.nameFilter, w.second.handleSymlinks)); + if (dirAvailable(w.first.dirnameRight)) + dirsToRead.insert(DirectoryKey(w.first.dirnameRight, w.second.filter.nameFilter, w.second.handleSymlinks)); + } + + FolderComparison outputTmp; //write to output as a transaction! + + //reduce peak memory by restricting lifetime of ComparisonBuffer to have ended when loading potentially huge InSyncDir instance in redetermineSyncDirection() + { + //------------ traverse/read folders ----------------------------------------------------- + ComparisonBuffer cmpBuff(dirsToRead, fileTimeTolerance, callback); + + //process binary comparison as one junk + std::vector> workLoadByContent; + for (const auto& w : totalWorkLoad) + switch (w.second.compareVar) + { + case CMP_BY_TIME_SIZE: + break; + case CMP_BY_CONTENT: + workLoadByContent.push_back(w); + break; + } + std::list> outputByContent = cmpBuff.compareByContent(workLoadByContent); + + //write output in expected order + for (const auto& w : totalWorkLoad) + switch (w.second.compareVar) + { + case CMP_BY_TIME_SIZE: + outputTmp.push_back(cmpBuff.compareByTimeSize(w.first, w.second)); + break; + case CMP_BY_CONTENT: + assert(!outputByContent.empty()); + if (!outputByContent.empty()) + { + outputTmp.push_back(outputByContent.front()); + outputByContent.pop_front(); + } + break; + } + } + + assert(outputTmp.size() == cfgList.size()); + + //--------- set initial sync-direction -------------------------------------------------- + + for (auto j = begin(outputTmp); j != end(outputTmp); ++j) + { + const FolderPairCfg& fpCfg = cfgList[j - outputTmp.begin()]; + + callback.reportStatus(_("Calculating sync directions...")); + callback.forceUiRefresh(); + zen::redetermineSyncDirection(fpCfg.directionCfg, *j, + [&](const std::wstring& warning) { callback.reportWarning(warning, warnings.warningDatabaseError); }); + } + + //output is written only if everything was processed correctly + //note: output mustn't change during this process to be in sync with GUI grid view!!! + outputTmp.swap(output); + } + catch (const std::bad_alloc& e) + { + callback.reportFatalError(_("Out of memory.") + L" " + utfCvrtTo(e.what())); + //we need to maintain the "output.size() == cfgList.size()" contract in ALL cases! => abort + callback.abortProcessNow(); //throw X + } +} diff --git a/FreeFileSync/Source/comparison.h b/FreeFileSync/Source/comparison.h new file mode 100644 index 00000000..c3c31d1d --- /dev/null +++ b/FreeFileSync/Source/comparison.h @@ -0,0 +1,59 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef COMPARISON_H_INCLUDED +#define COMPARISON_H_INCLUDED + +#include "file_hierarchy.h" +#include "lib/process_xml.h" +#include "process_callback.h" +#include "lib/norm_filter.h" +#include "lib/lock_holder.h" + + +namespace zen +{ +struct FolderPairCfg +{ + FolderPairCfg(const Zstring& dirPhraseLeft, + const Zstring& dirPhraseRight, + CompareVariant cmpVar, + SymLinkHandling handleSymlinksIn, + const NormalizedFilter& filterIn, + const DirectionConfig& directCfg) : + dirnamePhraseLeft(dirPhraseLeft), + dirnamePhraseRight(dirPhraseRight), + compareVar(cmpVar), + handleSymlinks(handleSymlinksIn), + filter(filterIn), + directionCfg(directCfg) {} + + Zstring dirnamePhraseLeft; //unresolved directory names as entered by user! + Zstring dirnamePhraseRight; // + + CompareVariant compareVar; + SymLinkHandling handleSymlinks; + + NormalizedFilter filter; + + DirectionConfig directionCfg; +}; + +std::vector extractCompareCfg(const MainConfiguration& mainCfg); //fill FolderPairCfg and resolve folder pairs + +//FFS core routine: +void compare(size_t fileTimeTolerance, //max allowed file time deviation + xmlAccess::OptionalDialogs& warnings, + bool allowUserInteraction, + bool runWithBackgroundPriority, + bool createDirLocks, + std::unique_ptr& dirLocks, //out + const std::vector& cfgList, + FolderComparison& output, //out + ProcessCallback& callback); +} + +#endif // COMPARISON_H_INCLUDED diff --git a/FreeFileSync/Source/dll/IFileDialog_Vista/IFileDialog_Vista.vcxproj b/FreeFileSync/Source/dll/IFileDialog_Vista/IFileDialog_Vista.vcxproj new file mode 100644 index 00000000..009d38dd --- /dev/null +++ b/FreeFileSync/Source/dll/IFileDialog_Vista/IFileDialog_Vista.vcxproj @@ -0,0 +1,165 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + {E93269BB-36D4-4CE8-A7BE-F7A335D1D8E8} + ShadowDll + Win32Proj + + + + DynamicLibrary + Unicode + true + v120_xp + + + DynamicLibrary + Unicode + v120_xp + + + DynamicLibrary + Unicode + true + v120_xp + + + DynamicLibrary + Unicode + v120_xp + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + ..\..\..\Build\Bin\ + ..\..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + ..\..\..\Build\Bin\ + ..\..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + ..\..\..\Build\Bin\ + ..\..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + false + ..\..\..\Build\Bin\ + ..\..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + false + $(ProjectName)_$(Platform) + $(ProjectName)_$(Platform) + $(ProjectName)_$(Platform) + $(ProjectName)_$(Platform) + + + + Disabled + ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;IFILE_DIALOG_VISTA_DLL_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + Level4 + 4100;4996;4512 + C:\Data\Projects; + true + true + NoExtensions + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + $(IntDir)$(TargetName).lib + + + + + Disabled + ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;IFILE_DIALOG_VISTA_DLL_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + Level4 + 4100;4996;4512 + C:\Data\Projects; + true + true + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + $(IntDir)$(TargetName).lib + + + + + MaxSpeed + ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;IFILE_DIALOG_VISTA_DLL_EXPORTS;%(PreprocessorDefinitions) + MultiThreaded + Level4 + 4100;4996;4512 + Speed + C:\Data\Projects; + true + NoExtensions + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + UseLinkTimeCodeGeneration + $(IntDir)$(TargetName).lib + + + + + MaxSpeed + ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;IFILE_DIALOG_VISTA_DLL_EXPORTS;%(PreprocessorDefinitions) + MultiThreaded + Level4 + 4100;4996;4512 + Speed + C:\Data\Projects; + true + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + UseLinkTimeCodeGeneration + $(IntDir)$(TargetName).lib + + + + \ No newline at end of file diff --git a/FreeFileSync/Source/dll/IFileDialog_Vista/ifile_dialog.cpp b/FreeFileSync/Source/dll/IFileDialog_Vista/ifile_dialog.cpp new file mode 100644 index 00000000..f56df9e7 --- /dev/null +++ b/FreeFileSync/Source/dll/IFileDialog_Vista/ifile_dialog.cpp @@ -0,0 +1,108 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "ifile_dialog.h" +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include + +using namespace zen; + + +namespace +{ +bool showFolderPickerImpl(HWND ownerWindow, //throw SysError; return "false" if cancelled by user + const wchar_t* defaultFolder, //optional! + const GUID* persistenceGuid, // + std::wstring& selectedFolder) +{ + ComPtr fileDlg; + ZEN_COM_CHECK(::CoCreateInstance(CLSID_FileOpenDialog, //throw SysError + nullptr, + CLSCTX_ALL, + IID_PPV_ARGS(fileDlg.init()))); + + if (persistenceGuid) + ZEN_COM_CHECK(fileDlg->SetClientGuid(*persistenceGuid)); + + FILEOPENDIALOGOPTIONS dlgOptions = 0; + ZEN_COM_CHECK(fileDlg->GetOptions(&dlgOptions)); //throw SysError + ZEN_COM_CHECK(fileDlg->SetOptions(dlgOptions | FOS_PICKFOLDERS | FOS_NOVALIDATE | FOS_FORCEFILESYSTEM)); + + if (defaultFolder) //show last selection instead of top level if no default available + { + ComPtr folderItem; + ZEN_COM_CHECK(::SHCreateItemFromParsingName(defaultFolder, + nullptr, + IID_PPV_ARGS(folderItem.init()))); + ZEN_COM_CHECK(fileDlg->SetFolder(folderItem.get())); + } + + try + { + ZEN_COM_CHECK(fileDlg->Show(ownerWindow)); //may fail with: HRESULT_FROM_WIN32(ERROR_CANCELLED) + } + catch (const SysError&) { return false; } + + ComPtr folderItem; + ZEN_COM_CHECK(fileDlg->GetResult(folderItem.init())); + + LPWSTR folderPath = nullptr; + ZEN_COM_CHECK(folderItem->GetDisplayName(SIGDN_FILESYSPATH, &folderPath)); + ZEN_ON_SCOPE_EXIT(::CoTaskMemFree(folderPath)); + + selectedFolder = folderPath; + return true; +} + +wchar_t* allocString(const std::wstring& msg) //ownership passed +{ + auto tmp = new wchar_t [msg.size() + 1]; //std::bad_alloc ? + ::wmemcpy(tmp, msg.c_str(), msg.size() + 1); //include 0-termination + return tmp; +} +} + +//################################################################################################## + +void ifile::showFolderPicker(void* ownerWindow, + const wchar_t* defaultFolder, + const GuidProxy* guid, + wchar_t*& selectedFolder, + bool& cancelled, + wchar_t*& errorMsg) +{ + selectedFolder = nullptr; + cancelled = false; + errorMsg = nullptr; + + try + { + static_assert(sizeof(GuidProxy) == sizeof(GUID), ""); + GUID winGuid = {}; + if (guid) + ::memcpy(&winGuid, guid, sizeof(GUID)); + + std::wstring folderPath; + if (showFolderPickerImpl(static_cast(ownerWindow), defaultFolder, guid ? &winGuid : nullptr, folderPath)) //throw SysError + selectedFolder = allocString(folderPath); + else + cancelled = true; + } + catch (const SysError& e) + { + errorMsg = allocString(e.toString()); //std::bad_alloc ? + } +} + + +void ifile::freeString(const wchar_t* str) +{ + delete [] str; +} diff --git a/FreeFileSync/Source/dll/IFileDialog_Vista/ifile_dialog.h b/FreeFileSync/Source/dll/IFileDialog_Vista/ifile_dialog.h new file mode 100644 index 00000000..5b4dc532 --- /dev/null +++ b/FreeFileSync/Source/dll/IFileDialog_Vista/ifile_dialog.h @@ -0,0 +1,67 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef IFILE_DIALOG_HEADER_916743921746324 +#define IFILE_DIALOG_HEADER_916743921746324 + +#ifdef IFILE_DIALOG_VISTA_DLL_EXPORTS +#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllexport) +#else +#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllimport) +#endif + +#include + + +namespace ifile +{ +/*-------------- + |declarations| + --------------*/ + +//COM needs to be initialized before calling any of these functions! CoInitializeEx/CoUninitialize +//Requires Windows Vista and later + +typedef char GuidProxy[16]; //= Windows 128-bit GUID; we don't want to include "Guiddef.h" here! + +DLL_FUNCTION_DECLARATION +void showFolderPicker(void* ownerWindow, //in; ==HWND + const wchar_t* defaultFolder, //in, optional! + const GuidProxy* guid, //set nullptr by default: Windows stores dialog state (position, x, y coordinates, ect.) associated with the process executable name => use other GUID when needed + wchar_t*& selectedFolder, //out: call freeString() after use! + bool& cancelled, //out + wchar_t*& errorMsg); //out, optional: call freeString() after use! + +DLL_FUNCTION_DECLARATION +void freeString(const wchar_t* str); + +/*---------- + |typedefs| + ----------*/ +typedef bool (*FunType_showFolderPicker)(void* ownerWindow, + const wchar_t* defaultFolder, + const GuidProxy* guid, + wchar_t*& selectedFolder, + bool& cancelled, + wchar_t*& errorMsg); +typedef void (*FunType_freeString)(const wchar_t* str); + +/*-------------- + |symbol names| + --------------*/ +//(use const pointers to ensure internal linkage) +const char funName_showFolderPicker[] = "showFolderPicker"; +const char funName_freeString [] = "freeString"; + +/*--------------- + |library names| + ---------------*/ +inline const wchar_t* getDllName() { return zen::is64BitBuild ? L"IFileDialog_Vista_x64.dll" : L"IFileDialog_Vista_Win32.dll"; } +} + +#undef DLL_FUNCTION_DECLARATION + +#endif //IFILE_DIALOG_HEADER_916743921746324 diff --git a/FreeFileSync/Source/dll/ShadowCopy/Shadow_Windows7.vcxproj b/FreeFileSync/Source/dll/ShadowCopy/Shadow_Windows7.vcxproj new file mode 100644 index 00000000..0ff01a5d --- /dev/null +++ b/FreeFileSync/Source/dll/ShadowCopy/Shadow_Windows7.vcxproj @@ -0,0 +1,169 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + Shadow_Windows7 + {7E217D76-90A5-4B03-A6F8-E7C3ADD22901} + ShadowDll + Win32Proj + + + + DynamicLibrary + Unicode + true + false + v120_xp + + + DynamicLibrary + Unicode + v120_xp + + + DynamicLibrary + Unicode + true + v120_xp + + + DynamicLibrary + Unicode + v120_xp + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + ..\..\..\Build\Bin\ + ..\..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + ..\..\..\Build\Bin\ + ..\..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + ..\..\..\Build\Bin\ + ..\..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + false + ..\..\..\Build\Bin\ + ..\..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + false + $(ProjectName)_$(Platform) + $(ProjectName)_$(Platform) + $(ProjectName)_$(Platform) + $(ProjectName)_$(Platform) + + + + Disabled + ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + Level4 + 4100;4996;4512 + C:\Data\Projects;C:\Data\C++\boost + true + true + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + C:\Data\C++\Boost\stage\lib + $(IntDir)$(TargetName).lib + + + + + Disabled + EnableFastChecks + MultiThreadedDebug + Level4 + 4100;4996;4512 + C:\Data\Projects;C:\Data\C++\boost + ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions) + true + true + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + C:\Data\C++\Boost\stage_x64\lib + $(IntDir)$(TargetName).lib + + + + + MaxSpeed + ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions) + MultiThreaded + Level4 + Speed + 4100;4996;4512 + C:\Data\Projects;C:\Data\C++\boost + true + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + UseLinkTimeCodeGeneration + C:\Data\C++\Boost\stage\lib + $(IntDir)$(TargetName).lib + + + + + MaxSpeed + MultiThreaded + Level4 + Speed + 4100;4996;4512 + C:\Data\Projects;C:\Data\C++\boost + ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions) + true + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + UseLinkTimeCodeGeneration + C:\Data\C++\Boost\stage_x64\lib + $(IntDir)$(TargetName).lib + + + + + + + + + + + \ No newline at end of file diff --git a/FreeFileSync/Source/dll/ShadowCopy/shadow.cpp b/FreeFileSync/Source/dll/ShadowCopy/shadow.cpp new file mode 100644 index 00000000..adc7c5c2 --- /dev/null +++ b/FreeFileSync/Source/dll/ShadowCopy/shadow.cpp @@ -0,0 +1,199 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "shadow.h" +#include +#include +#include +#include +#include +#include + +#ifdef USE_SHADOW_XP +#include "xp/inc/vss.h" +#include "xp/inc/vswriter.h" +#include "xp/inc/vsbackup.h" + +#elif defined USE_SHADOW_2003 +#include "Server 2003/inc/vss.h" +#include "Server 2003/inc/vswriter.h" +#include "Server 2003/inc/vsbackup.h" + +#elif defined USE_SHADOW_WINDOWS7 +#include // +#include //part of Windows SDK for Windows 7 +#include // +#pragma comment(lib, "VssApi.lib") + +#else +#error adapt! +#endif + +using namespace zen; + + +struct shadow::ShadowData +{ + ShadowData(const ComPtr& backupComp, + const std::wstring& shadowVolume) : backupComp_(backupComp), shadowVolume_(shadowVolume) {} + + ComPtr backupComp_; + std::wstring shadowVolume_; +}; + + +namespace +{ +std::wstring formatVssError(HRESULT hr) //at least the one's from IVssBackupComponents::AddToSnapshotSet; return empty if no format found +{ + switch (hr) + { + case VSS_E_BAD_STATE: + return L"VSS_E_BAD_STATE"; + case VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED: + return L"VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED"; + case VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED: + return L"VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED"; + case VSS_E_OBJECT_NOT_FOUND: + return L"VSS_E_OBJECT_NOT_FOUND"; + case VSS_E_PROVIDER_NOT_REGISTERED: + return L"VSS_E_PROVIDER_NOT_REGISTERED"; + case VSS_E_PROVIDER_VETO: + return L"VSS_E_PROVIDER_VETO"; + case VSS_E_VOLUME_NOT_SUPPORTED: + return L"VSS_E_VOLUME_NOT_SUPPORTED"; + case VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER: + return L"VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER"; + case VSS_E_UNEXPECTED_PROVIDER_ERROR: + return L"VSS_E_UNEXPECTED_PROVIDER_ERROR"; + default: + return std::wstring(); + } +} + + +shadow::ShadowData createShadowCopy(const wchar_t* volumeName) //throw SysError +{ + ComPtr backupComp; + { + HRESULT hr = ::CreateVssBackupComponents(backupComp.init()); + if (FAILED(hr)) + { + if (hr == E_ACCESSDENIED) + throw SysError(formatComError(L"The caller does not have sufficient backup privileges or is not an administrator.", hr)); + throw SysError(formatComError(L"Error calling \"CreateVssBackupComponents\".", hr)); + } + } + + ZEN_COM_CHECK(backupComp->InitializeForBackup()); //throw SysError + + //SetContext() only required if different than the default, VSS_CTX_BACKUP; not implemented on XP!!! + //ZEN_COM_CHECK(backupComp->SetContext(VSS_CTX_BACKUP)); //throw SysError + + ZEN_COM_CHECK(backupComp->SetBackupState(false, false, VSS_BT_FULL)); //throw SysError + + + //the Shadow Copy Optimization Writer removes items it considers non-essential, + //http://msdn.microsoft.com/en-US/library/bb968827#shadow_copy_optimization_writer + //like the exclusions in HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\BackupRestore\FilesNotToSnapshot + //http://msdn.microsoft.com/en-us/library/aa819132%28v=vs.85%29.aspx + //Outlook *.ost files in particular: + //https://sourceforge.net/p/freefilesync/discussion/help/thread/722dcbfb + const VSS_ID disabledWriters[] = { { 0x4dc3bdd4, 0xab48, 0x4d07, { 0xad, 0xb0, 0x3b, 0xee, 0x29, 0x26, 0xfd, 0x7f } } }; //Shadow Copy Optimization Writer + { + HRESULT hr = backupComp->DisableWriterClasses(disabledWriters, 1); + if (FAILED(hr) && hr != E_NOTIMPL) //DisableWriterClasses() is not implemented on Windows XP, although MSDN documented otherwise! + throw SysError(formatComError(L"Error calling \"backupComp->DisableWriterClasses\".", hr)); + } + + + auto waitForComFuture = [](IVssAsync& fut) + { + ZEN_COM_CHECK(fut.Wait()); + + HRESULT hr = S_OK; + ZEN_COM_CHECK(fut.QueryStatus(&hr, nullptr)); //check if the async operation succeeded... + if (FAILED(hr)) + throw SysError(formatComError(L"Error calling \"fut->QueryStatus\".", hr)); + }; + + ComPtr gatherAsync; + ZEN_COM_CHECK(backupComp->GatherWriterMetadata(gatherAsync.init())); + waitForComFuture(*gatherAsync); //failure can happen if XP-version of VSS is used on Windows Vista (which needs at least VSS-Server2003 build) + + VSS_ID snapshotSetId = {}; + ZEN_COM_CHECK(backupComp->StartSnapshotSet(&snapshotSetId)); + ScopeGuard guardSnapShot = makeGuard([&] { backupComp->AbortBackup(); }); + //Quote: "This method must be called if a backup operation terminates after the creation of a + //shadow copy set with "StartSnapshotSet" and before "DoSnapshotSet" returns." + + VSS_ID SnapShotId = {}; + { + HRESULT hr = backupComp->AddToSnapshotSet(const_cast(volumeName), GUID_NULL, &SnapShotId); + if (FAILED(hr)) + { + if (hr == VSS_E_VOLUME_NOT_SUPPORTED) + throw SysError(L"Volume Shadow Copy Service is not supported on this volume!"); + const std::wstring vssError = formatVssError(hr); + if (!vssError.empty()) + throw SysError(L"Error calling \"backupComp->AddToSnapshotSet\": " + vssError); + else + throw SysError(formatComError(L"Error calling \"backupComp->AddToSnapshotSet\".", hr)); + } + } + + ComPtr prepareAsync; + ZEN_COM_CHECK(backupComp->PrepareForBackup(prepareAsync.init())); + waitForComFuture(*prepareAsync); + + ComPtr snapshotAsync; + ZEN_COM_CHECK(backupComp->DoSnapshotSet(snapshotAsync.init())); + guardSnapShot.dismiss(); + waitForComFuture(*snapshotAsync); + + VSS_SNAPSHOT_PROP props = {}; + ZEN_COM_CHECK(backupComp->GetSnapshotProperties(SnapShotId, &props)); + ZEN_ON_SCOPE_EXIT(::VssFreeSnapshotProperties(&props)); + + //finally: write volume name of newly created shadow copy + return shadow::ShadowData(backupComp, props.m_pwszSnapshotDeviceObject); +} + +boost::thread_specific_ptr lastErrorMessage; //use "thread_local" in C++11 +} + + +shadow::ShadowHandle shadow::createShadowCopy(const wchar_t* volumeName) +{ + try + { + ShadowData result = ::createShadowCopy(volumeName); //throw SysError + return new ShadowData(result); //shadow handle owned by caller! std::bad_alloc? + } + catch (const zen::SysError& e) + { + lastErrorMessage.reset(new std::wstring(e.toString())); + return nullptr; + } +} + + +const wchar_t* shadow::getShadowVolume(shadow::ShadowHandle handle) +{ + return handle ? handle->shadowVolume_.c_str() : nullptr; //better fail in client code than here! +} + + +void shadow::releaseShadowCopy(ShadowHandle handle) +{ + delete handle; +} + + +const wchar_t* shadow::getLastError() +{ + return !lastErrorMessage.get() ? L"" : lastErrorMessage->c_str(); +} diff --git a/FreeFileSync/Source/dll/ShadowCopy/shadow.h b/FreeFileSync/Source/dll/ShadowCopy/shadow.h new file mode 100644 index 00000000..e68b2655 --- /dev/null +++ b/FreeFileSync/Source/dll/ShadowCopy/shadow.h @@ -0,0 +1,94 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef SHADOWCOPY_H_14837413434 +#define SHADOWCOPY_H_14837413434 + +#ifdef SHADOWDLL_EXPORTS +#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllexport) +#else +#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllimport) +#endif + +#include +#include + + +class IVssBackupComponents; + +namespace shadow +{ +/*-------------- + |declarations| + --------------*/ + +//COM needs to be initialized before calling any of these functions! CoInitializeEx/CoUninitialize +struct ShadowData; +typedef ShadowData* ShadowHandle; + +//volumeName *must* end with "\" +DLL_FUNCTION_DECLARATION +ShadowHandle createShadowCopy(const wchar_t* volumeName); //returns nullptr on failure! + +//release the backupHandle after shadow copy is not needed anymore! +DLL_FUNCTION_DECLARATION +void releaseShadowCopy(ShadowHandle handle); + +DLL_FUNCTION_DECLARATION +const wchar_t* getShadowVolume(ShadowHandle handle); //never fails, returns shadowVolName, never ending with "\" + +//get last error message if any of the functions above fail +DLL_FUNCTION_DECLARATION +const wchar_t* getLastError(); //no nullptr check required! +//########################################################################################## + + +/*---------- + |typedefs| + ----------*/ +typedef ShadowHandle (*FunType_createShadowCopy )(const wchar_t* volumeName); +typedef void (*FunType_releaseShadowCopy)(ShadowHandle handle); +typedef const wchar_t* (*FunType_getShadowVolume )(ShadowHandle handle); +typedef const wchar_t* (*FunType_getLastError)(); + +/*-------------- + |symbol names| + --------------*/ +//(use const pointers to ensure internal linkage) +const char funName_createShadowCopy [] = "createShadowCopy"; +const char funName_releaseShadowCopy[] = "releaseShadowCopy"; +const char funName_getShadowVolume [] = "getShadowVolume"; +const char funName_getLastError [] = "getLastError"; +/*--------------- + |library names| + ---------------*/ + +inline +const wchar_t* getDllName() +{ + // distinguish a bunch of VSS builds: we use XP, Server 2003 and Server 2008 R2 implementations... + // VSS version and compatibility overview: http://msdn.microsoft.com/en-us/library/aa384627(VS.85).aspx + + if (zen::win7OrLater()) //Windows Server 2008 R2 or Windows 7 + return zen::is64BitBuild ? + L"Shadow_Windows7_x64.dll" : + L"Shadow_Windows7_Win32.dll"; + //else if (vistaOrLater()) -> skip Windows Server 2008 and Windows Vista + // ; + else if (zen::winServer2003orLater()) //Windows Server 2003 and Windows Server 2003 R2 + return zen::is64BitBuild ? + L"Shadow_Server2003_x64.dll" : + L"Shadow_Server2003_Win32.dll"; + else //Windows XP + return zen::is64BitBuild ? + L"Shadow_XP_x64.dll" : + L"Shadow_XP_Win32.dll"; +} +} + +#undef DLL_FUNCTION_DECLARATION + +#endif //SHADOWCOPY_H_14837413434 diff --git a/FreeFileSync/Source/dll/Taskbar_Seven/Taskbar_Seven.vcxproj b/FreeFileSync/Source/dll/Taskbar_Seven/Taskbar_Seven.vcxproj new file mode 100644 index 00000000..99c7fd65 --- /dev/null +++ b/FreeFileSync/Source/dll/Taskbar_Seven/Taskbar_Seven.vcxproj @@ -0,0 +1,167 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {78782859-7081-4C0E-BD6C-43DFDE634406} + ShadowDll + Win32Proj + Taskbar7 + + + + DynamicLibrary + Unicode + true + v120_xp + + + DynamicLibrary + Unicode + v120_xp + + + DynamicLibrary + Unicode + true + v120_xp + + + DynamicLibrary + Unicode + v120_xp + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + ..\..\..\Build\Bin\ + ..\..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + ..\..\..\Build\Bin\ + ..\..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + ..\..\..\Build\Bin\ + ..\..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + false + ..\..\..\Build\Bin\ + ..\..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + false + $(ProjectName)_$(Platform) + $(ProjectName)_$(Platform) + $(ProjectName)_$(Platform) + $(ProjectName)_$(Platform) + + + + Disabled + ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;TASKBAR_SEVEN_DLL_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + Level4 + 4100;4996;4512 + C:\Data\Projects; + true + true + NoExtensions + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + $(IntDir)$(TargetName).lib + + + + + Disabled + ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;TASKBAR_SEVEN_DLL_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + Level4 + 4100;4996;4512 + C:\Data\Projects; + true + true + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + $(IntDir)$(TargetName).lib + + + + + MaxSpeed + ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;TASKBAR_SEVEN_DLL_EXPORTS;%(PreprocessorDefinitions) + MultiThreaded + Level4 + 4100;4996;4512 + Speed + C:\Data\Projects; + true + NoExtensions + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + UseLinkTimeCodeGeneration + $(IntDir)$(TargetName).lib + + + + + MaxSpeed + ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;TASKBAR_SEVEN_DLL_EXPORTS;%(PreprocessorDefinitions) + MultiThreaded + Level4 + 4100;4996;4512 + Speed + C:\Data\Projects; + true + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + UseLinkTimeCodeGeneration + $(IntDir)$(TargetName).lib + + + + + + + + + + + \ No newline at end of file diff --git a/FreeFileSync/Source/dll/Taskbar_Seven/taskbar.cpp b/FreeFileSync/Source/dll/Taskbar_Seven/taskbar.cpp new file mode 100644 index 00000000..72be7016 --- /dev/null +++ b/FreeFileSync/Source/dll/Taskbar_Seven/taskbar.cpp @@ -0,0 +1,113 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "taskbar.h" +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include +#include + +#include + +using namespace zen; + + +namespace +{ +std::wstring lastErrorMessage; + + +ComPtr getInstance() +{ + ComPtr taskbarlist; + + HRESULT hr = ::CoCreateInstance(CLSID_TaskbarList, + nullptr, + CLSCTX_ALL, + IID_PPV_ARGS(taskbarlist.init())); + if (FAILED(hr)) + { + lastErrorMessage = formatComError(L"Error calling \"CoCreateInstance\".", hr); + return ComPtr(); + } + + return taskbarlist; +} +} +//################################################################################################## + + +bool tbseven::setStatus(void* hwnd, //HWND: window assciated to the taskbar icon + TaskBarStatus status) +{ + TBPFLAG flag = TBPF_NORMAL; + switch (status) + { + case STATUS_NOPROGRESS: + flag = TBPF_NOPROGRESS; + break; + case STATUS_INDETERMINATE: + flag = TBPF_INDETERMINATE; + break; + case STATUS_NORMAL: + flag = TBPF_NORMAL; + break; + case STATUS_ERROR: + flag = TBPF_ERROR; + break; + case STATUS_PAUSED: + flag = TBPF_PAUSED; + break; + } + + ComPtr taskbarlist = getInstance(); + if (!taskbarlist) //error msg already set + return false; + + HRESULT hr = taskbarlist->SetProgressState(static_cast(hwnd), //[in] HWND hwnd, + flag); //[in] TBPFLAG tbpFlags + if (FAILED(hr)) + { + lastErrorMessage = formatComError(L"Error calling \"SetProgressState\".", hr); + return false; + } + + return true; +} + + +bool tbseven::setProgress(void* hwnd, //HWND: window assciated to the taskbar icon + size_t current, + size_t total) +{ + ComPtr taskbarlist = getInstance(); + if (!taskbarlist) //error msg already set + return false; + + HRESULT hr = taskbarlist->SetProgressValue( + static_cast(hwnd), //[in] HWND hwnd, + current, //[in] ULONGLONG ullCompleted, + total); //[in] ULONGLONG ullTotal + if (FAILED(hr)) + { + lastErrorMessage = formatComError(L"Error calling \"SetProgressValue\".", hr); + return false; + } + + return true; +} + + +void tbseven::getLastError(wchar_t* buffer, size_t bufferSize) +{ + if (bufferSize > 0) + { + size_t endPos = lastErrorMessage.copy(buffer, bufferSize - 1); + buffer[endPos] = 0; + } +} diff --git a/FreeFileSync/Source/dll/Taskbar_Seven/taskbar.h b/FreeFileSync/Source/dll/Taskbar_Seven/taskbar.h new file mode 100644 index 00000000..4a158d1f --- /dev/null +++ b/FreeFileSync/Source/dll/Taskbar_Seven/taskbar.h @@ -0,0 +1,74 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef TASKBAR_SEVEN_DLL_H +#define TASKBAR_SEVEN_DLL_H + +#ifdef TASKBAR_SEVEN_DLL_EXPORTS +#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllexport) +#else +#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllimport) +#endif + +#include + + +namespace tbseven +{ +/*-------------- + |declarations| + --------------*/ + +enum TaskBarStatus +{ + STATUS_NOPROGRESS, + STATUS_INDETERMINATE, + STATUS_NORMAL, + STATUS_ERROR, + STATUS_PAUSED +}; + + +//COM needs to be initialized before calling any of these functions! CoInitializeEx/CoUninitialize + +DLL_FUNCTION_DECLARATION +bool setStatus(void* hwnd, //HWND: window assciated to the taskbar icon + TaskBarStatus status); + + +DLL_FUNCTION_DECLARATION +bool setProgress(void* hwnd, //HWND: window assciated to the taskbar icon + size_t current, + size_t total); + +//if any of the functions above returns 'false', this message returns last error +DLL_FUNCTION_DECLARATION +void getLastError(wchar_t* buffer, size_t bufferSize); + +/*---------- + |typedefs| + ----------*/ +typedef bool (*FunType_setStatus )(void* hwnd, TaskBarStatus status); +typedef bool (*FunType_setProgress )(void* hwnd, size_t current, size_t total); +typedef void (*FunType_getLastError)(wchar_t* buffer, size_t bufferSize); + +/*-------------- + |symbol names| + --------------*/ +//(use const pointers to ensure internal linkage) +const char funName_setStatus [] = "setStatus"; +const char funName_setProgress [] = "setProgress"; +const char funName_getLastError[] = "getLastError"; + +/*--------------- + |library names| + ---------------*/ +inline const wchar_t* getDllName() { return zen::is64BitBuild ? L"Taskbar7_x64.dll" : L"Taskbar7_Win32.dll"; } +} + +#undef DLL_FUNCTION_DECLARATION + +#endif //TASKBAR_SEVEN_DLL_H diff --git a/FreeFileSync/Source/dll/Thumbnail/Thumbnail.vcxproj b/FreeFileSync/Source/dll/Thumbnail/Thumbnail.vcxproj new file mode 100644 index 00000000..b54cf4f3 --- /dev/null +++ b/FreeFileSync/Source/dll/Thumbnail/Thumbnail.vcxproj @@ -0,0 +1,165 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {47AB4CD0-08E7-4F4C-8517-5B139957DB71} + ShadowDll + Win32Proj + + + + DynamicLibrary + Unicode + true + v120_xp + + + DynamicLibrary + Unicode + v120_xp + + + DynamicLibrary + Unicode + true + v120_xp + + + DynamicLibrary + Unicode + v120_xp + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + ..\..\..\Build\Bin\ + ..\..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + ..\..\..\Build\Bin\ + ..\..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + ..\..\..\Build\Bin\ + ..\..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + false + ..\..\..\Build\Bin\ + ..\..\..\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + false + $(ProjectName)_$(Platform) + $(ProjectName)_$(Platform) + $(ProjectName)_$(Platform) + $(ProjectName)_$(Platform) + + + + Disabled + ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;_DEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + Level4 + 4100;4996;4512 + C:\Data\Projects; + true + true + NoExtensions + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + $(IntDir)$(TargetName).lib + + + + + Disabled + ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + Level4 + 4100;4996;4512 + C:\Data\Projects; + true + true + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + $(IntDir)$(TargetName).lib + + + + + MaxSpeed + ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions) + MultiThreaded + Level4 + 4100;4996;4512 + Speed + C:\Data\Projects; + true + NoExtensions + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + UseLinkTimeCodeGeneration + $(IntDir)$(TargetName).lib + + + + + MaxSpeed + ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions) + MultiThreaded + Level4 + 4100;4996;4512 + Speed + C:\Data\Projects; + true + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + UseLinkTimeCodeGeneration + $(IntDir)$(TargetName).lib + + + + + + + + + + + \ No newline at end of file diff --git a/FreeFileSync/Source/dll/Thumbnail/thumbnail.cpp b/FreeFileSync/Source/dll/Thumbnail/thumbnail.cpp new file mode 100644 index 00000000..53c30bc3 --- /dev/null +++ b/FreeFileSync/Source/dll/Thumbnail/thumbnail.cpp @@ -0,0 +1,486 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "thumbnail.h" +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +#define STRICT_TYPED_ITEMIDS //better type safety for IDLists +#include + +#include +#include + +#include +#include +#include +#include +#include +//#include + +using namespace zen; + + +namespace +{ +thumb::ImageData* allocImageData(int width, int height) //throw SysError; return value always bound! +{ + ZEN_COM_ASSERT(width >= 0 && height >= 0); //throw SysError + + std::unique_ptr idata = make_unique(); + + idata->width = width; + idata->height = height; + idata->rgb = new unsigned char[width * height * 4]; + idata->alpha = idata->rgb + width * height * 3; + + return idata.release(); +} + +void releaseImageData_impl(const thumb::ImageData* id) +{ + if (id) + { + delete [] id->rgb; + delete id; + } +} + + +//caller takes ownership! +HICON createIconFromBitmap(HBITMAP bitmap) //throw SysError +{ + BITMAP bmpInfo = {}; + ZEN_COM_ASSERT(::GetObject(bitmap, //__in HGDIOBJ hgdiobj, + sizeof(bmpInfo), //__in int cbBuffer, + &bmpInfo)); //__out LPVOID lpvObject + //no documented extended error info + + HDC hScreenDC = ::GetDC(nullptr); + ZEN_COM_ASSERT(hScreenDC); //no documented extended error info + ZEN_ON_SCOPE_EXIT(::ReleaseDC(nullptr, hScreenDC)); + + HBITMAP bitmapMask = ::CreateCompatibleBitmap(hScreenDC, bmpInfo.bmWidth, bmpInfo.bmHeight); + ZEN_COM_ASSERT(bitmapMask); //no documented extended error info + ZEN_ON_SCOPE_EXIT(::DeleteObject(bitmapMask)); + + ICONINFO iconInfo = {}; + iconInfo.fIcon = true; + iconInfo.hbmColor = bitmap; + iconInfo.hbmMask = bitmapMask; + + HICON result = ::CreateIconIndirect(&iconInfo); + if (!result) throw SysError(formatSystemError(L"CreateIconIndirect", getLastError())); + return result; +} + + +//caller takes ownership! +thumb::ImageData* convertToImageData(HBITMAP bmp) //throw SysError +{ + //GetDIBits ???? + + BITMAP bmpInfo = {}; + ZEN_COM_ASSERT(::GetObject(bmp, //__in HGDIOBJ hgdiobj, + sizeof(BITMAP), //__in int cbBuffer, + &bmpInfo)); //__out LPVOID lpvObject + + HDC hScreenDC = ::GetDC(nullptr); + ZEN_COM_ASSERT(hScreenDC); //no documented extended error info + ZEN_ON_SCOPE_EXIT(::ReleaseDC(nullptr, hScreenDC)); + + //32-bit RGB with alpha channel support + BITMAPV5HEADER bi = {}; + bi.bV5Size = sizeof(bi); + bi.bV5Width = bmpInfo.bmWidth; + bi.bV5Height = -bmpInfo.bmHeight; //negative for top left origin + bi.bV5Planes = 1; + bi.bV5BitCount = 32; + bi.bV5Compression = BI_BITFIELDS; + bi.bV5AlphaMask = 0xFF000000; + bi.bV5RedMask = 0x00FF0000; + bi.bV5GreenMask = 0x0000FF00; + bi.bV5BlueMask = 0x000000FF; + unsigned char* bitsRgbBmp = nullptr; + + HBITMAP rgbBmp = ::CreateDIBSection(hScreenDC, //_In_ HDC hdc, + reinterpret_cast(&bi), //_In_ const BITMAPINFO *pbmi, + DIB_RGB_COLORS, //_In_ UINT iUsage, + reinterpret_cast(&bitsRgbBmp), //_Out_ VOID **ppvBits, + nullptr, //_In_ HANDLE hSection, + 0); //_In_ DWORD dwOffset + ZEN_COM_ASSERT(rgbBmp); //no documented extended error info + ZEN_ON_SCOPE_EXIT(::DeleteObject(rgbBmp);); + ZEN_COM_ASSERT(bitsRgbBmp); //check after rgbBmp is owned by us + + HDC memDCSrc = ::CreateCompatibleDC(hScreenDC); + ZEN_COM_ASSERT(memDCSrc); //no documented extended error info + ZEN_ON_SCOPE_EXIT(::DeleteDC(memDCSrc)); + + HDC memDCTrg = ::CreateCompatibleDC(hScreenDC); + ZEN_COM_ASSERT(memDCTrg); //no documented extended error info + ZEN_ON_SCOPE_EXIT(::DeleteDC(memDCTrg)); + + HGDIOBJ hgdiSrcOld = ::SelectObject(memDCSrc, bmp); + ZEN_COM_ASSERT(hgdiSrcOld); //no documented extended error info + ZEN_ON_SCOPE_EXIT(::SelectObject(memDCSrc, hgdiSrcOld)); + + HGDIOBJ hgdiTrgOld = ::SelectObject(memDCTrg, rgbBmp); + ZEN_COM_ASSERT(hgdiTrgOld); //no documented extended error info + ZEN_ON_SCOPE_EXIT(::SelectObject(memDCTrg, hgdiTrgOld)); + + if (!::BitBlt(memDCTrg, //_In_ HDC hdcDest, + 0, //_In_ int nXDest, + 0, //_In_ int nYDest, + bmpInfo.bmWidth, //_In_ int nWidth, + bmpInfo.bmHeight, //_In_ int nHeight, + memDCSrc, //_In_ HDC hdcSrc, + 0, //_In_ int nXSrc, + 0, //_In_ int nYSrc, + SRCCOPY)) //_In_ DWORD dwRop + throw SysError(formatSystemError(L"BitBlt", getLastError())); + + //CreateDIBSection: "Access to the bitmap must be synchronized. [...]. This applies to any use of the pointer to the bitmap bit values." + /*bool rv = */ + ::GdiFlush(); + + thumb::ImageData* imgOut = allocImageData(bmpInfo.bmWidth, bmpInfo.bmHeight); //throw SysError + ScopeGuard guardImgData = zen::makeGuard([&] { releaseImageData_impl(imgOut); }); + + unsigned char* rgbPtr = imgOut->rgb; + unsigned char* alphaPtr = imgOut->alpha; + + for (int i = 0; i < bmpInfo.bmWidth * bmpInfo.bmHeight; ++i) + { + unsigned char b = *bitsRgbBmp++; + unsigned char g = *bitsRgbBmp++; + unsigned char r = *bitsRgbBmp++; + unsigned char a = *bitsRgbBmp++; + + *rgbPtr++ = r; + *rgbPtr++ = g; + *rgbPtr++ = b; + *alphaPtr++ = a; + } + + guardImgData.dismiss(); + return imgOut; +} + + +//caller takes ownership! +const thumb::ImageData* getThumbnail_impl(const wchar_t* filename, int requestedSize) //throw SysError +{ + const std::wstring filenameStr(filename); + + ComPtr desktopFolder; + ZEN_COM_CHECK(::SHGetDesktopFolder(desktopFolder.init())); //throw SysError + ZEN_COM_ASSERT(desktopFolder); //throw SysError -> better safe than sorry? + + PIDLIST_RELATIVE pidlFolder = nullptr; + { + std::wstring pathName = beforeLast(filenameStr, L'\\'); + ZEN_COM_CHECK(desktopFolder->ParseDisplayName(nullptr, // [in] HWND hwnd, + nullptr, // [in] IBindCtx *pbc, + const_cast(pathName.c_str()), // [in] LPWSTR pszDisplayName, + nullptr, // [out] ULONG *pchEaten, + &pidlFolder, // [out] PIDLIST_RELATIVE* ppidl, + nullptr)); // [in, out] ULONG *pdwAttributes + } + ZEN_COM_ASSERT(pidlFolder); + ZEN_ON_SCOPE_EXIT(::ILFree(pidlFolder)); //older version: ::CoTaskMemFree + + ComPtr imageFolder; + ZEN_COM_CHECK(desktopFolder->BindToObject(pidlFolder, // [in] PCUIDLIST_RELATIVE pidl, + nullptr, // [in] IBindCtx *pbc, + IID_PPV_ARGS(imageFolder.init()))); + ZEN_COM_ASSERT(imageFolder); + + PIDLIST_RELATIVE pidImage = nullptr; + { + std::wstring shortName = afterLast(filenameStr, L'\\'); + ZEN_COM_CHECK(imageFolder->ParseDisplayName(nullptr, // [in] HWND hwnd, + nullptr, // [in] IBindCtx *pbc, + const_cast(shortName.c_str()), // [in] LPWSTR pszDisplayName, + nullptr, // [out] ULONG *pchEaten, + &pidImage, // [out] PIDLIST_RELATIVE *ppidl, + nullptr)); // [in, out] ULONG *pdwAttributes + } + ZEN_COM_ASSERT(pidImage); + ZEN_ON_SCOPE_EXIT(::ILFree(pidImage)); //older version: ::CoTaskMemFree + + ComPtr extractImage; + ZEN_COM_CHECK(imageFolder->GetUIObjectOf(nullptr, // [in] HWND hwndOwner, + 1, // [in] UINT cidl, + reinterpret_cast(&pidImage), // [in] PCUITEMID_CHILD_ARRAY apidl, + //this is where STRICT_TYPED_ITEMIDS gets us ;) + IID_IExtractImage, // [in] REFIID riid, + nullptr, // [in, out] UINT *rgfReserved, + reinterpret_cast(extractImage.init()))); // [out] void **ppv + ZEN_COM_ASSERT(extractImage); + + { + wchar_t pathBuffer[MAX_PATH]; + DWORD priority = 0; + const SIZE prgSize = { requestedSize, requestedSize }; + DWORD clrDepth = 32; //"recommended color depth" + DWORD flags = IEIFLAG_SCREEN | IEIFLAG_OFFLINE; + + ZEN_COM_CHECK(extractImage->GetLocation(pathBuffer, // [out] LPWSTR pszPathBuffer, + MAX_PATH, // [in] DWORD cchMax, + &priority, // [out] DWORD *pdwPriority, + &prgSize, // [in] const SIZE *prgSize, + clrDepth, // [in] DWORD dwRecClrDepth, + &flags)); // [in, out] DWORD *pdwFlags + } + + HBITMAP bitmap = nullptr; + ZEN_COM_CHECK(extractImage->Extract(&bitmap)); + ZEN_COM_ASSERT(bitmap); + ZEN_ON_SCOPE_EXIT(::DeleteObject(bitmap)); + + return convertToImageData(bitmap); //throw SysError, pass ownership +} + + +const bool wereVistaOrLater = vistaOrLater(); //thread-safety: init at startup + +//caller takes ownership! +const thumb::ImageData* getIconByIndex_impl(int iconIndex, thumb::IconSizeType st) //throw SysError +{ + //Note: + //- using IExtractIcon::Extract is *no* alternative, just as ::SHGetFileInfo(), it only supports small (16x16) and large (32x32) icons + //- IShellItemImageFactory::GetImage requires Vista or later + + using namespace thumb; + int requestedSize = 16; + int shilIconType = SHIL_SMALL; //16x16, size can be customized by the user. + { + if (!wereVistaOrLater && //XP doesn't have jumbo icons + (st == ICON_SIZE_128 || + st == ICON_SIZE_256)) + st = ICON_SIZE_48; + + switch (st) + { + case ICON_SIZE_16: + break; + case ICON_SIZE_32: + requestedSize = 32; + shilIconType = SHIL_LARGE; //32x32, may be 48x48 if "Use large icon" option is set in Display Properties + break; + case ICON_SIZE_48: + requestedSize = 48; + shilIconType = SHIL_EXTRALARGE; //48x48, size can be customized by the user. + break; + case ICON_SIZE_128: + requestedSize = 128; + shilIconType = SHIL_JUMBO; //256x256 pixels -> scale down! + break; + case ICON_SIZE_256: + requestedSize = 256; + shilIconType = SHIL_JUMBO; //256x256 pixels; Vista and later only + break; + } + } + + ComPtr imageList; //perf: 0,12 s only to get the image list + ZEN_COM_CHECK(::SHGetImageList(shilIconType, //__in int iImageList, + IID_PPV_ARGS(imageList.init()))); + ZEN_COM_ASSERT(imageList); + + int srcWidth = 0; + int srcHeight = 0; + ZEN_COM_CHECK(imageList->GetIconSize(&srcWidth, &srcHeight)); + + int targetWidth = srcWidth; + int targetHeight = srcHeight; + bool needDownScale = false; //scale down if required (e.g Vista Jumbo icons/user-customized icon sizes) + + ZEN_COM_ASSERT(srcWidth > 0 && srcHeight > 0 && requestedSize > 0); + + const int maxExtent = std::max(srcWidth, srcHeight); + if (requestedSize < maxExtent) + { + needDownScale = true; + targetWidth = srcWidth * requestedSize / maxExtent; + targetHeight = srcHeight * requestedSize / maxExtent; + } + + HDC hScreenDC = ::GetDC(nullptr); + ZEN_COM_ASSERT(hScreenDC); //no documented extended error info + ZEN_ON_SCOPE_EXIT(::ReleaseDC(nullptr, hScreenDC)); + + auto createRGBDib = [&](unsigned char** rawBits) -> HBITMAP + { + BITMAPINFO bi = {}; + bi.bmiHeader.biSize = sizeof(bi); + bi.bmiHeader.biWidth = targetWidth; + bi.bmiHeader.biHeight = -targetHeight; //negative for top left origin + bi.bmiHeader.biPlanes = 1; + bi.bmiHeader.biBitCount = 24; //we don't want an alpha channel + bi.bmiHeader.biCompression = BI_RGB; + + return ::CreateDIBSection(hScreenDC, //_In_ HDC hdc, + &bi, //_In_ const BITMAPINFO *pbmi, + DIB_RGB_COLORS, //_In_ UINT iUsage, + reinterpret_cast(rawBits), //_Out_ VOID **ppvBits, + nullptr, //_In_ HANDLE hSection, + 0); //_In_ DWORD dwOffset + }; + + unsigned char* bitsBlackBg = nullptr; + HBITMAP bmpBlackBg = createRGBDib(&bitsBlackBg); + ZEN_COM_ASSERT(bmpBlackBg); //no documented extended error info + ZEN_ON_SCOPE_EXIT(::DeleteObject(bmpBlackBg);); + ZEN_COM_ASSERT(bitsBlackBg); //check after bmpBlackBg is owned by us + + HDC memDC = ::CreateCompatibleDC(hScreenDC); + ZEN_COM_ASSERT(memDC); //no documented extended error info + ZEN_ON_SCOPE_EXIT(::DeleteDC(memDC)); + + HGDIOBJ hgdiOld = ::SelectObject(memDC, bmpBlackBg); + ZEN_COM_ASSERT(hgdiOld); //no documented extended error info + ZEN_ON_SCOPE_EXIT(::SelectObject(memDC, hgdiOld)); + + IMAGELISTDRAWPARAMS drawParams = {}; + drawParams.cbSize = sizeof(drawParams); + drawParams.hdcDst = memDC; + drawParams.i = iconIndex; + drawParams.rgbBk = 0x000000; //black + drawParams.fStyle = ILD_NORMAL; + //other flags: http://msdn.microsoft.com/en-us/library/windows/desktop/bb775230(v=vs.85).aspx + + if (needDownScale) + { + drawParams.fStyle |= ILD_SCALE; + drawParams.cx = targetWidth; + drawParams.cy = targetHeight; + } + + //IDO_SHGIOI_LINK does not draw properly in some cases: + //Win7: draws link overlay *twice* if SHIL_JUMBO is requested, but icon does not have this size + //XP: drawing IDO_SHGIOI_LINK generally draws corrupted icons and links + //if (addShortcutOverlay) + //{ + // int linkOverlay = ::SHGetIconOverlayIndex(nullptr, IDO_SHGIOI_LINK); //-1 on error + // if (linkOverlay != -1) + // { + // //int imgIndex = 0; + // //if (SUCCEEDED(imageList->GetOverlayImage(linkOverlay, &imgIndex))) + // //{ + // // drawParams.i = imgIndex; + // // ZEN_COM_CHECK(imageList->Draw(&drawParams)); + // //} + + // drawParams.fStyle |= INDEXTOOVERLAYMASK(linkOverlay); + // } + //} + + ZEN_COM_CHECK(imageList->Draw(&drawParams)); + + //----------------------------------------------- + //we draw the icon twice on different backgrounds to extract the alpha channel: + //- IImageList::GetIcon doesn't properly render SHIL_JUMBO for icons that don't have jumbo sizes, but IImageList::Draw does! + //- minor: each HICON consumes 3 GDI handles + //- IImageList::Draw does not reliably support alpha channel on device context (Windows XP) + //- wxBitmap created from HBITMAP is very unreliable; often drawn incorrectly by wxDC::DrawBitmap => support wxImage instead + + unsigned char* bitsWhiteBg = nullptr; + HBITMAP bmpWhiteBg = createRGBDib(&bitsWhiteBg); + ZEN_COM_ASSERT(bmpWhiteBg); //no documented extended error info + ZEN_ON_SCOPE_EXIT(::DeleteObject(bmpWhiteBg)); + ZEN_COM_ASSERT(bitsWhiteBg); //check after bmpWhiteBg is owned by us + + HGDIOBJ hgdiOld2 = ::SelectObject(memDC, bmpWhiteBg); + ZEN_COM_ASSERT(hgdiOld2); //no documented extended error info + ZEN_ON_SCOPE_EXIT(::SelectObject(memDC, hgdiOld2)); + + drawParams.rgbBk = 0xFFFFFF; //white + + ZEN_COM_CHECK(imageList->Draw(&drawParams)); + + //##################################################################################### + + //"Access to the bitmap must be synchronized. [...]. This applies to any use of the pointer to the bitmap bit values." + /*bool rv = */ + ::GdiFlush(); + + ImageData* imgOut = allocImageData(targetWidth, targetHeight); //throw SysError + ScopeGuard guardImgData = zen::makeGuard([&] { releaseImageData_impl(imgOut); }); + + unsigned char* rgbPtr = imgOut->rgb; + unsigned char* alphaPtr = imgOut->alpha; + + for (int i = 0; i < targetWidth * targetHeight; ++i) + { + unsigned char b_black = *bitsBlackBg++; + unsigned char g_black = *bitsBlackBg++; + unsigned char r_black = *bitsBlackBg++; + + unsigned char b_white = *bitsWhiteBg++; + unsigned char g_white = *bitsWhiteBg++; + unsigned char r_white = *bitsWhiteBg++; + + const int tmp = 255 + r_black - r_white + //mixed mode arithmetics! + 255 + g_black - g_white + + 255 + b_black - b_white; + unsigned char alpha = static_cast(numeric::confineCpy(tmp / 3, 0, 255)); + + auto calcColor = [&](unsigned char c_black, unsigned char c_white) + { + return static_cast(tmp == 0 ? 0 : numeric::confineCpy + (255 * (3 * (-255 + c_white + c_black) + tmp) / (2 * tmp), //mixed mode arithmetics! + 0, 255)); + }; + + *rgbPtr++ = calcColor(r_black, r_white); + *rgbPtr++ = calcColor(g_black, g_white); + *rgbPtr++ = calcColor(b_black, b_white); + *alphaPtr++ = alpha; + } + + guardImgData.dismiss(); + return imgOut; +} +} + + +const thumb::ImageData* thumb::getThumbnail(const wchar_t* filename, int requestedSize) //return 0 on failure, caller takes ownership! +{ + try + { + return getThumbnail_impl(filename, requestedSize); //throw SysError + } + catch (const SysError&) + { + return nullptr; + } +} + + +const thumb::ImageData* thumb::getIconByIndex(int iconIndex, thumb::IconSizeType st) //return 0 on failure, caller takes ownership! +{ + try + { + return getIconByIndex_impl(iconIndex, st); //throw SysError + } + catch (const SysError&) + { + return nullptr; + } +} + + +void thumb::releaseImageData(const thumb::ImageData* id) +{ + releaseImageData_impl(id); +} diff --git a/FreeFileSync/Source/dll/Thumbnail/thumbnail.h b/FreeFileSync/Source/dll/Thumbnail/thumbnail.h new file mode 100644 index 00000000..307fc7cc --- /dev/null +++ b/FreeFileSync/Source/dll/Thumbnail/thumbnail.h @@ -0,0 +1,85 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef THUMBNAIL_DLL_HEADER_487108471324 +#define THUMBNAIL_DLL_HEADER_487108471324 + +#ifdef THUMBNAIL_DLL_EXPORTS +#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllexport) +#else +#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllimport) +#endif + +#include +//#include + +namespace thumb +{ +/* +PREREQUISITES: + +1. COM must be initialized for the current thread via ::CoInitialize(nullptr) or ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED), + but NOT ::CoInitializeEx(nullptr, COINIT_MULTITHREADED) -> internal access violation crash! +2. call ::FileIconInit() on app start to remedy obscure errors like SHELL_E_WRONG_BITDEPTH (0x80270102) + for certain file types, e.g. lnk, mpg - required on Windows 7 see http://msdn.microsoft.com/en-us/library/ms683212(v=VS.85).aspx +*/ + +/*-------------- + |declarations| + --------------*/ +struct ImageData //consider alignment! +{ + unsigned char* rgb; //rgb-byte order for use with wxImage + unsigned char* alpha; + int width; + int height; +}; + +DLL_FUNCTION_DECLARATION +const ImageData* getThumbnail(const wchar_t* filename, int requestedSize); //return nullptr on failure, release after use +//Note: not all file types support thumbnails! implement fallback to file icon! + +enum IconSizeType +{ + ICON_SIZE_16, + ICON_SIZE_32, + ICON_SIZE_48, + ICON_SIZE_128, + ICON_SIZE_256, +}; +//"iconIndex" as returned by ::SHGetFileInfo() +DLL_FUNCTION_DECLARATION +const ImageData* getIconByIndex(int iconIndex, IconSizeType st); //return nullptr on failure, release after use + +DLL_FUNCTION_DECLARATION +void releaseImageData(const ImageData* id); + + +/*---------- + |typedefs| + ----------*/ +typedef const ImageData* (*FunType_getThumbnail )(const wchar_t* filename, int requestedSize); +typedef const ImageData* (*FunType_getIconByIndex )(int iconIndex, IconSizeType st); +typedef void (*FunType_releaseImageData)(const ImageData* id); + + +/*-------------- + |symbol names| + --------------*/ +//(use const pointers to ensure internal linkage) +const char funName_getThumbnail [] = "getThumbnail"; +const char funName_getIconByIndex [] = "getIconByIndex"; +const char funName_releaseImageData[] = "releaseImageData"; + +/*--------------- + |library names| + ---------------*/ +inline const wchar_t* getDllName() { return zen::is64BitBuild ? L"Thumbnail_x64.dll" : L"Thumbnail_Win32.dll"; } +} + +#undef DLL_FUNCTION_DECLARATION + +#endif //THUMBNAIL_DLL_HEADER_487108471324 diff --git a/FreeFileSync/Source/file_hierarchy.cpp b/FreeFileSync/Source/file_hierarchy.cpp new file mode 100644 index 00000000..780f05de --- /dev/null +++ b/FreeFileSync/Source/file_hierarchy.cpp @@ -0,0 +1,409 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "file_hierarchy.h" +#include +#include +#include + +using namespace zen; + + +void HierarchyObject::removeEmptyRec() +{ + bool emptyExisting = false; + auto isEmpty = [&](const FileSystemObject& fsObj) -> bool + { + const bool objEmpty = fsObj.isEmpty(); + if (objEmpty) + emptyExisting = true; + return objEmpty; + }; + + refSubFiles().remove_if(isEmpty); + refSubLinks().remove_if(isEmpty); + refSubDirs ().remove_if(isEmpty); + + if (emptyExisting) //notify if actual deletion happened + notifySyncCfgChanged(); //mustn't call this in ~FileSystemObject(), since parent, usually a DirPair, is already partially destroyed and existing as a pure HierarchyObject! + + for (DirPair& subDir : refSubDirs()) + subDir.removeEmptyRec(); //recurse +} + +namespace +{ +SyncOperation getIsolatedSyncOperation(CompareFilesResult cmpResult, + bool selectedForSynchronization, + SyncDirection syncDir, + bool hasDirConflict) //perf: std::wstring was wasteful here +{ + assert(!hasDirConflict || syncDir == SyncDirection::NONE); + + if (!selectedForSynchronization) + return cmpResult == FILE_EQUAL ? + SO_EQUAL : + SO_DO_NOTHING; + + switch (cmpResult) + { + case FILE_LEFT_SIDE_ONLY: + switch (syncDir) + { + case SyncDirection::LEFT: + return SO_DELETE_LEFT; //delete files on left + case SyncDirection::RIGHT: + return SO_CREATE_NEW_RIGHT; //copy files to right + case SyncDirection::NONE: + return hasDirConflict ? SO_UNRESOLVED_CONFLICT : SO_DO_NOTHING; + } + break; + + case FILE_RIGHT_SIDE_ONLY: + switch (syncDir) + { + case SyncDirection::LEFT: + return SO_CREATE_NEW_LEFT; //copy files to left + case SyncDirection::RIGHT: + return SO_DELETE_RIGHT; //delete files on right + case SyncDirection::NONE: + return hasDirConflict ? SO_UNRESOLVED_CONFLICT : SO_DO_NOTHING; + } + break; + + case FILE_LEFT_NEWER: + case FILE_RIGHT_NEWER: + case FILE_DIFFERENT: + case FILE_CONFLICT: + switch (syncDir) + { + case SyncDirection::LEFT: + return SO_OVERWRITE_LEFT; //copy from right to left + case SyncDirection::RIGHT: + return SO_OVERWRITE_RIGHT; //copy from left to right + case SyncDirection::NONE: + return hasDirConflict ? SO_UNRESOLVED_CONFLICT : SO_DO_NOTHING; + } + break; + + case FILE_DIFFERENT_METADATA: + switch (syncDir) + { + case SyncDirection::LEFT: + return SO_COPY_METADATA_TO_LEFT; + case SyncDirection::RIGHT: + return SO_COPY_METADATA_TO_RIGHT; + case SyncDirection::NONE: + return hasDirConflict ? SO_UNRESOLVED_CONFLICT : SO_DO_NOTHING; + } + break; + + case FILE_EQUAL: + assert(syncDir == SyncDirection::NONE); + return SO_EQUAL; + } + + assert(false); + return SO_DO_NOTHING; //dummy +} + + +template inline +bool hasDirectChild(const HierarchyObject& hierObj, Predicate p) +{ + return std::any_of(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), p) || + std::any_of(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(), p) || + std::any_of(hierObj.refSubDirs(). begin(), hierObj.refSubDirs(). end(), p); +} +} + + +SyncOperation FileSystemObject::testSyncOperation(SyncDirection testSyncDir) const //semantics: "what if"! assumes "active, no conflict, no recursion (directory)! +{ + return getIsolatedSyncOperation(getCategory(), true, testSyncDir, false); +} + + +SyncOperation FileSystemObject::getSyncOperation() const +{ + return getIsolatedSyncOperation(getCategory(), selectedForSynchronization, getSyncDir(), syncDirConflict.get() != nullptr); + //no *not* make a virtual call to testSyncOperation()! See FilePair::testSyncOperation()! <- better not implement one in terms of the other!!! +} + + +//SyncOperation DirPair::testSyncOperation() const -> no recursion: we do NOT want to consider child elements when testing! + + +SyncOperation DirPair::getSyncOperation() const +{ + if (!syncOpUpToDate) + { + syncOpUpToDate = true; + //redetermine... + + //suggested operation *not* considering child elements + syncOpBuffered = FileSystemObject::getSyncOperation(); + + //action for child elements may occassionally have to overwrite parent task: + switch (syncOpBuffered) + { + case SO_OVERWRITE_LEFT: + case SO_OVERWRITE_RIGHT: + case SO_MOVE_LEFT_SOURCE: + case SO_MOVE_LEFT_TARGET: + case SO_MOVE_RIGHT_SOURCE: + case SO_MOVE_RIGHT_TARGET: + assert(false); + case SO_CREATE_NEW_LEFT: + case SO_CREATE_NEW_RIGHT: + case SO_COPY_METADATA_TO_LEFT: + case SO_COPY_METADATA_TO_RIGHT: + case SO_EQUAL: + break; //take over suggestion, no problem for child-elements + case SO_DELETE_LEFT: + case SO_DELETE_RIGHT: + case SO_DO_NOTHING: + case SO_UNRESOLVED_CONFLICT: + if (isEmpty()) + { + //1. if at least one child-element is to be created, make sure parent folder is created also + //note: this automatically fulfills "create parent folders even if excluded"; + //see http://sourceforge.net/tracker/index.php?func=detail&aid=2628943&group_id=234430&atid=1093080 + if (hasDirectChild(*this, + [](const FileSystemObject& fsObj) -> bool + { + const SyncOperation op = fsObj.getSyncOperation(); + return op == SO_CREATE_NEW_LEFT || + op == SO_MOVE_LEFT_TARGET; + })) + syncOpBuffered = SO_CREATE_NEW_LEFT; + //2. cancel parent deletion if only a single child is not also scheduled for deletion + else if (syncOpBuffered == SO_DELETE_RIGHT && + hasDirectChild(*this, + [](const FileSystemObject& fsObj) -> bool + { + if (fsObj.isEmpty()) + return false; //fsObj may already be empty because it once contained a "move source" + const SyncOperation op = fsObj.getSyncOperation(); + return op != SO_DELETE_RIGHT && + op != SO_MOVE_RIGHT_SOURCE; + })) + syncOpBuffered = SO_DO_NOTHING; + } + else if (isEmpty()) + { + if (hasDirectChild(*this, + [](const FileSystemObject& fsObj) -> bool + { + const SyncOperation op = fsObj.getSyncOperation(); + return op == SO_CREATE_NEW_RIGHT || + op == SO_MOVE_RIGHT_TARGET; + })) + syncOpBuffered = SO_CREATE_NEW_RIGHT; + else if (syncOpBuffered == SO_DELETE_LEFT && + hasDirectChild(*this, + [](const FileSystemObject& fsObj) -> bool + { + if (fsObj.isEmpty()) + return false; + const SyncOperation op = fsObj.getSyncOperation(); + return op != SO_DELETE_LEFT && + op != SO_MOVE_LEFT_SOURCE; + })) + syncOpBuffered = SO_DO_NOTHING; + } + break; + } + } + return syncOpBuffered; +} + + +inline //it's private! +SyncOperation FilePair::applyMoveOptimization(SyncOperation op) const +{ + /* + check whether we can optimize "create + delete" via "move": + 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_TARGET; + else if (op == SO_DELETE_LEFT && + opRef == SO_CREATE_NEW_LEFT) + op = SO_MOVE_LEFT_SOURCE; + else if (op == SO_CREATE_NEW_RIGHT && + opRef == SO_DELETE_RIGHT) + op = SO_MOVE_RIGHT_TARGET; + else if (op == SO_DELETE_RIGHT && + opRef == SO_CREATE_NEW_RIGHT) + op = SO_MOVE_RIGHT_SOURCE; + } + return op; +} + + +SyncOperation FilePair::testSyncOperation(SyncDirection testSyncDir) const +{ + return applyMoveOptimization(FileSystemObject::testSyncOperation(testSyncDir)); +} + + +SyncOperation FilePair::getSyncOperation() const +{ + return applyMoveOptimization(FileSystemObject::getSyncOperation()); +} + + +std::wstring zen::getCategoryDescription(CompareFilesResult cmpRes) +{ + switch (cmpRes) + { + case FILE_LEFT_SIDE_ONLY: + return _("Item exists on left side only"); + case FILE_RIGHT_SIDE_ONLY: + return _("Item exists on right side only"); + case FILE_LEFT_NEWER: + return _("Left side is newer"); + case FILE_RIGHT_NEWER: + return _("Right side is newer"); + case FILE_DIFFERENT: + return _("Items have different content"); + case FILE_EQUAL: + return _("Both sides are equal"); + case FILE_DIFFERENT_METADATA: + return _("Items differ in attributes only"); + case FILE_CONFLICT: + return _("Conflict/item cannot be categorized"); + } + assert(false); + return std::wstring(); +} + + +std::wstring zen::getCategoryDescription(const FileSystemObject& fsObj) +{ + const CompareFilesResult cmpRes = fsObj.getCategory(); + if (cmpRes == FILE_CONFLICT || + cmpRes == FILE_DIFFERENT_METADATA) + return fsObj.getCatExtraDescription(); + + return getCategoryDescription(cmpRes); +} + + +std::wstring zen::getSyncOpDescription(SyncOperation op) +{ + switch (op) + { + case SO_CREATE_NEW_LEFT: + return _("Copy new item to left"); + case SO_CREATE_NEW_RIGHT: + return _("Copy new item to right"); + case SO_DELETE_LEFT: + return _("Delete left item"); + case SO_DELETE_RIGHT: + return _("Delete right item"); + case SO_MOVE_LEFT_SOURCE: + case SO_MOVE_LEFT_TARGET: + return _("Move file on left"); //move only supported for files + case SO_MOVE_RIGHT_SOURCE: + case SO_MOVE_RIGHT_TARGET: + return _("Move file on right"); + case SO_OVERWRITE_LEFT: + return _("Overwrite left item"); + case SO_OVERWRITE_RIGHT: + return _("Overwrite right item"); + case SO_DO_NOTHING: + return _("Do nothing"); + case SO_EQUAL: + return _("Both sides are equal"); + case SO_COPY_METADATA_TO_LEFT: + return _("Update attributes on left"); + case SO_COPY_METADATA_TO_RIGHT: + return _("Update attributes on right"); + case SO_UNRESOLVED_CONFLICT: //not used on GUI, but in .csv + return _("Conflict/item cannot be categorized"); + } + assert(false); + return std::wstring(); +} + + +std::wstring zen::getSyncOpDescription(const FileSystemObject& fsObj) +{ + const SyncOperation op = fsObj.getSyncOperation(); + switch (op) + { + case SO_CREATE_NEW_LEFT: + case SO_CREATE_NEW_RIGHT: + case SO_DELETE_LEFT: + case SO_DELETE_RIGHT: + case SO_OVERWRITE_LEFT: + case SO_OVERWRITE_RIGHT: + case SO_DO_NOTHING: + case SO_EQUAL: + return getSyncOpDescription(op); //use generic description + + case SO_COPY_METADATA_TO_LEFT: + case SO_COPY_METADATA_TO_RIGHT: + //harmonize with synchronization.cpp::SynchronizeFolderPair::synchronizeFileInt, ect!! + { + Zstring shortNameOld = fsObj.getShortName(); + Zstring shortNameNew = fsObj.getShortName(); + if (op == SO_COPY_METADATA_TO_LEFT) + std::swap(shortNameOld, shortNameNew); + + if (shortNameOld != shortNameNew) //detected change in case + return getSyncOpDescription(op) + L"\n" + + fmtFileName(shortNameOld) + L" ->\n" + //show short name only + fmtFileName(shortNameNew); + } + //fallback: + return getSyncOpDescription(op); + + case SO_MOVE_LEFT_SOURCE: + case SO_MOVE_LEFT_TARGET: + case SO_MOVE_RIGHT_SOURCE: + case SO_MOVE_RIGHT_TARGET: + if (const FilePair* sourceFile = dynamic_cast(&fsObj)) + if (const FilePair* targetFile = dynamic_cast(FileSystemObject::retrieve(sourceFile->getMoveRef()))) + { + const bool onLeft = op == SO_MOVE_LEFT_SOURCE || op == SO_MOVE_LEFT_TARGET; + const bool isSource = op == SO_MOVE_LEFT_SOURCE || op == SO_MOVE_RIGHT_SOURCE; + + if (!isSource) + std::swap(sourceFile, targetFile); + + auto getRelName = [&](const FileSystemObject& fso, bool leftSide) { return leftSide ? fso.getRelativeName() : fso.getRelativeName(); }; + + const Zstring relSource = getRelName(*sourceFile, onLeft); + const Zstring relTarget = getRelName(*targetFile, !onLeft); + + return getSyncOpDescription(op) + L"\n" + + (EqualFilename()(beforeLast(relSource, FILE_NAME_SEPARATOR), beforeLast(relTarget, FILE_NAME_SEPARATOR)) ? //returns empty string if ch not found + //detected pure "rename" + fmtFileName(afterLast(relSource, FILE_NAME_SEPARATOR)) + L" ->\n" + //show short name only + fmtFileName(afterLast(relTarget, FILE_NAME_SEPARATOR)) : + //"move" or "move + rename" + fmtFileName(relSource) + L" ->\n" + + fmtFileName(relTarget)); + //attention: ::SetWindowText() doesn't handle tab characters correctly in combination with certain file names, so don't use them + } + break; + + case SO_UNRESOLVED_CONFLICT: + return fsObj.getSyncOpConflict(); + } + + assert(false); + return std::wstring(); +} diff --git a/FreeFileSync/Source/file_hierarchy.h b/FreeFileSync/Source/file_hierarchy.h new file mode 100644 index 00000000..e8f35246 --- /dev/null +++ b/FreeFileSync/Source/file_hierarchy.h @@ -0,0 +1,1112 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef FILEHIERARCHY_H_INCLUDED +#define FILEHIERARCHY_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include "structures.h" +#include +#include +#include "structures.h" +#include "lib/hard_filter.h" + +namespace zen +{ +struct FileDescriptor +{ + FileDescriptor() : fileIdx(), devId(), isFollowedSymlink() {} + FileDescriptor(const Int64& lastWriteTimeRawIn, + const UInt64& fileSizeIn, + const FileId& idIn, + bool isSymlink) : + lastWriteTimeRaw(lastWriteTimeRawIn), + fileSize(fileSizeIn), + fileIdx(idIn.second), + devId(idIn.first), + isFollowedSymlink(isSymlink) {} + + Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC, same semantics like time_t (== signed long) + UInt64 fileSize; + FileIndex fileIdx; // == file id: optional! (however, always set on Linux, and *generally* available on Windows) + DeviceId devId; //split into file id into components to avoid padding overhead of a std::pair! + bool isFollowedSymlink; +}; + +inline +FileId getFileId(const FileDescriptor& fd) { return FileId(fd.devId, fd.fileIdx); } + +struct LinkDescriptor +{ + LinkDescriptor() {} + explicit LinkDescriptor(const Int64& lastWriteTimeRawIn) : lastWriteTimeRaw(lastWriteTimeRawIn) {} + + Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC, same semantics like time_t (== signed long) +}; + + +enum SelectedSide +{ + LEFT_SIDE, + RIGHT_SIDE +}; + +template +struct OtherSide; + +template <> +struct OtherSide { static const SelectedSide result = RIGHT_SIDE; }; + +template <> +struct OtherSide { static const SelectedSide result = LEFT_SIDE; }; + + +template +struct SelectParam; + +template <> +struct SelectParam +{ + template + static T& get(T& left, T& right) { return left; } +}; + +template <> +struct SelectParam +{ + template + static T& get(T& left, T& right) { return right; } +}; + + +class BaseDirPair; +class DirPair; +class FilePair; +class SymlinkPair; +class FileSystemObject; + +//------------------------------------------------------------------ + +/* +ERD: + DirContainer 1 --> 0..n DirContainer + DirContainer 1 --> 0..n FileDescriptor + DirContainer 1 --> 0..n LinkDescriptor +*/ + +struct DirContainer +{ + //------------------------------------------------------------------ + typedef std::map DirList; // + typedef std::map FileList; //key: shortName + typedef std::map LinkList; // + //------------------------------------------------------------------ + + DirList dirs; + FileList files; + LinkList links; //non-followed symlinks + + //convenience + DirContainer& addSubDir(const Zstring& shortName) + { + //use C++11 emplace when available + return dirs[shortName]; //value default-construction is okay here + //return dirs.insert(std::make_pair(shortName, DirContainer())).first->second; + } + + void addSubFile(const Zstring& shortName, const FileDescriptor& fileData) + { + files.insert(std::make_pair(shortName, fileData)); + } + + void addSubLink(const Zstring& shortName, const LinkDescriptor& linkData) + { + links.insert(std::make_pair(shortName, linkData)); + } +}; + +/*------------------------------------------------------------------ + inheritance diagram: + + ObjectMgr + /|\ + | + FileSystemObject HierarchyObject + /|\ /|\ + _______________|______________ ______|______ + | | | | | + SymlinkPair FilePair DirPair BaseDirPair + +------------------------------------------------------------------*/ + +class HierarchyObject +{ + friend class DirPair; + friend class FileSystemObject; + +public: + typedef zen::FixedList SubFileVec; //MergeSides::execute() requires a structure that doesn't invalidate pointers after push_back() + typedef zen::FixedList SubLinkVec; //Note: deque<> has circular dependency in VCPP! + typedef zen::FixedList SubDirVec; + + DirPair& addSubDir(const Zstring& shortNameLeft, + const Zstring& shortNameRight, + CompareDirResult defaultCmpResult); + + template + DirPair& addSubDir(const Zstring& shortName); //dir exists on one side only + + + FilePair& addSubFile(const Zstring& shortNameLeft, + const FileDescriptor& left, //file exists on both sides + CompareFilesResult defaultCmpResult, + const Zstring& shortNameRight, + const FileDescriptor& right); + + template + FilePair& addSubFile(const Zstring& shortName, //file exists on one side only + const FileDescriptor& descr); + + SymlinkPair& addSubLink(const Zstring& shortNameLeft, + const LinkDescriptor& left, //link exists on both sides + CompareSymlinkResult defaultCmpResult, + const Zstring& shortNameRight, + const LinkDescriptor& right); + + template + SymlinkPair& addSubLink(const Zstring& shortName, //link exists on one side only + const LinkDescriptor& descr); + + const SubFileVec& refSubFiles() const { return subFiles; } + /**/ SubFileVec& refSubFiles() { return subFiles; } + + const SubLinkVec& refSubLinks() const { return subLinks; } + /**/ SubLinkVec& refSubLinks() { return subLinks; } + + const SubDirVec& refSubDirs() const { return subDirs; } + /**/ SubDirVec& refSubDirs() { return subDirs; } + + BaseDirPair& getRoot() { return root_; } + + const Zstring& getObjRelativeNamePf() const { return objRelNamePf; } //postfixed or empty! + +protected: + HierarchyObject(const Zstring& relativeNamePf, + BaseDirPair& baseDirObj) : + objRelNamePf(relativeNamePf), + root_(baseDirObj) {} + + ~HierarchyObject() {} //don't need polymorphic deletion + + virtual void flip(); + + void removeEmptyRec(); + +private: + virtual void notifySyncCfgChanged() {} + + HierarchyObject(const HierarchyObject&); //this class is referenced by it's child elements => make it non-copyable/movable! + HierarchyObject& operator=(const HierarchyObject&); // + + SubFileVec subFiles; //contained file maps + SubLinkVec subLinks; //contained symbolic link maps + SubDirVec subDirs; //contained directory maps + + Zstring objRelNamePf; //postfixed or empty + BaseDirPair& root_; +}; + +//------------------------------------------------------------------ + +class BaseDirPair : public HierarchyObject //synchronization base directory +{ +public: + BaseDirPair(const Zstring& dirPostfixedLeft, + bool dirExistsLeft, + const Zstring& dirPostfixedRight, + bool dirExistsRight, + const HardFilter::FilterRef& filter, + CompareVariant cmpVar, + size_t fileTimeTolerance) : +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4355) //"The this pointer is valid only within nonstatic member functions. It cannot be used in the initializer list for a base class." +#endif + HierarchyObject(Zstring(), *this), +#ifdef _MSC_VER +#pragma warning(pop) +#endif + filter_(filter), cmpVar_(cmpVar), fileTimeTolerance_(fileTimeTolerance), + baseDirPfL (dirPostfixedLeft ), + baseDirPfR (dirPostfixedRight), + dirExistsLeft_ (dirExistsLeft ), + dirExistsRight_(dirExistsRight) {} + + template const Zstring& getBaseDirPf() const; //base sync directory postfixed with FILE_NAME_SEPARATOR (or empty!) + static void removeEmpty(BaseDirPair& baseDir) { baseDir.removeEmptyRec(); }; //physically remove all invalid entries (where both sides are empty) recursively + + template bool isExisting() const; //status of directory existence at the time of comparison! + template void setExisting(bool value); //update after creating the directory in FFS + + //get settings which were used while creating BaseDirPair + const HardFilter& getFilter() const { return *filter_; } + CompareVariant getCompVariant() const { return cmpVar_; } + size_t getFileTimeTolerance() const { return fileTimeTolerance_; } + + virtual void flip(); + +private: + BaseDirPair(const BaseDirPair&); //this class is referenced by HierarchyObject => make it non-copyable/movable! + BaseDirPair& operator=(const BaseDirPair&); // + + HardFilter::FilterRef filter_; //filter used while scanning directory: represents sub-view of actual files! + CompareVariant cmpVar_; + size_t fileTimeTolerance_; + + Zstring baseDirPfL; //base sync dir postfixed + Zstring baseDirPfR; // + + bool dirExistsLeft_; + bool dirExistsRight_; +}; + + +template <> inline +const Zstring& BaseDirPair::getBaseDirPf() const { return baseDirPfL; } + +template <> inline +const Zstring& BaseDirPair::getBaseDirPf() const { return baseDirPfR; } + + +//get rid of shared_ptr indirection +template //target object type +class DerefIter : public std::iterator +{ +public: + DerefIter() {} + DerefIter(IterTy it) : iter(it) {} + DerefIter(const DerefIter& other) : iter(other.iter) {} + DerefIter& operator++() { ++iter; return *this; } + DerefIter& operator--() { --iter; return *this; } + DerefIter operator++(int) { DerefIter tmp(*this); operator++(); return tmp; } + DerefIter operator--(int) { DerefIter tmp(*this); operator--(); return tmp; } + inline friend ptrdiff_t operator-(const DerefIter& lhs, const DerefIter& rhs) { return lhs.iter - rhs.iter; } + inline friend bool operator==(const DerefIter& lhs, const DerefIter& rhs) { return lhs.iter == rhs.iter; } + inline friend bool operator!=(const DerefIter& lhs, const DerefIter& rhs) { return !(lhs == rhs); } + U& operator* () { return **iter; } + U* operator->() { return &** iter; } +private: + IterTy iter; +}; + +typedef std::vector> FolderComparison; //make sure pointers to sub-elements remain valid +//don't change this back to std::vector too easily: comparison uses push_back to add entries which may result in a full copy! + +DerefIter inline begin(FolderComparison& vect) { return vect.begin(); } +DerefIter inline end (FolderComparison& vect) { return vect.end (); } +DerefIter inline begin(const FolderComparison& vect) { return vect.begin(); } +DerefIter inline end (const FolderComparison& vect) { return vect.end (); } + +//------------------------------------------------------------------ +class FSObjectVisitor +{ +public: + virtual ~FSObjectVisitor() {} + virtual void visit(const FilePair& fileObj) = 0; + virtual void visit(const SymlinkPair& linkObj) = 0; + virtual void visit(const DirPair& dirObj) = 0; +}; + +//inherit from this class to allow safe random access by id instead of unsafe raw pointer +//allow for similar semantics like std::weak_ptr without having to use std::shared_ptr +template +class ObjectMgr +{ +public: + typedef ObjectMgr* ObjectId; + typedef const ObjectMgr* ObjectIdConst; + + ObjectIdConst getId() const { return this; } + /**/ ObjectId getId() { return this; } + + static const T* retrieve(ObjectIdConst id) //returns nullptr if object is not valid anymore + { + auto it = activeObjects().find(id); + return static_cast(it == activeObjects().end() ? nullptr : *it); + } + static T* retrieve(ObjectId id) { return const_cast(retrieve(static_cast(id))); } + +protected: + ObjectMgr () { activeObjects().insert(this); } + ~ObjectMgr() { activeObjects().erase (this); } + +private: + ObjectMgr(const ObjectMgr& rhs); //= delete + ObjectMgr& operator=(const ObjectMgr& rhs); //it's not well-defined what copying an objects means regarding object-identity in this context + + static zen::hash_set& activeObjects() { static zen::hash_set inst; return inst; } //external linkage (even in header file!) +}; + +//------------------------------------------------------------------ + +class FileSystemObject : public ObjectMgr +{ +public: + virtual void accept(FSObjectVisitor& visitor) const = 0; + + Zstring getObjShortName () const; //same as getShortName() but also returns value if either side is empty + Zstring getObjRelativeName() const; //same as getRelativeName() but also returns value if either side is empty + template bool isEmpty() const; + template const Zstring& getShortName() const; //case sensitive! + template Zstring getRelativeName() const; //get name relative to base sync dir without FILE_NAME_SEPARATOR prefix + template const Zstring& getBaseDirPf() const; //base sync directory postfixed with FILE_NAME_SEPARATOR + template Zstring getFullName() const; //getFullName() == getBaseDirPf() + getRelativeName() + + //comparison result + CompareFilesResult getCategory() const { return cmpResult; } + std::wstring getCatExtraDescription() const; //only filled if getCategory() == FILE_CONFLICT or FILE_DIFFERENT_METADATA + + //sync settings + SyncDirection getSyncDir() const; + void setSyncDir(SyncDirection newDir); + void setSyncDirConflict(const std::wstring& description); //set syncDir = SyncDirection::NONE + fill conflict description + + bool isActive() const; + void setActive(bool active); + + //sync operation + virtual SyncOperation testSyncOperation(SyncDirection testSyncDir) const; //semantics: "what if"! assumes "active, no conflict, no recursion (directory)! + 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 + + bool isEmpty() const; //true, if both sides are empty + + const HierarchyObject& parent() const { return parent_; } + /**/ HierarchyObject& parent() { return parent_; } + const BaseDirPair& root() const { return parent_.getRoot(); } + /**/ BaseDirPair& root() { return parent_.getRoot(); } + + //for use during init in "CompareProcess" only: + template void setCategory(); + void setCategoryConflict(const std::wstring& description); + void setCategoryDiffMetadata(const std::wstring& description); + +protected: + FileSystemObject(const Zstring& shortNameLeft, + const Zstring& shortNameRight, + HierarchyObject& parentObj, + CompareFilesResult defaultCmpResult) : + cmpResult(defaultCmpResult), + selectedForSynchronization(true), + syncDir_(SyncDirection::NONE), + shortNameLeft_(shortNameLeft), + shortNameRight_(shortNameRight), + //shortNameRight_(shortNameRight == shortNameLeft ? shortNameLeft : shortNameRight), -> strangely doesn't seem to shrink peak memory consumption at all! + parent_(parentObj) + { + parent_.notifySyncCfgChanged(); + } + + ~FileSystemObject() {} //don't need polymorphic deletion + //mustn't call parent here, it is already partially destroyed and nothing more than a pure HierarchyObject! + + virtual void flip(); + virtual void notifySyncCfgChanged() { parent().notifySyncCfgChanged(); /*propagate!*/ } + + void setSynced(const Zstring& shortName); + +private: + virtual void removeObjectL() = 0; + virtual void removeObjectR() = 0; + + //categorization + std::unique_ptr cmpResultDescr; //only filled if getCategory() == FILE_CONFLICT or FILE_DIFFERENT_METADATA + CompareFilesResult cmpResult; //although this uses 4 bytes there is currently *no* space wasted in class layout! + + bool selectedForSynchronization; + + SyncDirection syncDir_; //1 byte: optimize memory layout! + std::unique_ptr syncDirConflict; //non-empty if we have a conflict setting sync-direction + //get rid of std::wstring small string optimization (consumes 32/48 byte on VS2010 x86/x64!) + + //Note: we model *four* states with last two variables => "syncDirConflict is empty or syncDir == NONE" is a class invariant!!! + + Zstring shortNameLeft_; //slightly redundant under linux, but on windows the "same" filenames can differ in case + Zstring shortNameRight_; //use as indicator: an empty name means: not existing! + + HierarchyObject& parent_; +}; + +//------------------------------------------------------------------ + +class DirPair : public FileSystemObject, public HierarchyObject +{ + friend class HierarchyObject; + +public: + virtual void accept(FSObjectVisitor& visitor) const; + + CompareDirResult getDirCategory() const; //returns actually used subset of CompareFilesResult + + DirPair(const Zstring& shortNameLeft, //use empty shortname if "not existing" + const Zstring& shortNameRight, // + HierarchyObject& parentObj, + CompareDirResult defaultCmpResult) : + FileSystemObject(shortNameLeft, shortNameRight, parentObj, static_cast(defaultCmpResult)), + HierarchyObject(getObjRelativeName() + FILE_NAME_SEPARATOR, parentObj.getRoot()), + syncOpBuffered(SO_DO_NOTHING), + syncOpUpToDate(false) {} + + virtual SyncOperation getSyncOperation() const; + + void setSyncedTo(const Zstring& shortName); //call after sync, sets DIR_EQUAL + +private: + virtual void flip(); + virtual void removeObjectL(); + virtual void removeObjectR(); + virtual void notifySyncCfgChanged() { syncOpUpToDate = false; FileSystemObject::notifySyncCfgChanged(); HierarchyObject::notifySyncCfgChanged(); } + + mutable SyncOperation syncOpBuffered; //determining sync-op for directory may be expensive as it depends on child-objects -> buffer it + mutable bool syncOpUpToDate; // +}; + +//------------------------------------------------------------------ + +class FilePair : public FileSystemObject +{ + friend class HierarchyObject; //construction + +public: + virtual void accept(FSObjectVisitor& visitor) const; + + FilePair(const Zstring& shortNameLeft, //use empty string if "not existing" + const FileDescriptor& left, + CompareFilesResult defaultCmpResult, + const Zstring& shortNameRight, // + const FileDescriptor& right, + HierarchyObject& parentObj) : + FileSystemObject(shortNameLeft, shortNameRight, parentObj, defaultCmpResult), + dataLeft(left), + dataRight(right), + moveFileRef(nullptr) {} + + template Int64 getLastWriteTime () const; + template UInt64 getFileSize () const; + template FileId getFileId () const; + template bool isFollowedSymlink() const; + + void setMoveRef(ObjectId refId) { moveFileRef = refId; } //reference to corresponding renamed file + ObjectId getMoveRef() const { return moveFileRef; } //may be nullptr + + CompareFilesResult getFileCategory() const; + + virtual SyncOperation testSyncOperation(SyncDirection testSyncDir) const; //semantics: "what if"! assumes "active, no conflict, no recursion (directory)! + virtual SyncOperation getSyncOperation() const; + + template + void setSyncedTo(const Zstring& shortName, //call after sync, sets FILE_EQUAL + const UInt64& fileSize, + const Int64& lastWriteTimeTrg, + const Int64& lastWriteTimeSrc, + const FileId& fileIdTrg, + const FileId& fileIdSrc, + bool isSymlinkTrg, + bool isSymlinkSrc); + +private: + SyncOperation applyMoveOptimization(SyncOperation op) const; + + virtual void flip(); + virtual void removeObjectL(); + virtual void removeObjectR(); + + FileDescriptor dataLeft; + FileDescriptor dataRight; + + ObjectId moveFileRef; //optional, filled by redetermineSyncDirection() +}; + +//------------------------------------------------------------------ + +class SymlinkPair : public FileSystemObject //this class models a TRUE symbolic link, i.e. one that is NEVER dereferenced: deref-links should be directly placed in class File/DirPair +{ + friend class HierarchyObject; //construction + +public: + virtual void accept(FSObjectVisitor& visitor) const; + + template zen::Int64 getLastWriteTime() const; //write time of the link, NOT target! + + CompareSymlinkResult getLinkCategory() const; //returns actually used subset of CompareFilesResult + + SymlinkPair(const Zstring& shortNameLeft, //use empty string if "not existing" + const LinkDescriptor& left, + CompareSymlinkResult defaultCmpResult, + const Zstring& shortNameRight, //use empty string if "not existing" + const LinkDescriptor& right, + HierarchyObject& parentObj) : + FileSystemObject(shortNameLeft, shortNameRight, parentObj, static_cast(defaultCmpResult)), + dataLeft(left), + dataRight(right) {} + + template + void setSyncedTo(const Zstring& shortName, //call after sync, sets SYMLINK_EQUAL + const Int64& lastWriteTimeTrg, + const Int64& lastWriteTimeSrc); + +private: + virtual void flip(); + virtual void removeObjectL(); + virtual void removeObjectR(); + + LinkDescriptor dataLeft; + LinkDescriptor dataRight; +}; + +//------------------------------------------------------------------ + +//generic type descriptions (usecase CSV legend, sync config) +std::wstring getCategoryDescription(CompareFilesResult cmpRes); +std::wstring getSyncOpDescription (SyncOperation op); + +//item-specific type descriptions +std::wstring getCategoryDescription(const FileSystemObject& fsObj); +std::wstring getSyncOpDescription (const FileSystemObject& fsObj); + +//------------------------------------------------------------------ + + + + + + + + + + + + + + + + + + + + + + +//---------------Inline Implementation--------------------------------------------------- +//inline virtual... admittedly its use may be limited +inline void FilePair ::accept(FSObjectVisitor& visitor) const { visitor.visit(*this); } +inline void DirPair ::accept(FSObjectVisitor& visitor) const { visitor.visit(*this); } +inline void SymlinkPair::accept(FSObjectVisitor& visitor) const { visitor.visit(*this); } + + +inline +CompareFilesResult FilePair::getFileCategory() const +{ + return getCategory(); +} + + +inline +CompareDirResult DirPair::getDirCategory() const +{ + return static_cast(getCategory()); +} + + +inline +std::wstring FileSystemObject::getCatExtraDescription() const +{ + assert(getCategory() == FILE_CONFLICT || getCategory() == FILE_DIFFERENT_METADATA); + return cmpResultDescr ? *cmpResultDescr : std::wstring(); +} + + +inline +SyncDirection FileSystemObject::getSyncDir() const +{ + return syncDir_; +} + + +inline +void FileSystemObject::setSyncDir(SyncDirection newDir) +{ + syncDir_ = newDir; + syncDirConflict.reset(); + + notifySyncCfgChanged(); +} + + +inline +void FileSystemObject::setSyncDirConflict(const std::wstring& description) +{ + syncDir_ = SyncDirection::NONE; + syncDirConflict.reset(new std::wstring(description)); + + notifySyncCfgChanged(); +} + + +inline +std::wstring FileSystemObject::getSyncOpConflict() const +{ + assert(getSyncOperation() == SO_UNRESOLVED_CONFLICT); + return syncDirConflict ? *syncDirConflict : std::wstring(); +} + + +inline +bool FileSystemObject::isActive() const +{ + return selectedForSynchronization; +} + + +inline +void FileSystemObject::setActive(bool active) +{ + selectedForSynchronization = active; + notifySyncCfgChanged(); +} + + +template inline +bool FileSystemObject::isEmpty() const +{ + return SelectParam::get(shortNameLeft_, shortNameRight_).empty(); +} + + +inline +bool FileSystemObject::isEmpty() const +{ + return isEmpty() && isEmpty(); +} + + +template inline +const Zstring& FileSystemObject::getShortName() const +{ + return SelectParam::get(shortNameLeft_, shortNameRight_); //empty if not existing +} + + +template inline +Zstring FileSystemObject::getRelativeName() const +{ + return isEmpty() ? Zstring() : parent_.getObjRelativeNamePf() + getShortName(); +} + + +inline +Zstring FileSystemObject::getObjRelativeName() const +{ + return parent_.getObjRelativeNamePf() + getObjShortName(); +} + + +inline +Zstring FileSystemObject::getObjShortName() const +{ + return isEmpty() ? getShortName() : getShortName(); +} + + +template inline +Zstring FileSystemObject::getFullName() const +{ + return isEmpty() ? Zstring() : getBaseDirPf() + parent_.getObjRelativeNamePf() + getShortName(); +} + + +template inline +const Zstring& FileSystemObject::getBaseDirPf() const +{ + return root().getBaseDirPf(); +} + + +template <> inline +void FileSystemObject::removeObject() +{ + cmpResult = isEmpty() ? FILE_EQUAL : FILE_RIGHT_SIDE_ONLY; + shortNameLeft_.clear(); + removeObjectL(); + + setSyncDir(SyncDirection::NONE); //calls notifySyncCfgChanged() +} + + +template <> inline +void FileSystemObject::removeObject() +{ + cmpResult = isEmpty() ? FILE_EQUAL : FILE_LEFT_SIDE_ONLY; + shortNameRight_.clear(); + removeObjectR(); + + setSyncDir(SyncDirection::NONE); //calls notifySyncCfgChanged() +} + + +inline +void FileSystemObject::setSynced(const Zstring& shortName) +{ + assert(!isEmpty()); + shortNameRight_ = shortNameLeft_ = shortName; + cmpResult = FILE_EQUAL; + setSyncDir(SyncDirection::NONE); +} + + +template inline +void FileSystemObject::setCategory() +{ + cmpResult = res; +} +template <> void FileSystemObject::setCategory(); // +template <> void FileSystemObject::setCategory(); //not defined! +template <> void FileSystemObject::setCategory(); // +template <> void FileSystemObject::setCategory(); // + +inline +void FileSystemObject::setCategoryConflict(const std::wstring& description) +{ + cmpResult = FILE_CONFLICT; + cmpResultDescr.reset(new std::wstring(description)); +} + +inline +void FileSystemObject::setCategoryDiffMetadata(const std::wstring& description) +{ + cmpResult = FILE_DIFFERENT_METADATA; + cmpResultDescr.reset(new std::wstring(description)); +} + +inline +void FileSystemObject::flip() +{ + std::swap(shortNameLeft_, shortNameRight_); + + switch (cmpResult) + { + case FILE_LEFT_SIDE_ONLY: + cmpResult = FILE_RIGHT_SIDE_ONLY; + break; + case FILE_RIGHT_SIDE_ONLY: + cmpResult = FILE_LEFT_SIDE_ONLY; + break; + case FILE_LEFT_NEWER: + cmpResult = FILE_RIGHT_NEWER; + break; + case FILE_RIGHT_NEWER: + cmpResult = FILE_LEFT_NEWER; + break; + case FILE_DIFFERENT: + case FILE_EQUAL: + case FILE_DIFFERENT_METADATA: + case FILE_CONFLICT: + break; + } + + notifySyncCfgChanged(); +} + + +inline +void HierarchyObject::flip() +{ + for (FilePair& fileObj : refSubFiles()) + fileObj.flip(); + for (SymlinkPair& linkObj : refSubLinks()) + linkObj.flip(); + for (DirPair& dirObj : refSubDirs()) + dirObj.flip(); +} + + +inline +DirPair& HierarchyObject::addSubDir(const Zstring& shortNameLeft, + const Zstring& shortNameRight, + CompareDirResult defaultCmpResult) +{ + subDirs.emplace_back(shortNameLeft, shortNameRight, *this, defaultCmpResult); + return subDirs.back(); +} + + +template <> inline +DirPair& HierarchyObject::addSubDir(const Zstring& shortName) +{ + subDirs.emplace_back(shortName, Zstring(), *this, DIR_LEFT_SIDE_ONLY); + return subDirs.back(); +} + + +template <> inline +DirPair& HierarchyObject::addSubDir(const Zstring& shortName) +{ + subDirs.emplace_back(Zstring(), shortName, *this, DIR_RIGHT_SIDE_ONLY); + return subDirs.back(); +} + + +inline +FilePair& HierarchyObject::addSubFile(const Zstring& shortNameLeft, + const FileDescriptor& left, //file exists on both sides + CompareFilesResult defaultCmpResult, + const Zstring& shortNameRight, + const FileDescriptor& right) +{ + subFiles.emplace_back(shortNameLeft, left, defaultCmpResult, shortNameRight, right, *this); + return subFiles.back(); +} + + +template <> inline +FilePair& HierarchyObject::addSubFile(const Zstring& shortName, const FileDescriptor& descr) +{ + subFiles.emplace_back(shortName, descr, FILE_LEFT_SIDE_ONLY, Zstring(), FileDescriptor(), *this); + return subFiles.back(); +} + + +template <> inline +FilePair& HierarchyObject::addSubFile(const Zstring& shortName, const FileDescriptor& descr) +{ + subFiles.emplace_back(Zstring(), FileDescriptor(), FILE_RIGHT_SIDE_ONLY, shortName, descr, *this); + return subFiles.back(); +} + + +inline +SymlinkPair& HierarchyObject::addSubLink( + const Zstring& shortNameLeft, + const LinkDescriptor& left, //link exists on both sides + CompareSymlinkResult defaultCmpResult, + const Zstring& shortNameRight, + const LinkDescriptor& right) +{ + subLinks.emplace_back(shortNameLeft, left, defaultCmpResult, shortNameRight, right, *this); + return subLinks.back(); +} + + +template <> inline +SymlinkPair& HierarchyObject::addSubLink(const Zstring& shortName, const LinkDescriptor& descr) +{ + subLinks.emplace_back(shortName, descr, SYMLINK_LEFT_SIDE_ONLY, Zstring(), LinkDescriptor(), *this); + return subLinks.back(); +} + + +template <> inline +SymlinkPair& HierarchyObject::addSubLink(const Zstring& shortName, const LinkDescriptor& descr) +{ + subLinks.emplace_back(Zstring(), LinkDescriptor(), SYMLINK_RIGHT_SIDE_ONLY, shortName, descr, *this); + return subLinks.back(); +} + + +inline +void BaseDirPair::flip() +{ + HierarchyObject::flip(); + std::swap(baseDirPfL, baseDirPfR); + std::swap(dirExistsLeft_, dirExistsRight_); +} + + +inline +void DirPair::flip() +{ + HierarchyObject ::flip(); //call base class versions + FileSystemObject::flip(); // +} + + +inline +void DirPair::removeObjectL() +{ + for (FilePair& fileObj : refSubFiles()) + fileObj.removeObject(); + for (SymlinkPair& linkObj : refSubLinks()) + linkObj.removeObject(); + for (DirPair& dirObj : refSubDirs()) + dirObj.removeObject(); +} + + +inline +void DirPair::removeObjectR() +{ + for (FilePair& fileObj : refSubFiles()) + fileObj.removeObject(); + for (SymlinkPair& linkObj : refSubLinks()) + linkObj.removeObject(); + for (DirPair& dirObj : refSubDirs()) + dirObj.removeObject(); +} + + +template inline +bool BaseDirPair::isExisting() const +{ + return SelectParam::get(dirExistsLeft_, dirExistsRight_); +} + + +template inline +void BaseDirPair::setExisting(bool value) +{ + SelectParam::get(dirExistsLeft_, dirExistsRight_) = value; +} + + +inline +void FilePair::flip() +{ + FileSystemObject::flip(); //call base class version + std::swap(dataLeft, dataRight); +} + + +inline +void FilePair::removeObjectL() +{ + dataLeft = FileDescriptor(); +} + + +inline +void FilePair::removeObjectR() +{ + dataRight = FileDescriptor(); +} + + +template inline +zen::Int64 FilePair::getLastWriteTime() const +{ + return SelectParam::get(dataLeft, dataRight).lastWriteTimeRaw; +} + + +template inline +zen::UInt64 FilePair::getFileSize() const +{ + return SelectParam::get(dataLeft, dataRight).fileSize; +} + + +template inline +FileId FilePair::getFileId() const +{ + return FileId(SelectParam::get(dataLeft, dataRight).devId, + SelectParam::get(dataLeft, dataRight).fileIdx); +} + + +template inline +bool FilePair::isFollowedSymlink() const +{ + return SelectParam::get(dataLeft, dataRight).isFollowedSymlink; +} + + +template inline +void FilePair::setSyncedTo(const Zstring& shortName, + const UInt64& fileSize, + const Int64& lastWriteTimeTrg, + const Int64& lastWriteTimeSrc, + const FileId& fileIdTrg, + const FileId& fileIdSrc, + bool isSymlinkTrg, + bool isSymlinkSrc) +{ + //FILE_EQUAL is only allowed for same short name and file size: enforced by this method! + static const SelectedSide sideSrc = OtherSide::result; + + SelectParam::get(dataLeft, dataRight) = FileDescriptor(lastWriteTimeTrg, fileSize, fileIdTrg, isSymlinkTrg); + SelectParam::get(dataLeft, dataRight) = FileDescriptor(lastWriteTimeSrc, fileSize, fileIdSrc, isSymlinkSrc); + + moveFileRef = nullptr; + FileSystemObject::setSynced(shortName); //set FileSystemObject specific part +} + + +template inline +void SymlinkPair::setSyncedTo(const Zstring& shortName, + const Int64& lastWriteTimeTrg, + const Int64& lastWriteTimeSrc) +{ + static const SelectedSide sideSrc = OtherSide::result; + + SelectParam::get(dataLeft, dataRight) = LinkDescriptor(lastWriteTimeTrg); + SelectParam::get(dataLeft, dataRight) = LinkDescriptor(lastWriteTimeSrc); + + FileSystemObject::setSynced(shortName); //set FileSystemObject specific part +} + + +inline +void DirPair::setSyncedTo(const Zstring& shortName) +{ + FileSystemObject::setSynced(shortName); //set FileSystemObject specific part +} + + +template inline +zen::Int64 SymlinkPair::getLastWriteTime() const +{ + return SelectParam::get(dataLeft, dataRight).lastWriteTimeRaw; +} + + +inline +CompareSymlinkResult SymlinkPair::getLinkCategory() const +{ + return static_cast(getCategory()); +} + + +inline +void SymlinkPair::flip() +{ + FileSystemObject::flip(); //call base class versions + std::swap(dataLeft, dataRight); +} + + +inline +void SymlinkPair::removeObjectL() +{ + dataLeft = LinkDescriptor(); +} + + +inline +void SymlinkPair::removeObjectR() +{ + dataRight = LinkDescriptor(); +} +} + +#endif // FILEHIERARCHY_H_INCLUDED diff --git a/FreeFileSync/Source/lib/Batch.ico b/FreeFileSync/Source/lib/Batch.ico new file mode 100644 index 00000000..faa2db64 Binary files /dev/null and b/FreeFileSync/Source/lib/Batch.ico differ diff --git a/FreeFileSync/Source/lib/FreeFileSync.ico b/FreeFileSync/Source/lib/FreeFileSync.ico new file mode 100644 index 00000000..88f656ee Binary files /dev/null and b/FreeFileSync/Source/lib/FreeFileSync.ico differ diff --git a/FreeFileSync/Source/lib/SyncDB.ico b/FreeFileSync/Source/lib/SyncDB.ico new file mode 100644 index 00000000..9740a338 Binary files /dev/null and b/FreeFileSync/Source/lib/SyncDB.ico differ diff --git a/FreeFileSync/Source/lib/binary.cpp b/FreeFileSync/Source/lib/binary.cpp new file mode 100644 index 00000000..0e41f7a6 --- /dev/null +++ b/FreeFileSync/Source/lib/binary.cpp @@ -0,0 +1,134 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "binary.h" +#include +#include +#include +#include +#include + +using namespace zen; + + +namespace +{ +inline +void setMinSize(std::vector& buffer, size_t minSize) +{ + if (buffer.size() < minSize) //this is similar to reserve(), but we need a "properly initialized" array here + buffer.resize(minSize); +} + +class BufferSize +{ +public: + BufferSize() : bufSize(BUFFER_SIZE_START) {} + + void inc() + { + if (bufSize < BUFFER_SIZE_MAX) + bufSize *= 2; + } + + void dec() + { + if (bufSize > BUFFER_SIZE_MIN) + bufSize /= 2; + } + + operator size_t() const { return bufSize; } + +private: + static const size_t BUFFER_SIZE_MIN = 64 * 1024; + static const size_t BUFFER_SIZE_START = 128 * 1024; //initial buffer size + static const size_t BUFFER_SIZE_MAX = 16 * 1024 * 1024; + + /*Tests on Win7 x64 show that buffer size does NOT matter if files are located on different physical disks! + Impact of buffer size when files are on same disk: + + buffer MB/s + ------------ + 64 10 + 128 19 + 512 40 + 1024 48 + 2048 56 + 4096 56 + 8192 56 + */ + + size_t bufSize; +}; + + +const std::int64_t TICKS_PER_SEC = ticksPerSec(); +} + + +bool zen::filesHaveSameContent(const Zstring& filename1, const Zstring& filename2, CompareCallback& callback) +{ + static boost::thread_specific_ptr> cpyBuf1; + static boost::thread_specific_ptr> cpyBuf2; + if (!cpyBuf1.get()) + cpyBuf1.reset(new std::vector()); + if (!cpyBuf2.get()) + cpyBuf2.reset(new std::vector()); + + std::vector& memory1 = *cpyBuf1; + std::vector& memory2 = *cpyBuf2; + + FileInput file1(filename1); //throw FileError + FileInput file2(filename2); // + + BufferSize bufferSize; + + TickVal lastDelayViolation = getTicks(); + + do + { + setMinSize(memory1, bufferSize); + setMinSize(memory2, bufferSize); + + const TickVal startTime = getTicks(); + + const size_t length1 = file1.read(&memory1[0], bufferSize); //throw FileError + const size_t length2 = file2.read(&memory2[0], bufferSize); //returns actual number of bytes read + //send progress updates immediately after reading to reliably allow speed calculations for our clients! + callback.updateCompareStatus(to(std::max(length1, length2))); + + if (length1 != length2 || ::memcmp(&memory1[0], &memory2[0], length1) != 0) + return false; + + //-------- dynamically set buffer size to keep callback interval between 100 - 500ms --------------------- + if (TICKS_PER_SEC > 0) + { + const TickVal now = getTicks(); + + const std::int64_t loopTime = dist(startTime, now) * 1000 / TICKS_PER_SEC; //unit: [ms] + if (loopTime < 100) + { + if (dist(lastDelayViolation, now) / TICKS_PER_SEC > 2) //avoid "flipping back": e.g. DVD-Roms read 32MB at once, so first read may be > 500 ms, but second one will be 0ms! + { + lastDelayViolation = now; + bufferSize.inc(); + } + } + else if (loopTime > 500) + { + lastDelayViolation = now; + bufferSize.dec(); + } + } + //------------------------------------------------------------------------------------------------ + } + while (!file1.eof()); + + if (!file2.eof()) //highly unlikely, but possible! (but then again, not in this context where both files have same size...) + return false; + + return true; +} diff --git a/FreeFileSync/Source/lib/binary.h b/FreeFileSync/Source/lib/binary.h new file mode 100644 index 00000000..8a4abe6b --- /dev/null +++ b/FreeFileSync/Source/lib/binary.h @@ -0,0 +1,25 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef BINARY_H_INCLUDED +#define BINARY_H_INCLUDED + +#include +#include +#include + +namespace zen +{ +struct CompareCallback +{ + virtual ~CompareCallback() {} + virtual void updateCompareStatus(Int64 bytesDelta) = 0; +}; + +bool filesHaveSameContent(const Zstring& filename1, const Zstring& filename2, CompareCallback& callback); //throw FileError +} + +#endif // BINARY_H_INCLUDED diff --git a/FreeFileSync/Source/lib/cmp_filetime.h b/FreeFileSync/Source/lib/cmp_filetime.h new file mode 100644 index 00000000..33d214ab --- /dev/null +++ b/FreeFileSync/Source/lib/cmp_filetime.h @@ -0,0 +1,58 @@ +#ifndef CMP_FILETIME_H_INCLUDED +#define CMP_FILETIME_H_INCLUDED + +//#include +#include +#include + +namespace zen +{ +//--------------------------------------------------------------------------------------------------------------- +inline +bool sameFileTime(const Int64& a, const Int64& b, size_t tolerance) +{ + if (a < b) + return b <= a + static_cast(tolerance); + else + return a <= b + static_cast(tolerance); +} +//--------------------------------------------------------------------------------------------------------------- + +//number of seconds since Jan 1st 1970 + 1 year (needn't be too precise) +static const long oneYearFromNow = wxGetUTCTime() + 365 * 24 * 3600; //init at program startup alas in *each* compilation untit -> avoid MT issues +//refactor when C++11 thread-safe static initialization is availalbe in VS (already in GCC) + +class CmpFileTime +{ +public: + enum Result + { + TIME_EQUAL, + TIME_LEFT_NEWER, + TIME_RIGHT_NEWER, + TIME_LEFT_INVALID, + TIME_RIGHT_INVALID + }; + + static Result getResult(const Int64& lhs, const Int64& rhs, size_t tolerance) + { + if (sameFileTime(lhs, rhs, tolerance)) //last write time may differ by up to 2 seconds (NTFS vs FAT32) + return TIME_EQUAL; + + //check for erroneous dates + if (lhs < 0 || lhs > oneYearFromNow) //earlier than Jan 1st 1970 or more than one year in future + return TIME_LEFT_INVALID; + + if (rhs < 0 || rhs > oneYearFromNow) + return TIME_RIGHT_INVALID; + + //regular time comparison + if (lhs < rhs) + return TIME_RIGHT_NEWER; + else + return TIME_LEFT_NEWER; + } +}; +} + +#endif // CMP_FILETIME_H_INCLUDED diff --git a/FreeFileSync/Source/lib/db_file.cpp b/FreeFileSync/Source/lib/db_file.cpp new file mode 100644 index 00000000..1c2a34f3 --- /dev/null +++ b/FreeFileSync/Source/lib/db_file.cpp @@ -0,0 +1,821 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "db_file.h" +#include +#include +#include +#include +#include +#include + +#ifdef ZEN_WIN +#include //includes "windows.h" +#include +#endif + +using namespace zen; + + +namespace +{ +//------------------------------------------------------------------------------------------------------------------------------- +const char FILE_FORMAT_DESCR[] = "FreeFileSync"; +const int DB_FORMAT_CONTAINER = 9; +const int DB_FORMAT_STREAM = 1; +//------------------------------------------------------------------------------------------------------------------------------- + +typedef std::string UniqueId; +typedef std::map DbStreams; //list of streams ordered by session UUID + +//----------------------------------------------------------------------------------- +//| ensure 32/64 bit portability: use fixed size data types only e.g. std::uint32_t | +//----------------------------------------------------------------------------------- + +template inline +Zstring getDBFilename(const BaseDirPair& baseDirObj, bool tempfile = false) +{ + //Linux and Windows builds are binary incompatible: different file id?, problem with case sensitivity? are UTC file times really compatible? + //what about endianess!? + //however 32 and 64 bit db files *are* designed to be binary compatible! + //Give db files different names. + //make sure they end with ".ffs_db". These files will be excluded from comparison +#ifdef ZEN_WIN + Zstring dbname = Zstring(Zstr("sync")) + (tempfile ? Zstr(".tmp") : Zstr("")) + SYNC_DB_FILE_ENDING; +#elif defined ZEN_LINUX || defined ZEN_MAC + //files beginning with dots are hidden e.g. in Nautilus + Zstring dbname = Zstring(Zstr(".sync")) + (tempfile ? Zstr(".tmp") : Zstr("")) + SYNC_DB_FILE_ENDING; +#endif + return baseDirObj.getBaseDirPf() + dbname; +} + +//####################################################################################################################################### + +void saveStreams(const DbStreams& streamList, const Zstring& filename) //throw FileError +{ + BinStreamOut streamOut; + + //write FreeFileSync file identifier + writeArray(streamOut, FILE_FORMAT_DESCR, sizeof(FILE_FORMAT_DESCR)); + + //save file format version + writeNumber(streamOut, DB_FORMAT_CONTAINER); + + //save stream list + writeNumber(streamOut, static_cast(streamList.size())); //number of streams, one for each sync-pair + + for (const auto& stream : streamList) + { + writeContainer(streamOut, stream.first ); + writeContainer(streamOut, stream.second); + } + + assert(!somethingExists(filename)); //orphan tmp files should be cleaned up already at this point! + saveBinStream(filename, streamOut.get()); //throw FileError + +#ifdef ZEN_WIN + //be careful to avoid CreateFile() + CREATE_ALWAYS on a hidden file -> see file_io.cpp + ::SetFileAttributes(applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_HIDDEN); //(try to) hide database file +#endif +} + + +DbStreams loadStreams(const Zstring& filename) //throw FileError, FileErrorDatabaseNotExisting +{ + try + { + BinStreamIn streamIn = loadBinStream(filename); //throw FileError + + //read FreeFileSync file identifier + char formatDescr[sizeof(FILE_FORMAT_DESCR)] = {}; + readArray(streamIn, formatDescr, sizeof(formatDescr)); //throw UnexpectedEndOfStreamError + + if (!std::equal(FILE_FORMAT_DESCR, FILE_FORMAT_DESCR + sizeof(FILE_FORMAT_DESCR), formatDescr)) + throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filename))); + + const int version = readNumber(streamIn); //throw UnexpectedEndOfStreamError + if (version != DB_FORMAT_CONTAINER) //read file format version number + throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filename))); + + DbStreams output; + + //read stream lists + size_t dbCount = readNumber(streamIn); //number of streams, one for each sync-pair + while (dbCount-- != 0) + { + //DB id of partner databases + std::string sessionID = readContainer(streamIn); //throw UnexpectedEndOfStreamError + BinaryStream stream = readContainer(streamIn); // + + output[sessionID] = std::move(stream); + } + return output; + } + catch (FileError&) + { + if (!somethingExists(filename)) //a benign(?) race condition with FileError + throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" + + replaceCpy(_("Database file %x does not yet exist."), L"%x", fmtFileName(filename))); + throw; + } + catch (UnexpectedEndOfStreamError&) + { + throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filename)); + } + catch (const std::bad_alloc& e) //still required? + { + throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filename), + _("Out of memory.") + L" " + utfCvrtTo(e.what())); + } +} + +//####################################################################################################################################### + +class StreamGenerator //for db-file back-wards compatibility we stick with two output streams until further +{ +public: + static void execute(const InSyncDir& dir, //throw FileError + const Zstring& filenameL, //used for diagnostics only + const Zstring& filenameR, + BinaryStream& streamL, + BinaryStream& streamR) + { + StreamGenerator generator; + + //PERF_START + generator.recurse(dir); + //PERF_STOP + + auto compStream = [](const BinaryStream& stream, const Zstring& filename) -> BinaryStream //throw FileError + { + try + { + /* Zlib: optimal level - testcase 1 million files + level/size [MB]/time [ms] + 0 49.54 272 (uncompressed) + 1 14.53 1013 + 2 14.13 1106 + 3 13.76 1288 - best compromise between speed and compression + 4 13.20 1526 + 5 12.73 1916 + 6 12.58 2765 + 7 12.54 3633 + 8 12.51 9032 + 9 12.50 19698 (maximal compression) */ + return compress(stream, 3); //throw ZlibInternalError + } + catch (ZlibInternalError&) + { + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename)), L"zlib internal error"); + } + }; + + const BinaryStream tmpL = compStream(generator.outputLeft .get(), filenameL); + const BinaryStream tmpR = compStream(generator.outputRight.get(), filenameR); + const BinaryStream tmpB = compStream(generator.outputBoth .get(), filenameL + Zstr("/") + filenameR); + + BinStreamOut outL; + BinStreamOut outR; + //save format version + writeNumber(outL, DB_FORMAT_STREAM); + writeNumber(outR, DB_FORMAT_STREAM); + + //distribute "outputBoth" over left and right streams: + writeNumber(outL, true); //this side contains first part of "outputBoth" + writeNumber(outR, false); + + const size_t size1stPart = tmpB.size() / 2; + const size_t size2ndPart = tmpB.size() - size1stPart; + + writeNumber(outL, size1stPart); + writeNumber(outR, size2ndPart); + + writeArray(outL, &*tmpB.begin(), size1stPart); + writeArray(outR, &*tmpB.begin() + size1stPart, size2ndPart); + + //write streams corresponding to one side only + writeContainer(outL, tmpL); + writeContainer(outR, tmpR); + + streamL = outL.get(); + streamR = outR.get(); + } + +private: + void recurse(const InSyncDir& container) + { + writeNumber(outputBoth, static_cast(container.files.size())); + for (const auto& dbFile : container.files) + { + writeUtf8(outputBoth, dbFile.first); + writeNumber(outputBoth, dbFile.second.cmpVar); + writeNumber(outputBoth, to(dbFile.second.fileSize)); + + writeFile(outputLeft, dbFile.second.left); + writeFile(outputRight, dbFile.second.right); + } + + writeNumber(outputBoth, static_cast(container.symlinks.size())); + for (const auto& dbSymlink : container.symlinks) + { + writeUtf8(outputBoth, dbSymlink.first); + writeNumber(outputBoth, dbSymlink.second.cmpVar); + + writeLink(outputLeft, dbSymlink.second.left); + writeLink(outputRight, dbSymlink.second.right); + } + + writeNumber(outputBoth, static_cast(container.dirs.size())); + for (const auto& dbDir : container.dirs) + { + writeUtf8(outputBoth, dbDir.first); + writeNumber(outputBoth, dbDir.second.status); + + recurse(dbDir.second); + } + } + + static void writeUtf8(BinStreamOut& output, const Zstring& str) { writeContainer(output, utfCvrtTo>(str)); } + + static void writeFile(BinStreamOut& output, const InSyncDescrFile& descr) + { + writeNumber(output, to(descr.lastWriteTimeRaw)); + writeNumber(output, descr.fileId.first); + writeNumber(output, descr.fileId.second); + assert_static(sizeof(descr.fileId.first ) <= sizeof(std::uint64_t)); + assert_static(sizeof(descr.fileId.second) <= sizeof(std::uint64_t)); + } + + static void writeLink(BinStreamOut& output, const InSyncDescrLink& descr) + { + writeNumber(output, to(descr.lastWriteTimeRaw)); + } + + BinStreamOut outputLeft; //data related to one side only + BinStreamOut outputRight; // + BinStreamOut outputBoth; //data concerning both sides +}; + + +class StreamParser +{ +public: + static std::shared_ptr execute(const BinaryStream& streamL, //throw FileError + const BinaryStream& streamR, + const Zstring& filenameL, //used for diagnostics only + const Zstring& filenameR) + { + auto decompStream = [](const BinaryStream& stream, const Zstring& filename) -> BinaryStream //throw FileError + { + try + { + return decompress(stream); //throw ZlibInternalError + } + catch (ZlibInternalError&) + { + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename)), L"zlib internal error"); + } + }; + + try + { + BinStreamIn inL(streamL); + BinStreamIn inR(streamR); + + const int streamVersion = readNumber(inL); //throw UnexpectedEndOfStreamError + warn_static("remove this case after migration:") + bool migrateStreamFromOldFormat = streamVersion != DB_FORMAT_STREAM; + if (migrateStreamFromOldFormat) + inL = BinStreamIn(streamL); + else + { + const int streamVersionRef = readNumber(inR); //throw UnexpectedEndOfStreamError + if (streamVersionRef != streamVersion) //throw UnexpectedEndOfStreamError + throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filenameR), L"stream format mismatch")); + if (streamVersion != DB_FORMAT_STREAM) + throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filenameL), L"stream format")); + } + + bool has1stPartL = false; + bool has1stPartR = false; + if (migrateStreamFromOldFormat) + { + has1stPartL = readNumber(inL) != 0; //throw UnexpectedEndOfStreamError + has1stPartR = readNumber(inR) != 0; // + } + else + { + has1stPartL = readNumber(inL) != 0; //throw UnexpectedEndOfStreamError + has1stPartR = readNumber(inR) != 0; // + } + + if (has1stPartL == has1stPartR) + throw UnexpectedEndOfStreamError(); + + BinStreamIn& in1stPart = has1stPartL ? inL : inR; + BinStreamIn& in2ndPart = has1stPartL ? inR : inL; + + const size_t size1stPart = static_cast(readNumber(in1stPart)); + const size_t size2ndPart = static_cast(readNumber(in2ndPart)); + + BinaryStream tmpB; + tmpB.resize(size1stPart + size2ndPart); //throw bad_alloc + readArray(in1stPart, &*tmpB.begin(), size1stPart); + readArray(in2ndPart, &*tmpB.begin() + size1stPart, size2ndPart); + + const BinaryStream tmpL = readContainer(inL); + const BinaryStream tmpR = readContainer(inR); + + auto output = std::make_shared(InSyncDir::DIR_STATUS_IN_SYNC); + StreamParser parser(decompStream(tmpL, filenameL), + decompStream(tmpR, filenameR), + decompStream(tmpB, filenameL + Zstr("/") + filenameR), migrateStreamFromOldFormat); + parser.recurse(*output); //throw UnexpectedEndOfStreamError + return output; + } + catch (const UnexpectedEndOfStreamError&) + { + throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filenameL) + L"\n" + fmtFileName(filenameR)); + } + catch (const std::bad_alloc& e) + { + throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filenameL) + L"\n" + fmtFileName(filenameR), + _("Out of memory.") + L" " + utfCvrtTo(e.what())); + } + } + +private: + StreamParser(const BinaryStream& bufferL, + const BinaryStream& bufferR, + const BinaryStream& bufferB, + bool migrateStreamFromOldFormat) : + migrateStreamFromOldFormat_(migrateStreamFromOldFormat), + inputLeft (bufferL), + inputRight(bufferR), + inputBoth (bufferB) {} + + void recurse(InSyncDir& container) + { + size_t fileCount = readNumber(inputBoth); + while (fileCount-- != 0) + { + const Zstring shortName = readUtf8(inputBoth); + + if (migrateStreamFromOldFormat_) + { + const auto inSyncType = readNumber(inputBoth); + const CompareVariant cmpVar = inSyncType == 0 ? CMP_BY_CONTENT : CMP_BY_TIME_SIZE; + + auto lastWriteTimeRawL = readNumber(inputLeft); //throw UnexpectedEndOfStreamError + const UInt64 fileSize = readNumber(inputLeft); + auto devIdL = static_cast(readNumber(inputLeft)); // + auto fileIdxL = static_cast(readNumber(inputLeft)); //silence "loss of precision" compiler warnings + const InSyncDescrFile dataL = InSyncDescrFile(lastWriteTimeRawL, FileId(devIdL, fileIdxL)); + + auto lastWriteTimeRaw = readNumber(inputRight); //throw UnexpectedEndOfStreamError + readNumber(inputRight); + auto devId = static_cast(readNumber(inputRight)); // + auto fileIdx = static_cast(readNumber(inputRight)); //silence "loss of precision" compiler warnings + const InSyncDescrFile dataR = InSyncDescrFile(lastWriteTimeRaw, FileId(devId, fileIdx)); + + container.addFile(shortName, dataL, dataR, cmpVar, fileSize); + } + else + { + const auto cmpVar = static_cast(readNumber(inputBoth)); + const UInt64 fileSize = readNumber(inputBoth); + const InSyncDescrFile dataL = readFile(inputLeft); + const InSyncDescrFile dataR = readFile(inputRight); + container.addFile(shortName, dataL, dataR, cmpVar, fileSize); + } + } + + size_t linkCount = readNumber(inputBoth); + while (linkCount-- != 0) + { + const Zstring shortName = readUtf8(inputBoth); + + if (migrateStreamFromOldFormat_) + { + const CompareVariant cmpVar = CMP_BY_CONTENT; + auto lastWriteTimeRaw = readNumber(inputLeft); + readUtf8(inputLeft);//descr.targetPath = readUtf8(input); + readNumber(inputLeft);//descr.type = static_cast(readNumber(input)); + InSyncDescrLink dataL = InSyncDescrLink(lastWriteTimeRaw); + + auto lastWriteTimeRawR = readNumber(inputRight); + readUtf8(inputRight);//descr.targetPath = readUtf8(input); + readNumber(inputRight);//descr.type = static_cast(readNumber(input)); + InSyncDescrLink dataR = InSyncDescrLink(lastWriteTimeRawR); + + container.addSymlink(shortName, dataL, dataR, cmpVar); + } + else + { + const auto cmpVar = static_cast(readNumber(inputBoth)); + InSyncDescrLink dataL = readLink(inputLeft); + InSyncDescrLink dataR = readLink(inputRight); + container.addSymlink(shortName, dataL, dataR, cmpVar); + } + } + + size_t dirCount = readNumber(inputBoth); + while (dirCount-- != 0) + { + const Zstring shortName = readUtf8(inputBoth); + auto status = static_cast(readNumber(inputBoth)); + + InSyncDir& subDir = container.addDir(shortName, status); + recurse(subDir); + } + } + + static Zstring readUtf8(BinStreamIn& input) { return utfCvrtTo(readContainer>(input)); } //throw UnexpectedEndOfStreamError + + static InSyncDescrFile readFile(BinStreamIn& input) + { + //attention: order of function argument evaluation is undefined! So do it one after the other... + auto lastWriteTimeRaw = readNumber(input); //throw UnexpectedEndOfStreamError + auto devId = static_cast(readNumber(input)); // + auto fileIdx = static_cast(readNumber(input)); //silence "loss of precision" compiler warnings + return InSyncDescrFile(lastWriteTimeRaw, FileId(devId, fileIdx)); + } + + static InSyncDescrLink readLink(BinStreamIn& input) + { + auto lastWriteTimeRaw = readNumber(input); + return InSyncDescrLink(lastWriteTimeRaw); + } + + warn_static("remove after migration") + bool migrateStreamFromOldFormat_; + + BinStreamIn inputLeft; //data related to one side only + BinStreamIn inputRight; // + BinStreamIn inputBoth; //data concerning both sides +}; + +//####################################################################################################################################### + +class UpdateLastSynchronousState +{ + /* + 1. filter by file name does *not* create a new hierarchy, but merely gives a different *view* on the existing file hierarchy + => only update database entries matching this view! + 2. Symlink handling *does* create a new (asymmetric) hierarchy during comparison + => update all database entries! + */ +public: + static void execute(const BaseDirPair& baseDirObj, InSyncDir& dir) + { + UpdateLastSynchronousState updater(baseDirObj.getCompVariant(), baseDirObj.getFilter()); + updater.recurse(baseDirObj, dir); + } + +private: + UpdateLastSynchronousState(CompareVariant activeCmpVar, const HardFilter& filter) : + filter_(filter), + activeCmpVar_(activeCmpVar) {} + + void recurse(const HierarchyObject& hierObj, InSyncDir& dir) + { + process(hierObj.refSubFiles(), hierObj.getObjRelativeNamePf(), dir.files); + process(hierObj.refSubLinks(), hierObj.getObjRelativeNamePf(), dir.symlinks); + process(hierObj.refSubDirs (), hierObj.getObjRelativeNamePf(), dir.dirs); + } + + template + static V& updateItem(M& map, const Zstring& key, const V& value) + { + auto rv = map.insert(typename M::value_type(key, value)); + if (!rv.second) + { +#if defined ZEN_WIN || defined ZEN_MAC //caveat: key must be updated, if there is a change in short name case!!! + if (rv.first->first != key) + { + map.erase(rv.first); + return map.insert(typename M::value_type(key, value)).first->second; + } +#endif + rv.first->second = value; + } + return rv.first->second; + + //www.cplusplus.com claims that hint position for map<>::insert(iterator position, const value_type& val) changed with C++11 -> standard is unclear in [map.modifiers] + // => let's use the more generic and potentially less performant version above! + + /* + //efficient create or update without "default-constructible" requirement (Effective STL, item 24) + + //first check if key already exists (if yes, we're saving a value construction/destruction compared to std::map<>::insert + auto it = map.lower_bound(key); + if (it != map.end() && !(map.key_comp()(key, it->first))) + { + #if defined ZEN_WIN || defined ZEN_MAC //caveat: key might need to be updated, too, if there is a change in short name case!!! + if (it->first != key) + { + map.erase(it); //don't fiddle with decrementing "it"! - you might lose while optimizing pointlessly + return map.insert(typename M::value_type(key, value)).first->second; + } + #endif + it->second = value; + return it->second; + } + return map.insert(it, typename M::value_type(key, value))->second; + */ + } + + void process(const HierarchyObject::SubFileVec& currentFiles, const Zstring& parentRelativeNamePf, InSyncDir::FileList& dbFiles) + { + hash_set toPreserve; //referencing fixed-in-memory std::map elements + for (const FilePair& fileObj : currentFiles) + if (!fileObj.isEmpty()) + { + if (fileObj.getCategory() == FILE_EQUAL) //data in sync: write current state + { + //Caveat: If FILE_EQUAL, we *implicitly* assume equal left and right short names matching case: InSyncDir's mapping tables use short name as a key! + //This makes us silently dependent from code in algorithm.h!!! + assert(fileObj.getShortName() == fileObj.getShortName()); + //this should be taken for granted: + assert(fileObj.getFileSize() == fileObj.getFileSize()); + + //create or update new "in-sync" state + InSyncFile& file = updateItem(dbFiles, fileObj.getObjShortName(), + InSyncFile(InSyncDescrFile(fileObj.getLastWriteTime(), + fileObj.getFileId ()), + InSyncDescrFile(fileObj.getLastWriteTime(), + fileObj.getFileId ()), + activeCmpVar_, + fileObj.getFileSize())); + toPreserve.insert(&file); + } + else //not in sync: preserve last synchronous state + { + auto it = dbFiles.find(fileObj.getObjShortName()); + if (it != dbFiles.end()) + toPreserve.insert(&it->second); + } + } + + warn_static("consider temporarily excluded items due to traveral error just like a fixed file filter here!?") + //delete removed items (= "in-sync") from database + map_remove_if(dbFiles, [&](const InSyncDir::FileList::value_type& v) -> bool + { + if (toPreserve.find(&v.second) != toPreserve.end()) + return false; + //all items not existing in "currentFiles" have either been deleted meanwhile or been excluded via filter: + const Zstring& shortName = v.first; + return filter_.passFileFilter(parentRelativeNamePf + shortName); + }); + } + + void process(const HierarchyObject::SubLinkVec& currentLinks, const Zstring& parentRelativeNamePf, InSyncDir::LinkList& dbLinks) + { + hash_set toPreserve; + for (const SymlinkPair& linkObj : currentLinks) + if (!linkObj.isEmpty()) + { + if (linkObj.getLinkCategory() == SYMLINK_EQUAL) //data in sync: write current state + { + assert(linkObj.getShortName() == linkObj.getShortName()); + + //create or update new "in-sync" state + InSyncSymlink& link = updateItem(dbLinks, linkObj.getObjShortName(), + InSyncSymlink(InSyncDescrLink(linkObj.getLastWriteTime()), + InSyncDescrLink(linkObj.getLastWriteTime()), + activeCmpVar_)); + toPreserve.insert(&link); + } + else //not in sync: preserve last synchronous state + { + auto it = dbLinks.find(linkObj.getObjShortName()); + if (it != dbLinks.end()) + toPreserve.insert(&it->second); + } + } + + //delete removed items (= "in-sync") from database + map_remove_if(dbLinks, [&](const InSyncDir::LinkList::value_type& v) -> bool + { + if (toPreserve.find(&v.second) != toPreserve.end()) + return false; + //all items not existing in "currentLinks" have either been deleted meanwhile or been excluded via filter: + const Zstring& shortName = v.first; + return filter_.passFileFilter(parentRelativeNamePf + shortName); + }); + } + + void process(const HierarchyObject::SubDirVec& currentDirs, const Zstring& parentRelativeNamePf, InSyncDir::DirList& dbDirs) + { + hash_set toPreserve; + for (const DirPair& dirObj : currentDirs) + if (!dirObj.isEmpty()) + switch (dirObj.getDirCategory()) + { + case DIR_EQUAL: + { + assert(dirObj.getShortName() == dirObj.getShortName()); + + //update directory entry only (shallow), but do *not touch* exising child elements!!! + const Zstring& key = dirObj.getObjShortName(); + auto insertResult = dbDirs.insert(std::make_pair(key, InSyncDir(InSyncDir::DIR_STATUS_IN_SYNC))); //get or create + auto it = insertResult.first; + +#if defined ZEN_WIN || defined ZEN_MAC //caveat: key might need to be updated, too, if there is a change in short name case!!! + const bool alreadyExisting = !insertResult.second; + if (alreadyExisting && it->first != key) + { + auto oldValue = std::move(it->second); + dbDirs.erase(it); //don't fiddle with decrementing "it"! - you might lose while optimizing pointlessly + it = dbDirs.insert(InSyncDir::DirList::value_type(key, std::move(oldValue))).first; + } +#endif + InSyncDir& dir = it->second; + dir.status = InSyncDir::DIR_STATUS_IN_SYNC; //update immediate directory entry + toPreserve.insert(&dir); + recurse(dirObj, dir); + } + break; + + case DIR_DIFFERENT_METADATA: + //if DIR_DIFFERENT_METADATA and no old database entry yet: we have to insert a new (bogus) database entry: + //we cannot simply skip the whole directory, since sub-items might be in sync! + //Example: directories on left and right differ in case while sub-files are equal + { + //reuse last "in-sync" if available or insert strawman entry (do not try to update thereby removing child elements!!!) + InSyncDir& dir = dbDirs.insert(std::make_pair(dirObj.getObjShortName(), InSyncDir(InSyncDir::DIR_STATUS_STRAW_MAN))).first->second; + toPreserve.insert(&dir); + recurse(dirObj, dir); + } + break; + + //not in sync: reuse last synchronous state: + case DIR_LEFT_SIDE_ONLY: + case DIR_RIGHT_SIDE_ONLY: + { + auto it = dbDirs.find(dirObj.getObjShortName()); + if (it != dbDirs.end()) + { + toPreserve.insert(&it->second); + recurse(dirObj, it->second); //although existing sub-items cannot be in sync, items deleted on both sides *are* in-sync!!! + } + } + break; + } + + //delete removed items (= "in-sync") from database + map_remove_if(dbDirs, [&](const InSyncDir::DirList::value_type& v) -> bool + { + if (toPreserve.find(&v.second) != toPreserve.end()) + return false; + const Zstring& shortName = v.first; + return filter_.passDirFilter(parentRelativeNamePf + shortName, nullptr); + //if directory is not included in "currentDirs", it is either not existing anymore, in which case it should be deleted from database + //or it was excluded via filter, in which case the database entry should be preserved: + //=> all child db elements are also preserved since they are not recursed in the loop above!!! + //=> no problem with filter logic of excluding complete directory subtrees, if top folder is excluded directly! + }); + } + + const HardFilter& filter_; //filter used while scanning directory: generates view on actual files! + const CompareVariant activeCmpVar_; +}; +} + +//####################################################################################################################################### + +std::shared_ptr zen::loadLastSynchronousState(const BaseDirPair& baseDirObj) //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! +{ + const Zstring fileNameLeft = getDBFilename(baseDirObj); + const Zstring fileNameRight = getDBFilename(baseDirObj); + + if (!baseDirObj.isExisting() || + !baseDirObj.isExisting()) + { + //avoid race condition with directory existence check: reading sync.ffs_db may succeed although first dir check had failed => conflicts! + //https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3531351&group_id=234430 + const Zstring filename = !baseDirObj.isExisting() ? fileNameLeft : fileNameRight; + throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" + //it could be due to a to-be-created target directory not yet existing => FileErrorDatabaseNotExisting + replaceCpy(_("Database file %x does not yet exist."), L"%x", fmtFileName(filename))); + } + + //read file data: list of session ID + DirInfo-stream + const DbStreams streamsLeft = ::loadStreams(fileNameLeft); //throw FileError, FileErrorDatabaseNotExisting + const DbStreams streamsRight = ::loadStreams(fileNameRight); // + + //find associated session: there can be at most one session within intersection of left and right ids + for (const auto& streamLeft : streamsLeft) + { + auto itRight = streamsRight.find(streamLeft.first); + if (itRight != streamsRight.end()) + { + return StreamParser::execute(streamLeft.second, //throw FileError + itRight->second, + fileNameLeft, + fileNameRight); + } + } + throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" + + _("Database files do not share a common session.")); +} + + +void zen::saveLastSynchronousState(const BaseDirPair& baseDirObj) //throw FileError +{ + //transactional behaviour! write to tmp files first + const Zstring dbNameLeftTmp = getDBFilename(baseDirObj, true); + const Zstring dbNameRightTmp = getDBFilename(baseDirObj, true); + + const Zstring dbNameLeft = getDBFilename(baseDirObj); + const Zstring dbNameRight = getDBFilename(baseDirObj); + + //delete old tmp file, if necessary -> throws if deletion fails! + removeFile(dbNameLeftTmp); // + removeFile(dbNameRightTmp); //throw FileError + + //(try to) load old database files... + DbStreams streamsLeft; //list of session ID + DirInfo-stream + DbStreams streamsRight; + + try { streamsLeft = ::loadStreams(dbNameLeft ); } + catch (FileError&) {} + try { streamsRight = ::loadStreams(dbNameRight); } + catch (FileError&) {} + //if error occurs: just overwrite old file! User is already informed about issues right after comparing! + + //find associated session: there can be at most one session within intersection of left and right ids + auto itStreamLeftOld = streamsLeft .cend(); + auto itStreamRightOld = streamsRight.cend(); + for (auto iterLeft = streamsLeft.begin(); iterLeft != streamsLeft.end(); ++iterLeft) + { + auto iterRight = streamsRight.find(iterLeft->first); + if (iterRight != streamsRight.end()) + { + itStreamLeftOld = iterLeft; + itStreamRightOld = iterRight; + break; + } + } + + //load last synchrounous state + std::shared_ptr lastSyncState = std::make_shared(InSyncDir::DIR_STATUS_IN_SYNC); + if (itStreamLeftOld != streamsLeft .end() && + itStreamRightOld != streamsRight.end()) + try + { + lastSyncState = StreamParser::execute(itStreamLeftOld ->second, //throw FileError + itStreamRightOld->second, + dbNameLeft, + dbNameRight); + } + catch (FileError&) {} //if error occurs: just overwrite old file! User is already informed about issues right after comparing! + + //update last synchrounous state + UpdateLastSynchronousState::execute(baseDirObj, *lastSyncState); + + //serialize again + BinaryStream updatedStreamLeft; + BinaryStream updatedStreamRight; + StreamGenerator::execute(*lastSyncState, + dbNameLeft, + dbNameRight, + updatedStreamLeft, + updatedStreamRight); //throw FileError + + //check if there is some work to do at all + if (itStreamLeftOld != streamsLeft .end() && updatedStreamLeft == itStreamLeftOld ->second && + itStreamRightOld != streamsRight.end() && updatedStreamRight == itStreamRightOld->second) + return; //some users monitor the *.ffs_db file with RTS => don't touch the file if it isnt't strictly needed + + //erase old session data + if (itStreamLeftOld != streamsLeft.end()) + streamsLeft.erase(itStreamLeftOld); + if (itStreamRightOld != streamsRight.end()) + streamsRight.erase(itStreamRightOld); + + //create new session data + const std::string sessionID = zen::generateGUID(); + + streamsLeft [sessionID] = std::move(updatedStreamLeft); + streamsRight[sessionID] = std::move(updatedStreamRight); + + //write (temp-) files... + zen::ScopeGuard guardTempFileLeft = zen::makeGuard([&] {zen::removeFile(dbNameLeftTmp); }); + saveStreams(streamsLeft, dbNameLeftTmp); //throw FileError + + zen::ScopeGuard guardTempFileRight = zen::makeGuard([&] {zen::removeFile(dbNameRightTmp); }); + saveStreams(streamsRight, dbNameRightTmp); //throw FileError + + //operation finished: rename temp files -> this should work transactionally: + //if there were no write access, creation of temp files would have failed + removeFile(dbNameLeft); // + removeFile(dbNameRight); //throw FileError + renameFile(dbNameLeftTmp, dbNameLeft); // + renameFile(dbNameRightTmp, dbNameRight); // + + guardTempFileLeft. dismiss(); //no need to delete temp files anymore + guardTempFileRight.dismiss(); // +} diff --git a/FreeFileSync/Source/lib/db_file.h b/FreeFileSync/Source/lib/db_file.h new file mode 100644 index 00000000..c432704d --- /dev/null +++ b/FreeFileSync/Source/lib/db_file.h @@ -0,0 +1,102 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef DBFILE_H_834275398588021574 +#define DBFILE_H_834275398588021574 + +#include +#include "../file_hierarchy.h" + +namespace zen +{ +const Zstring SYNC_DB_FILE_ENDING = Zstr(".ffs_db"); + +struct InSyncDescrFile //subset of FileDescriptor +{ + InSyncDescrFile(const Int64& lastWriteTimeRawIn, + const FileId& idIn) : + lastWriteTimeRaw(lastWriteTimeRawIn), + fileId(idIn) {} + + Int64 lastWriteTimeRaw; + FileId fileId; // == file id: optional! (however, always set on Linux, and *generally* available on Windows) +}; + +struct InSyncDescrLink +{ + explicit InSyncDescrLink(const Int64& lastWriteTimeRawIn) : lastWriteTimeRaw(lastWriteTimeRawIn) {} + Int64 lastWriteTimeRaw; +}; + + +//artificial hierarchy of last synchronous state: +struct InSyncFile +{ + InSyncFile(const InSyncDescrFile& l, const InSyncDescrFile& r, CompareVariant cv, const UInt64& fileSizeIn) : left(l), right(r), cmpVar(cv), fileSize(fileSizeIn) {} + InSyncDescrFile left; + InSyncDescrFile right; + CompareVariant cmpVar; //the one active while finding "file in sync" + UInt64 fileSize; //file size must be identical on both sides! +}; + +struct InSyncSymlink +{ + InSyncSymlink(const InSyncDescrLink& l, const InSyncDescrLink& r, CompareVariant cv) : left(l), right(r), cmpVar(cv) {} + InSyncDescrLink left; + InSyncDescrLink right; + CompareVariant cmpVar; +}; + +struct InSyncDir +{ + //for directories we have a logical problem: we cannot have "not existent" as an indicator for + //"no last synchronous state" since this precludes child elements that may be in sync! + enum InSyncStatus + { + DIR_STATUS_IN_SYNC, + DIR_STATUS_STRAW_MAN //there is no last synchronous state, but used as container only + }; + InSyncDir(InSyncStatus statusIn) : status(statusIn) {} + + InSyncStatus status; + + //------------------------------------------------------------------ + typedef std::map DirList; // + typedef std::map FileList; // key: shortName + typedef std::map LinkList; // + //------------------------------------------------------------------ + + DirList dirs; + FileList files; + LinkList symlinks; //non-followed symlinks + + //convenience + InSyncDir& addDir(const Zstring& shortName, InSyncStatus st) + { + //use C++11 emplace when available + return dirs.insert(std::make_pair(shortName, InSyncDir(st))).first->second; + } + + void addFile(const Zstring& shortName, const InSyncDescrFile& dataL, const InSyncDescrFile& dataR, CompareVariant cmpVar, const UInt64& fileSize) + { + files.insert(std::make_pair(shortName, InSyncFile(dataL, dataR, cmpVar, fileSize))); + } + + void addSymlink(const Zstring& shortName, const InSyncDescrLink& dataL, const InSyncDescrLink& dataR, CompareVariant cmpVar) + { + symlinks.insert(std::make_pair(shortName, InSyncSymlink(dataL, dataR, cmpVar))); + } +}; + + +DEFINE_NEW_FILE_ERROR(FileErrorDatabaseNotExisting); + +std::shared_ptr loadLastSynchronousState(const BaseDirPair& baseDirObj); //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! + +void saveLastSynchronousState(const BaseDirPair& baseDirObj); //throw FileError +} + +#endif //DBFILE_H_834275398588021574 diff --git a/FreeFileSync/Source/lib/dir_exist_async.h b/FreeFileSync/Source/lib/dir_exist_async.h new file mode 100644 index 00000000..a6eff1c0 --- /dev/null +++ b/FreeFileSync/Source/lib/dir_exist_async.h @@ -0,0 +1,78 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef DIR_EXIST_HEADER_08173281673432158067342132467183267 +#define DIR_EXIST_HEADER_08173281673432158067342132467183267 + +#include +#include +#include +#include "../process_callback.h" +#include "resolve_path.h" + +namespace zen +{ +namespace +{ +//directory existence checking may hang for non-existent network drives => run asynchronously and update UI! +//- check existence of all directories in parallel! (avoid adding up search times if multiple network drives are not reachable) +//- add reasonable time-out time! +//- avoid checking duplicate entries by design: set +std::set getExistingDirsUpdating(const std::set& dirnames, + std::set& missing, + bool allowUserInteraction, + ProcessCallback& procCallback) +{ + using namespace zen; + + missing.clear(); + + std::list>> futureInfo; + for (const Zstring& dirname : dirnames) + if (!dirname.empty()) //skip empty dirs + futureInfo.push_back(std::make_pair(dirname, async2([=]() -> bool + { +#ifdef ZEN_WIN + //1. login to network share, if necessary + loginNetworkShare(dirname, allowUserInteraction); +#endif + //2. check dir existence + return dirExists(dirname); + }))); + + std::set output; + //don't wait (almost) endlessly like win32 would on not existing network shares: + const boost::system_time endTime = boost::get_system_time() + boost::posix_time::seconds(20); //consider CD-rom insert or hard disk spin up time from sleep + + for (auto& fi : futureInfo) + { + procCallback.reportStatus(replaceCpy(_("Searching for folder %x..."), L"%x", fmtFileName(fi.first), false)); //may throw! + + while (boost::get_system_time() < endTime && + !fi.second.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL / 2))) + procCallback.requestUiRefresh(); //may throw! + + if (fi.second.is_ready() && fi.second.get()) + output.insert(fi.first); + else + missing.insert(fi.first); + } + return output; +} +} + +inline //also silences Clang "unused function" for compilation units depending from getExistingDirsUpdating() only +bool dirExistsUpdating(const Zstring& dirname, bool allowUserInteraction, ProcessCallback& procCallback) +{ + if (dirname.empty()) return false; + std::set missing; + std::set dirsEx = getExistingDirsUpdating({ dirname }, missing, allowUserInteraction, procCallback); + assert(dirsEx.empty() != missing.empty()); + return dirsEx.find(dirname) != dirsEx.end(); +} +} + +#endif //DIR_EXIST_HEADER_08173281673432158067342132467183267 diff --git a/FreeFileSync/Source/lib/dir_lock.cpp b/FreeFileSync/Source/lib/dir_lock.cpp new file mode 100644 index 00000000..47249d3e --- /dev/null +++ b/FreeFileSync/Source/lib/dir_lock.cpp @@ -0,0 +1,688 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** +#include "dir_lock.h" +#include +#include +#include +#include +#include //includes +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ZEN_WIN +#include +#include //includes "windows.h" +#include +#include //login sid +#include //UNLEN + +#elif defined ZEN_LINUX || defined ZEN_MAC +#include //open() +#include // +#include //getsid() +#include //kill() +#include //getpwuid_r() +#endif + +using namespace zen; +using namespace std::rel_ops; + + +namespace +{ +const int EMIT_LIFE_SIGN_INTERVAL = 5; //show life sign; unit: [s] +const int POLL_LIFE_SIGN_INTERVAL = 4; //poll for life sign; unit: [s] +const int DETECT_ABANDONED_INTERVAL = 30; //assume abandoned lock; unit: [s] + +const char LOCK_FORMAT_DESCR[] = "FreeFileSync"; +const int LOCK_FORMAT_VER = 2; //lock file format version +} + +//worker thread +class LifeSigns +{ +public: + LifeSigns(const Zstring& lockfilename) : //throw()!!! siehe SharedDirLock() + lockfilename_(lockfilename) {} //thread safety: make deep copy! + + void operator()() const //thread entry + { + try + { + while (true) + { + boost::this_thread::sleep(boost::posix_time::seconds(EMIT_LIFE_SIGN_INTERVAL)); //interruption point! + + //actual work + emitLifeSign(); //throw () + } + } + catch (const std::exception& e) //exceptions must be catched per thread + { + wxSafeShowMessage(L"FreeFileSync - " + _("An exception occurred"), utfCvrtTo(e.what()) + L" (Dirlock)"); //simple wxMessageBox won't do for threads + } + } + + void emitLifeSign() const //try to append one byte...; throw() + { + const char buffer[1] = {' '}; +#ifdef ZEN_WIN + //ATTENTION: setting file pointer IS required! => use CreateFile/GENERIC_WRITE + SetFilePointerEx! + //although CreateFile/FILE_APPEND_DATA without SetFilePointerEx works locally, it MAY NOT work on some network shares creating a 4 gig file!!! + + const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilename_).c_str(), + GENERIC_READ | GENERIC_WRITE, //use both when writing over network, see comment in file_io.cpp + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + if (fileHandle == INVALID_HANDLE_VALUE) + return; + ZEN_ON_SCOPE_EXIT(::CloseHandle(fileHandle)); + + const LARGE_INTEGER moveDist = {}; + if (!::SetFilePointerEx(fileHandle, //__in HANDLE hFile, + moveDist, //__in LARGE_INTEGER liDistanceToMove, + nullptr, //__out_opt PLARGE_INTEGER lpNewFilePointer, + FILE_END)) //__in DWORD dwMoveMethod + return; + + DWORD bytesWritten = 0; //this parameter is NOT optional: http://blogs.msdn.com/b/oldnewthing/archive/2013/04/04/10407417.aspx + if (!::WriteFile(fileHandle, //_In_ HANDLE hFile, + buffer, //_In_ LPCVOID lpBuffer, + 1, //_In_ DWORD nNumberOfBytesToWrite, + &bytesWritten, //_Out_opt_ LPDWORD lpNumberOfBytesWritten, + nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped + return; + +#elif defined ZEN_LINUX || defined ZEN_MAC + const int fileHandle = ::open(lockfilename_.c_str(), O_WRONLY | O_APPEND); + if (fileHandle == -1) + return; + ZEN_ON_SCOPE_EXIT(::close(fileHandle)); + + const ssize_t bytesWritten = ::write(fileHandle, buffer, 1); + (void)bytesWritten; +#endif + } + +private: + const Zstring lockfilename_; //thread local! atomic ref-count => binary value-type semantics! +}; + + +namespace +{ +UInt64 getLockFileSize(const Zstring& filename) //throw FileError +{ +#ifdef ZEN_WIN + WIN32_FIND_DATA fileInfo = {}; + const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(filename).c_str(), &fileInfo); + if (searchHandle != INVALID_HANDLE_VALUE) + { + ::FindClose(searchHandle); + return UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); + } + const wchar_t functionName[] = L"FindFirstFile"; +#elif defined ZEN_LINUX || defined ZEN_MAC + struct ::stat fileInfo = {}; + if (::stat(filename.c_str(), &fileInfo) == 0) //follow symbolic links + return UInt64(fileInfo.st_size); + const wchar_t functionName[] = L"stat"; +#endif + + const ErrorCode lastError = getLastError(); + const std::wstring errorMsg = replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)); + const std::wstring errorDescr = formatSystemError(functionName, lastError); + throw FileError(errorMsg, errorDescr); +} + + +Zstring deleteAbandonedLockName(const Zstring& lockfilename) //make sure to NOT change file ending! +{ + const size_t pos = lockfilename.rfind(FILE_NAME_SEPARATOR); //search from end + return pos == Zstring::npos ? Zstr("Del.") + lockfilename : + Zstring(lockfilename.c_str(), pos + 1) + //include path separator + Zstr("Del.") + + afterLast(lockfilename, FILE_NAME_SEPARATOR); //returns the whole string if ch not found +} + + +#ifdef ZEN_WIN +Zstring getLoginSid() //throw FileError +{ + HANDLE hToken = 0; + if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle, + TOKEN_ALL_ACCESS, //__in DWORD DesiredAccess, + &hToken)) //__out PHANDLE TokenHandle + throw FileError(_("Cannot get process information."), formatSystemError(L"OpenProcessToken", getLastError())); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken)); + + DWORD bufferSize = 0; + ::GetTokenInformation(hToken, TokenGroups, nullptr, 0, &bufferSize); + + std::vector buffer(bufferSize); + if (!::GetTokenInformation(hToken, //__in HANDLE TokenHandle, + TokenGroups, //__in TOKEN_INFORMATION_CLASS TokenInformationClass, + &buffer[0], //__out_opt LPVOID TokenInformation, + bufferSize, //__in DWORD TokenInformationLength, + &bufferSize)) //__out PDWORD ReturnLength + throw FileError(_("Cannot get process information."), formatSystemError(L"GetTokenInformation", getLastError())); + + auto groups = reinterpret_cast(&buffer[0]); + + for (DWORD i = 0; i < groups->GroupCount; ++i) + if ((groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) != 0) + { + LPTSTR sidStr = nullptr; + if (!::ConvertSidToStringSid(groups->Groups[i].Sid, //__in PSID Sid, + &sidStr)) //__out LPTSTR *StringSid + throw FileError(_("Cannot get process information."), formatSystemError(L"ConvertSidToStringSid", getLastError())); + ZEN_ON_SCOPE_EXIT(::LocalFree(sidStr)); + return sidStr; + } + throw FileError(_("Cannot get process information."), L"no login found"); //shouldn't happen +} +#endif + + +#ifdef ZEN_WIN +typedef DWORD ProcessId; +typedef DWORD SessionId; +#elif defined ZEN_LINUX || defined ZEN_MAC +typedef pid_t ProcessId; +typedef pid_t SessionId; +#endif + +//return ppid on Windows, sid on Linux/Mac, "no value" if process corresponding to "processId" is not existing +Opt getSessionId(ProcessId processId) //throw FileError +{ +#ifdef ZEN_WIN + //note: ::OpenProcess() is no alternative as it may successfully return for crashed processes! -> remark: "WaitForSingleObject" may identify this case! + HANDLE snapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, //__in DWORD dwFlags, + 0); //__in DWORD th32ProcessID + if (snapshot == INVALID_HANDLE_VALUE) + throw FileError(_("Cannot get process information."), formatSystemError(L"CreateToolhelp32Snapshot", getLastError())); + ZEN_ON_SCOPE_EXIT(::CloseHandle(snapshot)); + + PROCESSENTRY32 processEntry = {}; + processEntry.dwSize = sizeof(processEntry); + + if (!::Process32First(snapshot, //__in HANDLE hSnapshot, + &processEntry)) //__inout LPPROCESSENTRY32 lppe + throw FileError(_("Cannot get process information."), formatSystemError(L"Process32First", getLastError())); //ERROR_NO_MORE_FILES not possible + do + { + if (processEntry.th32ProcessID == processId) //yes, MSDN says this is the way: http://msdn.microsoft.com/en-us/library/windows/desktop/ms684868(v=vs.85).aspx + return processEntry.th32ParentProcessID; //parent id is stable, even if parent process has already terminated! + } + while (::Process32Next(snapshot, &processEntry)); + if (::GetLastError() != ERROR_NO_MORE_FILES) //yes, they call it "files" + throw FileError(_("Cannot get process information."), formatSystemError(L"Process32Next", getLastError())); + + return NoValue(); + +#elif defined ZEN_LINUX || defined ZEN_MAC + if (::kill(processId, 0) != 0) //sig == 0: no signal sent, just existence check + return NoValue(); + + pid_t procSid = ::getsid(processId); //NOT to be confused with "login session", e.g. not stable on OS X!!! + if (procSid == -1) + throw FileError(_("Cannot get process information."), formatSystemError(L"getsid", getLastError())); + + return procSid; +#endif +} + + +class FromCurrentProcess {}; //tag + +struct LockInformation //throw FileError +{ + explicit LockInformation(FromCurrentProcess) : + lockId(zen::generateGUID()), + sessionId(), //dummy value +#ifdef ZEN_WIN + processId(::GetCurrentProcessId()) //never fails + { + DWORD bufferSize = 0; + ::GetComputerNameEx(ComputerNameDnsFullyQualified, nullptr, &bufferSize); //get required buffer size + + std::vector buffer(bufferSize); + if (!::GetComputerNameEx(ComputerNameDnsFullyQualified, //__in COMPUTER_NAME_FORMAT NameType, + bufferSize > 0 ? &buffer[0] : nullptr, //__out LPTSTR lpBuffer, + &bufferSize)) //__inout LPDWORD lpnSize + throw FileError(_("Cannot get process information."), formatSystemError(L"GetComputerNameEx", getLastError())); + + computerName = "Windows." + utfCvrtTo(&buffer[0]); + + bufferSize = UNLEN + 1; + buffer.resize(bufferSize); + if (!::GetUserName(&buffer[0], //__out LPTSTR lpBuffer, + &bufferSize)) //__inout LPDWORD lpnSize + throw FileError(_("Cannot get process information."), formatSystemError(L"GetUserName", getLastError())); + userId = utfCvrtTo(&buffer[0]); + +#elif defined ZEN_LINUX || defined ZEN_MAC + processId(::getpid()) //never fails + { + std::vector buffer(10000); + + if (::gethostname(&buffer[0], buffer.size()) != 0) + throw FileError(_("Cannot get process information."), formatSystemError(L"gethostname", getLastError())); + computerName += "Linux."; //distinguish linux/windows lock files + computerName += &buffer[0]; + + if (::getdomainname(&buffer[0], buffer.size()) != 0) + throw FileError(_("Cannot get process information."), formatSystemError(L"getdomainname", getLastError())); + computerName += "."; + computerName += &buffer[0]; + + const uid_t userIdNo = ::getuid(); //never fails + userId = numberTo(userIdNo); + + //the id alone is not very distinctive, e.g. often 1000 on Ubuntu => add name + buffer.resize(std::max(buffer.size(), ::sysconf(_SC_GETPW_R_SIZE_MAX))); //::sysconf may return long(-1) + struct passwd buffer2 = {}; + struct passwd* pwsEntry = nullptr; + if (::getpwuid_r(userIdNo, &buffer2, &buffer[0], buffer.size(), &pwsEntry) != 0) //getlogin() is deprecated and not working on Ubuntu at all!!! + throw FileError(_("Cannot get process information."), formatSystemError(L"getpwuid_r", getLastError())); + if (!pwsEntry) + throw FileError(_("Cannot get process information."), L"no login found"); //should not happen? + userId += '(' + std::string(pwsEntry->pw_name) + ')'; //follow Linux naming convention "1000(zenju)" +#endif + + Opt sessionIdTmp = getSessionId(processId); //throw FileError + if (!sessionIdTmp) + throw FileError(_("Cannot get process information."), L"no session id found"); //should not happen? + sessionId = *sessionIdTmp; + } + + explicit LockInformation(BinStreamIn& stream) //throw UnexpectedEndOfStreamError + { + char tmp[sizeof(LOCK_FORMAT_DESCR)] = {}; + readArray(stream, &tmp, sizeof(tmp)); //file format header + const int lockFileVersion = readNumber(stream); // + + if (!std::equal(std::begin(tmp), std::end(tmp), std::begin(LOCK_FORMAT_DESCR)) || + lockFileVersion != LOCK_FORMAT_VER) + throw UnexpectedEndOfStreamError(); //well, not really...!? + + lockId = readContainer(stream); // + computerName = readContainer(stream); //UnexpectedEndOfStreamError + userId = readContainer(stream); // + sessionId = static_cast(readNumber(stream)); //[!] conversion + processId = static_cast(readNumber(stream)); //[!] conversion + } + + void toStream(BinStreamOut& stream) const //throw () + { + writeArray(stream, LOCK_FORMAT_DESCR, sizeof(LOCK_FORMAT_DESCR)); + writeNumber(stream, LOCK_FORMAT_VER); + + assert_static(sizeof(processId) <= sizeof(std::uint64_t)); //ensure cross-platform compatibility! + assert_static(sizeof(sessionId) <= sizeof(std::uint64_t)); // + + writeContainer(stream, lockId); + writeContainer(stream, computerName); + writeContainer(stream, userId); + writeNumber(stream, sessionId); + writeNumber(stream, processId); + } + + std::string lockId; //16 byte GUID - a universal identifier for this lock (no matter what the path is, considering symlinks, distributed network, etc.) + + //identify local computer + std::string computerName; //format: HostName.DomainName + std::string userId; + + //identify running process + SessionId sessionId; //Windows: parent process id; Linux/OS X: session of the process, NOT the user + ProcessId processId; +}; + + +//wxGetFullHostName() is a performance killer for some users, so don't touch! + + +LockInformation retrieveLockInfo(const Zstring& lockfilename) //throw FileError +{ + BinStreamIn streamIn = loadBinStream(lockfilename); //throw FileError + try + { + return LockInformation(streamIn); //throw UnexpectedEndOfStreamError + } + catch (UnexpectedEndOfStreamError&) + { + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(lockfilename)), L"unexpected end of stream"); + } +} + + +inline +std::string retrieveLockId(const Zstring& lockfilename) //throw FileError +{ + return retrieveLockInfo(lockfilename).lockId; //throw FileError +} + + +enum ProcessStatus +{ + PROC_STATUS_NOT_RUNNING, + PROC_STATUS_RUNNING, + PROC_STATUS_ITS_US, + PROC_STATUS_CANT_TELL +}; + +ProcessStatus getProcessStatus(const LockInformation& lockInfo) //throw FileError +{ + const LockInformation localInfo((FromCurrentProcess())); //throw FileError + + if (lockInfo.computerName != localInfo.computerName || + lockInfo.userId != localInfo.userId) //another user may run a session right now! + return PROC_STATUS_CANT_TELL; //lock owned by different computer in this network + + if (lockInfo.sessionId == localInfo.sessionId && + lockInfo.processId == localInfo.processId) //obscure, but possible: deletion failed or a lock file is "stolen" and put back while the program is running + return PROC_STATUS_ITS_US; + + if (Opt sessionId = getSessionId(lockInfo.processId)) //throw FileError + return *sessionId == lockInfo.sessionId ? PROC_STATUS_RUNNING : PROC_STATUS_NOT_RUNNING; + return PROC_STATUS_NOT_RUNNING; +} + + +const std::int64_t TICKS_PER_SEC = ticksPerSec(); //= 0 on error + + +void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError +{ + std::wstring infoMsg = _("Waiting while directory is locked:") + L' ' + fmtFileName(lockfilename); + + if (callback) + callback->reportStatus(infoMsg); + //--------------------------------------------------------------- + try + { + //convenience optimization only: if we know the owning process crashed, we needn't wait DETECT_ABANDONED_INTERVAL sec + bool lockOwnderDead = false; + std::string originalLockId; //empty if it cannot be retrieved + try + { + const LockInformation& lockInfo = retrieveLockInfo(lockfilename); //throw FileError + //enhance status message and show which user is holding the lock: + infoMsg += L" | " + _("Lock owner:") + L' ' + utfCvrtTo(lockInfo.userId); + + originalLockId = lockInfo.lockId; + switch (getProcessStatus(lockInfo)) //throw FileError + { + case PROC_STATUS_ITS_US: //since we've already passed LockAdmin, the lock file seems abandoned ("stolen"?) although it's from this process + case PROC_STATUS_NOT_RUNNING: + lockOwnderDead = true; + break; + case PROC_STATUS_RUNNING: + case PROC_STATUS_CANT_TELL: + break; + } + } + catch (FileError&) {} //logfile may be only partly written -> this is no error! + + UInt64 fileSizeOld; + TickVal lastLifeSign = getTicks(); + + while (true) + { + const TickVal now = getTicks(); + const UInt64 fileSizeNew = ::getLockFileSize(lockfilename); //throw FileError + + if (TICKS_PER_SEC <= 0 || !lastLifeSign.isValid() || !now.isValid()) + throw FileError(L"System timer failed."); //no i18n: "should" never throw ;) + + if (fileSizeNew != fileSizeOld) //received life sign from lock + { + fileSizeOld = fileSizeNew; + lastLifeSign = now; + } + + if (lockOwnderDead || //no need to wait any longer... + dist(lastLifeSign, now) / TICKS_PER_SEC > DETECT_ABANDONED_INTERVAL) + { + DirLock dummy(deleteAbandonedLockName(lockfilename), callback); //throw FileError + + //now that the lock is in place check existence again: meanwhile another process may have deleted and created a new lock! + + if (!originalLockId.empty()) + if (retrieveLockId(lockfilename) != originalLockId) //throw FileError -> since originalLockId is filled, we are not expecting errors! + return; //another process has placed a new lock, leave scope: the wait for the old lock is technically over... + + if (::getLockFileSize(lockfilename) != fileSizeOld) //throw FileError + continue; //late life sign + + removeFile(lockfilename); //throw FileError + return; + } + + //wait some time... + assert_static(1000 * POLL_LIFE_SIGN_INTERVAL % GUI_CALLBACK_INTERVAL == 0); + for (size_t i = 0; i < 1000 * POLL_LIFE_SIGN_INTERVAL / GUI_CALLBACK_INTERVAL; ++i) + { + if (callback) callback->requestUiRefresh(); + boost::this_thread::sleep(boost::posix_time::milliseconds(GUI_CALLBACK_INTERVAL)); + + if (callback) + { + //one signal missed: it's likely this is an abandoned lock => show countdown + if (dist(lastLifeSign, now) / TICKS_PER_SEC > EMIT_LIFE_SIGN_INTERVAL) + { + const int remainingSeconds = std::max(0, DETECT_ABANDONED_INTERVAL - dist(lastLifeSign, getTicks()) / TICKS_PER_SEC); + const std::wstring remSecMsg = replaceCpy(_P("1 sec", "%x sec", remainingSeconds), L"%x", numberTo(remainingSeconds)); + callback->reportStatus(infoMsg + L" | " + _("Detecting abandoned lock...") + L' ' + remSecMsg); + } + else + callback->reportStatus(infoMsg); //emit a message in any case (might clear other one) + } + } + } + } + catch (FileError&) + { + if (!somethingExists(lockfilename)) //a benign(?) race condition with FileError + return; //what we are waiting for... + throw; + } +} + + +void releaseLock(const Zstring& lockfilename) //throw () +{ + try + { + removeFile(lockfilename); //throw FileError + } + catch (FileError&) {} +} + + +bool tryLock(const Zstring& lockfilename) //throw FileError +{ +#ifdef ZEN_WIN + const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilename).c_str(), + GENERIC_READ | GENERIC_WRITE, //use both when writing over network, see comment in file_io.cpp + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + nullptr); + if (fileHandle == INVALID_HANDLE_VALUE) + { + const DWORD lastError = ::GetLastError(); + if (lastError == ERROR_FILE_EXISTS || //confirmed to be used + lastError == ERROR_ALREADY_EXISTS) //comment on msdn claims, this one is used on Windows Mobile 6 + return false; + else + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(lockfilename)), formatSystemError(L"CreateFile", getLastError())); + } + ScopeGuard guardLockFile = zen::makeGuard([&] { removeFile(lockfilename); }); + FileOutput fileOut(fileHandle, lockfilename); //pass handle ownership + + //be careful to avoid CreateFile() + CREATE_ALWAYS on a hidden file -> see file_io.cpp + //=> we don't need it that badly //::SetFileAttributes(applyLongPathPrefix(lockfilename).c_str(), FILE_ATTRIBUTE_HIDDEN); //(try to) hide it + +#elif defined ZEN_LINUX || defined ZEN_MAC + ::umask(0); //important! -> why? + //O_EXCL contains a race condition on NFS file systems: http://linux.die.net/man/2/open + const int fileHandle = ::open(lockfilename.c_str(), O_CREAT | O_WRONLY | O_EXCL, S_IRWXU | S_IRWXG | S_IRWXO); + if (fileHandle == -1) + { + if (errno == EEXIST) + return false; + else + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(lockfilename)), formatSystemError(L"open", getLastError())); + } + ScopeGuard guardLockFile = zen::makeGuard([&] { removeFile(lockfilename); }); + FileOutputUnbuffered fileOut(fileHandle, lockfilename); //pass handle ownership +#endif + + //write housekeeping info: user, process info, lock GUID + BinaryStream binStream; + { + BinStreamOut streamOut; + LockInformation(FromCurrentProcess()).toStream(streamOut); + binStream = streamOut.get(); + } + if (!binStream.empty()) + fileOut.write(&*binStream.begin(), binStream.size()); //throw FileError + + guardLockFile.dismiss(); //lockfile created successfully + return true; +} +} + + +class DirLock::SharedDirLock +{ +public: + SharedDirLock(const Zstring& lockfilename, DirLockCallback* callback) : //throw FileError + lockfilename_(lockfilename) + { + while (!::tryLock(lockfilename)) //throw FileError + ::waitOnDirLock(lockfilename, callback); // + + threadObj = boost::thread(LifeSigns(lockfilename)); + } + + ~SharedDirLock() + { + threadObj.interrupt(); //thread lifetime is subset of this instances's life + threadObj.join(); //we assert precondition "threadObj.joinable()"!!! + + ::releaseLock(lockfilename_); //throw () + } + +private: + SharedDirLock(const DirLock&); + SharedDirLock& operator=(const DirLock&); + + const Zstring lockfilename_; + boost::thread threadObj; +}; + + +class DirLock::LockAdmin //administrate all locks held by this process to avoid deadlock by recursion +{ +public: + static LockAdmin& instance() + { + static LockAdmin inst; + return inst; + } + + //create or retrieve a SharedDirLock + std::shared_ptr retrieve(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError + { + tidyUp(); + + //optimization: check if we already own a lock for this path + auto iterGuid = fileToGuid.find(lockfilename); + if (iterGuid != fileToGuid.end()) + if (const std::shared_ptr& activeLock = getActiveLock(iterGuid->second)) //returns null-lock if not found + return activeLock; //SharedDirLock is still active -> enlarge circle of shared ownership + + try //check based on lock GUID, deadlock prevention: "lockfilename" may be an alternative name for a lock already owned by this process + { + const std::string lockId = retrieveLockId(lockfilename); //throw FileError + if (const std::shared_ptr& activeLock = getActiveLock(lockId)) //returns null-lock if not found + { + fileToGuid[lockfilename] = lockId; //found an alias for one of our active locks + return activeLock; + } + } + catch (FileError&) {} //catch everything, let SharedDirLock constructor deal with errors, e.g. 0-sized/corrupted lock files + + //lock not owned by us => create a new one + auto newLock = std::make_shared(lockfilename, callback); //throw FileError + const std::string& newLockGuid = retrieveLockId(lockfilename); //throw FileError + + //update registry + fileToGuid[lockfilename] = newLockGuid; //throw() + guidToLock[newLockGuid] = newLock; // + + return newLock; + } + +private: + LockAdmin() {} + LockAdmin(const LockAdmin&); //=delete + LockAdmin& operator=(const LockAdmin&); //=delete + + typedef std::string UniqueId; + typedef std::map FileToGuidMap; //n:1 handle uppper/lower case correctly + typedef std::map> GuidToLockMap; //1:1 + + std::shared_ptr getActiveLock(const UniqueId& lockId) //returns null if none found + { + auto iterLock = guidToLock.find(lockId); + return iterLock != guidToLock.end() ? iterLock->second.lock() : nullptr; //try to get shared_ptr; throw() + } + + void tidyUp() //remove obsolete lock entries + { + map_remove_if(guidToLock, [ ](const GuidToLockMap::value_type& v) { return !v.second.lock(); }); + map_remove_if(fileToGuid, [&](const FileToGuidMap::value_type& v) { return guidToLock.find(v.second) == guidToLock.end(); }); + } + + FileToGuidMap fileToGuid; //lockname |-> GUID; locks can be referenced by a lockfilename or alternatively a GUID + GuidToLockMap guidToLock; //GUID |-> "shared lock ownership" +}; + + +DirLock::DirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError +{ + if (callback) + callback->reportStatus(replaceCpy(_("Creating file %x"), L"%x", fmtFileName(lockfilename))); + +#ifdef ZEN_WIN + const DWORD bufferSize = 10000; + std::vector volName(bufferSize); + if (::GetVolumePathName(lockfilename.c_str(), //__in LPCTSTR lpszFileName, + &volName[0], //__out LPTSTR lpszVolumePathName, + bufferSize)) //__in DWORD cchBufferLength + { + DWORD dt = ::GetDriveType(&volName[0]); + if (dt == DRIVE_CDROM) + return; //we don't need a lock for a CD ROM + } +#endif + + sharedLock = LockAdmin::instance().retrieve(lockfilename, callback); //throw FileError +} diff --git a/FreeFileSync/Source/lib/dir_lock.h b/FreeFileSync/Source/lib/dir_lock.h new file mode 100644 index 00000000..ec2a431a --- /dev/null +++ b/FreeFileSync/Source/lib/dir_lock.h @@ -0,0 +1,45 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** +#ifndef DIR_LOCK_H_INCLUDED +#define DIR_LOCK_H_INCLUDED + +#include +#include + +namespace zen +{ +const size_t GUI_CALLBACK_INTERVAL = 100; + +struct DirLockCallback //while waiting for the lock +{ + virtual ~DirLockCallback() {} + virtual void requestUiRefresh() = 0; //allowed to throw exceptions + virtual void reportStatus(const std::wstring& text) = 0; +}; + +/* +RAII structure to place a directory lock against other FFS processes: + - recursive locking supported, even with alternate lockfile names, e.g. via symlinks, network mounts etc. + - ownership shared between all object instances refering to a specific lock location(= GUID) + - can be copied safely and efficiently! (ref-counting) + - detects and resolves abandoned locks (instantly if lock is associated with local pc, else after 30 seconds) + - temporary locks created during abandoned lock resolution keep "lockfilename"'s extension + - race-free (Windows, almost on Linux(NFS)) + - NOT thread-safe! (1. static LockAdmin 2. directory name aliases must be resolved sequentially!) +*/ +class DirLock +{ +public: + DirLock(const Zstring& lockfilename, DirLockCallback* callback = nullptr); //throw FileError, callback only used during construction + +private: + class LockAdmin; + class SharedDirLock; + std::shared_ptr sharedLock; +}; +} + +#endif // DIR_LOCK_H_INCLUDED diff --git a/FreeFileSync/Source/lib/error_log.h b/FreeFileSync/Source/lib/error_log.h new file mode 100644 index 00000000..2971f746 --- /dev/null +++ b/FreeFileSync/Source/lib/error_log.h @@ -0,0 +1,43 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef ERROR_LOG_89734181783491324134 +#define ERROR_LOG_89734181783491324134 + +#include +#include +#include +#include "ffs_paths.h" + + +namespace zen +{ +//write error message to a file (even with corrupted stack)- call in desperate situations when no other means of error handling is available +void logError(const std::string& msg); //throw() + + + + + + + + + +//##################### implementation ############################ +inline +void logError(const std::string& msg) //throw() +{ + assert(false); //this is stuff we like to debug + const std::string logEntry = "[" + formatTime(FORMAT_DATE) + " "+ formatTime(FORMAT_TIME) + "] " + msg; + try + { + saveBinStream(getConfigDir() + Zstr("LastError.log"), logEntry); //throw FileError + } + catch (const FileError&) {} +} +} + +#endif //ERROR_LOG_89734181783491324134 diff --git a/FreeFileSync/Source/lib/ffs_paths.cpp b/FreeFileSync/Source/lib/ffs_paths.cpp new file mode 100644 index 00000000..5c775d3e --- /dev/null +++ b/FreeFileSync/Source/lib/ffs_paths.cpp @@ -0,0 +1,144 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "ffs_paths.h" +#include +#include +#include +#include + +#ifdef ZEN_MAC +#include +#include +#include +//keep in .cpp file to not pollute global namespace! e.g. with UInt64: +#include //LSFindApplicationForInfo +#endif + +using namespace zen; + + +namespace +{ +#if defined ZEN_WIN || defined ZEN_LINUX +inline +Zstring getExecutableDir() //directory containing executable WITH path separator at end +{ + return appendSeparator(beforeLast(utfCvrtTo(wxStandardPaths::Get().GetExecutablePath()), FILE_NAME_SEPARATOR)); +} +#endif + +#ifdef ZEN_WIN +inline +Zstring getInstallDir() //root install directory WITH path separator at end +{ + return appendSeparator(beforeLast(beforeLast(getExecutableDir(), FILE_NAME_SEPARATOR), FILE_NAME_SEPARATOR)); +} +#endif + + +#ifdef ZEN_WIN +inline +bool isPortableVersion() { return !fileExists(getInstallDir() + L"uninstall.exe"); } //this check is a bit lame... +#elif defined ZEN_LINUX +inline +bool isPortableVersion() { return !endsWith(getExecutableDir(), "/bin/"); } //this check is a bit lame... +#endif +} + + +bool zen::manualProgramUpdateRequired() +{ +#if defined ZEN_WIN || defined ZEN_MAC + return true; +#elif defined ZEN_LINUX + return isPortableVersion(); //locally installed version is updated by system +#endif +} + + +Zstring zen::getResourceDir() +{ + //make independent from wxWidgets global variable "appname"; support being called by RealtimeSync + auto appName = wxTheApp->GetAppName(); + wxTheApp->SetAppName(L"FreeFileSync"); + ZEN_ON_SCOPE_EXIT(wxTheApp->SetAppName(appName)); + +#ifdef ZEN_WIN + return getInstallDir(); +#elif defined ZEN_LINUX + if (isPortableVersion()) + return getExecutableDir(); + else //use OS' standard paths + return appendSeparator(toZ(wxStandardPathsBase::Get().GetResourcesDir())); +#elif defined ZEN_MAC + return appendSeparator(toZ(wxStandardPathsBase::Get().GetResourcesDir())); //if packaged, used "Contents/Resources", else the executable directory +#endif +} + + +Zstring zen::getConfigDir() +{ + //make independent from wxWidgets global variable "appname"; support being called by RealtimeSync + auto appName = wxTheApp->GetAppName(); + wxTheApp->SetAppName(L"FreeFileSync"); + ZEN_ON_SCOPE_EXIT(wxTheApp->SetAppName(appName)); + +#ifdef ZEN_WIN + if (isPortableVersion()) + return getInstallDir(); +#elif defined ZEN_LINUX + if (isPortableVersion()) + return getExecutableDir(); +#elif defined ZEN_MAC + //portable apps do not seem common on OS - fine with me: http://theocacao.com/document.page/319 +#endif + //use OS' standard paths + Zstring userDirectory = toZ(wxStandardPathsBase::Get().GetUserDataDir()); + + if (!dirExists(userDirectory)) + try + { + makeDirectory(userDirectory); //throw FileError + } + catch (const FileError&) {} + + return appendSeparator(userDirectory); +} + + +//this function is called by RealtimeSync!!! +Zstring zen::getFreeFileSyncLauncher() +{ +#ifdef ZEN_WIN + return getInstallDir() + Zstr("FreeFileSync.exe"); + +#elif defined ZEN_LINUX + return getExecutableDir() + Zstr("FreeFileSync"); + +#elif defined ZEN_MAC + CFURLRef appURL = nullptr; + ZEN_ON_SCOPE_EXIT(if (appURL) ::CFRelease(appURL)); + + if (::LSFindApplicationForInfo(kLSUnknownCreator, // OSType inCreator, + CFSTR("net.SourceForge.FreeFileSync"),//CFStringRef inBundleID, + nullptr, //CFStringRef inName, + nullptr, //FSRef *outAppRef, + &appURL) == noErr) //CFURLRef *outAppURL + if (appURL) + if (CFURLRef absUrl = ::CFURLCopyAbsoluteURL(appURL)) + { + ZEN_ON_SCOPE_EXIT(::CFRelease(absUrl)); + + if (CFStringRef path = ::CFURLCopyFileSystemPath(absUrl, kCFURLPOSIXPathStyle)) + { + ZEN_ON_SCOPE_EXIT(::CFRelease(path)); + return appendSeparator(osx::cfStringToZstring(path)) + "Contents/MacOS/FreeFileSync"; + } + } + return Zstr("./FreeFileSync"); //fallback: at least give some hint... +#endif +} diff --git a/FreeFileSync/Source/lib/ffs_paths.h b/FreeFileSync/Source/lib/ffs_paths.h new file mode 100644 index 00000000..28516a3f --- /dev/null +++ b/FreeFileSync/Source/lib/ffs_paths.h @@ -0,0 +1,25 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef STANDARDPATHS_H_84275908342534253425 +#define STANDARDPATHS_H_84275908342534253425 + +#include + +namespace zen +{ +//------------------------------------------------------------------------------ +//global program directories +//------------------------------------------------------------------------------ +Zstring getResourceDir(); //resource directory WITH path separator at end +Zstring getConfigDir (); //config directory WITH path separator at end +//------------------------------------------------------------------------------ + +Zstring getFreeFileSyncLauncher(); //full path to application launcher C:\...\FreeFileSync.exe +bool manualProgramUpdateRequired(); +} + +#endif //STANDARDPATHS_H_84275908342534253425 diff --git a/FreeFileSync/Source/lib/generate_logfile.h b/FreeFileSync/Source/lib/generate_logfile.h new file mode 100644 index 00000000..34e1c247 --- /dev/null +++ b/FreeFileSync/Source/lib/generate_logfile.h @@ -0,0 +1,181 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef GEN_LOGFILE_H_93172643216748973216458732165415 +#define GEN_LOGFILE_H_93172643216748973216458732165415 + +#include +#include +#include +#include +#include "ffs_paths.h" + + +namespace zen +{ +struct SummaryInfo +{ + std::wstring jobName; //may be empty + std::wstring finalStatus; + int itemsSynced; + Int64 dataSynced; + int itemsTotal; + Int64 dataTotal; + int64_t totalTime; //unit: [sec] +}; + +void saveLogToFile(const SummaryInfo& summary, //throw FileError + const ErrorLog& log, + FileOutput& fileOut); + +void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError + const ErrorLog& log, + size_t maxBytesToWrite); + + + + + +//####################### implementation ####################### +namespace +{ +std::wstring generateLogHeader(const SummaryInfo& s) +{ + assert(s.itemsSynced <= s.itemsTotal); + assert(s.dataSynced <= s.dataTotal); + + std::wstring output; + + //write header + std::wstring headerLine = formatTime(FORMAT_DATE); + if (!s.jobName.empty()) + headerLine += L" - " + s.jobName; + headerLine += L": " + s.finalStatus; + + //assemble results box + std::vector results; + results.push_back(headerLine); + results.push_back(L""); + + const wchar_t tabSpace[] = L" "; + + std::wstring itemsProc = tabSpace + _("Items processed:") + L" " + toGuiString(s.itemsSynced); //show always, even if 0! + if (s.itemsSynced != 0 || s.dataSynced != 0) //[!] don't show 0 bytes processed if 0 items were processed + itemsProc += + L" (" + filesizeToShortString(s.dataSynced) + L")"; + results.push_back(itemsProc); + + if (s.itemsTotal != 0 || s.dataTotal != 0) //=: sync phase was reached and there were actual items to sync + { + if (s.itemsSynced != s.itemsTotal || + s.dataSynced != s.dataTotal) + results.push_back(tabSpace + _("Items remaining:") + L" " + toGuiString(s.itemsTotal - s.itemsSynced) + L" (" + filesizeToShortString(s.dataTotal - s.dataSynced) + L")"); + } + + results.push_back(tabSpace + _("Total time:") + L" " + copyStringTo(wxTimeSpan::Seconds(s.totalTime).Format())); + + //calculate max width, this considers UTF-16 only, not true Unicode...but maybe good idea? those 2-char-UTF16 codes are usually wider than fixed width chars anyway! + size_t sepLineLen = 0; + std::for_each(results.begin(), results.end(), [&](const std::wstring& str) { sepLineLen = std::max(sepLineLen, str.size()); }); + + output.resize(output.size() + sepLineLen + 1, L'_'); + output += L'\n'; + + std::for_each(results.begin(), results.end(), [&](const std::wstring& str) { output += L'|'; output += str; output += L'\n'; }); + + output += L'|'; + output.resize(output.size() + sepLineLen, L'_'); + output += L'\n'; + + return output; +} +} + + +inline +void saveLogToFile(const SummaryInfo& summary, //throw FileError + const ErrorLog& log, + FileOutput& fileOut) +{ + Utf8String header = utfCvrtTo(generateLogHeader(summary)); + replace(header, '\n', LINE_BREAK); //don't replace line break any earlier + header += LINE_BREAK; //make sure string is not empty! + + fileOut.write(&*header.begin(), header.size()); //throw FileError + + //write log items one after the other instead of creating one big string: memory allocation might fail; think 1 million entries! + for (auto iter = log.begin(); iter != log.end(); ++iter) + { + Utf8String msg = replaceCpy(utfCvrtTo(formatMessage(*iter)), '\n', LINE_BREAK); + msg += LINE_BREAK; //make sure string is not empty! + + fileOut.write(&*msg.begin(), msg.size()); //throw FileError + } +} + + +inline +void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError + const ErrorLog& log, + size_t maxBytesToWrite) //log may be *huge*, e.g. 1 million items; LastSyncs.log *must not* create performance problems! +{ + const Zstring filename = getConfigDir() + Zstr("LastSyncs.log"); + + Utf8String newStream = utfCvrtTo(generateLogHeader(summary)); + replace(newStream, '\n', LINE_BREAK); //don't replace line break any earlier + newStream += LINE_BREAK; + + //check size of "newStream": memory allocation might fail - think 1 million entries! + for (auto iter = log.begin(); iter != log.end(); ++iter) + { + newStream += replaceCpy(utfCvrtTo(formatMessage(*iter)), '\n', LINE_BREAK); + newStream += LINE_BREAK; + + if (newStream.size() > maxBytesToWrite) + { + newStream += "[...]"; + newStream += LINE_BREAK; + break; + } + } + + //fill up the rest of permitted space by appending old log + if (newStream.size() < maxBytesToWrite) + { + Utf8String oldStream; + try + { + oldStream = loadBinStream(filename); //throw FileError + } + catch (FileError&) {} + + if (!oldStream.empty()) + { + newStream += LINE_BREAK; + newStream += LINE_BREAK; + newStream += oldStream; //impliticly limited by "maxBytesToWrite"! + + //truncate size if required + if (newStream.size() > maxBytesToWrite) + { + //but do not cut in the middle of a row + auto iter = std::search(newStream.cbegin() + maxBytesToWrite, newStream.cend(), std::begin(LINE_BREAK), std::end(LINE_BREAK) - 1); + if (iter != newStream.cend()) + { + newStream.resize(iter - newStream.cbegin()); + newStream += LINE_BREAK; + + newStream += "[...]"; + newStream += LINE_BREAK; + } + } + } + } + + saveBinStream(filename, newStream); //throw FileError +} +} + +#endif //GEN_LOGFILE_H_93172643216748973216458732165415 diff --git a/FreeFileSync/Source/lib/hard_filter.cpp b/FreeFileSync/Source/lib/hard_filter.cpp new file mode 100644 index 00000000..687aecd8 --- /dev/null +++ b/FreeFileSync/Source/lib/hard_filter.cpp @@ -0,0 +1,404 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "hard_filter.h" +#include +#include +#include +#include +#include +//#include "../structures.h" + +using namespace zen; + +//inline bool operator<(const std::type_info& lhs, const std::type_info& rhs) { return lhs.before(rhs) != 0; } -> not working on GCC :( + + +//-------------------------------------------------------------------------------------------------- +bool zen::operator<(const HardFilter& lhs, const HardFilter& rhs) +{ + if (typeid(lhs) != typeid(rhs)) + return typeid(lhs).before(typeid(rhs)) != 0; //note: in worst case, order is guaranteed to be stable only during each program run + + //this and other are same type: + return lhs.cmpLessSameType(rhs); +} + + +//void HardFilter::saveFilter(ZstreamOut& stream) const //serialize derived object +//{ +// //save type information +// writeString(stream, uniqueClassIdentifier()); +// +// //save actual object +// save(stream); +//} + + +//HardFilter::FilterRef HardFilter::loadFilter(ZstreamIn& stream) //throw UnexpectedEndOfStreamError +//{ +// //read type information +// const std::string uniqueClassId = readString(stream); //throw UnexpectedEndOfStreamError +// +// //read actual object +// if (uniqueClassId == "NullFilter") +// return NullFilter::load(stream); +// else if (uniqueClassId == "NameFilter") +// return NameFilter::load(stream); +// else if (uniqueClassId == "CombinedFilter") +// return CombinedFilter::load(stream); +// else +// throw std::logic_error("Programming Error: Unknown filter!"); +//} + + +namespace +{ +//constructing them in addFilterEntry becomes perf issue for large filter lists +const Zstring asterisk(Zstr('*')); +const Zstring sepAsterisk = FILE_NAME_SEPARATOR + asterisk; +const Zstring asteriskSep = asterisk + FILE_NAME_SEPARATOR; +const Zstring asteriskSepAsterisk = asteriskSep + asterisk; +} + + +void addFilterEntry(const Zstring& filterPhrase, std::vector& fileFilter, std::vector& directoryFilter) +{ +#if defined ZEN_WIN || defined ZEN_MAC + //Windows does NOT distinguish between upper/lower-case + Zstring filterFormatted = filterPhrase; + makeUpper(filterFormatted); +#elif defined ZEN_LINUX + const Zstring& filterFormatted = filterPhrase; + //Linux DOES distinguish between upper/lower-case: nothing to do here +#endif + /* + phrase | action + +---------+-------- + | \blah | remove \ + | \*blah | remove \ + | \*\blah | remove \ + | \*\* | remove \ + +---------+-------- + | *blah | + | *\blah | -> add blah + | *\*blah | -> add *blah + +---------+-------- + | blah\ | remove \; directory only + | blah*\ | remove \; directory only + | blah\*\ | remove \; directory only + +---------+-------- + | blah* | + | blah\* | add blah for directory only + | blah*\* | add blah* for directory only + +---------+-------- + */ + auto processTail = [&fileFilter, &directoryFilter](const Zstring& phrase) + { + if (endsWith(phrase, FILE_NAME_SEPARATOR)) //only relevant for directory filtering + { + const Zstring dirPhrase = beforeLast(phrase, FILE_NAME_SEPARATOR); + if (!dirPhrase.empty()) + directoryFilter.push_back(dirPhrase); + } + else if (!phrase.empty()) + { + fileFilter .push_back(phrase); + directoryFilter.push_back(phrase); + if (endsWith(phrase, sepAsterisk)) // abc\* + { + const Zstring dirPhrase = beforeLast(phrase, sepAsterisk); + if (!dirPhrase.empty()) + directoryFilter.push_back(dirPhrase); + } + } + }; + + if (startsWith(filterFormatted, FILE_NAME_SEPARATOR)) // \abc + processTail(afterFirst(filterFormatted, FILE_NAME_SEPARATOR)); + else + { + processTail(filterFormatted); + if (startsWith(filterFormatted, asteriskSep)) // *\abc + processTail(afterFirst(filterFormatted, asteriskSep)); + } +} + + +namespace +{ +template inline +const Char* cStringFind(const Char* str, Char ch) //strchr() +{ + for (;;) + { + const Char s = *str; + if (s == ch) //ch is allowed to be 0 by contract! must return end of string in this case + return str; + + if (s == 0) + return nullptr; + ++str; + } +} + + +bool matchesMask(const Zchar* str, const Zchar* mask) +{ + for (;; ++mask, ++str) + { + Zchar m = *mask; + if (m == 0) + return *str == 0; + + switch (m) + { + case Zstr('?'): + if (*str == 0) + return false; + break; + + case Zstr('*'): + //advance mask to next non-* char + do + { + m = *++mask; + } + while (m == Zstr('*')); + + if (m == 0) //mask ends with '*': + return true; + + //*? - pattern + if (m == Zstr('?')) + { + ++mask; + while (*str++ != 0) + if (matchesMask(str, mask)) + return true; + return false; + } + + //*[letter] - pattern + ++mask; + for (;;) + { + str = cStringFind(str, m); + if (!str) + return false; + + ++str; + if (matchesMask(str, mask)) + return true; + } + + default: + if (*str != m) + return false; + } + } +} + + +//returns true if string matches at least the beginning of mask +inline +bool matchesMaskBegin(const Zchar* str, const Zchar* mask) +{ + for (;; ++mask, ++str) + { + const Zchar m = *mask; + if (m == 0) + return *str == 0; + + switch (m) + { + case Zstr('?'): + if (*str == 0) + return true; + break; + + case Zstr('*'): + return true; + + default: + if (*str != m) + return *str == 0; + } + } +} +} + + +inline +bool matchesFilter(const Zstring& name, const std::vector& filter) +{ + return std::any_of(filter.begin(), filter.end(), [&](const Zstring& mask) { return matchesMask(name.c_str(), mask.c_str()); }); +} + + +inline +bool matchesFilterBegin(const Zstring& name, const std::vector& filter) +{ + return std::any_of(filter.begin(), filter.end(), [&](const Zstring& mask) { return matchesMaskBegin(name.c_str(), mask.c_str()); }); +} + + +std::vector splitByDelimiter(const Zstring& filterString) +{ + //delimiters may be ';' or '\n' + std::vector output; + + const std::vector blocks = split(filterString, Zchar(';')); //split by less common delimiter first + std::for_each(blocks.begin(), blocks.end(), + [&](const Zstring& item) + { + const std::vector blocks2 = split(item, Zchar('\n')); + + std::for_each(blocks2.begin(), blocks2.end(), + [&](Zstring entry) + { + trim(entry); + if (!entry.empty()) + output.push_back(entry); + }); + }); + + return output; +} + +//################################################################################################# +NameFilter::NameFilter(const Zstring& includeFilter, const Zstring& excludeFilter) : + includeFilterTmp(includeFilter), //save constructor arguments for serialization + excludeFilterTmp(excludeFilter) +{ + //no need for regular expressions: In tests wxRegex was by factor of 10 slower than wxString::Matches() + + //load filter into vectors of strings + //delimiters may be ';' or '\n' + const std::vector& includeList = splitByDelimiter(includeFilter); + const std::vector& excludeList = splitByDelimiter(excludeFilter); + + //setup include/exclude filters for files and directories + std::for_each(includeList.begin(), includeList.end(), [&](const Zstring& entry) { addFilterEntry(entry, filterFileIn, filterFolderIn); }); + std::for_each(excludeList.begin(), excludeList.end(), [&](const Zstring& entry) { addFilterEntry(entry, filterFileEx, filterFolderEx); }); + + auto removeDuplicates = [](std::vector& cont) + { + std::vector output; + std::set used; + std::copy_if(cont.begin(), cont.end(), std::back_inserter(output), [&](const Zstring& item) { return used.insert(item).second; }); + output.swap(cont); + }; + + removeDuplicates(filterFileIn); + removeDuplicates(filterFolderIn); + removeDuplicates(filterFileEx); + removeDuplicates(filterFolderEx); +} + + +bool NameFilter::passFileFilter(const Zstring& relFilename) const +{ +#if defined ZEN_WIN || defined ZEN_MAC //Windows does NOT distinguish between upper/lower-case + Zstring nameFormatted = relFilename; + makeUpper(nameFormatted); +#elif defined ZEN_LINUX //Linux DOES distinguish between upper/lower-case + const Zstring& nameFormatted = relFilename; //nothing to do here +#endif + + return matchesFilter(nameFormatted, filterFileIn) && //process include filters + !matchesFilter(nameFormatted, filterFileEx); //process exclude filters +} + + +bool NameFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const +{ + assert(!subObjMightMatch || *subObjMightMatch == true); //check correct usage + +#if defined ZEN_WIN || defined ZEN_MAC //Windows does NOT distinguish between upper/lower-case + Zstring nameFormatted = relDirname; + makeUpper(nameFormatted); +#elif defined ZEN_LINUX //Linux DOES distinguish between upper/lower-case + const Zstring& nameFormatted = relDirname; //nothing to do here +#endif + + if (matchesFilter(nameFormatted, filterFolderEx)) //process exclude filters + { + if (subObjMightMatch) + *subObjMightMatch = false; //exclude subfolders/subfiles as well + return false; + } + + if (!matchesFilter(nameFormatted, filterFolderIn)) //process include filters + { + if (subObjMightMatch) + { + const Zstring& subNameBegin = nameFormatted + FILE_NAME_SEPARATOR; //const-ref optimization + + *subObjMightMatch = matchesFilterBegin(subNameBegin, filterFileIn) || //might match a file in subdirectory + matchesFilterBegin(subNameBegin, filterFolderIn); //or another subdirectory + } + return false; + } + + return true; +} + + +bool NameFilter::isNull(const Zstring& includeFilter, const Zstring& excludeFilter) +{ + Zstring include = includeFilter; + Zstring exclude = excludeFilter; + trim(include); + trim(exclude); + + return include == Zstr("*") && exclude.empty(); + //return NameFilter(includeFilter, excludeFilter).isNull(); -> very expensive for huge lists +} + +bool NameFilter::isNull() const +{ + static NameFilter output(Zstr("*"), Zstring()); + return *this == output; +} + + +bool NameFilter::cmpLessSameType(const HardFilter& other) const +{ + assert(typeid(*this) == typeid(other)); //always given in this context! + + const NameFilter& otherNameFilt = static_cast(other); + + if (filterFileIn != otherNameFilt.filterFileIn) + return filterFileIn < otherNameFilt.filterFileIn; + + if (filterFolderIn != otherNameFilt.filterFolderIn) + return filterFolderIn < otherNameFilt.filterFolderIn; + + if (filterFileEx != otherNameFilt.filterFileEx) + return filterFileEx < otherNameFilt.filterFileEx; + + if (filterFolderEx != otherNameFilt.filterFolderEx) + return filterFolderEx < otherNameFilt.filterFolderEx; + + return false; //vectors equal +} + + +//void NameFilter::save(ZstreamOut& stream) const +//{ +// writeString(stream, includeFilterTmp); +// writeString(stream, excludeFilterTmp); +//} +// +// +//HardFilter::FilterRef NameFilter::load(ZstreamIn& stream) //throw UnexpectedEndOfStreamError +//{ +// const Zstring include = readString(stream); //throw UnexpectedEndOfStreamError +// const Zstring exclude = readString(stream); // +// +// return FilterRef(new NameFilter(include, exclude)); +//} diff --git a/FreeFileSync/Source/lib/hard_filter.h b/FreeFileSync/Source/lib/hard_filter.h new file mode 100644 index 00000000..e721fe4f --- /dev/null +++ b/FreeFileSync/Source/lib/hard_filter.h @@ -0,0 +1,270 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef HARD_FILTER_H_825780275842758345 +#define HARD_FILTER_H_825780275842758345 + +#include +#include +#include +//#include + +namespace zen +{ +//------------------------------------------------------------------ +/* +Semantics of HardFilter: +1. using it creates a NEW folder hierarchy! -> must be considered by -mode! +2. it applies equally to both sides => it always matches either both sides or none! => can be used while traversing a single folder! + + class hierarchy: + + HardFilter (interface) + /|\ + _________|_____________ + | | | +NullFilter NameFilter CombinedFilter +*/ + +class HardFilter //interface for filtering +{ +public: + virtual ~HardFilter() {} + + //filtering + virtual bool passFileFilter(const Zstring& relFilename) const = 0; + virtual bool passDirFilter (const Zstring& relDirname, bool* subObjMightMatch) const = 0; + //subObjMightMatch: file/dir in subdirectories could(!) match + //note: variable is only set if passDirFilter returns false! + + virtual bool isNull() const = 0; //filter is equivalent to NullFilter, but may be technically slower + + typedef std::shared_ptr FilterRef; //always bound by design! + + //serialization + // void saveFilter(ZstreamOut& stream) const; //serialize derived object + // static FilterRef loadFilter(ZstreamIn& stream); //throw UnexpectedEndOfStreamError; CAVEAT!!! adapt this method for each new derivation!!! + +private: + friend bool operator<(const HardFilter& lhs, const HardFilter& rhs); + + // virtual void save(ZstreamOut& stream) const = 0; //serialization + virtual std::string uniqueClassIdentifier() const = 0; //get identifier, used for serialization + virtual bool cmpLessSameType(const HardFilter& other) const = 0; //typeid(*this) == typeid(other) in this context! +}; + +inline bool operator==(const HardFilter& lhs, const HardFilter& rhs) { return !(lhs < rhs) && !(rhs < lhs); } +inline bool operator!=(const HardFilter& lhs, const HardFilter& rhs) { return !(lhs == rhs); } + + +//small helper method: merge two hard filters (thereby remove Null-filters) +HardFilter::FilterRef combineFilters(const HardFilter::FilterRef& first, + const HardFilter::FilterRef& second); + + +class NullFilter : public HardFilter //no filtering at all +{ +public: + virtual bool passFileFilter(const Zstring& relFilename) const; + virtual bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const; + virtual bool isNull() const; + +private: + friend class HardFilter; + // virtual void save(ZstreamOut& stream) const {} + virtual std::string uniqueClassIdentifier() const { return "NullFilter"; } + // static FilterRef load(ZstreamIn& stream); //throw UnexpectedEndOfStreamError + virtual bool cmpLessSameType(const HardFilter& other) const; +}; + + +class NameFilter : public HardFilter //standard filter by filename +{ +public: + NameFilter(const Zstring& includeFilter, const Zstring& excludeFilter); + + virtual bool passFileFilter(const Zstring& relFilename) const; + virtual bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const; + + virtual bool isNull() const; + static bool isNull(const Zstring& includeFilter, const Zstring& excludeFilter); //*fast* check without expensively constructing NameFilter instance! + +private: + friend class HardFilter; + // virtual void save(ZstreamOut& stream) const; + virtual std::string uniqueClassIdentifier() const { return "NameFilter"; } + // static FilterRef load(ZstreamIn& stream); //throw UnexpectedEndOfStreamError + virtual bool cmpLessSameType(const HardFilter& other) const; + + std::vector filterFileIn; // + std::vector filterFolderIn; //upper case (windows) + unique items by construction + std::vector filterFileEx; // + std::vector filterFolderEx; // + + const Zstring includeFilterTmp; //save constructor arguments for serialization + const Zstring excludeFilterTmp; // +}; + + +class CombinedFilter : public HardFilter //combine two filters to match if and only if both match +{ +public: + CombinedFilter(const FilterRef& first, const FilterRef& second) : first_(first), second_(second) {} + + virtual bool passFileFilter(const Zstring& relFilename) const; + virtual bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const; + virtual bool isNull() const; + +private: + friend class HardFilter; + // virtual void save(ZstreamOut& stream) const; + virtual std::string uniqueClassIdentifier() const { return "CombinedFilter"; } + // static FilterRef load(ZstreamIn& stream); //throw UnexpectedEndOfStreamError + virtual bool cmpLessSameType(const HardFilter& other) const; + + const FilterRef first_; + const FilterRef second_; +}; + + + + + + + + + + + + + + + + + + +//---------------Inline Implementation--------------------------------------------------- +//inline +//HardFilter::FilterRef NullFilter::load(ZstreamIn& stream) +//{ +// return FilterRef(new NullFilter); +//} + + +inline +bool NullFilter::passFileFilter(const Zstring& relFilename) const +{ + return true; +} + + +inline +bool NullFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const +{ + assert(!subObjMightMatch || *subObjMightMatch == true); //check correct usage + return true; +} + + +inline +bool NullFilter::isNull() const +{ + return true; +} + + +inline +bool NullFilter::cmpLessSameType(const HardFilter& other) const +{ + assert(typeid(*this) == typeid(other)); //always given in this context! + return false; +} + + +inline +bool CombinedFilter::passFileFilter(const Zstring& relFilename) const +{ + return first_ ->passFileFilter(relFilename) && //short-circuit behavior + second_->passFileFilter(relFilename); +} + + +inline +bool CombinedFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const +{ + if (first_->passDirFilter(relDirname, subObjMightMatch)) + return second_->passDirFilter(relDirname, subObjMightMatch); + else + { + if (subObjMightMatch && *subObjMightMatch) + second_->passDirFilter(relDirname, subObjMightMatch); + return false; + } +} + + +inline +bool CombinedFilter::isNull() const +{ + return first_->isNull() && second_->isNull(); +} + + +inline +bool CombinedFilter::cmpLessSameType(const HardFilter& other) const +{ + assert(typeid(*this) == typeid(other)); //always given in this context! + + const CombinedFilter& otherCombFilt = static_cast(other); + + if (*first_ != *otherCombFilt.first_) + return *first_ < *otherCombFilt.first_; + + return *second_ < *otherCombFilt.second_; +} + + +//inline +//void CombinedFilter::save(ZstreamOut& stream) const +//{ +// first_ ->saveFilter(stream); +// second_->saveFilter(stream); +//} + + +//inline +//HardFilter::FilterRef CombinedFilter::load(ZstreamIn& stream) //throw UnexpectedEndOfStreamError +//{ +// FilterRef first = loadFilter(stream); //throw UnexpectedEndOfStreamError +// FilterRef second = loadFilter(stream); // +// +// return combineFilters(first, second); +//} + + +inline +HardFilter::FilterRef combineFilters(const HardFilter::FilterRef& first, + const HardFilter::FilterRef& second) +{ + if (first->isNull()) + { + if (second->isNull()) + return std::make_shared(); + else + return second; + } + else + { + if (second->isNull()) + return first; + else + return std::make_shared(first, second); + } +} +} + + +#endif //HARD_FILTER_H_825780275842758345 diff --git a/FreeFileSync/Source/lib/help_provider.h b/FreeFileSync/Source/lib/help_provider.h new file mode 100644 index 00000000..8ddc34c7 --- /dev/null +++ b/FreeFileSync/Source/lib/help_provider.h @@ -0,0 +1,98 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef HELPPROVIDER_H_INCLUDED +#define HELPPROVIDER_H_INCLUDED + +#ifdef ZEN_WIN +#include +#include + +#elif defined ZEN_LINUX || defined ZEN_MAC +#include +#endif + +#include "ffs_paths.h" + +namespace zen +{ +//use '/' as path separator! +void displayHelpEntry(wxWindow* parent); +void displayHelpEntry(const wxString& section, wxWindow* parent); + + + + + + + + + +//######################## implementation ######################## +namespace impl +{ +//finish wxWidgets' job +#ifdef ZEN_WIN +class FfsHelpController +{ +public: + FfsHelpController() + { + chmHlp.Initialize(utfCvrtTo(zen::getResourceDir()) + L"FreeFileSync.chm"); + } + + void openSection(const wxString& section, wxWindow* parent) + { + if (section.empty()) + chmHlp.DisplayContents(); + else + chmHlp.DisplaySection(replaceCpy(section, L'/', utfCvrtTo(FILE_NAME_SEPARATOR))); + } +private: + wxCHMHelpController chmHlp; +}; + +#elif defined ZEN_LINUX || defined ZEN_MAC +class FfsHelpController +{ +public: + void openSection(const wxString& section, wxWindow* parent) + { + wxHtmlModalHelp dlg(parent, utfCvrtTo(zen::getResourceDir()) + L"Help/FreeFileSync.hhp", section, + wxHF_DEFAULT_STYLE | wxHF_DIALOG | wxHF_MODAL | wxHF_MERGE_BOOKS); + (void)dlg; + //-> solves modal help craziness on OSX! + //-> Suse Linux: avoids program hang on exit if user closed help parent dialog before the help dialog itself was closed (why is this even possible???) + // avoids ESC key not being recognized by help dialog (but by parent dialog instead) + } +}; +#endif + + +inline +FfsHelpController& getHelpCtrl() +{ + static FfsHelpController ctrl; //external linkage, despite inline definition! + return ctrl; +} +} + + +inline +void displayHelpEntry(const wxString& section, wxWindow* parent) +{ + impl::getHelpCtrl().openSection(section, parent); +} + + +inline +void displayHelpEntry(wxWindow* parent) +{ + impl::getHelpCtrl().openSection(wxString(), parent); +} +} + +#endif //HELPPROVIDER_H_INCLUDED diff --git a/FreeFileSync/Source/lib/icon_buffer.cpp b/FreeFileSync/Source/lib/icon_buffer.cpp new file mode 100644 index 00000000..db83460d --- /dev/null +++ b/FreeFileSync/Source/lib/icon_buffer.cpp @@ -0,0 +1,758 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "icon_buffer.h" +#include +#include +#include //includes +#include + +#ifdef ZEN_WIN +#include +#include +#include +#include "../dll/Thumbnail/thumbnail.h" + +#elif defined ZEN_LINUX +#include + +#elif defined ZEN_MAC +#include "osx_file_icon.h" +#endif + +using namespace zen; + + +namespace +{ +const size_t BUFFER_SIZE_MAX = 800; //maximum number of icons to hold in buffer: must be big enough to hold visible icons + preload buffer! Consider OS limit on GDI resources (wxBitmap)!!! + +#ifndef NDEBUG +boost::thread::id mainThreadId = boost::this_thread::get_id(); +#endif + +#ifdef ZEN_WIN +const bool isXpOrLater = winXpOrLater(); //VS2010 compiled DLLs are not supported on Win 2000: Popup dialog "DecodePointer not found" + +#define DEF_DLL_FUN(name) const auto name = isXpOrLater ? DllFun(thumb::getDllName(), thumb::funName_##name) : DllFun(); +DEF_DLL_FUN(getIconByIndex); // +DEF_DLL_FUN(getThumbnail); //let's spare the boost::call_once hustle and allocate statically +DEF_DLL_FUN(releaseImageData); // +#endif + +class IconHolder //handle HICON/GdkPixbuf ownership supporting thread-safe usage (in contrast to wxIcon/wxBitmap) +{ +public: +#ifdef ZEN_WIN + typedef const thumb::ImageData* HandleType; +#elif defined ZEN_LINUX + typedef GdkPixbuf* HandleType; +#elif defined ZEN_MAC + typedef osx::ImageData* HandleType; +#endif + + explicit IconHolder(HandleType handle = nullptr) : handle_(handle) {} //take ownership! + + IconHolder(IconHolder&& other) : handle_(other.release()) {} + + IconHolder& operator=(IconHolder other) //unifying assignment + { + other.swap(*this); + return *this; + } + + ~IconHolder() + { + if (handle_ != nullptr) +#ifdef ZEN_WIN + releaseImageData(handle_); //should be checked already before creating IconHolder! +#elif defined ZEN_LINUX + ::g_object_unref(handle_); //superseedes "::gdk_pixbuf_unref"! +#elif defined ZEN_MAC + delete handle_; +#endif + } + + HandleType release() + { + ZEN_ON_SCOPE_EXIT(handle_ = nullptr); + return handle_; + } + + void swap(IconHolder& other) { std::swap(handle_, other.handle_); } //throw() + + //destroys raw icon! Call from GUI thread only! + wxBitmap extractWxBitmap() + { + ZEN_ON_SCOPE_EXIT(assert(!*this)); + assert(boost::this_thread::get_id() == mainThreadId ); + + if (!handle_) + return wxNullBitmap; + +#ifdef ZEN_WIN + ZEN_ON_SCOPE_EXIT(IconHolder().swap(*this)); //destroy after extraction + + //let wxImage reference data without taking ownership: + wxImage fileIcon(handle_->width, handle_->height, handle_->rgb, true); + fileIcon.SetAlpha(handle_->alpha, true); + return wxBitmap(fileIcon); + +#elif defined ZEN_LINUX + return wxBitmap(release()); //ownership passed! + +#elif defined ZEN_MAC + ZEN_ON_SCOPE_EXIT(IconHolder().swap(*this)); //destroy after extraction + + //let wxImage reference data without taking ownership: + assert(!handle_->rgb.empty() && !handle_->alpha.empty()); + if (!handle_->rgb.empty()) + { + wxImage fileIcon(handle_->width, handle_->height, &handle_->rgb[0], true); + if (!handle_->alpha.empty()) + fileIcon.SetAlpha(&handle_->alpha[0], true); + return wxBitmap(fileIcon); + } + return wxBitmap(); +#endif + } + +private: + HandleType handle_; + + IconHolder(const IconHolder& other); //move semantics! + struct ConversionToBool { int dummy; }; +public: + //use member pointer as implicit conversion to bool (C++ Templates - Vandevoorde/Josuttis; chapter 20) + operator int ConversionToBool::* () const { return handle_ != nullptr ? &ConversionToBool::dummy : nullptr; } +}; + + +#ifdef ZEN_WIN +Zstring getFileExtension(const Zstring& filename) +{ + const Zstring shortName = afterLast(filename, Zchar('\\')); //warning: using windows file name separator! + + return contains(shortName, Zchar('.')) ? + afterLast(filename, Zchar('.')) : + Zstring(); +} + + +std::set priceyExtensions; //thread-safe! +boost::once_flag initExtensionsOnce = BOOST_ONCE_INIT; // + +//test for extension for non-thumbnail icons that physically have to be retrieved from disc +bool isCheapExtension(const Zstring& extension) +{ + boost::call_once(initExtensionsOnce, []() + { + priceyExtensions.insert(L"exe"); + priceyExtensions.insert(L"ico"); + priceyExtensions.insert(L"ani"); + priceyExtensions.insert(L"cur"); + priceyExtensions.insert(L"msc"); + priceyExtensions.insert(L"scr"); + + priceyExtensions.insert(L"lnk"); // + priceyExtensions.insert(L"url"); //make sure shortcuts are pricey to get them to be detected by SHGetFileInfo + priceyExtensions.insert(L"pif"); // + priceyExtensions.insert(L"website"); // + + }); + return priceyExtensions.find(extension) == priceyExtensions.end(); +} + +const bool wereVistaOrLater = vistaOrLater(); //thread-safety: init at startup + + +thumb::IconSizeType getThumbSizeType(IconBuffer::IconSize sz) +{ + using namespace thumb; + switch (sz) + { + case IconBuffer::SIZE_SMALL: + return ICON_SIZE_16; + case IconBuffer::SIZE_MEDIUM: + if (!wereVistaOrLater) return ICON_SIZE_32; //48x48 doesn't look sharp on XP + return ICON_SIZE_48; + case IconBuffer::SIZE_LARGE: + return ICON_SIZE_128; + } + return ICON_SIZE_16; +} + + +IconHolder getIconByAttribute(LPCWSTR pszPath, DWORD dwFileAttributes, IconBuffer::IconSize sz) +{ + //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! + SHFILEINFO fileInfo = {}; //initialize hIcon + DWORD_PTR imgList = ::SHGetFileInfo(pszPath, //Windows 7 doesn't like this parameter to be an empty string + dwFileAttributes, + &fileInfo, + sizeof(fileInfo), + SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES); + if (!imgList) //no need to IUnknown::Release() imgList! + return IconHolder(); + + if (getIconByIndex && releaseImageData) + return IconHolder(getIconByIndex(fileInfo.iIcon, getThumbSizeType(sz))); + + return IconHolder(); +} + + +IconHolder getAssociatedIconByExt(const Zstring& extension, IconBuffer::IconSize sz) +{ + //no read-access to disk! determine icon by extension + return getIconByAttribute((L"dummy." + extension).c_str(), FILE_ATTRIBUTE_NORMAL, sz); +} + +#elif defined ZEN_LINUX +IconHolder iconHolderFromGicon(GIcon* gicon, IconBuffer::IconSize sz) +{ + if (gicon) + if (GtkIconTheme* defaultTheme = ::gtk_icon_theme_get_default()) //not owned! + if (GtkIconInfo* iconInfo = ::gtk_icon_theme_lookup_by_gicon(defaultTheme, gicon, IconBuffer::getSize(sz), GTK_ICON_LOOKUP_USE_BUILTIN)) //this may fail if icon is not installed on system + { + ZEN_ON_SCOPE_EXIT(::gtk_icon_info_free(iconInfo);) + if (GdkPixbuf* pixBuf = ::gtk_icon_info_load_icon(iconInfo, nullptr)) + return IconHolder(pixBuf); //pass ownership + } + return IconHolder(); +} +#endif +} + +//################################################################################################################################################ + +IconHolder getThumbnailIcon(const Zstring& filename, int requestedSize) //return 0 on failure +{ +#ifdef ZEN_WIN + if (getThumbnail && releaseImageData) + return IconHolder(getThumbnail(filename.c_str(), requestedSize)); + +#elif defined ZEN_LINUX + gint width = 0; + gint height = 0; + if (GdkPixbufFormat* fmt = ::gdk_pixbuf_get_file_info(filename.c_str(), &width, &height)) + { + (void)fmt; + if (width > 0 && height > 0 && requestedSize > 0) + { + int trgWidth = width; + int trgHeight = height; + + const int maxExtent = std::max(width, height); //don't stretch small images, but shrink large ones instead! + if (requestedSize < maxExtent) + { + trgWidth = width * requestedSize / maxExtent; + trgHeight = height * requestedSize / maxExtent; + } + if (GdkPixbuf* pixBuf = ::gdk_pixbuf_new_from_file_at_size(filename.c_str(), trgWidth, trgHeight, nullptr)) + return IconHolder(pixBuf); //pass ownership + } + } + +#elif defined ZEN_MAC + try + { + return IconHolder(new osx::ImageData(osx::getThumbnail(filename.c_str(), requestedSize))); //throw SysError + } + catch (zen::SysError&) {} +#endif + return IconHolder(); +} + + +IconHolder getGenericFileIcon(IconBuffer::IconSize sz) +{ + //we're called by getAssociatedIcon()! -> avoid endless recursion! +#ifdef ZEN_WIN + return getIconByAttribute(L"dummy", FILE_ATTRIBUTE_NORMAL, sz); + +#elif defined ZEN_LINUX + const char* mimeFileIcons[] = + { + "application-x-zerosize", //Kubuntu: /usr/share/icons/oxygen/48x48/mimetypes + "text-x-generic", //http://live.gnome.org/GnomeArt/Tutorials/IconThemes + "empty", //Ubuntu: /usr/share/icons/Humanity/mimes/48 + GTK_STOCK_FILE, //"gtk-file", + "gnome-fs-regular", // + }; + + if (GtkIconTheme* defaultTheme = gtk_icon_theme_get_default()) //not owned! + for (auto it = std::begin(mimeFileIcons); it != std::end(mimeFileIcons); ++it) + if (GdkPixbuf* pixBuf = gtk_icon_theme_load_icon(defaultTheme, *it, IconBuffer::getSize(sz), GTK_ICON_LOOKUP_USE_BUILTIN, nullptr)) + return IconHolder(pixBuf); //pass ownership + return IconHolder(); + +#elif defined ZEN_MAC + try + { + return IconHolder(new osx::ImageData(osx::getDefaultFileIcon(IconBuffer::getSize(sz)))); //throw SysError + } + catch (zen::SysError&) {} + return IconHolder(); +#endif +} + + +IconHolder getGenericDirectoryIcon(IconBuffer::IconSize sz) +{ +#ifdef ZEN_WIN + return getIconByAttribute(L"dummy", //Windows 7 doesn't like this parameter to be an empty string! + FILE_ATTRIBUTE_DIRECTORY, sz); +#elif defined ZEN_LINUX + if (GIcon* dirIcon = ::g_content_type_get_icon("inode/directory")) //should contain fallback to GTK_STOCK_DIRECTORY ("gtk-directory") + return iconHolderFromGicon(dirIcon, sz); + return IconHolder(); + +#elif defined ZEN_MAC + try + { + return IconHolder(new osx::ImageData(osx::getDefaultFolderIcon(IconBuffer::getSize(sz)))); //throw SysError + } + catch (zen::SysError&) { return IconHolder(); } +#endif +} + + +IconHolder getAssociatedIcon(const Zstring& filename, IconBuffer::IconSize sz) +{ + //1. try to load thumbnails + switch (sz) + { + case IconBuffer::SIZE_SMALL: + break; + case IconBuffer::SIZE_MEDIUM: + case IconBuffer::SIZE_LARGE: + if (IconHolder ico = getThumbnailIcon(filename, IconBuffer::getSize(sz))) + return ico; + //else: fallback to non-thumbnail icon + break; + } + + warn_static("problem: fr folder links ist getThumbnail erfolgreich => SFGAO_LINK nicht gecheckt!") + + //2. retrieve file icons +#ifdef ZEN_WIN + //perf: optimize fallback case for SIZE_MEDIUM and SIZE_LARGE: + const Zstring& extension = getFileExtension(filename); + if (isCheapExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension + return getAssociatedIconByExt(extension, sz); + //result will not be buffered under extension name, but full filename; this is okay, since we're in SIZE_MEDIUM or SIZE_LARGE context, + //which means the access to get thumbnail failed: thumbnail failure is not dependent from extension in general! + + SHFILEINFO fileInfo = {}; + if (DWORD_PTR imgList = ::SHGetFileInfo(filename.c_str(), //_In_ LPCTSTR pszPath, -> note: ::SHGetFileInfo() can't handle \\?\-prefix! + 0, //DWORD dwFileAttributes, + &fileInfo, //_Inout_ SHFILEINFO *psfi, + sizeof(fileInfo), //UINT cbFileInfo, + SHGFI_SYSICONINDEX /*| SHGFI_ATTRIBUTES*/)) //UINT uFlags + { + (void)imgList; + //imgList->Release(); //empiric study: crash on XP if we release this! Seems we do not own it... -> also no GDI leak on Win7 -> okay + //another comment on http://msdn.microsoft.com/en-us/library/bb762179(v=VS.85).aspx describes exact same behavior on Win7/XP + + //Quote: "The IImageList pointer type, such as that returned in the ppv parameter, can be cast as an HIMAGELIST as needed; + // for example, for use in a list view. Conversely, an HIMAGELIST can be cast as a pointer to an IImageList." + //http://msdn.microsoft.com/en-us/library/windows/desktop/bb762185(v=vs.85).aspx + +#ifdef __MINGW32__ //Shobjidl.h +#define SFGAO_LINK 0x00010000L // Shortcut (link) or symlinks +#endif + + warn_static("support SFGAO_GHOSTED or hidden?") + //requires SHGFI_ATTRIBUTES + //const bool isLink = (fileInfo.dwAttributes & SFGAO_LINK) != 0; + + if (getIconByIndex && releaseImageData) + if (const thumb::ImageData* imgData = getIconByIndex(fileInfo.iIcon, getThumbSizeType(sz))) + return IconHolder(imgData); + } + +#elif defined ZEN_LINUX + GFile* file = ::g_file_new_for_path(filename.c_str()); //documented to "never fail" + ZEN_ON_SCOPE_EXIT(::g_object_unref(file);) + + if (GFileInfo* fileInfo = ::g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_ICON, G_FILE_QUERY_INFO_NONE, nullptr, nullptr)) + { + ZEN_ON_SCOPE_EXIT(::g_object_unref(fileInfo);) + if (GIcon* gicon = ::g_file_info_get_icon(fileInfo)) //not owned! + return iconHolderFromGicon(gicon, sz); + } + //need fallback: icon lookup may fail because some icons are currently not present on system + +#elif defined ZEN_MAC + try + { + return IconHolder(new osx::ImageData(osx::getFileIcon(filename.c_str(), IconBuffer::getSize(sz)))); //throw SysError + } + catch (zen::SysError&) { assert(false); } +#endif + return ::getGenericFileIcon(sz); //make sure this does not internally call getAssociatedIcon("someDefaultFile.txt")!!! => endless recursion! +} + +//################################################################################################################################################ + +//---------------------- Shared Data ------------------------- +class WorkLoad +{ +public: + Zstring extractNextFile() //context of worker thread, blocking + { + assert(boost::this_thread::get_id() != mainThreadId ); + boost::unique_lock dummy(lockFiles); + + while (filesToLoad.empty()) + conditionNewFiles.timed_wait(dummy, boost::posix_time::milliseconds(100)); //interruption point! + + Zstring fileName = filesToLoad.back(); + filesToLoad.pop_back(); + return fileName; + } + + void setWorkload(const std::vector& newLoad) //context of main thread + { + assert(boost::this_thread::get_id() == mainThreadId ); + { + boost::lock_guard dummy(lockFiles); + filesToLoad = newLoad; + } + conditionNewFiles.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796 + //condition handling, see: http://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref + } + + void addToWorkload(const Zstring& newEntry) //context of main thread + { + assert(boost::this_thread::get_id() == mainThreadId ); + { + boost::lock_guard dummy(lockFiles); + filesToLoad.push_back(newEntry); //set as next item to retrieve + } + conditionNewFiles.notify_all(); + } + +private: + std::vector filesToLoad; //processes last elements of vector first! + boost::mutex lockFiles; + boost::condition_variable conditionNewFiles; //signal event: data for processing available +}; + + +class Buffer +{ +public: + Buffer() : firstInsertPos(iconList.end()), lastInsertPos(iconList.end()) {} + + //called by main and worker thread: + bool hasIcon(const Zstring& fileName) const + { + boost::lock_guard dummy(lockIconList); + return iconList.find(fileName) != iconList.end(); + } + + //must be called by main thread only! => wxBitmap is NOT thread-safe like an int (non-atomic ref-count!!!) + Opt retrieve(const Zstring& fileName) + { + assert(boost::this_thread::get_id() == mainThreadId); + boost::lock_guard dummy(lockIconList); + + auto it = iconList.find(fileName); + if (it == iconList.end()) + return NoValue(); + + markAsHot(it); + + IconData& idata = refData(it); + if (idata.iconRaw) //if not yet converted... + { + idata.iconFmt = make_unique(idata.iconRaw.extractWxBitmap()); //convert in main thread! + assert(!idata.iconRaw); + } + return idata.iconFmt ? *idata.iconFmt : wxNullBitmap; //idata.iconRaw may be inserted as empty from worker thread! + } + + //called by main and worker thread: + void insert(const Zstring& entryName, IconHolder&& icon) + { + boost::lock_guard dummy(lockIconList); + + //thread safety: moving IconHolder is free from side effects, but ~wxBitmap() is NOT! => do NOT delete items from iconList here! + auto rc = iconList.insert(std::make_pair(entryName, makeValueObject())); + assert(rc.second); + if (rc.second) //if insertion took place + { + refData(rc.first).iconRaw = std::move(icon); + priorityListPushBack(rc.first); + } + } + + //must be called by main thread only! => ~wxBitmap() is NOT thread-safe! + //call at an appropriate time, e.g. after Workload::setWorkload() + void limitSize() + { + assert(boost::this_thread::get_id() == mainThreadId); + boost::lock_guard dummy(lockIconList); + + while (iconList.size() > BUFFER_SIZE_MAX) + { + auto itDelPos = firstInsertPos; + priorityListPopFront(); + iconList.erase(itDelPos); //remove oldest element + } + } + +private: + struct IconData; + +#if defined ZEN_WIN || defined ZEN_LINUX + typedef std::map FileIconMap; + IconData& refData(FileIconMap::iterator it) { return it->second; } + static IconData makeValueObject() { return IconData(); } +#elif defined ZEN_MAC //workaround libc++ limitation for incomplete types: http://llvm.org/bugs/show_bug.cgi?id=17701 + typedef std::map, LessFilename> FileIconMap; + static IconData& refData(FileIconMap::iterator it) { return *(it->second); } + static std::unique_ptr makeValueObject() { return make_unique(); } +#endif + + //call while holding lock: + void priorityListPopFront() + { + assert(firstInsertPos!= iconList.end()); + firstInsertPos = refData(firstInsertPos).next_; + + if (firstInsertPos != iconList.end()) + refData(firstInsertPos).prev_ = iconList.end(); + else //BUFFER_SIZE_MAX > 0, but still for completeness: + lastInsertPos = iconList.end(); + } + + //call while holding lock: + void priorityListPushBack(FileIconMap::iterator it) + { + if (lastInsertPos == iconList.end()) + { + assert(firstInsertPos == iconList.end()); + firstInsertPos = lastInsertPos = it; + refData(it).prev_ = refData(it).next_ = iconList.end(); + } + else + { + refData(it).next_ = iconList.end(); + refData(it).prev_ = lastInsertPos; + refData(lastInsertPos).next_ = it; + lastInsertPos = it; + } + } + + //call while holding lock: + void markAsHot(FileIconMap::iterator it) //mark existing buffer entry as if newly inserted + { + assert(it != iconList.end()); + if (refData(it).next_ != iconList.end()) + { + if (refData(it).prev_ != iconList.end()) + { + refData(refData(it).prev_).next_ = refData(it).next_; //remove somewhere from the middle + refData(refData(it).next_).prev_ = refData(it).prev_; // + } + else + { + assert(it == firstInsertPos); + priorityListPopFront(); + } + priorityListPushBack(it); + } + else + { + if (refData(it).prev_ != iconList.end()) + assert(it == lastInsertPos); //nothing to do + else + assert(iconList.size() == 1 && it == firstInsertPos && it == lastInsertPos); //nothing to do + } + } + + struct IconData + { + IconData() {} + IconData(IconData&& tmp) : iconRaw(std::move(tmp.iconRaw)), iconFmt(std::move(tmp.iconFmt)), prev_(tmp.prev_), next_(tmp.next_) {} + + IconHolder iconRaw; //native icon representation: may be used by any thread + + std::unique_ptr iconFmt; //use ONLY from main thread! + //wxBitmap is NOT thread-safe: non-atomic ref-count just to begin with... + //- prohibit implicit calls to wxBitmap(const wxBitmap&) + //- prohibit calls to ~wxBitmap() and transitively ~IconData() + //- prohibit even wxBitmap() default constructor - better be safe than sorry! + + FileIconMap::iterator prev_; //store list sorted by time of insertion into buffer + FileIconMap::iterator next_; // + }; + + mutable boost::mutex lockIconList; + FileIconMap iconList; //shared resource; Zstring is thread-safe like an int + FileIconMap::iterator firstInsertPos; + FileIconMap::iterator lastInsertPos; +}; + +//################################################################################################################################################ + +class WorkerThread //lifetime is part of icon buffer +{ +public: + WorkerThread(const std::shared_ptr& workload, + const std::shared_ptr& buffer, + IconBuffer::IconSize st) : + workload_(workload), + buffer_(buffer), + iconSizeType(st) {} + + void operator()(); //thread entry + +private: + std::shared_ptr workload_; //main/worker thread may access different shared_ptr instances safely (even though they have the same target!) + std::shared_ptr buffer_; //http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/shared_ptr.htm?sess=8153b05b34d890e02d48730db1ff7ddc#ThreadSafety + const IconBuffer::IconSize iconSizeType; +}; + + +void WorkerThread::operator()() //thread entry +{ + //failure to initialize COM for each thread is a source of hard to reproduce bugs: https://sourceforge.net/tracker/?func=detail&aid=3160472&group_id=234430&atid=1093080 +#ifdef ZEN_WIN + //Prerequisites, see thumbnail.h + + //1. Initialize COM + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + ZEN_ON_SCOPE_EXIT(::CoUninitialize()); + + //2. Initialize system image list + typedef BOOL (WINAPI* FileIconInitFun)(BOOL fRestoreCache); + const SysDllFun fileIconInit(L"Shell32.dll", reinterpret_cast(660)); //MS requires and documents this magic number + assert(fileIconInit); + if (fileIconInit) + fileIconInit(false); //TRUE to restore the system image cache from disk; FALSE otherwise. +#endif + + while (true) + { + boost::this_thread::interruption_point(); + + const Zstring fileName = workload_->extractNextFile(); //start work: blocks until next icon to load is retrieved + + if (!buffer_->hasIcon(fileName)) //perf: workload may contain duplicate entries? + buffer_->insert(fileName, getAssociatedIcon(fileName, iconSizeType)); + } +} + +//######################### redirect to impl ##################################################### + +struct IconBuffer::Pimpl +{ + Pimpl() : + workload(std::make_shared()), + buffer (std::make_shared()) {} + + std::shared_ptr workload; + std::shared_ptr buffer; + + boost::thread worker; +}; + + +IconBuffer::IconBuffer(IconSize sz) : pimpl(make_unique()), + iconSizeType(sz), + genDirIcon (::getGenericDirectoryIcon(sz).extractWxBitmap()), + genFileIcon(::getGenericFileIcon (sz).extractWxBitmap()) +{ + pimpl->worker = boost::thread(WorkerThread(pimpl->workload, pimpl->buffer, sz)); +} + + +IconBuffer::~IconBuffer() +{ + setWorkload(std::vector()); //make sure interruption point is always reached! + pimpl->worker.interrupt(); + pimpl->worker.join(); //we assume precondition "worker.joinable()"!!! +} + + +int IconBuffer::getSize(IconSize icoSize) +{ + switch (icoSize) + { + case IconBuffer::SIZE_SMALL: +#if defined ZEN_WIN || defined ZEN_MAC + return 16; +#elif defined ZEN_LINUX + return 24; +#endif + case IconBuffer::SIZE_MEDIUM: +#ifdef ZEN_WIN + if (!wereVistaOrLater) return 32; //48x48 doesn't look sharp on XP +#endif + return 48; + + case IconBuffer::SIZE_LARGE: + return 128; + } + assert(false); + return 0; +} + + +bool IconBuffer::readyForRetrieval(const Zstring& filename) +{ +#ifdef ZEN_WIN + if (iconSizeType == IconBuffer::SIZE_SMALL) + if (isCheapExtension(getFileExtension(filename))) + return true; +#endif + return pimpl->buffer->hasIcon(filename); +} + + +Opt IconBuffer::retrieveFileIcon(const Zstring& filename) +{ +#ifdef ZEN_WIN + //perf: let's read icons which don't need file access right away! No async delay justified! + if (iconSizeType == IconBuffer::SIZE_SMALL) //non-thumbnail view, we need file type icons only! + { + const Zstring& extension = getFileExtension(filename); + if (isCheapExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension + { + if (Opt ico = pimpl->buffer->retrieve(extension)) + return ico; + + //make sure icon is in buffer, even if icon needs not be retrieved! + pimpl->buffer->insert(extension, getAssociatedIconByExt(extension, iconSizeType)); + + Opt ico = pimpl->buffer->retrieve(extension); + assert(ico); + return ico; + } + } +#endif + + if (Opt ico = pimpl->buffer->retrieve(filename)) + return ico; + + //since this icon seems important right now, we don't want to wait until next setWorkload() to start retrieving + pimpl->workload->addToWorkload(filename); + pimpl->buffer->limitSize(); + return NoValue(); +} + + +void IconBuffer::setWorkload(const std::vector& load) +{ + assert(load.size() < BUFFER_SIZE_MAX / 2); + + pimpl->workload->setWorkload(load); //since buffer can only increase due to new workload, + pimpl->buffer->limitSize(); //this is the place to impose the limit from main thread! +} diff --git a/FreeFileSync/Source/lib/icon_buffer.h b/FreeFileSync/Source/lib/icon_buffer.h new file mode 100644 index 00000000..e1a475cb --- /dev/null +++ b/FreeFileSync/Source/lib/icon_buffer.h @@ -0,0 +1,52 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef ICONBUFFER_H_INCLUDED +#define ICONBUFFER_H_INCLUDED + +#include +#include +#include +#include +#include + +namespace zen +{ +class IconBuffer +{ +public: + enum IconSize + { + SIZE_SMALL, + SIZE_MEDIUM, + SIZE_LARGE + }; + + IconBuffer(IconSize sz); + ~IconBuffer(); + + static int getSize(IconSize icoSizeType); //expected and *maximum* icon size in pixel + int getSize() const { return getSize(iconSizeType); } // + + const wxBitmap& genericFileIcon() { return genFileIcon; } + const wxBitmap& genericDirIcon () { return genDirIcon; } + + bool readyForRetrieval(const Zstring& filename); + Opt retrieveFileIcon(const Zstring& filename); //... and mark as hot + + void setWorkload(const std::vector& load); //(re-)set new workload of icons to be retrieved; + +private: + struct Pimpl; + std::unique_ptr pimpl; + + const IconSize iconSizeType; + const wxBitmap genDirIcon; + const wxBitmap genFileIcon; +}; +} + +#endif // ICONBUFFER_H_INCLUDED diff --git a/FreeFileSync/Source/lib/localization.cpp b/FreeFileSync/Source/lib/localization.cpp new file mode 100644 index 00000000..3536ba70 --- /dev/null +++ b/FreeFileSync/Source/lib/localization.cpp @@ -0,0 +1,485 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "localization.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "parse_plural.h" +#include "parse_lng.h" +#include "ffs_paths.h" + +#ifdef ZEN_LINUX +#include //wcscasecmp + +#elif defined ZEN_MAC +#include +#endif + +using namespace zen; + + +namespace +{ +class FFSTranslation : public TranslationHandler +{ +public: + FFSTranslation(const Zstring& filename, wxLanguage languageId); //throw lngfile::ParsingError, parse_plural::ParsingError + + wxLanguage langId() const { return langId_; } + + virtual std::wstring translate(const std::wstring& text) override + { + //look for translation in buffer table + auto it = transMapping.find(text); + if (it != transMapping.end() && !it->second.empty()) + return it->second; + return text; //fallback + } + + virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, std::int64_t n) override + { + auto it = transMappingPl.find(std::make_pair(singular, plural)); + if (it != transMappingPl.end()) + { + const size_t formNo = pluralParser->getForm(n); + if (formNo < it->second.size()) + return replaceCpy(it->second[formNo], L"%x", toGuiString(n)); + } + return replaceCpy(std::abs(n) == 1 ? singular : plural, L"%x", toGuiString(n)); //fallback + } + +private: + typedef hash_map Translation; //hash_map is 15% faster than std::map on GCC + typedef std::map, std::vector> TranslationPlural; + + Translation transMapping; //map original text |-> translation + TranslationPlural transMappingPl; + std::unique_ptr pluralParser; //bound! + wxLanguage langId_; +}; + + +FFSTranslation::FFSTranslation(const Zstring& filename, wxLanguage languageId) : langId_(languageId) //throw lngfile::ParsingError, parse_plural::ParsingError +{ + std::string inputStream; + try + { + inputStream = loadBinStream(filename); //throw FileError + } + catch (const FileError& e) + { + throw lngfile::ParsingError(e.toString(), 0, 0); //passing FileError is too high a level for Parsing error, OTOH user is unlikely to see this since file I/O issues are sorted out by ExistingTranslations()! + } + + lngfile::TransHeader header; + lngfile::TranslationMap transInput; + lngfile::TranslationPluralMap transPluralInput; + lngfile::parseLng(inputStream, header, transInput, transPluralInput); //throw ParsingError + + for (const auto& item : transInput) + { + const std::wstring original = utfCvrtTo(item.first); + const std::wstring translation = utfCvrtTo(item.second); + transMapping.insert(std::make_pair(original, translation)); + } + + for (const auto& item : transPluralInput) + { + const std::wstring engSingular = utfCvrtTo(item.first.first); + const std::wstring engPlural = utfCvrtTo(item.first.second); + + std::vector plFormsWide; + for (const std::string& pf : item.second) + plFormsWide.push_back(utfCvrtTo(pf)); + + transMappingPl.insert(std::make_pair(std::make_pair(engSingular, engPlural), plFormsWide)); + } + + pluralParser = make_unique(header.pluralDefinition); //throw parse_plural::ParsingError +} + + +class FindLngfiles : public zen::TraverseCallback +{ +public: + FindLngfiles(std::vector& lngFiles) : lngFiles_(lngFiles) {} + + virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) + { + if (endsWith(fullName, Zstr(".lng"))) + lngFiles_.push_back(fullName); + } + + virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) { return LINK_SKIP; } + virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& fullName) { return nullptr; } + virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) { assert(false); return ON_ERROR_IGNORE; } //errors are not really critical in this context + virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) { assert(false); return ON_ERROR_IGNORE; } // + +private: + std::vector& lngFiles_; +}; + + +struct LessTranslation +{ + bool operator()(const ExistingTranslations::Entry& lhs, const ExistingTranslations::Entry& rhs) const + { + //use a more "natural" sort: ignore case and diacritics +#ifdef ZEN_WIN + const int rv = ::CompareString(LOCALE_USER_DEFAULT, //__in LCID Locale, + NORM_IGNORECASE, //__in DWORD dwCmpFlags, + lhs.languageName.c_str(), //__in LPCTSTR lpString1, + static_cast(lhs.languageName.size()), //__in int cchCount1, + rhs.languageName.c_str(), //__in LPCTSTR lpString2, + static_cast(rhs.languageName.size())); //__in int cchCount2 + if (rv == 0) + throw std::runtime_error("Error comparing strings."); + else + return rv == CSTR_LESS_THAN; //convert to C-style string compare result + +#elif defined ZEN_LINUX + return ::wcscasecmp(lhs.languageName.c_str(), rhs.languageName.c_str()) < 0; //ignores case; locale-dependent! + //return lhs.languageName.CmpNoCase(rhs.languageName) < 0; + +#elif defined ZEN_MAC + auto allocCFStringRef = [](const std::wstring& str) -> CFStringRef //output not owned! + { + return ::CFStringCreateWithCString(nullptr, //CFAllocatorRef alloc, + utfCvrtTo(str).c_str(), //const char *cStr, + kCFStringEncodingUTF8); //CFStringEncoding encoding + }; + + CFStringRef langL = allocCFStringRef(lhs.languageName); + ZEN_ON_SCOPE_EXIT(::CFRelease(langL)); + + CFStringRef langR = allocCFStringRef(rhs.languageName); + ZEN_ON_SCOPE_EXIT(::CFRelease(langR)); + + return::CFStringCompare(langL, langR, kCFCompareLocalized | kCFCompareCaseInsensitive) == kCFCompareLessThan; //no-fail +#endif + } +}; +} + + +ExistingTranslations::ExistingTranslations() +{ + { + //default entry: + ExistingTranslations::Entry newEntry; + newEntry.languageID = wxLANGUAGE_ENGLISH_US; + newEntry.languageName = L"English (US)"; + newEntry.languageFile = L""; + newEntry.translatorName = L"Zenju"; + newEntry.languageFlag = L"flag_usa.png"; + locMapping.push_back(newEntry); + } + + //search language files available + std::vector lngFiles; + FindLngfiles traverseCallback(lngFiles); + + traverseFolder(zen::getResourceDir() + Zstr("Languages"), //throw(); + traverseCallback); + + for (const Zstring& filename : lngFiles) + { + try + { + const std::string stream = loadBinStream(filename); //throw FileError + + lngfile::TransHeader lngHeader; + lngfile::parseHeader(stream, lngHeader); //throw ParsingError + + assert(!lngHeader.languageName .empty()); + assert(!lngHeader.translatorName.empty()); + assert(!lngHeader.localeName .empty()); + assert(!lngHeader.flagFile .empty()); + /* + There is some buggy behavior in wxWidgets which maps "zh_TW" to simplified chinese. + Fortunately locales can be also entered as description. I changed to "Chinese (Traditional)" which works fine. + */ + if (const wxLanguageInfo* locInfo = wxLocale::FindLanguageInfo(utfCvrtTo(lngHeader.localeName))) + { + ExistingTranslations::Entry newEntry; + newEntry.languageID = locInfo->Language; + newEntry.languageName = utfCvrtTo(lngHeader.languageName); + newEntry.languageFile = utfCvrtTo(filename); + newEntry.translatorName = utfCvrtTo(lngHeader.translatorName); + newEntry.languageFlag = utfCvrtTo(lngHeader.flagFile); + locMapping.push_back(newEntry); + } + else assert(false); + } + catch (FileError&) { assert(false); } + catch (lngfile::ParsingError&) { assert(false); } //better not show an error message here; scenario: batch jobs + } + + std::sort(locMapping.begin(), locMapping.end(), LessTranslation()); +} + + +const std::vector& ExistingTranslations::get() +{ + static ExistingTranslations instance; + return instance.locMapping; +} + + +namespace +{ +wxLanguage mapLanguageDialect(wxLanguage language) +{ + switch (static_cast(language)) //avoid enumeration value wxLANGUAGE_*' not handled in switch [-Wswitch-enum] + { + //variants of wxLANGUAGE_ARABIC + case wxLANGUAGE_ARABIC_ALGERIA: + case wxLANGUAGE_ARABIC_BAHRAIN: + case wxLANGUAGE_ARABIC_EGYPT: + case wxLANGUAGE_ARABIC_IRAQ: + case wxLANGUAGE_ARABIC_JORDAN: + case wxLANGUAGE_ARABIC_KUWAIT: + case wxLANGUAGE_ARABIC_LEBANON: + case wxLANGUAGE_ARABIC_LIBYA: + case wxLANGUAGE_ARABIC_MOROCCO: + case wxLANGUAGE_ARABIC_OMAN: + case wxLANGUAGE_ARABIC_QATAR: + case wxLANGUAGE_ARABIC_SAUDI_ARABIA: + case wxLANGUAGE_ARABIC_SUDAN: + case wxLANGUAGE_ARABIC_SYRIA: + case wxLANGUAGE_ARABIC_TUNISIA: + case wxLANGUAGE_ARABIC_UAE: + case wxLANGUAGE_ARABIC_YEMEN: + return wxLANGUAGE_ARABIC; + + //variants of wxLANGUAGE_CHINESE_SIMPLIFIED + case wxLANGUAGE_CHINESE: + case wxLANGUAGE_CHINESE_SINGAPORE: + return wxLANGUAGE_CHINESE_SIMPLIFIED; + + //variants of wxLANGUAGE_CHINESE_TRADITIONAL + case wxLANGUAGE_CHINESE_TAIWAN: + case wxLANGUAGE_CHINESE_HONGKONG: + case wxLANGUAGE_CHINESE_MACAU: + return wxLANGUAGE_CHINESE_TRADITIONAL; + + //variants of wxLANGUAGE_DUTCH + case wxLANGUAGE_DUTCH_BELGIAN: + return wxLANGUAGE_DUTCH; + + //variants of wxLANGUAGE_ENGLISH_UK + case wxLANGUAGE_ENGLISH_AUSTRALIA: + case wxLANGUAGE_ENGLISH_NEW_ZEALAND: + case wxLANGUAGE_ENGLISH_TRINIDAD: + case wxLANGUAGE_ENGLISH_CARIBBEAN: + case wxLANGUAGE_ENGLISH_JAMAICA: + case wxLANGUAGE_ENGLISH_BELIZE: + case wxLANGUAGE_ENGLISH_EIRE: + case wxLANGUAGE_ENGLISH_SOUTH_AFRICA: + case wxLANGUAGE_ENGLISH_ZIMBABWE: + case wxLANGUAGE_ENGLISH_BOTSWANA: + case wxLANGUAGE_ENGLISH_DENMARK: + return wxLANGUAGE_ENGLISH_UK; + + //variants of wxLANGUAGE_ENGLISH_US + case wxLANGUAGE_ENGLISH: + case wxLANGUAGE_ENGLISH_CANADA: + case wxLANGUAGE_ENGLISH_PHILIPPINES: + return wxLANGUAGE_ENGLISH_US; + + //variants of wxLANGUAGE_FRENCH + case wxLANGUAGE_FRENCH_BELGIAN: + case wxLANGUAGE_FRENCH_CANADIAN: + case wxLANGUAGE_FRENCH_LUXEMBOURG: + case wxLANGUAGE_FRENCH_MONACO: + case wxLANGUAGE_FRENCH_SWISS: + return wxLANGUAGE_FRENCH; + + //variants of wxLANGUAGE_GERMAN + case wxLANGUAGE_GERMAN_AUSTRIAN: + case wxLANGUAGE_GERMAN_BELGIUM: + case wxLANGUAGE_GERMAN_LIECHTENSTEIN: + case wxLANGUAGE_GERMAN_LUXEMBOURG: + case wxLANGUAGE_GERMAN_SWISS: + return wxLANGUAGE_GERMAN; + + //variants of wxLANGUAGE_ITALIAN + case wxLANGUAGE_ITALIAN_SWISS: + return wxLANGUAGE_ITALIAN; + + //variants of wxLANGUAGE_NORWEGIAN_BOKMAL + case wxLANGUAGE_NORWEGIAN_NYNORSK: + return wxLANGUAGE_NORWEGIAN_BOKMAL; + + //variants of wxLANGUAGE_ROMANIAN + case wxLANGUAGE_MOLDAVIAN: + return wxLANGUAGE_ROMANIAN; + + //variants of wxLANGUAGE_RUSSIAN + case wxLANGUAGE_RUSSIAN_UKRAINE: + return wxLANGUAGE_RUSSIAN; + + //variants of wxLANGUAGE_SERBIAN + case wxLANGUAGE_SERBIAN_CYRILLIC: + case wxLANGUAGE_SERBIAN_LATIN: + case wxLANGUAGE_SERBO_CROATIAN: + return wxLANGUAGE_SERBIAN; + + //variants of wxLANGUAGE_SPANISH + case wxLANGUAGE_SPANISH_ARGENTINA: + case wxLANGUAGE_SPANISH_BOLIVIA: + case wxLANGUAGE_SPANISH_CHILE: + case wxLANGUAGE_SPANISH_COLOMBIA: + case wxLANGUAGE_SPANISH_COSTA_RICA: + case wxLANGUAGE_SPANISH_DOMINICAN_REPUBLIC: + case wxLANGUAGE_SPANISH_ECUADOR: + case wxLANGUAGE_SPANISH_EL_SALVADOR: + case wxLANGUAGE_SPANISH_GUATEMALA: + case wxLANGUAGE_SPANISH_HONDURAS: + case wxLANGUAGE_SPANISH_MEXICAN: + case wxLANGUAGE_SPANISH_MODERN: + case wxLANGUAGE_SPANISH_NICARAGUA: + case wxLANGUAGE_SPANISH_PANAMA: + case wxLANGUAGE_SPANISH_PARAGUAY: + case wxLANGUAGE_SPANISH_PERU: + case wxLANGUAGE_SPANISH_PUERTO_RICO: + case wxLANGUAGE_SPANISH_URUGUAY: + case wxLANGUAGE_SPANISH_US: + case wxLANGUAGE_SPANISH_VENEZUELA: + return wxLANGUAGE_SPANISH; + + //variants of wxLANGUAGE_SWEDISH + case wxLANGUAGE_SWEDISH_FINLAND: + return wxLANGUAGE_SWEDISH; + + //languages without variants: + //case wxLANGUAGE_CROATIAN: + //case wxLANGUAGE_CZECH: + //case wxLANGUAGE_DANISH: + //case wxLANGUAGE_FINNISH: + //case wxLANGUAGE_GREEK: + //case wxLANGUAGE_HEBREW: + //case wxLANGUAGE_HUNGARIAN: + //case wxLANGUAGE_JAPANESE: + //case wxLANGUAGE_KOREAN: + //case wxLANGUAGE_LITHUANIAN: + //case wxLANGUAGE_POLISH: + //case wxLANGUAGE_PORTUGUESE: + //case wxLANGUAGE_PORTUGUESE_BRAZILIAN: + //case wxLANGUAGE_SCOTS_GAELIC: + //case wxLANGUAGE_SLOVENIAN: + //case wxLANGUAGE_TURKISH: + //case wxLANGUAGE_UKRAINIAN: + default: + return language; + } +} + + +//global wxWidgets localization: sets up C localization runtime as well! +class wxWidgetsLocale +{ +public: + static void init(wxLanguage lng) + { + locale.reset(); //avoid global locale lifetime overlap! wxWidgets cannot handle this and will crash! + locale.reset(new wxLocale); + + const wxLanguageInfo* sysLngInfo = wxLocale::GetLanguageInfo(wxLocale::GetSystemLanguage()); + const wxLanguageInfo* selLngInfo = wxLocale::GetLanguageInfo(lng); + + const bool sysLangIsRTL = sysLngInfo ? sysLngInfo->LayoutDirection == wxLayout_RightToLeft : false; + const bool selectedLangIsRTL = selLngInfo ? selLngInfo->LayoutDirection == wxLayout_RightToLeft : false; + +#ifdef NDEBUG + wxLogNull dummy; //rather than implementing a reasonable error handling wxWidgets decides to shows a modal dialog in wxLocale::Init -> at least we can shut it up! +#endif + if (sysLangIsRTL == selectedLangIsRTL) + locale->Init(wxLANGUAGE_DEFAULT); //use sys-lang to preserve sub-language specific rules (e.g. german swiss number punctation) + else + locale->Init(lng); //have to use the supplied language to enable RTL layout different than user settings + locLng = lng; + } + + static void release() { locale.reset(); locLng = wxLANGUAGE_UNKNOWN; } + + static wxLanguage getLanguage() { return locLng; } + +private: + static std::unique_ptr locale; + static wxLanguage locLng; +}; +std::unique_ptr wxWidgetsLocale::locale; +wxLanguage wxWidgetsLocale::locLng = wxLANGUAGE_UNKNOWN; +} + + +void zen::releaseWxLocale() +{ + wxWidgetsLocale::release(); +} + + +void zen::setLanguage(int language) //throw FileError +{ + if (language == getLanguage() && wxWidgetsLocale::getLanguage() == language) + return; //support polling + + //(try to) retrieve language file + std::wstring languageFile; + + for (auto it = ExistingTranslations::get().begin(); it != ExistingTranslations::get().end(); ++it) + if (it->languageID == language) + { + languageFile = it->languageFile; + break; + } + + //load language file into buffer + if (languageFile.empty()) //if languageFile is empty, texts will be english by default + zen::setTranslator(); + else + try + { + zen::setTranslator(new FFSTranslation(utfCvrtTo(languageFile), static_cast(language))); //throw lngfile::ParsingError, parse_plural::ParsingError + } + catch (lngfile::ParsingError& e) + { + throw FileError(replaceCpy(replaceCpy(replaceCpy(_("Error parsing file %x, row %y, column %z."), + L"%x", fmtFileName(utfCvrtTo(languageFile))), + L"%y", numberTo(e.row_ + 1)), + L"%z", numberTo(e.col_ + 1)) + + L"\n\n" + e.msg_); + } + catch (parse_plural::ParsingError&) + { + throw FileError(L"Invalid plural form definition"); //user should never see this! + } + + //handle RTL swapping: we need wxWidgets to do this + wxWidgetsLocale::init(languageFile.empty() ? wxLANGUAGE_ENGLISH : static_cast(language)); +} + + +int zen::getLanguage() +{ + const FFSTranslation* loc = dynamic_cast(zen::getTranslator()); + return loc ? loc->langId() : wxLANGUAGE_ENGLISH_US; +} + + +int zen::retrieveSystemLanguage() +{ + return mapLanguageDialect(static_cast(wxLocale::GetSystemLanguage())); +} diff --git a/FreeFileSync/Source/lib/localization.h b/FreeFileSync/Source/lib/localization.h new file mode 100644 index 00000000..2d871dd7 --- /dev/null +++ b/FreeFileSync/Source/lib/localization.h @@ -0,0 +1,46 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef LOCALIZATION_H_8917342083178321534 +#define LOCALIZATION_H_8917342083178321534 + +#include +#include +//#include + +namespace zen +{ +class ExistingTranslations +{ +public: + struct Entry + { + int languageID; + std::wstring languageName; + std::wstring languageFile; + std::wstring translatorName; + std::wstring languageFlag; + }; + static const std::vector& get(); + +private: + ExistingTranslations(); + ExistingTranslations(const ExistingTranslations&); + ExistingTranslations& operator=(const ExistingTranslations&); + + std::vector locMapping; +}; + + +void setLanguage(int language); //throw FileError +int getLanguage(); +int retrieveSystemLanguage(); + +void releaseWxLocale(); //wxLocale crashes miserably on wxGTK when destructor runs during global cleanup => call in wxApp::OnExit +//"You should delete all wxWidgets object that you created by the time OnExit finishes. In particular, do not destroy them from application class' destructor!" +} + +#endif //LOCALIZATION_H_8917342083178321534 diff --git a/FreeFileSync/Source/lib/lock_holder.h b/FreeFileSync/Source/lib/lock_holder.h new file mode 100644 index 00000000..cf4c460e --- /dev/null +++ b/FreeFileSync/Source/lib/lock_holder.h @@ -0,0 +1,56 @@ +#ifndef LOCK_HOLDER_H_489572039485723453425 +#define LOCK_HOLDER_H_489572039485723453425 + +#include +#include +#include +#include "dir_lock.h" +#include "status_handler.h" +//#include "dir_exist_async.h" + +namespace zen +{ +const Zstring LOCK_FILE_ENDING = Zstr(".ffs_lock"); //intermediate locks created by DirLock use this extension, too! + +//hold locks for a number of directories without blocking during lock creation +//call after having checked directory existence! +class LockHolder +{ +public: + LockHolder(const std::set& dirnamesExisting, //resolved dirname ending with path separator + bool& warningDirectoryLockFailed, + ProcessCallback& procCallback) + { + for (const Zstring& dirnameFmt : dirnamesExisting) + { + assert(endsWith(dirnameFmt, FILE_NAME_SEPARATOR)); //this is really the contract, formatting does other things as well, e.g. macro substitution + + class WaitOnLockHandler : public DirLockCallback + { + public: + WaitOnLockHandler(ProcessCallback& pc) : pc_(pc) {} + virtual void requestUiRefresh() { pc_.requestUiRefresh(); } //allowed to throw exceptions + virtual void reportStatus(const std::wstring& text) { pc_.reportStatus(text); } + private: + ProcessCallback& pc_; + } callback(procCallback); + + try + { + //lock file creation is synchronous and may block noticeably for very slow devices (usb sticks, mapped cloud storages) + lockHolder.push_back(DirLock(dirnameFmt + Zstr("sync") + LOCK_FILE_ENDING, &callback)); //throw FileError + } + catch (const FileError& e) + { + const std::wstring msg = replaceCpy(_("Cannot set directory lock for %x."), L"%x", fmtFileName(dirnameFmt)) + L"\n\n" + e.toString(); + procCallback.reportWarning(msg, warningDirectoryLockFailed); //may throw! + } + } + } + +private: + std::vector lockHolder; +}; +} + +#endif //LOCK_HOLDER_H_489572039485723453425 diff --git a/FreeFileSync/Source/lib/norm_filter.h b/FreeFileSync/Source/lib/norm_filter.h new file mode 100644 index 00000000..552931e2 --- /dev/null +++ b/FreeFileSync/Source/lib/norm_filter.h @@ -0,0 +1,85 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef NORM_FILTER_H_INCLUDED +#define NORM_FILTER_H_INCLUDED + +#include "hard_filter.h" +#include "soft_filter.h" + +namespace zen +{ +struct NormalizedFilter //grade-a filter: global/local filter settings combined, units resolved, ready for use +{ + NormalizedFilter(const HardFilter::FilterRef& hf, const SoftFilter& sf) : nameFilter(hf), timeSizeFilter(sf) {} + + //"hard" filter: relevant during comparison, physically skips files + HardFilter::FilterRef nameFilter; + //"soft" filter: relevant after comparison; equivalent to user selection + SoftFilter timeSizeFilter; +}; + + +//combine global and local filters via "logical and" +NormalizedFilter normalizeFilters(const FilterConfig& global, const FilterConfig& local); + +inline +bool isNullFilter(const FilterConfig& filterCfg) +{ + return NameFilter::isNull(filterCfg.includeFilter, filterCfg.excludeFilter) && + SoftFilter(filterCfg.timeSpan, filterCfg.unitTimeSpan, + filterCfg.sizeMin, filterCfg.unitSizeMin, + filterCfg.sizeMax, filterCfg.unitSizeMax).isNull(); +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + +// ----------------------- implementation ----------------------- +inline +NormalizedFilter normalizeFilters(const FilterConfig& global, const FilterConfig& local) +{ + HardFilter::FilterRef globalName = std::make_shared(global.includeFilter, global.excludeFilter); + HardFilter::FilterRef localName = std::make_shared(local .includeFilter, local .excludeFilter); + + SoftFilter globalTimeSize(global.timeSpan, global.unitTimeSpan, + global.sizeMin, global.unitSizeMin, + global.sizeMax, global.unitSizeMax); + + SoftFilter localTimeSize(local.timeSpan, local.unitTimeSpan, + local.sizeMin, local.unitSizeMin, + local.sizeMax, local.unitSizeMax); + + return NormalizedFilter(combineFilters(globalName, localName), + combineFilters(globalTimeSize, localTimeSize)); +} +} + +#endif //NORM_FILTER_H_INCLUDED diff --git a/FreeFileSync/Source/lib/osx_file_icon.h b/FreeFileSync/Source/lib/osx_file_icon.h new file mode 100644 index 00000000..5edfd740 --- /dev/null +++ b/FreeFileSync/Source/lib/osx_file_icon.h @@ -0,0 +1,36 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef OSX_FILE_ICON_8427508422345342 +#define OSX_FILE_ICON_8427508422345342 + +#include +#include + +namespace osx +{ +struct ImageData +{ + ImageData(int w, int h) : width(w), height(h), rgb(w* h * 3), alpha(w* h) {} + ImageData(ImageData&& tmp) : width(tmp.width), height(tmp.height) { rgb.swap(tmp.rgb); alpha.swap(tmp.alpha); } + + const int width; + const int height; + std::vector rgb; //rgb-byte order for use with wxImage + std::vector alpha; + +private: + ImageData(const ImageData&); + ImageData& operator=(const ImageData&); +}; + +ImageData getThumbnail(const char* filename, int requestedSize); //throw SysError +ImageData getFileIcon (const char* filename, int requestedSize); //throw SysError +ImageData getDefaultFileIcon (int requestedSize); //throw SysError +ImageData getDefaultFolderIcon(int requestedSize); //throw SysError +} + +#endif //OSX_FILE_ICON_8427508422345342 diff --git a/FreeFileSync/Source/lib/osx_file_icon.mm b/FreeFileSync/Source/lib/osx_file_icon.mm new file mode 100644 index 00000000..fb3c6490 --- /dev/null +++ b/FreeFileSync/Source/lib/osx_file_icon.mm @@ -0,0 +1,179 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "osx_file_icon.h" +#include +#include +#include + +namespace +{ +osx::ImageData extractBytes(NSImage* nsImg, int requestedSize) //throw SysError; NSException? +{ + /* + wxBitmap(NSImage*) is not good enough: it calls "[NSBitmapImageRep imageRepWithData:[img TIFFRepresentation]]" + => inefficient: TIFFRepresentation converts all contained images of an NSImage + => lacking: imageRepWithData extracts the first contained image only! + => wxBitmap(NSImage*) is wxCocoa only, deprecated! + => wxWidgets generally is not thread-safe so care must be taken to use wxBitmap from main thread only! (e.g. race-condition on non-atomic ref-count!!!) + + -> we need only a single bitmap at a specific size => extract raw bytes for use with wxImage in a thread-safe way! + */ + + //we choose the Core Graphics solution; for the equivalent App-Kit way see: http://www.cocoabuilder.com/archive/cocoa/193131-is-lockfocus-main-thread-specific.html#193191 + + ZEN_OSX_ASSERT(requestedSize > 0); + NSRect rectProposed = NSMakeRect(0, 0, requestedSize, requestedSize); //this is merely a hint! + + CGImageRef imgRef = [nsImg CGImageForProposedRect:&rectProposed context:nil hints:nil]; + ZEN_OSX_ASSERT(imgRef != NULL); //can this fail? not documented; ownership?? not documented! + + const size_t width = ::CGImageGetWidth (imgRef); + const size_t height = ::CGImageGetHeight(imgRef); + + ZEN_OSX_ASSERT(width > 0 && height > 0 && requestedSize > 0); + + int trgWidth = width; + int trgHeight = height; + + const int maxExtent = std::max(width, height); //don't stretch small images, but shrink large ones instead! + if (requestedSize < maxExtent) + { + trgWidth = width * requestedSize / maxExtent; + trgHeight = height * requestedSize / maxExtent; + } + + CGColorSpaceRef colorSpace = ::CGColorSpaceCreateDeviceRGB(); + ZEN_OSX_ASSERT(colorSpace != NULL); //may fail + ZEN_ON_SCOPE_EXIT(::CGColorSpaceRelease(colorSpace)); + + std::vector buf(trgWidth* trgHeight * 4); //32-bit ARGB, little endian byte order -> already initialized with 0 = fully transparent + + //supported color spaces: https://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-BCIBHHBB + CGContextRef ctxRef = ::CGBitmapContextCreate(&buf[0], //void *data, + trgWidth, //size_t width, + trgHeight, //size_t height, + 8, //size_t bitsPerComponent, + 4 * trgWidth, //size_t bytesPerRow, + colorSpace, //CGColorSpaceRef colorspace, + kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little); //CGBitmapInfo bitmapInfo + ZEN_OSX_ASSERT(ctxRef != NULL); + ZEN_ON_SCOPE_EXIT(::CGContextRelease(ctxRef)); + + ::CGContextDrawImage(ctxRef, CGRectMake(0, 0, trgWidth, trgHeight), imgRef); //can this fail? not documented + + //::CGContextFlush(ctxRef); //"If you pass [...] a bitmap context, this function does nothing." + + osx::ImageData imgOut(trgWidth, trgHeight); + + auto it = buf.begin(); + auto itOutRgb = imgOut.rgb.begin(); + auto itOutAlpha = imgOut.alpha.begin(); + for (int i = 0; i < trgWidth * trgHeight; ++i) + { + const unsigned char b = *it++; + const unsigned char g = *it++; + const unsigned char r = *it++; + const unsigned char a = *it++; + + //unsigned arithmetics caveat! + auto demultiplex = [&](unsigned char c) { return static_cast(numeric::confineCpy(a == 0 ? 0 : (c * 255 + a - 1) / a, 0, 255)); }; //=ceil(c * 255 / a) + + *itOutRgb++ = demultiplex(r); + *itOutRgb++ = demultiplex(g); + *itOutRgb++ = demultiplex(b); + *itOutAlpha++ = a; + } + + return imgOut; +} +} + + +osx::ImageData osx::getThumbnail(const char* filename, int requestedSize) //throw SysError +{ + @try + { + @autoreleasepool + { + NSString* nsFile = [NSString stringWithCString:filename encoding:NSUTF8StringEncoding]; + ZEN_OSX_ASSERT(nsFile != nil); //throw SysError; can this fail? not documented + //stringWithCString returns string which is already set to autorelease! + + NSImage* nsImg = [[[NSImage alloc] initWithContentsOfFile:nsFile] autorelease]; + ZEN_OSX_ASSERT(nsImg != nil); //may fail + + return extractBytes(nsImg, requestedSize); //throw SysError + } + } + @catch(NSException* e) + { + throwSysError(e); //throw SysError + } +} + + +osx::ImageData osx::getFileIcon(const char* filename, int requestedSize) //throw SysError +{ + @try + { + @autoreleasepool + { + NSString* nsFile = [NSString stringWithCString:filename encoding:NSUTF8StringEncoding]; + ZEN_OSX_ASSERT(nsFile != nil); //throw SysError; can this fail? not documented + //stringWithCString returns string which is already set to autorelease! + + NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFile:nsFile]; + ZEN_OSX_ASSERT(nsImg != nil); //can this fail? not documented + + return extractBytes(nsImg, requestedSize); //throw SysError + } + } + @catch (NSException* e) + { + throwSysError(e); //throw SysError + } +} + + +osx::ImageData osx::getDefaultFileIcon(int requestedSize) //throw SysError +{ + @try + { + @autoreleasepool + { + NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericDocumentIcon)]; + //NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFileType:@"dat"]; + ZEN_OSX_ASSERT(nsImg != nil); //can this fail? not documented + + return extractBytes(nsImg, requestedSize); //throw SysError + } + } + @catch (NSException* e) + { + throwSysError(e); //throw SysError + } +} + + +osx::ImageData osx::getDefaultFolderIcon(int requestedSize) //throw SysError +{ + @try + { + @autoreleasepool + { + NSImage* nsImg = [NSImage imageNamed:NSImageNameFolder]; + //NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericFolderIcon)]; + ZEN_OSX_ASSERT(nsImg != nil); //may fail + + return extractBytes(nsImg, requestedSize); //throw SysError + } + } + @catch (NSException* e) + { + throwSysError(e); //throw SysError + } +} diff --git a/FreeFileSync/Source/lib/parallel_scan.cpp b/FreeFileSync/Source/lib/parallel_scan.cpp new file mode 100644 index 00000000..9c6b16a9 --- /dev/null +++ b/FreeFileSync/Source/lib/parallel_scan.cpp @@ -0,0 +1,588 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "parallel_scan.h" +#include +#include +#include //includes +#include +#include +#include +#include "db_file.h" +#include "lock_holder.h" + +using namespace zen; + + +namespace +{ +/* +#ifdef ZEN_WIN + +struct DiskInfo +{ + DiskInfo() : + driveType(DRIVE_UNKNOWN), + diskID(-1) {} + + UINT driveType; + int diskID; // -1 if id could not be determined, this one is filled if driveType == DRIVE_FIXED or DRIVE_REMOVABLE; +}; + +inline +bool operator<(const DiskInfo& lhs, const DiskInfo& rhs) +{ + if (lhs.driveType != rhs.driveType) + return lhs.driveType < rhs.driveType; + + if (lhs.diskID < 0 || rhs.diskID < 0) + return false; + //consider "same", reason: one volume may be uniquely associated with one disk, while the other volume is associated to the same disk AND another one! + //volume <-> disk is 0..N:1..N + + return lhs.diskID < rhs.diskID ; +} + + +DiskInfo retrieveDiskInfo(const Zstring& pathName) +{ + std::vector volName(std::max(pathName.size(), static_cast(10000))); + + DiskInfo output; + + //full pathName need not yet exist! + if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName, + &volName[0], //__out LPTSTR lpszVolumePathName, + static_cast(volName.size()))) //__in DWORD cchBufferLength + return output; + + const Zstring rootPathName = &volName[0]; + + output.driveType = ::GetDriveType(rootPathName.c_str()); + + if (output.driveType == DRIVE_NO_ROOT_DIR) //these two should be the same error category + output.driveType = DRIVE_UNKNOWN; + + if (output.driveType != DRIVE_FIXED && output.driveType != DRIVE_REMOVABLE) + return output; //no reason to get disk ID + + //go and find disk id: + + //format into form: "\\.\C:" + Zstring volnameFmt = rootPathName; + if (endsWith(volnameFmt, FILE_NAME_SEPARATOR)) + volnameFmt.resize(volnameFmt.size() - 1); + volnameFmt = L"\\\\.\\" + volnameFmt; + + HANDLE hVolume = ::CreateFile(volnameFmt.c_str(), + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + 0, + nullptr); + if (hVolume == INVALID_HANDLE_VALUE) + return output; + ZEN_ON_SCOPE_EXIT(::CloseHandle(hVolume)); + + std::vector buffer(sizeof(VOLUME_DISK_EXTENTS) + sizeof(DISK_EXTENT)); //reserve buffer for at most one disk! call below will then fail if volume spans multiple disks! + + DWORD bytesReturned = 0; + if (!::DeviceIoControl(hVolume, // handle to device + IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, // dwIoControlCode + nullptr, // lpInBuffer + 0, // nInBufferSize + &buffer[0], // output buffer + static_cast(buffer.size()), // size of output buffer + &bytesReturned, // number of bytes returned + nullptr)) // OVERLAPPED structure + return output; + + const VOLUME_DISK_EXTENTS& volDisks = *reinterpret_cast(&buffer[0]); + + if (volDisks.NumberOfDiskExtents != 1) + return output; + + output.diskID = volDisks.Extents[0].DiskNumber; + + return output; +} +#endif +*/ + +/* +PERF NOTE + +-------------------------------------------- +|Testcase: Reading from two different disks| +-------------------------------------------- +Windows 7: + 1st(unbuffered) |2nd (OS buffered) + ---------------------------------- +1 Thread: 57s | 8s +2 Threads: 39s | 7s + +-------------------------------------------------- +|Testcase: Reading two directories from same disk| +-------------------------------------------------- +Windows 7: Windows XP: + 1st(unbuffered) |2nd (OS buffered) 1st(unbuffered) |2nd (OS buffered) + ---------------------------------- ---------------------------------- +1 Thread: 41s | 13s 1 Thread: 45s | 13s +2 Threads: 42s | 11s 2 Threads: 38s | 8s + +=> Traversing does not take any advantage of file locality so that even multiple threads operating on the same disk impose no performance overhead! (even faster on XP) + +std::vector> separateByDistinctDisk(const std::set& dirkeys) +{ + //use one thread per physical disk: + typedef std::map> DiskKeyMapping; + DiskKeyMapping tmp; + std::for_each(dirkeys.begin(), dirkeys.end(), + [&](const DirectoryKey& key) { tmp[retrieveDiskInfo(key.dirnameFull_)].insert(key); }); + + std::vector> buckets; + std::transform(tmp.begin(), tmp.end(), std::back_inserter(buckets), + [&](const DiskKeyMapping::value_type& diskToKey) { return diskToKey.second; }); + return buckets; +} +*/ + +//------------------------------------------------------------------------------------------ +typedef Zbase BasicWString; //thread safe string class for UI texts + + +class AsyncCallback //actor pattern +{ +public: + AsyncCallback() : + notifyingThreadID(0), + textScanning(_("Scanning:")), + itemsScanned(0), + activeWorker(0) {} + + FillBufferCallback::HandleError reportError(const std::wstring& msg, size_t retryNumber) //blocking call: context of worker thread + { + boost::unique_lock dummy(lockErrorInfo); + while (errorInfo.get() || errorResponse.get()) + conditionCanReportError.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point! + + errorInfo = make_unique>(BasicWString(msg), retryNumber); + + while (!errorResponse.get()) + conditionGotResponse.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point! + + FillBufferCallback::HandleError rv = *errorResponse; + + errorInfo.reset(); + errorResponse.reset(); + + dummy.unlock(); //optimization for condition_variable::notify_all() + conditionCanReportError.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796 + + return rv; + } + + void processErrors(FillBufferCallback& callback) //context of main thread, call repreatedly + { + boost::unique_lock dummy(lockErrorInfo); + if (errorInfo.get() && !errorResponse.get()) + { + FillBufferCallback::HandleError rv = callback.reportError(copyStringTo(errorInfo->first), errorInfo->second); //throw! + errorResponse = make_unique(rv); + + dummy.unlock(); //optimization for condition_variable::notify_all() + conditionGotResponse.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796 + } + } + + void incrementNotifyingThreadId() { ++notifyingThreadID; } //context of main thread + + void reportCurrentFile(const Zstring& filename, long threadID) //context of worker thread + { + if (threadID != notifyingThreadID) return; //only one thread at a time may report status + + boost::lock_guard dummy(lockCurrentStatus); + currentFile = filename; + currentStatus.clear(); + } + + void reportCurrentStatus(const std::wstring& status, long threadID) //context of worker thread + { + if (threadID != notifyingThreadID) return; //only one thread may report status + + boost::lock_guard dummy(lockCurrentStatus); + currentFile.clear(); + currentStatus = BasicWString(status); //we cannot assume std::wstring to be thread safe (yet)! + } + + std::wstring getCurrentStatus() //context of main thread, call repreatedly + { + Zstring filename; + std::wstring statusMsg; + { + boost::lock_guard dummy(lockCurrentStatus); + if (!currentFile.empty()) + filename = currentFile; + else if (!currentStatus.empty()) + statusMsg = copyStringTo(currentStatus); + } + + if (!filename.empty()) + { + std::wstring statusText = copyStringTo(textScanning); + const long activeCount = activeWorker; + if (activeCount >= 2) + statusText += L" [" + replaceCpy(_P("1 thread", "%x threads", activeCount), L"%x", numberTo(activeCount)) + L"]"; + + statusText += L" " + fmtFileName(filename); + return statusText; + } + else + return statusMsg; + } + + void incItemsScanned() { ++itemsScanned; } + long getItemsScanned() const { return itemsScanned; } + + void incActiveWorker() { ++activeWorker; } + void decActiveWorker() { --activeWorker; } + long getActiveWorker() const { return activeWorker; } + +private: + //---- error handling ---- + boost::mutex lockErrorInfo; + boost::condition_variable conditionCanReportError; + boost::condition_variable conditionGotResponse; + std::unique_ptr> errorInfo; //error message + retry number + std::unique_ptr errorResponse; + + //---- status updates ---- + boost::detail::atomic_count notifyingThreadID; + //CAVEAT: do NOT use boost::thread::id as long as this showstopper exists: https://svn.boost.org/trac/boost/ticket/5754 + boost::mutex lockCurrentStatus; //use a different lock for current file: continue traversing while some thread may process an error + Zstring currentFile; //only one of these two is filled at a time! + BasicWString currentStatus; // + + const BasicWString textScanning; //this one is (currently) not shared and could be made a std::wstring, but we stay consistent and use thread-safe variables in this class only! + + //---- status updates II (lock free) ---- + boost::detail::atomic_count itemsScanned; + boost::detail::atomic_count activeWorker; +}; + +//------------------------------------------------------------------------------------------------- + +struct TraverserShared +{ +public: + TraverserShared(long threadID, + SymLinkHandling handleSymlinks, + const HardFilter::FilterRef& filter, + std::set& failedDirReads, + std::set& failedItemReads, + AsyncCallback& acb) : + handleSymlinks_(handleSymlinks), + filterInstance(filter), + failedDirReads_(failedDirReads), + failedItemReads_(failedItemReads), + acb_(acb), + threadID_(threadID) {} + + const SymLinkHandling handleSymlinks_; + const HardFilter::FilterRef filterInstance; //always bound! + + std::set& failedDirReads_; + std::set& failedItemReads_; + + AsyncCallback& acb_; + const long threadID_; +}; + + +class DirCallback : public zen::TraverseCallback +{ +public: + DirCallback(TraverserShared& config, + const Zstring& relNameParentPf, //postfixed with FILE_NAME_SEPARATOR! + DirContainer& output) : + cfg(config), + relNameParentPf_(relNameParentPf), + output_(output) {} + + virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details); + virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details); + virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& fullName); + virtual void releaseDirTraverser(TraverseCallback* trav); + + virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber); + virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName); + +private: + TraverserShared& cfg; + const Zstring relNameParentPf_; + DirContainer& output_; +}; + + +void DirCallback::onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) +{ + boost::this_thread::interruption_point(); + + const Zstring fileNameShort(shortName); + + //do not list the database file(s) sync.ffs_db, sync.x64.ffs_db, etc. or lock files + if (endsWith(fileNameShort, SYNC_DB_FILE_ENDING) || + endsWith(fileNameShort, LOCK_FILE_ENDING)) + return; + + //update status information no matter whether object is excluded or not! + cfg.acb_.reportCurrentFile(fullName, cfg.threadID_); + + //------------------------------------------------------------------------------------ + //apply filter before processing (use relative name!) + if (!cfg.filterInstance->passFileFilter(relNameParentPf_ + fileNameShort)) + return; + + // std::string fileId = details.fileSize >= 1024 * 1024U ? util::retrieveFileID(fullName) : std::string(); + /* + Perf test Windows 7, SSD, 350k files, 50k dirs, files > 1MB: 7000 + regular: 6.9s + ID per file: 43.9s + ID per file > 1MB: 7.2s + ID per dir: 8.4s + + Linux: retrieveFileID takes about 50% longer in VM! (avoidable because of redundant stat() call!) + */ + + output_.addSubFile(fileNameShort, FileDescriptor(details.lastWriteTime, details.fileSize, details.id, details.symlinkInfo != nullptr)); + + cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator +} + + +DirCallback::HandleLink DirCallback::onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) +{ + boost::this_thread::interruption_point(); + + //update status information no matter whether object is excluded or not! + cfg.acb_.reportCurrentFile(fullName, cfg.threadID_); + + switch (cfg.handleSymlinks_) + { + case SYMLINK_EXCLUDE: + return LINK_SKIP; + + case SYMLINK_USE_DIRECTLY: + if (cfg.filterInstance->passFileFilter(relNameParentPf_ + shortName)) //always use file filter: Link type may not be "stable" on Linux! + { + output_.addSubLink(shortName, LinkDescriptor(details.lastWriteTime)); + cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator + } + return LINK_SKIP; + + case SYMLINK_FOLLOW_LINK: + //filter symlinks before trying to follow them: handle user-excluded broken symlinks! + //since we don't know what the symlink will resolve to, only do this when both variants agree: + if (!cfg.filterInstance->passFileFilter(relNameParentPf_ + shortName)) + { + bool subObjMightMatch = true; + if (!cfg.filterInstance->passDirFilter(relNameParentPf_ + shortName, &subObjMightMatch)) + if (!subObjMightMatch) + return LINK_SKIP; + } + return LINK_FOLLOW; + } + + assert(false); + return LINK_SKIP; +} + + +TraverseCallback* DirCallback::onDir(const Zchar* shortName, const Zstring& fullName) +{ + boost::this_thread::interruption_point(); + + //update status information no matter whether object is excluded or not! + cfg.acb_.reportCurrentFile(fullName, cfg.threadID_); + + //------------------------------------------------------------------------------------ + const Zstring& relPath = relNameParentPf_ + shortName; + + //apply filter before processing (use relative name!) + bool subObjMightMatch = true; + const bool passFilter = cfg.filterInstance->passDirFilter(relPath, &subObjMightMatch); + if (!passFilter && !subObjMightMatch) + return nullptr; //do NOT traverse subdirs + //else: attention! ensure directory filtering is applied later to exclude actually filtered directories + + DirContainer& subDir = output_.addSubDir(shortName); + if (passFilter) + cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator + + return new DirCallback(cfg, relPath + FILE_NAME_SEPARATOR, subDir); //releaseDirTraverser() is guaranteed to be called in any case +} + + +void DirCallback::releaseDirTraverser(TraverseCallback* trav) +{ + TraverseCallback::releaseDirTraverser(trav); //no-op, introduce compile-time coupling + delete trav; +} + + +DirCallback::HandleError DirCallback::reportDirError(const std::wstring& msg, size_t retryNumber) +{ + switch (cfg.acb_.reportError(msg, retryNumber)) + { + case FillBufferCallback::ON_ERROR_IGNORE: + cfg.failedDirReads_.insert(relNameParentPf_); + return ON_ERROR_IGNORE; + + case FillBufferCallback::ON_ERROR_RETRY: + return ON_ERROR_RETRY; + } + assert(false); + return ON_ERROR_IGNORE; +} + + +DirCallback::HandleError DirCallback::reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) +{ + switch (cfg.acb_.reportError(msg, retryNumber)) + { + case FillBufferCallback::ON_ERROR_IGNORE: + cfg.failedItemReads_.insert(relNameParentPf_ + shortName); + return ON_ERROR_IGNORE; + + case FillBufferCallback::ON_ERROR_RETRY: + return ON_ERROR_RETRY; + } + assert(false); + return ON_ERROR_IGNORE; +} + + +#ifdef ZEN_WIN +class DstHackCallbackImpl : public DstHackCallback +{ +public: + DstHackCallbackImpl(AsyncCallback& acb, long threadID) : + acb_(acb), + threadID_(threadID), + textApplyingDstHack(replaceCpy(_("Encoding extended time information: %x"), L"%x", L"\n%x")) {} + +private: + virtual void requestUiRefresh(const Zstring& filename) //applying DST hack imposes significant one-time performance drawback => callback to inform user + { + acb_.reportCurrentStatus(replaceCpy(textApplyingDstHack, L"%x", fmtFileName(filename)), threadID_); + } + + AsyncCallback& acb_; + long threadID_; + const std::wstring textApplyingDstHack; +}; +#endif + +//------------------------------------------------------------------------------------------ + +class WorkerThread +{ +public: + WorkerThread(long threadID, + const std::shared_ptr& acb, + const DirectoryKey& dirKey, + DirectoryValue& dirOutput) : + threadID_(threadID), + acb_(acb), + dirKey_(dirKey), + dirOutput_(dirOutput) {} + + void operator()() //thread entry + { + acb_->incActiveWorker(); + ZEN_ON_SCOPE_EXIT(acb_->decActiveWorker();); + + acb_->reportCurrentFile(dirKey_.dirnameFull_, threadID_); //just in case first directory access is blocking + + TraverserShared travCfg(threadID_, + dirKey_.handleSymlinks_, //shared by all(!) instances of DirCallback while traversing a folder hierarchy + dirKey_.filter_, + dirOutput_.failedDirReads, + dirOutput_.failedItemReads, + *acb_); + + DirCallback traverser(travCfg, + Zstring(), + dirOutput_.dirCont); + + DstHackCallback* dstCallbackPtr = nullptr; +#ifdef ZEN_WIN + DstHackCallbackImpl dstCallback(*acb_, threadID_); + dstCallbackPtr = &dstCallback; +#endif + + //get all files and folders from directoryPostfixed (and subdirectories) + traverseFolder(dirKey_.dirnameFull_, traverser, dstCallbackPtr); //exceptions may be thrown! + } + +private: + long threadID_; + std::shared_ptr acb_; + const DirectoryKey dirKey_; + DirectoryValue& dirOutput_; +}; +} + + +void zen::fillBuffer(const std::set& keysToRead, //in + std::map& buf, //out + FillBufferCallback& callback, + size_t updateInterval) +{ + buf.clear(); + + FixedList worker; //note: we cannot use std::vector: compiler error on GCC 4.7, probably a boost screw-up + + zen::ScopeGuard guardWorker = zen::makeGuard([&] + { + for (boost::thread& wt : worker) + wt.interrupt(); //interrupt all at once first, then join + for (boost::thread& wt : worker) + if (wt.joinable()) //= precondition of thread::join(), which throws an exception if violated! + wt.join(); //in this context it is possible a thread is *not* joinable anymore due to the thread::timed_join() below! + }); + + auto acb = std::make_shared(); + + //init worker threads + for (const DirectoryKey& key : keysToRead) + { + assert(buf.find(key) == buf.end()); + DirectoryValue& dirOutput = buf[key]; + + const long threadId = static_cast(worker.size()); + worker.emplace_back(WorkerThread(threadId, acb, key, dirOutput)); + } + + //wait until done + for (boost::thread& wt : worker) + { + do + { + //update status + callback.reportStatus(acb->getCurrentStatus(), acb->getItemsScanned()); //throw! + + //process errors + acb->processErrors(callback); + } + while (!wt.timed_join(boost::posix_time::milliseconds(updateInterval))); + + acb->incrementNotifyingThreadId(); //process info messages of one thread at a time only + } + + guardWorker.dismiss(); +} diff --git a/FreeFileSync/Source/lib/parallel_scan.h b/FreeFileSync/Source/lib/parallel_scan.h new file mode 100644 index 00000000..408882bf --- /dev/null +++ b/FreeFileSync/Source/lib/parallel_scan.h @@ -0,0 +1,76 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef PARALLEL_SCAN_H_INCLUDED +#define PARALLEL_SCAN_H_INCLUDED + +#include +#include +#include "hard_filter.h" +#include "../structures.h" +#include "../file_hierarchy.h" + +namespace zen +{ +struct DirectoryKey +{ + DirectoryKey(const Zstring& dirnameFull, + const HardFilter::FilterRef& filter, + SymLinkHandling handleSymlinks) : + dirnameFull_(dirnameFull), + filter_(filter), + handleSymlinks_(handleSymlinks) {} + + Zstring dirnameFull_; + HardFilter::FilterRef filter_; //filter interface: always bound by design! + SymLinkHandling handleSymlinks_; +}; + +inline +bool operator<(const DirectoryKey& lhs, const DirectoryKey& rhs) +{ + if (lhs.handleSymlinks_ != rhs.handleSymlinks_) + return lhs.handleSymlinks_ < rhs.handleSymlinks_; + + const int cmpName = cmpFileName(lhs.dirnameFull_, rhs.dirnameFull_); + if (cmpName != 0) + return cmpName < 0; + + return *lhs.filter_ < *rhs.filter_; +} + + +struct DirectoryValue +{ + DirContainer dirCont; + std::set failedDirReads; //relative postfixed names (or empty string for root) for directories that could not be read (completely), e.g. access denied, or temporal network drop + std::set failedItemReads; //relative postfixed names (never empty) for failure to read single file/dir/symlink +}; + + +class FillBufferCallback +{ +public: + virtual ~FillBufferCallback() {} + + enum HandleError + { + ON_ERROR_RETRY, + ON_ERROR_IGNORE + }; + virtual HandleError reportError (const std::wstring& msg, size_t retryNumber) = 0; //may throw! + virtual void reportStatus(const std::wstring& msg, int itemsTotal ) = 0; // +}; + +//attention: ensure directory filtering is applied later to exclude filtered directories which have been kept as parent folders + +void fillBuffer(const std::set& keysToRead, //in + std::map& buf, //out + FillBufferCallback& callback, + size_t updateInterval); //unit: [ms] +} + +#endif // PARALLEL_SCAN_H_INCLUDED diff --git a/FreeFileSync/Source/lib/parse_lng.h b/FreeFileSync/Source/lib/parse_lng.h new file mode 100644 index 00000000..19a8e751 --- /dev/null +++ b/FreeFileSync/Source/lib/parse_lng.h @@ -0,0 +1,706 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef PARSE_LNG_HEADER_INCLUDED +#define PARSE_LNG_HEADER_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "parse_plural.h" +//#include + +namespace lngfile +{ +//singular forms +typedef std::map TranslationMap; //orig |-> translation + +//plural forms +typedef std::pair SingularPluralPair; //1 house| n houses +typedef std::vector PluralForms; //1 dom | 2 domy | 5 domw +typedef std::map TranslationPluralMap; //(sing/plu) |-> pluralforms + +struct TransHeader +{ + TransHeader() : pluralCount(0) {} + std::string languageName; //display name: "English (UK)" + std::string translatorName; //"Zenju" + std::string localeName; //ISO 639 language code + ISO 3166 country code, e.g. "en_GB", or "en_US" + std::string flagFile; //"england.png" + int pluralCount; //2 + std::string pluralDefinition; //"n == 1 ? 0 : 1" +}; + + +struct ParsingError +{ + ParsingError(const std::wstring& msg, size_t row, size_t col) : msg_(msg), row_(row), col_(col) {} + std::wstring msg_; //parser error message + size_t row_; //starting with 0 + size_t col_; // +}; +void parseLng(const std::string& fileStream, TransHeader& header, TranslationMap& out, TranslationPluralMap& pluralOut); //throw ParsingError +void parseHeader(const std::string& fileStream, TransHeader& header); //throw ParsingError + +class TranslationUnorderedList; //unordered list of unique translation items +std::string generateLng(const TranslationUnorderedList& in, const TransHeader& header); + + + + + + + + + + + + + + + + + + + +//--------------------------- implementation --------------------------- +enum class TranslationNewItemPos +{ + REL, + TOP +}; + +class TranslationUnorderedList //unordered list of unique translation items +{ +public: + TranslationUnorderedList(TranslationNewItemPos newItemPos, TranslationMap&& transOld, TranslationPluralMap&& transPluralOld) : + newItemPos_(newItemPos), transOld_(std::move(transOld)), transPluralOld_(std::move(transPluralOld)) {} + + void addItem(const std::string& orig) + { + if (!transUnique.insert(orig).second) return; + auto it = transOld_.find(orig); + if (it != transOld_.end() && !it->second.empty()) //preserve old translation from .lng file if existing + sequence.push_back(std::make_shared(std::make_pair(orig, it->second))); + else + switch (newItemPos_) + { + case TranslationNewItemPos::REL: + sequence.push_back(std::make_shared(std::make_pair(orig, std::string()))); + break; + case TranslationNewItemPos::TOP: + sequence.push_front(std::make_shared(std::make_pair(orig, std::string()))); //put untranslated items to the front of the .lng filebreak; + break; + } + } + + void addItem(const SingularPluralPair& orig) + { + if (!pluralUnique.insert(orig).second) return; + auto it = transPluralOld_.find(orig); + if (it != transPluralOld_.end() && !it->second.empty()) //preserve old translation from .lng file if existing + sequence.push_back(std::make_shared(std::make_pair(orig, it->second))); + else + switch (newItemPos_) + { + case TranslationNewItemPos::REL: + sequence.push_back(std::make_shared(std::make_pair(orig, PluralForms()))); + break; + case TranslationNewItemPos::TOP: + sequence.push_front(std::make_shared(std::make_pair(orig, PluralForms()))); //put untranslated items to the front of the .lng file + break; + } + } + + bool untranslatedTextExists() const { return std::any_of(sequence.begin(), sequence.end(), [](const std::shared_ptr& item) { return !item->hasTranslation(); }); } + + template + void visitItems(Function onTrans, Function2 onPluralTrans) const //onTrans takes (const TranslationMap::value_type&), onPluralTrans takes (const TranslationPluralMap::value_type&) + { + for (const auto& item : sequence) + if (auto regular = dynamic_cast(item.get())) + onTrans(regular->value); + else if (auto plural = dynamic_cast(item.get())) + onPluralTrans(plural->value); + else assert(false); + } + +private: + struct Item { virtual ~Item() {} virtual bool hasTranslation() const = 0; }; + struct RegularItem : public Item { RegularItem(const TranslationMap ::value_type& val) : value(val) {} virtual bool hasTranslation() const { return !value.second.empty(); } TranslationMap ::value_type value; }; + struct PluralItem : public Item { PluralItem (const TranslationPluralMap::value_type& val) : value(val) {} virtual bool hasTranslation() const { return !value.second.empty(); } TranslationPluralMap::value_type value; }; + + const TranslationNewItemPos newItemPos_; + std::list> sequence; //ordered list of translation elements + + std::set transUnique; //check uniqueness + std::set pluralUnique; // + + const TranslationMap transOld_; //reuse existing translation + const TranslationPluralMap transPluralOld_; // +}; + + +struct Token +{ + enum Type + { + //header information + TK_HEADER_BEGIN, + TK_HEADER_END, + TK_LANG_NAME_BEGIN, + TK_LANG_NAME_END, + TK_TRANS_NAME_BEGIN, + TK_TRANS_NAME_END, + TK_LOCALE_NAME_BEGIN, + TK_LOCALE_NAME_END, + TK_FLAG_FILE_BEGIN, + TK_FLAG_FILE_END, + TK_PLURAL_COUNT_BEGIN, + TK_PLURAL_COUNT_END, + TK_PLURAL_DEF_BEGIN, + TK_PLURAL_DEF_END, + + //item level + TK_SRC_BEGIN, + TK_SRC_END, + TK_TRG_BEGIN, + TK_TRG_END, + TK_TEXT, + TK_PLURAL_BEGIN, + TK_PLURAL_END, + TK_END + }; + + Token(Type t) : type(t) {} + Type type; + + std::string text; +}; + + +class KnownTokens +{ +public: + typedef std::map TokenMap; + + static const TokenMap& getList() + { + static KnownTokens inst; + return inst.tokens; + } + + static std::string text(Token::Type t) + { + auto it = getList().find(t); + return it != getList().end() ? it->second : std::string(); + } + +private: + KnownTokens() + { + //header information + tokens.insert(std::make_pair(Token::TK_HEADER_BEGIN, "
    ")); + tokens.insert(std::make_pair(Token::TK_HEADER_END, "
    ")); + tokens.insert(std::make_pair(Token::TK_LANG_NAME_BEGIN, "")); + tokens.insert(std::make_pair(Token::TK_LANG_NAME_END, "")); + tokens.insert(std::make_pair(Token::TK_TRANS_NAME_BEGIN, "")); + tokens.insert(std::make_pair(Token::TK_TRANS_NAME_END, "")); + tokens.insert(std::make_pair(Token::TK_LOCALE_NAME_BEGIN, "")); + tokens.insert(std::make_pair(Token::TK_LOCALE_NAME_END, "")); + tokens.insert(std::make_pair(Token::TK_FLAG_FILE_BEGIN, "")); + tokens.insert(std::make_pair(Token::TK_FLAG_FILE_END, "")); + tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_BEGIN, "")); + tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_END, "")); + tokens.insert(std::make_pair(Token::TK_PLURAL_DEF_BEGIN, "")); + tokens.insert(std::make_pair(Token::TK_PLURAL_DEF_END, "")); + + //item level + tokens.insert(std::make_pair(Token::TK_SRC_BEGIN, "")); + tokens.insert(std::make_pair(Token::TK_SRC_END, "")); + tokens.insert(std::make_pair(Token::TK_TRG_BEGIN, "")); + tokens.insert(std::make_pair(Token::TK_TRG_END, "")); + tokens.insert(std::make_pair(Token::TK_PLURAL_BEGIN, "")); + tokens.insert(std::make_pair(Token::TK_PLURAL_END, "")); + } + TokenMap tokens; +}; + + +class Scanner +{ +public: + Scanner(const std::string& fileStream) : stream(fileStream), pos(stream.begin()) + { + if (zen::startsWith(stream, zen::BYTE_ORDER_MARK_UTF8)) + pos += zen::strLength(zen::BYTE_ORDER_MARK_UTF8); + } + + Token nextToken() + { + //skip whitespace + pos = std::find_if(pos, stream.end(), [](char c) { return !zen::isWhiteSpace(c); }); + + if (pos == stream.end()) + return Token(Token::TK_END); + + for (const auto& token : KnownTokens::getList()) + if (startsWith(token.second)) + { + pos += token.second.size(); + return Token(token.first); + } + + //rest must be "text" + auto itBegin = pos; + while (pos != stream.end() && !startsWithKnownTag()) + pos = std::find(pos + 1, stream.end(), '<'); + + std::string text(itBegin, pos); + + normalize(text); //remove whitespace from end ect. + + if (text.empty() && pos == stream.end()) + return Token(Token::TK_END); + + Token out(Token::TK_TEXT); + out.text = text; + return out; + } + + size_t posRow() const //current row beginning with 0 + { + //count line endings + const size_t crSum = std::count(stream.begin(), pos, '\r'); //carriage returns + const size_t nlSum = std::count(stream.begin(), pos, '\n'); //new lines + assert(crSum == 0 || nlSum == 0 || crSum == nlSum); + return std::max(crSum, nlSum); //be compatible with Linux/Mac/Win + } + + size_t posCol() const //current col beginning with 0 + { + //seek beginning of line + for (auto it = pos; it != stream.begin(); ) + { + --it; + if (*it == '\r' || *it == '\n') + return pos - it - 1; + } + return pos - stream.begin(); + } + +private: + bool startsWithKnownTag() const + { + return std::any_of(KnownTokens::getList().begin(), KnownTokens::getList().end(), + [&](const KnownTokens::TokenMap::value_type& p) { return startsWith(p.second); }); + } + + bool startsWith(const std::string& prefix) const + { + if (stream.end() - pos < static_cast(prefix.size())) + return false; + return std::equal(prefix.begin(), prefix.end(), pos); + } + + static void normalize(std::string& text) + { + zen::trim(text); //remove whitespace from both ends + + //Delimiter: + //---------- + //Linux: 0xA \n + //Mac: 0xD \r + //Win: 0xD 0xA \r\n <- language files are in Windows format + zen::replace(text, "\r\n", '\n'); // + zen::replace(text, "\r", '\n'); //ensure c-style line breaks + } + + const std::string stream; + std::string::const_iterator pos; +}; + + +class LngParser +{ +public: + LngParser(const std::string& fileStream) : scn(fileStream), tk(scn.nextToken()) {} + + void parse(TranslationMap& out, TranslationPluralMap& pluralOut, TransHeader& header) + { + parseHeader(header); + + try + { + parse_plural::PluralFormInfo pi(header.pluralDefinition, header.pluralCount); + + //items + while (token().type != Token::TK_END) + parseRegular(out, pluralOut, pi); + } + catch (const parse_plural::InvalidPluralForm&) + { + throw ParsingError(L"Invalid plural form definition", scn.posRow(), scn.posCol()); + } + } + + void parseHeader(TransHeader& header) + { + consumeToken(Token::TK_HEADER_BEGIN); + + consumeToken(Token::TK_LANG_NAME_BEGIN); + header.languageName = tk.text; + consumeToken(Token::TK_TEXT); + consumeToken(Token::TK_LANG_NAME_END); + + consumeToken(Token::TK_TRANS_NAME_BEGIN); + header.translatorName = tk.text; + consumeToken(Token::TK_TEXT); + consumeToken(Token::TK_TRANS_NAME_END); + + consumeToken(Token::TK_LOCALE_NAME_BEGIN); + header.localeName = tk.text; + consumeToken(Token::TK_TEXT); + consumeToken(Token::TK_LOCALE_NAME_END); + + consumeToken(Token::TK_FLAG_FILE_BEGIN); + header.flagFile = tk.text; + consumeToken(Token::TK_TEXT); + consumeToken(Token::TK_FLAG_FILE_END); + + consumeToken(Token::TK_PLURAL_COUNT_BEGIN); + header.pluralCount = zen::stringTo(tk.text); + consumeToken(Token::TK_TEXT); + consumeToken(Token::TK_PLURAL_COUNT_END); + + consumeToken(Token::TK_PLURAL_DEF_BEGIN); + header.pluralDefinition = tk.text; + consumeToken(Token::TK_TEXT); + consumeToken(Token::TK_PLURAL_DEF_END); + + consumeToken(Token::TK_HEADER_END); + } + +private: + void parseRegular(TranslationMap& out, TranslationPluralMap& pluralOut, const parse_plural::PluralFormInfo& pluralInfo) + { + consumeToken(Token::TK_SRC_BEGIN); + + if (token().type == Token::TK_PLURAL_BEGIN) + return parsePlural(pluralOut, pluralInfo); + + if (token().type != Token::TK_TEXT) + throw ParsingError(L"Source text empty", scn.posRow(), scn.posCol()); + std::string original = tk.text; + nextToken(); + + consumeToken(Token::TK_SRC_END); + + consumeToken(Token::TK_TRG_BEGIN); + std::string translation; + if (token().type == Token::TK_TEXT) + { + translation = token().text; + nextToken(); + } + consumeToken(Token::TK_TRG_END); + + validateTranslation(original, translation); //throw throw ParsingError + out.insert(std::make_pair(original, translation)); + } + + void parsePlural(TranslationPluralMap& pluralOut, const parse_plural::PluralFormInfo& pluralInfo) + { + //Token::TK_SRC_BEGIN already consumed + + consumeToken(Token::TK_PLURAL_BEGIN); + std::string engSingular = tk.text; + consumeToken(Token::TK_TEXT); + consumeToken(Token::TK_PLURAL_END); + + consumeToken(Token::TK_PLURAL_BEGIN); + std::string engPlural = tk.text; + consumeToken(Token::TK_TEXT); + consumeToken(Token::TK_PLURAL_END); + + consumeToken(Token::TK_SRC_END); + + consumeToken(Token::TK_TRG_BEGIN); + + PluralForms pluralList; + while (token().type == Token::TK_PLURAL_BEGIN) + { + consumeToken(Token::TK_PLURAL_BEGIN); + std::string pluralForm = tk.text; + consumeToken(Token::TK_TEXT); + consumeToken(Token::TK_PLURAL_END); + pluralList.push_back(pluralForm); + } + + consumeToken(Token::TK_TRG_END); + + const SingularPluralPair original(engSingular, engPlural); + validateTranslation(original, pluralList, pluralInfo); + pluralOut.insert(std::make_pair(original, pluralList)); + } + + void validateTranslation(const std::string& original, const std::string& translation) //throw ParsingError + { + if (original.empty()) + throw ParsingError(L"Source translation is empty", scn.posRow(), scn.posCol()); + + if (!translation.empty()) + { + //if original contains placeholder, so should translation! + auto checkPlaceholder = [&](const std::string& placeholder) + { + if (zen::contains(original, placeholder) && + !zen::contains(translation, placeholder)) + throw ParsingError(zen::replaceCpy(L"Placeholder %x missing in translation", L"%x", zen::utfCvrtTo(placeholder)), scn.posRow(), scn.posCol()); + }; + checkPlaceholder("%x"); + checkPlaceholder("%y"); + checkPlaceholder("%z"); + + //if source contains ampersand to mark menu accellerator key, so must translation + if (hasSingleAmpersand(original) && !hasSingleAmpersand(translation)) + throw ParsingError(L"Translation is missing the & character to mark an access key for the menu item", scn.posRow(), scn.posCol()); + } + } + + void validateTranslation(const SingularPluralPair& original, const PluralForms& translation, const parse_plural::PluralFormInfo& pluralInfo) //throw ParsingError + { + using namespace zen; + //check the primary placeholder is existing at least for the second english text + if (!contains(original.second, "%x")) + throw ParsingError(L"Plural form source does not contain %x placeholder", scn.posRow(), scn.posCol()); + + if (!translation.empty()) + { + //check for invalid number of plural forms + if (pluralInfo.getCount() != static_cast(translation.size())) + throw ParsingError(replaceCpy(replaceCpy(L"Invalid number of plural forms; actual: %x, expected: %y", L"%x", numberTo(translation.size())), L"%y", numberTo(pluralInfo.getCount())), scn.posRow(), scn.posCol()); + + //check for duplicate plural form translations (catch copy & paste errors for single-number form translations) + for (auto it = translation.begin(); it != translation.end(); ++it) + if (!contains(*it, "%x")) + { + auto it2 = std::find(it + 1, translation.end(), *it); + if (it2 != translation.end()) + throw ParsingError(replaceCpy(L"Duplicate plural form translation at index position %x", L"%x", numberTo(it2 - translation.begin())), scn.posRow(), scn.posCol()); + } + + for (int pos = 0; pos < static_cast(translation.size()); ++pos) + if (pluralInfo.isSingleNumberForm(pos)) + { + //translation needs to use decimal number if english source does so (e.g. frequently changing text like statistics) + if (contains(original.first, "%x") || + contains(original.first, "1")) + { + const int firstNumber = pluralInfo.getFirstNumber(pos); + if (!(contains(translation[pos], "%x") || + contains(translation[pos], numberTo(firstNumber)))) + throw ParsingError(replaceCpy(replaceCpy(L"Plural form translation at index position %y needs to use the decimal number %z or the %x placeholder", + L"%y", numberTo(pos)), L"%z", numberTo(firstNumber)), scn.posRow(), scn.posCol()); + } + } + else + { + //ensure the placeholder is used when needed + if (!contains(translation[pos], "%x")) + throw ParsingError(replaceCpy(L"Plural form at index position %y is missing the %x placeholder", L"%y", numberTo(pos)), scn.posRow(), scn.posCol()); + } + + 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::utfCvrtTo(placeholder)), scn.posRow(), scn.posCol()); + + //secondary placeholder is required for all plural forms + if (!std::all_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::utfCvrtTo(placeholder)), scn.posRow(), scn.posCol()); + } + }; + + checkSecondaryPlaceholder("%y"); + checkSecondaryPlaceholder("%z"); + } + } + + static bool hasSingleAmpersand(const std::string& str) + { + size_t pos = 0; + for (;;) + { + pos = str.find('&', pos); + if (pos == std::string::npos) + return false; + + bool freeBefore = pos == 0 || str[pos - 1] != '&'; + bool freeAfter = pos >= str.size() - 1 || str[pos + 1] != '&'; //str.size() > 0 here! + + if (freeBefore && freeAfter) //make sure to not catch && which windows resolves as just one & for display! + return true; + ++pos; + } + } + + void nextToken() { tk = scn.nextToken(); } + const Token& token() const { return tk; } + + void consumeToken(Token::Type t) //throw ParsingError + { + expectToken(t); //throw ParsingError + nextToken(); + } + + void expectToken(Token::Type t) //throw ParsingError + { + if (token().type != t) + throw ParsingError(L"Unexpected token", scn.posRow(), scn.posCol()); + } + + Scanner scn; + Token tk; +}; + + +inline +void parseLng(const std::string& fileStream, TransHeader& header, TranslationMap& out, TranslationPluralMap& pluralOut) //throw ParsingError +{ + out.clear(); + pluralOut.clear(); + + LngParser(fileStream).parse(out, pluralOut, header); +} + + +inline +void parseHeader(const std::string& fileStream, TransHeader& header) //throw ParsingError +{ + LngParser(fileStream).parseHeader(header); +} + + +inline +void formatMultiLineText(std::string& text) +{ + assert(!zen::contains(text, "\r\n")); + + if (text.find('\n') != std::string::npos) //multiple lines + { + if (*text.begin() != '\n') + text = '\n' + text; + if (*text.rbegin() != '\n') + text += '\n'; + } +} + + +std::string generateLng(const TranslationUnorderedList& in, const TransHeader& header) +{ + std::string out; + //header + out += KnownTokens::text(Token::TK_HEADER_BEGIN) + '\n'; + + out += '\t' + KnownTokens::text(Token::TK_LANG_NAME_BEGIN); + out += header.languageName; + out += KnownTokens::text(Token::TK_LANG_NAME_END) + '\n'; + + out += '\t' + KnownTokens::text(Token::TK_TRANS_NAME_BEGIN); + out += header.translatorName; + out += KnownTokens::text(Token::TK_TRANS_NAME_END) + '\n'; + + out += '\t' + KnownTokens::text(Token::TK_LOCALE_NAME_BEGIN); + out += header.localeName; + out += KnownTokens::text(Token::TK_LOCALE_NAME_END) + '\n'; + + out += '\t' + KnownTokens::text(Token::TK_FLAG_FILE_BEGIN); + out += header.flagFile; + out += KnownTokens::text(Token::TK_FLAG_FILE_END) + '\n'; + + out += '\t' + KnownTokens::text(Token::TK_PLURAL_COUNT_BEGIN); + out += zen::numberTo(header.pluralCount); + out += KnownTokens::text(Token::TK_PLURAL_COUNT_END) + '\n'; + + out += '\t' + KnownTokens::text(Token::TK_PLURAL_DEF_BEGIN); + out += header.pluralDefinition; + out += KnownTokens::text(Token::TK_PLURAL_DEF_END) + '\n'; + + out += KnownTokens::text(Token::TK_HEADER_END) + '\n'; + + out += '\n'; + + + in.visitItems([&](const TranslationMap::value_type& trans) + { + std::string original = trans.first; + std::string translation = trans.second; + + formatMultiLineText(original); + formatMultiLineText(translation); + + out += KnownTokens::text(Token::TK_SRC_BEGIN); + out += original; + out += KnownTokens::text(Token::TK_SRC_END) + '\n'; + + out += KnownTokens::text(Token::TK_TRG_BEGIN); + out += translation; + out += KnownTokens::text(Token::TK_TRG_END) + '\n' + '\n'; + }, + [&](const TranslationPluralMap::value_type& transPlural) + { + std::string engSingular = transPlural.first.first; + std::string engPlural = transPlural.first.second; + const PluralForms& forms = transPlural.second; + + formatMultiLineText(engSingular); + formatMultiLineText(engPlural); + + out += KnownTokens::text(Token::TK_SRC_BEGIN) + '\n'; + out += KnownTokens::text(Token::TK_PLURAL_BEGIN); + out += engSingular; + out += KnownTokens::text(Token::TK_PLURAL_END) + '\n'; + out += KnownTokens::text(Token::TK_PLURAL_BEGIN); + out += engPlural; + out += KnownTokens::text(Token::TK_PLURAL_END) + '\n'; + out += KnownTokens::text(Token::TK_SRC_END) + '\n'; + + out += KnownTokens::text(Token::TK_TRG_BEGIN); + out += '\n'; + + for (std::string plForm : forms) + { + formatMultiLineText(plForm); + + out += KnownTokens::text(Token::TK_PLURAL_BEGIN); + out += plForm; + out += KnownTokens::text(Token::TK_PLURAL_END) + '\n'; + } + out += KnownTokens::text(Token::TK_TRG_END) + '\n' + '\n'; + }); + + assert(!zen::contains(out, "\r\n") && !zen::contains(out, "\r")); + return zen::replaceCpy(out, '\n', "\r\n"); //back to win line endings +} +} + +#endif //PARSE_LNG_HEADER_INCLUDED diff --git a/FreeFileSync/Source/lib/parse_plural.h b/FreeFileSync/Source/lib/parse_plural.h new file mode 100644 index 00000000..bac933c9 --- /dev/null +++ b/FreeFileSync/Source/lib/parse_plural.h @@ -0,0 +1,478 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef PARSE_PLURAL_H_INCLUDED +#define PARSE_PLURAL_H_INCLUDED + +#include +#include +#include +#include + +namespace parse_plural +{ +//expression interface +struct Expression { virtual ~Expression() {} }; + +template +struct Expr : public Expression +{ + typedef T ValueType; + virtual ValueType eval() const = 0; +}; + + +class ParsingError {}; + +class PluralForm +{ +public: + PluralForm(const std::string& stream); //throw ParsingError + int getForm(std::int64_t n) const { n_ = std::abs(n) ; return static_cast(expr->eval()); } + +private: + std::shared_ptr> expr; + mutable std::int64_t n_; +}; + + +//validate plural form +class InvalidPluralForm {}; + +class PluralFormInfo +{ +public: + PluralFormInfo(const std::string& definition, int pluralCount); //throw InvalidPluralForm + + int getCount() const { return static_cast(forms.size()); } + bool isSingleNumberForm(int index) const { return 0 <= index && index < static_cast(forms.size()) ? forms[index].count == 1 : false; } + int getFirstNumber (int index) const { return 0 <= index && index < static_cast(forms.size()) ? forms[index].firstNumber : -1; } + +private: + struct FormInfo + { + FormInfo() : count(0), firstNumber(0) {} + int count; + int firstNumber; //which maps to the plural form index position + }; + std::vector forms; +}; + + + + + +//--------------------------- implementation --------------------------- + +//http://www.gnu.org/software/hello/manual/gettext/Plural-forms.html +//http://translate.sourceforge.net/wiki/l10n/pluralforms +/* +Grammar for Plural forms parser +------------------------------- +expression: + conditional-expression + +conditional-expression: + logical-or-expression + logical-or-expression ? expression : expression + +logical-or-expression: + logical-and-expression + logical-or-expression || logical-and-expression + +logical-and-expression: + equality-expression + logical-and-expression && equality-expression + +equality-expression: + relational-expression + relational-expression == relational-expression + relational-expression != relational-expression + +relational-expression: + multiplicative-expression + multiplicative-expression > multiplicative-expression + multiplicative-expression < multiplicative-expression + multiplicative-expression >= multiplicative-expression + multiplicative-expression <= multiplicative-expression + +multiplicative-expression: + pm-expression + multiplicative-expression % pm-expression + +pm-expression: + variable-number-n-expression + constant-number-expression + ( expression ) + + +.po format,e.g.: (n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2) +*/ + +namespace implementation +{ +//specific binary expression based on STL function objects +template +struct BinaryExp : public Expr +{ + typedef std::shared_ptr> ExpLhs; + typedef std::shared_ptr> ExpRhs; + + BinaryExp(const ExpLhs& lhs, const ExpRhs& rhs, StlOp biop) : lhs_(lhs), rhs_(rhs), biop_(biop) { assert(lhs && rhs); } + virtual typename StlOp::result_type eval() const { return biop_(lhs_->eval(), rhs_->eval()); } +private: + ExpLhs lhs_; + ExpRhs rhs_; + StlOp biop_; +}; + +template inline +std::shared_ptr> makeBiExp(const std::shared_ptr& lhs, const std::shared_ptr& rhs, StlOp biop) //throw ParsingError +{ + auto exLeft = std::dynamic_pointer_cast>(lhs); + auto exRight = std::dynamic_pointer_cast>(rhs); + if (!exLeft || !exRight) + throw ParsingError(); + return std::make_shared>(exLeft, exRight, biop); +} + +template +struct ConditionalExp : public Expr +{ + ConditionalExp(const std::shared_ptr>& ifExp, + const std::shared_ptr>& thenExp, + const std::shared_ptr>& elseExp) : ifExp_(ifExp), thenExp_(thenExp), elseExp_(elseExp) { assert(ifExp && thenExp && elseExp); } + + virtual typename Expr::ValueType eval() const { return ifExp_->eval() ? thenExp_->eval() : elseExp_->eval(); } +private: + std::shared_ptr> ifExp_; + std::shared_ptr> thenExp_; + std::shared_ptr> elseExp_; +}; + +struct ConstNumberExp : public Expr +{ + ConstNumberExp(std::int64_t n) : n_(n) {} + virtual std::int64_t eval() const { return n_; } +private: + std::int64_t n_; +}; + +struct VariableNumberNExp : public Expr +{ + VariableNumberNExp(std::int64_t& n) : n_(n) {} + virtual std::int64_t eval() const { return n_; } +private: + std::int64_t& n_; +}; + +//------------------------------------------------------------------------------- + +struct Token +{ + enum Type + { + TK_TERNARY_QUEST, + TK_TERNARY_COLON, + TK_OR, + TK_AND, + TK_EQUAL, + TK_NOT_EQUAL, + TK_LESS, + TK_LESS_EQUAL, + TK_GREATER, + TK_GREATER_EQUAL, + TK_MODULUS, + TK_VARIABLE_N, + TK_CONST_NUMBER, + TK_BRACKET_LEFT, + TK_BRACKET_RIGHT, + TK_END + }; + + Token(Type t) : type(t), number(0) {} + Token(std::int64_t num) : type(TK_CONST_NUMBER), number(num) {} + + Type type; + std::int64_t number; //if type == TK_CONST_NUMBER +}; + +class Scanner +{ +public: + Scanner(const std::string& stream) : stream_(stream), pos(stream_.begin()) + { + tokens.push_back(std::make_pair("?" , Token::TK_TERNARY_QUEST)); + tokens.push_back(std::make_pair(":" , Token::TK_TERNARY_COLON)); + tokens.push_back(std::make_pair("||", Token::TK_OR )); + tokens.push_back(std::make_pair("&&", Token::TK_AND )); + tokens.push_back(std::make_pair("==", Token::TK_EQUAL )); + tokens.push_back(std::make_pair("!=", Token::TK_NOT_EQUAL )); + tokens.push_back(std::make_pair("<=", Token::TK_LESS_EQUAL )); + tokens.push_back(std::make_pair("<" , Token::TK_LESS )); + tokens.push_back(std::make_pair(">=", Token::TK_GREATER_EQUAL)); + tokens.push_back(std::make_pair(">" , Token::TK_GREATER )); + tokens.push_back(std::make_pair("%" , Token::TK_MODULUS )); + tokens.push_back(std::make_pair("n" , Token::TK_VARIABLE_N )); + tokens.push_back(std::make_pair("N" , Token::TK_VARIABLE_N )); + tokens.push_back(std::make_pair("(" , Token::TK_BRACKET_LEFT )); + tokens.push_back(std::make_pair(")" , Token::TK_BRACKET_RIGHT)); + } + + Token nextToken() + { + //skip whitespace + pos = std::find_if(pos, stream_.end(), [](char c) { return !zen::isWhiteSpace(c); }); + + if (pos == stream_.end()) + return Token::TK_END; + + for (auto iter = tokens.begin(); iter != tokens.end(); ++iter) + if (startsWith(iter->first)) + { + pos += iter->first.size(); + return Token(iter->second); + } + + auto digitEnd = std::find_if(pos, stream_.end(), [](char c) { return !zen::isDigit(c); }); + + if (digitEnd != pos) + { + auto number = zen::stringTo(std::string(pos, digitEnd)); + pos = digitEnd; + return number; + } + + throw ParsingError(); //unknown token + } + +private: + bool startsWith(const std::string& prefix) const + { + if (stream_.end() - pos < static_cast(prefix.size())) + return false; + return std::equal(prefix.begin(), prefix.end(), pos); + } + + typedef std::vector> TokenList; + TokenList tokens; + + const std::string stream_; + std::string::const_iterator pos; +}; + +//------------------------------------------------------------------------------- + +class Parser +{ +public: + Parser(const std::string& stream, std::int64_t& n) : + scn(stream), + tk(scn.nextToken()), + n_(n) {} + + std::shared_ptr> parse() //throw ParsingError; return value always bound! + { + auto e = std::dynamic_pointer_cast>(parseExpression()); //throw ParsingError + if (!e) + throw ParsingError(); + expectToken(Token::TK_END); + return e; + } + +private: + std::shared_ptr parseExpression() { return parseConditional(); }//throw ParsingError + + std::shared_ptr parseConditional() //throw ParsingError + { + std::shared_ptr e = parseLogicalOr(); + + if (token().type == Token::TK_TERNARY_QUEST) + { + nextToken(); + + auto ifExp = std::dynamic_pointer_cast>(e); + auto thenExp = std::dynamic_pointer_cast>(parseExpression()); //associativity: <- + + expectToken(Token::TK_TERNARY_COLON); + nextToken(); + + auto elseExp = std::dynamic_pointer_cast>(parseExpression()); // + if (!ifExp || !thenExp || !elseExp) + throw ParsingError(); + return std::make_shared>(ifExp, thenExp, elseExp); + } + return e; + } + + std::shared_ptr parseLogicalOr() + { + std::shared_ptr e = parseLogicalAnd(); + while (token().type == Token::TK_OR) //associativity: -> + { + nextToken(); + + std::shared_ptr rhs = parseLogicalAnd(); + e = makeBiExp(e, rhs, std::logical_or()); //throw ParsingError + } + return e; + } + + std::shared_ptr parseLogicalAnd() + { + std::shared_ptr e = parseEquality(); + while (token().type == Token::TK_AND) //associativity: -> + { + nextToken(); + std::shared_ptr rhs = parseEquality(); + + e = makeBiExp(e, rhs, std::logical_and()); //throw ParsingError + } + return e; + } + + std::shared_ptr parseEquality() + { + std::shared_ptr e = parseRelational(); + + Token::Type t = token().type; + if (t == Token::TK_EQUAL || //associativity: n/a + t == Token::TK_NOT_EQUAL) + { + nextToken(); + std::shared_ptr rhs = parseRelational(); + + if (t == Token::TK_EQUAL) return makeBiExp(e, rhs, std::equal_to ()); //throw ParsingError + if (t == Token::TK_NOT_EQUAL) return makeBiExp(e, rhs, std::not_equal_to()); // + } + return e; + } + + std::shared_ptr parseRelational() + { + std::shared_ptr e = parseMultiplicative(); + + Token::Type t = token().type; + if (t == Token::TK_LESS || //associativity: n/a + t == Token::TK_LESS_EQUAL || + t == Token::TK_GREATER || + t == Token::TK_GREATER_EQUAL) + { + nextToken(); + std::shared_ptr rhs = parseMultiplicative(); + + if (t == Token::TK_LESS) return makeBiExp(e, rhs, std::less ()); // + if (t == Token::TK_LESS_EQUAL) return makeBiExp(e, rhs, std::less_equal ()); //throw ParsingError + if (t == Token::TK_GREATER) return makeBiExp(e, rhs, std::greater ()); // + if (t == Token::TK_GREATER_EQUAL) return makeBiExp(e, rhs, std::greater_equal()); // + } + return e; + } + + std::shared_ptr parseMultiplicative() + { + std::shared_ptr e = parsePrimary(); + + while (token().type == Token::TK_MODULUS) //associativity: -> + { + nextToken(); + std::shared_ptr rhs = parsePrimary(); + + //"compile-time" check: n % 0 + if (auto literal = std::dynamic_pointer_cast(rhs)) + if (literal->eval() == 0) + throw ParsingError(); + + e = makeBiExp(e, rhs, std::modulus()); //throw ParsingError + } + return e; + } + + std::shared_ptr parsePrimary() + { + if (token().type == Token::TK_VARIABLE_N) + { + nextToken(); + return std::make_shared(n_); + } + else if (token().type == Token::TK_CONST_NUMBER) + { + const std::int64_t number = token().number; + nextToken(); + return std::make_shared(number); + } + else if (token().type == Token::TK_BRACKET_LEFT) + { + nextToken(); + std::shared_ptr e = parseExpression(); + + expectToken(Token::TK_BRACKET_RIGHT); + nextToken(); + return e; + } + else + throw ParsingError(); + } + + void nextToken() { tk = scn.nextToken(); } + const Token& token() const { return tk; } + + void expectToken(Token::Type t) //throw ParsingError + { + if (token().type != t) + throw ParsingError(); + } + + Scanner scn; + Token tk; + std::int64_t& n_; +}; +} + + +inline +PluralFormInfo::PluralFormInfo(const std::string& definition, int pluralCount) //throw InvalidPluralForm +{ + if (pluralCount < 1) + throw InvalidPluralForm(); + + forms.resize(pluralCount); + try + { + parse_plural::PluralForm pf(definition); //throw parse_plural::ParsingError + //PERF_START + + //perf: 80ns per iteration max (for arabic) + //=> 1000 iterations should be fast enough and still detect all "single number forms" + for (int j = 0; j < 1000; ++j) + { + int form = pf.getForm(j); + if (0 <= form && form < static_cast(forms.size())) + { + if (forms[form].count == 0) + forms[form].firstNumber = j; + ++forms[form].count; + } + else + throw InvalidPluralForm(); + } + } + catch (const parse_plural::ParsingError&) + { + throw InvalidPluralForm(); + } + + //ensure each form is used at least once: + if (!std::all_of(forms.begin(), forms.end(), [](const FormInfo& fi) { return fi.count >= 1; })) + throw InvalidPluralForm(); +} + + +inline +PluralForm::PluralForm(const std::string& stream) : expr(implementation::Parser(stream, n_).parse()) {} //throw ParsingError +} + +#endif // PARSE_PLURAL_H_INCLUDED diff --git a/FreeFileSync/Source/lib/perf_check.cpp b/FreeFileSync/Source/lib/perf_check.cpp new file mode 100644 index 00000000..e016bc6c --- /dev/null +++ b/FreeFileSync/Source/lib/perf_check.cpp @@ -0,0 +1,261 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "perf_check.h" + +#include +//#include +#include +#include +#include + +using namespace zen; + + +PerfCheck::PerfCheck(unsigned int windowSizeRemainingTime, + unsigned int windowSizeSpeed) : + windowSizeRemTime(windowSizeRemainingTime), + windowSizeSpeed_(windowSizeSpeed), + windowMax(std::max(windowSizeRemainingTime, windowSizeSpeed)) {} + + +PerfCheck::~PerfCheck() +{ + /* + //write samples to a file + wxFFile outputFile(wxT("statistics.dat"), wxT("w")); + + outputFile.Write(wxT("Time(ms);Objects;Data\n")); + + for (auto it = samples.begin(); it != samples.end(); ++it) + { + outputFile.Write(numberTo(it->first)); + outputFile.Write(wxT(";")); + outputFile.Write(numberTo(it->second.objCount_)); + outputFile.Write(wxT(";")); + outputFile.Write(numberTo(it->second.data_)); + outputFile.Write(wxT("\n")); + } + */ +} + + +void PerfCheck::addSample(int itemsCurrent, double dataCurrent, int64_t timeMs) +{ + samples.insert(samples.end(), std::make_pair(timeMs, Record(itemsCurrent, dataCurrent))); //use fact that time is monotonously ascending + + //remove all records earlier than "now - windowMax" + auto it = samples.upper_bound(timeMs - windowMax); + if (it != samples.begin()) + samples.erase(samples.begin(), --it); //keep one point before newBegin in order to handle "measurement holes" +} + + +inline +std::pair::value_type*, const std::multimap::value_type*> PerfCheck::getBlockFromEnd(int64_t windowSize) const +{ + if (!samples.empty()) + { + auto itBack = samples.rbegin(); + //find start of records "window" + auto itFront = samples.upper_bound(itBack->first - windowSize); + if (itFront != samples.begin()) + --itFront; //one point before window begin in order to handle "measurement holes" + return std::make_pair(&*itFront, &*itBack); + } + return std::make_pair(nullptr, nullptr); +} + + +zen::Opt PerfCheck::getRemainingTime(double dataRemaining) const +{ + auto blk = getBlockFromEnd(windowSizeRemTime); + if (blk.first && blk.second) + { + const auto& itemFront = *blk.first; + const auto& itemBack = *blk.second; + //----------------------------------------------------------------------------------------------- + const int64_t timeDelta = itemBack.first - itemFront.first; + const double dataDelta = itemBack.second.data_ - itemFront.second.data_; + + //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(dataDelta)) //sign(dataRemaining) != sign(dataDelta) usually an error, so show it! + return remainingTimeToString(dataRemaining * timeDelta / (1000.0 * dataDelta)); + } + return NoValue(); +} + + +zen::Opt PerfCheck::getBytesPerSecond() const +{ + auto blk = getBlockFromEnd(windowSizeSpeed_); + if (blk.first && blk.second) + { + const auto& itemFront = *blk.first; + const auto& itemBack = *blk.second; + //----------------------------------------------------------------------------------------------- + const int64_t timeDelta = itemBack.first - itemFront.first; + const double dataDelta = itemBack.second.data_ - itemFront.second.data_; + + if (timeDelta != 0/* && dataDelta > 0*/) + return filesizeToShortString(Int64(dataDelta * 1000.0 / timeDelta)) + _("/sec"); + } + return NoValue(); +} + + +zen::Opt PerfCheck::getItemsPerSecond() const +{ + auto blk = getBlockFromEnd(windowSizeSpeed_); + if (blk.first && blk.second) + { + const auto& itemFront = *blk.first; + const auto& itemBack = *blk.second; + //----------------------------------------------------------------------------------------------- + const int64_t timeDelta = itemBack.first - itemFront.first; + const int itemsDelta = itemBack.second.itemCount_ - itemFront.second.itemCount_; + + if (timeDelta != 0) + return replaceCpy(_("%x items/sec"), L"%x", formatThreeDigitPrecision(itemsDelta * 1000.0 / timeDelta)); + } + return NoValue(); +} + + +/* +class for calculation of remaining time: +---------------------------------------- +"filesize |-> time" is an affine linear function f(x) = z_1 + z_2 x + +For given n measurements, sizes x_0, ..., x_n and times f_0, ..., f_n, the function f (as a polynom of degree 1) can be lineary approximated by + +z_1 = (r - s * q / p) / ((n + 1) - s * s / p) +z_2 = (q - s * z_1) / p = (r - (n + 1) z_1) / s + +with +p := x_0^2 + ... + x_n^2 +q := f_0 x_0 + ... + f_n x_n +r := f_0 + ... + f_n +s := x_0 + ... + x_n + +=> the time to process N files with amount of data D is: N * z_1 + D * z_2 + +Problem: +-------- +Times f_0, ..., f_n can be very small so that precision of the PC clock is poor. +=> Times have to be accumulated to enhance precision: +Copying of m files with sizes x_i and times f_i (i = 1, ..., m) takes sum_i f(x_i) := m * z_1 + z_2 * sum x_i = sum f_i +With X defined as the accumulated sizes and F the accumulated times this gives: (in theory...) +m * z_1 + z_2 * X = F <=> +z_1 + z_2 * X / m = F / m + +=> we obtain a new (artificial) measurement with size X / m and time F / m to be used in the linear approximation above + + +Statistics::Statistics(int totalObjectCount, double totalDataAmount, unsigned recordCount) : + objectsTotal(totalObjectCount), + dataTotal(totalDataAmount), + recordsMax(recordCount), + objectsLast(0), + dataLast(0), + timeLast(wxGetLocalTimeMillis()), + z1_current(0), + z2_current(0), + dummyRecordPresent(false) {} + + +wxString Statistics::getRemainingTime(int objectsCurrent, double dataCurrent) +{ + //add new measurement point + const int m = objectsCurrent - objectsLast; + if (m != 0) + { + objectsLast = objectsCurrent; + + const double X = dataCurrent - dataLast; + dataLast = dataCurrent; + + const zen::Int64 timeCurrent = wxGetLocalTimeMillis(); + const double F = (timeCurrent - timeLast).ToDouble(); + timeLast = timeCurrent; + + record newEntry; + newEntry.x_i = X / m; + newEntry.f_i = F / m; + + //remove dummy record + if (dummyRecordPresent) + { + measurements.pop_back(); + dummyRecordPresent = false; + } + + //insert new record + measurements.push_back(newEntry); + if (measurements.size() > recordsMax) + measurements.pop_front(); + } + else //dataCurrent increased without processing new objects: + { //modify last measurement until m != 0 + const double X = dataCurrent - dataLast; //do not set dataLast, timeLast variables here, but write dummy record instead + if (!isNull(X)) + { + const zen::Int64 timeCurrent = wxGetLocalTimeMillis(); + const double F = (timeCurrent - timeLast).ToDouble(); + + record modifyEntry; + modifyEntry.x_i = X; + modifyEntry.f_i = F; + + //insert dummy record + if (!dummyRecordPresent) + { + measurements.push_back(modifyEntry); + if (measurements.size() > recordsMax) + measurements.pop_front(); + dummyRecordPresent = true; + } + else //modify dummy record + measurements.back() = modifyEntry; + } + } + + //calculate remaining time based on stored measurement points + double p = 0; + double q = 0; + double r = 0; + double s = 0; + for (const record& rec : measurements) + { + const double x_i = rec.x_i; + const double f_i = rec.f_i; + p += x_i * x_i; + q += f_i * x_i; + r += f_i; + s += x_i; + } + + if (!isNull(p)) + { + const double n = measurements.size(); + const double tmp = (n - s * s / p); + + if (!isNull(tmp) && !isNull(s)) + { + const double z1 = (r - s * q / p) / tmp; + const double z2 = (r - n * z1) / s; //not (n + 1) here, since n already is the number of measurements + + //refresh current values for z1, z2 + z1_current = z1; + z2_current = z2; + } + } + + return formatRemainingTime((objectsTotal - objectsCurrent) * z1_current + (dataTotal - dataCurrent) * z2_current); +} +*/ diff --git a/FreeFileSync/Source/lib/perf_check.h b/FreeFileSync/Source/lib/perf_check.h new file mode 100644 index 00000000..894ba31d --- /dev/null +++ b/FreeFileSync/Source/lib/perf_check.h @@ -0,0 +1,46 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef STATISTICS_H_INCLUDED +#define STATISTICS_H_INCLUDED + +#include +#include +#include +#include + +class PerfCheck +{ +public: + PerfCheck(unsigned int windowSizeRemainingTime, //unit: [ms] + unsigned int windowSizeSpeed); // + ~PerfCheck(); + + void addSample(int itemsCurrent, double dataCurrent, int64_t timeMs); //timeMs must be ascending! + + zen::Opt getRemainingTime(double dataRemaining) const; + zen::Opt getBytesPerSecond() const; //for window + zen::Opt getItemsPerSecond() const; //for window + +private: + struct Record + { + Record(int itemCount, double data) : itemCount_(itemCount), data_(data) {} + int itemCount_; + double data_; //unit: [bytes] + }; + + std::pair::value_type*, + const std::multimap::value_type*> getBlockFromEnd(int64_t windowSize) const; + + const int64_t windowSizeRemTime; //unit: [ms] + const int64_t windowSizeSpeed_; // + const int64_t windowMax; + + std::map samples; //time, unit: [ms] +}; + +#endif // STATISTICS_H_INCLUDED diff --git a/FreeFileSync/Source/lib/process_xml.cpp b/FreeFileSync/Source/lib/process_xml.cpp new file mode 100644 index 00000000..3bf4d81b --- /dev/null +++ b/FreeFileSync/Source/lib/process_xml.cpp @@ -0,0 +1,1575 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "process_xml.h" +#include +#include +#include "ffs_paths.h" +#include +#include +//#include +#include "xml_base.h" + +using namespace zen; +using namespace xmlAccess; //functionally needed for correct overload resolution!!! +using namespace std::rel_ops; + +namespace +{ +//------------------------------------------------------------------------------------------------------------------------------- +const int XML_FORMAT_VER_GLOBAL = 1; +const int XML_FORMAT_VER_FFS_GUI = 3; //for FFS 5.22 +const int XML_FORMAT_VER_FFS_BATCH = 3; // +//------------------------------------------------------------------------------------------------------------------------------- +} + +XmlType getXmlTypeNoThrow(const zen::XmlDoc& doc) //throw() +{ + if (doc.root().getNameAs() == "FreeFileSync") + { + std::string type; + if (doc.root().getAttribute("XmlType", type)) + { + if (type == "GUI") + return XML_TYPE_GUI; + else if (type == "BATCH") + return XML_TYPE_BATCH; + else if (type == "GLOBAL") + return XML_TYPE_GLOBAL; + } + } + return XML_TYPE_OTHER; +} + + +XmlType xmlAccess::getXmlType(const Zstring& filename) //throw FfsXmlError +{ + //do NOT use zen::loadStream as it will needlessly load even huge files! + XmlDoc doc = loadXmlDocument(filename); //throw FfsXmlError, quick exit if file is not an FFS XML + return ::getXmlTypeNoThrow(doc); +} + + +void setXmlType(XmlDoc& doc, XmlType type) //throw() +{ + switch (type) + { + case XML_TYPE_GUI: + doc.root().setAttribute("XmlType", "GUI"); + break; + case XML_TYPE_BATCH: + doc.root().setAttribute("XmlType", "BATCH"); + break; + case XML_TYPE_GLOBAL: + doc.root().setAttribute("XmlType", "GLOBAL"); + break; + case XML_TYPE_OTHER: + assert(false); + break; + } +} + +//################################################################################################################ + +Zstring xmlAccess::getGlobalConfigFile() +{ + return zen::getConfigDir() + Zstr("GlobalSettings.xml"); +} + + +void xmlAccess::OptionalDialogs::resetDialogs() +{ + warningDependentFolders = true; + warningFolderPairRaceCondition = true; + warningSignificantDifference = true; + warningNotEnoughDiskSpace = true; + warningUnresolvedConflicts = true; + warningDatabaseError = true; + warningRecyclerMissing = true; + warningInputFieldEmpty = true; + warningDirectoryLockFailed = true; + popupOnConfigChange = true; + confirmSyncStart = true; + confirmExternalCommandMassInvoke = true; +} + + +xmlAccess::XmlGuiConfig xmlAccess::convertBatchToGui(const xmlAccess::XmlBatchConfig& batchCfg) //noexcept +{ + XmlGuiConfig output; + output.mainCfg = batchCfg.mainCfg; + + switch (batchCfg.handleError) + { + case ON_ERROR_POPUP: + case ON_ERROR_STOP: + output.handleError = ON_GUIERROR_POPUP; + break; + case ON_ERROR_IGNORE: + output.handleError = ON_GUIERROR_IGNORE; + break; + } + return output; +} + + +xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatch(const xmlAccess::XmlGuiConfig& guiCfg) //noexcept +{ + XmlBatchConfig output; //use default batch-settings + output.mainCfg = guiCfg.mainCfg; + + switch (guiCfg.handleError) + { + case ON_GUIERROR_POPUP: + output.handleError = ON_ERROR_POPUP; + break; + case ON_GUIERROR_IGNORE: + output.handleError = ON_ERROR_IGNORE; + break; + } + return output; +} + + +xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatchPreservingExistingBatch(const xmlAccess::XmlGuiConfig& guiCfg, const Zstring& referenceBatchFile) //noexcept +{ + //try to take over batch-specific settings from reference file if possible + if (!referenceBatchFile.empty()) + try + { + XmlBatchConfig batchCfg; + readConfig(referenceBatchFile, batchCfg); //throw FfsXmlError + + batchCfg.mainCfg = guiCfg.mainCfg; + return batchCfg; + } + catch (xmlAccess::FfsXmlError&) {} + warn_static("not good enough") + + return convertGuiToBatch(guiCfg); +} + + +namespace +{ +std::vector splitFilterByLines(const Zstring& filterPhrase) +{ + if (filterPhrase.empty()) + return std::vector(); + return split(filterPhrase, Zstr('\n')); +} + +Zstring mergeFilterLines(const std::vector& filterLines) +{ + if (filterLines.empty()) + return Zstring(); + Zstring out = filterLines[0]; + std::for_each(filterLines.begin() + 1, filterLines.end(), [&](const Zstring& line) { out += Zstr('\n'); out += line; }); + return out; +} +} + + +namespace zen +{ +template <> inline +void writeText(const CompareVariant& value, std::string& output) +{ + switch (value) + { + case zen::CMP_BY_TIME_SIZE: + output = "TimeAndSize"; + break; + case zen::CMP_BY_CONTENT: + output = "Content"; + break; + } +} + +template <> inline +bool readText(const std::string& input, CompareVariant& value) +{ + std::string tmp = input; + zen::trim(tmp); + warn_static("remove after migration. 2013.08.20") + if (tmp == "ByTimeAndSize") //obsolete + value = zen::CMP_BY_TIME_SIZE; + else if (tmp == "ByContent") //obsolete + value = zen::CMP_BY_CONTENT; + else + + if (tmp == "TimeAndSize") + value = zen::CMP_BY_TIME_SIZE; + else if (tmp == "Content") + value = zen::CMP_BY_CONTENT; + else + return false; + return true; +} + + +template <> inline +void writeText(const SyncDirection& value, std::string& output) +{ + switch (value) + { + case SyncDirection::LEFT: + output = "left"; + break; + case SyncDirection::RIGHT: + output = "right"; + break; + case SyncDirection::NONE: + output = "none"; + break; + } +} + +template <> inline +bool readText(const std::string& input, SyncDirection& value) +{ + std::string tmp = input; + zen::trim(tmp); + if (tmp == "left") + value = SyncDirection::LEFT; + else if (tmp == "right") + value = SyncDirection::RIGHT; + else if (tmp == "none") + value = SyncDirection::NONE; + else + return false; + return true; +} + + +template <> inline +void writeText(const OnError& value, std::string& output) +{ + switch (value) + { + case ON_ERROR_IGNORE: + output = "Ignore"; + break; + case ON_ERROR_POPUP: + output = "Popup"; + break; + case ON_ERROR_STOP: + output = "Stop"; + break; + } +} + +template <> inline +bool readText(const std::string& input, OnError& value) +{ + std::string tmp = input; + zen::trim(tmp); + warn_static("remove after migration. 2013.08.20") + if (tmp == "Exit") //obsolete + value = ON_ERROR_STOP; + else if (tmp == "Abort") //obsolete: 2013.09.04 + value = ON_ERROR_STOP; + else + + if (tmp == "Ignore") + value = ON_ERROR_IGNORE; + else if (tmp == "Popup") + value = ON_ERROR_POPUP; + else if (tmp == "Stop") + value = ON_ERROR_STOP; + else + return false; + return true; +} + + +template <> inline +void writeText(const OnGuiError& value, std::string& output) +{ + switch (value) + { + case ON_GUIERROR_IGNORE: + output = "Ignore"; + break; + case ON_GUIERROR_POPUP: + output = "Popup"; + break; + } +} + +template <> inline +bool readText(const std::string& input, OnGuiError& value) +{ + std::string tmp = input; + zen::trim(tmp); + if (tmp == "Ignore") + value = ON_GUIERROR_IGNORE; + else if (tmp == "Popup") + value = ON_GUIERROR_POPUP; + else + return false; + return true; +} + + +template <> inline +void writeText(const FileIconSize& value, std::string& output) +{ + switch (value) + { + case ICON_SIZE_SMALL: + output = "Small"; + break; + case ICON_SIZE_MEDIUM: + output = "Medium"; + break; + case ICON_SIZE_LARGE: + output = "Large"; + break; + } +} + + +template <> inline +bool readText(const std::string& input, FileIconSize& value) +{ + std::string tmp = input; + zen::trim(tmp); + if (tmp == "Small") + value = ICON_SIZE_SMALL; + else if (tmp == "Medium") + value = ICON_SIZE_MEDIUM; + else if (tmp == "Large") + value = ICON_SIZE_LARGE; + else + return false; + return true; +} + + +template <> inline +void writeText(const DeletionPolicy& value, std::string& output) +{ + switch (value) + { + case DELETE_PERMANENTLY: + output = "Permanent"; + break; + case DELETE_TO_RECYCLER: + output = "RecycleBin"; + break; + case DELETE_TO_VERSIONING: + output = "Versioning"; + break; + } +} + +template <> inline +bool readText(const std::string& input, DeletionPolicy& value) +{ + std::string tmp = input; + zen::trim(tmp); + //------------------ + warn_static("remove after migration?") + if (tmp == "DeletePermanently")//obsolete name + value = DELETE_PERMANENTLY; + else if (tmp == "MoveToRecycleBin")//obsolete name + value = DELETE_TO_RECYCLER; + else if (tmp == "MoveToCustomDirectory")//obsolete name + value = DELETE_TO_VERSIONING; + else + + if (tmp == "Permanent") + value = DELETE_PERMANENTLY; + else if (tmp == "RecycleBin") + value = DELETE_TO_RECYCLER; + else if (tmp == "Versioning") + value = DELETE_TO_VERSIONING; + else + return false; + return true; +} + + +template <> inline +void writeText(const SymLinkHandling& value, std::string& output) +{ + switch (value) + { + case SYMLINK_EXCLUDE: + output = "Exclude"; + break; + case SYMLINK_USE_DIRECTLY: + output = "Direct"; + break; + case SYMLINK_FOLLOW_LINK: + output = "Follow"; + break; + } +} + +template <> inline +bool readText(const std::string& input, SymLinkHandling& value) +{ + std::string tmp = input; + zen::trim(tmp); + warn_static("remove after migration. 2013.08.20") + if (tmp == "UseDirectly") //obsolete! + value = SYMLINK_USE_DIRECTLY; + else if (tmp == "FollowLink") //obsolete! + value = SYMLINK_FOLLOW_LINK; + else if (tmp == "Ignore") //obsolete! + value = SYMLINK_EXCLUDE; + else + + if (tmp == "Exclude") + value = SYMLINK_EXCLUDE; + else if (tmp == "Direct") + value = SYMLINK_USE_DIRECTLY; + else if (tmp == "Follow") + value = SYMLINK_FOLLOW_LINK; + else + return false; + return true; +} + + +template <> inline +void writeText(const ColumnTypeRim& value, std::string& output) +{ + switch (value) + { + case COL_TYPE_DIRECTORY: + output = "Base"; + break; + case COL_TYPE_FULL_PATH: + output = "Full"; + break; + case COL_TYPE_REL_PATH: + output = "Rel"; + break; + case COL_TYPE_FILENAME: + output = "Name"; + break; + case COL_TYPE_SIZE: + output = "Size"; + break; + case COL_TYPE_DATE: + output = "Date"; + break; + case COL_TYPE_EXTENSION: + output = "Ext"; + break; + } +} + +template <> inline +bool readText(const std::string& input, ColumnTypeRim& value) +{ + std::string tmp = input; + zen::trim(tmp); + if (tmp == "Base") + value = COL_TYPE_DIRECTORY; + else if (tmp == "Full") + value = COL_TYPE_FULL_PATH; + else if (tmp == "Rel") + value = COL_TYPE_REL_PATH; + else if (tmp == "Name") + value = COL_TYPE_FILENAME; + else if (tmp == "Size") + value = COL_TYPE_SIZE; + else if (tmp == "Date") + value = COL_TYPE_DATE; + else if (tmp == "Ext") + value = COL_TYPE_EXTENSION; + else + return false; + return true; +} + + +template <> inline +void writeText(const ColumnTypeNavi& value, std::string& output) +{ + switch (value) + { + case COL_TYPE_NAVI_BYTES: + output = "Bytes"; + break; + case COL_TYPE_NAVI_DIRECTORY: + output = "Tree"; + break; + case COL_TYPE_NAVI_ITEM_COUNT: + output = "Count"; + break; + } +} + +template <> inline +bool readText(const std::string& input, ColumnTypeNavi& value) +{ + std::string tmp = input; + zen::trim(tmp); + if (tmp == "Bytes") + value = COL_TYPE_NAVI_BYTES; + else if (tmp == "Tree") + value = COL_TYPE_NAVI_DIRECTORY; + else if (tmp == "Count") + value = COL_TYPE_NAVI_ITEM_COUNT; + else + return false; + return true; +} + + +template <> inline +void writeText(const UnitSize& value, std::string& output) +{ + switch (value) + { + case USIZE_NONE: + output = "None"; + break; + case USIZE_BYTE: + output = "Byte"; + break; + case USIZE_KB: + output = "KB"; + break; + case USIZE_MB: + output = "MB"; + break; + } +} + +template <> inline +bool readText(const std::string& input, UnitSize& value) +{ + std::string tmp = input; + zen::trim(tmp); + warn_static("remove after migration. 2013.08.20") + if (tmp == "Inactive") //obsolete! + value = USIZE_NONE; + else + + if (tmp == "None") + value = USIZE_NONE; + else if (tmp == "Byte") + value = USIZE_BYTE; + else if (tmp == "KB") + value = USIZE_KB; + else if (tmp == "MB") + value = USIZE_MB; + else + return false; + return true; +} + +template <> inline +void writeText(const UnitTime& value, std::string& output) +{ + switch (value) + { + case UTIME_NONE: + output = "None"; + break; + case UTIME_TODAY: + output = "Today"; + break; + case UTIME_THIS_MONTH: + output = "Month"; + break; + case UTIME_THIS_YEAR: + output = "Year"; + break; + case UTIME_LAST_X_DAYS: + output = "x-days"; + break; + } +} + +template <> inline +bool readText(const std::string& input, UnitTime& value) +{ + std::string tmp = input; + zen::trim(tmp); + warn_static("remove after migration. 2013.08.20") + if (tmp == "Inactive") //obsolete! + value = UTIME_NONE; + else + + if (tmp == "None") + value = UTIME_NONE; + else if (tmp == "Today") + value = UTIME_TODAY; + else if (tmp == "Month") + value = UTIME_THIS_MONTH; + else if (tmp == "Year") + value = UTIME_THIS_YEAR; + else if (tmp == "x-days") + value = UTIME_LAST_X_DAYS; + else + return false; + return true; +} + +template <> inline +void writeText(const VersioningStyle& value, std::string& output) +{ + switch (value) + { + case VER_STYLE_REPLACE: + output = "Replace"; + break; + case VER_STYLE_ADD_TIMESTAMP: + output = "TimeStamp"; + break; + } +} + +template <> inline +bool readText(const std::string& input, VersioningStyle& value) +{ + std::string tmp = input; + zen::trim(tmp); + warn_static("remove after migration. 2013.08.20") + if (tmp == "AddTimeStamp") //obsolete + value = VER_STYLE_ADD_TIMESTAMP; + else + + if (tmp == "Replace") + value = VER_STYLE_REPLACE; + else if (tmp == "TimeStamp") + value = VER_STYLE_ADD_TIMESTAMP; + else + return false; + return true; +} + + +template <> inline +void writeText(const DirectionConfig::Variant& value, std::string& output) +{ + switch (value) + { + case DirectionConfig::TWOWAY: + output = "TwoWay"; + break; + case DirectionConfig::MIRROR: + output = "Mirror"; + break; + case DirectionConfig::UPDATE: + output = "Update"; + break; + case DirectionConfig::CUSTOM: + output = "Custom"; + break; + } +} + +template <> inline +bool readText(const std::string& input, DirectionConfig::Variant& value) +{ + std::string tmp = input; + zen::trim(tmp); + warn_static("remove after migration. 2013.08.20") + if (tmp == "Automatic") //obsolete! + value = DirectionConfig::TWOWAY; + else + + if (tmp == "TwoWay") + value = DirectionConfig::TWOWAY; + else if (tmp == "Mirror") + value = DirectionConfig::MIRROR; + else if (tmp == "Update") + value = DirectionConfig::UPDATE; + else if (tmp == "Custom") + value = DirectionConfig::CUSTOM; + else + return false; + return true; +} + + +template <> inline +bool readStruc(const XmlElement& input, ColumnAttributeRim& value) +{ + XmlIn in(input); + bool rv1 = in.attribute("Type", value.type_); + bool rv2 = in.attribute("Visible", value.visible_); + bool rv3 = in.attribute("Width", value.offset_); //offset == width if stretch is 0 + bool rv4 = in.attribute("Stretch", value.stretch_); + return rv1 && rv2 && rv3 && rv4; +} + +template <> inline +void writeStruc(const ColumnAttributeRim& value, XmlElement& output) +{ + XmlOut out(output); + out.attribute("Type", value.type_); + out.attribute("Visible", value.visible_); + out.attribute("Width", value.offset_); + out.attribute("Stretch", value.stretch_); +} + + +template <> inline +bool readStruc(const XmlElement& input, ColumnAttributeNavi& value) +{ + XmlIn in(input); + bool rv1 = in.attribute("Type", value.type_); + bool rv2 = in.attribute("Visible", value.visible_); + bool rv3 = in.attribute("Width", value.offset_); //offset == width if stretch is 0 + bool rv4 = in.attribute("Stretch", value.stretch_); + return rv1 && rv2 && rv3 && rv4; +} + +template <> inline +void writeStruc(const ColumnAttributeNavi& value, XmlElement& output) +{ + XmlOut out(output); + out.attribute("Type", value.type_); + out.attribute("Visible", value.visible_); + out.attribute("Width", value.offset_); + out.attribute("Stretch", value.stretch_); +} + + +template <> inline +bool readStruc(const XmlElement& input, ViewFilterDefault& value) +{ + XmlIn in(input); + + bool success = true; + auto readAttr = [&](XmlIn& elemIn, const char name[], bool& v) + { + if (!elemIn.attribute(name, v)) + success = false; + }; + + XmlIn catView = in["CategoryView"]; + readAttr(catView, "LeftOnly" , value.leftOnly); + readAttr(catView, "RightOnly" , value.rightOnly); + readAttr(catView, "LeftNewer" , value.leftNewer); + readAttr(catView, "RightNewer", value.rightNewer); + readAttr(catView, "Different" , value.different); + readAttr(catView, "Equal" , value.equal); + readAttr(catView, "Conflict" , value.conflict); + + XmlIn actView = in["ActionView"]; + readAttr(actView, "CreateLeft" , value.createLeft); + readAttr(actView, "CreateRight", value.createRight); + readAttr(actView, "UpdateLeft" , value.updateLeft); + readAttr(actView, "UpdateRight", value.updateRight); + readAttr(actView, "DeleteLeft" , value.deleteLeft); + readAttr(actView, "DeleteRight", value.deleteRight); + readAttr(actView, "DoNothing" , value.doNothing); + + return success; //[!] avoid short-circuit evaluation above +} + +template <> inline +void writeStruc(const ViewFilterDefault& value, XmlElement& output) +{ + XmlOut out(output); + + XmlOut catView = out["CategoryView"]; + catView.attribute("LeftOnly" , value.leftOnly); + catView.attribute("RightOnly" , value.rightOnly); + catView.attribute("LeftNewer" , value.leftNewer); + catView.attribute("RightNewer", value.rightNewer); + catView.attribute("Different" , value.different); + catView.attribute("Equal" , value.equal); + catView.attribute("Conflict" , value.conflict); + + XmlOut actView = out["ActionView"]; + actView.attribute("CreateLeft" , value.createLeft); + actView.attribute("CreateRight", value.createRight); + actView.attribute("UpdateLeft" , value.updateLeft); + actView.attribute("UpdateRight", value.updateRight); + actView.attribute("DeleteLeft" , value.deleteLeft); + actView.attribute("DeleteRight", value.deleteRight); + actView.attribute("DoNothing" , value.doNothing); +} + + +template <> inline +bool readStruc(const XmlElement& input, ConfigHistoryItem& value) +{ + XmlIn in(input); + bool rv1 = in(value.configFile); + //bool rv2 = in.attribute("LastUsed", value.lastUseTime); + return rv1 /*&& rv2*/; +} + + +template <> inline +void writeStruc(const ConfigHistoryItem& value, XmlElement& output) +{ + XmlOut out(output); + out(value.configFile); + //out.attribute("LastUsed", value.lastUseTime); +} +} + + +namespace +{ +void readConfig(const XmlIn& in, CompConfig& cmpConfig) +{ + in["Variant" ](cmpConfig.compareVar); + + warn_static("remove check after migration. 2013.09.7") + if (in["HandleSymlinks"])//obsolete name + in["HandleSymlinks"](cmpConfig.handleSymlinks); + else + in["Symlinks"](cmpConfig.handleSymlinks); +} + + +void readConfig(const XmlIn& in, DirectionConfig& directCfg) +{ + in["Variant"](directCfg.var); + + XmlIn inCustDir = in["CustomDirections"]; + inCustDir["LeftOnly" ](directCfg.custom.exLeftSideOnly); + inCustDir["RightOnly" ](directCfg.custom.exRightSideOnly); + inCustDir["LeftNewer" ](directCfg.custom.leftNewer); + inCustDir["RightNewer"](directCfg.custom.rightNewer); + inCustDir["Different" ](directCfg.custom.different); + inCustDir["Conflict" ](directCfg.custom.conflict); + + warn_static("remove check after migration. 2013.08.17") + if (in["DetectMovedFiles"]) //new value: remove check + in["DetectMovedFiles"](directCfg.detectMovedFiles); +} + + +void readConfig(const XmlIn& in, SyncConfig& syncCfg) +{ + readConfig(in, syncCfg.directionCfg); + + in["DeletionPolicy"](syncCfg.handleDeletion); + + warn_static("remove after migration?") + if (in["CustomDeletionFolder"]) + in["CustomDeletionFolder"](syncCfg.versioningDirectory);//obsolete name + else + in["VersioningFolder"](syncCfg.versioningDirectory); + + warn_static("remove after migration?") + if (in["VersioningFolder"] && + in["VersioningFolder"].get()->getAttribute("Style", syncCfg.versioningStyle)) //new parameter, do not complain when missing + ; + else if (in["VersioningStyle"]) //obsolete name + in["VersioningStyle"](syncCfg.versioningStyle); + else + syncCfg.versioningStyle = VER_STYLE_ADD_TIMESTAMP; //obsolete fallback +} + + +void readConfig(const XmlIn& in, FilterConfig& filter) +{ + warn_static("remove after migration?") + auto haveFilterAsSingleString = [&]() -> bool + { + if (in["Include"]) + if (auto elem = in["Include"].get()) + { + std::string tmp; + if (elem->getValue(tmp)) + return !tmp.empty(); + } + return false; + }; + if (haveFilterAsSingleString()) //obsolete style + { + in["Include"](filter.includeFilter); + in["Exclude"](filter.excludeFilter); + } + else + { + std::vector tmp = splitFilterByLines(filter.includeFilter); //default value + in["Include"](tmp); + filter.includeFilter = mergeFilterLines(tmp); + + std::vector tmp2 = splitFilterByLines(filter.excludeFilter); //default value + in["Exclude"](tmp2); + filter.excludeFilter = mergeFilterLines(tmp2); + } + + in["TimeSpan"](filter.timeSpan); + warn_static("remove after migration?") + if (in["UnitTimeSpan"]) in["UnitTimeSpan"](filter.unitTimeSpan);//obsolete name + else + in["TimeSpan"].attribute("Type", filter.unitTimeSpan); + + in["SizeMin"](filter.sizeMin); + if (in["UnitSizeMin"]) in["UnitSizeMin"](filter.unitSizeMin);//obsolete name + else + in["SizeMin"].attribute("Unit", filter.unitSizeMin); + + in["SizeMax"](filter.sizeMax); + if (in["UnitSizeMax"]) in["UnitSizeMax"](filter.unitSizeMax);//obsolete name + else + in["SizeMax"].attribute("Unit", filter.unitSizeMax); +} + + +void readConfig(const XmlIn& in, FolderPairEnh& enhPair) +{ + //read folder pairs + in["Left" ](enhPair.dirnamePhraseLeft); + in["Right"](enhPair.dirnamePhraseRight); + + //########################################################### + //alternate comp configuration (optional) + if (XmlIn inAltCmp = in["CompareConfig"]) + { + CompConfig altCmpCfg; + readConfig(inAltCmp, altCmpCfg); + + enhPair.altCmpConfig = std::make_shared(altCmpCfg); + } + //########################################################### + //alternate sync configuration (optional) + if (XmlIn inAltSync = in["SyncConfig"]) + { + SyncConfig altSyncCfg; + readConfig(inAltSync, altSyncCfg); + + enhPair.altSyncConfig = std::make_shared(altSyncCfg); + } + + //########################################################### + //alternate filter configuration + if (XmlIn inLocFilter = in["LocalFilter"]) + readConfig(inLocFilter, enhPair.localFilter); +} + + +void readConfig(const XmlIn& in, MainConfiguration& mainCfg) +{ + //read compare settings + XmlIn inMain = in["MainConfig"]; + + readConfig(inMain["Comparison"], mainCfg.cmpConfig); + //########################################################### + + //read sync configuration + readConfig(inMain["SyncConfig"], mainCfg.syncCfg); + //########################################################### + + //read filter settings + readConfig(inMain["GlobalFilter"], mainCfg.globalFilter); + + //########################################################### + //read all folder pairs + mainCfg.additionalPairs.clear(); + + bool firstItem = true; + for (XmlIn inPair = inMain["FolderPairs"]["Pair"]; inPair; inPair.next()) + { + FolderPairEnh newPair; + readConfig(inPair, newPair); + + if (firstItem) + { + firstItem = false; + mainCfg.firstPair = newPair; //set first folder pair + } + else + mainCfg.additionalPairs.push_back(newPair); //set additional folder pairs + } + + warn_static("remove after migration?") + if (inMain["ExecuteWhenFinished"]) inMain["ExecuteWhenFinished"](mainCfg.onCompletion); //obsolete name + else + inMain["OnCompletion"](mainCfg.onCompletion); +} + + +void readConfig(const XmlIn& in, xmlAccess::XmlGuiConfig& config) +{ + readConfig(in, config.mainCfg); //read main config + + //read GUI specific config data + XmlIn inGuiCfg = in["GuiConfig"]; + + warn_static("remove after migration?") + if (inGuiCfg["HideFiltered" ]) //obsolete name + inGuiCfg["HideFiltered" ](config.hideExcludedItems); + else if (inGuiCfg["ShowFiltered" ]) //obsolete name + { + inGuiCfg["ShowFiltered"](config.hideExcludedItems); + config.hideExcludedItems = !config.hideExcludedItems; + } + else + inGuiCfg["HideExcluded"](config.hideExcludedItems); + + inGuiCfg["HandleError" ](config.handleError); + + warn_static("remove after migration?") + if (inGuiCfg["SyncPreviewActive"]) //obsolete name + inGuiCfg["SyncPreviewActive"](config.highlightSyncAction); + else + { + std::string val; + if (inGuiCfg["MiddleGridView"](val)) //refactor into enum!? + config.highlightSyncAction = val == "Action"; + } +} + + +void readConfig(const XmlIn& in, xmlAccess::XmlBatchConfig& config) +{ + readConfig(in, config.mainCfg); //read main config + + //read GUI specific config data + XmlIn inBatchCfg = in["BatchConfig"]; + + inBatchCfg["HandleError" ](config.handleError); + inBatchCfg["ShowProgress" ](config.showProgress); + + warn_static("remove after migration?") + if (inBatchCfg["LogfileDirectory"]) inBatchCfg["LogfileDirectory"](config.logFileDirectory); //obsolete name + else + inBatchCfg["LogfileFolder"](config.logFileDirectory); + + if (inBatchCfg["LogfileCountMax" ]) inBatchCfg["LogfileCountMax"](config.logfilesCountLimit); //obsolete name + else + inBatchCfg["LogfileFolder"].attribute("Limit", config.logfilesCountLimit); +} + + +void readConfig(const XmlIn& in, XmlGlobalSettings& config) +{ + XmlIn inShared = in["Shared"]; + + inShared["Language"].attribute("Id", config.programLanguage); + + inShared["FailSafeFileCopy" ].attribute("Enabled", config.failsafeFileCopy); + inShared["CopyLockedFiles" ].attribute("Enabled", config.copyLockedFiles); + inShared["CopyFilePermissions" ].attribute("Enabled", config.copyFilePermissions); + inShared["AutomaticRetry" ].attribute("Count" , config.automaticRetryCount); + inShared["AutomaticRetry" ].attribute("Delay" , config.automaticRetryDelay); + inShared["FileTimeTolerance" ].attribute("Seconds", config.fileTimeTolerance); + inShared["RunWithBackgroundPriority"].attribute("Enabled", config.runWithBackgroundPriority); + inShared["LockDirectoriesDuringSync"].attribute("Enabled", config.createLockFile); + inShared["VerifyCopiedFiles" ].attribute("Enabled", config.verifyFileCopy); + inShared["LastSyncsLogSizeMax" ].attribute("Bytes" , config.lastSyncsLogFileSizeMax); + + XmlIn inOpt = inShared["OptionalDialogs"]; + inOpt["WarnUnresolvedConflicts" ].attribute("Enabled", config.optDialogs.warningUnresolvedConflicts); + inOpt["WarnNotEnoughDiskSpace" ].attribute("Enabled", config.optDialogs.warningNotEnoughDiskSpace); + inOpt["WarnSignificantDifference" ].attribute("Enabled", config.optDialogs.warningSignificantDifference); + inOpt["WarnRecycleBinNotAvailable" ].attribute("Enabled", config.optDialogs.warningRecyclerMissing); + inOpt["WarnInputFieldEmpty" ].attribute("Enabled", config.optDialogs.warningInputFieldEmpty); + inOpt["WarnDatabaseError" ].attribute("Enabled", config.optDialogs.warningDatabaseError); + inOpt["WarnDependentFolders" ].attribute("Enabled", config.optDialogs.warningDependentFolders); + inOpt["WarnFolderPairRaceCondition"].attribute("Enabled", config.optDialogs.warningFolderPairRaceCondition); + inOpt["WarnDirectoryLockFailed" ].attribute("Enabled", config.optDialogs.warningDirectoryLockFailed); + inOpt["ConfirmSaveConfig" ].attribute("Enabled", config.optDialogs.popupOnConfigChange); + inOpt["ConfirmStartSync" ].attribute("Enabled", config.optDialogs.confirmSyncStart); + inOpt["ConfirmExternalCommandMassInvoke"].attribute("Enabled", config.optDialogs.confirmExternalCommandMassInvoke); + + //gui specific global settings (optional) + XmlIn inGui = in["Gui"]; + XmlIn inWnd = inGui["MainDialog"]; + + //read application window size and position + inWnd.attribute("Width", config.gui.dlgSize.x); + inWnd.attribute("Height", config.gui.dlgSize.y); + inWnd.attribute("PosX", config.gui.dlgPos.x); + inWnd.attribute("PosY", config.gui.dlgPos.y); + inWnd.attribute("Maximized", config.gui.isMaximized); + + XmlIn inManualDel = inWnd["ManualDeletion"]; + //inManualDel.attribute("DeleteOnBothSides", config.gui.deleteOnBothSides); + inManualDel.attribute("UseRecycler" , config.gui.useRecyclerForManualDeletion); + + inWnd["CaseSensitiveSearch"].attribute("Enabled", config.gui.textSearchRespectCase); + inWnd["FolderPairsVisible" ].attribute("Max", config.gui.maxFolderPairsVisible); + + //########################################################### + + XmlIn inOverview = inWnd["OverviewPanel"]; + inOverview.attribute("ShowPercentage", config.gui.showPercentBar); + inOverview.attribute("SortByColumn", config.gui.naviLastSortColumn); + inOverview.attribute("SortAscending", config.gui.naviLastSortAscending); + + //read column attributes + XmlIn inColNavi = inOverview["Columns"]; + inColNavi(config.gui.columnAttribNavi); + + XmlIn inMainGrid = inWnd["MainGrid"]; + inMainGrid.attribute("ShowIcons", config.gui.showIcons); + inMainGrid.attribute("IconSize", config.gui.iconSize); + inMainGrid.attribute("SashOffset", config.gui.sashOffset); + + XmlIn inColLeft = inMainGrid["ColumnsLeft"]; + inColLeft(config.gui.columnAttribLeft); + + XmlIn inColRight = inMainGrid["ColumnsRight"]; + inColRight(config.gui.columnAttribRight); + //########################################################### + + inWnd["ViewFilterDefault"](config.gui.viewFilterDefault); + inWnd["Perspective2" ](config.gui.guiPerspectiveLast); + + std::vector tmp = splitFilterByLines(config.gui.defaultExclusionFilter); //default value + inGui["DefaultExclusionFilter"](tmp); + config.gui.defaultExclusionFilter = mergeFilterLines(tmp); + + //load config file history + inGui["LastUsedConfig"](config.gui.lastUsedConfigFiles); + + inGui["ConfigHistory"](config.gui.cfgFileHistory); + inGui["ConfigHistory"].attribute("MaxSize", config.gui.cfgFileHistMax); + + inGui["FolderHistoryLeft" ](config.gui.folderHistoryLeft); + inGui["FolderHistoryRight"](config.gui.folderHistoryRight); + inGui["FolderHistoryLeft"].attribute("MaxSize", config.gui.folderHistMax); + + inGui["OnCompletionHistory"](config.gui.onCompletionHistory); + inGui["OnCompletionHistory"].attribute("MaxSize", config.gui.onCompletionHistoryMax); + + //external applications + inGui["ExternalApplications"](config.gui.externelApplications); + + warn_static("remove after migration: cleanup the old placeholder syntax") //26.10.2013 + if (std::any_of(config.gui.externelApplications.begin(), config.gui.externelApplications.end(), [](const std::pair& ea) { return contains(ea.second, L"%name") || contains(ea.second, L"%nameCo") || contains(ea.second, L"%dir") || contains(ea.second, L"%dirCo"); })) + config.gui.externelApplications = XmlGlobalSettings().gui.externelApplications; + + //last update check + inGui["LastOnlineCheck"](config.gui.lastUpdateCheck); + + //batch specific global settings + //XmlIn inBatch = in["Batch"]; +} + + +bool needsMigration(const XmlDoc& doc, int currentXmlFormatVer) +{ + //(try to) migrate old configuration if needed + int xmlFormatVer = 0; + /*bool success = */doc.root().getAttribute("XmlFormat", xmlFormatVer); + return xmlFormatVer < currentXmlFormatVer; +} + + +template +void readConfig(const Zstring& filename, XmlType type, ConfigType& cfg, int currentXmlFormatVer, bool& needMigration) //throw FfsXmlError +{ + XmlDoc doc = loadXmlDocument(filename); //throw FfsXmlError + + if (getXmlTypeNoThrow(doc) != type) //throw() + throw FfsXmlError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename))); + + XmlIn in(doc); + ::readConfig(in, cfg); + + if (in.errorsOccured()) + throw FfsXmlError(replaceCpy(_("Configuration file %x loaded partially only."), L"%x", fmtFileName(filename)) + L"\n\n" + + getErrorMessageFormatted(in.getErrorsAs()), FfsXmlError::WARNING); + + //(try to) migrate old configuration if needed + needMigration = needsMigration(doc, currentXmlFormatVer); +} +} + + +void xmlAccess::readConfig(const Zstring& filename, xmlAccess::XmlGuiConfig& cfg) +{ + bool needMigration = false; + ::readConfig(filename, XML_TYPE_GUI, cfg, XML_FORMAT_VER_FFS_GUI, needMigration); //throw FfsXmlError + + if (needMigration) //(try to) migrate old configuration + try { xmlAccess::writeConfig(cfg, filename); /*throw FfsXmlError*/ } + catch (FfsXmlError&) { assert(false); } //don't bother user! +} + + +void xmlAccess::readConfig(const Zstring& filename, xmlAccess::XmlBatchConfig& cfg) +{ + bool needMigration = false; + ::readConfig(filename, XML_TYPE_BATCH, cfg, XML_FORMAT_VER_FFS_BATCH, needMigration); //throw FfsXmlError + + if (needMigration) //(try to) migrate old configuration + try { xmlAccess::writeConfig(cfg, filename); /*throw FfsXmlError*/ } + catch (FfsXmlError&) { assert(false); } //don't bother user! +} + + +void xmlAccess::readConfig(xmlAccess::XmlGlobalSettings& cfg) +{ + bool needMigration = false; + ::readConfig(getGlobalConfigFile(), XML_TYPE_GLOBAL, cfg, XML_FORMAT_VER_GLOBAL, needMigration); //throw FfsXmlError +} + + +namespace +{ +template +XmlCfg parseConfig(const XmlDoc& doc, const Zstring& filename, int currentXmlFormatVer, std::unique_ptr& warning) //nothrow +{ + XmlCfg cfg; + XmlIn in(doc); + ::readConfig(in, cfg); + + if (in.errorsOccured()) + { + if (!warning) + warning = make_unique(replaceCpy(_("Configuration file %x loaded partially only."), L"%x", fmtFileName(filename)) + L"\n\n" + + getErrorMessageFormatted(in.getErrorsAs()), FfsXmlError::WARNING); + } + else + { + //(try to) migrate old configuration if needed + if (needsMigration(doc, currentXmlFormatVer)) + try { xmlAccess::writeConfig(cfg, filename); /*throw FfsXmlError*/ } + catch (FfsXmlError&) { assert(false); } //don't bother user! + } + return cfg; +} +} + + +void xmlAccess::readAnyConfig(const std::vector& filenames, XmlGuiConfig& config) //throw FfsXmlError +{ + assert(!filenames.empty()); + + std::vector mainCfgs; + std::unique_ptr warning; + + for (auto it = filenames.begin(); it != filenames.end(); ++it) + { + const Zstring& filename = *it; + const bool firstItem = it == filenames.begin(); //init all non-"mainCfg" settings with first config file + + XmlDoc doc = loadXmlDocument(filename); //throw FfsXmlError + //do NOT use zen::loadStream as it will superfluously load even huge files! + + switch (getXmlTypeNoThrow(doc)) + { + case XML_TYPE_GUI: + { + XmlGuiConfig guiCfg = parseConfig(doc, filename, XML_FORMAT_VER_FFS_GUI, warning); //nothrow + if (firstItem) + config = guiCfg; + mainCfgs.push_back(guiCfg.mainCfg); + } + break; + + case XML_TYPE_BATCH: + { + XmlBatchConfig batchCfg = parseConfig(doc, filename, XML_FORMAT_VER_FFS_BATCH, warning); //nothrow + if (firstItem) + config = convertBatchToGui(batchCfg); + mainCfgs.push_back(batchCfg.mainCfg); + } + break; + + case XML_TYPE_GLOBAL: + case XML_TYPE_OTHER: + throw FfsXmlError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename))); + } + } + + config.mainCfg = merge(mainCfgs); + + if (warning) + throw* warning; +} + +//################################################################################################ + +namespace +{ +void writeConfig(const CompConfig& cmpConfig, XmlOut& out) +{ + out["Variant" ](cmpConfig.compareVar); + out["Symlinks"](cmpConfig.handleSymlinks); +} + + +void writeConfig(const DirectionConfig& directCfg, XmlOut& out) +{ + out["Variant"](directCfg.var); + + XmlOut outCustDir = out["CustomDirections"]; + outCustDir["LeftOnly" ](directCfg.custom.exLeftSideOnly); + outCustDir["RightOnly" ](directCfg.custom.exRightSideOnly); + outCustDir["LeftNewer" ](directCfg.custom.leftNewer); + outCustDir["RightNewer"](directCfg.custom.rightNewer); + outCustDir["Different" ](directCfg.custom.different); + outCustDir["Conflict" ](directCfg.custom.conflict); + + out["DetectMovedFiles"](directCfg.detectMovedFiles); +} + + +void writeConfig(const SyncConfig& syncCfg, XmlOut& out) +{ + writeConfig(syncCfg.directionCfg, out); + + out["DeletionPolicy" ](syncCfg.handleDeletion); + out["VersioningFolder"](syncCfg.versioningDirectory); + //out["VersioningFolder"].attribute("Limit", syncCfg.versionCountLimit); + out["VersioningFolder"].attribute("Style", syncCfg.versioningStyle); +} + + +void writeConfig(const FilterConfig& filter, XmlOut& out) +{ + out["Include"](splitFilterByLines(filter.includeFilter)); + out["Exclude"](splitFilterByLines(filter.excludeFilter)); + + out["TimeSpan"](filter.timeSpan); + out["TimeSpan"].attribute("Type", filter.unitTimeSpan); + + out["SizeMin"](filter.sizeMin); + out["SizeMin"].attribute("Unit", filter.unitSizeMin); + + out["SizeMax"](filter.sizeMax); + out["SizeMax"].attribute("Unit", filter.unitSizeMax); +} + + +void writeConfigFolderPair(const FolderPairEnh& enhPair, XmlOut& out) +{ + XmlOut outPair = out.ref().addChild("Pair"); + + //read folder pairs + outPair["Left" ](enhPair.dirnamePhraseLeft); + outPair["Right"](enhPair.dirnamePhraseRight); + + //########################################################### + //alternate comp configuration (optional) + if (enhPair.altCmpConfig.get()) + { + XmlOut outAlt = outPair["CompareConfig"]; + writeConfig(*enhPair.altCmpConfig, outAlt); + } + //########################################################### + //alternate sync configuration (optional) + if (enhPair.altSyncConfig.get()) + { + XmlOut outAltSync = outPair["SyncConfig"]; + writeConfig(*enhPair.altSyncConfig, outAltSync); + } + + //########################################################### + //alternate filter configuration + if (enhPair.localFilter != FilterConfig()) //don't spam .ffs_gui file with default filter entries + { + XmlOut outFilter = outPair["LocalFilter"]; + writeConfig(enhPair.localFilter, outFilter); + } +} + + +void writeConfig(const MainConfiguration& mainCfg, XmlOut& out) +{ + XmlOut outMain = out["MainConfig"]; + + XmlOut outCmp = outMain["Comparison"]; + + writeConfig(mainCfg.cmpConfig, outCmp); + //########################################################### + + XmlOut outSync = outMain["SyncConfig"]; + + writeConfig(mainCfg.syncCfg, outSync); + //########################################################### + + XmlOut outFilter = outMain["GlobalFilter"]; + //write filter settings + writeConfig(mainCfg.globalFilter, outFilter); + + //########################################################### + //write all folder pairs + + XmlOut outFp = outMain["FolderPairs"]; + + //write first folder pair + writeConfigFolderPair(mainCfg.firstPair, outFp); + + //write additional folder pairs + std::for_each(mainCfg.additionalPairs.begin(), mainCfg.additionalPairs.end(), + [&](const FolderPairEnh& fp) { writeConfigFolderPair(fp, outFp); }); + + outMain["OnCompletion"](mainCfg.onCompletion); +} + + +void writeConfig(const XmlGuiConfig& config, XmlOut& out) +{ + writeConfig(config.mainCfg, out); //write main config + + //write GUI specific config data + XmlOut outGuiCfg = out["GuiConfig"]; + + outGuiCfg["HideExcluded" ](config.hideExcludedItems); + outGuiCfg["HandleError" ](config.handleError); + outGuiCfg["MiddleGridView"](config.highlightSyncAction ? "Action" : "Category"); //refactor into enum!? +} + +void writeConfig(const XmlBatchConfig& config, XmlOut& out) +{ + + writeConfig(config.mainCfg, out); //write main config + + //write GUI specific config data + XmlOut outBatchCfg = out["BatchConfig"]; + + outBatchCfg["HandleError" ](config.handleError); + outBatchCfg["ShowProgress" ](config.showProgress); + outBatchCfg["LogfileFolder" ](config.logFileDirectory); + outBatchCfg["LogfileFolder"].attribute("Limit", config.logfilesCountLimit); +} + + +void writeConfig(const XmlGlobalSettings& config, XmlOut& out) +{ + XmlOut outShared = out["Shared"]; + + outShared["Language"].attribute("Id", config.programLanguage); + + outShared["FailSafeFileCopy" ].attribute("Enabled", config.failsafeFileCopy); + outShared["CopyLockedFiles" ].attribute("Enabled", config.copyLockedFiles); + outShared["CopyFilePermissions" ].attribute("Enabled", config.copyFilePermissions); + outShared["AutomaticRetry" ].attribute("Count" , config.automaticRetryCount); + outShared["AutomaticRetry" ].attribute("Delay" , config.automaticRetryDelay); + outShared["FileTimeTolerance" ].attribute("Seconds", config.fileTimeTolerance); + outShared["RunWithBackgroundPriority"].attribute("Enabled", config.runWithBackgroundPriority); + outShared["LockDirectoriesDuringSync"].attribute("Enabled", config.createLockFile); + outShared["VerifyCopiedFiles" ].attribute("Enabled", config.verifyFileCopy); + outShared["LastSyncsLogSizeMax" ].attribute("Bytes" , config.lastSyncsLogFileSizeMax); + + XmlOut outOpt = outShared["OptionalDialogs"]; + outOpt["WarnUnresolvedConflicts" ].attribute("Enabled", config.optDialogs.warningUnresolvedConflicts); + outOpt["WarnNotEnoughDiskSpace" ].attribute("Enabled", config.optDialogs.warningNotEnoughDiskSpace); + outOpt["WarnSignificantDifference" ].attribute("Enabled", config.optDialogs.warningSignificantDifference); + outOpt["WarnRecycleBinNotAvailable" ].attribute("Enabled", config.optDialogs.warningRecyclerMissing); + outOpt["WarnInputFieldEmpty" ].attribute("Enabled", config.optDialogs.warningInputFieldEmpty); + outOpt["WarnDatabaseError" ].attribute("Enabled", config.optDialogs.warningDatabaseError); + outOpt["WarnDependentFolders" ].attribute("Enabled", config.optDialogs.warningDependentFolders); + outOpt["WarnFolderPairRaceCondition"].attribute("Enabled", config.optDialogs.warningFolderPairRaceCondition); + outOpt["WarnDirectoryLockFailed" ].attribute("Enabled", config.optDialogs.warningDirectoryLockFailed); + outOpt["ConfirmSaveConfig" ].attribute("Enabled", config.optDialogs.popupOnConfigChange); + outOpt["ConfirmStartSync" ].attribute("Enabled", config.optDialogs.confirmSyncStart); + outOpt["ConfirmExternalCommandMassInvoke"].attribute("Enabled", config.optDialogs.confirmExternalCommandMassInvoke); + + //gui specific global settings (optional) + XmlOut outGui = out["Gui"]; + XmlOut outWnd = outGui["MainDialog"]; + + //write application window size and position + outWnd.attribute("Width", config.gui.dlgSize.x); + outWnd.attribute("Height", config.gui.dlgSize.y); + outWnd.attribute("PosX", config.gui.dlgPos.x); + outWnd.attribute("PosY", config.gui.dlgPos.y); + outWnd.attribute("Maximized", config.gui.isMaximized); + + XmlOut outManualDel = outWnd["ManualDeletion"]; + //outManualDel.attribute("DeleteOnBothSides", config.gui.deleteOnBothSides); + outManualDel.attribute("UseRecycler" , config.gui.useRecyclerForManualDeletion); + + outWnd["CaseSensitiveSearch"].attribute("Enabled", config.gui.textSearchRespectCase); + outWnd["FolderPairsVisible" ].attribute("Max", config.gui.maxFolderPairsVisible); + + //########################################################### + + XmlOut outOverview = outWnd["OverviewPanel"]; + outOverview.attribute("ShowPercentage", config.gui.showPercentBar); + outOverview.attribute("SortByColumn", config.gui.naviLastSortColumn); + outOverview.attribute("SortAscending", config.gui.naviLastSortAscending); + + //write column attributes + XmlOut outColNavi = outOverview["Columns"]; + outColNavi(config.gui.columnAttribNavi); + + XmlOut outMainGrid = outWnd["MainGrid"]; + outMainGrid.attribute("ShowIcons", config.gui.showIcons); + outMainGrid.attribute("IconSize", config.gui.iconSize); + outMainGrid.attribute("SashOffset", config.gui.sashOffset); + + XmlOut outColLeft = outMainGrid["ColumnsLeft"]; + outColLeft(config.gui.columnAttribLeft); + + XmlOut outColRight = outMainGrid["ColumnsRight"]; + outColRight(config.gui.columnAttribRight); + //########################################################### + + outWnd["ViewFilterDefault"](config.gui.viewFilterDefault); + outWnd["Perspective2" ](config.gui.guiPerspectiveLast); + + outGui["DefaultExclusionFilter"](splitFilterByLines(config.gui.defaultExclusionFilter)); + + //load config file history + outGui["LastUsedConfig"](config.gui.lastUsedConfigFiles); + + outGui["ConfigHistory" ](config.gui.cfgFileHistory); + outGui["ConfigHistory"].attribute("MaxSize", config.gui.cfgFileHistMax); + + outGui["FolderHistoryLeft" ](config.gui.folderHistoryLeft); + outGui["FolderHistoryRight"](config.gui.folderHistoryRight); + outGui["FolderHistoryLeft" ].attribute("MaxSize", config.gui.folderHistMax); + + outGui["OnCompletionHistory"](config.gui.onCompletionHistory); + outGui["OnCompletionHistory"].attribute("MaxSize", config.gui.onCompletionHistoryMax); + + //external applications + outGui["ExternalApplications"](config.gui.externelApplications); + + //last update check + outGui["LastOnlineCheck"](config.gui.lastUpdateCheck); + + //batch specific global settings + //XmlOut outBatch = out["Batch"]; +} + + +template +void writeConfig(const ConfigType& config, XmlType type, int xmlFormatVer, const Zstring& filename) +{ + XmlDoc doc("FreeFileSync"); + setXmlType(doc, type); //throw() + + doc.root().setAttribute("XmlFormat", xmlFormatVer); + + XmlOut out(doc); + writeConfig(config, out); + + saveXmlDocument(doc, filename); //throw FfsXmlError +} +} + +void xmlAccess::writeConfig(const XmlGuiConfig& cfg, const Zstring& filename) +{ + ::writeConfig(cfg, XML_TYPE_GUI, XML_FORMAT_VER_FFS_GUI, filename); //throw FfsXmlError +} + + +void xmlAccess::writeConfig(const XmlBatchConfig& cfg, const Zstring& filename) +{ + ::writeConfig(cfg, XML_TYPE_BATCH, XML_FORMAT_VER_FFS_BATCH, filename); //throw FfsXmlError +} + + +void xmlAccess::writeConfig(const XmlGlobalSettings& cfg) +{ + ::writeConfig(cfg, XML_TYPE_GLOBAL, XML_FORMAT_VER_GLOBAL, getGlobalConfigFile()); //throw FfsXmlError +} + + +std::wstring xmlAccess::extractJobName(const Zstring& configFilename) +{ + const Zstring shortName = afterLast(configFilename, FILE_NAME_SEPARATOR); //returns the whole string if separator not found + const Zstring jobName = beforeLast(shortName, Zstr('.')); //returns empty string if seperator not found + return utfCvrtTo(jobName.empty() ? shortName : jobName); +} diff --git a/FreeFileSync/Source/lib/process_xml.h b/FreeFileSync/Source/lib/process_xml.h new file mode 100644 index 00000000..046cbca2 --- /dev/null +++ b/FreeFileSync/Source/lib/process_xml.h @@ -0,0 +1,298 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef PROCESSXML_H_INCLUDED +#define PROCESSXML_H_INCLUDED + +#include +#include "../structures.h" +#include "xml_base.h" +#include "localization.h" +#include "../ui/column_attr.h" +#include "../ui/folder_history_types.h" +//#include "ffs_paths.h" + +namespace xmlAccess +{ +enum XmlType +{ + XML_TYPE_GUI, + XML_TYPE_BATCH, + XML_TYPE_GLOBAL, + XML_TYPE_OTHER +}; + +XmlType getXmlType(const Zstring& filename); //throw FfsXmlError + + +enum OnError +{ + ON_ERROR_IGNORE, + ON_ERROR_POPUP, + ON_ERROR_STOP +}; + +enum OnGuiError +{ + ON_GUIERROR_POPUP, + ON_GUIERROR_IGNORE +}; + +typedef std::wstring Description; +typedef std::wstring Commandline; +typedef std::vector> ExternalApps; + +//--------------------------------------------------------------------- +struct XmlGuiConfig +{ + XmlGuiConfig() : + hideExcludedItems(false), + handleError(ON_GUIERROR_POPUP), + highlightSyncAction(true) {} //initialize values + + zen::MainConfiguration mainCfg; + + bool hideExcludedItems; + OnGuiError handleError; //reaction on error situation during synchronization + bool highlightSyncAction; +}; + + +inline +bool operator==(const XmlGuiConfig& lhs, const XmlGuiConfig& rhs) +{ + return lhs.mainCfg == rhs.mainCfg && + lhs.hideExcludedItems == rhs.hideExcludedItems && + lhs.handleError == rhs.handleError && + lhs.highlightSyncAction == rhs.highlightSyncAction; +} + + +struct XmlBatchConfig +{ + XmlBatchConfig() : + showProgress(true), + logfilesCountLimit(-1), + handleError(ON_ERROR_POPUP) {} + + zen::MainConfiguration mainCfg; + + bool showProgress; + Zstring logFileDirectory; + int logfilesCountLimit; //max logfiles; 0 := don't save logfiles; < 0 := no limit + OnError handleError; //reaction on error situation during synchronization +}; + + +struct OptionalDialogs +{ + OptionalDialogs() { resetDialogs();} + + void resetDialogs(); + + bool warningDependentFolders; + bool warningFolderPairRaceCondition; + bool warningSignificantDifference; + bool warningNotEnoughDiskSpace; + bool warningUnresolvedConflicts; + bool warningDatabaseError; + bool warningRecyclerMissing; + bool warningInputFieldEmpty; + bool warningDirectoryLockFailed; + bool popupOnConfigChange; + bool confirmSyncStart; + bool confirmExternalCommandMassInvoke; +}; + + +enum FileIconSize +{ + ICON_SIZE_SMALL, + ICON_SIZE_MEDIUM, + ICON_SIZE_LARGE +}; + + +struct ViewFilterDefault +{ + ViewFilterDefault() : equal(false) + { + leftOnly = rightOnly = leftNewer = rightNewer = different = conflict = true; + createLeft = createRight = updateLeft = updateRight = deleteLeft = deleteRight = doNothing = true; + } + bool equal; + bool leftOnly, rightOnly, leftNewer, rightNewer, different, conflict; //category view + bool createLeft, createRight, updateLeft, updateRight, deleteLeft, deleteRight, doNothing; //action view +}; + +Zstring getGlobalConfigFile(); + +struct XmlGlobalSettings +{ + //--------------------------------------------------------------------- + //Shared (GUI/BATCH) settings + XmlGlobalSettings() : + programLanguage(zen::retrieveSystemLanguage()), + failsafeFileCopy(true), + copyLockedFiles(true), + copyFilePermissions(false), + automaticRetryCount(0), + automaticRetryDelay(5), + fileTimeTolerance(2), //default 2s: FAT vs NTFS + runWithBackgroundPriority(false), + createLockFile(true), + verifyFileCopy(false), + lastSyncsLogFileSizeMax(100000) //maximum size for LastSyncs.log: use a human-readable number + {} + + int programLanguage; + bool failsafeFileCopy; + bool copyLockedFiles; + bool copyFilePermissions; + size_t automaticRetryCount; + size_t automaticRetryDelay; //unit: [sec] + + size_t fileTimeTolerance; //max. allowed file time deviation + bool runWithBackgroundPriority; + bool createLockFile; + bool verifyFileCopy; //verify copied files + size_t lastSyncsLogFileSizeMax; + + OptionalDialogs optDialogs; + + //--------------------------------------------------------------------- + struct Gui + { + Gui() : + dlgPos(wxDefaultCoord, wxDefaultCoord), + dlgSize(wxDefaultCoord, wxDefaultCoord), + isMaximized(false), + sashOffset(0), + maxFolderPairsVisible(6), + columnAttribNavi (zen::getDefaultColumnAttributesNavi()), + columnAttribLeft (zen::getDefaultColumnAttributesLeft()), + columnAttribRight(zen::getDefaultColumnAttributesRight()), + naviLastSortColumn(zen::defaultValueLastSortColumn), + naviLastSortAscending(zen::defaultValueLastSortAscending), + showPercentBar(zen::defaultValueShowPercentage), + cfgFileHistMax(30), + folderHistMax(15), + onCompletionHistoryMax(8), +#ifdef ZEN_WIN + defaultExclusionFilter(Zstr("\\System Volume Information\\") Zstr("\n") + Zstr("\\$Recycle.Bin\\") Zstr("\n") + Zstr("\\RECYCLER\\") Zstr("\n") + Zstr("\\RECYCLED\\") Zstr("\n") + Zstr("*\\desktop.ini") Zstr("\n") + Zstr("*\\thumbs.db")), +#elif defined ZEN_LINUX + defaultExclusionFilter(Zstr("/.Trash-*/") Zstr("\n") + Zstr("/.recycle/")), +#elif defined ZEN_MAC + defaultExclusionFilter(Zstr("/.fseventsd/") Zstr("\n") + Zstr("/.Spotlight-V100/") Zstr("\n") + Zstr("/.Trashes/") Zstr("\n") + Zstr("/._.Trashes") Zstr("\n") + Zstr("*/.DS_Store")), +#endif + //deleteOnBothSides(false), + useRecyclerForManualDeletion(true), //enable if OS supports it; else user will have to activate first and then get an error message +#if defined ZEN_WIN || defined ZEN_MAC + textSearchRespectCase(false), +#elif defined ZEN_LINUX + textSearchRespectCase(true), +#endif + showIcons(true), + iconSize(ICON_SIZE_SMALL), + lastUpdateCheck(0) + { + //default external apps will be translated "on the fly"!!! First entry will be used for [Enter] or mouse double-click! +#ifdef ZEN_WIN + externelApplications.push_back(std::make_pair(L"Show in Explorer", L"explorer /select, \"%item_path%\"")); + externelApplications.push_back(std::make_pair(L"Open with default application", L"\"%item_path%\"")); + //mark for extraction: _("Show in Explorer") + //mark for extraction: _("Open with default application") +#elif defined ZEN_LINUX + externelApplications.push_back(std::make_pair(L"Browse directory", L"xdg-open \"%item_folder%\"")); + externelApplications.push_back(std::make_pair(L"Open with default application", L"xdg-open \"%item_path%\"")); + //mark for extraction: _("Browse directory") Linux doesn't use the term "folder" +#elif defined ZEN_MAC + externelApplications.push_back(std::make_pair(L"Browse directory", L"open -R \"%item_path%\"")); + externelApplications.push_back(std::make_pair(L"Open with default application", L"open \"%item_path%\"")); +#endif + } + + wxPoint dlgPos; + wxSize dlgSize; + bool isMaximized; + int sashOffset; + + int maxFolderPairsVisible; + + std::vector columnAttribNavi; //compressed view/navigation + std::vector columnAttribLeft; + std::vector columnAttribRight; + + zen::ColumnTypeNavi naviLastSortColumn; //remember sort on navigation panel + bool naviLastSortAscending; // + + bool showPercentBar; //in navigation panel + + ExternalApps externelApplications; + + std::vector cfgFileHistory; + size_t cfgFileHistMax; + + std::vector lastUsedConfigFiles; + + std::vector folderHistoryLeft; + std::vector folderHistoryRight; + size_t folderHistMax; + + std::vector onCompletionHistory; + size_t onCompletionHistoryMax; + + Zstring defaultExclusionFilter; + + //bool deleteOnBothSides; + bool useRecyclerForManualDeletion; + bool textSearchRespectCase; + + bool showIcons; + FileIconSize iconSize; + + long lastUpdateCheck; //time of last update check + + ViewFilterDefault viewFilterDefault; + wxString guiPerspectiveLast; //used by wxAuiManager + } gui; + + //--------------------------------------------------------------------- + //struct Batch +}; + +//read/write specific config types +void readConfig(const Zstring& filename, XmlGuiConfig& config); // +void readConfig(const Zstring& filename, XmlBatchConfig& config); //throw FfsXmlError +void readConfig( XmlGlobalSettings& config); // + +void writeConfig(const XmlGuiConfig& config, const Zstring& filename); // +void writeConfig(const XmlBatchConfig& config, const Zstring& filename); //throw FfsXmlError +void writeConfig(const XmlGlobalSettings& config); // + +//convert (multiple) *.ffs_gui, *.ffs_batch files or combinations of both into target config structure: +void readAnyConfig(const std::vector& filenames, XmlGuiConfig& config); //throw FfsXmlError + +//config conversion utilities +XmlGuiConfig convertBatchToGui(const XmlBatchConfig& batchCfg); //noexcept +XmlBatchConfig convertGuiToBatch(const XmlGuiConfig& guiCfg ); // +XmlBatchConfig convertGuiToBatchPreservingExistingBatch(const xmlAccess::XmlGuiConfig& guiCfg, const Zstring& referenceBatchFile); //noexcept + +std::wstring extractJobName(const Zstring& configFilename); +} + + +#endif // PROCESSXML_H_INCLUDED diff --git a/FreeFileSync/Source/lib/resolve_path.cpp b/FreeFileSync/Source/lib/resolve_path.cpp new file mode 100644 index 00000000..768876c9 --- /dev/null +++ b/FreeFileSync/Source/lib/resolve_path.cpp @@ -0,0 +1,708 @@ +#include "resolve_path.h" +#include //not necessarily included by ! +#include +#include +#include +#include +#include +#include //wxGetEnv + +#ifdef ZEN_WIN +#include +#include +#include //includes "windows.h" +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "Mpr.lib") +#endif + +#elif defined ZEN_LINUX || defined ZEN_MAC +#include //getenv() +#include //getcwd +#endif + +using namespace zen; + + +namespace +{ +#ifdef ZEN_WIN +Zstring resolveRelativePath(const Zstring& relativeName) //note: ::GetFullPathName() is documented not threadsafe! +{ + //don't use long path prefix! does not work with relative paths "." and ".." + const DWORD bufferSize = ::GetFullPathName(relativeName.c_str(), 0, nullptr, nullptr); + if (bufferSize > 0) + { + std::vector buffer(bufferSize); + const DWORD charsWritten = ::GetFullPathName(relativeName.c_str(), //__in LPCTSTR lpFileName, + bufferSize, //__in DWORD nBufferLength, + &buffer[0], //__out LPTSTR lpBuffer, + nullptr); //__out LPTSTR *lpFilePart + if (0 < charsWritten && charsWritten < bufferSize) //theoretically, charsWritten can never be == "bufferSize" + return Zstring(&buffer[0], charsWritten); + } + return relativeName; //ERROR! Don't do anything +} + +#elif defined ZEN_LINUX || defined ZEN_MAC +Zstring resolveRelativePath(const Zstring& relativeName) +{ + //http://linux.die.net/man/2/path_resolution + if (!startsWith(relativeName, FILE_NAME_SEPARATOR)) //absolute names are exactly those starting with a '/' + { + /* + basic support for '~': strictly speaking this is a shell-layer feature, so "realpath()" won't handle it + http://www.gnu.org/software/bash/manual/html_node/Tilde-Expansion.html + + http://linux.die.net/man/3/getpwuid: An application that wants to determine its user's home directory + should inspect the value of HOME (rather than the value getpwuid(getuid())->pw_dir) since this allows + the user to modify their notion of "the home directory" during a login session. + */ + if (startsWith(relativeName, "~/") || relativeName == "~") + { + const char* homeDir = ::getenv("HOME"); + if (!homeDir) + return relativeName; //error! no further processing! + + if (startsWith(relativeName, "~/")) + return appendSeparator(homeDir) + afterFirst(relativeName, '/'); + else if (relativeName == "~") + return homeDir; + } + + //we cannot use ::realpath() since it resolves *existing* relative paths only! + if (char* dirpath = ::getcwd(nullptr, 0)) + { + ZEN_ON_SCOPE_EXIT(::free(dirpath)); + return appendSeparator(dirpath) + relativeName; + } + } + return relativeName; +} +#endif + + +#ifdef ZEN_WIN +class CsidlConstants +{ +public: + typedef std::map CsidlToDirMap; //case-insensitive comparison + + static const CsidlToDirMap& get() + { + static CsidlConstants inst; //potential MT solved by intializing at startup, see below + return inst.csidlToDir; + } + +private: + CsidlConstants() + { + auto addCsidl = [&](int csidl, const Zstring& paramName) + { + wchar_t buffer[MAX_PATH] = {}; + if (SUCCEEDED(::SHGetFolderPath(nullptr, //__in HWND hwndOwner, + csidl | CSIDL_FLAG_DONT_VERIFY, //__in int nFolder, + nullptr, //__in HANDLE hToken, + 0 /* == SHGFP_TYPE_CURRENT*/, //__in DWORD dwFlags, + buffer))) //__out LPTSTR pszPath + { + Zstring dirname = buffer; + if (!dirname.empty()) + csidlToDir.insert(std::make_pair(paramName, dirname)); + } + }; + + //================================================================================================ + //SHGetKnownFolderPath: API available only with Windows Vista and later: +#ifdef __MINGW32__ +#define KF_FLAG_DONT_VERIFY 0x00004000 +#endif + typedef HRESULT (STDAPICALLTYPE* SHGetKnownFolderPathFunc)(REFKNOWNFOLDERID rfid, DWORD dwFlags, HANDLE hToken, PWSTR* ppszPath); + const SysDllFun shGetKnownFolderPath(L"Shell32.dll", "SHGetKnownFolderPath"); + + auto addFolderId = [&](REFKNOWNFOLDERID rfid, const Zstring& paramName) + { + if (shGetKnownFolderPath != nullptr) //[!] avoid bogus MSVC "performance warning" + { + PWSTR path = nullptr; + if (SUCCEEDED(shGetKnownFolderPath(rfid, //_In_ REFKNOWNFOLDERID rfid, + KF_FLAG_DONT_VERIFY, //_In_ DWORD dwFlags, + nullptr, //_In_opt_ HANDLE hToken, + &path))) //_Out_ PWSTR *ppszPath + { + ZEN_ON_SCOPE_EXIT(::CoTaskMemFree(path)); + + Zstring dirname = path; + if (!dirname.empty()) + csidlToDir.insert(std::make_pair(paramName, dirname)); + } + } + }; + + addCsidl(CSIDL_DESKTOPDIRECTORY, L"csidl_Desktop"); // C:\Users\\Desktop + addCsidl(CSIDL_COMMON_DESKTOPDIRECTORY, L"csidl_PublicDesktop"); // C:\Users\All Users\Desktop + + addCsidl(CSIDL_FAVORITES, L"csidl_Favorites"); // C:\Users\\Favorites + addCsidl(CSIDL_COMMON_FAVORITES, L"csidl_PublicFavorites"); // C:\Users\\Favorites; unused? -> http://blogs.msdn.com/b/oldnewthing/archive/2012/09/04/10346022.aspx + + addCsidl(CSIDL_PERSONAL, L"csidl_MyDocuments"); // C:\Users\\Documents + addCsidl(CSIDL_COMMON_DOCUMENTS, L"csidl_PublicDocuments"); // C:\Users\Public\Documents + + addCsidl(CSIDL_MYMUSIC, L"csidl_MyMusic"); // C:\Users\\Music + addCsidl(CSIDL_COMMON_MUSIC, L"csidl_PublicMusic"); // C:\Users\Public\Music + + addCsidl(CSIDL_MYPICTURES, L"csidl_MyPictures"); // C:\Users\\Pictures + addCsidl(CSIDL_COMMON_PICTURES, L"csidl_PublicPictures"); // C:\Users\Public\Pictures + + addCsidl(CSIDL_MYVIDEO, L"csidl_MyVideos"); // C:\Users\\Videos + addCsidl(CSIDL_COMMON_VIDEO, L"csidl_PublicVideos"); // C:\Users\Public\Videos + + addCsidl(CSIDL_NETHOOD, L"csidl_Nethood"); // C:\Users\\AppData\Roaming\Microsoft\Windows\Network Shortcuts + + addCsidl(CSIDL_PROGRAMS, L"csidl_Programs"); // C:\Users\\AppData\Roaming\Microsoft\Windows\Start Menu\Programs + addCsidl(CSIDL_COMMON_PROGRAMS, L"csidl_PublicPrograms"); // C:\ProgramData\Microsoft\Windows\Start Menu\Programs + + addCsidl(CSIDL_RESOURCES, L"csidl_Resources"); // C:\Windows\Resources + + addCsidl(CSIDL_STARTMENU, L"csidl_StartMenu"); // C:\Users\\AppData\Roaming\Microsoft\Windows\Start Menu + addCsidl(CSIDL_COMMON_STARTMENU, L"csidl_PublicStartMenu"); // C:\ProgramData\Microsoft\Windows\Start Menu + + addCsidl(CSIDL_STARTUP, L"csidl_Startup"); // C:\Users\\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\StartUp + addCsidl(CSIDL_COMMON_STARTUP, L"csidl_PublicStartup"); // C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp + + addCsidl(CSIDL_TEMPLATES, L"csidl_Templates"); // C:\Users\\AppData\Roaming\Microsoft\Windows\Templates + addCsidl(CSIDL_COMMON_TEMPLATES, L"csidl_PublicTemplates"); // C:\ProgramData\Microsoft\Windows\Templates + + //Vista and later: + addFolderId(FOLDERID_Downloads, L"csidl_Downloads"); // C:\Users\\Downloads + addFolderId(FOLDERID_PublicDownloads, L"csidl_PublicDownloads"); // C:\Users\Public\Downloads + + addFolderId(FOLDERID_QuickLaunch, L"csidl_QuickLaunch"); // C:\Users\\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch + + /* + CSIDL_APPDATA covered by %AppData% + CSIDL_LOCAL_APPDATA covered by %LocalAppData% -> not on XP! + CSIDL_COMMON_APPDATA covered by %ProgramData% -> not on XP! + CSIDL_PROFILE covered by %UserProfile% + CSIDL_WINDOWS covered by %WinDir% + CSIDL_SYSTEM covered by %WinDir% + CSIDL_SYSTEMX86 covered by %WinDir% + CSIDL_PROGRAM_FILES covered by %ProgramFiles% + CSIDL_PROGRAM_FILES_COMMON covered by %CommonProgramFiles% + CSIDL_PROGRAM_FILESX86 covered by %ProgramFiles(x86)% -> not on XP! + CSIDL_PROGRAM_FILES_COMMONX86 covered by %CommonProgramFiles(x86)% -> not on XP! + CSIDL_ADMINTOOLS not relevant? + CSIDL_COMMON_ADMINTOOLS not relevant? + + FOLDERID_Public covered by %Public% + */ + } + + CsidlConstants(const CsidlConstants&); + CsidlConstants& operator=(const CsidlConstants&); + + CsidlToDirMap csidlToDir; +}; + +//caveat: function scope static initialization is not thread-safe in VS 2010! => make sure to call at app start! +struct Dummy { Dummy() { CsidlConstants::get(); }} blah; +#endif + + +std::unique_ptr getEnvironmentVar(const Zstring& envName) //return nullptr if not found +{ + wxString value; + if (!wxGetEnv(utfCvrtTo(envName), &value)) + return nullptr; + + //some postprocessing: + trim(value); //remove leading, trailing blanks + + //remove leading, trailing double-quotes + if (startsWith(value, L"\"") && + endsWith (value, L"\"") && + value.length() >= 2) + value = wxString(value.c_str() + 1, value.length() - 2); + + return make_unique(utfCvrtTo(value)); +} + + +std::unique_ptr resolveMacro(const Zstring& macro, //macro without %-characters + const std::vector>& ext) //return nullptr if not resolved +{ + auto equalNoCase = [](const Zstring& lhs, const Zstring& rhs) { return utfCvrtTo(lhs).CmpNoCase(utfCvrtTo(rhs)) == 0; }; + + //there exist environment variables named %TIME%, %DATE% so check for our internal macros first! + if (equalNoCase(macro, Zstr("time"))) + return make_unique(formatTime(Zstr("%H%M%S"))); + + if (equalNoCase(macro, Zstr("date"))) + return make_unique(formatTime(FORMAT_ISO_DATE)); + + if (equalNoCase(macro, Zstr("timestamp"))) + return make_unique(formatTime(Zstr("%Y-%m-%d %H%M%S"))); //e.g. "2012-05-15 131513" + + std::unique_ptr cand; + auto processPhrase = [&](const Zchar* phrase, const Zchar* format) -> bool + { + if (!equalNoCase(macro, phrase)) + return false; + + cand = make_unique(formatTime(format)); + return true; + }; + + if (processPhrase(Zstr("weekday"), Zstr("%A"))) return cand; + if (processPhrase(Zstr("day" ), Zstr("%d"))) return cand; + if (processPhrase(Zstr("month" ), Zstr("%m"))) return cand; + if (processPhrase(Zstr("week" ), Zstr("%U"))) return cand; + if (processPhrase(Zstr("year" ), Zstr("%Y"))) return cand; + if (processPhrase(Zstr("hour" ), Zstr("%H"))) return cand; + if (processPhrase(Zstr("min" ), Zstr("%M"))) return cand; + if (processPhrase(Zstr("sec" ), Zstr("%S"))) return cand; + + //check domain-specific extensions + { + auto it = std::find_if(ext.begin(), ext.end(), [&](const std::pair& p) { return equalNoCase(macro, p.first); }); + if (it != ext.end()) + return make_unique(it->second); + } + + //try to resolve as environment variable + if (std::unique_ptr value = getEnvironmentVar(macro)) + return value; + +#ifdef ZEN_WIN + //try to resolve as CSIDL value + { + const auto& csidlMap = CsidlConstants::get(); + auto it = csidlMap.find(macro); + if (it != csidlMap.end()) + return make_unique(it->second); + } +#endif + + return nullptr; +} + +const Zchar MACRO_SEP = Zstr('%'); + +//returns expanded or original string +Zstring expandMacros(const Zstring& text, const std::vector>& ext) +{ + if (contains(text, MACRO_SEP)) + { + Zstring prefix = beforeFirst(text, MACRO_SEP); + Zstring rest = afterFirst (text, MACRO_SEP); + if (contains(rest, MACRO_SEP)) + { + Zstring potentialMacro = beforeFirst(rest, MACRO_SEP); + Zstring postfix = afterFirst (rest, MACRO_SEP); //text == prefix + MACRO_SEP + potentialMacro + MACRO_SEP + postfix + + if (std::unique_ptr value = resolveMacro(potentialMacro, ext)) + return prefix + *value + expandMacros(postfix, ext); + else + return prefix + MACRO_SEP + potentialMacro + expandMacros(MACRO_SEP + postfix, ext); + } + } + return text; +} +} + + +Zstring zen::expandMacros(const Zstring& text) { return ::expandMacros(text, std::vector>()); } + + +namespace +{ +#ifdef ZEN_WIN +//networks and cdrom excluded - may still block for slow USB sticks! +Zstring getPathByVolumenName(const Zstring& volumeName) //return empty string on error +{ + //FindFirstVolume(): traverses volumes on local hard disks only! + //GetLogicalDriveStrings(): traverses all *logical* volumes, including CD-ROM, FreeOTFE virtual volumes + + const DWORD bufferSize = ::GetLogicalDriveStrings(0, nullptr); + std::vector buffer(bufferSize); + + const DWORD rv = ::GetLogicalDriveStrings(bufferSize, //__in DWORD nBufferLength, + &buffer[0]); //__out LPTSTR lpBuffer + if (0 < rv && rv < bufferSize) + { + //search for matching path in parallel until first hit + RunUntilFirstHit findFirstMatch; + + for (const wchar_t* it = &buffer[0]; *it != 0; it += strLength(it) + 1) //list terminated by empty c-string + { + const Zstring path = it; + + findFirstMatch.addJob([path, volumeName]() -> std::unique_ptr + { + UINT type = ::GetDriveType(appendSeparator(path).c_str()); //non-blocking call! + if (type == DRIVE_REMOTE || type == DRIVE_CDROM) + return nullptr; + + //next call seriously blocks for non-existing network drives! + std::vector volName(MAX_PATH + 1); //docu says so + + if (::GetVolumeInformation(appendSeparator(path).c_str(), //__in_opt LPCTSTR lpRootPathName, + &volName[0], //__out LPTSTR lpVolumeNameBuffer, + static_cast(volName.size()), //__in DWORD nVolumeNameSize, + nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, + nullptr, //__out_opt LPDWORD lpMaximumComponentLength, + nullptr, //__out_opt LPDWORD lpFileSystemFlags, + nullptr, //__out LPTSTR lpFileSystemNameBuffer, + 0)) //__in DWORD nFileSystemNameSize + if (EqualFilename()(volumeName, Zstring(&volName[0]))) + return zen::make_unique(path); + return nullptr; + }); + } + if (auto result = findFirstMatch.get()) //blocks until ready + return *result; + } + + return Zstring(); +} + + +//networks and cdrom excluded - this should not block +Zstring getVolumeName(const Zstring& volumePath) //return empty string on error +{ + UINT rv = ::GetDriveType(appendSeparator(volumePath).c_str()); //non-blocking call! + if (rv != DRIVE_REMOTE && + rv != DRIVE_CDROM) + { + const DWORD bufferSize = MAX_PATH + 1; + std::vector buffer(bufferSize); + if (::GetVolumeInformation(appendSeparator(volumePath).c_str(), //__in_opt LPCTSTR lpRootPathName, + &buffer[0], //__out LPTSTR lpVolumeNameBuffer, + bufferSize, //__in DWORD nVolumeNameSize, + nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, + nullptr, //__out_opt LPDWORD lpMaximumComponentLength, + nullptr, //__out_opt LPDWORD lpFileSystemFlags, + nullptr, //__out LPTSTR lpFileSystemNameBuffer, + 0)) //__in DWORD nFileSystemNameSize + return &buffer[0]; + } + return Zstring(); +} +#endif + + +//expand volume name if possible, return original input otherwise +Zstring expandVolumeName(const Zstring& text) // [volname]:\folder [volname]\folder [volname]folder -> C:\folder +{ + //this would be a nice job for a C++11 regex... + + //we only expect the [.*] pattern at the beginning => do not touch dir names like "C:\somedir\[stuff]" + Zstring textTmp = text; + trim(textTmp, true, false); + + if (startsWith(textTmp, Zstr("["))) + { + size_t posEnd = textTmp.find(Zstr("]")); + if (posEnd != Zstring::npos) + { + Zstring volname = Zstring(textTmp.c_str() + 1, posEnd - 1); + Zstring rest = Zstring(textTmp.c_str() + posEnd + 1); + + if (startsWith(rest, Zstr(':'))) + rest = afterFirst(rest, Zstr(':')); + if (startsWith(rest, FILE_NAME_SEPARATOR)) + rest = afterFirst(rest, FILE_NAME_SEPARATOR); +#ifdef ZEN_WIN + //[.*] pattern was found... + if (!volname.empty()) + { + Zstring volPath = getPathByVolumenName(volname); //may block for slow USB sticks! + if (!volPath.empty()) + return appendSeparator(volPath) + rest; //successfully replaced pattern + } + //error: did not find corresponding volume name: + + /*make sure directory creation will fail later if attempted, instead of inconveniently interpreting this string as a relative name! + [FFS USB]\FreeFileSync will be resolved as + ?:\[FFS USB]\FreeFileSync\ - Windows + /.../[FFS USB]/FreeFileSync/ - Linux + instead of: + C:\Program Files\FreeFileSync\[FFS USB]\FreeFileSync\ */ + return L"?:\\[" + volname + L"]\\" + rest; + +#elif defined ZEN_LINUX || defined ZEN_MAC //neither supported nor needed + return "/.../[" + volname + "]/" + rest; +#endif + } + } + return text; +} +} + + +void getDirectoryAliasesRecursive(const Zstring& dirname, std::set& output) +{ +#ifdef ZEN_WIN + //1. replace volume path by volume name: c:\dirname -> [SYSTEM]\dirname + if (dirname.size() >= 3 && + std::iswalpha(dirname[0]) && + dirname[1] == L':' && + dirname[2] == L'\\') + { + Zstring volname = getVolumeName(Zstring(dirname.c_str(), 3)); //should not block + if (!volname.empty()) + output.insert(L"[" + volname + L"]" + Zstring(dirname.c_str() + 2)); + } + + //2. replace volume name by volume path: [SYSTEM]\dirname -> c:\dirname + { + Zstring testVolname = expandVolumeName(dirname); //should not block + if (testVolname != dirname) + if (output.insert(testVolname).second) + getDirectoryAliasesRecursive(testVolname, output); //recurse! + } +#endif + + //3. environment variables: C:\Users\ -> %USERPROFILE% + { + std::map envToDir; + + //get list of useful variables + auto addEnvVar = [&](const Zstring& envName) + { + if (std::unique_ptr value = getEnvironmentVar(envName)) + envToDir.insert(std::make_pair(envName, *value)); + }; +#ifdef ZEN_WIN + addEnvVar(L"AllUsersProfile"); // C:\ProgramData + addEnvVar(L"AppData"); // C:\Users\\AppData\Roaming + addEnvVar(L"LocalAppData"); // C:\Users\\AppData\Local + addEnvVar(L"ProgramData"); // C:\ProgramData + addEnvVar(L"ProgramFiles"); // C:\Program Files + addEnvVar(L"ProgramFiles(x86)");// C:\Program Files (x86) + addEnvVar(L"CommonProgramFiles"); // C:\Program Files\Common Files + addEnvVar(L"CommonProgramFiles(x86)"); // C:\Program Files (x86)\Common Files + addEnvVar(L"Public"); // C:\Users\Public + addEnvVar(L"UserProfile"); // C:\Users\ + addEnvVar(L"WinDir"); // C:\Windows + addEnvVar(L"Temp"); // C:\Windows\Temp + + //add CSIDL values: http://msdn.microsoft.com/en-us/library/bb762494(v=vs.85).aspx + const auto& csidlMap = CsidlConstants::get(); + envToDir.insert(csidlMap.begin(), csidlMap.end()); + +#elif defined ZEN_LINUX || defined ZEN_MAC + addEnvVar("HOME"); //Linux: /home/ Mac: /Users/ +#endif + //substitute paths by symbolic names + auto pathStartsWith = [](const Zstring& path, const Zstring& prefix) -> bool + { +#if defined ZEN_WIN || defined ZEN_MAC + Zstring tmp = path; + Zstring tmp2 = prefix; + ::makeUpper(tmp); + ::makeUpper(tmp2); + return startsWith(tmp, tmp2); +#elif defined ZEN_LINUX + return startsWith(path, prefix); +#endif + }; + for (const auto& entry : envToDir) + if (pathStartsWith(dirname, entry.second)) + output.insert(MACRO_SEP + entry.first + MACRO_SEP + (dirname.c_str() + entry.second.size())); + } + + //4. replace (all) macros: %USERPROFILE% -> C:\Users\ + { + Zstring testMacros = expandMacros(dirname); + if (testMacros != dirname) + if (output.insert(testMacros).second) + getDirectoryAliasesRecursive(testMacros, output); //recurse! + } +} + + +std::vector zen::getDirectoryAliases(const Zstring& dirString) +{ + Zstring dirname = dirString; + trim(dirname, true, false); + if (dirname.empty()) + return std::vector(); + + std::set tmp; + getDirectoryAliasesRecursive(dirname, tmp); + + tmp.erase(dirname); + tmp.erase(Zstring()); + + return std::vector(tmp.begin(), tmp.end()); +} + + +Zstring zen::getFormattedDirectoryName(const Zstring& dirString) // throw() +{ + //formatting is needed since functions expect the directory to end with '\' to be able to split the relative names. + + Zstring dirname = dirString; + + //remove leading/trailing whitespace before allowing misinterpretation in applyLongPathPrefix() + trim(dirname, true, false); + while (endsWith(dirname, Zstr(' '))) //don't remove all whitespace from right, e.g. 0xa0 may be used as part of dir name + dirname.resize(dirname.size() - 1); + + if (dirname.empty()) //an empty string would later be resolved as "\"; this is not desired + return Zstring(); + + dirname = expandMacros(dirname); + dirname = expandVolumeName(dirname); //may block for slow USB sticks! + + /* + need to resolve relative paths: + WINDOWS: + - \\?\-prefix which needs absolute names + - Volume Shadow Copy: volume name needs to be part of each filename + - file icon buffer (at least for extensions that are actually read from disk, like "exe") + - ::SHFileOperation(): Using relative path names is not thread safe + WINDOWS/LINUX: + - detection of dependent directories, e.g. "\" and "C:\test" + */ + dirname = resolveRelativePath(dirname); + + return appendSeparator(dirname); +} + + +#ifdef ZEN_WIN +void zen::loginNetworkShare(const Zstring& dirnameOrig, bool allowUserInteraction) //throw() - user interaction: show OS password prompt +{ + /* + ATTENTION: it is not safe to retrieve UNC path via ::WNetGetConnection() for every type of network share: + + network type |::WNetGetConnection rv | lpRemoteName | existing UNC path + -----------------------------|-------------------------|---------------------------------|---------------- + inactive local network share | ERROR_CONNECTION_UNAVAIL| \\192.168.1.27\new2 | YES + WebDrive | NO_ERROR | \\Webdrive-ZenJu\GNU | NO + Box.net (WebDav) | NO_ERROR | \\www.box.net\DavWWWRoot\dav | YES + NetDrive | ERROR_NOT_CONNECTED | | NO + ____________________________________________________________________________________________________________ + + Windows Login Prompt Naming Conventions: + user account: \ e.g. WIN-XP\ZenJu + network share: \\\ e.g. \\WIN-XP\test + + Windows Command Line: + - list *all* active network connections, including deviceless ones which are hidden in Explorer: + net use + - delete active connection: + net use /delete \\server\share + ____________________________________________________________________________________________________________ + + Scenario: XP-shared folder is accessed by Win 7 over LAN with access limited to a certain user + + Problems: + I. WNetAddConnection2() allows (at least certain) invalid credentials (e.g. username: a/password: a) and establishes an *unusable* connection + II. WNetAddConnection2() refuses to overwrite an existing (unusable) connection created in I), but shows prompt repeatedly + III. WNetAddConnection2() won't bring up the prompt if *wrong* credentials had been entered just recently, even with CONNECT_INTERACTIVE specified! => 2-step proccess + */ + + auto connect = [&](NETRESOURCE& trgRes) //blocks heavily if network is not reachable!!! + { + //1. first try to connect without user interaction - blocks! + DWORD rv = ::WNetAddConnection2(&trgRes, // __in LPNETRESOURCE lpNetResource, + nullptr, // __in LPCTSTR lpPassword, + nullptr, // __in LPCTSTR lpUsername, + 0); //__in DWORD dwFlags + //53L ERROR_BAD_NETPATH The network path was not found. + //86L ERROR_INVALID_PASSWORD + //1219L ERROR_SESSION_CREDENTIAL_CONFLICT Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again. + //1326L ERROR_LOGON_FAILURE Logon failure: unknown user name or bad password. + //1236L ERROR_CONNECTION_ABORTED + if (somethingExists(trgRes.lpRemoteName)) //blocks! + return; //success: connection usable! -> don't care about "rv" + + if (rv == ERROR_BAD_NETPATH || //Windows 7 + rv == ERROR_BAD_NET_NAME|| //XP + rv == ERROR_CONNECTION_ABORTED) //failed to connect to a network that existed not too long ago; will later return ERROR_BAD_NETPATH + return; //no need to show a prompt for an unreachable network device + + //2. if first attempt failed, we need to *force* prompt by using CONNECT_PROMPT + if (allowUserInteraction) + { + //avoid problem II.) + DWORD rv2= ::WNetCancelConnection2(trgRes.lpRemoteName, //_In_ LPCTSTR lpName, + 0, //_In_ DWORD dwFlags, + true); //_In_ BOOL fForce + //2250L ERROR_NOT_CONNECTED + + //enforce login prompt + DWORD rv3 = ::WNetAddConnection2(&trgRes, // __in LPNETRESOURCE lpNetResource, + nullptr, // __in LPCTSTR lpPassword, + nullptr, // __in LPCTSTR lpUsername, + CONNECT_INTERACTIVE | CONNECT_PROMPT); //__in DWORD dwFlags + (void)rv2; + (void)rv3; + } + }; + + + Zstring dirname = removeLongPathPrefix(dirnameOrig); + trim(dirname, true, false); + + //1. locally mapped network share + if (dirname.size() >= 2 && iswalpha(dirname[0]) && dirname[1] == L':') + { + Zstring driveLetter(dirname.c_str(), 2); //e.g.: "Q:" + { + DWORD bufferSize = 10000; + std::vector remoteNameBuffer(bufferSize); + + //map local -> remote + + //note: following function call does NOT block! + DWORD rv = ::WNetGetConnection(driveLetter.c_str(), //__in LPCTSTR lpLocalName in the form ":" + &remoteNameBuffer[0], //__out LPTSTR lpRemoteName, + &bufferSize); //__inout LPDWORD lpnLength + if (rv == ERROR_CONNECTION_UNAVAIL) //remoteNameBuffer will be filled nevertheless! + { + //ERROR_CONNECTION_UNAVAIL: network mapping is existing, but not connected + + Zstring networkShare = &remoteNameBuffer[0]; + if (!networkShare.empty()) + { + NETRESOURCE trgRes = {}; + trgRes.dwType = RESOURCETYPE_DISK; + trgRes.lpLocalName = const_cast(driveLetter.c_str()); //lpNetResource is marked "__in", seems WNetAddConnection2 is not const correct! + trgRes.lpRemoteName = const_cast(networkShare.c_str()); // + + connect(trgRes); //blocks! + } + } + } + } + //2. deviceless network connection + else if (startsWith(dirname, L"\\\\")) //UNC path + { + const Zstring networkShare = [&]() -> Zstring //extract prefix "\\server\share" + { + size_t pos = dirname.find('\\', 2); + if (pos == Zstring::npos) + return Zstring(); + pos = dirname.find('\\', pos + 1); + return pos == Zstring::npos ? dirname : Zstring(dirname.c_str(), pos); + }(); + + if (!networkShare.empty()) + { + //::WNetGetResourceInformation seems to fail with ERROR_BAD_NET_NAME even for existing unconnected network shares! + // => unconditionally try to connect to network share, seems we cannot reliably detect connection status otherwise + + NETRESOURCE trgRes = {}; + trgRes.dwType = RESOURCETYPE_DISK; + trgRes.lpRemoteName = const_cast(networkShare.c_str()); //trgRes is "__in" + + connect(trgRes); //blocks! + } + } +} +#endif diff --git a/FreeFileSync/Source/lib/resolve_path.h b/FreeFileSync/Source/lib/resolve_path.h new file mode 100644 index 00000000..b9c7196f --- /dev/null +++ b/FreeFileSync/Source/lib/resolve_path.h @@ -0,0 +1,36 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef RESOLVE_PATH_H_INCLUDED +#define RESOLVE_PATH_H_INCLUDED + +#include +#include + +namespace zen +{ +/* +FULL directory format: + - expand macros + - expand volume path by name + - convert relative paths into absolute + - trim whitespace and append file name separator +*/ +Zstring getFormattedDirectoryName(const Zstring& dirString); //throw() - may still block for slow USB sticks! not thread-safe!!!(see ::GetFullPathName()) + +//macro substitution only +Zstring expandMacros(const Zstring& text); + +std::vector getDirectoryAliases(const Zstring& dirString); //may block for slow USB sticks when resolving [] + +#ifdef ZEN_WIN +//*blocks* if network is not reachable or when showing login prompt dialog! +void loginNetworkShare(const Zstring& dirname, bool allowUserInteraction); //throw() - user interaction: show OS password prompt +#endif +} + + +#endif // RESOLVE_PATH_H_INCLUDED diff --git a/FreeFileSync/Source/lib/return_codes.h b/FreeFileSync/Source/lib/return_codes.h new file mode 100644 index 00000000..a37e11f2 --- /dev/null +++ b/FreeFileSync/Source/lib/return_codes.h @@ -0,0 +1,30 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef RETURN_CODES_H_INCLUDED +#define RETURN_CODES_H_INCLUDED + +namespace zen +{ +enum FfsReturnCode +{ + FFS_RC_SUCCESS = 0, + FFS_RC_FINISHED_WITH_WARNINGS, + FFS_RC_FINISHED_WITH_ERRORS, + FFS_RC_ABORTED, + FFS_RC_EXCEPTION, +}; + + +inline +void raiseReturnCode(FfsReturnCode& rc, FfsReturnCode rcProposed) +{ + if (rc < rcProposed) + rc = rcProposed; +} +} + +#endif // RETURN_CODES_H_INCLUDED diff --git a/FreeFileSync/Source/lib/shadow.cpp b/FreeFileSync/Source/lib/shadow.cpp new file mode 100644 index 00000000..8a30bd3f --- /dev/null +++ b/FreeFileSync/Source/lib/shadow.cpp @@ -0,0 +1,131 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "shadow.h" +#include +#include //includes "windows.h" +#include +#include +#include +#include +#include +#include "../dll/ShadowCopy/shadow.h" +#include + +using namespace zen; +using namespace shadow; + + +namespace +{ +bool runningWOW64() //test if process is running under WOW64 (reference http://msdn.microsoft.com/en-us/library/ms684139(VS.85).aspx) +{ + typedef BOOL (WINAPI* IsWow64ProcessFun)(HANDLE hProcess, PBOOL Wow64Process); + + const SysDllFun isWow64Process(L"kernel32.dll", "IsWow64Process"); + if (isWow64Process) + { + BOOL isWow64 = FALSE; + if (isWow64Process(::GetCurrentProcess(), &isWow64)) + return isWow64 != FALSE; + } + return false; +} + +const bool wereVistaOrLater = vistaOrLater(); //thread-safety: init at startup +} + +//############################################################################################################# + +class ShadowCopy::ShadowVolume +{ +public: + ShadowVolume(const Zstring& volumeNamePf) : //throw FileError + createShadowCopy (getDllName(), funName_createShadowCopy), + releaseShadowCopy(getDllName(), funName_releaseShadowCopy), + getShadowVolume (getDllName(), funName_getShadowVolume), + getLastError (getDllName(), funName_getLastError), + backupHandle(nullptr) + { + //VSS does not support running under WOW64 except for Windows XP and Windows Server 2003 + //reference: http://msdn.microsoft.com/en-us/library/aa384627(VS.85).aspx + if (runningWOW64()) + throw FileError(_("Cannot access the Volume Shadow Copy Service."), + _("Please use FreeFileSync 64-bit version to create shadow copies on this system.")); + + //check if shadow copy dll was loaded correctly + if (!createShadowCopy || !releaseShadowCopy || !getShadowVolume || !getLastError) + throw FileError(_("Cannot access the Volume Shadow Copy Service."), + replaceCpy(_("Cannot load file %x."), L"%x", fmtFileName(getDllName()))); + + //--------------------------------------------------------------------------------------------------------- + //start volume shadow copy service: + backupHandle = createShadowCopy(volumeNamePf.c_str()); + if (!backupHandle) + throw FileError(_("Cannot access the Volume Shadow Copy Service."), + getLastError() + std::wstring(L" Volume: ") + fmtFileName(volumeNamePf)); + + shadowVolPf = appendSeparator(getShadowVolume(backupHandle)); //shadowVolName NEVER has a trailing backslash + } + + ~ShadowVolume() { releaseShadowCopy(backupHandle); } //fast! no performance optimization necessary + + Zstring geNamePf() const { return shadowVolPf; } //with trailing path separator + +private: + ShadowVolume(const ShadowVolume&); + ShadowVolume& operator=(const ShadowVolume&); + + const DllFun createShadowCopy; + const DllFun releaseShadowCopy; + const DllFun getShadowVolume; + const DllFun getLastError; + + Zstring shadowVolPf; + ShadowHandle backupHandle; +}; + +//############################################################################################################# + +Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile, const std::function& onBeforeMakeVolumeCopy) +{ + Zstring filenameFinal = inputFile; + + //try to resolve symlinks and junctions: + //1. symlinks: we need to retrieve the target path, else we would just return a symlink on a VSS volume while the target outside were still locked! + //2. junctions: C:\Users\ is a junction that may link to e.g. D:\Users\, so GetVolumePathName() returns "D:\" => "Volume name %x not part of file name %y." + if (wereVistaOrLater) + filenameFinal = getResolvedFilePath(inputFile); //throw FileError; requires Vista or later! + //-> returns paths with \\?\ prefix! => make sure to avoid duplicate shadow copies for volume paths with/without prefix + + DWORD bufferSize = 10000; + std::vector volBuffer(bufferSize); + if (!::GetVolumePathName(filenameFinal.c_str(), //__in LPCTSTR lpszFileName, + &volBuffer[0], //__out LPTSTR lpszVolumePathName, + bufferSize)) //__in DWORD cchBufferLength + throw FileError(replaceCpy(_("Cannot determine volume name for %x."), L"%x", fmtFileName(filenameFinal)), formatSystemError(L"GetVolumePathName", ::GetLastError())); + + const Zstring volumeNamePf = appendSeparator(&volBuffer[0]); //msdn: if buffer is 1 char too short, GetVolumePathName() may skip last separator without error! + + //input file is always absolute! directory formatting takes care of this! Therefore volume name can always be found. + const size_t pos = filenameFinal.find(volumeNamePf); //filenameFinal needs NOT to begin with volumeNamePf: consider \\?\ prefix! + if (pos == Zstring::npos) + throw FileError(replaceCpy(replaceCpy(_("Volume name %x is not part of file path %y."), + L"%x", fmtFileName(volumeNamePf)), + L"%y", fmtFileName(filenameFinal))); + + //get or create instance of shadow volume + auto it = shadowVol.find(volumeNamePf); + if (it == shadowVol.end()) + { + onBeforeMakeVolumeCopy(volumeNamePf); //notify client before (potentially) blocking some time + auto newEntry = std::make_shared(volumeNamePf); //throw FileError + it = shadowVol.insert(std::make_pair(volumeNamePf, newEntry)).first; + } + + //return filename alias on shadow copy volume + return it->second->geNamePf() + Zstring(filenameFinal.c_str() + pos + volumeNamePf.length()); +} diff --git a/FreeFileSync/Source/lib/shadow.h b/FreeFileSync/Source/lib/shadow.h new file mode 100644 index 00000000..36c72c25 --- /dev/null +++ b/FreeFileSync/Source/lib/shadow.h @@ -0,0 +1,36 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef SHADOW_H_INCLUDED +#define SHADOW_H_INCLUDED + +#include +#include +#include +#include +#include + +namespace shadow +{ +class ShadowCopy //take and buffer Windows Volume Shadow Copy snapshots as needed +{ +public: + ShadowCopy() {} + + //return filename on shadow copy volume - follows symlinks! + Zstring makeShadowCopy(const Zstring& inputFile, const std::function& onBeforeMakeVolumeCopy); //throw FileError + +private: + ShadowCopy(const ShadowCopy&); + ShadowCopy& operator=(const ShadowCopy&); + + class ShadowVolume; + typedef std::map, LessFilename> VolNameShadowMap; + VolNameShadowMap shadowVol; +}; +} + +#endif //SHADOW_H_INCLUDED diff --git a/FreeFileSync/Source/lib/soft_filter.h b/FreeFileSync/Source/lib/soft_filter.h new file mode 100644 index 00000000..010a8913 --- /dev/null +++ b/FreeFileSync/Source/lib/soft_filter.h @@ -0,0 +1,112 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef SOFT_FILTER_H_INCLUDED +#define SOFT_FILTER_H_INCLUDED + +#include +#include +#include "../structures.h" +//#include + +namespace zen +{ +/* +Semantics of SoftFilter: +1. It potentially may match only one side => it MUST NOT be applied while traversing a single folder to avoid mismatches +2. => it is applied after traversing and just marks rows, (NO deletions after comparison are allowed) +3. => equivalent to a user temporarily (de-)selecting rows => not relevant for -mode! +*/ + +class SoftFilter +{ +public: + SoftFilter(size_t timeSpan, UnitTime unitTimeSpan, + size_t sizeMin, UnitSize unitSizeMin, + size_t sizeMax, UnitSize unitSizeMax); + + bool matchTime(Int64 writeTime) const { return timeFrom_ <= writeTime; } + bool matchSize(UInt64 fileSize) const { return sizeMin_ <= fileSize && fileSize <= sizeMax_; } + bool matchFolder() const { return matchesFolder_; } + bool isNull() const; //filter is equivalent to NullFilter, but may be technically slower + + //small helper method: merge two soft filters + friend SoftFilter combineFilters(const SoftFilter& first, const SoftFilter& second); + +private: + SoftFilter(const Int64& timeFrom, + const UInt64& sizeMin, + const UInt64& sizeMax, + bool matchesFolder); + + Int64 timeFrom_; //unit: UTC, seconds + UInt64 sizeMin_; //unit: bytes + UInt64 sizeMax_; //unit: bytes + bool matchesFolder_; +}; +} + + + + + + + + + + + + + +// ----------------------- implementation ----------------------- +namespace zen +{ +inline +SoftFilter::SoftFilter(size_t timeSpan, UnitTime unitTimeSpan, + size_t sizeMin, UnitSize unitSizeMin, + size_t sizeMax, UnitSize unitSizeMax) : + matchesFolder_(unitTimeSpan == UTIME_NONE&& + unitSizeMin == USIZE_NONE&& + unitSizeMax == USIZE_NONE) //exclude folders if size or date filter is active: avoids creating empty folders if not needed! +{ + resolveUnits(timeSpan, unitTimeSpan, + sizeMin, unitSizeMin, + sizeMax, unitSizeMax, + timeFrom_, + sizeMin_, + sizeMax_); +} + +inline +SoftFilter::SoftFilter(const Int64& timeFrom, + const UInt64& sizeMin, + const UInt64& sizeMax, + bool matchesFolder) : + timeFrom_(timeFrom), + sizeMin_ (sizeMin), + sizeMax_ (sizeMax), + matchesFolder_(matchesFolder) {} + +inline +SoftFilter combineFilters(const SoftFilter& lhs, const SoftFilter& rhs) +{ + return SoftFilter(std::max(lhs.timeFrom_, rhs.timeFrom_), + std::max(lhs.sizeMin_, rhs.sizeMin_), + std::min(lhs.sizeMax_, rhs.sizeMax_), + lhs.matchesFolder_ && rhs.matchesFolder_); +} + +inline +bool SoftFilter::isNull() const //filter is equivalent to NullFilter, but may be technically slower +{ + return timeFrom_ == std::numeric_limits::min() && + sizeMin_ == 0U && + sizeMax_ == std::numeric_limits::max() && + matchesFolder_ == true;; +} +} + +#endif // SOFT_FILTER_H_INCLUDED diff --git a/FreeFileSync/Source/lib/status_handler.cpp b/FreeFileSync/Source/lib/status_handler.cpp new file mode 100644 index 00000000..5fb80161 --- /dev/null +++ b/FreeFileSync/Source/lib/status_handler.cpp @@ -0,0 +1,29 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "status_handler.h" +//#include +#include + +using namespace zen; + + +namespace +{ +const std::int64_t TICKS_UPDATE_INTERVAL = UI_UPDATE_INTERVAL* ticksPerSec() / 1000; +TickVal lastExec = getTicks(); +}; + +bool zen::updateUiIsAllowed() +{ + const TickVal now = getTicks(); //0 on error + if (dist(lastExec, now) >= TICKS_UPDATE_INTERVAL) //perform ui updates not more often than necessary + { + lastExec = now; + return true; + } + return false; +} diff --git a/FreeFileSync/Source/lib/status_handler.h b/FreeFileSync/Source/lib/status_handler.h new file mode 100644 index 00000000..baf772d3 --- /dev/null +++ b/FreeFileSync/Source/lib/status_handler.h @@ -0,0 +1,144 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef STATUSHANDLER_H_INCLUDED +#define STATUSHANDLER_H_INCLUDED + +#include "../process_callback.h" +#include +#include +#include +#include + +namespace zen +{ +bool updateUiIsAllowed(); //test if a specific amount of time is over + +/* +Updating GUI is fast! + time per single call to ProcessCallback::forceUiRefresh() + - Comparison 0.025 ms + - Synchronization 0.74 ms (despite complex graph control!) +*/ + +//gui may want to abort process +struct AbortCallback +{ + virtual ~AbortCallback() {} + virtual void requestAbortion() = 0; +}; + + +//common statistics "everybody" needs +struct Statistics +{ + virtual ~Statistics() {} + + virtual ProcessCallback::Phase currentPhase() const = 0; + + virtual int getObjectsCurrent(ProcessCallback::Phase phaseId) const = 0; + virtual int getObjectsTotal (ProcessCallback::Phase phaseId) const = 0; + + virtual Int64 getDataCurrent(ProcessCallback::Phase phaseId) const = 0; + virtual Int64 getDataTotal (ProcessCallback::Phase phaseId) const = 0; + + virtual const std::wstring& currentStatusText() const = 0; +}; + + +//partial callback implementation with common functionality for "batch", "GUI/Compare" and "GUI/Sync" +class StatusHandler : public ProcessCallback, public AbortCallback, public Statistics +{ +public: + StatusHandler() : currentPhase_(PHASE_NONE), + numbersCurrent_(4), //init with phase count + numbersTotal_ (4), // + abortRequested(false) {} + +protected: + //implement parts of ProcessCallback + virtual void initNewPhase(int objectsTotal, Int64 dataTotal, Phase phaseId) + { + currentPhase_ = phaseId; + refNumbers(numbersTotal_, currentPhase_) = std::make_pair(objectsTotal, dataTotal); + } + + virtual void updateProcessedData(int objectsDelta, Int64 dataDelta) { updateData(numbersCurrent_, objectsDelta, dataDelta); } //note: these methods MUST NOT throw in order + virtual void updateTotalData (int objectsDelta, Int64 dataDelta) { updateData(numbersTotal_ , objectsDelta, dataDelta); } //to properly allow undoing setting of statistics! + + virtual void requestUiRefresh() //throw X + { + if (abortRequested) //triggered by requestAbortion() + { + forceUiRefresh(); + abortProcessNow(); //throw X + } + else if (updateUiIsAllowed()) //test if specific time span between ui updates is over + forceUiRefresh(); + } + + virtual void reportStatus(const std::wstring& text) { statusText_ = text; requestUiRefresh(); /*throw X */ } + virtual void reportInfo (const std::wstring& text) { statusText_ = text; requestUiRefresh(); /*throw X */ } //log text in derived class + + //implement AbortCallback + virtual void requestAbortion() + { + abortRequested = true; + statusText_ = _("Stop requested: Waiting for current operation to finish..."); + } //this does NOT call abortProcessNow() immediately, but when we're out of the C GUI call stack + + //implement Statistics + virtual Phase currentPhase() const { return currentPhase_; } + + virtual int getObjectsCurrent(Phase phaseId) const { return refNumbers(numbersCurrent_, phaseId).first; } + virtual int getObjectsTotal (Phase phaseId) const { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersTotal_ , phaseId).first; } + + virtual Int64 getDataCurrent(Phase phaseId) const { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersCurrent_, phaseId).second; } + virtual Int64 getDataTotal (Phase phaseId) const { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersTotal_ , phaseId).second; } + + virtual const std::wstring& currentStatusText() const { return statusText_; } + + bool abortIsRequested() const { return abortRequested; } + +private: + typedef std::vector> StatNumbers; + + void updateData(StatNumbers& num, int objectsDelta, Int64 dataDelta) + { + auto& st = refNumbers(num, currentPhase_); + st.first += objectsDelta; + st.second += dataDelta; + } + + static const std::pair& refNumbers(const StatNumbers& num, Phase phaseId) + { + switch (phaseId) + { + case PHASE_SCANNING: + return num[0]; + case PHASE_COMPARING_CONTENT: + return num[1]; + case PHASE_SYNCHRONIZING: + return num[2]; + case PHASE_NONE: + break; + } + assert(false); + return num[3]; //dummy entry! + } + + static std::pair& refNumbers(StatNumbers& num, Phase phaseId) { return const_cast&>(refNumbers(static_cast(num), phaseId)); } + + Phase currentPhase_; + StatNumbers numbersCurrent_; + StatNumbers numbersTotal_; + std::wstring statusText_; + + bool abortRequested; +}; +} + +#endif // STATUSHANDLER_H_INCLUDED diff --git a/FreeFileSync/Source/lib/status_handler_impl.h b/FreeFileSync/Source/lib/status_handler_impl.h new file mode 100644 index 00000000..6b364ba3 --- /dev/null +++ b/FreeFileSync/Source/lib/status_handler_impl.h @@ -0,0 +1,37 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef STATUSHANDLER_IMPL_H_INCLUDED +#define STATUSHANDLER_IMPL_H_INCLUDED + +#include +#include +#include "../process_callback.h" + + +template inline +zen::Opt tryReportingError(Function cmd, ProcessCallback& handler) //return ignored error message if available +{ + for (size_t retryNumber = 0;; ++retryNumber) + try + { + cmd(); //throw FileError + return zen::NoValue(); + } + catch (zen::FileError& error) + { + switch (handler.reportError(error.toString(), retryNumber)) //may throw! + { + case ProcessCallback::IGNORE_ERROR: + return error.toString(); + case ProcessCallback::RETRY: + break; //continue with loop + } + } +} + + +#endif //STATUSHANDLER_IMPL_H_INCLUDED diff --git a/FreeFileSync/Source/lib/versioning.cpp b/FreeFileSync/Source/lib/versioning.cpp new file mode 100644 index 00000000..82696e3a --- /dev/null +++ b/FreeFileSync/Source/lib/versioning.cpp @@ -0,0 +1,439 @@ +#include "versioning.h" +#include +#include +#include +#include + +using namespace zen; + + +namespace +{ +Zstring getExtension(const Zstring& relativeName) //including "." if extension is existing, returns empty string otherwise +{ + auto iterSep = find_last(relativeName.begin(), relativeName.end(), FILE_NAME_SEPARATOR); + auto iterName = iterSep != relativeName.end() ? iterSep + 1 : relativeName.begin(); //find beginning of short name + auto iterDot = find_last(iterName, relativeName.end(), Zstr('.')); //equal to relativeName.end() if file has no extension!! + return Zstring(&*iterDot, relativeName.end() - iterDot); +}; +} + +bool impl::isMatchingVersion(const Zstring& shortname, const Zstring& shortnameVersion) //e.g. ("Sample.txt", "Sample.txt 2012-05-15 131513.txt") +{ + auto it = shortnameVersion.begin(); + auto last = shortnameVersion.end(); + + auto nextDigit = [&]() -> bool + { + if (it == last || !isDigit(*it)) + return false; + ++it; + return true; + }; + auto nextDigits = [&](size_t count) -> bool + { + while (count-- > 0) + if (!nextDigit()) + return false; + return true; + }; + auto nextChar = [&](Zchar c) -> bool + { + if (it == last || *it != c) + return false; + ++it; + return true; + }; + auto nextStringI = [&](const Zstring& str) -> bool //windows: ignore case! + { + if (last - it < static_cast(str.size()) || !EqualFilename()(str, Zstring(&*it, str.size()))) + return false; + it += str.size(); + return true; + }; + + return nextStringI(shortname) && //versioned file starts with original name + nextChar(Zstr(' ')) && //validate timestamp: e.g. "2012-05-15 131513"; Regex: \d{4}-\d{2}-\d{2} \d{6} + nextDigits(4) && //YYYY + nextChar(Zstr('-')) && // + nextDigits(2) && //MM + nextChar(Zstr('-')) && // + nextDigits(2) && //DD + nextChar(Zstr(' ')) && // + nextDigits(6) && //HHMMSS + nextStringI(getExtension(shortname)) && + it == last; +} + + +namespace +{ +/* +- handle not existing source +- create target super directories if missing +*/ +template +void moveItemToVersioning(const Zstring& fullName, //throw FileError + const Zstring& relativeName, + const Zstring& versioningDirectory, + const Zstring& timestamp, + VersioningStyle versioningStyle, + Function moveObj) //move source -> target; may throw FileError +{ + assert(!startsWith(relativeName, FILE_NAME_SEPARATOR)); + assert(!endsWith (relativeName, FILE_NAME_SEPARATOR)); + + Zstring targetName; + switch (versioningStyle) + { + case VER_STYLE_REPLACE: + targetName = appendSeparator(versioningDirectory) + relativeName; + break; + + case VER_STYLE_ADD_TIMESTAMP: + //assemble time-stamped version name + targetName = appendSeparator(versioningDirectory) + relativeName + Zstr(' ') + timestamp + getExtension(relativeName); + assert(impl::isMatchingVersion(afterLast(relativeName, FILE_NAME_SEPARATOR), afterLast(targetName, FILE_NAME_SEPARATOR))); //paranoid? no! + break; + } + + try + { + moveObj(fullName, targetName); //throw FileError + } + catch (FileError&) //expected to fail if target directory is not yet existing! + { + if (!somethingExists(fullName)) //no source at all is not an error (however a directory as source when a file is expected, *is* an error!) + return; //object *not* processed + + //create intermediate directories if missing + const Zstring targetDir = beforeLast(targetName, FILE_NAME_SEPARATOR); + if (!dirExists(targetDir)) //->(minor) file system race condition! + { + makeDirectory(targetDir); //throw FileError + moveObj(fullName, targetName); //throw FileError -> this should work now! + } + else + throw; + } +} + + +//move source to target across volumes +//no need to check if: - super-directories of target exist - source exists: done by moveItemToVersioning() +//if target already exists, it is overwritten, even if it is a different type, e.g. a directory! +template +void moveObject(const Zstring& sourceFile, //throw FileError + const Zstring& targetFile, + Function copyDelete) //throw FileError; fallback if move failed +{ + assert(fileExists(sourceFile) || symlinkExists(sourceFile) || !somethingExists(sourceFile)); //we process files and symlinks only + + auto removeTarget = [&] + { + //remove target object + if (dirExists(targetFile)) //directory or dir-symlink + removeDirectory(targetFile); //throw FileError; we do not expect targetFile to be a directory in general => no callback required + else //file or (broken) file-symlink + removeFile(targetFile); //throw FileError + }; + + //first try to move directly without copying + try + { + renameFile(sourceFile, targetFile); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting + return; //great, we get away cheaply! + } + //if moving failed treat as error (except when it tried to move to a different volume: in this case we will copy the file) + catch (const ErrorDifferentVolume&) + { + removeTarget(); //throw FileError + copyDelete(); // + } + catch (const ErrorTargetExisting&) + { + removeTarget(); //throw FileError + try + { + renameFile(sourceFile, targetFile); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting + } + catch (const ErrorDifferentVolume&) + { + copyDelete(); //throw FileError + } + } +} + + +void moveFile(const Zstring& sourceFile, const Zstring& targetFile, CallbackCopyFile& callback) //throw FileError +{ + moveObject(sourceFile, //throw FileError + targetFile, + [&] + { + //create target + if (symlinkExists(sourceFile)) + copySymlink(sourceFile, targetFile, false); //throw FileError; don't copy filesystem permissions + else + copyFile(sourceFile, targetFile, false, true, &callback); //throw FileError - permissions "false", transactional copy "true" + + //delete source + removeFile(sourceFile); //throw FileError; newly copied file is NOT deleted if exception is thrown here! + }); +} + + +void moveDirSymlink(const Zstring& sourceLink, const Zstring& targetLink) //throw FileError +{ + moveObject(sourceLink, //throw FileError + targetLink, + [&] + { + //create target + copySymlink(sourceLink, targetLink, false); //throw FileError; don't copy filesystem permissions + + //delete source + removeDirectory(sourceLink); //throw FileError; newly copied link is NOT deleted if exception is thrown here! + }); +} + + +class TraverseFilesOneLevel : public TraverseCallback +{ +public: + TraverseFilesOneLevel(std::vector& files, std::vector& dirs) : files_(files), dirs_(dirs) {} + +private: + virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) + { + files_.push_back(shortName); + } + + virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) + { + if (dirExists(fullName)) //dir symlink + dirs_.push_back(shortName); + else //file symlink, broken symlink + files_.push_back(shortName); + return LINK_SKIP; + } + + virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& fullName) + { + dirs_.push_back(shortName); + return nullptr; //DON'T traverse into subdirs; moveDirectory works recursively! + } + + virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) { throw FileError(msg); } + virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) { throw FileError(msg); } + + std::vector& files_; + std::vector& dirs_; +}; +} + + +bool FileVersioner::revisionFile(const Zstring& fullName, const Zstring& relativeName, CallbackMoveFile& callback) //throw FileError +{ + struct CallbackMoveFileImpl : public CallbackMoveDir + { + CallbackMoveFileImpl(CallbackMoveFile& callback) : callback_(callback) {} + private: + virtual void onBeforeFileMove(const Zstring& fileFrom, const Zstring& fileTo) {} + virtual void onBeforeDirMove (const Zstring& dirFrom, const Zstring& dirTo ) {} + virtual void updateStatus(Int64 bytesDelta) { callback_.updateStatus(bytesDelta); } + CallbackMoveFile& callback_; + } cb(callback); + + return revisionFileImpl(fullName, relativeName, cb); //throw FileError +} + + +bool FileVersioner::revisionFileImpl(const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError +{ + bool moveSuccessful = false; + + moveItemToVersioning(fullName, //throw FileError + relativeName, + versioningDirectory_, + timeStamp_, + versioningStyle_, + [&](const Zstring& source, const Zstring& target) + { + callback.onBeforeFileMove(source, target); //if we're called by revisionDirImpl() we know that "source" exists! + //when called by revisionFile(), "source" might not exist, however onBeforeFileMove() is not propagated in this case! + + struct CopyCallbackImpl : public CallbackCopyFile + { + CopyCallbackImpl(CallbackMoveDir& callback) : callback_(callback) {} + private: + virtual void deleteTargetFile(const Zstring& targetFile) { assert(!somethingExists(targetFile)); } + virtual void updateCopyStatus(Int64 bytesDelta) { callback_.updateStatus(bytesDelta); } + CallbackMoveDir& callback_; + } copyCallback(callback); + + moveFile(source, target, copyCallback); //throw FileError + moveSuccessful = true; + }); + return moveSuccessful; +} + + +void FileVersioner::revisionDir(const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError +{ + //no error situation if directory is not existing! manual deletion relies on it! + if (!somethingExists(fullName)) + return; //neither directory nor any other object (e.g. broken symlink) with that name existing + revisionDirImpl(fullName, relativeName, callback); //throw FileError +} + + +void FileVersioner::revisionDirImpl(const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError +{ + assert(somethingExists(fullName)); //[!] + + //create target + if (symlinkExists(fullName)) //on Linux there is just one type of symlink, and since we do revision file symlinks, we should revision dir symlinks as well! + { + moveItemToVersioning(fullName, //throw FileError + relativeName, + versioningDirectory_, + timeStamp_, + versioningStyle_, + [&](const Zstring& source, const Zstring& target) + { + callback.onBeforeDirMove(source, target); + moveDirSymlink(source, target); //throw FileError + }); + } + else + { + assert(!startsWith(relativeName, FILE_NAME_SEPARATOR)); + assert(endsWith(fullName, relativeName)); //usually, yes, but we might relax this in the future + const Zstring targetDir = appendSeparator(versioningDirectory_) + relativeName; + + //makeDirectory(targetDir); //FileError -> create only when needed in moveFileToVersioning(); avoids empty directories + + //traverse source directory one level + std::vector fileList; //list of *short* names + std::vector dirList; // + { + TraverseFilesOneLevel tol(fileList, dirList); //throw FileError + traverseFolder(fullName, tol); // + } + + const Zstring fullNamePf = appendSeparator(fullName); + const Zstring relnamePf = appendSeparator(relativeName); + + //move files + std::for_each(fileList.begin(), fileList.end(), + [&](const Zstring& shortname) + { + revisionFileImpl(fullNamePf + shortname, //throw FileError + relnamePf + shortname, + callback); + }); + + //move items in subdirectories + std::for_each(dirList.begin(), dirList.end(), + [&](const Zstring& shortname) + { + revisionDirImpl(fullNamePf + shortname, //throw FileError + relnamePf + shortname, + callback); + }); + + //delete source + callback.onBeforeDirMove(fullName, targetDir); + removeDirectory(fullName); //throw FileError + } +} + + +/* +namespace +{ +class TraverseVersionsOneLevel : public TraverseCallback +{ +public: + TraverseVersionsOneLevel(std::vector& files, std::function updateUI) : files_(files), updateUI_(updateUI) {} + +private: + virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) { files_.push_back(shortName); updateUI_(); } + virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) { files_.push_back(shortName); updateUI_(); return LINK_SKIP; } + virtual std::shared_ptr onDir(const Zchar* shortName, const Zstring& fullName) { updateUI_(); return nullptr; } //DON'T traverse into subdirs + virtual HandleError reportDirError (const std::wstring& msg) { throw FileError(msg); } + virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) { throw FileError(msg); } + + std::vector& files_; + std::function updateUI_; +}; +} + +void FileVersioner::limitVersions(std::function updateUI) //throw FileError +{ + if (versionCountLimit_ < 0) //no limit! + return; + + //buffer map "directory |-> list of immediate child file and symlink short names" + std::map, LessFilename> dirBuffer; + + auto getVersionsBuffered = [&](const Zstring& dirname) -> const std::vector& + { + auto it = dirBuffer.find(dirname); + if (it != dirBuffer.end()) + return it->second; + + std::vector fileShortNames; + TraverseVersionsOneLevel tol(fileShortNames, updateUI); //throw FileError + traverseFolder(dirname, tol); + + auto& newEntry = dirBuffer[dirname]; //transactional behavior!!! + newEntry.swap(fileShortNames); //-> until C++11 emplace is available + + return newEntry; + }; + + std::for_each(fileRelNames.begin(), fileRelNames.end(), + [&](const Zstring& relativeName) //e.g. "subdir\Sample.txt" + { + const Zstring fullname = appendSeparator(versioningDirectory_) + relativeName; //e.g. "D:\Revisions\subdir\Sample.txt" + const Zstring parentDir = beforeLast(fullname, FILE_NAME_SEPARATOR); //e.g. "D:\Revisions\subdir" + const Zstring shortname = afterLast(relativeName, FILE_NAME_SEPARATOR); //e.g. "Sample.txt"; returns the whole string if seperator not found + + const std::vector& allVersions = getVersionsBuffered(parentDir); + + //filter out only those versions that match the given relative name + std::vector matches; //e.g. "Sample.txt 2012-05-15 131513.txt" + + std::copy_if(allVersions.begin(), allVersions.end(), std::back_inserter(matches), + [&](const Zstring& shortnameVer) { return impl::isMatchingVersion(shortname, shortnameVer); }); + + //take advantage of version naming convention to find oldest versions + if (matches.size() <= static_cast(versionCountLimit_)) + return; + std::nth_element(matches.begin(), matches.end() - versionCountLimit_, matches.end(), LessFilename()); //windows: ignore case! + + //delete obsolete versions + std::for_each(matches.begin(), matches.end() - versionCountLimit_, + [&](const Zstring& shortnameVer) + { + updateUI(); + const Zstring fullnameVer = parentDir + FILE_NAME_SEPARATOR + shortnameVer; + try + { + removeFile(fullnameVer); //throw FileError + } + catch (FileError&) + { +#ifdef ZEN_WIN //if it's a directory symlink: + if (symlinkExists(fullnameVer) && dirExists(fullnameVer)) + removeDirectory(fullnameVer); //throw FileError + else +#endif + throw; + } + }); + }); +} +*/ diff --git a/FreeFileSync/Source/lib/versioning.h b/FreeFileSync/Source/lib/versioning.h new file mode 100644 index 00000000..06065656 --- /dev/null +++ b/FreeFileSync/Source/lib/versioning.h @@ -0,0 +1,89 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef VERSIONING_HEADER_8760247652438056 +#define VERSIONING_HEADER_8760247652438056 + +#include +#include +#include +#include +#include +#include +#include "../structures.h" + +namespace zen +{ +struct CallbackMoveDir; +struct CallbackMoveFile; + +//e.g. move C:\Source\subdir\Sample.txt -> D:\Revisions\subdir\Sample.txt 2012-05-15 131513.txt +//scheme: \\. YYYY-MM-DD HHMMSS. +/* + - ignores missing source files/dirs + - creates missing intermediate directories + - does not create empty directories + - handles symlinks + - replaces already existing target files/dirs (supports retry) + => (unlikely) risk of data loss for naming convention "versioning": + race-condition if two FFS instances start at the very same second OR multiple folder pairs process the same filename!! +*/ + +class FileVersioner +{ +public: + FileVersioner(const Zstring& versioningDirectory, //throw FileError + VersioningStyle versioningStyle, + const TimeComp& timeStamp) : //max versions per file; < 0 := no limit + versioningStyle_(versioningStyle), + versioningDirectory_(versioningDirectory), + timeStamp_(formatTime(Zstr("%Y-%m-%d %H%M%S"), timeStamp)) //e.g. "2012-05-15 131513" + { + if (timeStamp_.size() != 17) //formatTime() returns empty string on error; unexpected length: e.g. problem in year 10000! + throw FileError(_("Unable to create timestamp for versioning:") + L" \"" + timeStamp_ + L"\""); + } + + bool revisionFile(const Zstring& fullName, const Zstring& relativeName, CallbackMoveFile& callback); //throw FileError; return "false" if file is not existing + void revisionDir (const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback); //throw FileError + + //void limitVersions(std::function updateUI); //throw FileError; call when done revisioning! + +private: + bool revisionFileImpl(const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback); //throw FileError + void revisionDirImpl (const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback); //throw FileError + + const VersioningStyle versioningStyle_; + const Zstring versioningDirectory_; + const Zstring timeStamp_; + + //std::vector fileRelNames; //store list of revisioned file and symlink relative names for limitVersions() +}; + + +struct CallbackMoveFile //see CallbackCopyFile for limitations when throwing exceptions! +{ + virtual ~CallbackMoveFile() {} + virtual void updateStatus(Int64 bytesDelta) = 0; //called frequently if move has to revert to copy + delete: +}; + +struct CallbackMoveDir //see CallbackCopyFile for limitations when throwing exceptions! +{ + virtual ~CallbackMoveDir() {} + + virtual void onBeforeFileMove(const Zstring& fileFrom, const Zstring& fileTo) = 0; //one call for each *existing* object! + virtual void onBeforeDirMove (const Zstring& dirFrom, const Zstring& dirTo ) = 0; // + virtual void updateStatus(Int64 bytesDelta) = 0; //called frequently if move has to revert to copy + delete: +}; + + + +namespace impl //declare for unit tests: +{ +bool isMatchingVersion(const Zstring& shortname, const Zstring& shortnameVersion); +} +} + +#endif //VERSIONING_HEADER_8760247652438056 diff --git a/FreeFileSync/Source/lib/xml_base.cpp b/FreeFileSync/Source/lib/xml_base.cpp new file mode 100644 index 00000000..f504d19a --- /dev/null +++ b/FreeFileSync/Source/lib/xml_base.cpp @@ -0,0 +1,108 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "xml_base.h" +#include +#include + +using namespace zen; + + +//loadXmlDocument vs loadStream: +//1. better error reporting +//2. quick exit if (potentially large) input file is not an XML +XmlDoc xmlAccess::loadXmlDocument(const Zstring& filename) //throw FfsXmlError +{ + std::string stream; + try + { + FileInput inputFile(filename); //throw FileError + { + //quick test whether input is an XML: avoid loading large binary files up front! + const std::string xmlBegin = "(e.row + 1)), + L"%z", numberTo(e.col + 1))); + } +} + + +const std::wstring xmlAccess::getErrorMessageFormatted(const std::vector& failedElements) +{ + std::wstring msg; + + if (!failedElements.empty()) + { + msg = _("Cannot read the following XML elements:") + L"\n"; + std::for_each(failedElements.begin(), failedElements.end(), [&](const std::wstring& elem) { msg += L"\n" + elem; }); + } + + return msg; +} + + +void xmlAccess::saveXmlDocument(const zen::XmlDoc& doc, const Zstring& filename) //throw FfsXmlError +{ + std::string stream = serialize(doc); //throw () + + bool saveNecessary = true; + try + { + if (zen::getFilesize(filename) == stream.size()) //throw FileError + try + { + if (zen::loadStream(filename) == stream) //throw XmlFileError + saveNecessary = false; + } + catch (const zen::XmlFileError&) {} + } + catch (FileError&) {} + + if (saveNecessary) + try + { + FileOutput outputFile(filename, FileOutput::ACC_OVERWRITE); //throw FileError + outputFile.write(stream.c_str(), stream.length()); // + } + catch (const FileError& error) + { + throw FfsXmlError(error.toString()); + } +} diff --git a/FreeFileSync/Source/lib/xml_base.h b/FreeFileSync/Source/lib/xml_base.h new file mode 100644 index 00000000..85d4dfa1 --- /dev/null +++ b/FreeFileSync/Source/lib/xml_base.h @@ -0,0 +1,42 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef XMLBASE_H_INCLUDED +#define XMLBASE_H_INCLUDED + +#include +#include +#include + +//bind zen::Xml and zen file handling together + +namespace xmlAccess +{ +class FfsXmlError //Exception class +{ +public: + enum Severity + { + WARNING = 77, + FATAL + }; + + explicit FfsXmlError(const std::wstring& message, Severity sev = FATAL) : errorMessage(message), m_severity(sev) {} + + const std::wstring& toString() const { return errorMessage; } + Severity getSeverity() const { return m_severity; } +private: + const std::wstring errorMessage; + const Severity m_severity; +}; + +void saveXmlDocument(const zen::XmlDoc& doc, const Zstring& filename); //throw FfsXmlError +zen::XmlDoc loadXmlDocument(const Zstring& filename); //throw FfsXmlError + +const std::wstring getErrorMessageFormatted(const std::vector& failedElements); +} + +#endif // XMLBASE_H_INCLUDED diff --git a/FreeFileSync/Source/process_callback.h b/FreeFileSync/Source/process_callback.h new file mode 100644 index 00000000..fb9648fe --- /dev/null +++ b/FreeFileSync/Source/process_callback.h @@ -0,0 +1,76 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef PROC_HEADER_48257827842345454545 +#define PROC_HEADER_48257827842345454545 + +#include +#include + +//interface for comparison and synchronization process status updates (used by GUI or Batch mode) +const int UI_UPDATE_INTERVAL = 100; //unit: [ms]; perform ui updates not more often than necessary, +//100 seems to be a good value with only a minimal performance loss; also used by Win 7 copy progress bar +//this one is required by async directory existence check! + +//report status during comparison and synchronization +struct ProcessCallback +{ + virtual ~ProcessCallback() {} + + //these methods have to be implemented in the derived classes to handle error and status information + + //notify synchronization phases + enum Phase + { + PHASE_NONE, //initial status + PHASE_SCANNING, + PHASE_COMPARING_CONTENT, + PHASE_SYNCHRONIZING + }; + virtual void initNewPhase(int objectsTotal, zen::Int64 dataTotal, Phase phaseId) = 0; //informs about the estimated amount of data that will be processed in this phase + + //note: this one must NOT throw in order to properly allow undoing setting of statistics! + //it is in general paired with a call to requestUiRefresh() to compensate! + virtual void updateProcessedData(int objectsDelta, zen::Int64 dataDelta) = 0; //noexcept!! + virtual void updateTotalData (int objectsDelta, zen::Int64 dataDelta) = 0; // + /*the estimated and actual total workload may change *during* sync: + 1. detected file can be moved -> fallback to copy + delete + 2. file copy, actual size changed after comparison + 3. file contains significant ADS data, is sparse or compressed + 4. auto-resolution for failed create operations due to missing source + 5. directory deletion: may contain more items than scanned by FFS (excluded by filter) or less (contains followed symlinks) + 6. delete directory to recycler: no matter how many child-elements exist, this is only 1 item to process! + 7. file/directory already deleted externally: nothing to do, 0 logical operations and data + 8. user-defined deletion directory on different volume: full file copy required (instead of move) + 9. Binary file comparison: if files differ at the first few bytes, the result is already known + 10. Error during file copy, retry: bytes were copied => increases total workload! + */ + + //opportunity to abort must be implemented in a frequently executed method like requestUiRefresh() + virtual void requestUiRefresh() = 0; //throw ? + virtual void forceUiRefresh () = 0; //throw ? - called before starting long running tasks which don't update regularly + + //called periodically after data was processed: expected(!) to request GUI update + virtual void reportStatus(const std::wstring& text) = 0; //UI info only, should not be logged! + + //called periodically after data was processed: expected(!) to request GUI update + virtual void reportInfo(const std::wstring& text) = 0; + + virtual void reportWarning(const std::wstring& warningMessage, bool& warningActive) = 0; + + //error handling: + enum Response + { + IGNORE_ERROR = 10, + RETRY + }; + virtual Response reportError (const std::wstring& errorMessage, size_t retryNumber) = 0; //recoverable error situation + virtual void reportFatalError(const std::wstring& errorMessage) = 0; //non-recoverable error situation + + virtual void abortProcessNow() = 0; //will throw an exception => don't call while in a C GUI callstack +}; + +#endif //PROC_HEADER_48257827842345454545 diff --git a/FreeFileSync/Source/structures.cpp b/FreeFileSync/Source/structures.cpp new file mode 100644 index 00000000..04edf537 --- /dev/null +++ b/FreeFileSync/Source/structures.cpp @@ -0,0 +1,516 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "structures.h" +#include +#include +#include +#include +#include + +using namespace zen; + + +std::wstring zen::getVariantName(CompareVariant var) +{ + switch (var) + { + case CMP_BY_CONTENT: + return _("File content"); + case CMP_BY_TIME_SIZE: + return _("File time and size"); + } + assert(false); + return _("Error"); +} + + +std::wstring zen::getVariantName(DirectionConfig::Variant var) +{ + switch (var) + { + case DirectionConfig::TWOWAY: + return L"<- " + _("Two way") + L" ->"; + case DirectionConfig::MIRROR: + return _("Mirror") + L" ->>"; + case DirectionConfig::UPDATE: + return _("Update") + L" ->"; + case DirectionConfig::CUSTOM: + return _("Custom"); + } + assert(false); + return _("Error"); +} + + +DirectionSet zen::extractDirections(const DirectionConfig& cfg) +{ + DirectionSet output; + switch (cfg.var) + { + case DirectionConfig::TWOWAY: + throw std::logic_error("there are no predefined directions for automatic mode!"); + + case DirectionConfig::MIRROR: + output.exLeftSideOnly = SyncDirection::RIGHT; + output.exRightSideOnly = SyncDirection::RIGHT; + output.leftNewer = SyncDirection::RIGHT; + output.rightNewer = SyncDirection::RIGHT; + output.different = SyncDirection::RIGHT; + output.conflict = SyncDirection::RIGHT; + break; + + case DirectionConfig::UPDATE: + output.exLeftSideOnly = SyncDirection::RIGHT; + output.exRightSideOnly = SyncDirection::NONE; + output.leftNewer = SyncDirection::RIGHT; + output.rightNewer = SyncDirection::NONE; + output.different = SyncDirection::RIGHT; + output.conflict = SyncDirection::NONE; + break; + + case DirectionConfig::CUSTOM: + output = cfg.custom; + break; + } + return output; +} + + +bool zen::detectMovedFilesSelectable(const DirectionConfig& cfg) +{ + if (cfg.var == DirectionConfig::TWOWAY) + return false; //moved files are always detected since we have the database file anyway + + const DirectionSet tmp = zen::extractDirections(cfg); + return (tmp.exLeftSideOnly == SyncDirection::RIGHT && + tmp.exRightSideOnly == SyncDirection::RIGHT) || + (tmp.exLeftSideOnly == SyncDirection::LEFT&& + tmp.exRightSideOnly == SyncDirection::LEFT); +} + + +bool zen::detectMovedFilesEnabled(const DirectionConfig& cfg) +{ + return detectMovedFilesSelectable(cfg) ? cfg.detectMovedFiles : cfg.var == DirectionConfig::TWOWAY; +} + + +DirectionSet zen::getTwoWayUpdateSet() +{ + DirectionSet output; + output.exLeftSideOnly = SyncDirection::RIGHT; + output.exRightSideOnly = SyncDirection::LEFT; + output.leftNewer = SyncDirection::RIGHT; + output.rightNewer = SyncDirection::LEFT; + output.different = SyncDirection::NONE; + output.conflict = SyncDirection::NONE; + return output; +} + + +std::wstring MainConfiguration::getCompVariantName() const +{ + const CompareVariant firstVariant = firstPair.altCmpConfig.get() ? + firstPair.altCmpConfig->compareVar : + cmpConfig.compareVar; //fallback to main sync cfg + + //test if there's a deviating variant within the additional folder pairs + for (const FolderPairEnh& fp : additionalPairs) + { + const CompareVariant thisVariant = fp.altCmpConfig.get() ? + fp.altCmpConfig->compareVar : + cmpConfig.compareVar; //fallback to main sync cfg + if (thisVariant != firstVariant) + return _("Multiple..."); + } + + //seems to be all in sync... + return getVariantName(firstVariant); +} + + +std::wstring MainConfiguration::getSyncVariantName() const +{ + const DirectionConfig::Variant firstVariant = firstPair.altSyncConfig.get() ? + firstPair.altSyncConfig->directionCfg.var : + syncCfg.directionCfg.var; //fallback to main sync cfg + + //test if there's a deviating variant within the additional folder pairs + for (const FolderPairEnh& fp : additionalPairs) + { + const DirectionConfig::Variant thisVariant = fp.altSyncConfig.get() ? + fp.altSyncConfig->directionCfg.var : + syncCfg.directionCfg.var; + if (thisVariant != firstVariant) + return _("Multiple..."); + } + + //seems to be all in sync... + return getVariantName(firstVariant); +} + + +std::wstring zen::getSymbol(CompareFilesResult cmpRes) +{ + switch (cmpRes) + { + case FILE_LEFT_SIDE_ONLY: + return L"only <-"; + case FILE_RIGHT_SIDE_ONLY: + return L"only ->"; + case FILE_LEFT_NEWER: + return L"newer <-"; + case FILE_RIGHT_NEWER: + return L"newer ->"; + case FILE_DIFFERENT: + return L"!="; + case FILE_EQUAL: + case FILE_DIFFERENT_METADATA: //= sub-category of equal! + return L"'=="; //added quotation mark to avoid error in Excel cell when exporting to *.cvs + case FILE_CONFLICT: + return L"conflict"; + } + assert(false); + return std::wstring(); +} + + +std::wstring zen::getSymbol(SyncOperation op) +{ + switch (op) + { + case SO_CREATE_NEW_LEFT: + return L"create <-"; + case SO_CREATE_NEW_RIGHT: + return L"create ->"; + case SO_DELETE_LEFT: + return L"delete <-"; + case SO_DELETE_RIGHT: + return L"delete ->"; + case SO_MOVE_LEFT_SOURCE: + return L"move from <-"; + case SO_MOVE_LEFT_TARGET: + return L"move to <-"; + case SO_MOVE_RIGHT_SOURCE: + return L"move from ->"; + case SO_MOVE_RIGHT_TARGET: + return L"move to ->"; + case SO_OVERWRITE_LEFT: + case SO_COPY_METADATA_TO_LEFT: + return L"update <-"; + case SO_OVERWRITE_RIGHT: + case SO_COPY_METADATA_TO_RIGHT: + return L"update ->"; + case SO_DO_NOTHING: + return L" -"; + case SO_EQUAL: + return L"'=="; //added quotation mark to avoid error in Excel cell when exporting to *.cvs + case SO_UNRESOLVED_CONFLICT: + return L"conflict"; + }; + assert(false); + return std::wstring(); +} + + +namespace +{ +assert_static(std::numeric_limits::is_specialized); +assert_static(std::numeric_limits::is_specialized); + +/* +int daysSinceBeginOfWeek(int dayOfWeek) //0-6, 0=Monday, 6=Sunday +{ + assert(0 <= dayOfWeek && dayOfWeek <= 6); +#ifdef ZEN_WIN + DWORD firstDayOfWeek = 0; + if (::GetLocaleInfo(LOCALE_USER_DEFAULT, //__in LCID Locale, + LOCALE_IFIRSTDAYOFWEEK | // first day of week specifier, 0-6, 0=Monday, 6=Sunday + LOCALE_RETURN_NUMBER, //__in LCTYPE LCType, + reinterpret_cast(&firstDayOfWeek), //__out LPTSTR lpLCData, + sizeof(firstDayOfWeek) / sizeof(TCHAR)) > 0) //__in int cchData + { + assert(firstDayOfWeek <= 6); + return (dayOfWeek + (7 - firstDayOfWeek)) % 7; + } + else //default +#endif + return dayOfWeek; //let all weeks begin with monday +} +*/ + + +Int64 resolve(size_t value, UnitTime unit, Int64 defaultVal) +{ + TimeComp locTimeStruc = zen::localTime(); + + switch (unit) + { + case UTIME_NONE: + return defaultVal; + + case UTIME_TODAY: + locTimeStruc.second = 0; //0-61 + locTimeStruc.minute = 0; //0-59 + locTimeStruc.hour = 0; //0-23 + return localToTimeT(locTimeStruc); //convert local time back to UTC + + //case UTIME_THIS_WEEK: + //{ + // localTimeFmt->tm_sec = 0; //0-61 + // localTimeFmt->tm_min = 0; //0-59 + // localTimeFmt->tm_hour = 0; //0-23 + // const time_t timeFrom = ::mktime(localTimeFmt); + + // int dayOfWeek = (localTimeFmt->tm_wday + 6) % 7; //tm_wday := days since Sunday 0-6 + // // +6 == -1 in Z_7 + + // return Int64(timeFrom) - daysSinceBeginOfWeek(dayOfWeek) * 24 * 3600; + //} + + case UTIME_THIS_MONTH: + locTimeStruc.second = 0; //0-61 + locTimeStruc.minute = 0; //0-59 + locTimeStruc.hour = 0; //0-23 + locTimeStruc.day = 1; //1-31 + return localToTimeT(locTimeStruc); + + case UTIME_THIS_YEAR: + locTimeStruc.second = 0; //0-61 + locTimeStruc.minute = 0; //0-59 + locTimeStruc.hour = 0; //0-23 + locTimeStruc.day = 1; //1-31 + locTimeStruc.month = 1; //1-12 + return localToTimeT(locTimeStruc); + + case UTIME_LAST_X_DAYS: + locTimeStruc.second = 0; //0-61 + locTimeStruc.minute = 0; //0-59 + locTimeStruc.hour = 0; //0-23 + return localToTimeT(locTimeStruc) - Int64(value) * 24 * 3600; + } + + assert(false); + return localToTimeT(locTimeStruc); +} + + +UInt64 resolve(size_t value, UnitSize unit, UInt64 defaultVal) +{ + const UInt64 maxVal =std::numeric_limits::max(); + + switch (unit) + { + case USIZE_NONE: + return defaultVal; + case USIZE_BYTE: + return value; + case USIZE_KB: + return value > maxVal / 1024U ? maxVal : //prevent overflow!!! + 1024U * value; + case USIZE_MB: + return value > maxVal / (1024 * 1024U) ? maxVal : //prevent overflow!!! + 1024 * 1024U * value; + } + assert(false); + return defaultVal; +} +} + +void zen::resolveUnits(size_t timeSpan, UnitTime unitTimeSpan, + size_t sizeMin, UnitSize unitSizeMin, + size_t sizeMax, UnitSize unitSizeMax, + zen::Int64& timeFrom, //unit: UTC time, seconds + zen::UInt64& sizeMinBy, //unit: bytes + zen::UInt64& sizeMaxBy) //unit: bytes +{ + timeFrom = resolve(timeSpan, unitTimeSpan, std::numeric_limits::min()); + sizeMinBy = resolve(sizeMin, unitSizeMin, 0U); + sizeMaxBy = resolve(sizeMax, unitSizeMax, std::numeric_limits::max()); +} + + +namespace +{ +FilterConfig mergeFilterConfig(const FilterConfig& global, const FilterConfig& local) +{ + FilterConfig out = local; + + //hard filter + if (out.includeFilter == FilterConfig().includeFilter) + out.includeFilter = global.includeFilter; + //else: if both global and local include filter contain data, only local filter is preserved + + trim(out.excludeFilter, true, false); + out.excludeFilter = global.excludeFilter + Zstr("\n") + out.excludeFilter; + trim(out.excludeFilter, true, false); + + //soft filter + Int64 loctimeFrom; + UInt64 locSizeMinBy; + UInt64 locSizeMaxBy; + resolveUnits(out.timeSpan, out.unitTimeSpan, + out.sizeMin, out.unitSizeMin, + out.sizeMax, out.unitSizeMax, + loctimeFrom, //unit: UTC time, seconds + locSizeMinBy, //unit: bytes + locSizeMaxBy); //unit: bytes + + //soft filter + Int64 glotimeFrom; + UInt64 gloSizeMinBy; + UInt64 gloSizeMaxBy; + resolveUnits(global.timeSpan, global.unitTimeSpan, + global.sizeMin, global.unitSizeMin, + global.sizeMax, global.unitSizeMax, + glotimeFrom, + gloSizeMinBy, + gloSizeMaxBy); + + if (glotimeFrom > loctimeFrom) + { + out.timeSpan = global.timeSpan; + out.unitTimeSpan = global.unitTimeSpan; + } + if (gloSizeMinBy > locSizeMinBy) + { + out.sizeMin = global.sizeMin; + out.unitSizeMin = global.unitSizeMin; + } + if (gloSizeMaxBy < locSizeMaxBy) + { + out.sizeMax = global.sizeMax; + out.unitSizeMax = global.unitSizeMax; + } + return out; +} + + +inline +bool effectivelyEmpty(const FolderPairEnh& fp) +{ + auto isEmpty = [](Zstring dirname) + { + trim(dirname); + return dirname.empty(); + }; + return isEmpty(fp.dirnamePhraseLeft) && isEmpty(fp.dirnamePhraseRight); +} +} + + +MainConfiguration zen::merge(const std::vector& mainCfgs) +{ + assert(!mainCfgs.empty()); + if (mainCfgs.empty()) + return MainConfiguration(); + + if (mainCfgs.size() == 1) //mergeConfigFilesImpl relies on this! + return mainCfgs[0]; // + + //merge folder pair config + std::vector fpMerged; + for (const MainConfiguration& mainCfg : mainCfgs) + { + std::vector fpTmp; + + //skip empty folder pairs + if (!effectivelyEmpty(mainCfg.firstPair)) + fpTmp.push_back(mainCfg.firstPair); + for (const FolderPairEnh& fp : mainCfg.additionalPairs) + if (!effectivelyEmpty(fp)) + fpTmp.push_back(fp); + + //move all configuration down to item level + for (FolderPairEnh& fp : fpTmp) + { + if (!fp.altCmpConfig.get()) + fp.altCmpConfig = std::make_shared(mainCfg.cmpConfig); + + if (!fp.altSyncConfig.get()) + fp.altSyncConfig = std::make_shared(mainCfg.syncCfg); + + fp.localFilter = mergeFilterConfig(mainCfg.globalFilter, fp.localFilter); + } + fpMerged.insert(fpMerged.end(), fpTmp.begin(), fpTmp.end()); + } + + if (fpMerged.empty()) + return MainConfiguration(); + + //optimization: remove redundant configuration + + //######################################################################################################################## + //find out which comparison and synchronization setting are used most often and use them as new "header" + std::vector> cmpCfgStat; + std::vector> syncCfgStat; + for (const FolderPairEnh& fp : fpMerged) + { + //rather inefficient algorithm, but it does not require a less-than operator: + { + const CompConfig& cmpCfg = *fp.altCmpConfig; + + auto it = std::find_if(cmpCfgStat.begin(), cmpCfgStat.end(), + [&](const std::pair& entry) { return effectivelyEqual(entry.first, cmpCfg); }); + if (it == cmpCfgStat.end()) + cmpCfgStat.push_back(std::make_pair(cmpCfg, 1)); + else + ++(it->second); + } + { + const SyncConfig& syncCfg = *fp.altSyncConfig; + + auto it = std::find_if(syncCfgStat.begin(), syncCfgStat.end(), + [&](const std::pair& entry) { return effectivelyEqual(entry.first, syncCfg); }); + if (it == syncCfgStat.end()) + syncCfgStat.push_back(std::make_pair(syncCfg, 1)); + else + ++(it->second); + } + } + + //set most-used comparison and synchronization settings as new header options + const CompConfig cmpCfgHead = cmpCfgStat.empty() ? CompConfig() : + std::max_element(cmpCfgStat.begin(), cmpCfgStat.end(), + [](const std::pair& lhs, const std::pair& rhs) { return lhs.second < rhs.second; })->first; + + const SyncConfig syncCfgHead = syncCfgStat.empty() ? SyncConfig() : + std::max_element(syncCfgStat.begin(), syncCfgStat.end(), + [](const std::pair& lhs, const std::pair& rhs) { return lhs.second < rhs.second; })->first; + //######################################################################################################################## + + FilterConfig globalFilter; + const bool allFiltersEqual = std::all_of(fpMerged.begin(), fpMerged.end(), [&](const FolderPairEnh& fp) { return fp.localFilter == fpMerged[0].localFilter; }); + if (allFiltersEqual) + globalFilter = fpMerged[0].localFilter; + + //strip redundancy... + for (FolderPairEnh& fp : fpMerged) + { + //if local config matches output global config we don't need local one + if (fp.altCmpConfig && + effectivelyEqual(*fp.altCmpConfig, cmpCfgHead)) + fp.altCmpConfig.reset(); + + if (fp.altSyncConfig && + effectivelyEqual(*fp.altSyncConfig, syncCfgHead)) + fp.altSyncConfig.reset(); + + if (allFiltersEqual) //use global filter in this case + fp.localFilter = FilterConfig(); + } + + //final assembly + zen::MainConfiguration cfgOut; + cfgOut.cmpConfig = cmpCfgHead; + cfgOut.syncCfg = syncCfgHead; + cfgOut.globalFilter = globalFilter; + cfgOut.firstPair = fpMerged[0]; + cfgOut.additionalPairs.assign(fpMerged.begin() + 1, fpMerged.end()); + cfgOut.onCompletion = mainCfgs[0].onCompletion; + return cfgOut; +} diff --git a/FreeFileSync/Source/structures.h b/FreeFileSync/Source/structures.h new file mode 100644 index 00000000..eeaf3f77 --- /dev/null +++ b/FreeFileSync/Source/structures.h @@ -0,0 +1,406 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef FREEFILESYNC_H_INCLUDED +#define FREEFILESYNC_H_INCLUDED + +#include +#include +#include +#include +#include + +namespace zen +{ +enum CompareVariant +{ + CMP_BY_TIME_SIZE, + CMP_BY_CONTENT +}; + +std::wstring getVariantName(CompareVariant var); + +enum SymLinkHandling +{ + SYMLINK_EXCLUDE, + SYMLINK_USE_DIRECTLY, + SYMLINK_FOLLOW_LINK +}; + + +enum class SyncDirection : unsigned char //we need to save space for use in FileSystemObject! +{ + LEFT, + RIGHT, + NONE +}; + + +enum CompareFilesResult +{ + FILE_EQUAL, + FILE_LEFT_SIDE_ONLY, + FILE_RIGHT_SIDE_ONLY, + FILE_LEFT_NEWER, //CMP_BY_TIME_SIZE only! + FILE_RIGHT_NEWER, // + FILE_DIFFERENT, //CMP_BY_CONTENT only! + FILE_DIFFERENT_METADATA, //both sides equal, but different metadata only: short name case, modification time + FILE_CONFLICT +}; +//attention make sure these /|\ \|/ three enums match!!! +enum CompareDirResult +{ + DIR_EQUAL = FILE_EQUAL, + DIR_LEFT_SIDE_ONLY = FILE_LEFT_SIDE_ONLY, + DIR_RIGHT_SIDE_ONLY = FILE_RIGHT_SIDE_ONLY, + DIR_DIFFERENT_METADATA = FILE_DIFFERENT_METADATA //both sides equal, but different metadata only: short name case +}; + +enum CompareSymlinkResult +{ + SYMLINK_EQUAL = FILE_EQUAL, + SYMLINK_LEFT_SIDE_ONLY = FILE_LEFT_SIDE_ONLY, + SYMLINK_RIGHT_SIDE_ONLY = FILE_RIGHT_SIDE_ONLY, + SYMLINK_LEFT_NEWER = FILE_LEFT_NEWER, + SYMLINK_RIGHT_NEWER = FILE_RIGHT_NEWER, + SYMLINK_DIFFERENT = FILE_DIFFERENT, + SYMLINK_DIFFERENT_METADATA = FILE_DIFFERENT_METADATA, //both sides equal, but different metadata only: short name case + SYMLINK_CONFLICT = FILE_CONFLICT +}; + + +std::wstring getSymbol(CompareFilesResult cmpRes); + + +enum SyncOperation +{ + SO_CREATE_NEW_LEFT, + SO_CREATE_NEW_RIGHT, + SO_DELETE_LEFT, + SO_DELETE_RIGHT, + + SO_MOVE_LEFT_SOURCE, //SO_DELETE_LEFT - optimization! + SO_MOVE_LEFT_TARGET, //SO_CREATE_NEW_LEFT + + SO_MOVE_RIGHT_SOURCE, //SO_DELETE_RIGHT - optimization! + SO_MOVE_RIGHT_TARGET, //SO_CREATE_NEW_RIGHT + + SO_OVERWRITE_LEFT, + SO_OVERWRITE_RIGHT, + SO_COPY_METADATA_TO_LEFT, //objects are already equal: transfer metadata only - optimization + SO_COPY_METADATA_TO_RIGHT, // + + SO_DO_NOTHING, //= both sides differ, but nothing will be synced + SO_EQUAL, //= both sides are equal, so nothing will be synced + SO_UNRESOLVED_CONFLICT +}; + +std::wstring getSymbol(SyncOperation op); //method used for exporting .csv file only! + + +struct DirectionSet +{ + DirectionSet() : + exLeftSideOnly (SyncDirection::RIGHT), + exRightSideOnly(SyncDirection::LEFT), + leftNewer (SyncDirection::RIGHT), + rightNewer (SyncDirection::LEFT), + different (SyncDirection::NONE), + conflict (SyncDirection::NONE) {} + + SyncDirection exLeftSideOnly; + SyncDirection exRightSideOnly; + SyncDirection leftNewer; //CMP_BY_TIME_SIZE only! + SyncDirection rightNewer; // + SyncDirection different; //CMP_BY_CONTENT only! + SyncDirection conflict; +}; + +DirectionSet getTwoWayUpdateSet(); + +inline +bool operator==(const DirectionSet& lhs, const DirectionSet& rhs) +{ + return lhs.exLeftSideOnly == rhs.exLeftSideOnly && + lhs.exRightSideOnly == rhs.exRightSideOnly && + lhs.leftNewer == rhs.leftNewer && + lhs.rightNewer == rhs.rightNewer && + lhs.different == rhs.different && + lhs.conflict == rhs.conflict; +} + +struct DirectionConfig //technical representation of sync-config +{ + enum Variant + { + TWOWAY, //use sync-database to determine directions + MIRROR, //predefined + UPDATE, // + CUSTOM //use custom directions + }; + + DirectionConfig() : var(TWOWAY), detectMovedFiles(false) {} + + Variant var; + DirectionSet custom; //custom sync directions + bool detectMovedFiles; //dependent from Variant: e.g. always active for DirectionConfig::TWOWAY! => use functions below for evaluation! +}; + +inline +bool operator==(const DirectionConfig& lhs, const DirectionConfig& rhs) +{ + return lhs.var == rhs.var && + lhs.custom == rhs.custom && + lhs.detectMovedFiles == rhs.detectMovedFiles; + //adapt effectivelyEqual() on changes, too! +} + +bool detectMovedFilesSelectable(const DirectionConfig& cfg); +bool detectMovedFilesEnabled (const DirectionConfig& cfg); + +DirectionSet extractDirections(const DirectionConfig& cfg); //get sync directions: DON'T call for DirectionConfig::TWOWAY! + +std::wstring getVariantName(DirectionConfig::Variant var); + +inline +bool effectivelyEqual(const DirectionConfig& lhs, const DirectionConfig& rhs) +{ + return (lhs.var == DirectionConfig::TWOWAY) == (rhs.var == DirectionConfig::TWOWAY) && //either both two-way or none + (lhs.var == DirectionConfig::TWOWAY || extractDirections(lhs) == extractDirections(rhs)) && + detectMovedFilesEnabled(lhs) == detectMovedFilesEnabled(rhs); +} + + +struct CompConfig +{ + CompConfig() : + compareVar(CMP_BY_TIME_SIZE), + handleSymlinks(SYMLINK_EXCLUDE) {} + + CompareVariant compareVar; + SymLinkHandling handleSymlinks; +}; + +inline +bool operator==(const CompConfig& lhs, const CompConfig& rhs) +{ + return lhs.compareVar == rhs.compareVar && + lhs.handleSymlinks == rhs.handleSymlinks; +} + +inline +bool effectivelyEqual(const CompConfig& lhs, const CompConfig& rhs) { return lhs == rhs; } //no change in behavior + + +enum DeletionPolicy +{ + DELETE_PERMANENTLY, + DELETE_TO_RECYCLER, + DELETE_TO_VERSIONING +}; + +enum VersioningStyle +{ + VER_STYLE_REPLACE, + VER_STYLE_ADD_TIMESTAMP, +}; + +struct SyncConfig +{ + SyncConfig() : + handleDeletion(DELETE_TO_RECYCLER), + versioningStyle(VER_STYLE_REPLACE) {} + + //sync direction settings + DirectionConfig directionCfg; + + DeletionPolicy handleDeletion; //use Recycle, delete permanently or move to user-defined location + //versioning options + VersioningStyle versioningStyle; + Zstring versioningDirectory; + //int versionCountLimit; //max versions per file (DELETE_TO_VERSIONING); < 0 := no limit +}; + + +inline +bool operator==(const SyncConfig& lhs, const SyncConfig& rhs) +{ + return lhs.directionCfg == rhs.directionCfg && + lhs.handleDeletion == rhs.handleDeletion && + lhs.versioningStyle == rhs.versioningStyle && + lhs.versioningDirectory == rhs.versioningDirectory; + //adapt effectivelyEqual() on changes, too! +} + + +inline +bool effectivelyEqual(const SyncConfig& lhs, const SyncConfig& rhs) +{ + return effectivelyEqual(lhs.directionCfg, rhs.directionCfg) && + lhs.handleDeletion == rhs.handleDeletion && + (lhs.handleDeletion != DELETE_TO_VERSIONING || //only compare deletion directory if required! + (lhs.versioningStyle == rhs.versioningStyle && + lhs.versioningDirectory == rhs.versioningDirectory)); +} + + +enum UnitSize +{ + USIZE_NONE, + USIZE_BYTE, + USIZE_KB, + USIZE_MB +}; + +enum UnitTime +{ + UTIME_NONE, + UTIME_TODAY, + // UTIME_THIS_WEEK, + UTIME_THIS_MONTH, + UTIME_THIS_YEAR, + UTIME_LAST_X_DAYS +}; + +struct FilterConfig +{ + FilterConfig(const Zstring& include = Zstr("*"), + const Zstring& exclude = Zstring(), + size_t timeSpanIn = 0, + UnitTime unitTimeSpanIn = UTIME_NONE, + size_t sizeMinIn = 0, + UnitSize unitSizeMinIn = USIZE_NONE, + size_t sizeMaxIn = 0, + UnitSize unitSizeMaxIn = USIZE_NONE) : + includeFilter(include), + excludeFilter(exclude), + timeSpan (timeSpanIn), + unitTimeSpan (unitTimeSpanIn), + sizeMin (sizeMinIn), + unitSizeMin (unitSizeMinIn), + sizeMax (sizeMaxIn), + unitSizeMax (unitSizeMaxIn) {} + + /* + Semantics of HardFilter: + 1. using it creates a NEW folder hierarchy! -> must be considered by -mode! (fortunately it turns out, doing nothing already has perfect semantics :) + 2. it applies equally to both sides => it always matches either both sides or none! => can be used while traversing a single folder! + */ + Zstring includeFilter; + Zstring excludeFilter; + + /* + Semantics of SoftFilter: + 1. It potentially may match only one side => it MUST NOT be applied while traversing a single folder to avoid mismatches + 2. => it is applied after traversing and just marks rows, (NO deletions after comparison are allowed) + 3. => equivalent to a user temporarily (de-)selecting rows -> not relevant for -mode! ;) + */ + size_t timeSpan; + UnitTime unitTimeSpan; + + size_t sizeMin; + UnitSize unitSizeMin; + + size_t sizeMax; + UnitSize unitSizeMax; +}; + +inline +bool operator==(const FilterConfig& lhs, const FilterConfig& rhs) +{ + return lhs.includeFilter == rhs.includeFilter && + lhs.excludeFilter == rhs.excludeFilter && + lhs.timeSpan == rhs.timeSpan && + lhs.unitTimeSpan == rhs.unitTimeSpan && + lhs.sizeMin == rhs.sizeMin && + lhs.unitSizeMin == rhs.unitSizeMin && + lhs.sizeMax == rhs.sizeMax && + lhs.unitSizeMax == rhs.unitSizeMax; +} + +void resolveUnits(size_t timeSpan, UnitTime unitTimeSpan, + size_t sizeMin, UnitSize unitSizeMin, + size_t sizeMax, UnitSize unitSizeMax, + zen::Int64& timeFrom, //unit: UTC time, seconds + zen::UInt64& sizeMinBy, //unit: bytes + zen::UInt64& sizeMaxBy); //unit: bytes + + +struct FolderPairEnh //enhanced folder pairs with (optional) alternate configuration +{ + FolderPairEnh() {} + + FolderPairEnh(const Zstring& phraseLeft, + const Zstring& phraseRight, + const std::shared_ptr& cmpConfig, + const std::shared_ptr& syncConfig, + const FilterConfig& filter) : + dirnamePhraseLeft (phraseLeft), + dirnamePhraseRight(phraseRight), + altCmpConfig(cmpConfig), + altSyncConfig(syncConfig), + localFilter(filter) {} + + Zstring dirnamePhraseLeft; //unresolved directory names as entered by user! + Zstring dirnamePhraseRight; // + + std::shared_ptr altCmpConfig; //optional + std::shared_ptr altSyncConfig; // + FilterConfig localFilter; +}; + + +inline +bool operator==(const FolderPairEnh& lhs, const FolderPairEnh& rhs) +{ + return lhs.dirnamePhraseLeft == rhs.dirnamePhraseLeft && + lhs.dirnamePhraseRight == rhs.dirnamePhraseRight && + + (lhs.altCmpConfig.get() && rhs.altCmpConfig.get() ? + *lhs.altCmpConfig == *rhs.altCmpConfig : + lhs.altCmpConfig.get() == rhs.altCmpConfig.get()) && + + (lhs.altSyncConfig.get() && rhs.altSyncConfig.get() ? + *lhs.altSyncConfig == *rhs.altSyncConfig : + lhs.altSyncConfig.get() == rhs.altSyncConfig.get()) && + + lhs.localFilter == rhs.localFilter; +} + + +struct MainConfiguration +{ + CompConfig cmpConfig; //global compare settings: may be overwritten by folder pair settings + SyncConfig syncCfg; //global synchronisation settings: may be overwritten by folder pair settings + FilterConfig globalFilter; //global filter settings: combined with folder pair settings + + FolderPairEnh firstPair; //there needs to be at least one pair! + std::vector additionalPairs; + + std::wstring onCompletion; //user-defined command line + + std::wstring getCompVariantName() const; + std::wstring getSyncVariantName() const; +}; + + +inline +bool operator==(const MainConfiguration& lhs, const MainConfiguration& rhs) +{ + return lhs.cmpConfig == rhs.cmpConfig && + lhs.syncCfg == rhs.syncCfg && + lhs.globalFilter == rhs.globalFilter && + lhs.firstPair == rhs.firstPair && + lhs.additionalPairs == rhs.additionalPairs && + lhs.onCompletion == rhs.onCompletion; +} + + +//facilitate drag & drop config merge: +MainConfiguration merge(const std::vector& mainCfgs); +} + +#endif // FREEFILESYNC_H_INCLUDED diff --git a/FreeFileSync/Source/synchronization.cpp b/FreeFileSync/Source/synchronization.cpp new file mode 100644 index 00000000..fbb7a499 --- /dev/null +++ b/FreeFileSync/Source/synchronization.cpp @@ -0,0 +1,2519 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "synchronization.h" +#include +#include +#include +#include //get rid!? +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lib/resolve_path.h" +#include "lib/db_file.h" +#include "lib/dir_exist_async.h" +#include "lib/cmp_filetime.h" +#include "lib/status_handler_impl.h" +#include "lib/versioning.h" + +#ifdef ZEN_WIN +#include +#include +#include "lib/shadow.h" +#endif + +using namespace zen; + + +namespace +{ +inline +int getCUD(const SyncStatistics& stat) +{ + return stat.getCreate() + + stat.getUpdate() + + stat.getDelete(); +} +} + +void SyncStatistics::init() +{ + createLeft = 0; + createRight = 0; + updateLeft = 0; + updateRight = 0; + deleteLeft = 0; + deleteRight = 0; + rowsTotal = 0; +} + + +SyncStatistics::SyncStatistics(const FolderComparison& folderCmp) +{ + init(); + std::for_each(begin(folderCmp), end(folderCmp), [&](const BaseDirPair& baseDirObj) { recurse(baseDirObj); }); +} + + +SyncStatistics::SyncStatistics(const HierarchyObject& hierObj) +{ + init(); + recurse(hierObj); +} + + +SyncStatistics::SyncStatistics(const FilePair& fileObj) +{ + init(); + processFile(fileObj); + rowsTotal += 1; +} + + +inline +void SyncStatistics::recurse(const HierarchyObject& hierObj) +{ + for (const FilePair& fileObj : hierObj.refSubFiles()) + processFile(fileObj); + for (const SymlinkPair& linkObj : hierObj.refSubLinks()) + processLink(linkObj); + for (const DirPair& dirObj : hierObj.refSubDirs()) + processDir(dirObj); + + rowsTotal += hierObj.refSubDirs(). size(); + rowsTotal += hierObj.refSubFiles().size(); + rowsTotal += hierObj.refSubLinks().size(); +} + + +inline +void SyncStatistics::processFile(const FilePair& fileObj) +{ + switch (fileObj.getSyncOperation()) //evaluate comparison result and sync direction + { + case SO_CREATE_NEW_LEFT: + ++createLeft; + dataToProcess += to(fileObj.getFileSize()); + break; + + case SO_CREATE_NEW_RIGHT: + ++createRight; + dataToProcess += to(fileObj.getFileSize()); + break; + + case SO_DELETE_LEFT: + ++deleteLeft; + break; + + case SO_DELETE_RIGHT: + ++deleteRight; + break; + + case SO_MOVE_LEFT_TARGET: + ++updateLeft; + break; + + case SO_MOVE_RIGHT_TARGET: + ++updateRight; + break; + + case SO_MOVE_LEFT_SOURCE: //ignore; already counted + case SO_MOVE_RIGHT_SOURCE: // + break; + + case SO_OVERWRITE_LEFT: + ++updateLeft; + dataToProcess += to(fileObj.getFileSize()); + break; + + case SO_OVERWRITE_RIGHT: + ++updateRight; + dataToProcess += to(fileObj.getFileSize()); + break; + + case SO_UNRESOLVED_CONFLICT: + conflictMsgs.push_back(std::make_pair(fileObj.getObjRelativeName(), fileObj.getSyncOpConflict())); + break; + + case SO_COPY_METADATA_TO_LEFT: + ++updateLeft; + break; + + case SO_COPY_METADATA_TO_RIGHT: + ++updateRight; + break; + + case SO_DO_NOTHING: + case SO_EQUAL: + break; + } +} + + +inline +void SyncStatistics::processLink(const SymlinkPair& linkObj) +{ + switch (linkObj.getSyncOperation()) //evaluate comparison result and sync direction + { + case SO_CREATE_NEW_LEFT: + ++createLeft; + break; + + case SO_CREATE_NEW_RIGHT: + ++createRight; + break; + + case SO_DELETE_LEFT: + ++deleteLeft; + break; + + case SO_DELETE_RIGHT: + ++deleteRight; + break; + + case SO_OVERWRITE_LEFT: + case SO_COPY_METADATA_TO_LEFT: + ++updateLeft; + break; + + case SO_OVERWRITE_RIGHT: + case SO_COPY_METADATA_TO_RIGHT: + ++updateRight; + break; + + case SO_UNRESOLVED_CONFLICT: + conflictMsgs.push_back(std::make_pair(linkObj.getObjRelativeName(), linkObj.getSyncOpConflict())); + break; + + case SO_MOVE_LEFT_SOURCE: + case SO_MOVE_RIGHT_SOURCE: + case SO_MOVE_LEFT_TARGET: + case SO_MOVE_RIGHT_TARGET: + assert(false); + case SO_DO_NOTHING: + case SO_EQUAL: + break; + } +} + + +inline +void SyncStatistics::processDir(const DirPair& dirObj) +{ + switch (dirObj.getSyncOperation()) //evaluate comparison result and sync direction + { + case SO_CREATE_NEW_LEFT: + ++createLeft; + break; + + case SO_CREATE_NEW_RIGHT: + ++createRight; + break; + + case SO_DELETE_LEFT: //if deletion variant == user-defined directory existing on other volume, this results in a full copy + delete operation! + ++deleteLeft; //however we cannot (reliably) anticipate this situation, fortunately statistics can be adapted during sync! + break; + + case SO_DELETE_RIGHT: + ++deleteRight; + break; + + case SO_UNRESOLVED_CONFLICT: + conflictMsgs.push_back(std::make_pair(dirObj.getObjRelativeName(), dirObj.getSyncOpConflict())); + break; + + case SO_COPY_METADATA_TO_LEFT: + ++updateLeft; + break; + + case SO_COPY_METADATA_TO_RIGHT: + ++updateRight; + break; + + case SO_OVERWRITE_LEFT: + case SO_OVERWRITE_RIGHT: + case SO_MOVE_LEFT_SOURCE: + case SO_MOVE_RIGHT_SOURCE: + case SO_MOVE_LEFT_TARGET: + case SO_MOVE_RIGHT_TARGET: + assert(false); + case SO_DO_NOTHING: + case SO_EQUAL: + break; + } + + recurse(dirObj); //since we model logical stats, we recurse, even if deletion variant is "recycler" or "versioning + same volume", which is a single physical operation! +} + +//----------------------------------------------------------------------------------------------------------- + +std::vector zen::extractSyncCfg(const MainConfiguration& mainCfg) +{ + //merge first and additional pairs + std::vector allPairs; + allPairs.push_back(mainCfg.firstPair); + allPairs.insert(allPairs.end(), + mainCfg.additionalPairs.begin(), //add additional pairs + mainCfg.additionalPairs.end()); + + std::vector output; + + //process all pairs + for (const FolderPairEnh& fp : allPairs) + { + SyncConfig syncCfg = fp.altSyncConfig.get() ? *fp.altSyncConfig : mainCfg.syncCfg; + + output.push_back( + FolderPairSyncCfg(syncCfg.directionCfg.var == DirectionConfig::TWOWAY || detectMovedFilesEnabled(syncCfg.directionCfg), + syncCfg.handleDeletion, + syncCfg.versioningStyle, + getFormattedDirectoryName(syncCfg.versioningDirectory))); + } + return output; +} + +//------------------------------------------------------------------------------------------------------------ + +namespace +{ +//test if user accidentally selected the wrong folders to sync +bool significantDifferenceDetected(const SyncStatistics& folderPairStat) +{ + //initial file copying shall not be detected as major difference + if ((folderPairStat.getCreate() == 0 || + folderPairStat.getCreate() == 0) && + folderPairStat.getUpdate () == 0 && + folderPairStat.getDelete () == 0 && + folderPairStat.getConflict() == 0) + return false; + + const int nonMatchingRows = folderPairStat.getCreate() + + folderPairStat.getDelete(); + //folderPairStat.getUpdate() + -> not relevant when testing for "wrong folder selected" + //folderPairStat.getConflict(); + + return nonMatchingRows >= 10 && nonMatchingRows > 0.5 * folderPairStat.getRowCount(); +} + +//################################################################################################################# + +class DeletionHandling //abstract deletion variants: permanently, recycle bin, user-defined directory +{ +public: + DeletionHandling(DeletionPolicy handleDel, //nothrow! + const Zstring& versioningDir, + VersioningStyle versioningStyle, + const TimeComp& timeStamp, + const Zstring& baseDirPf, //with separator postfix + ProcessCallback& procCallback); + ~DeletionHandling() + { + //always (try to) clean up, even if synchronization is aborted! + try + { + tryCleanup(false); //throw FileError, (throw X) + } + catch (FileError&) {} + catch (...) { assert(false); } //what is this? + /* + may block heavily, but still do not allow user callback: + -> avoid throwing user cancel exception again, leading to incomplete clean-up! + */ + } + + //clean-up temporary directory (recycle bin optimization) + void tryCleanup(bool allowUserCallback = true); //throw FileError; throw X -> call this in non-exceptional coding, i.e. somewhere after sync! + + template void removeFileUpdating(const Zstring& fullName, const Zstring& relativeName, const Int64& bytesExpected, Function notifyItemDeletion); //throw FileError + template void removeDirUpdating (const Zstring& fullName, const Zstring& relativeName, const Int64& bytesExpected, Function notifyItemDeletion); //reports ONLY data delta via updateProcessedData()! + template void removeLinkUpdating(const Zstring& fullName, const Zstring& relativeName, const Int64& bytesExpected, Function notifyItemDeletion); // + + const std::wstring& getTxtRemovingFile () const { return txtRemovingFile; } // + const std::wstring& getTxtRemovingSymLink() const { return txtRemovingSymlink; } //buffered status texts + const std::wstring& getTxtRemovingDir () const { return txtRemovingDirectory; } // + +private: + DeletionHandling(const DeletionHandling&); + DeletionHandling& operator=(const DeletionHandling&); + + FileVersioner& getOrCreateVersioner() //throw FileError! => dont create in DeletionHandling()!!! + { + if (!versioner.get()) + versioner = make_unique(versioningDir_, versioningStyle_, timeStamp_); //throw FileError + return *versioner; + }; + + ProcessCallback& procCallback_; + const Zstring baseDirPf_; //ends with path separator + const Zstring versioningDir_; + const VersioningStyle versioningStyle_; + const TimeComp timeStamp_; + +#ifdef ZEN_WIN + Zstring getOrCreateRecyclerTempDirPf(); //throw FileError + Zstring recyclerTmpDir; //temporary folder holding files/folders for *deferred* recycling + std::vector toBeRecycled; //full path of files located in temporary folder, waiting for batch-recycling +#endif + + //magage three states: allow dynamic fallback from recycler to permanent deletion + const DeletionPolicy deletionPolicy_; + std::unique_ptr versioner; //used for DELETE_TO_VERSIONING; throw FileError in constructor => create on demand! + + //buffer status texts: + std::wstring txtRemovingFile; + std::wstring txtRemovingSymlink; + std::wstring txtRemovingDirectory; +}; + + +DeletionHandling::DeletionHandling(DeletionPolicy handleDel, //nothrow! + const Zstring& versioningDir, + VersioningStyle versioningStyle, + const TimeComp& timeStamp, + const Zstring& baseDirPf, //with separator postfix + ProcessCallback& procCallback) : + procCallback_(procCallback), + baseDirPf_(baseDirPf), + versioningDir_(versioningDir), + versioningStyle_(versioningStyle), + timeStamp_(timeStamp), + deletionPolicy_(handleDel) +{ + switch (deletionPolicy_) + { + case DELETE_PERMANENTLY: + txtRemovingFile = _("Deleting file %x" ); + txtRemovingDirectory = _("Deleting folder %x" ); + txtRemovingSymlink = _("Deleting symbolic link %x"); + break; + + case DELETE_TO_RECYCLER: + txtRemovingFile = _("Moving file %x to the recycle bin" ); + txtRemovingDirectory = _("Moving folder %x to the recycle bin" ); + txtRemovingSymlink = _("Moving symbolic link %x to the recycle bin"); + break; + + case DELETE_TO_VERSIONING: + txtRemovingFile = replaceCpy(_("Moving file %x to %y" ), L"%y", fmtFileName(versioningDir_)); + txtRemovingDirectory = replaceCpy(_("Moving folder %x to %y" ), L"%y", fmtFileName(versioningDir_)); + txtRemovingSymlink = replaceCpy(_("Moving symbolic link %x to %y"), L"%y", fmtFileName(versioningDir_)); + break; + } +} + + +#ifdef ZEN_WIN +class CallbackMassRecycling : public CallbackRecycling +{ +public: + CallbackMassRecycling(ProcessCallback& statusHandler) : + statusHandler_(statusHandler), + txtRecyclingFile(_("Moving file %x to the recycle bin")) {} + + //may throw: first exception is swallowed, updateStatus() is then called again where it should throw again and the exception will propagate as expected + virtual void updateStatus(const Zstring& currentItem) + { + if (!currentItem.empty()) + statusHandler_.reportStatus(replaceCpy(txtRecyclingFile, L"%x", fmtFileName(currentItem))); //throw ? + else + statusHandler_.requestUiRefresh(); //throw ? + } + +private: + ProcessCallback& statusHandler_; + const std::wstring txtRecyclingFile; +}; + + +//create + returns temporary directory postfixed with file name separator +//to support later cleanup if automatic deletion fails for whatever reason +Zstring DeletionHandling::getOrCreateRecyclerTempDirPf() //throw FileError +{ + assert(!baseDirPf_.empty()); + if (baseDirPf_.empty()) + return Zstring(); + + if (recyclerTmpDir.empty()) + { + recyclerTmpDir = [&] + { + assert(endsWith(baseDirPf_, FILE_NAME_SEPARATOR)); + /* + -> this naming convention is too cute and confusing for end users: + + //1. generate random directory name + static std::mt19937 rng(std::time(nullptr)); //don't use std::default_random_engine and leave the choice to the STL implementer! + //- the alternative std::random_device may not always be available and can even throw an exception! + //- seed with second precision is sufficient: collisions are handled below + + const Zstring chars(Zstr("abcdefghijklmnopqrstuvwxyz") + Zstr("1234567890")); + std::uniform_int_distribution distrib(0, chars.size() - 1); //takes closed range + + auto generatePath = [&]() -> Zstring //e.g. C:\Source\3vkf74fq.ffs_tmp + { + Zstring path = baseDirPf; + for (int i = 0; i < 8; ++i) + path += chars[distrib(rng)]; + return path + TEMP_FILE_ENDING; + }; + */ + + //ensure unique ownership: + Zstring dirname = baseDirPf_ + Zstr("RecycleBin") + TEMP_FILE_ENDING; + for (int i = 1;; ++i) + try + { + makeDirectory(dirname, /*bool failIfExists*/ true); //throw FileError, ErrorTargetExisting + return dirname; + } + catch (const ErrorTargetExisting&) + { + dirname = baseDirPf_ + Zstr("RecycleBin") + Zchar('_') + numberTo(i) + TEMP_FILE_ENDING; + } + }(); + } + //assemble temporary recycle bin directory with random name and .ffs_tmp ending + return appendSeparator(recyclerTmpDir); +} +#endif + + +void DeletionHandling::tryCleanup(bool allowUserCallback) //throw FileError; throw X +{ + switch (deletionPolicy_) + { + case DELETE_PERMANENTLY: + break; + + case DELETE_TO_RECYCLER: +#ifdef ZEN_WIN + if (!toBeRecycled.empty()) + { + //move content of temporary directory to recycle bin in a single call + CallbackMassRecycling cbmr(procCallback_); + recycleOrDelete(toBeRecycled, allowUserCallback ? &cbmr : nullptr); //throw FileError + toBeRecycled.clear(); + } + + //clean up temp directory itself (should contain remnant empty directories only) + if (!recyclerTmpDir.empty()) + { + removeDirectory(recyclerTmpDir); //throw FileError + recyclerTmpDir.clear(); + } +#endif + break; + + case DELETE_TO_VERSIONING: + //if (versioner.get()) + //{ + // if (allowUserCallback) + // { + // procCallback_.reportStatus(_("Removing old versions...")); //throw ? + // versioner->limitVersions([&] { procCallback_.requestUiRefresh(); /*throw ? */ }); //throw FileError + // } + // else + // versioner->limitVersions([] {}); //throw FileError + //} + break; + } +} + + +template +struct CallbackRemoveDirImpl : public CallbackRemoveDir +{ + CallbackRemoveDirImpl(ProcessCallback& statusHandler, + const DeletionHandling& delHandling, + Function notifyItemDeletion) : + statusHandler_(statusHandler), + notifyItemDeletion_(notifyItemDeletion), + txtDeletingFile (delHandling.getTxtRemovingFile()), + txtDeletingFolder(delHandling.getTxtRemovingDir ()) {} + +private: + virtual void onBeforeFileDeletion(const Zstring& filename) { notifyDeletion(txtDeletingFile, filename); } + virtual void onBeforeDirDeletion (const Zstring& dirname ) { notifyDeletion(txtDeletingFolder, dirname ); } + + void notifyDeletion(const std::wstring& statusText, const Zstring& objName) + { + notifyItemDeletion_(); //it would be more correct to report *after* work was done! + statusHandler_.reportStatus(replaceCpy(statusText, L"%x", fmtFileName(objName))); + } + + ProcessCallback& statusHandler_; + Function notifyItemDeletion_; + const std::wstring txtDeletingFile; + const std::wstring txtDeletingFolder; +}; + + +template +class CallbackMoveDirImpl : public CallbackMoveDir +{ +public: + CallbackMoveDirImpl(ProcessCallback& callback, + Int64& bytesReported, + Function notifyItemDeletion) : + callback_ (callback), + bytesReported_(bytesReported), + notifyItemDeletion_(notifyItemDeletion), + txtMovingFile (_("Moving file %x to %y")), + txtMovingFolder (_("Moving folder %x to %y")) {} + +private: + virtual void onBeforeFileMove(const Zstring& fileFrom, const Zstring& fileTo) { notifyMove(txtMovingFile, fileFrom, fileTo); } + virtual void onBeforeDirMove (const Zstring& dirFrom, const Zstring& dirTo ) { notifyMove(txtMovingFolder, dirFrom, dirTo); } + + void notifyMove(const std::wstring& statusText, const Zstring& fileFrom, const Zstring& fileTo) const + { + notifyItemDeletion_(); //it would be more correct to report *after* work was done! + callback_.reportStatus(replaceCpy(replaceCpy(statusText, L"%x", L"\n" + fmtFileName(fileFrom)), L"%y", L"\n" + fmtFileName(fileTo))); + }; + + virtual void updateStatus(Int64 bytesDelta) + { + callback_.updateProcessedData(0, bytesDelta); //throw()! -> ensure client and service provider are in sync! + bytesReported_ += bytesDelta; // + + callback_.requestUiRefresh(); //may throw + } + + ProcessCallback& callback_; + Int64& bytesReported_; + Function notifyItemDeletion_; + const std::wstring txtMovingFile; + const std::wstring txtMovingFolder; +}; + + +template +void DeletionHandling::removeDirUpdating(const Zstring& fullName, + const Zstring& relativeName, + const Int64& bytesExpected, Function notifyItemDeletion) //throw FileError +{ + Int64 bytesReported; + ScopeGuard guardStatistics = makeGuard([&] { procCallback_.updateTotalData(0, bytesReported); }); //error = unexpected increase of total workload + + switch (deletionPolicy_) + { + case DELETE_PERMANENTLY: + { + CallbackRemoveDirImpl remDirCallback(procCallback_, *this, notifyItemDeletion); + removeDirectory(fullName, &remDirCallback); + } + break; + + case DELETE_TO_RECYCLER: + { +#ifdef ZEN_WIN + const Zstring targetDir = getOrCreateRecyclerTempDirPf() + relativeName; //throw FileError + bool deleted = false; + + auto moveToTempDir = [&] + { + try + { + //performance optimization: Instead of moving each object into recycle bin separately, + //we rename them one by one into a temporary directory and batch-recycle this directory after sync + renameFile(fullName, targetDir); //throw FileError, ErrorDifferentVolume + this->toBeRecycled.push_back(targetDir); + deleted = true; + } + catch (ErrorDifferentVolume&) //MoveFileEx() returns ERROR_PATH_NOT_FOUND *before* considering ERROR_NOT_SAME_DEVICE! => we have to create targetDir in any case! + { + deleted = recycleOrDelete(fullName); //throw FileError + } + }; + + try + { + moveToTempDir(); //throw FileError, ErrorDifferentVolume + } + catch (FileError&) + { + if (somethingExists(fullName)) + { + const Zstring targetSuperDir = beforeLast(targetDir, FILE_NAME_SEPARATOR); //what if C:\ ? + if (!dirExists(targetSuperDir)) + { + makeDirectory(targetSuperDir); //throw FileError -> may legitimately fail on Linux if permissions are missing + moveToTempDir(); //throw FileError -> this should work now! + } + else + throw; + } + } +#elif defined ZEN_LINUX || defined ZEN_MAC + const bool deleted = recycleOrDelete(fullName); //throw FileError +#endif + if (deleted) + notifyItemDeletion(); //moving to recycler is ONE logical operation, irrespective of the number of child elements! + } + break; + + case DELETE_TO_VERSIONING: + { + CallbackMoveDirImpl callback(procCallback_, bytesReported, notifyItemDeletion); + getOrCreateVersioner().revisionDir(fullName, relativeName, callback); //throw FileError + } + break; + } + + //update statistics to consider the real amount of data + guardStatistics.dismiss(); + if (bytesReported != bytesExpected) + procCallback_.updateTotalData(0, bytesReported - bytesExpected); //noexcept! +} + + +template +void DeletionHandling::removeFileUpdating(const Zstring& fullName, + const Zstring& relativeName, + const Int64& bytesExpected, Function notifyItemDeletion) //throw FileError +{ + Int64 bytesReported; + auto guardStatistics = makeGuard([&] { procCallback_.updateTotalData(0, bytesReported); }); //error = unexpected increase of total workload + bool deleted = false; + + if (endsWith(relativeName, TEMP_FILE_ENDING)) //special rule for .ffs_tmp files: always delete permanently! + deleted = zen::removeFile(fullName); + else + switch (deletionPolicy_) + { + case DELETE_PERMANENTLY: + deleted = zen::removeFile(fullName); //[!] scope specifier resolves nameclash! + break; + + case DELETE_TO_RECYCLER: +#ifdef ZEN_WIN + { + const Zstring targetFile = getOrCreateRecyclerTempDirPf() + relativeName; //throw FileError + + auto moveToTempDir = [&] + { + try + { + //performance optimization: Instead of moving each object into recycle bin separately, + //we rename them one by one into a temporary directory and batch-recycle this directory after sync + renameFile(fullName, targetFile); //throw FileError, ErrorDifferentVolume + this->toBeRecycled.push_back(targetFile); + deleted = true; + } + catch (ErrorDifferentVolume&) //MoveFileEx() returns ERROR_PATH_NOT_FOUND *before* considering ERROR_NOT_SAME_DEVICE! => we have to create targetDir in any case! + { + deleted = recycleOrDelete(fullName); //throw FileError + } + }; + + try + { + moveToTempDir(); //throw FileError, ErrorDifferentVolume + } + catch (FileError&) + { + if (somethingExists(fullName)) + { + const Zstring targetDir = beforeLast(targetFile, FILE_NAME_SEPARATOR); + if (!dirExists(targetDir)) + { + makeDirectory(targetDir); //throw FileError -> may legitimately fail on Linux if permissions are missing + moveToTempDir(); //throw FileError -> this should work now! + } + else + throw; + } + } + } +#elif defined ZEN_LINUX || defined ZEN_MAC + deleted = recycleOrDelete(fullName); //throw FileError +#endif + break; + + case DELETE_TO_VERSIONING: + { + struct CallbackMoveFileImpl : public CallbackMoveFile + { + CallbackMoveFileImpl(ProcessCallback& callback, Int64& bytes) : callback_(callback), bytesReported_(bytes) {} + + private: + virtual void updateStatus(Int64 bytesDelta) + { + callback_.updateProcessedData(0, bytesDelta); //throw()! -> ensure client and service provider are in sync! + bytesReported_ += bytesDelta; // + + callback_.requestUiRefresh(); //may throw + } + ProcessCallback& callback_; + Int64& bytesReported_; + } cb(procCallback_, bytesReported); + + deleted = getOrCreateVersioner().revisionFile(fullName, relativeName, cb); //throw FileError + } + break; + } + if (deleted) + notifyItemDeletion(); + + //update statistics to consider the real amount of data + guardStatistics.dismiss(); + if (bytesReported != bytesExpected) + procCallback_.updateTotalData(0, bytesReported - bytesExpected); //noexcept! +} + + +template inline +void DeletionHandling::removeLinkUpdating(const Zstring& fullName, const Zstring& relativeName, const Int64& bytesExpected, Function notifyItemDeletion) //throw FileError +{ + if (dirExists(fullName)) //dir symlink + return removeDirUpdating(fullName, relativeName, bytesExpected, notifyItemDeletion); //throw FileError + else //file symlink, broken symlink + return removeFileUpdating(fullName, relativeName, bytesExpected, notifyItemDeletion); //throw FileError +} + +//------------------------------------------------------------------------------------------------------------ + +/* + DELETE_PERMANENTLY: deletion frees space + DELETE_TO_RECYCLER: won't free space until recycler is full, but then frees space + DELETE_TO_VERSIONING: depends on whether versioning folder is on a different volume +-> if deleted item is a followed symlink, no space is freed +-> created/updated/deleted item may be on a different volume than base directory: consider symlinks, junctions! + +=> generally assume deletion frees space; may avoid false positive disk space warnings for recycler and versioning +*/ +class MinimumDiskSpaceNeeded +{ +public: + static std::pair calculate(const BaseDirPair& baseObj) + { + MinimumDiskSpaceNeeded inst; + inst.recurse(baseObj); + return std::make_pair(inst.spaceNeededLeft, inst.spaceNeededRight); + } + +private: + MinimumDiskSpaceNeeded() {} + + void recurse(const HierarchyObject& hierObj) + { + //don't process directories + + //process files + for (const FilePair& fileObj : hierObj.refSubFiles()) + switch (fileObj.getSyncOperation()) //evaluate comparison result and sync direction + { + case SO_CREATE_NEW_LEFT: + spaceNeededLeft += to(fileObj.getFileSize()); + break; + + case SO_CREATE_NEW_RIGHT: + spaceNeededRight += to(fileObj.getFileSize()); + break; + + case SO_DELETE_LEFT: + //if (freeSpaceDelLeft_) + spaceNeededLeft -= to(fileObj.getFileSize()); + break; + + case SO_DELETE_RIGHT: + //if (freeSpaceDelRight_) + spaceNeededRight -= to(fileObj.getFileSize()); + break; + + case SO_OVERWRITE_LEFT: + //if (freeSpaceDelLeft_) + spaceNeededLeft -= to(fileObj.getFileSize()); + spaceNeededLeft += to(fileObj.getFileSize()); + break; + + case SO_OVERWRITE_RIGHT: + //if (freeSpaceDelRight_) + spaceNeededRight -= to(fileObj.getFileSize()); + spaceNeededRight += to(fileObj.getFileSize()); + break; + + case SO_DO_NOTHING: + case SO_EQUAL: + case SO_UNRESOLVED_CONFLICT: + case SO_COPY_METADATA_TO_LEFT: + case SO_COPY_METADATA_TO_RIGHT: + case SO_MOVE_LEFT_SOURCE: + case SO_MOVE_RIGHT_SOURCE: + case SO_MOVE_LEFT_TARGET: + case SO_MOVE_RIGHT_TARGET: + break; + } + + //symbolic links + //[...] + + //recurse into sub-dirs + for (auto& subDir : hierObj.refSubDirs()) + recurse(subDir); + } + + Int64 spaceNeededLeft; + Int64 spaceNeededRight; +}; + +//---------------------------------------------------------------------------------------- + +class SynchronizeFolderPair +{ +public: + SynchronizeFolderPair(ProcessCallback& procCallback, + bool verifyCopiedFiles, + bool copyFilePermissions, + bool transactionalFileCopy, +#ifdef ZEN_WIN + shadow::ShadowCopy* shadowCopyHandler, +#endif + DeletionHandling& delHandlingLeft, + DeletionHandling& delHandlingRight) : + procCallback_(procCallback), +#ifdef ZEN_WIN + shadowCopyHandler_(shadowCopyHandler), +#endif + delHandlingLeft_(delHandlingLeft), + delHandlingRight_(delHandlingRight), + verifyCopiedFiles_(verifyCopiedFiles), + copyFilePermissions_(copyFilePermissions), + transactionalFileCopy_(transactionalFileCopy), + txtCreatingFile (_("Creating file %x" )), + txtCreatingLink (_("Creating symbolic link %x" )), + txtCreatingFolder (_("Creating folder %x" )), + txtOverwritingFile (_("Overwriting file %x" )), + txtOverwritingLink (_("Overwriting symbolic link %x")), + txtVerifying (_("Verifying file %x" )), + txtWritingAttributes(_("Updating attributes of %x" )), + txtMovingFile (_("Moving file %x to %y")) + {} + + void startSync(BaseDirPair& baseDirObj) + { + runZeroPass(baseDirObj); //first process file moves + runPass(baseDirObj); //delete files (or overwrite big ones with smaller ones) + runPass(baseDirObj); //copy rest + } + +private: + enum PassId + { + PASS_ONE, //delete files + PASS_TWO, //create, modify + PASS_NEVER //skip + }; + + static PassId getPass(const FilePair& fileObj); + static PassId getPass(const SymlinkPair& linkObj); + static PassId getPass(const DirPair& dirObj); + + template + void prepare2StepMove(FilePair& sourceObj, FilePair& targetObj); //throw FileError + bool createParentDir(FileSystemObject& fsObj); //throw FileError + template + void manageFileMove(FilePair& sourceObj, FilePair& targetObj); //throw FileError + + void runZeroPass(HierarchyObject& hierObj); + template + void runPass(HierarchyObject& hierObj); + + void synchronizeFile(FilePair& fileObj); + template void synchronizeFileInt(FilePair& fileObj, SyncOperation syncOp); + + void synchronizeLink(SymlinkPair& linkObj); + template void synchronizeLinkInt(SymlinkPair& linkObj, SyncOperation syncOp); + + void synchronizeFolder(DirPair& dirObj); + template void synchronizeFolderInt(DirPair& dirObj, SyncOperation syncOp); + + void reportStatus(const std::wstring& rawText, const Zstring& objname) const { procCallback_.reportStatus(replaceCpy(rawText, L"%x", fmtFileName(objname))); }; + void reportInfo (const std::wstring& rawText, const Zstring& objname) const { procCallback_.reportInfo (replaceCpy(rawText, L"%x", fmtFileName(objname))); }; + void reportInfo (const std::wstring& rawText, + const Zstring& objname1, + const Zstring& objname2) const + { + procCallback_.reportInfo(replaceCpy(replaceCpy(rawText, L"%x", L"\n" + fmtFileName(objname1)), L"%y", L"\n" + fmtFileName(objname2))); + }; + + template + InSyncAttributes copyFileUpdating(const Zstring& sourceFile, const Zstring& targetFile, const Int64& bytesExpected, Function delTargetCommand) const; //throw FileError; reports data delta via updateProcessedData() + void verifyFileCopy(const Zstring& source, const Zstring& target) const; + + template + DeletionHandling& getDelHandling(); + + ProcessCallback& procCallback_; +#ifdef ZEN_WIN + shadow::ShadowCopy* shadowCopyHandler_; //optional! +#endif + DeletionHandling& delHandlingLeft_; + DeletionHandling& delHandlingRight_; + + const bool verifyCopiedFiles_; + const bool copyFilePermissions_; + const bool transactionalFileCopy_; + + //preload status texts + const std::wstring txtCreatingFile; + const std::wstring txtCreatingLink; + const std::wstring txtCreatingFolder; + const std::wstring txtOverwritingFile; + const std::wstring txtOverwritingLink; + const std::wstring txtVerifying; + const std::wstring txtWritingAttributes; + const std::wstring txtMovingFile; +}; + +//--------------------------------------------------------------------------------------------------------------- + +template <> inline +DeletionHandling& SynchronizeFolderPair::getDelHandling() { return delHandlingLeft_; } + +template <> inline +DeletionHandling& SynchronizeFolderPair::getDelHandling() { return delHandlingRight_; } + +/* +__________________________ +|Move algorithm, 0th pass| +-------------------------- +1. loop over hierarchy and find "move source" + +2. check whether parent directory of "move source" is going to be deleted or location of "move source" may lead to name clash with other dir/symlink + -> no: delay move until 2nd pass + +3. create move target's parent directory recursively + execute move + do we have name clash? + -> prepare a 2-step move operation: 1. move source to root and update "move target" accordingly 2. delay move until 2nd pass + +4. If any of the operations above did not succeed (even after retry), update statistics and revert to "copy + delete" + Note: first pass may delete "move source"!!! + +__________________ +|killer-scenarios| +------------------ +propagate the following move sequences: +I) a -> a/a caveat sync'ing parent directory first leads to circular dependency! + +II) a/a -> a caveat: fixing name clash will remove source! + +III) c -> d caveat: move-sequence needs to be processed in correct order! + b -> c/b + a -> b/a +*/ + +template inline +bool haveNameClash(const Zstring& shortname, List& m) +{ + return std::any_of(m.begin(), m.end(), + [&](const typename List::value_type& obj) { return EqualFilename()(obj.getObjShortName(), shortname); }); +} + + +Zstring findUnusedTempName(const Zstring& filename) +{ + Zstring output = filename + zen::TEMP_FILE_ENDING; + + //ensure uniqueness (+ minor file system race condition!) + for (int i = 1; somethingExists(output); ++i) + output = filename + Zchar('_') + numberTo(i) + zen::TEMP_FILE_ENDING; + + return output; +} + + +template +void SynchronizeFolderPair::prepare2StepMove(FilePair& sourceObj, + FilePair& targetObj) //throw FileError +{ + const Zstring& source = sourceObj.getFullName(); + const Zstring& tmpTarget = findUnusedTempName(sourceObj.getBaseDirPf() + sourceObj.getShortName()); + //this could still lead to a name-clash in obscure cases, if some file exists on the other side with + //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 the move + + reportInfo(txtMovingFile, source, tmpTarget); + + warn_static("was wenn diff volume: symlink aliasing!") //throw FileError, ErrorDifferentVolume, ErrorTargetExisting + + renameFile(source, tmpTarget); //throw FileError + + //update file hierarchy + const FileDescriptor descrSource(sourceObj.getLastWriteTime (), + sourceObj.getFileSize (), + sourceObj.getFileId (), + sourceObj.isFollowedSymlink()); + + FilePair& tempFile = sourceObj.root().addSubFile(afterLast(tmpTarget, FILE_NAME_SEPARATOR), descrSource); + static_assert(IsSameType, HierarchyObject::SubFileVec>::value, + "ATTENTION: we're adding to the file list WHILE looping over it! This is only working because FixedList iterators are not invalidated by insertion!"); + sourceObj.removeObject(); //remove only *after* evaluating "sourceObj, side"! + + //prepare move in second pass + tempFile.setSyncDir(side == LEFT_SIDE ? SyncDirection::LEFT : SyncDirection::RIGHT); + + targetObj.setMoveRef(tempFile .getId()); + tempFile .setMoveRef(targetObj.getId()); + + //NO statistics update! + procCallback_.requestUiRefresh(); //may throw +} + + +bool SynchronizeFolderPair::createParentDir(FileSystemObject& fsObj) //throw FileError, "false" on name clash +{ + if (DirPair* parentDir = dynamic_cast(&fsObj.parent())) + { + if (!createParentDir(*parentDir)) + return false; + + //detect (and try to resolve) file type conflicts: 1. symlinks 2. files + const Zstring& shortname = parentDir->getObjShortName(); + if (haveNameClash(shortname, parentDir->parent().refSubLinks()) || + haveNameClash(shortname, parentDir->parent().refSubFiles())) + return false; + + //in this context "parentDir" cannot be scheduled for deletion since it contains a "move target"! + //note: if parentDir were deleted, we'd end up destroying "fsObj"! + assert(parentDir->getSyncOperation() != SO_DELETE_LEFT && + parentDir->getSyncOperation() != SO_DELETE_RIGHT); + + synchronizeFolder(*parentDir); //throw FileError + } + return true; +} + + +template +void SynchronizeFolderPair::manageFileMove(FilePair& sourceObj, + FilePair& targetObj) //throw FileError +{ + assert((sourceObj.getSyncOperation() == SO_MOVE_LEFT_SOURCE && targetObj.getSyncOperation() == SO_MOVE_LEFT_TARGET && side == LEFT_SIDE) || + (sourceObj.getSyncOperation() == SO_MOVE_RIGHT_SOURCE && targetObj.getSyncOperation() == SO_MOVE_RIGHT_TARGET && side == RIGHT_SIDE)); + + const bool sourceWillBeDeleted = [&]() -> bool + { + if (DirPair* parentDir = dynamic_cast(&sourceObj.parent())) + { + switch (parentDir->getSyncOperation()) //evaluate comparison result and sync direction + { + case SO_DELETE_LEFT: + case SO_DELETE_RIGHT: + return true; //we need to do something about it + case SO_MOVE_LEFT_SOURCE: + case SO_MOVE_RIGHT_SOURCE: + case SO_MOVE_LEFT_TARGET: + case SO_MOVE_RIGHT_TARGET: + case SO_OVERWRITE_LEFT: + case SO_OVERWRITE_RIGHT: + case SO_CREATE_NEW_LEFT: + case SO_CREATE_NEW_RIGHT: + case SO_DO_NOTHING: + case SO_EQUAL: + case SO_UNRESOLVED_CONFLICT: + case SO_COPY_METADATA_TO_LEFT: + case SO_COPY_METADATA_TO_RIGHT: + break; + } + } + return false; + }(); + + auto haveNameClash = [](const FilePair& fileObj) + { + return ::haveNameClash(fileObj.getObjShortName(), fileObj.parent().refSubLinks()) || + ::haveNameClash(fileObj.getObjShortName(), fileObj.parent().refSubDirs()); + }; + + if (sourceWillBeDeleted || haveNameClash(sourceObj)) + { + //prepare for move now: - revert to 2-step move on name clashes + if (haveNameClash(targetObj) || + !createParentDir(targetObj)) //throw FileError + return prepare2StepMove(sourceObj, targetObj); //throw FileError + + //finally start move! this should work now: + synchronizeFile(targetObj); //throw FileError + //SynchronizeFolderPair::synchronizeFileInt() is *not* expecting SO_MOVE_LEFT_SOURCE/SO_MOVE_RIGHT_SOURCE => start move from targetObj, not sourceObj! + } + //else: sourceObj will not be deleted, and is not standing in the way => delay to second pass + //note: this case may include new "move sources" from two-step sub-routine!!! +} + + +//search for file move-operations +void SynchronizeFolderPair::runZeroPass(HierarchyObject& hierObj) +{ + for (FilePair& fileObj : hierObj.refSubFiles()) + { + const SyncOperation syncOp = fileObj.getSyncOperation(); + switch (syncOp) //evaluate comparison result and sync direction + { + case SO_MOVE_LEFT_SOURCE: + case SO_MOVE_RIGHT_SOURCE: + if (FilePair* targetObj = dynamic_cast(FileSystemObject::retrieve(fileObj.getMoveRef()))) + { + FilePair* sourceObj = &fileObj; + assert(dynamic_cast(FileSystemObject::retrieve(targetObj->getMoveRef())) == sourceObj); + + zen::Opt errMsg = tryReportingError([&] + { + if (syncOp == SO_MOVE_LEFT_SOURCE) + this->manageFileMove(*sourceObj, *targetObj); //throw FileError + else + this->manageFileMove(*sourceObj, *targetObj); // + }, procCallback_); + + if (errMsg) + { + //move operation has failed! We cannot allow to continue and have move source's parent directory deleted, messing up statistics! + // => revert to ordinary "copy + delete" + + auto getStat = [&]() -> std::pair + { + SyncStatistics statSrc(*sourceObj); + SyncStatistics statTrg(*targetObj); + + return std::make_pair(getCUD(statSrc) + getCUD(statTrg), + statSrc.getDataToProcess() + statTrg.getDataToProcess()); + }; + + const auto statBefore = getStat(); + sourceObj->setMoveRef(nullptr); + targetObj->setMoveRef(nullptr); + const auto statAfter = getStat(); + //fix statistics to total to match "copy + delete" + procCallback_.updateTotalData(statAfter.first - statBefore.first, statAfter.second - statBefore.second); + } + } + break; + + case SO_MOVE_LEFT_TARGET: //it's enough to try each move-pair *once* + case SO_MOVE_RIGHT_TARGET: // + case SO_DELETE_LEFT: + case SO_DELETE_RIGHT: + case SO_OVERWRITE_LEFT: + case SO_OVERWRITE_RIGHT: + case SO_CREATE_NEW_LEFT: + case SO_CREATE_NEW_RIGHT: + case SO_DO_NOTHING: + case SO_EQUAL: + case SO_UNRESOLVED_CONFLICT: + case SO_COPY_METADATA_TO_LEFT: + case SO_COPY_METADATA_TO_RIGHT: + break; + } + } + + for (DirPair& dirObj : hierObj.refSubDirs()) + runZeroPass(dirObj); //recurse +} + +//--------------------------------------------------------------------------------------------------------------- + +//1st, 2nd pass requirements: +// - avoid disk space shortage: 1. delete files, 2. overwrite big with small files first +// - support change in type: overwrite file by directory, symlink by file, ect. + +inline +SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const FilePair& fileObj) +{ + switch (fileObj.getSyncOperation()) //evaluate comparison result and sync direction + { + case SO_DELETE_LEFT: + case SO_DELETE_RIGHT: + return PASS_ONE; + + case SO_OVERWRITE_LEFT: + return fileObj.getFileSize() > fileObj.getFileSize() ? PASS_ONE : PASS_TWO; + + case SO_OVERWRITE_RIGHT: + return fileObj.getFileSize() < fileObj.getFileSize() ? PASS_ONE : PASS_TWO; + + case SO_MOVE_LEFT_SOURCE: // + case SO_MOVE_RIGHT_SOURCE: // [!] + return PASS_NEVER; + case SO_MOVE_LEFT_TARGET: // + case SO_MOVE_RIGHT_TARGET: //make sure 2-step move is processed in second pass, after move *target* parent directory was created! + return PASS_TWO; + + case SO_CREATE_NEW_LEFT: + case SO_CREATE_NEW_RIGHT: + case SO_COPY_METADATA_TO_LEFT: + case SO_COPY_METADATA_TO_RIGHT: + return PASS_TWO; + + case SO_DO_NOTHING: + case SO_EQUAL: + case SO_UNRESOLVED_CONFLICT: + return PASS_NEVER; + } + assert(false); + return PASS_TWO; //dummy +} + + +inline +SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const SymlinkPair& linkObj) +{ + switch (linkObj.getSyncOperation()) //evaluate comparison result and sync direction + { + case SO_DELETE_LEFT: + case SO_DELETE_RIGHT: + return PASS_ONE; //make sure to delete symlinks in first pass, and equally named file or dir in second pass: usecase "overwrite symlink with regular file"! + + case SO_OVERWRITE_LEFT: + case SO_OVERWRITE_RIGHT: + case SO_CREATE_NEW_LEFT: + case SO_CREATE_NEW_RIGHT: + case SO_COPY_METADATA_TO_LEFT: + case SO_COPY_METADATA_TO_RIGHT: + return PASS_TWO; + + case SO_MOVE_LEFT_SOURCE: + case SO_MOVE_RIGHT_SOURCE: + case SO_MOVE_LEFT_TARGET: + case SO_MOVE_RIGHT_TARGET: + assert(false); + case SO_DO_NOTHING: + case SO_EQUAL: + case SO_UNRESOLVED_CONFLICT: + return PASS_NEVER; + } + assert(false); + return PASS_TWO; //dummy +} + + +inline +SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const DirPair& dirObj) +{ + switch (dirObj.getSyncOperation()) //evaluate comparison result and sync direction + { + case SO_DELETE_LEFT: + case SO_DELETE_RIGHT: + return PASS_ONE; + + case SO_CREATE_NEW_LEFT: + case SO_CREATE_NEW_RIGHT: + case SO_COPY_METADATA_TO_LEFT: + case SO_COPY_METADATA_TO_RIGHT: + return PASS_TWO; + + case SO_OVERWRITE_LEFT: + case SO_OVERWRITE_RIGHT: + case SO_MOVE_LEFT_SOURCE: + case SO_MOVE_RIGHT_SOURCE: + case SO_MOVE_LEFT_TARGET: + case SO_MOVE_RIGHT_TARGET: + assert(false); + case SO_DO_NOTHING: + case SO_EQUAL: + case SO_UNRESOLVED_CONFLICT: + return PASS_NEVER; + } + assert(false); + return PASS_TWO; //dummy +} + + +template +void SynchronizeFolderPair::runPass(HierarchyObject& hierObj) +{ + //synchronize files: + for (FilePair& fileObj : hierObj.refSubFiles()) + if (pass == this->getPass(fileObj)) //"this->" required by two-pass lookup as enforced by GCC 4.7 + tryReportingError([&] { synchronizeFile(fileObj); }, procCallback_); + + //synchronize symbolic links: + for (SymlinkPair& linkObj : hierObj.refSubLinks()) + if (pass == this->getPass(linkObj)) + tryReportingError([&] { synchronizeLink(linkObj); }, procCallback_); + + //synchronize folders: + for (DirPair& dirObj : hierObj.refSubDirs()) + { + if (pass == this->getPass(dirObj)) + tryReportingError([&] { synchronizeFolder(dirObj); }, procCallback_); + + this->runPass(dirObj); //recurse + } +} + +//--------------------------------------------------------------------------------------------------------------- + +inline +Opt getTargetDirection(SyncOperation syncOp) +{ + switch (syncOp) + { + case SO_CREATE_NEW_LEFT: + case SO_DELETE_LEFT: + case SO_OVERWRITE_LEFT: + case SO_COPY_METADATA_TO_LEFT: + case SO_MOVE_LEFT_SOURCE: + case SO_MOVE_LEFT_TARGET: + return LEFT_SIDE; + + case SO_CREATE_NEW_RIGHT: + case SO_DELETE_RIGHT: + case SO_OVERWRITE_RIGHT: + case SO_COPY_METADATA_TO_RIGHT: + case SO_MOVE_RIGHT_SOURCE: + case SO_MOVE_RIGHT_TARGET: + return RIGHT_SIDE; + + case SO_DO_NOTHING: + case SO_EQUAL: + case SO_UNRESOLVED_CONFLICT: + break; //nothing to do + } + return NoValue(); +} + + +inline +void SynchronizeFolderPair::synchronizeFile(FilePair& fileObj) +{ + const SyncOperation syncOp = fileObj.getSyncOperation(); + + if (Opt sideTrg = getTargetDirection(syncOp)) + { + if (*sideTrg == LEFT_SIDE) + synchronizeFileInt(fileObj, syncOp); + else + synchronizeFileInt(fileObj, syncOp); + } +} + + +template +void SynchronizeFolderPair::synchronizeFileInt(FilePair& fileObj, SyncOperation syncOp) +{ + static const SelectedSide sideSrc = OtherSide::result; + + switch (syncOp) + { + case SO_CREATE_NEW_LEFT: + case SO_CREATE_NEW_RIGHT: + { + if (const DirPair* parentDir = dynamic_cast(&fileObj.parent())) + if (parentDir->isEmpty()) //BaseDirPair OTOH is always non-empty and existing in this context => else: fatal error in zen::synchronize() + return; //if parent directory creation failed, there's no reason to show more errors! + + const Zstring& target = fileObj.getBaseDirPf() + fileObj.getRelativeName(); //can't use "getFullName" as target is not yet existing + reportInfo(txtCreatingFile, target); + + try + { + const InSyncAttributes newAttr = copyFileUpdating(fileObj.getFullName(), + target, + to(fileObj.getFileSize()), [] {} /*no target to delete*/); //throw FileError + //update FilePair + fileObj.setSyncedTo(fileObj.getShortName(), newAttr.fileSize, + newAttr.modificationTime, //target time set from source + newAttr.modificationTime, + newAttr.targetFileId, + newAttr.sourceFileId, + false, fileObj.isFollowedSymlink()); + + procCallback_.updateProcessedData(1, 0); //processed bytes are reported in copyFileUpdating()! + } + catch (FileError&) + { + if (somethingExists(fileObj.getFullName())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should error out! + throw; + //source deleted meanwhile...nothing was done (logical point of view!) + procCallback_.updateTotalData(-1, -to(fileObj.getFileSize())); + fileObj.removeObject(); //remove only *after* evaluating "fileObj, sideSrc"! + } + } + break; + + case SO_DELETE_LEFT: + case SO_DELETE_RIGHT: + reportInfo(getDelHandling().getTxtRemovingFile(), fileObj.getFullName()); + { + int objectsReported = 0; + auto guardStatistics = makeGuard([&] { procCallback_.updateTotalData(objectsReported, 0); }); //error = unexpected increase of total workload + const int objectsExpected = 1; + const Int64 bytesExpected = 0; + + getDelHandling().removeFileUpdating(fileObj.getFullName(), fileObj.getObjRelativeName(), bytesExpected, [&] //throw FileError + { + procCallback_.updateProcessedData(1, 0); //noexcept + ++objectsReported; + }); + + guardStatistics.dismiss(); //update statistics to consider the real amount of data + if (objectsReported != objectsExpected) + procCallback_.updateTotalData(objectsReported - objectsExpected, 0); //noexcept! + } + fileObj.removeObject(); //update FilePair + break; + + case SO_MOVE_LEFT_TARGET: + case SO_MOVE_RIGHT_TARGET: + if (FilePair* moveSource = dynamic_cast(FileSystemObject::retrieve(fileObj.getMoveRef()))) + { + FilePair* moveTarget = &fileObj; + + assert((moveSource->getSyncOperation() == SO_MOVE_LEFT_SOURCE && moveTarget->getSyncOperation() == SO_MOVE_LEFT_TARGET && sideTrg == LEFT_SIDE) || + (moveSource->getSyncOperation() == SO_MOVE_RIGHT_SOURCE && moveTarget->getSyncOperation() == SO_MOVE_RIGHT_TARGET && sideTrg == RIGHT_SIDE)); + + const Zstring& oldName = moveSource->getFullName(); + const Zstring& newName = moveSource->getBaseDirPf() + moveTarget->getRelativeName(); + + reportInfo(txtMovingFile, oldName, newName); + warn_static("was wenn diff volume: symlink aliasing!") //throw FileError, ErrorDifferentVolume, ErrorTargetExisting + renameFile(oldName, newName); //throw FileError + + //update FilePair + assert(moveSource->getFileSize() == moveTarget->getFileSize()); + moveTarget->setSyncedTo(moveTarget->getShortName(), moveTarget->getFileSize(), + moveSource->getLastWriteTime(), //awkward naming! moveSource is renamed on "sideTrg" side! + moveTarget->getLastWriteTime(), + moveSource->getFileId(), + moveTarget->getFileId(), + moveSource->isFollowedSymlink(), + moveTarget->isFollowedSymlink()); + moveSource->removeObject(); //remove only *after* evaluating "moveSource, sideTrg"! + + procCallback_.updateProcessedData(1, 0); + } + else (assert(false)); + break; + + case SO_OVERWRITE_LEFT: + case SO_OVERWRITE_RIGHT: + { + const Zstring targetFile = fileObj.isFollowedSymlink() ? //follow link when updating file rather than delete it and replace with regular file!!! + zen::getResolvedFilePath(fileObj.getFullName()) : //throw FileError + fileObj.getBaseDirPf() + fileObj.getRelativeName(); //respect differences in case of source object + + reportInfo(txtOverwritingFile, targetFile); + + if (fileObj.isFollowedSymlink()) //since we follow the link, we need to handle case sensitivity of the link manually! + if (fileObj.getShortName() != fileObj.getShortName()) //adapt difference in case (windows only) + renameFile(fileObj.getFullName(), + beforeLast(fileObj.getFullName(), FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + fileObj.getShortName()); //throw FileError + + const InSyncAttributes newAttr = copyFileUpdating(fileObj.getFullName(), + targetFile, + to(fileObj.getFileSize()), + [&] //delete target at appropriate time + { + reportStatus(this->getDelHandling().getTxtRemovingFile(), targetFile); + + this->getDelHandling().removeFileUpdating(targetFile, fileObj.getObjRelativeName(), 0, [] {}); //throw FileError; + //no (logical) item count update desired - but total byte count may change, e.g. move(copy) deleted file to versioning dir + + //fileObj.removeObject(); -> doesn't make sense for isFollowedSymlink(); "fileObj, sideTrg" evaluated below! + + //if fail-safe file copy is active, then the next operation will be a simple "rename" + //=> don't risk reportStatus() throwing GuiAbortProcess() leaving the target deleted rather than updated! + if (!transactionalFileCopy_) + reportStatus(txtOverwritingFile, targetFile); //restore status text copy file + }); //throw FileError + + //update FilePair + fileObj.setSyncedTo(fileObj.getShortName(), newAttr.fileSize, + newAttr.modificationTime, //target time set from source + newAttr.modificationTime, + newAttr.targetFileId, + newAttr.sourceFileId, + fileObj.isFollowedSymlink(), + fileObj.isFollowedSymlink()); + + procCallback_.updateProcessedData(1, 0); //we model "delete + copy" as ONE logical operation + } + break; + + case SO_COPY_METADATA_TO_LEFT: + case SO_COPY_METADATA_TO_RIGHT: + //harmonize with file_hierarchy.cpp::getSyncOpDescription!! + + reportInfo(txtWritingAttributes, fileObj.getFullName()); + + if (fileObj.getShortName() != fileObj.getShortName()) //adapt difference in case (windows only) + renameFile(fileObj.getFullName(), + beforeLast(fileObj.getFullName(), FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + fileObj.getShortName()); //throw FileError + + if (!sameFileTime(fileObj.getLastWriteTime(), fileObj.getLastWriteTime(), 2)) //respect 2 second FAT/FAT32 precision + setFileTime(fileObj.getFullName(), fileObj.getLastWriteTime(), SYMLINK_FOLLOW); //throw FileError + //do NOT read *current* source file time, but use buffered value which corresponds to time of comparison! + + //-> both sides *should* be completely equal now... + assert(fileObj.getFileSize() == fileObj.getFileSize()); + fileObj.setSyncedTo(fileObj.getShortName(), fileObj.getFileSize(), + fileObj.getLastWriteTime(), //target time set from source + fileObj.getLastWriteTime(), + fileObj.getFileId (), + fileObj.getFileId (), + fileObj.isFollowedSymlink(), + fileObj.isFollowedSymlink()); + + procCallback_.updateProcessedData(1, 0); + break; + + case SO_MOVE_LEFT_SOURCE: //use SO_MOVE_LEFT_TARGET/SO_MOVE_RIGHT_TARGET to execute move: + case SO_MOVE_RIGHT_SOURCE: //=> makes sure parent directory has been created + case SO_DO_NOTHING: + case SO_EQUAL: + case SO_UNRESOLVED_CONFLICT: + assert(false); //should have been filtered out by SynchronizeFolderPair::getPass() + return; //no update on processed data! + } + + procCallback_.requestUiRefresh(); //may throw +} + + +inline +void SynchronizeFolderPair::synchronizeLink(SymlinkPair& linkObj) +{ + const SyncOperation syncOp = linkObj.getSyncOperation(); + + if (Opt sideTrg = getTargetDirection(syncOp)) + { + if (*sideTrg == LEFT_SIDE) + synchronizeLinkInt(linkObj, syncOp); + else + synchronizeLinkInt(linkObj, syncOp); + } +} + + +template +void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& linkObj, SyncOperation syncOp) +{ + static const SelectedSide sideSrc = OtherSide::result; + + switch (syncOp) + { + case SO_CREATE_NEW_LEFT: + case SO_CREATE_NEW_RIGHT: + { + if (const DirPair* parentDir = dynamic_cast(&linkObj.parent())) + if (parentDir->isEmpty()) //BaseDirPair OTOH is always non-empty and existing in this context => else: fatal error in zen::synchronize() + return; //if parent directory creation failed, there's no reason to show more errors! + + const Zstring& target = linkObj.getBaseDirPf() + linkObj.getRelativeName(); + + reportInfo(txtCreatingLink, target); + + try + { + zen::copySymlink(linkObj.getFullName(), target, copyFilePermissions_); //throw FileError + //update SymlinkPair + linkObj.setSyncedTo(linkObj.getShortName(), + linkObj.getLastWriteTime(), //target time set from source + linkObj.getLastWriteTime()); + + procCallback_.updateProcessedData(1, 0); + } + catch (FileError&) + { + if (somethingExists(linkObj.getFullName())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should not be quiet about it! + throw; + //source deleted meanwhile...nothing was done (logical point of view!) + procCallback_.updateTotalData(-1, 0); + linkObj.removeObject(); + } + } + break; + + case SO_DELETE_LEFT: + case SO_DELETE_RIGHT: + reportInfo(getDelHandling().getTxtRemovingSymLink(), linkObj.getFullName()); + { + int objectsReported = 0; + auto guardStatistics = makeGuard([&] { procCallback_.updateTotalData(objectsReported, 0); }); //error = unexpected increase of total workload + const int objectsExpected = 1; + const Int64 bytesExpected = 0; + + getDelHandling().removeLinkUpdating(linkObj.getFullName(), linkObj.getObjRelativeName(), bytesExpected, [&] //throw FileError + { + procCallback_.updateProcessedData(1, 0); //noexcept + ++objectsReported; + }); + + guardStatistics.dismiss(); //update statistics to consider the real amount of data + if (objectsReported != objectsExpected) + procCallback_.updateTotalData(objectsReported - objectsExpected, 0); //noexcept! + } + linkObj.removeObject(); //update SymlinkPair + break; + + case SO_OVERWRITE_LEFT: + case SO_OVERWRITE_RIGHT: + reportInfo(txtOverwritingLink, linkObj.getFullName()); + + //reportStatus(getDelHandling().getTxtRemovingSymLink(), linkObj.getFullName()); + getDelHandling().removeLinkUpdating(linkObj.getFullName(), linkObj.getObjRelativeName(), 0, [] {}); //throw FileError + + //linkObj.removeObject(); -> "linkObj, sideTrg" evaluated below! + + //=> don't risk reportStatus() throwing GuiAbortProcess() leaving the target deleted rather than updated: + + //reportStatus(txtOverwritingLink, linkObj.getFullName()); //restore status text + zen::copySymlink(linkObj.getFullName(), + linkObj.getBaseDirPf() + linkObj.getRelativeName(), //respect differences in case of source object + copyFilePermissions_); //throw FileError + + //update SymlinkPair + linkObj.setSyncedTo(linkObj.getShortName(), + linkObj.getLastWriteTime(), //target time set from source + linkObj.getLastWriteTime()); + + procCallback_.updateProcessedData(1, 0); //we model "delete + copy" as ONE logical operation + break; + + case SO_COPY_METADATA_TO_LEFT: + case SO_COPY_METADATA_TO_RIGHT: + reportInfo(txtWritingAttributes, linkObj.getFullName()); + + if (linkObj.getShortName() != linkObj.getShortName()) //adapt difference in case (windows only) + renameFile(linkObj.getFullName(), + beforeLast(linkObj.getFullName(), FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + linkObj.getShortName()); //throw FileError + + if (!sameFileTime(linkObj.getLastWriteTime(), linkObj.getLastWriteTime(), 2)) //respect 2 second FAT/FAT32 precision + setFileTime(linkObj.getFullName(), linkObj.getLastWriteTime(), SYMLINK_DIRECT); //throw FileError + + //-> both sides *should* be completely equal now... + linkObj.setSyncedTo(linkObj.getShortName(), + linkObj.getLastWriteTime(), //target time set from source + linkObj.getLastWriteTime()); + + procCallback_.updateProcessedData(1, 0); + break; + + case SO_MOVE_LEFT_SOURCE: + case SO_MOVE_RIGHT_SOURCE: + case SO_MOVE_LEFT_TARGET: + case SO_MOVE_RIGHT_TARGET: + case SO_DO_NOTHING: + case SO_EQUAL: + case SO_UNRESOLVED_CONFLICT: + assert(false); //should have been filtered out by SynchronizeFolderPair::getPass() + return; //no update on processed data! + } + + procCallback_.requestUiRefresh(); //may throw +} + + +inline +void SynchronizeFolderPair::synchronizeFolder(DirPair& dirObj) +{ + const SyncOperation syncOp = dirObj.getSyncOperation(); + + if (Opt sideTrg = getTargetDirection(syncOp)) + { + if (*sideTrg == LEFT_SIDE) + synchronizeFolderInt(dirObj, syncOp); + else + synchronizeFolderInt(dirObj, syncOp); + } +} + + +template +void SynchronizeFolderPair::synchronizeFolderInt(DirPair& dirObj, SyncOperation syncOp) +{ + static const SelectedSide sideSrc = OtherSide::result; + + switch (syncOp) + { + case SO_CREATE_NEW_LEFT: + case SO_CREATE_NEW_RIGHT: + if (const DirPair* parentDir = dynamic_cast(&dirObj.parent())) + if (parentDir->isEmpty()) //BaseDirPair OTOH is always non-empty and existing in this context => else: fatal error in zen::synchronize() + return; //if parent directory creation failed, there's no reason to show more errors! + + if (somethingExists(dirObj.getFullName())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should error out! + { + const Zstring& target = dirObj.getBaseDirPf() + dirObj.getRelativeName(); + + reportInfo(txtCreatingFolder, target); + try + { + makeDirectoryPlain(target, dirObj.getFullName(), copyFilePermissions_); //throw FileError, ErrorTargetExisting, (ErrorTargetPathMissing) + } + catch (const ErrorTargetExisting&) { if (!dirExists(target)) throw; } //detect clash with file (dir-symlink OTOH is okay) + + //update DirPair + dirObj.setSyncedTo(dirObj.getShortName()); + + procCallback_.updateProcessedData(1, 0); + } + else //source deleted meanwhile...nothing was done (logical point of view!) -> uh....what about a temporary network drop??? + { + const SyncStatistics subStats(dirObj); + procCallback_.updateTotalData(-getCUD(subStats) - 1, -subStats.getDataToProcess()); + + //remove only *after* evaluating dirObj!! + dirObj.refSubFiles().clear(); // + dirObj.refSubLinks().clear(); //update DirPair + dirObj.refSubDirs ().clear(); // + dirObj.removeObject(); // + } + break; + + case SO_DELETE_LEFT: + case SO_DELETE_RIGHT: + reportInfo(getDelHandling().getTxtRemovingDir(), dirObj.getFullName()); + { + int objectsReported = 0; + auto guardStatistics = makeGuard([&] { procCallback_.updateTotalData(objectsReported, 0); }); //error = unexpected increase of total workload + const SyncStatistics subStats(dirObj); //counts sub-objects only! + const int objectsExpected = 1 + getCUD(subStats); + const Int64 bytesExpected = subStats.getDataToProcess(); + assert(bytesExpected == 0); + + getDelHandling().removeDirUpdating(dirObj.getFullName(), dirObj.getObjRelativeName(), bytesExpected, [&] //throw FileError + { + procCallback_.updateProcessedData(1, 0); //noexcept + ++objectsReported; + }); + + guardStatistics.dismiss(); //update statistics to consider the real amount of data + if (objectsReported != objectsExpected) + procCallback_.updateTotalData(objectsReported - objectsExpected, 0); //noexcept! + } + dirObj.refSubFiles().clear(); // + dirObj.refSubLinks().clear(); //update DirPair + dirObj.refSubDirs ().clear(); // + dirObj.removeObject(); // + break; + + case SO_COPY_METADATA_TO_LEFT: + case SO_COPY_METADATA_TO_RIGHT: + reportInfo(txtWritingAttributes, dirObj.getFullName()); + + if (dirObj.getShortName() != dirObj.getShortName()) //adapt difference in case (windows only) + renameFile(dirObj.getFullName(), + beforeLast(dirObj.getFullName(), FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + dirObj.getShortName()); //throw FileError + //copyFileTimes -> useless: modification time changes with each child-object creation/deletion + + //-> both sides *should* be completely equal now... + dirObj.setSyncedTo(dirObj.getShortName()); + + procCallback_.updateProcessedData(1, 0); + break; + + case SO_OVERWRITE_LEFT: + case SO_OVERWRITE_RIGHT: + case SO_MOVE_LEFT_SOURCE: + case SO_MOVE_RIGHT_SOURCE: + case SO_MOVE_LEFT_TARGET: + case SO_MOVE_RIGHT_TARGET: + case SO_DO_NOTHING: + case SO_EQUAL: + case SO_UNRESOLVED_CONFLICT: + assert(false); //should have been filtered out by SynchronizeFolderPair::getPass() + return; //no update on processed data! + } + + procCallback_.requestUiRefresh(); //may throw +} + +//########################################################################################### + +template +class WhileCopying : public zen::CallbackCopyFile +{ +public: + WhileCopying(Int64& bytesReported, + ProcessCallback& statusHandler, + Function delTargetCmd) : + bytesReported_(bytesReported), + statusHandler_(statusHandler), + delTargetCmd_(std::move(delTargetCmd)) {} + + virtual void deleteTargetFile(const Zstring& targetFile) { delTargetCmd_(); } + + virtual void updateCopyStatus(Int64 bytesDelta) + { + statusHandler_.updateProcessedData(0, bytesDelta); //throw()! -> ensure client and service provider are in sync! + bytesReported_ += bytesDelta; // + + statusHandler_.requestUiRefresh(); //may throw + } + +private: + Int64& bytesReported_; + ProcessCallback& statusHandler_; + Function delTargetCmd_; +}; + + +//throw FileError; reports data delta via updateProcessedData() +template +InSyncAttributes SynchronizeFolderPair::copyFileUpdating(const Zstring& sourceFile, + const Zstring& targetFile, + const Int64& bytesExpected, + Function delTargetCommand) const //returns current attributes of source file +{ + Zstring source = sourceFile; + InSyncAttributes newAttr; + Int64 bytesReported; + + auto copyOperation = [&] + { + auto guardStatistics = makeGuard([&] + { + procCallback_.updateTotalData(0, bytesReported); //error = unexpected increase of total workload + bytesReported = 0; + }); + + WhileCopying callback(bytesReported, procCallback_, delTargetCommand); + + copyFile(source, //type File implicitly means symlinks need to be dereferenced! + targetFile, + copyFilePermissions_, + transactionalFileCopy_, + &callback, + &newAttr); //throw FileError, ErrorFileLocked + + //#################### Verification ############################# + if (verifyCopiedFiles_) + { + auto guardTarget = makeGuard([&] { removeFile(targetFile); }); //delete target if verification fails + verifyFileCopy(source, targetFile); //throw FileError + guardTarget.dismiss(); + } + //#################### /Verification ############################# + + //update statistics to consider the real amount of data, e.g. more than the "file size" for ADS streams, + //less for sparse and compressed files, or file changed in the meantime! + if (bytesReported != bytesExpected) + procCallback_.updateTotalData(0, bytesReported - bytesExpected); //noexcept! + + guardStatistics.dismiss(); + }; + +#ifdef ZEN_WIN + try + { + copyOperation(); + } + catch (ErrorFileLocked& e1) + { + //if file is locked (try to) use Windows Volume Shadow Copy Service + if (!shadowCopyHandler_) + throw; + try + { + //contains prefix: E.g. "\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Program Files\FFS\sample.dat" + source = shadowCopyHandler_->makeShadowCopy(source, //throw FileError + [&](const Zstring& volumeName) + { + procCallback_.reportStatus(replaceCpy(_("Creating a Volume Shadow Copy for %x..."), L"%x", fmtFileName(volumeName))); + procCallback_.forceUiRefresh(); + }); + } + catch (const FileError& e2) + { + throw FileError(e1.toString(), e2.toString()); + } + + //now try again + copyOperation(); + } +#else + copyOperation(); +#endif + + return newAttr; +} + +//--------------------- data verification ------------------------- +struct VerifyCallback +{ + virtual ~VerifyCallback() {} + virtual void updateStatus() = 0; +}; + +void verifyFiles(const Zstring& source, const Zstring& target, VerifyCallback& callback) //throw FileError +{ + static std::vector memory1(1024 * 1024); //1024 kb seems to be a reasonable buffer size + static std::vector memory2(1024 * 1024); + +#ifdef ZEN_WIN + wxFile file1(applyLongPathPrefix(source).c_str(), wxFile::read); //don't use buffered file input for verification! +#elif defined ZEN_LINUX || defined ZEN_MAC + wxFile file1(::open(source.c_str(), O_RDONLY)); //utilize UTF-8 filename +#endif + if (!file1.IsOpened()) + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(source)) + L" (open)"); + +#ifdef ZEN_WIN + wxFile file2(applyLongPathPrefix(target).c_str(), wxFile::read); //don't use buffered file input for verification! +#elif defined ZEN_LINUX || defined ZEN_MAC + wxFile file2(::open(target.c_str(), O_RDONLY)); //utilize UTF-8 filename +#endif + if (!file2.IsOpened()) //NO cleanup necessary for (wxFile) file1 + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(target)) + L" (open)"); + + do + { + const size_t length1 = file1.Read(&memory1[0], memory1.size()); + if (file1.Error()) + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(source))); + callback.updateStatus(); + + const size_t length2 = file2.Read(&memory2[0], memory2.size()); + if (file2.Error()) + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(target))); + callback.updateStatus(); + + if (length1 != length2 || ::memcmp(&memory1[0], &memory2[0], length1) != 0) + throw FileError(replaceCpy(replaceCpy(_("Data verification error: %x and %y have different content."), L"%x", L"\n" + fmtFileName(source)), L"%y", L"\n" + fmtFileName(target))); + } + while (!file1.Eof()); + + if (!file2.Eof()) + throw FileError(replaceCpy(replaceCpy(_("Data verification error: %x and %y have different content."), L"%x", L"\n" + fmtFileName(source)), L"%y", L"\n" + fmtFileName(target))); +} + + +class VerifyStatusUpdater : public VerifyCallback +{ +public: + VerifyStatusUpdater(ProcessCallback& statusHandler) : statusHandler_(statusHandler) {} + + virtual void updateStatus() { statusHandler_.requestUiRefresh(); } //trigger display refresh + +private: + ProcessCallback& statusHandler_; +}; + + +void SynchronizeFolderPair::verifyFileCopy(const Zstring& source, const Zstring& target) const +{ + procCallback_.reportInfo(replaceCpy(txtVerifying, L"%x", fmtFileName(target))); + + VerifyStatusUpdater callback(procCallback_); + tryReportingError([&] { verifyFiles(source, target, callback); }, procCallback_); +} + +//########################################################################################### + +#ifdef ZEN_WIN +//recycleBinStatus() blocks seriously if recycle bin is really full and drive is slow +StatusRecycler recycleBinStatusUpdating(const Zstring& dirname, ProcessCallback& procCallback) +{ + procCallback.reportStatus(replaceCpy(_("Checking recycle bin availability for folder %x..."), L"%x", fmtFileName(dirname), false)); + + auto ft = async([=] { return recycleBinStatus(dirname); }); + + while (!ft.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL / 2))) + procCallback.requestUiRefresh(); //may throw! + return ft.get(); +} +#endif + + +/* +struct LessDependentDirectory : public std::binary_function +{ +-> a *very* bad idea: this is NOT a strict weak ordering! No transitivity of equivalence! + + bool operator()(const Zstring& lhs, const Zstring& rhs) const + { + return LessFilename()(Zstring(lhs.c_str(), std::min(lhs.length(), rhs.length())), + Zstring(rhs.c_str(), std::min(lhs.length(), rhs.length()))); + } +}; +*/ + +template //create base directories first (if not yet existing) -> no symlink or attribute copying! +bool createBaseDirectory(BaseDirPair& baseDirObj, ProcessCallback& callback) //nothrow; return false if fatal error occurred +{ + const Zstring dirname = beforeLast(baseDirObj.getBaseDirPf(), FILE_NAME_SEPARATOR); //what about C:\ ??? + if (dirname.empty()) + return true; + + if (baseDirObj.isExisting()) //atomicity: do NOT check directory existence again! + { + //just convenience: exit sync right here instead of showing tons of error messages during file copy + zen::Opt errMsg = tryReportingError([&] + { + if (!dirExistsUpdating(dirname, false, callback)) + throw FileError(replaceCpy(_("Cannot find %x."), L"%x", fmtFileName(dirname))); //should be logged as a "fatal error" if ignored by the user... + }, callback); //may throw in error-callback! + + return !errMsg; + } + else //create target directory: user presumably ignored error "dir existing" in order to have it created automatically + { + bool temporaryNetworkDrop = false; + zen::Opt errMsg = tryReportingError([&] + { + try + { + //a nice race-free check and set operation: + makeDirectory(dirname, /*bool failIfExists*/ true); //throw FileError, ErrorTargetExisting + baseDirObj.setExisting(true); //update our model! + } + catch (const ErrorTargetExisting&) + { + //TEMPORARY network drop: base directory not found during comparison, but reappears during synchronization + //=> sync-directions are based on false assumptions! Abort. + callback.reportFatalError(replaceCpy(_("Target folder %x already existing."), L"%x", fmtFileName(dirname))); + temporaryNetworkDrop = true; + + //Is it possible we're catching a "false-positive" here, could FFS have created the directory indirectly after comparison? + // 1. deletion handling: recycler -> no, temp directory created only at first deletion + // 2. deletion handling: versioning -> " + // 3. log file creates containing folder -> no, log only created in batch mode, and only *before* comparison + } + }, callback); //may throw in error-callback! + return !errMsg && !temporaryNetworkDrop; + } +} +} + + +void zen::synchronize(const TimeComp& timeStamp, + xmlAccess::OptionalDialogs& warnings, + bool verifyCopiedFiles, + bool copyLockedFiles, + bool copyFilePermissions, + bool transactionalFileCopy, + bool runWithBackgroundPriority, + const std::vector& syncConfig, + FolderComparison& folderCmp, + ProcessCallback& callback) +{ + //specify process and resource handling priorities + std::unique_ptr backgroundPrio; + if (runWithBackgroundPriority) + try + { + backgroundPrio = make_unique(); //throw FileError + } + catch (const FileError& e) //not an error in this context + { + callback.reportInfo(e.toString()); //may throw! + } + + //prevent operating system going into sleep state + std::unique_ptr noStandby; + try + { + noStandby = make_unique(); //throw FileError + } + catch (const FileError& e) //not an error in this context + { + callback.reportInfo(e.toString()); //may throw! + } + + //PERF_START; + + if (syncConfig.size() != folderCmp.size()) + throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo(__LINE__)); + + //inform about the total amount of data that will be processed from now on + const SyncStatistics statisticsTotal(folderCmp); + + //keep at beginning so that all gui elements are initialized properly + callback.initNewPhase(getCUD(statisticsTotal), + statisticsTotal.getDataToProcess(), + ProcessCallback::PHASE_SYNCHRONIZING); + + + std::deque skipFolderPair(folderCmp.size()); //folder pairs may be skipped after fatal errors were found + + //-------------------execute basic checks all at once before starting sync-------------------------------------- + + auto dependentDir = [](const Zstring& lhs, const Zstring& rhs) //note: this is NOT an equivalence relation! + { + return EqualFilename()(Zstring(lhs.c_str(), std::min(lhs.length(), rhs.length())), + Zstring(rhs.c_str(), std::min(lhs.length(), rhs.length()))); + }; + + //aggregate information + std::map> dirReadWriteCount; //count read/write accesses + auto incReadCount = [&](const Zstring& baseDir) + { + dirReadWriteCount[baseDir]; //create entry + for (auto it = dirReadWriteCount.begin(); it != dirReadWriteCount.end(); ++it) + { + auto& countRef = it->second; + if (dependentDir(baseDir, it->first)) + ++countRef.first; + } + }; + auto incWriteCount = [&](const Zstring& baseDir) + { + dirReadWriteCount[baseDir]; //create entry + for (auto it = dirReadWriteCount.begin(); it != dirReadWriteCount.end(); ++it) + { + auto& countRef = it->second; + if (dependentDir(baseDir, it->first)) + ++countRef.second; + } + }; + + typedef std::vector> DirPairList; + DirPairList significantDiff; + + typedef std::vector>> DirSpaceRequAvailList; //dirname / space required / space available + DirSpaceRequAvailList diskSpaceMissing; + +#ifdef ZEN_WIN + //status of base directories which are set to DELETE_TO_RECYCLER (and contain actual items to be deleted) + std::map baseDirHasRecycler; //might be expensive to determine => buffer + check recycle bin existence only once per base directory! +#endif + + //start checking folder pairs + for (auto j = begin(folderCmp); j != end(folderCmp); ++j) + { + const size_t folderIndex = j - begin(folderCmp); + + //exclude some pathological case (leftdir, rightdir are empty) + if (EqualFilename()(j->getBaseDirPf(), j->getBaseDirPf())) + continue; + + const FolderPairSyncCfg& folderPairCfg = syncConfig[folderIndex]; + + const SyncStatistics folderPairStat(*j); + + //aggregate basic information + const bool writeLeft = folderPairStat.getCreate() + + folderPairStat.getUpdate() + + folderPairStat.getDelete() > 0; + + const bool writeRight = folderPairStat.getCreate() + + folderPairStat.getUpdate() + + folderPairStat.getDelete() > 0; + + //skip folder pair if there is nothing to do (except for two-way mode and move-detection, where DB files need to be written) + if (!writeLeft && !writeRight && + !folderPairCfg.saveSyncDB_) + { + skipFolderPair[folderIndex] = true; //skip creating (not yet existing) base directories in particular if there's no need + continue; + } + + //check empty input fields: this only makes sense if empty field is source (and no DB files need to be created) + if ((j->getBaseDirPf().empty() && (writeLeft || folderPairCfg.saveSyncDB_)) || + (j->getBaseDirPf().empty() && (writeRight || folderPairCfg.saveSyncDB_))) + { + callback.reportFatalError(_("Target folder input field must not be empty.")); + skipFolderPair[folderIndex] = true; + continue; + } + + //aggregate information of folders used by multiple pairs in read/write access + if (!dependentDir(j->getBaseDirPf(), j->getBaseDirPf())) //true in general + { + if (writeLeft && writeRight) + { + incWriteCount(j->getBaseDirPf()); + incWriteCount(j->getBaseDirPf()); + } + else if (writeLeft) + { + incWriteCount(j->getBaseDirPf()); + incReadCount (j->getBaseDirPf()); + } + else if (writeRight) + { + incReadCount (j->getBaseDirPf()); + incWriteCount(j->getBaseDirPf()); + } + } + else //if folder pair contains two dependent folders, a warning was already issued after comparison; in this context treat as one write access at most + { + if (writeLeft || writeRight) + incWriteCount(j->getBaseDirPf()); + } + + + if (folderPairStat.getUpdate() + folderPairStat.getDelete() > 0 && + folderPairCfg.handleDeletion == zen::DELETE_TO_VERSIONING) + { + //check if user-defined directory for deletion was specified + if (folderPairCfg.versioningFolder.empty()) //already trimmed by getFormattedDirectoryName() + { + //should never arrive here: already checked in SyncCfgDialog + callback.reportFatalError(_("Please enter a target folder for versioning.")); + skipFolderPair[folderIndex] = true; + continue; + } + } + + //the following scenario is covered by base directory creation below in case source directory exists (accessible or not), but latter doesn't cover source created after comparison, but before sync!!! + auto checkSourceMissing = [&](const Zstring& baseDirPf, bool wasExisting) -> bool //avoid race-condition: we need to evaluate existence status from time of comparison! + { + if (!baseDirPf.empty()) + { + //PERMANENT network drop: avoid data loss when source directory is not found AND user chose to ignore errors (else we wouldn't arrive here) + if (folderPairStat.getCreate() + + folderPairStat.getUpdate() == 0 && + folderPairStat.getDelete() > 0) //deletions only... (respect filtered items!) + //folderPairStat.getConflict() == 0 && -> there COULD be conflicts for if directory existence check fails, but loading sync.ffs_db succeeds + //https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3531351&group_id=234430 -> fixed, but still better not consider conflicts + { + if (!wasExisting) //avoid race-condition: we need to evaluate existence status from time of comparison! + { + callback.reportFatalError(replaceCpy(_("Source folder %x not found."), L"%x", fmtFileName(baseDirPf))); + skipFolderPair[folderIndex] = true; + return false; + } + } + } + return true; + }; + if (!checkSourceMissing(j->getBaseDirPf(), j->isExisting()) || + !checkSourceMissing(j->getBaseDirPf(), j->isExisting())) + continue; + + //check if more than 50% of total number of files/dirs are to be created/overwritten/deleted + if (significantDifferenceDetected(folderPairStat)) + significantDiff.push_back(std::make_pair(j->getBaseDirPf(), j->getBaseDirPf())); + + //check for sufficient free diskspace + auto checkSpace = [&](const Zstring& baseDirPf, const Int64& minSpaceNeeded) + { + try + { + const Int64 freeSpace = to(getFreeDiskSpace(baseDirPf)); //throw FileError + + if (0 < freeSpace && //zero disk space probably means "request not supported" (e.g. see WebDav) + freeSpace < minSpaceNeeded) + diskSpaceMissing.push_back(std::make_pair(baseDirPf, std::make_pair(minSpaceNeeded, freeSpace))); + } + catch (FileError&) {} + }; + const std::pair spaceNeeded = MinimumDiskSpaceNeeded::calculate(*j); + checkSpace(j->getBaseDirPf(), spaceNeeded.first); + checkSpace(j->getBaseDirPf(), spaceNeeded.second); + +#ifdef ZEN_WIN + //windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong + auto checkRecycler = [&](const Zstring& baseDirPf) + { + if (!baseDirPf.empty()) //should be + if (baseDirHasRecycler.find(baseDirPf) == baseDirHasRecycler.end()) //perf: avoid duplicate checks! + baseDirHasRecycler[baseDirPf] = recycleBinStatusUpdating(baseDirPf, callback) == STATUS_REC_EXISTS; + }; + + if (folderPairCfg.handleDeletion == DELETE_TO_RECYCLER) + { + if (folderPairStat.getUpdate() + + folderPairStat.getDelete() > 0) + checkRecycler(j->getBaseDirPf()); + + if (folderPairStat.getUpdate() + + folderPairStat.getDelete() > 0) + checkRecycler(j->getBaseDirPf()); + } +#endif + } + + //check if unresolved conflicts exist + if (statisticsTotal.getConflict() > 0) + { + //show the first few conflicts in warning message also: + std::wstring msg = _("The following items have unresolved conflicts and will not be synchronized:"); + + const auto& conflictMsgs = statisticsTotal.getConflictMessages(); //get *all* sync conflicts + for (auto it = conflictMsgs.begin(); it != conflictMsgs.end(); ++it) + msg += L"\n\n" + fmtFileName(it->first) + L": " + it->second; + + callback.reportWarning(msg, warnings.warningUnresolvedConflicts); + } + + + //check if user accidentally selected wrong directories for sync + if (!significantDiff.empty()) + { + std::wstring msg = _("The following folders are significantly different. Make sure you are matching the correct folders for synchronization."); + + for (auto it = significantDiff.begin(); it != significantDiff.end(); ++it) + msg += std::wstring(L"\n\n") + + it->first + L" <-> " + L"\n" + + it->second; + + callback.reportWarning(msg, warnings.warningSignificantDifference); + } + + + //check for sufficient free diskspace + if (!diskSpaceMissing.empty()) + { + std::wstring msg = _("Not enough free disk space available in:"); + + for (auto it = diskSpaceMissing.begin(); it != diskSpaceMissing.end(); ++it) + msg += std::wstring(L"\n\n") + + it->first + L"\n" + + _("Required:") + L" " + filesizeToShortString(it->second.first) + L"\n" + + _("Available:") + L" " + filesizeToShortString(it->second.second); + + callback.reportWarning(msg, warnings.warningNotEnoughDiskSpace); + } + +#ifdef ZEN_WIN + //windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong + { + std::wstring dirListMissingRecycler; + for (auto it = baseDirHasRecycler.begin(); it != baseDirHasRecycler.end(); ++it) + if (!it->second) + dirListMissingRecycler += std::wstring(L"\n") + it->first; + + if (!dirListMissingRecycler.empty()) + callback.reportWarning(_("The recycle bin is not available for the following folders. Files will be deleted permanently instead:") + L"\n" + dirListMissingRecycler, warnings.warningRecyclerMissing); + } +#endif + + //check if folders are used by multiple pairs in read/write access + std::vector conflictDirs; + for (auto it = dirReadWriteCount.cbegin(); it != dirReadWriteCount.cend(); ++it) + { + const std::pair& countRef = it->second; //# read/write accesses + + if (countRef.first + countRef.second >= 2 && countRef.second >= 1) //race condition := multiple accesses of which at least one is a write + conflictDirs.push_back(it->first); + } + + if (!conflictDirs.empty()) + { + std::wstring msg = _("A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.") + L"\n"; + std::for_each(conflictDirs.begin(), conflictDirs.end(), + [&](const Zstring& dirname) { msg += std::wstring(L"\n") + dirname; }); + + callback.reportWarning(msg, warnings.warningFolderPairRaceCondition); + } + + //-------------------end of basic checks------------------------------------------ + +#ifdef ZEN_WIN + //shadow copy buffer: per sync-instance, not folder pair + std::unique_ptr shadowCopyHandler; + if (copyLockedFiles) + shadowCopyHandler = make_unique(); +#endif + + try + { + //loop through all directory pairs + for (auto j = begin(folderCmp); j != end(folderCmp); ++j) + { + //exclude pathological cases (e.g. leftdir, rightdir are empty) + if (EqualFilename()(j->getBaseDirPf(), j->getBaseDirPf())) + continue; + + //------------------------------------------------------------------------------------------ + callback.reportInfo(_("Synchronizing folder pair:") + L"\n" + + L" " + j->getBaseDirPf() + L"\n" + + L" " + j->getBaseDirPf()); + //------------------------------------------------------------------------------------------ + + const size_t folderIndex = j - begin(folderCmp); + const FolderPairSyncCfg& folderPairCfg = syncConfig[folderIndex]; + + if (skipFolderPair[folderIndex]) //folder pairs may be skipped after fatal errors were found + continue; + + //create base directories first (if not yet existing) -> no symlink or attribute copying! + if (!createBaseDirectory(*j, callback) || + !createBaseDirectory(*j, callback)) + continue; //skip this folder pair + + //------------------------------------------------------------------------------------------ + //execute synchronization recursively + + //update synchronization database (automatic sync only) + ScopeGuard guardUpdateDb = makeGuard([&] + { + if (folderPairCfg.saveSyncDB_) + try { zen::saveLastSynchronousState(*j); } //throw FileError + catch (FileError&) {} + }); + + //guarantee removal of invalid entries (where element on both sides is empty) + ZEN_ON_SCOPE_EXIT(BaseDirPair::removeEmpty(*j);); + + bool copyPermissionsFp = false; + tryReportingError([&] + { + copyPermissionsFp = copyFilePermissions && //copy permissions only if asked for and supported by *both* sides! + !j->getBaseDirPf().empty() && //scenario: directory selected on one side only + !j->getBaseDirPf().empty() && // + supportsPermissions(beforeLast(j->getBaseDirPf(), FILE_NAME_SEPARATOR)) && //throw FileError + supportsPermissions(beforeLast(j->getBaseDirPf(), FILE_NAME_SEPARATOR)); + }, callback); //show error dialog if necessary + + + auto getEffectiveDeletionPolicy = [&](const Zstring& baseDirPf) -> DeletionPolicy + { +#ifdef ZEN_WIN + if (folderPairCfg.handleDeletion == DELETE_TO_RECYCLER) + { + auto it = baseDirHasRecycler.find(baseDirPf); + if (it != baseDirHasRecycler.end()) + if (!it->second) + return DELETE_PERMANENTLY; //Windows' ::SHFileOperation() will do this anyway, but we have a better and faster deletion routine (e.g. on networks) + } +#endif + return folderPairCfg.handleDeletion; + }; + + + DeletionHandling delHandlerL(getEffectiveDeletionPolicy(j->getBaseDirPf()), + folderPairCfg.versioningFolder, + folderPairCfg.versioningStyle_, + timeStamp, + j->getBaseDirPf(), + callback); + + DeletionHandling delHandlerR(getEffectiveDeletionPolicy(j->getBaseDirPf()), + folderPairCfg.versioningFolder, + folderPairCfg.versioningStyle_, + timeStamp, + j->getBaseDirPf(), + callback); + + + SynchronizeFolderPair syncFP(callback, verifyCopiedFiles, copyPermissionsFp, transactionalFileCopy, +#ifdef ZEN_WIN + shadowCopyHandler.get(), +#endif + delHandlerL, delHandlerR); + syncFP.startSync(*j); + + //(try to gracefully) cleanup temporary Recycle bin folders and versioning -> will be done in ~DeletionHandling anyway... + tryReportingError([&] { delHandlerL.tryCleanup(); }, callback); //show error dialog if necessary + tryReportingError([&] { delHandlerR.tryCleanup(); }, callback); // + + //(try to gracefully) write database file + if (folderPairCfg.saveSyncDB_) + { + callback.reportStatus(_("Generating database...")); + callback.forceUiRefresh(); + + tryReportingError([&] { zen::saveLastSynchronousState(*j); }, callback); //throw FileError + guardUpdateDb.dismiss(); + } + } + } + catch (const std::exception& e) + { + callback.reportFatalError(utfCvrtTo(e.what())); + } +} \ No newline at end of file diff --git a/FreeFileSync/Source/synchronization.h b/FreeFileSync/Source/synchronization.h new file mode 100644 index 00000000..bc6b79db --- /dev/null +++ b/FreeFileSync/Source/synchronization.h @@ -0,0 +1,159 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef SYNCHRONIZATION_H_INCLUDED +#define SYNCHRONIZATION_H_INCLUDED + +#include +#include "file_hierarchy.h" +#include "lib/process_xml.h" +#include "process_callback.h" + + +namespace zen +{ +class SyncStatistics //this class counts *logical* operations, (create, update, delete + bytes), *not* disk accesses! +{ + //-> note the fundamental difference to counting disk accesses! +public: + SyncStatistics(const HierarchyObject& hierObj); + SyncStatistics(const FolderComparison& folderCmp); + SyncStatistics(const FilePair& fileObj); + + int getCreate() const; + template int getCreate() const; + + int getUpdate() const; + template int getUpdate() const; + + int getDelete() const; + template int getDelete() const; + + int getConflict() const { return static_cast(conflictMsgs.size()); } + + typedef std::vector> ConflictTexts; // Pair(filename/conflict text) + const ConflictTexts& getConflictMessages() const { return conflictMsgs; } + + Int64 getDataToProcess() const { return dataToProcess; } + size_t getRowCount() const { return rowsTotal; } + +private: + void init(); + + void recurse(const HierarchyObject& hierObj); + + void processFile(const FilePair& fileObj); + void processLink(const SymlinkPair& linkObj); + void processDir(const DirPair& dirObj); + + int createLeft, createRight; + int updateLeft, updateRight; + int deleteLeft, deleteRight; + ConflictTexts conflictMsgs; //conflict texts to display as a warning message + Int64 dataToProcess; + size_t rowsTotal; +}; + + +struct FolderPairSyncCfg +{ + FolderPairSyncCfg(bool saveSyncDB, + const DeletionPolicy handleDel, + VersioningStyle versioningStyle, + const Zstring& versioningDirFmt) : + saveSyncDB_(saveSyncDB), + handleDeletion(handleDel), + versioningStyle_(versioningStyle), + versioningFolder(versioningDirFmt) {} + + bool saveSyncDB_; //save database if in automatic mode or dection of moved files is active + DeletionPolicy handleDeletion; + VersioningStyle versioningStyle_; + Zstring versioningFolder; //formatted directory name +}; +std::vector extractSyncCfg(const MainConfiguration& mainCfg); + + +//FFS core routine: +void synchronize(const TimeComp& timeStamp, + xmlAccess::OptionalDialogs& warnings, + bool verifyCopiedFiles, + bool copyLockedFiles, + bool copyFilePermissions, + bool transactionalFileCopy, + bool runWithBackgroundPriority, + + const std::vector& syncConfig, //CONTRACT: syncConfig and folderCmp correspond row-wise! + FolderComparison& folderCmp, // + ProcessCallback& callback); + + + + + + + + + + +// ----------- implementation ---------------- +template <> inline +int SyncStatistics::getCreate() const +{ + return createLeft; +} + +template <> inline +int SyncStatistics::getCreate() const +{ + return createRight; +} + +inline +int SyncStatistics::getCreate() const +{ + return getCreate() + getCreate(); +} + +template <> inline +int SyncStatistics::getUpdate() const +{ + return updateLeft; +} + +template <> inline +int SyncStatistics::getUpdate() const +{ + return updateRight; +} + +inline +int SyncStatistics::getUpdate() const +{ + return getUpdate() + getUpdate(); +} + + +template <> inline +int SyncStatistics::getDelete() const +{ + return deleteLeft; +} + +template <> inline +int SyncStatistics::getDelete() const +{ + return deleteRight; +} + +inline +int SyncStatistics::getDelete() const +{ + return getDelete() + getDelete(); +} +} + +#endif // SYNCHRONIZATION_H_INCLUDED diff --git a/FreeFileSync/Source/ui/app_icon.h b/FreeFileSync/Source/ui/app_icon.h new file mode 100644 index 00000000..a1240fee --- /dev/null +++ b/FreeFileSync/Source/ui/app_icon.h @@ -0,0 +1,40 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef APP_ICON_H6748179634932174683214 +#define APP_ICON_H6748179634932174683214 + +#include +#include + +namespace zen +{ +inline +wxIcon getFfsIcon() +{ + //wxWidgets' bitmap to icon conversion on OS X can only deal with very specific sizes => check on all platforms! + assert(getResourceImage(L"FreeFileSync").GetWidth () == getResourceImage(L"FreeFileSync").GetHeight() && + getResourceImage(L"FreeFileSync").GetWidth() % 128 == 0); +#ifdef ZEN_WIN + //for compatibility it seems we need to stick with a "real" icon + return wxIcon(L"A_FFS_ICON"); + +#elif defined ZEN_LINUX + //attention: make sure to not implicitly call "instance()" again => deadlock on Linux + wxIcon icon; + icon.CopyFromBitmap(getResourceImage(L"FreeFileSync")); //use big logo bitmap for better quality + return icon; + +#elif defined ZEN_MAC + wxIcon icon; + icon.CopyFromBitmap(getResourceImage(L"FreeFileSync").ConvertToImage().Scale(128, 128, wxIMAGE_QUALITY_HIGH)); //"von hinten durch die Brust ins Auge" + return icon; +#endif +} +} + + +#endif //APP_ICON_H6748179634932174683214 diff --git a/FreeFileSync/Source/ui/batch_config.cpp b/FreeFileSync/Source/ui/batch_config.cpp new file mode 100644 index 00000000..4807e4a1 --- /dev/null +++ b/FreeFileSync/Source/ui/batch_config.cpp @@ -0,0 +1,177 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "batch_config.h" +#include +#include +#include +#include +#include +#include "gui_generated.h" +#include "dir_name.h" +#include "../ui/exec_finished_box.h" +#include "../lib/help_provider.h" + +using namespace zen; +using namespace xmlAccess; + + +namespace +{ +enum ButtonPressed +{ + BUTTON_CANCEL, + BUTTON_SAVE_AS +}; + + +class BatchDialog : public BatchDlgGenerated +{ +public: + BatchDialog(wxWindow* parent, + XmlBatchConfig& batchCfg, //in/out + std::vector& onCompletionHistory, + size_t onCompletionHistoryMax); + +private: + virtual void OnClose (wxCloseEvent& event) { EndModal(BUTTON_CANCEL); } + virtual void OnCancel (wxCommandEvent& event) { EndModal(BUTTON_CANCEL); } + virtual void OnSaveBatchJob(wxCommandEvent& event); + virtual void OnErrorPopup (wxCommandEvent& event) { localBatchCfg.handleError = ON_ERROR_POPUP; updateGui(); } + virtual void OnErrorIgnore(wxCommandEvent& event) { localBatchCfg.handleError = ON_ERROR_IGNORE; updateGui(); } + virtual void OnErrorStop (wxCommandEvent& event) { localBatchCfg.handleError = ON_ERROR_STOP; updateGui(); } + virtual void OnHelpScheduleBatch(wxHyperlinkEvent& event) { displayHelpEntry(L"html/Schedule a Batch Job.html", this); } + + virtual void OnToggleGenerateLogfile(wxCommandEvent& event) { updateGui(); } + virtual void OnToggleLogfilesLimit (wxCommandEvent& event) { updateGui(); } + + void updateGui(); //re-evaluate gui after config changes + + void setConfig(const XmlBatchConfig& batchCfg); + XmlBatchConfig getConfig() const; + + XmlBatchConfig& batchCfgOutRef; //output only! + XmlBatchConfig localBatchCfg; //a mixture of settings some of which have OWNERSHIP WITHIN GUI CONTROLS! use getConfig() to resolve + + std::unique_ptr> logfileDir; //always bound, solve circular compile-time dependency +}; + +//################################################################################################################################### + +BatchDialog::BatchDialog(wxWindow* parent, + XmlBatchConfig& batchCfg, + std::vector& onCompletionHistory, + size_t onCompletionHistoryMax) : + BatchDlgGenerated(parent), + batchCfgOutRef(batchCfg) +{ +#ifdef ZEN_WIN + new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" + wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! +#endif + setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonSaveAs).setCancel(m_buttonCancel)); + + m_staticTextDescr->SetLabel(replaceCpy(m_staticTextDescr->GetLabel(), L"%x", L"FreeFileSync.exe <" + _("job name") + L">.ffs_batch")); + + m_comboBoxExecFinished->initHistory(onCompletionHistory, onCompletionHistoryMax); + + m_bitmapBatchJob->SetBitmap(getResourceImage(L"batch")); + + logfileDir = make_unique>(*m_panelLogfile, *m_buttonSelectLogfileDir, *m_logfileDir); + + setConfig(batchCfg); + + GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() + //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! + + // Layout(); + + m_buttonSaveAs->SetFocus(); +} + + +void BatchDialog::updateGui() //re-evaluate gui after config changes +{ + XmlBatchConfig cfg = getConfig(); //resolve parameter ownership: some on GUI controls, others member variables + + m_panelLogfile ->Enable(m_checkBoxGenerateLogfile->GetValue()); //enabled status is *not* directly dependent from resolved config! (but transitively) + m_spinCtrlLogfileLimit->Enable(m_checkBoxGenerateLogfile->GetValue() && m_checkBoxLogfilesLimit->GetValue()); + + m_toggleBtnErrorIgnore->SetValue(false); + m_toggleBtnErrorPopup ->SetValue(false); + m_toggleBtnErrorStop ->SetValue(false); + switch (cfg.handleError) //*not* owned by GUI controls + { + case ON_ERROR_IGNORE: + m_toggleBtnErrorIgnore->SetValue(true); + break; + case ON_ERROR_POPUP: + m_toggleBtnErrorPopup->SetValue(true); + break; + case ON_ERROR_STOP: + m_toggleBtnErrorStop->SetValue(true); + break; + } +} + + +void BatchDialog::setConfig(const XmlBatchConfig& batchCfg) +{ +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! +#endif + + localBatchCfg = batchCfg; //contains some parameters not owned by GUI controls + + //transfer parameter ownership to GUI + m_checkBoxShowProgress->SetValue(batchCfg.showProgress); + logfileDir->setName(utfCvrtTo(batchCfg.logFileDirectory)); + m_comboBoxExecFinished->setValue(batchCfg.mainCfg.onCompletion); + + //map single parameter "logfiles limit" to all three checkboxs and spin ctrl: + m_checkBoxGenerateLogfile->SetValue(batchCfg.logfilesCountLimit != 0); + m_checkBoxLogfilesLimit ->SetValue(batchCfg.logfilesCountLimit >= 0); + m_spinCtrlLogfileLimit ->SetValue(batchCfg.logfilesCountLimit >= 0 ? batchCfg.logfilesCountLimit : 100 /*XmlBatchConfig().logfilesCountLimit*/); + //attention: emits a "change value" event!! => updateGui() called implicitly! + + updateGui(); //re-evaluate gui after config changes +} + + +XmlBatchConfig BatchDialog::getConfig() const +{ + XmlBatchConfig batchCfg = localBatchCfg; + + //load parameters with ownership within GIU controls... + + //load structure with batch settings "batchCfg" + batchCfg.showProgress = m_checkBoxShowProgress->GetValue(); + batchCfg.logFileDirectory = utfCvrtTo(logfileDir->getName()); + batchCfg.mainCfg.onCompletion = m_comboBoxExecFinished->getValue(); + //get single parameter "logfiles limit" from all three checkboxes and spin ctrl: + batchCfg.logfilesCountLimit = m_checkBoxGenerateLogfile->GetValue() ? (m_checkBoxLogfilesLimit->GetValue() ? m_spinCtrlLogfileLimit->GetValue() : -1) : 0; + + return batchCfg; +} + + +void BatchDialog::OnSaveBatchJob(wxCommandEvent& event) +{ + batchCfgOutRef = getConfig(); + m_comboBoxExecFinished->addItemHistory(); //a good place to commit current "on completion" history item + EndModal(BUTTON_SAVE_AS); +} +} + + +bool zen::customizeBatchConfig(wxWindow* parent, + xmlAccess::XmlBatchConfig& batchCfg, //in/out + std::vector& execFinishedhistory, + size_t execFinishedhistoryMax) +{ + BatchDialog batchDlg(parent, batchCfg, execFinishedhistory, execFinishedhistoryMax); + return static_cast(batchDlg.ShowModal()) == BUTTON_SAVE_AS; +} diff --git a/FreeFileSync/Source/ui/batch_config.h b/FreeFileSync/Source/ui/batch_config.h new file mode 100644 index 00000000..5bff09e1 --- /dev/null +++ b/FreeFileSync/Source/ui/batch_config.h @@ -0,0 +1,22 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef BATCHCONFIG_H_INCLUDED +#define BATCHCONFIG_H_INCLUDED + +#include +#include "../lib/process_xml.h" + +namespace zen +{ +//show and let user customize batch settings (without saving) +bool customizeBatchConfig(wxWindow* parent, //return "false" if aborted, "true" on "do save" + xmlAccess::XmlBatchConfig& batchCfg, //in/out + std::vector& execFinishedhistory, + size_t execFinishedhistoryMax); +} + +#endif // BATCHCONFIG_H_INCLUDED diff --git a/FreeFileSync/Source/ui/batch_status_handler.cpp b/FreeFileSync/Source/ui/batch_status_handler.cpp new file mode 100644 index 00000000..bd08da73 --- /dev/null +++ b/FreeFileSync/Source/ui/batch_status_handler.cpp @@ -0,0 +1,476 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "batch_status_handler.h" +#include +#include +//#include +#include +#include +#include +#include "exec_finished_box.h" +#include "../lib/ffs_paths.h" +#include "../lib/resolve_path.h" +#include "../lib/status_handler_impl.h" +#include "../lib/generate_logfile.h" + +using namespace zen; + + +namespace +{ +class FindLogfiles : public TraverseCallback +{ +public: + FindLogfiles(const Zstring& prefix, std::vector& logfiles) : prefix_(prefix), logfiles_(logfiles) {} + +private: + virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) + { + const Zstring fileName(shortName); + if (startsWith(fileName, prefix_) && endsWith(fileName, Zstr(".log"))) + logfiles_.push_back(fullName); + } + + virtual TraverseCallback* onDir (const Zchar* shortName, const Zstring& fullName) { return nullptr; } //DON'T traverse into subdirs + virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) { return LINK_SKIP; } + virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) { assert(false); return ON_ERROR_IGNORE; } //errors are not really critical in this context + virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) { assert(false); return ON_ERROR_IGNORE; } // + + const Zstring prefix_; + std::vector& logfiles_; +}; + + +void limitLogfileCount(const Zstring& logdir, const std::wstring& jobname, size_t maxCount) //throw() +{ + std::vector logFiles; + FindLogfiles traverseCallback(utfCvrtTo(jobname), logFiles); //throw()! + + traverseFolder(logdir, + traverseCallback); + + if (logFiles.size() <= maxCount) + return; + + //delete oldest logfiles: take advantage of logfile naming convention to find them + std::nth_element(logFiles.begin(), logFiles.end() - maxCount, logFiles.end(), LessFilename()); + + std::for_each(logFiles.begin(), logFiles.end() - maxCount, + [](const Zstring& filename) { try { removeFile(filename); } catch (FileError&) {} }); +} + + +std::unique_ptr prepareNewLogfile(const Zstring& logfileDirectory, //throw FileError + const std::wstring& jobName, + const TimeComp& timeStamp) //return value always bound! +{ + //create logfile directory if required + Zstring logfileDir = logfileDirectory.empty() ? + getConfigDir() + Zstr("Logs") : + getFormattedDirectoryName(logfileDirectory); + + makeDirectory(logfileDir); //throw FileError + + //assemble logfile name + const Zstring body = appendSeparator(logfileDir) + utfCvrtTo(jobName) + Zstr(" ") + formatTime(Zstr("%Y-%m-%d %H%M%S"), timeStamp); + + //ensure uniqueness + for (int i = 0;; ++i) + try + { + const Zstring& filename = i == 0 ? + body + Zstr(".log") : + body + Zstr('_') + numberTo(i) + Zstr(".log"); + + return make_unique(filename, FileOutput::ACC_CREATE_NEW); //throw FileError, ErrorTargetExisting + //*no* file system race-condition! + } + catch (const ErrorTargetExisting&) {} +} +} + +//############################################################################################################################## + +BatchStatusHandler::BatchStatusHandler(bool showProgress, + const std::wstring& jobName, + const TimeComp& timeStamp, + const Zstring& logfileDirectory, //may be empty + int logfilesCountLimit, + size_t lastSyncsLogFileSizeMax, + const xmlAccess::OnError handleError, + size_t automaticRetryCount, + size_t automaticRetryDelay, + const SwitchToGui& switchBatchToGui, //functionality to change from batch mode to GUI mode + FfsReturnCode& returnCode, + const std::wstring& execWhenFinished, + std::vector& execFinishedHistory) : + switchBatchToGui_(switchBatchToGui), + showFinalResults(showProgress), //=> exit immediately or wait when finished + switchToGuiRequested(false), + logfilesCountLimit_(logfilesCountLimit), + lastSyncsLogFileSizeMax_(lastSyncsLogFileSizeMax), + handleError_(handleError), + returnCode_(returnCode), + automaticRetryCount_(automaticRetryCount), + automaticRetryDelay_(automaticRetryDelay), + progressDlg(createProgressDialog(*this, [this] { this->onProgressDialogTerminate(); }, *this, nullptr, showProgress, jobName, execWhenFinished, execFinishedHistory)), + jobName_(jobName), + startTime_(wxGetUTCTimeMillis().GetValue()) +{ + if (logfilesCountLimit != 0) + { + zen::Opt errMsg = tryReportingError([&] { logFile = prepareNewLogfile(logfileDirectory, jobName, timeStamp); }, //throw FileError; return value always bound! + *this); + if (errMsg) + { + raiseReturnCode(returnCode_, FFS_RC_ABORTED); + throw BatchAbortProcess(); + } + } + + //if (logFile) + // ::wxSetEnv(L"logfile", utfCvrtTo(logFile->getFilename())); +} + + +BatchStatusHandler::~BatchStatusHandler() +{ + //------------ "on completion" command conceptually is part of the sync, not cleanup -------------------------------------- + + //decide whether to stay on status screen or exit immediately... + if (switchToGuiRequested) //-> avoid recursive yield() calls, thous switch not before ending batch mode + { + try + { + switchBatchToGui_.execute(); //open FreeFileSync GUI + } + catch (...) {} + showFinalResults = false; + } + else if (progressDlg) + { + if (progressDlg->getWindowIfVisible()) + showFinalResults = true; + + //execute "on completion" command (even in case of ignored errors) + if (!abortIsRequested()) //if aborted (manually), we don't execute the command + { + const std::wstring finalCommand = progressDlg->getExecWhenFinishedCommand(); //final value (after possible user modification) + if (!finalCommand.empty()) + { + if (isCloseProgressDlgCommand(finalCommand)) + showFinalResults = false; //take precedence over current visibility status + else + try + { + tryReportingError([&] { shellExecute2(expandMacros(utfCvrtTo(finalCommand)), EXEC_TYPE_SYNC); }, //throw FileError, throw X? + *this); + } + catch (...) {} + } + } + } + //------------ end of sync: begin of cleanup -------------------------------------- + + const int totalErrors = errorLog.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR); //evaluate before finalizing log + const int totalWarnings = errorLog.getItemCount(TYPE_WARNING); + + //finalize error log + std::wstring finalStatus; + if (abortIsRequested()) + { + raiseReturnCode(returnCode_, FFS_RC_ABORTED); + finalStatus = _("Synchronization stopped"); + errorLog.logMsg(finalStatus, TYPE_ERROR); + } + else if (totalErrors > 0) + { + raiseReturnCode(returnCode_, FFS_RC_FINISHED_WITH_ERRORS); + finalStatus = _("Synchronization completed with errors"); + errorLog.logMsg(finalStatus, TYPE_ERROR); + } + else if (totalWarnings > 0) + { + raiseReturnCode(returnCode_, FFS_RC_FINISHED_WITH_WARNINGS); + finalStatus = _("Synchronization completed with warnings"); + errorLog.logMsg(finalStatus, TYPE_WARNING); + } + else + { + if (getObjectsTotal(PHASE_SYNCHRONIZING) == 0 && //we're past "initNewPhase(PHASE_SYNCHRONIZING)" at this point! + getDataTotal (PHASE_SYNCHRONIZING) == 0) + finalStatus = _("Nothing to synchronize"); //even if "ignored conflicts" occurred! + else + finalStatus = _("Synchronization completed successfully"); + errorLog.logMsg(finalStatus, TYPE_INFO); + } + + const SummaryInfo summary = + { + jobName_, + finalStatus, + getObjectsCurrent(PHASE_SYNCHRONIZING), getDataCurrent(PHASE_SYNCHRONIZING), + getObjectsTotal (PHASE_SYNCHRONIZING), getDataTotal (PHASE_SYNCHRONIZING), + (wxGetUTCTimeMillis().GetValue() - startTime_) / 1000 + }; + + //print the results list: logfile + if (logFile.get()) + { + try + { + //saving log file below may take a *long* time, so report (without logging) + reportStatus(replaceCpy(_("Saving log file %x..."), L"%x", fmtFileName(logFile->getFilename()))); //throw? + forceUiRefresh(); // + } + catch (...) {} + + if (logfilesCountLimit_ > 0) + limitLogfileCount(beforeLast(logFile->getFilename(), FILE_NAME_SEPARATOR), jobName_, logfilesCountLimit_); //throw() + + try + { + saveLogToFile(summary, errorLog, *logFile); //throw FileError + } + catch (FileError&) {} + } + try + { + saveToLastSyncsLog(summary, errorLog, lastSyncsLogFileSizeMax_); //throw FileError + } + catch (FileError&) {} + + if (progressDlg) + { + if (showFinalResults) //warning: wxWindow::Show() is called within processHasFinished()! + { + //notify about (logical) application main window => program won't quit, but stay on this dialog + //setMainWindow(progressDlg->getAsWindow()); -> not required anymore since we block waiting until dialog is closed below + + //notify to progressDlg that current process has ended + if (abortIsRequested()) + progressDlg->processHasFinished(SyncProgressDialog::RESULT_ABORTED, errorLog); //enable okay and close events + else if (totalErrors > 0) + progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_ERROR, errorLog); + else if (totalWarnings > 0) + progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS, errorLog); + else + progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS, errorLog); + } + else + progressDlg->closeWindowDirectly(); //progressDlg is main window => program will quit directly + + //wait until progress dialog notified shutdown via onProgressDialogTerminate() + //-> required since it has our "this" pointer captured in lambda "notifyWindowTerminate"! + //-> nicely manages dialog lifetime + while (progressDlg) + { + wxTheApp->Yield(); //*first* refresh GUI (removing flicker) before sleeping! + boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL)); + } + } +} + + +void BatchStatusHandler::initNewPhase(int objectsTotal, Int64 dataTotal, ProcessCallback::Phase phaseID) +{ + StatusHandler::initNewPhase(objectsTotal, dataTotal, phaseID); + if (progressDlg) + progressDlg->initNewPhase(); //call after "StatusHandler::initNewPhase" +} + + +void BatchStatusHandler::updateProcessedData(int objectsDelta, Int64 dataDelta) +{ + StatusHandler::updateProcessedData(objectsDelta, dataDelta); + + if (progressDlg) + progressDlg->notifyProgressChange(); //noexcept + //note: this method should NOT throw in order to properly allow undoing setting of statistics! +} + + +void BatchStatusHandler::reportInfo(const std::wstring& text) +{ + StatusHandler::reportInfo(text); + errorLog.logMsg(text, TYPE_INFO); +} + + +void BatchStatusHandler::reportWarning(const std::wstring& warningMessage, bool& warningActive) +{ + errorLog.logMsg(warningMessage, TYPE_WARNING); + + if (!warningActive) + return; + + switch (handleError_) + { + case xmlAccess::ON_ERROR_POPUP: + { + if (!progressDlg) abortProcessNow(); + PauseTimers dummy(*progressDlg); + forceUiRefresh(); + + bool dontWarnAgain = false; + switch (showConfirmationDialog3(progressDlg->getWindowIfVisible(), DialogInfoType::WARNING, PopupDialogCfg3(). + setDetailInstructions(warningMessage + L"\n\n" + _("You can switch to FreeFileSync's main window to resolve this issue.")). + setCheckBox(dontWarnAgain, _("&Don't show this warning again"), ConfirmationButton3::DONT_DO_IT), + _("&Ignore"), _("&Switch"))) + { + case ConfirmationButton3::DO_IT: //ignore + warningActive = !dontWarnAgain; + break; + + case ConfirmationButton3::DONT_DO_IT: //switch + errorLog.logMsg(_("Switching to FreeFileSync's main window"), TYPE_INFO); + switchToGuiRequested = true; + abortProcessNow(); + break; + + case ConfirmationButton3::CANCEL: + abortProcessNow(); + break; + } + } + break; //keep it! last switch might not find match + + case xmlAccess::ON_ERROR_STOP: + abortProcessNow(); + break; + + case xmlAccess::ON_ERROR_IGNORE: + break; + } +} + + +ProcessCallback::Response BatchStatusHandler::reportError(const std::wstring& errorMessage, size_t retryNumber) +{ + //auto-retry + if (retryNumber < automaticRetryCount_) + { + errorLog.logMsg(errorMessage + L"\n=> " + + _P("Automatic retry in 1 second...", "Automatic retry in %x seconds...", automaticRetryDelay_), TYPE_INFO); + //delay + const int iterations = static_cast(1000 * automaticRetryDelay_ / UI_UPDATE_INTERVAL); //always round down: don't allow for negative remaining time below + for (int i = 0; i < iterations; ++i) + { + reportStatus(_("Error") + L": " + _P("Automatic retry in 1 second...", "Automatic retry in %x seconds...", + (1000 * automaticRetryDelay_ - i * UI_UPDATE_INTERVAL + 999) / 1000)); //integer round up + boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL)); + } + return ProcessCallback::RETRY; + } + + + //always, except for "retry": + zen::ScopeGuard guardWriteLog = zen::makeGuard([&] { errorLog.logMsg(errorMessage, TYPE_ERROR); }); + + switch (handleError_) + { + case xmlAccess::ON_ERROR_POPUP: + { + if (!progressDlg) abortProcessNow(); + PauseTimers dummy(*progressDlg); + forceUiRefresh(); + + bool ignoreNextErrors = false; + switch (showConfirmationDialog3(progressDlg->getWindowIfVisible(), DialogInfoType::ERROR2, PopupDialogCfg3(). + setDetailInstructions(errorMessage). + setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors"), ConfirmationButton3::DONT_DO_IT), + _("&Ignore"), _("&Retry"))) + { + case ConfirmationButton3::DO_IT: //ignore + if (ignoreNextErrors) //falsify only + handleError_ = xmlAccess::ON_ERROR_IGNORE; + return ProcessCallback::IGNORE_ERROR; + + case ConfirmationButton3::DONT_DO_IT: //retry + guardWriteLog.dismiss(); + errorLog.logMsg(errorMessage + L"\n=> " + _("Retrying operation..."), TYPE_INFO); + return ProcessCallback::RETRY; + + case ConfirmationButton3::CANCEL: + abortProcessNow(); + break; + } + } + break; //used if last switch didn't find a match + + case xmlAccess::ON_ERROR_STOP: + abortProcessNow(); + break; + + case xmlAccess::ON_ERROR_IGNORE: + return ProcessCallback::IGNORE_ERROR; + } + + assert(false); + return ProcessCallback::IGNORE_ERROR; //dummy value +} + + +void BatchStatusHandler::reportFatalError(const std::wstring& errorMessage) +{ + errorLog.logMsg(errorMessage, TYPE_FATAL_ERROR); + + switch (handleError_) + { + case xmlAccess::ON_ERROR_POPUP: + { + if (!progressDlg) abortProcessNow(); + PauseTimers dummy(*progressDlg); + forceUiRefresh(); + + bool ignoreNextErrors = false; + switch (showConfirmationDialog(progressDlg->getWindowIfVisible(), DialogInfoType::ERROR2, + PopupDialogCfg().setTitle(_("Serious Error")). + setDetailInstructions(errorMessage). + setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors")), + _("&Ignore"))) + { + case ConfirmationButton::DO_IT: + if (ignoreNextErrors) //falsify only + handleError_ = xmlAccess::ON_ERROR_IGNORE; + break; + case ConfirmationButton::CANCEL: + abortProcessNow(); + break; + } + } + break; + + case xmlAccess::ON_ERROR_STOP: + abortProcessNow(); + break; + + case xmlAccess::ON_ERROR_IGNORE: + break; + } +} + + +void BatchStatusHandler::forceUiRefresh() +{ + if (progressDlg) + progressDlg->updateGui(); +} + + +void BatchStatusHandler::abortProcessNow() +{ + requestAbortion(); //just make sure... + throw BatchAbortProcess(); //abort can be triggered by progressDlg +} + + +void BatchStatusHandler::onProgressDialogTerminate() +{ + //it's responsibility of "progressDlg" to call requestAbortion() when closing dialog + progressDlg = nullptr; +} diff --git a/FreeFileSync/Source/ui/batch_status_handler.h b/FreeFileSync/Source/ui/batch_status_handler.h new file mode 100644 index 00000000..e8caa7e8 --- /dev/null +++ b/FreeFileSync/Source/ui/batch_status_handler.h @@ -0,0 +1,77 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef BATCHSTATUSHANDLER_H_INCLUDED +#define BATCHSTATUSHANDLER_H_INCLUDED + +#include +#include +#include +#include "../lib/status_handler.h" +#include "../lib/process_xml.h" +#include "progress_indicator.h" +#include "switch_to_gui.h" +#include "../lib/return_codes.h" + + +//Exception class used to abort the "compare" and "sync" process +class BatchAbortProcess {}; + + +//BatchStatusHandler(SyncProgressDialog) will internally process Window messages! disable GUI controls to avoid unexpected callbacks! +class BatchStatusHandler : public zen::StatusHandler //throw BatchAbortProcess +{ +public: + BatchStatusHandler(bool showProgress, //defines: -start minimized and -quit immediately when finished + const std::wstring& jobName, //should not be empty for a batch job! + const zen::TimeComp& timeStamp, + const Zstring& logfileDirectory, + int logfilesCountLimit, //0: logging inactive; < 0: no limit + size_t lastSyncsLogFileSizeMax, + const xmlAccess::OnError handleError, + size_t automaticRetryCount, + size_t automaticRetryDelay, + const zen::SwitchToGui& switchBatchToGui, //functionality to change from batch mode to GUI mode + zen::FfsReturnCode& returnCode, + const std::wstring& execWhenFinished, + std::vector& execFinishedHistory); + ~BatchStatusHandler(); + + virtual void initNewPhase (int objectsTotal, zen::Int64 dataTotal, Phase phaseID); + virtual void updateProcessedData(int objectsDelta, zen::Int64 dataDelta); + virtual void reportInfo(const std::wstring& text); + virtual void forceUiRefresh(); + + virtual void reportWarning(const std::wstring& warningMessage, bool& warningActive); + virtual Response reportError(const std::wstring& errorMessage, size_t retryNumber); + virtual void reportFatalError(const std::wstring& errorMessage); + + virtual void abortProcessNow(); //throw BatchAbortProcess + +private: + void onProgressDialogTerminate(); + + const zen::SwitchToGui& switchBatchToGui_; //functionality to change from batch mode to GUI mode + bool showFinalResults; + bool switchToGuiRequested; + const int logfilesCountLimit_; + const size_t lastSyncsLogFileSizeMax_; + xmlAccess::OnError handleError_; + zen::ErrorLog errorLog; //list of non-resolved errors and warnings + zen::FfsReturnCode& returnCode_; + + const size_t automaticRetryCount_; + const size_t automaticRetryDelay_; + + SyncProgressDialog* progressDlg; //managed to have shorter lifetime than this handler! + std::unique_ptr logFile; //optional! + + const std::wstring jobName_; + const int64_t startTime_; //don't use wxStopWatch: may overflow after a few days due to ::QueryPerformanceCounter() +}; + + +#endif // BATCHSTATUSHANDLER_H_INCLUDED diff --git a/FreeFileSync/Source/ui/check_version.cpp b/FreeFileSync/Source/ui/check_version.cpp new file mode 100644 index 00000000..79fd2507 --- /dev/null +++ b/FreeFileSync/Source/ui/check_version.cpp @@ -0,0 +1,318 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "check_version.h" +//#include +#include +#include +#include +#include +#include +#include +#include "../version/version.h" +////#include "../lib/ffs_paths.h" +#include + +#ifdef ZEN_WIN +#include //tame wininet include +#include + +#elif defined ZEN_LINUX || defined ZEN_MAC +#include +#include +#endif + +using namespace zen; + + +namespace +{ +#ifdef ZEN_WIN +class InternetConnectionError {}; + +class WinInetAccess //using IE proxy settings! :) +{ +public: + WinInetAccess(const wchar_t* url) //throw InternetConnectionError (if url cannot be reached; no need to also call readBytes()) + { + //::InternetAttemptConnect(0) -> not working as expected: succeeds even when there is no internet connection! + + hInternet = ::InternetOpen(L"FreeFileSync", //_In_ LPCTSTR lpszAgent, + INTERNET_OPEN_TYPE_PRECONFIG, //_In_ DWORD dwAccessType, + nullptr, //_In_ LPCTSTR lpszProxyName, + nullptr, //_In_ LPCTSTR lpszProxyBypass, + 0); //_In_ DWORD dwFlags + if (!hInternet) + throw InternetConnectionError(); + zen::ScopeGuard guardInternet = zen::makeGuard([&] { ::InternetCloseHandle(hInternet); }); + + hRequest = ::InternetOpenUrl(hInternet, //_In_ HINTERNET hInternet, + url, //_In_ LPCTSTR lpszUrl, + nullptr, //_In_ LPCTSTR lpszHeaders, + 0, //_In_ DWORD dwHeadersLength, + INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_UI, //_In_ DWORD dwFlags, + 0); //_In_ DWORD_PTR dwContext + if (!hRequest) //won't fail due to unreachable url here! There is no substitute for HTTP_QUERY_STATUS_CODE!!! + throw InternetConnectionError(); + zen::ScopeGuard guardRequest = zen::makeGuard([&] { ::InternetCloseHandle(hRequest); }); + + DWORD statusCode = 0; + DWORD bufferLength = sizeof(statusCode); + if (!::HttpQueryInfo(hRequest, //_In_ HINTERNET hRequest, + HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, //_In_ DWORD dwInfoLevel, + &statusCode, //_Inout_ LPVOID lpvBuffer, + &bufferLength, //_Inout_ LPDWORD lpdwBufferLength, + nullptr)) //_Inout_ LPDWORD lpdwIndex + throw InternetConnectionError(); + + if (statusCode != HTTP_STATUS_OK) + throw InternetConnectionError(); //e.g. 404 - HTTP_STATUS_NOT_FOUND + + guardRequest .dismiss(); + guardInternet.dismiss(); + } + + ~WinInetAccess() + { + ::InternetCloseHandle(hRequest); + ::InternetCloseHandle(hInternet); + } + + template + OutputIterator readBytes(OutputIterator result) //throw InternetConnectionError + { + //internet says "HttpQueryInfo() + HTTP_QUERY_CONTENT_LENGTH" not supported by all http servers... + const DWORD bufferSize = 64 * 1024; + std::vector buffer(bufferSize); + for (;;) + { + DWORD bytesRead = 0; + if (!::InternetReadFile(hRequest, //_In_ HINTERNET hFile, + &buffer[0], //_Out_ LPVOID lpBuffer, + bufferSize, //_In_ DWORD dwNumberOfBytesToRead, + &bytesRead)) //_Out_ LPDWORD lpdwNumberOfBytesRead + throw InternetConnectionError(); + if (bytesRead == 0) + return result; + + result = std::copy(buffer.begin(), buffer.begin() + bytesRead, result); + } + } + +private: + HINTERNET hInternet; + HINTERNET hRequest; +}; + + +inline +bool canAccessUrl(const wchar_t* url) //throw () +{ + try + { + (void)WinInetAccess(url); //throw InternetConnectionError + return true; + } + catch (const InternetConnectionError&) { return false; } +} + + +template inline +OutputIterator readBytesUrl(const wchar_t* url, OutputIterator result) //throw InternetConnectionError +{ + return WinInetAccess(url).readBytes(result); //throw InternetConnectionError +} +#endif + + +enum GetVerResult +{ + GET_VER_SUCCESS, + GET_VER_NO_CONNECTION, //no internet connection or just Sourceforge down? + GET_VER_PAGE_NOT_FOUND //version file seems to have moved! => trigger an update! +}; + +GetVerResult getOnlineVersion(wxString& version) //empty string on error; +{ +#ifdef ZEN_WIN + //internet access supporting proxy connections + std::vector output; + try + { + readBytesUrl(L"http://freefilesync.sourceforge.net/latest_version.txt", std::back_inserter(output)); //throw InternetConnectionError + } + catch (const InternetConnectionError&) + { + return canAccessUrl(L"http://sourceforge.net/") ? GET_VER_PAGE_NOT_FOUND : GET_VER_NO_CONNECTION; + } + + output.push_back('\0'); + version = utfCvrtTo(&output[0]); + return GET_VER_SUCCESS; + +#elif defined ZEN_LINUX || defined ZEN_MAC + wxWindowDisabler dummy; + + auto getStringFromUrl = [](const wxString& server, const wxString& page, int timeout, wxString* output) -> bool //true on successful connection + { + wxHTTP webAccess; + webAccess.SetHeader(L"content-type", L"text/html; charset=utf-8"); + webAccess.SetTimeout(timeout); //default: 10 minutes(WTF are these wxWidgets people thinking???)... + + if (webAccess.Connect(server)) //will *not* fail for non-reachable url here! + { + //wxApp::IsMainLoopRunning(); // should return true + + std::unique_ptr httpStream(webAccess.GetInputStream(page)); + //must be deleted BEFORE webAccess is closed + + if (httpStream && webAccess.GetError() == wxPROTO_NOERR) + { + if (output) + { + output->clear(); + wxStringOutputStream outStream(output); + httpStream->Read(outStream); + } + return true; + } + } + return false; + }; + + if (getStringFromUrl(L"freefilesync.sourceforge.net", L"/latest_version.txt", 5, &version)) + return GET_VER_SUCCESS; + + const bool canConnectToSf = getStringFromUrl(L"sourceforge.net", L"/", 1, nullptr); + return canConnectToSf ? GET_VER_PAGE_NOT_FOUND : GET_VER_NO_CONNECTION; +#endif +} + + +const wchar_t VERSION_SEP = L'.'; + +std::vector parseVersion(const wxString& version) +{ + std::vector digits = split(version, VERSION_SEP); + + std::vector output; + std::transform(digits.begin(), digits.end(), std::back_inserter(output), [&](const wxString& d) { return stringTo(d); }); + return output; +} + + +bool haveNewerVersion(const wxString& onlineVersion) +{ + std::vector current = parseVersion(zen::currentVersion); + std::vector online = parseVersion(onlineVersion); + + if (online.empty() || online[0] == 0) //online version may be "This website has been moved..." In this case better check for an update + return true; + + return std::lexicographical_compare(current.begin(), current.end(), + online .begin(), online .end()); +} +} + + +void zen::checkForUpdateNow(wxWindow* parent) +{ + wxString onlineVersion; + switch (getOnlineVersion(onlineVersion)) + { + case GET_VER_SUCCESS: + if (haveNewerVersion(onlineVersion)) + { + switch (showConfirmationDialog(parent, DialogInfoType::INFO, PopupDialogCfg(). + setTitle(_("Check for Program Updates")). + setMainInstructions(_("A new version of FreeFileSync is available:") + L" " + onlineVersion + L"\n\n" + _("Download now?")), + _("&Download"))) + { + case ConfirmationButton::DO_IT: + wxLaunchDefaultBrowser(L"http://freefilesync.sourceforge.net/get_latest.php"); + break; + case ConfirmationButton::CANCEL: + break; + } + } + else + showNotificationDialog(parent, DialogInfoType::INFO, PopupDialogCfg(). + setTitle(_("Check for Program Updates")). + setMainInstructions(_("FreeFileSync is up to date."))); + break; + + case GET_VER_NO_CONNECTION: + showNotificationDialog(parent, DialogInfoType::ERROR2, PopupDialogCfg(). + setTitle(("Check for Program Updates")). + setMainInstructions(_("Unable to connect to sourceforge.net."))); + break; + + case GET_VER_PAGE_NOT_FOUND: + switch (showConfirmationDialog(parent, DialogInfoType::ERROR2, PopupDialogCfg(). + setTitle(_("Check for Program Updates")). + setMainInstructions(_("Cannot find current FreeFileSync version number online. Do you want to check manually?")), + _("&Check"))) + { + case ConfirmationButton::DO_IT: + wxLaunchDefaultBrowser(L"http://freefilesync.sourceforge.net/"); + break; + case ConfirmationButton::CANCEL: + break; + } + break; + } +} + + +void zen::checkForUpdatePeriodically(wxWindow* parent, long& lastUpdateCheck, const std::function& onBeforeInternetAccess) +{ + if (lastUpdateCheck != -1) + { + if (wxGetLocalTime() >= lastUpdateCheck + 7 * 24 * 3600) //check weekly + { + onBeforeInternetAccess(); //notify client before (potentially) blocking some time + wxString onlineVersion; + switch (getOnlineVersion(onlineVersion)) + { + case GET_VER_SUCCESS: + lastUpdateCheck = wxGetLocalTime(); + + if (haveNewerVersion(onlineVersion)) + { + switch (showConfirmationDialog(parent, DialogInfoType::INFO, PopupDialogCfg(). + setTitle(_("Check for Program Updates")). + setMainInstructions(_("A new version of FreeFileSync is available:") + L" " + onlineVersion + L"\n\n" + _("Download now?")), + _("&Download"))) + { + case ConfirmationButton::DO_IT: + wxLaunchDefaultBrowser(L"http://freefilesync.sourceforge.net/get_latest.php"); + break; + case ConfirmationButton::CANCEL: + break; + } + } + break; + + case GET_VER_NO_CONNECTION: + break; //ignore this error + + case GET_VER_PAGE_NOT_FOUND: + switch (showConfirmationDialog(parent, DialogInfoType::ERROR2, PopupDialogCfg(). + setTitle(_("Check for Program Updates")). + setMainInstructions(_("Cannot find current FreeFileSync version number online. Do you want to check manually?")), + _("&Check"))) + { + case ConfirmationButton::DO_IT: + wxLaunchDefaultBrowser(L"http://freefilesync.sourceforge.net/"); + break; + case ConfirmationButton::CANCEL: + break; + } + break; + } + } + } +} diff --git a/FreeFileSync/Source/ui/check_version.h b/FreeFileSync/Source/ui/check_version.h new file mode 100644 index 00000000..d2e7220f --- /dev/null +++ b/FreeFileSync/Source/ui/check_version.h @@ -0,0 +1,20 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef UPDATEVERSION_H_INCLUDED +#define UPDATEVERSION_H_INCLUDED + +#include +#include + + +namespace zen +{ +void checkForUpdateNow(wxWindow* parent); +void checkForUpdatePeriodically(wxWindow* parent, long& lastUpdateCheck, const std::function& onBeforeInternetAccess); //-1: check never +} + +#endif // UPDATEVERSION_H_INCLUDED diff --git a/FreeFileSync/Source/ui/column_attr.h b/FreeFileSync/Source/ui/column_attr.h new file mode 100644 index 00000000..f930f2ef --- /dev/null +++ b/FreeFileSync/Source/ui/column_attr.h @@ -0,0 +1,111 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef COL_ATTR_HEADER_189467891346732143214 +#define COL_ATTR_HEADER_189467891346732143214 + +#include + +namespace zen +{ +enum ColumnTypeRim +{ + COL_TYPE_DIRECTORY, + COL_TYPE_FULL_PATH, + COL_TYPE_REL_PATH, + COL_TYPE_FILENAME, + COL_TYPE_SIZE, + COL_TYPE_DATE, + COL_TYPE_EXTENSION +}; + +struct ColumnAttributeRim +{ + ColumnAttributeRim() : type_(COL_TYPE_DIRECTORY), offset_(0), stretch_(0), visible_(false) {} + ColumnAttributeRim(ColumnTypeRim type, int offset, int stretch, bool visible) : type_(type), offset_(offset), stretch_(stretch), visible_(visible) {} + + ColumnTypeRim type_; + int offset_; + int stretch_; + bool visible_; +}; + +warn_static("two stretched oclumsn: hide vergrert range!") +inline +std::vector getDefaultColumnAttributesLeft() +{ + std::vector attr; + attr.push_back(ColumnAttributeRim(COL_TYPE_FULL_PATH, 250, 0, false)); + attr.push_back(ColumnAttributeRim(COL_TYPE_DIRECTORY, 200, 0, false)); + attr.push_back(ColumnAttributeRim(COL_TYPE_REL_PATH, -280, 1, true)); //stretch to full width and substract sum of fixed size widths! + attr.push_back(ColumnAttributeRim(COL_TYPE_FILENAME, 200, 0, true)); + attr.push_back(ColumnAttributeRim(COL_TYPE_DATE, 112, 0, false)); + attr.push_back(ColumnAttributeRim(COL_TYPE_SIZE, 80, 0, true)); + attr.push_back(ColumnAttributeRim(COL_TYPE_EXTENSION, 60, 0, false)); + return attr; +} + +inline +std::vector getDefaultColumnAttributesRight() +{ + std::vector attr; + attr.push_back(ColumnAttributeRim(COL_TYPE_FULL_PATH, 250, 0, false)); + attr.push_back(ColumnAttributeRim(COL_TYPE_DIRECTORY, 200, 0, false)); + attr.push_back(ColumnAttributeRim(COL_TYPE_REL_PATH, 200, 0, false)); //already shown on left side + attr.push_back(ColumnAttributeRim(COL_TYPE_FILENAME, 200, 0, true)); + attr.push_back(ColumnAttributeRim(COL_TYPE_DATE, 112, 0, false)); + attr.push_back(ColumnAttributeRim(COL_TYPE_SIZE, 80, 0, true)); + attr.push_back(ColumnAttributeRim(COL_TYPE_EXTENSION, 60, 0, false)); + return attr; +} + +//------------------------------------------------------------------ + +enum ColumnTypeMiddle +{ + COL_TYPE_CHECKBOX, + COL_TYPE_CMP_CATEGORY, + COL_TYPE_SYNC_ACTION, +}; + +//------------------------------------------------------------------ + +enum ColumnTypeNavi +{ + COL_TYPE_NAVI_BYTES, + COL_TYPE_NAVI_DIRECTORY, + COL_TYPE_NAVI_ITEM_COUNT +}; + + +struct ColumnAttributeNavi +{ + ColumnAttributeNavi() : type_(COL_TYPE_NAVI_DIRECTORY), offset_(0), stretch_(0), visible_(false) {} + ColumnAttributeNavi(ColumnTypeNavi type, int offset, int stretch, bool visible) : type_(type), offset_(offset), stretch_(stretch), visible_(visible) {} + + ColumnTypeNavi type_; + int offset_; + int stretch_; + bool visible_; +}; + + +const bool defaultValueShowPercentage = true; +const ColumnTypeNavi defaultValueLastSortColumn = COL_TYPE_NAVI_BYTES; //remember sort on navigation panel +const bool defaultValueLastSortAscending = false; // + +inline +std::vector getDefaultColumnAttributesNavi() +{ + std::vector attr; + attr.push_back(ColumnAttributeNavi(COL_TYPE_NAVI_DIRECTORY, -120, 1, true)); //stretch to full width and substract sum of fixed size widths + attr.push_back(ColumnAttributeNavi(COL_TYPE_NAVI_ITEM_COUNT, 60, 0, true)); + attr.push_back(ColumnAttributeNavi(COL_TYPE_NAVI_BYTES, 60, 0, true)); //GTK needs a few pixels width more + return attr; +} +} + +#endif // COL_ATTR_HEADER_189467891346732143214 diff --git a/FreeFileSync/Source/ui/custom_grid.cpp b/FreeFileSync/Source/ui/custom_grid.cpp new file mode 100644 index 00000000..8ab6e2db --- /dev/null +++ b/FreeFileSync/Source/ui/custom_grid.cpp @@ -0,0 +1,1835 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "custom_grid.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../file_hierarchy.h" + +using namespace zen; +using namespace gridview; + + +const wxEventType zen::EVENT_GRID_CHECK_ROWS = wxNewEventType(); +const wxEventType zen::EVENT_GRID_SYNC_DIRECTION = wxNewEventType(); + +namespace +{ +const wxColour COLOR_ORANGE (238, 201, 0); +const wxColour COLOR_GREY (212, 208, 200); +const wxColour COLOR_YELLOW (247, 252, 62); +const wxColour COLOR_YELLOW_LIGHT(253, 252, 169); +const wxColour COLOR_CMP_RED (255, 185, 187); +const wxColour COLOR_SYNC_BLUE (185, 188, 255); +const wxColour COLOR_SYNC_GREEN (196, 255, 185); +const wxColour COLOR_NOT_ACTIVE (228, 228, 228); //light grey + + +const Zstring ICON_FILE_FOLDER = Zstr("folder"); +const size_t ROW_COUNT_NO_DATA = 10; + +/* +class hierarchy: + GridDataBase + /|\ + ________________|________________ + | | + GridDataRim | + /|\ | + __________|__________ | + | | | + GridDataLeft GridDataRight GridDataMiddle +*/ + + + +void refreshCell(Grid& grid, size_t row, ColumnType colType) +{ + wxRect cellArea = grid.getCellArea(row, colType); //returns empty rect if column not found; absolute coordinates! + if (cellArea.height > 0) + { + cellArea.SetTopLeft(grid.CalcScrolledPosition(cellArea.GetTopLeft())); + grid.getMainWin().RefreshRect(cellArea, false); + } +} + + +std::pair getVisibleRows(const Grid& grid) //returns range [from, to) +{ + const wxSize clientSize = grid.getMainWin().GetClientSize(); + if (clientSize.GetHeight() > 0) + { + wxPoint topLeft = grid.CalcUnscrolledPosition(wxPoint(0, 0)); + wxPoint bottom = grid.CalcUnscrolledPosition(wxPoint(0, clientSize.GetHeight() - 1)); + + const ptrdiff_t rowCount = grid.getRowCount(); + const ptrdiff_t rowFrom = grid.getRowAtPos(topLeft.y); //return -1 for invalid position, rowCount if out of range + if (rowFrom >= 0) + { + const ptrdiff_t rowTo = grid.getRowAtPos(bottom.y); + if (0 <= rowTo && rowTo < rowCount) + return std::make_pair(rowFrom, rowTo + 1); + else + return std::make_pair(rowFrom, rowCount); + } + } + return std::make_pair(0, 0); +} + + +void fillBackgroundDefaultColorAlternating(wxDC& dc, const wxRect& rect, bool evenRowNumber) +{ + //alternate background color to improve readability (while lacking cell borders) + if (!evenRowNumber) + { + //accessibility, support high-contrast schemes => work with user-defined background color! + const auto backCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + + auto incChannel = [](unsigned char c, int diff) { return static_cast(std::max(0, std::min(255, c + diff))); }; + + auto getAdjustedColor = [&](int diff) + { + return wxColor(incChannel(backCol.Red (), diff), + incChannel(backCol.Green(), diff), + incChannel(backCol.Blue (), diff)); + }; + + auto colorDist = [](const wxColor& lhs, const wxColor& rhs) //just some metric + { + return numeric::power<2>(static_cast(lhs.Red ()) - static_cast(rhs.Red ())) + + numeric::power<2>(static_cast(lhs.Green()) - static_cast(rhs.Green())) + + numeric::power<2>(static_cast(lhs.Blue ()) - static_cast(rhs.Blue ())); + }; + + const int signLevel = colorDist(backCol, *wxBLACK) < colorDist(backCol, *wxWHITE) ? 1 : -1; //brighten or darken + + const wxColor colOutter = getAdjustedColor(signLevel * 14); //just some very faint gradient to avoid visual distraction + const wxColor colInner = getAdjustedColor(signLevel * 11); // + + //clearArea(dc, rect, backColAlt); + + //add some nice background gradient + wxRect rectUpper = rect; + rectUpper.height /= 2; + wxRect rectLower = rect; + rectLower.y += rectUpper.height; + rectLower.height -= rectUpper.height; + dc.GradientFillLinear(rectUpper, colOutter, colInner, wxSOUTH); + dc.GradientFillLinear(rectLower, colOutter, colInner, wxNORTH); + } + else + clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); +} + + +Zstring getExtension(const Zstring& shortName) +{ + return contains(shortName, Zchar('.')) ? afterLast(shortName, Zchar('.')) : Zstring(); +}; + + +class IconUpdater; +class GridEventManager; +class GridDataLeft; +class GridDataRight; + +struct IconManager +{ + IconManager(GridDataLeft& provLeft, GridDataRight& provRight, IconBuffer::IconSize sz) : iconBuffer(sz), + iconUpdater(make_unique(provLeft, provRight, iconBuffer)) {} + + void startIconUpdater(); + IconBuffer& refIconBuffer() { return iconBuffer; } +private: + IconBuffer iconBuffer; + std::unique_ptr iconUpdater; //bind ownership to GridDataRim<>! +}; + +//######################################################################################################## + +class GridDataBase : public GridData +{ +public: + GridDataBase(Grid& grid, const std::shared_ptr& gridDataView) : grid_(grid), gridDataView_(gridDataView) {} + + void holdOwnership(const std::shared_ptr& evtMgr) { evtMgr_ = evtMgr; } + GridEventManager* getEventManager() { return evtMgr_.get(); } + +protected: + Grid& refGrid() { return grid_; } + const Grid& refGrid() const { return grid_; } + + const GridView* getGridDataView() const { return gridDataView_.get(); } + + const FileSystemObject* getRawData(size_t row) const + { + if (auto view = getGridDataView()) + return view->getObject(row); + return nullptr; + } + +private: + virtual size_t getRowCount() const + { + if (gridDataView_) + { + if (gridDataView_->rowsTotal() == 0) + return ROW_COUNT_NO_DATA; + return gridDataView_->rowsOnView(); + } + else + return ROW_COUNT_NO_DATA; + + //return std::max(MIN_ROW_COUNT, gridDataView_ ? gridDataView_->rowsOnView() : 0); + } + + std::shared_ptr evtMgr_; + Grid& grid_; + std::shared_ptr gridDataView_; +}; + +//######################################################################################################## + +template +class GridDataRim : public GridDataBase +{ +public: + GridDataRim(const std::shared_ptr& gridDataView, Grid& grid) : GridDataBase(grid, gridDataView) {} + + void setIconManager(const std::shared_ptr& iconMgr) { iconMgr_ = iconMgr; } + + + void updateNewAndGetUnbufferedIcons(std::vector& newLoad) //loads all not yet drawn icons + { + if (iconMgr_) + { + const auto& rowsOnScreen = getVisibleRows(refGrid()); + const ptrdiff_t visibleRowCount = rowsOnScreen.second - rowsOnScreen.first; + + //loop over all visible rows + for (ptrdiff_t i = 0; i < visibleRowCount; ++i) + { + //alternate when adding rows: first, last, first + 1, last - 1 ... + const ptrdiff_t currentRow = rowsOnScreen.first + getAlternatingPos(i, visibleRowCount); + + if (isFailedLoad(currentRow)) //find failed attempts to load icon + { + const Zstring filename = getIconFile(currentRow); + if (!filename.empty() && filename != ICON_FILE_FOLDER) + { + //test if they are already loaded in buffer: + if (iconMgr_->refIconBuffer().readyForRetrieval(filename)) + { + //do a *full* refresh for *every* failed load to update partial DC updates while scrolling + refreshCell(refGrid(), currentRow, static_cast(COL_TYPE_FILENAME)); + setFailedLoad(currentRow, false); + } + else //not yet in buffer: mark for async. loading + newLoad.push_back(filename); + } + } + } + } + } + + void getUnbufferedIconsForPreload(std::vector>& newLoad) //return (priority, filename) list + { + if (iconMgr_) + { + const auto& rowsOnScreen = getVisibleRows(refGrid()); + const ptrdiff_t visibleRowCount = rowsOnScreen.second - rowsOnScreen.first; + + //preload icons not yet on screen: + const int preloadSize = 2 * std::max(20, visibleRowCount); //:= sum of lines above and below of visible range to preload + //=> use full visible height to handle "next page" command and a minimum of 20 for excessive mouse wheel scrolls + + for (ptrdiff_t i = 0; i < preloadSize; ++i) + { + const ptrdiff_t currentRow = rowsOnScreen.first - (preloadSize + 1) / 2 + getAlternatingPos(i, visibleRowCount + preloadSize); //for odd preloadSize start one row earlier + + const Zstring filename = getIconFile(currentRow); + if (!filename.empty() && filename != ICON_FILE_FOLDER) + if (!iconMgr_->refIconBuffer().readyForRetrieval(filename)) + newLoad.push_back(std::make_pair(i, filename)); //insert least-important items on outer rim first + } + } + } + +private: + bool isFailedLoad(size_t row) const { return row < failedLoads.size() ? failedLoads[row] != 0 : false; } + + void setFailedLoad(size_t row, bool failed = true) + { + if (failedLoads.size() != refGrid().getRowCount()) + failedLoads.resize(refGrid().getRowCount()); + + if (row < failedLoads.size()) + failedLoads[row] = failed; + } + + //icon buffer will load reversely, i.e. if we want to go from inside out, we need to start from outside in + static size_t getAlternatingPos(size_t pos, size_t total) + { + assert(pos < total); + return pos % 2 == 0 ? pos / 2 : total - 1 - pos / 2; + } + +protected: + virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) override + { + if (enabled) + { + if (selected) + dc.GradientFillLinear(rect, getColorSelectionGradientFrom(), getColorSelectionGradientTo(), wxEAST); + //ignore focus + else + { + //alternate background color to improve readability (while lacking cell borders) + if (getRowDisplayType(row) == DISP_TYPE_NORMAL) + fillBackgroundDefaultColorAlternating(dc, rect, row % 2 == 0); + else + clearArea(dc, rect, getBackGroundColor(row)); + + //draw horizontal border if required + DisplayType dispTp = getRowDisplayType(row); + if (dispTp != DISP_TYPE_NORMAL && + dispTp == getRowDisplayType(row + 1)) + { + const wxColor colorGridLine = wxColour(192, 192, 192); //light grey + wxDCPenChanger dummy2(dc, wxPen(colorGridLine, 1, wxSOLID)); + dc.DrawLine(rect.GetBottomLeft(), rect.GetBottomRight() + wxPoint(1, 0)); + } + } + } + else + clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); + } + + wxColor getBackGroundColor(size_t row) const + { + //accessibility: always set both foreground AND background colors! + // => harmonize with renderCell()! + + switch (getRowDisplayType(row)) + { + case DISP_TYPE_NORMAL: + break; + case DISP_TYPE_FOLDER: + return COLOR_GREY; + case DISP_TYPE_SYMLINK: + return COLOR_ORANGE; + case DISP_TYPE_INACTIVE: + return COLOR_NOT_ACTIVE; + } + return wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + } + +private: + enum DisplayType + { + DISP_TYPE_NORMAL, + DISP_TYPE_FOLDER, + DISP_TYPE_SYMLINK, + DISP_TYPE_INACTIVE, + }; + + DisplayType getRowDisplayType(size_t row) const + { + const FileSystemObject* fsObj = getRawData(row); + if (!fsObj ) + return DISP_TYPE_NORMAL; + + //mark filtered rows + if (!fsObj->isActive()) + return DISP_TYPE_INACTIVE; + + if (fsObj->isEmpty()) //always show not existing files/dirs/symlinks as empty + return DISP_TYPE_NORMAL; + + DisplayType output = DISP_TYPE_NORMAL; + //mark directories and symlinks + struct GetRowType : public FSObjectVisitor + { + GetRowType(DisplayType& result) : result_(result) {} + + virtual void visit(const FilePair& fileObj) {} + virtual void visit(const SymlinkPair& linkObj) + { + result_ = DISP_TYPE_SYMLINK; + } + virtual void visit(const DirPair& dirObj) + { + result_ = DISP_TYPE_FOLDER; + } + private: + DisplayType& result_; + } getType(output); + fsObj->accept(getType); + return output; + } + + virtual wxString getValue(size_t row, ColumnType colType) const + { + if (const FileSystemObject* fsObj = getRawData(row)) + { + struct GetTextValue : public FSObjectVisitor + { + GetTextValue(ColumnTypeRim colType, const FileSystemObject& fso) : colType_(colType), fsObj_(fso) {} + + virtual void visit(const FilePair& fileObj) + { + switch (colType_) + { + case COL_TYPE_FULL_PATH: + value = toWx(appendSeparator(beforeLast(fileObj.getBaseDirPf() + fileObj.getObjRelativeName(), FILE_NAME_SEPARATOR))); + break; + case COL_TYPE_FILENAME: //filename + value = toWx(fileObj.getShortName()); + break; + case COL_TYPE_REL_PATH: //relative path + value = toWx(beforeLast(fileObj.getObjRelativeName(), FILE_NAME_SEPARATOR)); //returns empty string if ch not found + break; + case COL_TYPE_DIRECTORY: + value = toWx(fileObj.getBaseDirPf()); + break; + case COL_TYPE_SIZE: //file size + if (!fsObj_.isEmpty()) + value = zen::toGuiString(fileObj.getFileSize()); + + // -> test file id + //if (!fsObj_.isEmpty()) + // value = toGuiString(fileObj.getFileId().second) + L" " + toGuiString(fileObj.getFileId().first); + break; + case COL_TYPE_DATE: //date + if (!fsObj_.isEmpty()) + value = zen::utcToLocalTimeString(fileObj.getLastWriteTime()); + break; + case COL_TYPE_EXTENSION: //file extension + value = toWx(getExtension(fileObj.getShortName())); + break; + } + } + + virtual void visit(const SymlinkPair& linkObj) + { + switch (colType_) + { + case COL_TYPE_FULL_PATH: + value = toWx(appendSeparator(beforeLast(linkObj.getBaseDirPf() + linkObj.getObjRelativeName(), FILE_NAME_SEPARATOR))); + break; + case COL_TYPE_FILENAME: //filename + value = toWx(linkObj.getShortName()); + break; + case COL_TYPE_REL_PATH: //relative path + value = toWx(beforeLast(linkObj.getObjRelativeName(), FILE_NAME_SEPARATOR)); //returns empty string if ch not found + break; + case COL_TYPE_DIRECTORY: + value = toWx(linkObj.getBaseDirPf()); + break; + case COL_TYPE_SIZE: //file size + if (!fsObj_.isEmpty()) + value = L"<" + _("Symlink") + L">"; + break; + case COL_TYPE_DATE: //date + if (!fsObj_.isEmpty()) + value = zen::utcToLocalTimeString(linkObj.getLastWriteTime()); + break; + case COL_TYPE_EXTENSION: //file extension + value = toWx(getExtension(linkObj.getShortName())); + break; + } + } + + virtual void visit(const DirPair& dirObj) + { + switch (colType_) + { + case COL_TYPE_FULL_PATH: + value = toWx(appendSeparator(beforeLast(dirObj.getBaseDirPf() + dirObj.getObjRelativeName(), FILE_NAME_SEPARATOR))); + break; + case COL_TYPE_FILENAME: + value = toWx(dirObj.getShortName()); + break; + case COL_TYPE_REL_PATH: + value = toWx(beforeLast(dirObj.getObjRelativeName(), FILE_NAME_SEPARATOR)); //returns empty string if ch not found + break; + case COL_TYPE_DIRECTORY: + value = toWx(dirObj.getBaseDirPf()); + break; + case COL_TYPE_SIZE: //file size + if (!fsObj_.isEmpty()) + value = L"<" + _("Folder") + L">"; + break; + case COL_TYPE_DATE: //date + if (!fsObj_.isEmpty()) + value = wxEmptyString; + break; + case COL_TYPE_EXTENSION: //file extension + value = wxEmptyString; + break; + } + } + ColumnTypeRim colType_; + wxString value; + + const FileSystemObject& fsObj_; + } getVal(static_cast(colType), *fsObj); + fsObj->accept(getVal); + return getVal.value; + } + //if data is not found: + return wxEmptyString; + } + + static const int GAP_SIZE = 2; + + virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool selected) override + { + wxRect rectTmp = rect; + + const bool isActive = [&]() -> bool + { + if (const FileSystemObject* fsObj = this->getRawData(row)) + return fsObj->isActive(); + return true; + }(); + + //draw file icon + if (static_cast(colType) == COL_TYPE_FILENAME && + iconMgr_) + { + rectTmp.x += GAP_SIZE; + rectTmp.width -= GAP_SIZE; + + const int iconSize = iconMgr_->refIconBuffer().getSize(); + if (rectTmp.GetWidth() >= iconSize) + { + // Partitioning: + // __________________________ + // | gap | icon | gap | text | + // -------------------------- + + //whenever there's something new to render on screen, start up watching for failed icon drawing: + //=> ideally it would suffice to start watching only when scrolling grid or showing new grid content, but this solution is more robust + //and the icon updater will stop automatically when finished anyway + //Note: it's not sufficient to start up on failed icon loads only, since we support prefetching of not yet visible rows!!! + iconMgr_->startIconUpdater(); + + const Zstring filename = getIconFile(row); + if (!filename.empty()) + { + wxBitmap fileIcon; + + //first check if it is a directory icon: + if (filename == ICON_FILE_FOLDER) + fileIcon = iconMgr_->refIconBuffer().genericDirIcon(); + else //retrieve file icon + { + if (Opt tmpIco = iconMgr_->refIconBuffer().retrieveFileIcon(filename)) + fileIcon = *tmpIco; + else + { + fileIcon = iconMgr_->refIconBuffer().genericFileIcon(); //better than nothing + setFailedLoad(row); //save status of failed icon load -> used for async. icon loading + //falsify only! we want to avoid writing incorrect success values when only partially updating the DC, e.g. when scrolling, + //see repaint behavior of ::ScrollWindow() function! + } + } + + if (fileIcon.IsOk()) + { + wxRect rectIcon = rectTmp; + rectIcon.width = iconSize; //support small thumbnail centering + if (isActive) + drawBitmapRtlNoMirror(dc, fileIcon, rectIcon, wxALIGN_CENTER, buffer); + else + drawBitmapRtlNoMirror(dc, wxBitmap(fileIcon.ConvertToImage().ConvertToGreyscale(1.0 / 3, 1.0 / 3, 1.0 / 3)), //treat all channels equally! + rectIcon, wxALIGN_CENTER, buffer); + } + } + } + rectTmp.x += iconSize; + rectTmp.width -= iconSize; + } + + std::unique_ptr dummy3; + if (getRowDisplayType(row) != DISP_TYPE_NORMAL) + dummy3 = make_unique(dc, *wxBLACK); //accessibility: always set both foreground AND background colors! + + //draw text + if (static_cast(colType) == COL_TYPE_SIZE && refGrid().GetLayoutDirection() != wxLayout_RightToLeft) + { + //have file size right-justified (but don't change for RTL languages) + rectTmp.width -= GAP_SIZE; + drawCellText(dc, rectTmp, getValue(row, colType), isActive, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL); + } + else + { + rectTmp.x += GAP_SIZE; + rectTmp.width -= GAP_SIZE; + drawCellText(dc, rectTmp, getValue(row, colType), isActive, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); + } + } + + virtual int getBestSize(wxDC& dc, size_t row, ColumnType colType) override + { + // Partitioning: + // ________________________________ + // | gap | icon | gap | text | gap | + // -------------------------------- + + int bestSize = 0; + if (static_cast(colType) == COL_TYPE_FILENAME && iconMgr_) + bestSize += GAP_SIZE + iconMgr_->refIconBuffer().getSize(); + + bestSize += GAP_SIZE + dc.GetTextExtent(getValue(row, colType)).GetWidth() + GAP_SIZE; + + return bestSize; // + 1 pix for cell border line -> not used anymore! + } + + virtual wxString getColumnLabel(ColumnType colType) const + { + switch (static_cast(colType)) + { + case COL_TYPE_FULL_PATH: + return _("Full path"); + case COL_TYPE_FILENAME: + return _("Name"); //= short name + case COL_TYPE_REL_PATH: + return _("Relative path"); + case COL_TYPE_DIRECTORY: + return _("Base folder"); + case COL_TYPE_SIZE: + return _("Size"); + case COL_TYPE_DATE: + return _("Date"); + case COL_TYPE_EXTENSION: + return _("Extension"); + } + return wxEmptyString; + } + + virtual void renderColumnLabel(Grid& tree, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted) override + { + wxRect rectInside = drawColumnLabelBorder(dc, rect); + drawColumnLabelBackground(dc, rectInside, highlighted); + + rectInside.x += COLUMN_GAP_LEFT; + rectInside.width -= COLUMN_GAP_LEFT; + drawColumnLabelText(dc, rectInside, getColumnLabel(colType)); + + //draw sort marker + if (getGridDataView()) + { + auto sortInfo = getGridDataView()->getSortInfo(); + if (sortInfo) + { + if (colType == static_cast(sortInfo->type_) && (side == LEFT_SIDE) == sortInfo->onLeft_) + { + const wxBitmap& marker = getResourceImage(sortInfo->ascending_ ? L"sortAscending" : L"sortDescending"); + wxPoint markerBegin = rectInside.GetTopLeft() + wxPoint((rectInside.width - marker.GetWidth()) / 2, 0); + dc.DrawBitmap(marker, markerBegin, true); //respect 2-pixel gap + } + } + } + } + + Zstring getIconFile(size_t row) const //return ICON_FILE_FOLDER if row points to a folder + { + const FileSystemObject* fsObj = getRawData(row); + if (fsObj && !fsObj->isEmpty()) + { + struct GetIcon : public FSObjectVisitor + { + virtual void visit(const FilePair& fileObj) + { + iconName = fileObj.getFullName(); + } + virtual void visit(const SymlinkPair& linkObj) + { + iconName = linkObj.getFullName(); + } + virtual void visit(const DirPair& dirObj) + { + iconName = ICON_FILE_FOLDER; + } + + Zstring iconName; + } getIcon; + fsObj->accept(getIcon); + return getIcon.iconName; + } + return Zstring(); + } + + virtual wxString getToolTip(size_t row, ColumnType colType) const override + { + wxString toolTip; + + const FileSystemObject* fsObj = getRawData(row); + if (fsObj && !fsObj->isEmpty()) + { + toolTip = toWx(getGridDataView() && getGridDataView()->getFolderPairCount() > 1 ? + fsObj->getFullName() : + fsObj->getRelativeName()); + + struct AssembleTooltip : public FSObjectVisitor + { + AssembleTooltip(wxString& tipMsg) : tipMsg_(tipMsg) {} + + virtual void visit(const FilePair& fileObj) + { + tipMsg_ += L"\n" + + _("Size:") + L" " + zen::filesizeToShortString(to(fileObj.getFileSize())) + L"\n" + + _("Date:") + L" " + zen::utcToLocalTimeString(fileObj.getLastWriteTime()); + } + + virtual void visit(const SymlinkPair& linkObj) + { + tipMsg_ += L"\n" + + _("Date:") + L" " + zen::utcToLocalTimeString(linkObj.getLastWriteTime()); + } + + virtual void visit(const DirPair& dirObj) {} + + wxString& tipMsg_; + } assembler(toolTip); + fsObj->accept(assembler); + } + return toolTip; + } + + std::shared_ptr iconMgr_; //optional + std::vector failedLoads; //effectively a vector of size "number of rows" + std::unique_ptr buffer; //avoid costs of recreating this temporal variable +}; + + +class GridDataLeft : public GridDataRim +{ +public: + GridDataLeft(const std::shared_ptr& gridDataView, Grid& grid) : GridDataRim(gridDataView, grid) {} + + void setNavigationMarker(hash_set&& markedFilesAndLinks, + hash_set&& markedContainer) + { + markedFilesAndLinks_.swap(markedFilesAndLinks); + markedContainer_ .swap(markedContainer); + } + +private: + virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) override + { + GridDataRim::renderRowBackgound(dc, rect, row, enabled, selected); + + //mark rows selected on navigation grid: + if (enabled && !selected) + { + const bool markRow = [&]() -> bool + { + if (const FileSystemObject* fsObj = getRawData(row)) + { + if (markedFilesAndLinks_.find(fsObj) != markedFilesAndLinks_.end()) //mark files/links directly + return true; + + if (auto dirObj = dynamic_cast(fsObj)) + { + if (markedContainer_.find(dirObj) != markedContainer_.end()) //mark directories which *are* the given HierarchyObject* + return true; + } + + //mark all objects which have the HierarchyObject as *any* matching ancestor + const HierarchyObject* parent = &(fsObj->parent()); + for (;;) + { + if (markedContainer_.find(parent) != markedContainer_.end()) + return true; + + if (auto dirObj = dynamic_cast(parent)) + parent = &(dirObj->parent()); + else + break; + } + } + return false; + }(); + + if (markRow) + { + //const wxColor COLOR_TREE_SELECTION_GRADIENT = wxColor(101, 148, 255); //H:158 S:255 V:178 + const wxColor COLOR_TREE_SELECTION_GRADIENT = getColorSelectionGradientFrom(); + + wxRect rectTmp = rect; + rectTmp.width /= 20; + dc.GradientFillLinear(rectTmp, COLOR_TREE_SELECTION_GRADIENT, GridDataRim::getBackGroundColor(row), wxEAST); + } + } + } + + hash_set markedFilesAndLinks_; //mark files/symlinks directly within a container + hash_set markedContainer_; //mark full container including all child-objects + //DO NOT DEREFERENCE!!!! NOT GUARANTEED TO BE VALID!!! +}; + + +class GridDataRight : public GridDataRim +{ +public: + GridDataRight(const std::shared_ptr& gridDataView, Grid& grid) : GridDataRim(gridDataView, grid) {} +}; + +//######################################################################################################## + +class GridDataMiddle : public GridDataBase +{ +public: + GridDataMiddle(const std::shared_ptr& gridDataView, Grid& grid) : + GridDataBase(grid, gridDataView), + highlightSyncAction_(false), + toolTip(grid), //tool tip must not live longer than grid! + notch(getResourceImage(L"notch").ConvertToImage()) {} + + void onSelectBegin(const wxPoint& clientPos, size_t row, ColumnType colType) + { + if (row < refGrid().getRowCount()) + { + refGrid().clearSelection(DENY_GRID_EVENT); //don't emit event, prevent recursion! + dragSelection = make_unique>(row, mousePosToBlock(clientPos, row, static_cast(colType))); + toolTip.hide(); //handle custom tooltip + } + } + + void onSelectEnd(size_t rowFirst, size_t rowLast) //we cannot reuse row from "onSelectBegin": if user is holding shift, this may now be in the middle of the range! + { + refGrid().clearSelection(DENY_GRID_EVENT); //don't emit event, prevent recursion! + + //issue custom event + if (dragSelection) + { + if (rowFirst < rowLast && //may be empty? probably not in this context + rowLast <= refGrid().getRowCount()) + { + if (wxEvtHandler* evtHandler = refGrid().GetEventHandler()) + switch (dragSelection->second) + { + case BLOCKPOS_CHECK_BOX: + { + const FileSystemObject* fsObj = getRawData(dragSelection->first); + const bool setIncluded = fsObj ? !fsObj->isActive() : true; + + CheckRowsEvent evt(rowFirst, rowLast, setIncluded); + evtHandler->ProcessEvent(evt); + } + break; + case BLOCKPOS_LEFT: + { + SyncDirectionEvent evt(rowFirst, rowLast, SyncDirection::LEFT); + evtHandler->ProcessEvent(evt); + } + break; + case BLOCKPOS_MIDDLE: + { + SyncDirectionEvent evt(rowFirst, rowLast, SyncDirection::NONE); + evtHandler->ProcessEvent(evt); + } + break; + case BLOCKPOS_RIGHT: + { + SyncDirectionEvent evt(rowFirst, rowLast, SyncDirection::RIGHT); + evtHandler->ProcessEvent(evt); + } + break; + } + } + dragSelection.reset(); + } + + //update highlight and tooltip: on OS X no mouse movement event is generated after a mouse button click (unlike on Windows) + wxPoint clientPos = refGrid().getMainWin().ScreenToClient(wxGetMousePosition()); + onMouseMovement(clientPos); + } + + void onMouseMovement(const wxPoint& clientPos) + { + //manage block highlighting and custom tooltip + if (!dragSelection) + { + auto refreshHighlight = [&](size_t row) + { + refreshCell(refGrid(), row, static_cast(COL_TYPE_CHECKBOX)); + refreshCell(refGrid(), row, static_cast(COL_TYPE_SYNC_ACTION)); + }; + + const wxPoint& topLeftAbs = refGrid().CalcUnscrolledPosition(clientPos); + const size_t row = refGrid().getRowAtPos(topLeftAbs.y); //return -1 for invalid position, rowCount if one past the end + const Opt ct = refGrid().getColumnAtPos(topLeftAbs.x); + + if (row < refGrid().getRowCount() && ct) + { + if (highlight) refreshHighlight(highlight->row_); //refresh old highlight + + highlight = make_unique(row, mousePosToBlock(clientPos, row, static_cast(*ct))); + + refreshHighlight(highlight->row_); + + //show custom tooltip + if (refGrid().getMainWin().GetClientRect().Contains(clientPos)) //cursor might have moved outside visible client area + showToolTip(row, static_cast(*ct), refGrid().getMainWin().ClientToScreen(clientPos)); + } + else + onMouseLeave(); + } + } + + void onMouseLeave() //wxEVT_LEAVE_WINDOW does not respect mouse capture! + { + if (!dragSelection) + { + if (highlight) + { + refreshCell(refGrid(), highlight->row_, static_cast(COL_TYPE_CHECKBOX)); + refreshCell(refGrid(), highlight->row_, static_cast(COL_TYPE_SYNC_ACTION)); + highlight.reset(); + } + toolTip.hide(); //handle custom tooltip + } + } + + void highlightSyncAction(bool value) { highlightSyncAction_ = value; } + +private: + virtual wxString getValue(size_t row, ColumnType colType) const override + { + if (const FileSystemObject* fsObj = getRawData(row)) + switch (static_cast(colType)) + { + case COL_TYPE_CHECKBOX: + break; + case COL_TYPE_CMP_CATEGORY: + return getSymbol(fsObj->getCategory()); + case COL_TYPE_SYNC_ACTION: + return getSymbol(fsObj->getSyncOperation()); + } + return wxString(); + } + + virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) override + { + const FileSystemObject* fsObj = getRawData(row); + GridData::drawCellBackground(dc, rect, enabled, selected, highlightSyncAction_ ? + getBackGroundColorSyncAction(fsObj) : + getBackGroundColorCmpCategory(fsObj)); + } + + virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool selected) override + { + auto drawInactiveColumBackground = [&](const FileSystemObject& fsObj) + { + if (fsObj.isActive()) + fillBackgroundDefaultColorAlternating(dc, rect, row % 2 == 0); + else + clearArea(dc, rect, COLOR_NOT_ACTIVE); + }; + + switch (static_cast(colType)) + { + case COL_TYPE_CHECKBOX: + if (const FileSystemObject* fsObj = getRawData(row)) + { + wxRect rectInside = rect; + + //if sync action is shown draw notch on left side, right side otherwise + if (notch.GetHeight() != rectInside.GetHeight()) + notch.Rescale(notch.GetWidth(), rectInside.GetHeight()); + if (highlightSyncAction_) + { + drawBitmapRtlMirror(dc, notch, rectInside, wxALIGN_LEFT, buffer); + rectInside.x += notch.GetWidth(); + rectInside.width -= notch.GetWidth(); + } + else + { + //wxWidgets screws up again and has wxALIGN_RIGHT off by one pixel! -> use wxALIGN_LEFT instead + wxRect rectNotch(rectInside.x + rectInside.width - notch.GetWidth(), rectInside.y, + notch.GetWidth(), rectInside.height); + drawBitmapRtlMirror(dc, notch, rectNotch, wxALIGN_LEFT, buffer); + rectInside.width -= notch.GetWidth(); + } + + const bool rowHighlighted = dragSelection ? row == dragSelection->first : highlight ? row == highlight->row_ : false; + const BlockPosition highlightBlock = dragSelection ? dragSelection->second : highlight ? highlight->blockPos_ : BLOCKPOS_CHECK_BOX; + + if (rowHighlighted && highlightBlock == BLOCKPOS_CHECK_BOX) + drawBitmapRtlMirror(dc, getResourceImage(fsObj->isActive() ? L"checkboxTrueFocus" : L"checkboxFalseFocus"), rectInside, wxALIGN_CENTER, buffer); + else //default + drawBitmapRtlMirror(dc, getResourceImage(fsObj->isActive() ? L"checkboxTrue" : L"checkboxFalse" ), rectInside, wxALIGN_CENTER, buffer); + } + break; + + case COL_TYPE_CMP_CATEGORY: + if (const FileSystemObject* fsObj = getRawData(row)) + { + if (highlightSyncAction_) + drawInactiveColumBackground(*fsObj); + + if (!highlightSyncAction_) + drawBitmapRtlMirror(dc, getCmpResultImage(fsObj->getCategory()), rect, wxALIGN_CENTER, buffer); + else if (fsObj->getCategory() != FILE_EQUAL) //don't show = in both middle columns + drawBitmapRtlMirror(dc, greyScale(getCmpResultImage(fsObj->getCategory())), rect, wxALIGN_CENTER, buffer); + } + break; + + case COL_TYPE_SYNC_ACTION: + if (const FileSystemObject* fsObj = getRawData(row)) + { + if (!highlightSyncAction_) + drawInactiveColumBackground(*fsObj); + + const bool rowHighlighted = dragSelection ? row == dragSelection->first : highlight ? row == highlight->row_ : false; + const BlockPosition highlightBlock = dragSelection ? dragSelection->second : highlight ? highlight->blockPos_ : BLOCKPOS_CHECK_BOX; + + //synchronization preview + if (rowHighlighted && highlightBlock != BLOCKPOS_CHECK_BOX) + switch (highlightBlock) + { + case BLOCKPOS_CHECK_BOX: + break; + case BLOCKPOS_LEFT: + drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::LEFT)), rect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, buffer); + break; + case BLOCKPOS_MIDDLE: + drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::NONE)), rect, wxALIGN_CENTER, buffer); + break; + case BLOCKPOS_RIGHT: + drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::RIGHT)), rect, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, buffer); + break; + } + else //default + { + if (highlightSyncAction_) + drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->getSyncOperation()), rect, wxALIGN_CENTER, buffer); + else if (fsObj->getSyncOperation() != SO_EQUAL) //don't show = in both middle columns + drawBitmapRtlMirror(dc, greyScale(getSyncOpImage(fsObj->getSyncOperation())), rect, wxALIGN_CENTER, buffer); + } + } + break; + } + } + + virtual wxString getColumnLabel(ColumnType colType) const override + { + switch (static_cast(colType)) + { + case COL_TYPE_CHECKBOX: + break; + case COL_TYPE_CMP_CATEGORY: + return _("Category") + L" (F9)"; + case COL_TYPE_SYNC_ACTION: + return _("Action") + L" (F9)"; + } + return wxEmptyString; + } + + virtual wxString getToolTip(ColumnType colType) const override { return getColumnLabel(colType); } + + virtual void renderColumnLabel(Grid& tree, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted) override + { + switch (static_cast(colType)) + { + case COL_TYPE_CHECKBOX: + drawColumnLabelBackground(dc, rect, false); + break; + + case COL_TYPE_CMP_CATEGORY: + { + wxRect rectInside = drawColumnLabelBorder(dc, rect); + drawColumnLabelBackground(dc, rectInside, highlighted); + + const wxBitmap& cmpIcon = getResourceImage(L"compare_small"); + drawBitmapRtlNoMirror(dc, highlightSyncAction_ ? greyScale(cmpIcon) : cmpIcon, rectInside, wxALIGN_CENTER, buffer); + } + break; + + case COL_TYPE_SYNC_ACTION: + { + wxRect rectInside = drawColumnLabelBorder(dc, rect); + drawColumnLabelBackground(dc, rectInside, highlighted); + + const wxBitmap& syncIcon = getResourceImage(L"sync_small"); + drawBitmapRtlNoMirror(dc, highlightSyncAction_ ? syncIcon : greyScale(syncIcon), rectInside, wxALIGN_CENTER, buffer); + } + break; + } + } + + static wxColor getBackGroundColorSyncAction(const FileSystemObject* fsObj) + { + if (fsObj) + { + if (!fsObj->isActive()) + return COLOR_NOT_ACTIVE; + + switch (fsObj->getSyncOperation()) //evaluate comparison result and sync direction + { + case SO_DO_NOTHING: + return COLOR_NOT_ACTIVE; + case SO_EQUAL: + break; //usually white + + case SO_CREATE_NEW_LEFT: + case SO_OVERWRITE_LEFT: + case SO_DELETE_LEFT: + case SO_MOVE_LEFT_SOURCE: + case SO_MOVE_LEFT_TARGET: + case SO_COPY_METADATA_TO_LEFT: + return COLOR_SYNC_BLUE; + + case SO_CREATE_NEW_RIGHT: + case SO_OVERWRITE_RIGHT: + case SO_DELETE_RIGHT: + case SO_MOVE_RIGHT_SOURCE: + case SO_MOVE_RIGHT_TARGET: + case SO_COPY_METADATA_TO_RIGHT: + return COLOR_SYNC_GREEN; + + case SO_UNRESOLVED_CONFLICT: + return COLOR_YELLOW; + } + } + return wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + } + + static wxColor getBackGroundColorCmpCategory(const FileSystemObject* fsObj) + { + if (fsObj) + { + if (!fsObj->isActive()) + return COLOR_NOT_ACTIVE; + + switch (fsObj->getCategory()) + { + case FILE_LEFT_SIDE_ONLY: + case FILE_LEFT_NEWER: + return COLOR_SYNC_BLUE; //COLOR_CMP_BLUE; + + case FILE_RIGHT_SIDE_ONLY: + case FILE_RIGHT_NEWER: + return COLOR_SYNC_GREEN; //COLOR_CMP_GREEN; + + case FILE_DIFFERENT: + return COLOR_CMP_RED; + case FILE_EQUAL: + break; //usually white + case FILE_CONFLICT: + case FILE_DIFFERENT_METADATA: //= sub-category of equal, but hint via background that sync direction follows conflict-setting + return COLOR_YELLOW; + //return COLOR_YELLOW_LIGHT; + } + } + return wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + } + + enum BlockPosition //each cell can be divided into four blocks concerning mouse selections + { + BLOCKPOS_CHECK_BOX, + BLOCKPOS_LEFT, + BLOCKPOS_MIDDLE, + BLOCKPOS_RIGHT + }; + + //determine block position within cell + BlockPosition mousePosToBlock(const wxPoint& clientPos, size_t row, ColumnTypeMiddle colType) const + { + switch (static_cast(colType)) + { + case COL_TYPE_CHECKBOX: + case COL_TYPE_CMP_CATEGORY: + break; + + case COL_TYPE_SYNC_ACTION: + { + const int absX = refGrid().CalcUnscrolledPosition(clientPos).x; + + const wxRect rect = refGrid().getCellArea(row, static_cast(COL_TYPE_SYNC_ACTION)); //returns empty rect if column not found; absolute coordinates! + if (rect.width > 0 && rect.height > 0) + if (const FileSystemObject* const fsObj = getRawData(row)) + if (fsObj->getSyncOperation() != SO_EQUAL) //in sync-preview equal files shall be treated like a checkbox + // cell: + // ----------------------- + // | left | middle | right| + // ----------------------- + if (rect.GetX() <= absX) + { + if (absX < rect.GetX() + rect.GetWidth() / 3) + return BLOCKPOS_LEFT; + else if (absX < rect.GetX() + 2 * rect.GetWidth() / 3) + return BLOCKPOS_MIDDLE; + else if (absX < rect.GetX() + rect.GetWidth()) + return BLOCKPOS_RIGHT; + } + } + break; + } + return BLOCKPOS_CHECK_BOX; + } + + void showToolTip(size_t row, ColumnTypeMiddle colType, wxPoint posScreen) + { + if (const FileSystemObject* fsObj = getRawData(row)) + { + bool showTooltipSyncAction = true; + switch (colType) + { + case COL_TYPE_CHECKBOX: + showTooltipSyncAction = highlightSyncAction_; + break; + case COL_TYPE_CMP_CATEGORY: + showTooltipSyncAction = false; + break; + case COL_TYPE_SYNC_ACTION: + break; + } + + if (showTooltipSyncAction) //synchronization preview + { + const wchar_t* imageName = [&]() -> const wchar_t* + { + const SyncOperation syncOp = fsObj->getSyncOperation(); + switch (syncOp) + { + case SO_CREATE_NEW_LEFT: + return L"so_create_left"; + case SO_CREATE_NEW_RIGHT: + return L"so_create_right"; + case SO_DELETE_LEFT: + return L"so_delete_left"; + case SO_DELETE_RIGHT: + return L"so_delete_right"; + case SO_MOVE_LEFT_SOURCE: + return L"so_move_left_source"; + case SO_MOVE_LEFT_TARGET: + return L"so_move_left_target"; + case SO_MOVE_RIGHT_SOURCE: + return L"so_move_right_source"; + case SO_MOVE_RIGHT_TARGET: + return L"so_move_right_target"; + case SO_OVERWRITE_LEFT: + return L"so_update_left"; + case SO_OVERWRITE_RIGHT: + return L"so_update_right"; + case SO_COPY_METADATA_TO_LEFT: + return L"so_move_left"; + case SO_COPY_METADATA_TO_RIGHT: + return L"so_move_right"; + case SO_DO_NOTHING: + return L"so_none"; + case SO_EQUAL: + return L"cat_equal"; + case SO_UNRESOLVED_CONFLICT: + return L"cat_conflict"; + }; + assert(false); + return L""; + }(); + const auto& img = mirrorIfRtl(getResourceImage(imageName)); + toolTip.show(getSyncOpDescription(*fsObj), posScreen, &img); + } + else + { + const wchar_t* imageName = [&]() -> const wchar_t* + { + const CompareFilesResult cmpRes = fsObj->getCategory(); + switch (cmpRes) + { + case FILE_LEFT_SIDE_ONLY: + return L"cat_left_only"; + case FILE_RIGHT_SIDE_ONLY: + return L"cat_right_only"; + case FILE_LEFT_NEWER: + return L"cat_left_newer"; + case FILE_RIGHT_NEWER: + return L"cat_right_newer"; + case FILE_DIFFERENT: + return L"cat_different"; + case FILE_EQUAL: + case FILE_DIFFERENT_METADATA: //= sub-category of equal + return L"cat_equal"; + case FILE_CONFLICT: + return L"cat_conflict"; + } + assert(false); + return L""; + }(); + const auto& img = mirrorIfRtl(getResourceImage(imageName)); + toolTip.show(getCategoryDescription(*fsObj), posScreen, &img); + } + } + else + toolTip.hide(); //if invalid row... + } + + bool highlightSyncAction_; + + struct MouseHighlight + { + MouseHighlight(size_t row, BlockPosition blockPos) : row_(row), blockPos_(blockPos) {} + const size_t row_; + const BlockPosition blockPos_; + }; + std::unique_ptr highlight; //current mouse highlight + std::unique_ptr> dragSelection; //(row, block); area clicked when beginning selection + std::unique_ptr buffer; //avoid costs of recreating this temporal variable + Tooltip toolTip; + wxImage notch; +}; + +//######################################################################################################## + +const wxEventType EVENT_ALIGN_SCROLLBARS = wxNewEventType(); + +class GridEventManager : private wxEvtHandler +{ +public: + GridEventManager(Grid& gridL, + Grid& gridC, + Grid& gridR, + GridDataMiddle& provMiddle) : + gridL_(gridL), gridC_(gridC), gridR_(gridR), scrollMaster(nullptr), + provMiddle_(provMiddle), + scrollbarUpdatePending(false) + { + gridL_.Connect(EVENT_GRID_COL_RESIZE, GridColumnResizeEventHandler(GridEventManager::onResizeColumnL), nullptr, this); + gridR_.Connect(EVENT_GRID_COL_RESIZE, GridColumnResizeEventHandler(GridEventManager::onResizeColumnR), nullptr, this); + + gridL_.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler (GridEventManager::onKeyDownL), nullptr, this); + gridC_.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler (GridEventManager::onKeyDownC), nullptr, this); + gridR_.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler (GridEventManager::onKeyDownR), nullptr, this); + + gridC_.getMainWin().Connect(wxEVT_MOTION, wxMouseEventHandler(GridEventManager::onCenterMouseMovement), nullptr, this); + gridC_.getMainWin().Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(GridEventManager::onCenterMouseLeave ), nullptr, this); + + gridC_.Connect(EVENT_GRID_MOUSE_LEFT_DOWN, GridClickEventHandler (GridEventManager::onCenterSelectBegin), nullptr, this); + gridC_.Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(GridEventManager::onCenterSelectEnd ), nullptr, this); + + //clear selection of other grid when selecting on + gridL_.Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(GridEventManager::onGridSelectionL), nullptr, this); + gridR_.Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(GridEventManager::onGridSelectionR), nullptr, this); + + //parallel grid scrolling: do NOT use DoPrepareDC() to align grids! GDI resource leak! Use regular paint event instead: + gridL_.getMainWin().Connect(wxEVT_PAINT, wxEventHandler(GridEventManager::onPaintGridL), nullptr, this); + gridC_.getMainWin().Connect(wxEVT_PAINT, wxEventHandler(GridEventManager::onPaintGridC), nullptr, this); + gridR_.getMainWin().Connect(wxEVT_PAINT, wxEventHandler(GridEventManager::onPaintGridR), nullptr, this); + + auto connectGridAccess = [&](Grid& grid, wxObjectEventFunction func) + { + grid.Connect(wxEVT_SCROLLWIN_TOP, func, nullptr, this); + grid.Connect(wxEVT_SCROLLWIN_BOTTOM, func, nullptr, this); + grid.Connect(wxEVT_SCROLLWIN_LINEUP, func, nullptr, this); + grid.Connect(wxEVT_SCROLLWIN_LINEDOWN, func, nullptr, this); + grid.Connect(wxEVT_SCROLLWIN_PAGEUP, func, nullptr, this); + grid.Connect(wxEVT_SCROLLWIN_PAGEDOWN, func, nullptr, this); + grid.Connect(wxEVT_SCROLLWIN_THUMBTRACK, func, nullptr, this); + //wxEVT_KILL_FOCUS -> there's no need to reset "scrollMaster" + //wxEVT_SET_FOCUS -> not good enough: + //e.g.: left grid has input, right grid is "scrollMaster" due to dragging scroll thumb via mouse. + //=> Next keyboard input on left does *not* emit focus change event, but still "scrollMaster" needs to change + //=> hook keyboard input instead of focus event: + grid.getMainWin().Connect(wxEVT_CHAR, func, nullptr, this); + grid.getMainWin().Connect(wxEVT_KEY_UP, func, nullptr, this); + grid.getMainWin().Connect(wxEVT_KEY_DOWN, func, nullptr, this); + }; + connectGridAccess(gridL_, wxEventHandler(GridEventManager::onGridAccessL)); // + connectGridAccess(gridC_, wxEventHandler(GridEventManager::onGridAccessC)); //connect *after* onKeyDown() in order to receive callback *before*!!! + connectGridAccess(gridR_, wxEventHandler(GridEventManager::onGridAccessR)); // + + Connect(EVENT_ALIGN_SCROLLBARS, wxEventHandler(GridEventManager::onAlignScrollBars), NULL, this); + } + + ~GridEventManager() { assert(!scrollbarUpdatePending); } + + void setScrollMaster(const Grid& grid) { scrollMaster = &grid; } + +private: + void onCenterSelectBegin(GridClickEvent& event) + { + + provMiddle_.onSelectBegin(event.GetPosition(), event.row_, event.colType_); + event.Skip(); + } + + void onCenterSelectEnd(GridRangeSelectEvent& event) + { + if (event.positive_) + provMiddle_.onSelectEnd(event.rowFirst_, event.rowLast_); + event.Skip(); + } + + void onCenterMouseMovement(wxMouseEvent& event) + { + provMiddle_.onMouseMovement(event.GetPosition()); + event.Skip(); + } + + void onCenterMouseLeave(wxMouseEvent& event) + { + provMiddle_.onMouseLeave(); + event.Skip(); + } + + void onGridSelectionL(GridRangeSelectEvent& event) { onGridSelection(gridL_, gridR_); event.Skip(); } + void onGridSelectionR(GridRangeSelectEvent& event) { onGridSelection(gridR_, gridL_); event.Skip(); } + + void onGridSelection(const Grid& grid, Grid& other) + { + if (!wxGetKeyState(WXK_CONTROL)) //clear other grid unless user is holding CTRL + other.clearSelection(DENY_GRID_EVENT); //don't emit event, prevent recursion! + } + + void onKeyDownL(wxKeyEvent& event) { onKeyDown(event, gridL_); } + void onKeyDownC(wxKeyEvent& event) { onKeyDown(event, gridC_); } + void onKeyDownR(wxKeyEvent& event) { onKeyDown(event, gridR_); } + + void onKeyDown(wxKeyEvent& event, const Grid& grid) + { + int keyCode = event.GetKeyCode(); + if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft) + { + if (keyCode == WXK_LEFT) + keyCode = WXK_RIGHT; + else if (keyCode == WXK_RIGHT) + keyCode = WXK_LEFT; + else if (keyCode == WXK_NUMPAD_LEFT) + keyCode = WXK_NUMPAD_RIGHT; + else if (keyCode == WXK_NUMPAD_RIGHT) + keyCode = WXK_NUMPAD_LEFT; + } + + //skip middle component when navigating via keyboard + const size_t row = grid.getGridCursor(); + + if (event.ShiftDown()) + ; + else if (event.ControlDown()) + ; + else + switch (keyCode) + { + case WXK_LEFT: + case WXK_NUMPAD_LEFT: + gridL_.setGridCursor(row); + gridL_.SetFocus(); + //since key event is likely originating from right grid, we need to set scrollMaster manually! + scrollMaster = &gridL_; //onKeyDown is called *after* onGridAccessL()! + return; //swallow event + + case WXK_RIGHT: + case WXK_NUMPAD_RIGHT: + gridR_.setGridCursor(row); + gridR_.SetFocus(); + scrollMaster = &gridR_; + return; //swallow event + } + + event.Skip(); + } + + void onResizeColumnL(GridColumnResizeEvent& event) { resizeOtherSide(gridL_, gridR_, event.colType_, event.offset_); } + void onResizeColumnR(GridColumnResizeEvent& event) { resizeOtherSide(gridR_, gridL_, event.colType_, event.offset_); } + + void resizeOtherSide(const Grid& src, Grid& trg, ColumnType type, int offset) + { + //find stretch factor of resized column: type is unique due to makeConsistent()! + std::vector cfgSrc = src.getColumnConfig(); + auto it = std::find_if(cfgSrc.begin(), cfgSrc.end(), [&](Grid::ColumnAttribute& ca) { return ca.type_ == type; }); + if (it == cfgSrc.end()) + return; + const int stretchSrc = it->stretch_; + + //we do not propagate resizings on stretched columns to the other side: awkward user experience + if (stretchSrc > 0) + return; + + //apply resized offset to other side, but only if stretch factors match! + std::vector cfgTrg = trg.getColumnConfig(); + std::for_each(cfgTrg.begin(), cfgTrg.end(), [&](Grid::ColumnAttribute& ca) + { + if (ca.type_ == type && ca.stretch_ == stretchSrc) + ca.offset_ = offset; + }); + trg.setColumnConfig(cfgTrg); + } + + void onGridAccessL(wxEvent& event) { scrollMaster = &gridL_; event.Skip(); } + void onGridAccessC(wxEvent& event) { scrollMaster = &gridC_; event.Skip(); } + void onGridAccessR(wxEvent& event) { scrollMaster = &gridR_; event.Skip(); } + + void onPaintGridL(wxEvent& event) { onPaintGrid(gridL_); event.Skip(); } + void onPaintGridC(wxEvent& event) { onPaintGrid(gridC_); event.Skip(); } + void onPaintGridR(wxEvent& event) { onPaintGrid(gridR_); event.Skip(); } + + void onPaintGrid(const Grid& grid) + { + //align scroll positions of all three grids *synchronously* during paint event! (wxGTK has visible delay when this is done asynchronously, no delay on Windows) + + //determine lead grid + const Grid* lead = nullptr; + Grid* follow1 = nullptr; + Grid* follow2 = nullptr; + auto setGrids = [&](const Grid& l, Grid& f1, Grid& f2) { lead = &l; follow1 = &f1; follow2 = &f2; }; + + if (&gridC_ == scrollMaster) + setGrids(gridC_, gridL_, gridR_); + else if (&gridR_ == scrollMaster) + setGrids(gridR_, gridL_, gridC_); + else //default: left panel + setGrids(gridL_, gridC_, gridR_); + + //align other grids only while repainting the lead grid to avoid scrolling and updating a grid at the same time! + if (lead != &grid) return; + + auto scroll = [](Grid& target, int y) //support polling + { + //scroll vertically only - scrolling horizontally becomes annoying if left and right sides have different widths; + //e.g. h-scroll on left would be undone when scrolling vertically on right which doesn't have a h-scrollbar + int yOld = 0; + target.GetViewStart(nullptr, &yOld); + if (yOld != y) + target.Scroll(-1, y); //empirical test Windows/Ubuntu: this call does NOT trigger a wxEVT_SCROLLWIN event, which would incorrectly set "scrollMaster" to "&target"! + }; + int y = 0; + lead->GetViewStart(nullptr, &y); + scroll(*follow1, y); + scroll(*follow2, y); + + //harmonize placement of horizontal scrollbar to avoid grids getting out of sync! + //since this affects the grid that is currently repainted as well, we do work asynchronously! + //avoids at least this problem: remaining graphics artifact when changing from Grid::SB_SHOW_ALWAYS to Grid::SB_SHOW_NEVER at location of old scrollbar (Windows only) + + //perf note: send one async event at most, else they may accumulate and create perf issues, see grid.cpp + if (!scrollbarUpdatePending) + { + scrollbarUpdatePending = true; + wxCommandEvent alignEvent(EVENT_ALIGN_SCROLLBARS); + AddPendingEvent(alignEvent); //waits until next idle event - may take up to a second if the app is busy on wxGTK! + } + } + + void onAlignScrollBars(wxEvent& event) + { + ZEN_ON_SCOPE_EXIT(scrollbarUpdatePending = false); + assert(scrollbarUpdatePending); + + auto needsHorizontalScrollbars = [](const Grid& grid) -> bool + { + const wxWindow& mainWin = grid.getMainWin(); + return mainWin.GetVirtualSize().GetWidth() > mainWin.GetClientSize().GetWidth(); + //assuming Grid::updateWindowSizes() does its job well, this should suffice! + //CAVEAT: if horizontal and vertical scrollbar are circular dependent from each other + //(h-scrollbar is shown due to v-scrollbar consuming horizontal width, ect...) + //while in fact both are NOT needed, this special case results in a bogus need for scrollbars! + //see https://sourceforge.net/tracker/?func=detail&aid=3514183&group_id=234430&atid=1093083 + // => since we're outside the Grid abstraction, we should not duplicate code to handle this special case as it seems to be insignificant + }; + + Grid::ScrollBarStatus sbStatusX = needsHorizontalScrollbars(gridL_) || + needsHorizontalScrollbars(gridR_) ? + Grid::SB_SHOW_ALWAYS : Grid::SB_SHOW_NEVER; + gridL_.showScrollBars(sbStatusX, Grid::SB_SHOW_NEVER); + gridC_.showScrollBars(sbStatusX, Grid::SB_SHOW_NEVER); + gridR_.showScrollBars(sbStatusX, Grid::SB_SHOW_AUTOMATIC); + } + + Grid& gridL_; + Grid& gridC_; + Grid& gridR_; + + const Grid* scrollMaster; //for address check only; this needn't be the grid having focus! + //e.g. mouse wheel events should set window under cursor as scrollMaster, but *not* change focus + + GridDataMiddle& provMiddle_; + bool scrollbarUpdatePending; +}; +} + +//######################################################################################################## + +void gridview::init(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, const std::shared_ptr& gridDataView) +{ + auto provLeft_ = std::make_shared(gridDataView, gridLeft); + auto provMiddle_ = std::make_shared(gridDataView, gridCenter); + auto provRight_ = std::make_shared(gridDataView, gridRight); + + gridLeft .setDataProvider(provLeft_); //data providers reference grid => + gridCenter.setDataProvider(provMiddle_); //ownership must belong *exclusively* to grid! + gridRight .setDataProvider(provRight_); + + auto evtMgr = std::make_shared(gridLeft, gridCenter, gridRight, *provMiddle_); + provLeft_ ->holdOwnership(evtMgr); + provMiddle_->holdOwnership(evtMgr); + provRight_ ->holdOwnership(evtMgr); + + gridCenter.enableColumnMove (false); + gridCenter.enableColumnResize(false); + + gridCenter.showRowLabel(false); + gridRight .showRowLabel(false); + + //gridLeft .showScrollBars(Grid::SB_SHOW_AUTOMATIC, Grid::SB_SHOW_NEVER); -> redundant: configuration happens in GridEventManager::onAlignScrollBars() + //gridCenter.showScrollBars(Grid::SB_SHOW_NEVER, Grid::SB_SHOW_NEVER); + + const int widthCategory = 30; + const int widthCheckbox = getResourceImage(L"checkboxTrue").GetWidth() + 4 + getResourceImage(L"notch").GetWidth(); + const int widthAction = 45; + gridCenter.SetSize(widthCategory + widthCheckbox + widthAction, -1); + + std::vector attribMiddle; + attribMiddle.push_back(Grid::ColumnAttribute(static_cast(COL_TYPE_CMP_CATEGORY), widthCategory, 0, true)); + attribMiddle.push_back(Grid::ColumnAttribute(static_cast(COL_TYPE_CHECKBOX ), widthCheckbox, 0, true)); + attribMiddle.push_back(Grid::ColumnAttribute(static_cast(COL_TYPE_SYNC_ACTION ), widthAction, 0, true)); + gridCenter.setColumnConfig(attribMiddle); +} + + +namespace +{ +std::vector makeConsistent(const std::vector& attribs) +{ + std::set usedTypes; + + std::vector output; + //remove duplicates: required by GridEventManager::resizeOtherSide() to find corresponding column on other side + std::copy_if(attribs.begin(), attribs.end(), std::back_inserter(output), + [&](const ColumnAttributeRim& a) { return usedTypes.insert(a.type_).second; }); + + //make sure each type is existing! -> should *only* be a problem if user manually messes with globalsettings.xml + const auto& defAttr = getDefaultColumnAttributesLeft(); + std::copy_if(defAttr.begin(), defAttr.end(), std::back_inserter(output), + [&](const ColumnAttributeRim& a) { return usedTypes.insert(a.type_).second; }); + + return output; +} +} + +std::vector gridview::convertConfig(const std::vector& attribs) +{ + const auto& attribClean = makeConsistent(attribs); + + std::vector output; + std::transform(attribClean.begin(), attribClean.end(), std::back_inserter(output), + [&](const ColumnAttributeRim& ca) { return Grid::ColumnAttribute(static_cast(ca.type_), ca.offset_, ca.stretch_, ca.visible_); }); + + return output; +} + + +std::vector gridview::convertConfig(const std::vector& attribs) +{ + std::vector output; + + std::transform(attribs.begin(), attribs.end(), std::back_inserter(output), + [&](const Grid::ColumnAttribute& ca) { return ColumnAttributeRim(static_cast(ca.type_), ca.offset_, ca.stretch_, ca.visible_); }); + + return makeConsistent(output); +} + + +namespace +{ +class IconUpdater : private wxEvtHandler //update file icons periodically: use SINGLE instance to coordinate left and right grids in parallel +{ +public: + IconUpdater(GridDataLeft& provLeft, GridDataRight& provRight, IconBuffer& iconBuffer) : provLeft_(provLeft), provRight_(provRight), iconBuffer_(iconBuffer) + { + timer.Connect(wxEVT_TIMER, wxEventHandler(IconUpdater::loadIconsAsynchronously), nullptr, this); + } + + void start() { if (!timer.IsRunning()) timer.Start(100); } //timer interval in [ms] + //don't check too often! give worker thread some time to fetch data + +private: + void stop() { if (timer.IsRunning()) timer.Stop(); } + + void loadIconsAsynchronously(wxEvent& event) //loads all (not yet) drawn icons + { + std::vector> prefetchLoad; + provLeft_ .getUnbufferedIconsForPreload(prefetchLoad); + provRight_.getUnbufferedIconsForPreload(prefetchLoad); + + //make sure least-important prefetch rows are inserted first into workload (=> processed last) + typedef std::pair Pft; //priority index nicely considers both grids at the same time! + std::sort(prefetchLoad.begin(), prefetchLoad.end(), [](const Pft& lhs, const Pft& rhs) { return lhs.first < rhs.first; }); + + //last inserted items are processed first in icon buffer: + std::vector newLoad; + for (const auto& item : prefetchLoad) + newLoad.push_back(item.second); + + provRight_.updateNewAndGetUnbufferedIcons(newLoad); + provLeft_ .updateNewAndGetUnbufferedIcons(newLoad); + + iconBuffer_.setWorkload(newLoad); + + if (newLoad.empty()) //let's only pay for iconupdater when needed + stop(); + } + + GridDataLeft& provLeft_; + GridDataRight& provRight_; + IconBuffer& iconBuffer_; + wxTimer timer; +}; + + +//resolve circular linker dependencies +inline +void IconManager::startIconUpdater() { if (iconUpdater) iconUpdater->start(); } +} + + +void gridview::setupIcons(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, bool show, IconBuffer::IconSize sz) +{ + auto* provLeft = dynamic_cast(gridLeft .getDataProvider()); + auto* provRight = dynamic_cast(gridRight.getDataProvider()); + + if (provLeft && provRight) + { + int iconHeight = 0; + if (show) + { + auto iconMgr = std::make_shared(*provLeft, *provRight, sz); + provLeft ->setIconManager(iconMgr); + provRight->setIconManager(iconMgr); + iconHeight = iconMgr->refIconBuffer().getSize(); + } + else + { + provLeft ->setIconManager(nullptr); + provRight->setIconManager(nullptr); + iconHeight = IconBuffer::getSize(IconBuffer::SIZE_SMALL); + } + + const int newRowHeight = std::max(iconHeight, gridLeft.getMainWin().GetCharHeight()) + 1; //add some space + + gridLeft .setRowHeight(newRowHeight); + gridCenter.setRowHeight(newRowHeight); + gridRight .setRowHeight(newRowHeight); + } + else + assert(false); +} + + +void gridview::refresh(Grid& gridLeft, Grid& gridCenter, Grid& gridRight) +{ + gridLeft .Refresh(); + gridCenter.Refresh(); + gridRight .Refresh(); +} + + +void gridview::setScrollMaster(Grid& grid) +{ + if (auto prov = dynamic_cast(grid.getDataProvider())) + if (auto evtMgr = prov->getEventManager()) + { + evtMgr->setScrollMaster(grid); + return; + } + assert(false); +} + + +void gridview::setNavigationMarker(Grid& gridLeft, + hash_set&& markedFilesAndLinks, + hash_set&& markedContainer) +{ + if (auto provLeft = dynamic_cast(gridLeft.getDataProvider())) + provLeft->setNavigationMarker(std::move(markedFilesAndLinks), std::move(markedContainer)); + else + assert(false); + gridLeft.Refresh(); +} + + +void gridview::highlightSyncAction(Grid& gridCenter, bool value) +{ + if (auto provMiddle = dynamic_cast(gridCenter.getDataProvider())) + provMiddle->highlightSyncAction(value); + else + assert(false); +} + +wxBitmap zen::getSyncOpImage(SyncOperation syncOp) +{ + switch (syncOp) //evaluate comparison result and sync direction + { + case SO_CREATE_NEW_LEFT: + return getResourceImage(L"so_create_left_small"); + case SO_CREATE_NEW_RIGHT: + return getResourceImage(L"so_create_right_small"); + case SO_DELETE_LEFT: + return getResourceImage(L"so_delete_left_small"); + case SO_DELETE_RIGHT: + return getResourceImage(L"so_delete_right_small"); + case SO_MOVE_LEFT_SOURCE: + return getResourceImage(L"so_move_left_source_small"); + case SO_MOVE_LEFT_TARGET: + return getResourceImage(L"so_move_left_target_small"); + case SO_MOVE_RIGHT_SOURCE: + return getResourceImage(L"so_move_right_source_small"); + case SO_MOVE_RIGHT_TARGET: + return getResourceImage(L"so_move_right_target_small"); + case SO_OVERWRITE_LEFT: + return getResourceImage(L"so_update_left_small"); + case SO_OVERWRITE_RIGHT: + return getResourceImage(L"so_update_right_small"); + case SO_COPY_METADATA_TO_LEFT: + return getResourceImage(L"so_move_left_small"); + case SO_COPY_METADATA_TO_RIGHT: + return getResourceImage(L"so_move_right_small"); + case SO_DO_NOTHING: + return getResourceImage(L"so_none_small"); + case SO_EQUAL: + return getResourceImage(L"cat_equal_small"); + case SO_UNRESOLVED_CONFLICT: + return getResourceImage(L"cat_conflict_small"); + } + return wxNullBitmap; +} + + +wxBitmap zen::getCmpResultImage(CompareFilesResult cmpResult) +{ + switch (cmpResult) + { + case FILE_LEFT_SIDE_ONLY: + return getResourceImage(L"cat_left_only_small"); + case FILE_RIGHT_SIDE_ONLY: + return getResourceImage(L"cat_right_only_small"); + case FILE_LEFT_NEWER: + return getResourceImage(L"cat_left_newer_small"); + case FILE_RIGHT_NEWER: + return getResourceImage(L"cat_right_newer_small"); + case FILE_DIFFERENT: + return getResourceImage(L"cat_different_small"); + case FILE_EQUAL: + case FILE_DIFFERENT_METADATA: //= sub-category of equal + return getResourceImage(L"cat_equal_small"); + case FILE_CONFLICT: + return getResourceImage(L"cat_conflict_small"); + } + return wxNullBitmap; +} diff --git a/FreeFileSync/Source/ui/custom_grid.h b/FreeFileSync/Source/ui/custom_grid.h new file mode 100644 index 00000000..9d7c8856 --- /dev/null +++ b/FreeFileSync/Source/ui/custom_grid.h @@ -0,0 +1,81 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef CUSTOMGRID_H_INCLUDED +#define CUSTOMGRID_H_INCLUDED + +#include +#include "grid_view.h" +#include "column_attr.h" +#include "../lib/icon_buffer.h" + +namespace zen +{ +//setup grid to show grid view within three components: +namespace gridview +{ +void init(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, const std::shared_ptr& gridDataView); + +std::vector convertConfig(const std::vector& attribs); //+ make consistent +std::vector convertConfig(const std::vector& attribs); // + +void highlightSyncAction(Grid& gridCenter, bool value); + +void setupIcons(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, bool show, IconBuffer::IconSize sz); + +void refresh(Grid& gridLeft, Grid& gridCenter, Grid& gridRight); + +void setScrollMaster(Grid& grid); + +//mark rows selected in navigation/compressed tree and navigate to leading object +void setNavigationMarker(Grid& gridLeft, + hash_set&& markedFilesAndLinks,//mark files/symlinks directly within a container + hash_set&& markedContainer); //mark full container including child-objects +} + +wxBitmap getSyncOpImage(SyncOperation syncOp); +wxBitmap getCmpResultImage(CompareFilesResult cmpResult); + + +//---------- custom events for middle grid ---------- + +//(UN-)CHECKING ROWS FROM SYNCHRONIZATION +extern const wxEventType EVENT_GRID_CHECK_ROWS; +//SELECTING SYNC DIRECTION +extern const wxEventType EVENT_GRID_SYNC_DIRECTION; + +struct CheckRowsEvent : public wxCommandEvent +{ + CheckRowsEvent(size_t rowFirst, size_t rowLast, bool setIncluded) : wxCommandEvent(EVENT_GRID_CHECK_ROWS), rowFirst_(rowFirst), rowLast_(rowLast), setIncluded_(setIncluded) { assert(rowFirst <= rowLast); } + virtual wxEvent* Clone() const { return new CheckRowsEvent(*this); } + + const size_t rowFirst_; //selected range: [rowFirst_, rowLast_) + const size_t rowLast_; //range is empty when clearing selection + const bool setIncluded_; +}; + + +struct SyncDirectionEvent : public wxCommandEvent +{ + SyncDirectionEvent(size_t rowFirst, size_t rowLast, SyncDirection direction) : wxCommandEvent(EVENT_GRID_SYNC_DIRECTION), rowFirst_(rowFirst), rowLast_(rowLast), direction_(direction) { assert(rowFirst <= rowLast); } + virtual wxEvent* Clone() const { return new SyncDirectionEvent(*this); } + + const size_t rowFirst_; //see CheckRowsEvent + const size_t rowLast_; // + const SyncDirection direction_; +}; + +typedef void (wxEvtHandler::*CheckRowsEventFunction)(CheckRowsEvent&); +typedef void (wxEvtHandler::*SyncDirectionEventFunction)(SyncDirectionEvent&); + +#define CheckRowsEventHandler(func) \ + (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(CheckRowsEventFunction, &func) + +#define SyncDirectionEventHandler(func) \ + (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(SyncDirectionEventFunction, &func) +} + +#endif // CUSTOMGRID_H_INCLUDED diff --git a/FreeFileSync/Source/ui/dir_name.cpp b/FreeFileSync/Source/ui/dir_name.cpp new file mode 100644 index 00000000..ee0ee64a --- /dev/null +++ b/FreeFileSync/Source/ui/dir_name.cpp @@ -0,0 +1,274 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "dir_name.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../lib/resolve_path.h" +#include "folder_history_box.h" + +#ifdef ZEN_WIN +#include +#include +#include "../dll/IFileDialog_Vista\ifile_dialog.h" +#endif + +using namespace zen; + + +const wxEventType zen::EVENT_ON_DIR_SELECTED = wxNewEventType(); +const wxEventType zen::EVENT_ON_DIR_MANUAL_CORRECTION = wxNewEventType(); + +namespace +{ +void setDirectoryNameImpl(const wxString& dirname, wxWindow& tooltipWnd, wxStaticText* staticText) +{ + const wxString dirFormatted = utfCvrtTo(getFormattedDirectoryName(toZ(dirname))); //may block when resolving [] + + tooltipWnd.SetToolTip(nullptr); //workaround wxComboBox bug http://trac.wxwidgets.org/ticket/10512 / http://trac.wxwidgets.org/ticket/12659 + tooltipWnd.SetToolTip(dirFormatted); //who knows when the real bugfix reaches mere mortals via an official release... + + if (staticText) + { + //change static box label only if there is a real difference to what is shown in wxTextCtrl anyway + wxString dirNormalized = dirname; + trim(dirNormalized); + if (!dirNormalized.empty()) + if (!endsWith(dirNormalized, FILE_NAME_SEPARATOR)) + dirNormalized += FILE_NAME_SEPARATOR; + + staticText->SetLabel(dirNormalized == dirFormatted ? wxString(_("Drag && drop")) : dirFormatted); + } +} + + +void setDirectoryName(const wxString& dirname, + wxTextCtrl* txtCtrl, + wxWindow& tooltipWnd, + wxStaticText* staticText) //pointers are optional +{ + if (txtCtrl) + txtCtrl->ChangeValue(dirname); + setDirectoryNameImpl(dirname, tooltipWnd, staticText); +} + + +void setDirectoryName(const wxString& dirname, + FolderHistoryBox* comboBox, + wxWindow& tooltipWnd, + wxStaticText* staticText) //pointers are optional +{ + if (comboBox) + comboBox->setValue(dirname); + setDirectoryNameImpl(dirname, tooltipWnd, staticText); +} +} +//############################################################################################################## + +template +DirectoryName::DirectoryName(wxWindow& dropWindow, + wxButton& selectButton, + NameControl& dirName, + wxStaticText* staticText, + wxWindow* dropWindow2) : + dropWindow_(dropWindow), + dropWindow2_(dropWindow2), + selectButton_(selectButton), + dirName_(dirName), + staticText_(staticText) +{ + //prepare drag & drop + setupFileDrop(dropWindow_); + dropWindow_.Connect(EVENT_DROP_FILE, FileDropEventHandler(DirectoryName::onFilesDropped), nullptr, this); + + if (dropWindow2_) + { + setupFileDrop(*dropWindow2_); + dropWindow2_->Connect(EVENT_DROP_FILE, FileDropEventHandler(DirectoryName::onFilesDropped), nullptr, this); + } + + //keep dirPicker and dirName synchronous + dirName_ .Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler (DirectoryName::onMouseWheel), nullptr, this); + dirName_ .Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(DirectoryName::onWriteDirManually), nullptr, this); + selectButton_.Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DirectoryName::onSelectDir ), nullptr, this); +} + + +template +DirectoryName::~DirectoryName() +{ + dropWindow_.Disconnect(EVENT_DROP_FILE, FileDropEventHandler(DirectoryName::onFilesDropped), nullptr, this); + if (dropWindow2_) + dropWindow2_->Disconnect(EVENT_DROP_FILE, FileDropEventHandler(DirectoryName::onFilesDropped), nullptr, this); + + dirName_ .Disconnect(wxEVT_MOUSEWHEEL, wxMouseEventHandler (DirectoryName::onMouseWheel), nullptr, this); + dirName_ .Disconnect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(DirectoryName::onWriteDirManually), nullptr, this); + selectButton_.Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DirectoryName::onSelectDir ), nullptr, this); +} + + +template +void DirectoryName::onMouseWheel(wxMouseEvent& event) +{ + //for combobox: although switching through available items is wxWidgets default, this is NOT windows default, e.g. explorer + //additionally this will delete manual entries, although all the users wanted is scroll the parent window! + + //redirect to parent scrolled window! + wxWindow* wnd = &dirName_; + while ((wnd = wnd->GetParent()) != nullptr) //silence MSVC warning + if (dynamic_cast(wnd) != nullptr) + if (wxEvtHandler* evtHandler = wnd->GetEventHandler()) + { + evtHandler->AddPendingEvent(event); + break; + } + + // event.Skip(); +} + + +template +void DirectoryName::onFilesDropped(FileDropEvent& event) +{ + const auto& files = event.getFiles(); + if (files.empty()) + return; + + if (acceptDrop(files, event.getDropPosition(), event.getDropWindow())) + { + const wxString fileName = event.getFiles()[0]; + if (dirExists(toZ(fileName))) + setDirectoryName(fileName, &dirName_, dirName_, staticText_); + else + { + wxString parentName = beforeLast(fileName, utfCvrtTo(FILE_NAME_SEPARATOR)); //returns empty string if ch not found +#ifdef ZEN_WIN + if (endsWith(parentName, L":")) //volume name + parentName += FILE_NAME_SEPARATOR; +#endif + if (dirExists(toZ(parentName))) + setDirectoryName(parentName, &dirName_, dirName_, staticText_); + else //set original name unconditionally: usecase: inactive mapped network shares + setDirectoryName(fileName, &dirName_, dirName_, staticText_); + } + + //notify action invoked by user + wxCommandEvent dummy(EVENT_ON_DIR_SELECTED); + ProcessEvent(dummy); + } + else + event.Skip(); //let other handlers try!!! +} + + +template +void DirectoryName::onWriteDirManually(wxCommandEvent& event) +{ + setDirectoryName(event.GetString(), static_cast(nullptr), dirName_, staticText_); + + wxCommandEvent dummy(EVENT_ON_DIR_MANUAL_CORRECTION); + ProcessEvent(dummy); + event.Skip(); +} + + +template +void DirectoryName::onSelectDir(wxCommandEvent& event) +{ + wxString defaultDirname; //default selection for dir picker + { + const Zstring dirFmt = getFormattedDirectoryName(toZ(getName())); + if (!dirFmt.empty()) + { + //convert to Zstring first: we don't want to pass wxString by value and risk MT issues! + auto ft = async([=] { return zen::dirExists(dirFmt); }); + + if (ft.timed_wait(boost::posix_time::milliseconds(200)) && ft.get()) //potentially slow network access: wait 200ms at most + defaultDirname = utfCvrtTo(dirFmt); + } + } + + //wxDirDialog internally uses lame-looking SHBrowseForFolder(); we better use IFileDialog() instead! (remembers size and position!) + std::unique_ptr newFolder; +#ifdef ZEN_WIN + if (vistaOrLater()) + { + using namespace ifile; + const DllFun showFolderPicker(getDllName(), funName_showFolderPicker); + const DllFun freeString (getDllName(), funName_freeString); + if (showFolderPicker && freeString) + { + wchar_t* selectedFolder = nullptr; + wchar_t* errorMsg = nullptr; + bool cancelled = false; + ZEN_ON_SCOPE_EXIT(freeString(selectedFolder)); + ZEN_ON_SCOPE_EXIT(freeString(errorMsg)); + + const GuidProxy guid = { '\x0', '\x4a', '\xf9', '\x31', '\xb4', '\x92', '\x40', '\xa0', + '\x8d', '\xc2', '\xc', '\xa5', '\xef', '\x59', '\x6e', '\x3b' + }; //some random GUID => have Windows save IFileDialog state separately from other file/dir pickers! + + showFolderPicker(static_cast(selectButton_.GetHWND()), //in; ==HWND + defaultDirname.empty() ? static_cast(nullptr) : defaultDirname.c_str(), //in, optional! + &guid, + selectedFolder, //out: call freeString() after use! + cancelled, //out + errorMsg); //out, optional: call freeString() after use! + if (errorMsg) + { + showNotificationDialog(&dropWindow_, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(errorMsg)); + return; + } + if (cancelled || !selectedFolder) + return; + newFolder = make_unique(selectedFolder); + } + } +#endif + if (!newFolder.get()) + { + wxDirDialog dirPicker(&selectButton_, _("Select a folder"), defaultDirname); //put modal wxWidgets dialogs on stack: creating on freestore leads to memleak! + if (dirPicker.ShowModal() != wxID_OK) + return; + newFolder = make_unique(dirPicker.GetPath()); + } + + setDirectoryName(*newFolder, &dirName_, dirName_, staticText_); + + //notify action invoked by user + wxCommandEvent dummy(EVENT_ON_DIR_SELECTED); + ProcessEvent(dummy); +} + + +template +wxString DirectoryName::getName() const +{ + return dirName_.GetValue(); +} + + +template +void DirectoryName::setName(const wxString& dirname) +{ + setDirectoryName(dirname, &dirName_, dirName_, staticText_); +} + + +//explicit template instantiations +namespace zen +{ +template class DirectoryName; +template class DirectoryName; +} diff --git a/FreeFileSync/Source/ui/dir_name.h b/FreeFileSync/Source/ui/dir_name.h new file mode 100644 index 00000000..21f2b574 --- /dev/null +++ b/FreeFileSync/Source/ui/dir_name.h @@ -0,0 +1,64 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef DRAGANDDROP_H_INCLUDED +#define DRAGANDDROP_H_INCLUDED + +#include +#include +#include +#include +#include +#include + +namespace zen +{ +//handle drag and drop, tooltip, label and manual input, coordinating a wxWindow, wxButton, and wxComboBox/wxTextCtrl +/* +Reasons NOT to use wxDirPickerCtrl, but wxButton instead: + - Crash on GTK 2: http://favapps.wordpress.com/2012/06/11/freefilesync-crash-in-linux-when-syncing-solved/ + - still uses outdated ::SHBrowseForFolder() (even on Windows 7) + - selection dialog remembers size, but NOT position => if user enlarges window, the next time he opens the dialog it may leap out of visible screen + - hard-codes "Browse" button label +*/ + +extern const wxEventType EVENT_ON_DIR_SELECTED; //directory is changed by the user (except manual type-in) +extern const wxEventType EVENT_ON_DIR_MANUAL_CORRECTION; //manual type-in +//example: wnd.Connect(EVENT_ON_DIR_SELECTED, wxCommandEventHandler(MyDlg::OnDirSelected), nullptr, this); + +template //NameControl may be wxTextCtrl, FolderHistoryBox +class DirectoryName: public wxEvtHandler +{ +public: + DirectoryName(wxWindow& dropWindow, + wxButton& selectButton, + NameControl& dirName, + wxStaticText* staticText = nullptr, //optional + wxWindow* dropWindow2 = nullptr); // + + ~DirectoryName(); + + wxString getName() const; + void setName(const wxString& dirname); + +private: + virtual bool acceptDrop(const std::vector& droppedFiles, const wxPoint& clientPos, const wxWindow& wnd) { return true; }; //return true if drop should be processed + + void onMouseWheel (wxMouseEvent& event); + void onFilesDropped (FileDropEvent& event); + void onWriteDirManually(wxCommandEvent& event); + void onSelectDir (wxCommandEvent& event); + + wxWindow& dropWindow_; + wxWindow* dropWindow2_; + wxButton& selectButton_; + NameControl& dirName_; + wxStaticText* staticText_; //optional +}; +} + + +#endif // DRAGANDDROP_H_INCLUDED diff --git a/FreeFileSync/Source/ui/exec_finished_box.cpp b/FreeFileSync/Source/ui/exec_finished_box.cpp new file mode 100644 index 00000000..cbbfa17d --- /dev/null +++ b/FreeFileSync/Source/ui/exec_finished_box.cpp @@ -0,0 +1,279 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "exec_finished_box.h" +#include +#include +#include +#include +#ifdef ZEN_WIN +#include +#endif + +using namespace zen; + + +namespace +{ +const std::wstring cmdTxtCloseProgressDlg = L"Close progress dialog"; //special command //mark for extraction: _("Close progress dialog") + +const std::wstring separationLine(L"---------------------------------------------------------------------------------------------------------------"); + +std::vector> getDefaultCommands() //(gui name/command) pairs +{ + std::vector> output; + + auto addEntry = [&](const std::wstring& name, const std::wstring& value) { output.push_back(std::make_pair(name, value)); }; + +#ifdef ZEN_WIN + if (zen::vistaOrLater()) + { + addEntry(_("Standby" ), L"rundll32.exe powrprof.dll,SetSuspendState Sleep"); //suspend/Suspend to RAM/sleep + addEntry(_("Log off" ), L"shutdown /l"); + addEntry(_("Shut down"), L"shutdown /s /t 60"); + //addEntry(_("Hibernate"), L"shutdown /h"); //Suspend to disk -> Standby is better anyway + } + else //XP + { + addEntry(_("Standby"), L"rundll32.exe powrprof.dll,SetSuspendState"); //this triggers standby OR hibernate, depending on whether hibernate setting is active! + addEntry(_("Log off" ), L"shutdown -l"); + addEntry(_("Shut down"), L"shutdown -s -t 60"); + //no suspend on XP? + } + +#elif defined ZEN_LINUX + addEntry(_("Standby" ), L"sudo pm-suspend"); + addEntry(_("Log off" ), L"gnome-session-quit"); //alternative requiring admin: sudo killall Xorg + addEntry(_("Shut down"), L"dbus-send --print-reply --dest=org.gnome.SessionManager /org/gnome/SessionManager org.gnome.SessionManager.RequestShutdown"); + //alternative requiring admin: sudo shutdown -h 1 + //addEntry(_("Hibernate"), L"sudo pm-hibernate"); + //alternative: "pmi action suspend" and "pmi action hibernate", require "sudo apt-get install powermanagement-interaface" + +#elif defined ZEN_MAC + addEntry(_("Standby" ), L"osascript -e \'tell application \"System Events\" to sleep\'"); + addEntry(_("Log off" ), L"osascript -e \'tell application \"System Events\" to log out\'"); + addEntry(_("Shut down"), L"osascript -e \'tell application \"System Events\" to shut down\'"); +#endif + return output; +} + +const wxEventType wxEVT_VALIDATE_USER_SELECTION = wxNewEventType(); +} + + +bool isCloseProgressDlgCommand(const std::wstring& value) +{ + std::wstring tmp = value; + trim(tmp); + return tmp == cmdTxtCloseProgressDlg; +} + + +ExecFinishedBox::ExecFinishedBox(wxWindow* parent, + wxWindowID id, + const wxString& value, + const wxPoint& pos, + const wxSize& size, + int n, + const wxString choices[], + long style, + const wxValidator& validator, + const wxString& name) : + wxComboBox(parent, id, value, pos, size, n, choices, style, validator, name), + history_(nullptr), + historyMax_(0), + defaultCommands(getDefaultCommands()) +{ + //##################################### + /*##*/ SetMinSize(wxSize(150, -1)); //## workaround yet another wxWidgets bug: default minimum size is much too large for a wxComboBox + //##################################### + + Connect(wxEVT_KEY_DOWN, wxKeyEventHandler (ExecFinishedBox::OnKeyEvent ), nullptr, this); + Connect(wxEVT_LEFT_DOWN, wxEventHandler (ExecFinishedBox::OnUpdateList), nullptr, this); + Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(ExecFinishedBox::OnSelection ), nullptr, this); + Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler (ExecFinishedBox::OnMouseWheel), nullptr, this); + + Connect(wxEVT_VALIDATE_USER_SELECTION, wxCommandEventHandler(ExecFinishedBox::OnValidateSelection), nullptr, this); +} + + +void ExecFinishedBox::addItemHistory() +{ + if (history_) + { + std::wstring command = getValue(); + trim(command); + + bool skipCmd = command == separationLine || //do not add sep. line + command == cmdTxtCloseProgressDlg || //do not add special command + command.empty(); + + //do not add built-in commands to history + if (!skipCmd) + { + for (auto it = defaultCommands.begin(); it != defaultCommands.end(); ++it) + if (command == it->first || + command == it->second) + { + skipCmd = true; + break; + } + } + + if (!skipCmd) + history_->insert(history_->begin(), command); + + if (history_->size() > historyMax_) + history_->resize(historyMax_); + } +} + + +std::wstring ExecFinishedBox::getValue() const +{ + const std::wstring value = zen::copyStringTo(GetValue()); + + { + std::wstring tmp = value; + trim(tmp); + if (tmp == implementation::translate(cmdTxtCloseProgressDlg)) //have this symbolic constant translated properly + return cmdTxtCloseProgressDlg; + } + + return value; +} + + +void ExecFinishedBox::setValue(const std::wstring& value) +{ + std::wstring tmp = value; + trim(tmp); + + if (tmp == cmdTxtCloseProgressDlg) + setValueAndUpdateList(implementation::translate(cmdTxtCloseProgressDlg)); //have this symbolic constant translated properly + else + setValueAndUpdateList(value); +} + +//set value and update list are technically entangled: see potential bug description below +void ExecFinishedBox::setValueAndUpdateList(const std::wstring& value) +{ + //it may be a little lame to update the list on each mouse-button click, but it should be working and we dont't have to manipulate wxComboBox internals + + std::deque items; + + //1. special command + items.push_back(implementation::translate(cmdTxtCloseProgressDlg)); + + //2. built in commands + for (auto it = defaultCommands.begin(); it != defaultCommands.end(); ++it) + items.push_back(it->first); + + //3. history elements + if (history_ && !history_->empty()) + { + items.push_back(separationLine); + items.insert(items.end(), history_->begin(), history_->end()); + std::sort(items.end() - history_->size(), items.end()); + } + + //attention: if the target value is not part of the dropdown list, SetValue() will look for a string that *starts with* this value: + //e.g. if the dropdown list contains "222" SetValue("22") will erroneously set and select "222" instead, while "111" would be set correctly! + // -> by design on Windows! + if (std::find(items.begin(), items.end(), value) == items.end()) + { + if (!value.empty()) + items.push_front(separationLine); + items.push_front(value); + } + + Clear(); + std::for_each(items.begin(), items.end(), [&](const std::wstring& item) { this->Append(item); }); + //this->SetSelection(wxNOT_FOUND); //don't select anything + SetValue(value); //preserve main text! +} + + +void ExecFinishedBox::OnSelection(wxCommandEvent& event) +{ + wxCommandEvent dummy2(wxEVT_VALIDATE_USER_SELECTION); //we cannot replace built-in commands at this position in call stack, so defer to a later time! + if (auto handler = GetEventHandler()) + handler->AddPendingEvent(dummy2); + + event.Skip(); +} + + +void ExecFinishedBox::OnValidateSelection(wxCommandEvent& event) +{ + const auto& value = getValue(); + + if (value == separationLine) + setValueAndUpdateList(std::wstring()); + else + for (auto it = defaultCommands.begin(); it != defaultCommands.end(); ++it) + if (it->first == value) + return setValueAndUpdateList(it->second); //replace GUI name by actual command string +} + + +void ExecFinishedBox::OnUpdateList(wxEvent& event) +{ + setValue(getValue()); + event.Skip(); +} + + +void ExecFinishedBox::OnKeyEvent(wxKeyEvent& event) +{ + switch (event.GetKeyCode()) + { + case WXK_DELETE: + case WXK_NUMPAD_DELETE: + { + //try to delete the currently selected config history item + int pos = this->GetCurrentSelection(); + if (0 <= pos && pos < static_cast(this->GetCount()) && + //what a mess...: + (GetValue() != GetString(pos) || //avoid problems when a character shall be deleted instead of list item + GetValue() == wxEmptyString)) //exception: always allow removing empty entry + { + const std::wstring selValue = copyStringTo(GetString(pos)); + + if (history_ && std::find(history_->begin(), history_->end(), selValue) != history_->end()) //only history elements may be deleted + { + //save old (selected) value: deletion seems to have influence on this + const wxString currentVal = this->GetValue(); + //this->SetSelection(wxNOT_FOUND); + + //delete selected row + vector_remove_if(*history_, [&](const std::wstring& item) { return item == selValue; }); + + SetString(pos, wxString()); //in contrast to Delete(), this one does not kill the drop-down list and gives a nice visual feedback! + //Delete(pos); + + //(re-)set value + SetValue(currentVal); + } + + //eat up key event + return; + } + } + break; + + case WXK_UP: + case WXK_NUMPAD_UP: + case WXK_DOWN: + case WXK_NUMPAD_DOWN: + case WXK_PAGEUP: + case WXK_NUMPAD_PAGEUP: + case WXK_PAGEDOWN: + case WXK_NUMPAD_PAGEDOWN: + return; //swallow -> using these keys gives a weird effect due to this weird control + } + event.Skip(); +} diff --git a/FreeFileSync/Source/ui/exec_finished_box.h b/FreeFileSync/Source/ui/exec_finished_box.h new file mode 100644 index 00000000..2a69faef --- /dev/null +++ b/FreeFileSync/Source/ui/exec_finished_box.h @@ -0,0 +1,60 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef EXEC_FINISHED_BOX_18947773210473214 +#define EXEC_FINISHED_BOX_18947773210473214 + +#include +#include +#include +#include +#include + +//combobox with history function + functionality to delete items (DEL) + +//special command +bool isCloseProgressDlgCommand(const std::wstring& value); + + +class ExecFinishedBox : public wxComboBox +{ +public: + ExecFinishedBox(wxWindow* parent, + wxWindowID id, + const wxString& value = wxEmptyString, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + int n = 0, + const wxString choices[] = nullptr, + long style = 0, + const wxValidator& validator = wxDefaultValidator, + const wxString& name = wxComboBoxNameStr); + + void initHistory(std::vector& history, size_t historyMax) { history_ = &history; historyMax_ = historyMax; } + void addItemHistory(); //adds current item to history + + // use these two accessors instead of GetValue()/SetValue(): + std::wstring getValue() const; + void setValue(const std::wstring& value); + //required for setting value correctly + Linux to ensure the dropdown is shown as being populated + +private: + void OnKeyEvent(wxKeyEvent& event); + void OnMouseWheel(wxMouseEvent& event) {} //swallow! this gives confusing UI feedback anyway + void OnSelection(wxCommandEvent& event); + void OnValidateSelection(wxCommandEvent& event); + void OnUpdateList(wxEvent& event); + + void setValueAndUpdateList(const std::wstring& value); + + std::vector* history_; + size_t historyMax_; + + const std::vector> defaultCommands; +}; + + +#endif //EXEC_FINISHED_BOX_18947773210473214 diff --git a/FreeFileSync/Source/ui/folder_history_box.cpp b/FreeFileSync/Source/ui/folder_history_box.cpp new file mode 100644 index 00000000..4676aa43 --- /dev/null +++ b/FreeFileSync/Source/ui/folder_history_box.cpp @@ -0,0 +1,137 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "folder_history_box.h" +#include +#include +#include +#include "../lib/resolve_path.h" +#ifdef ZEN_LINUX +#include +#endif + +using namespace zen; + + +FolderHistoryBox::FolderHistoryBox(wxWindow* parent, + wxWindowID id, + const wxString& value, + const wxPoint& pos, + const wxSize& size, + int n, + const wxString choices[], + long style, + const wxValidator& validator, + const wxString& name) : + wxComboBox(parent, id, value, pos, size, n, choices, style, validator, name) +{ + //##################################### + /*##*/ SetMinSize(wxSize(150, -1)); //## workaround yet another wxWidgets bug: default minimum size is much too large for a wxComboBox + //##################################### + + Connect(wxEVT_KEY_DOWN, wxKeyEventHandler (FolderHistoryBox::OnKeyEvent ), nullptr, this); + + warn_static("mac") + warn_static("linux") + +#if defined ZEN_WIN + //on Win, this mouse click event only fires, when clicking on the small down arrow, NOT when clicking on the text field + //thanks to wxWidgets' non-portability it's exactly the converse on Linux! + Connect(wxEVT_LEFT_DOWN, wxEventHandler(FolderHistoryBox::OnRequireHistoryUpdate), nullptr, this); +#elif defined ZEN_LINUX || defined ZEN_MAC + /* + we can't attach to wxEVT_COMMAND_TEXT_UPDATED, since setValueAndUpdateList() will implicitly emit wxEVT_COMMAND_TEXT_UPDATED again when calling Clear()! + => Crash on Suse/X11/wxWidgets 2.9.4 on startup (setting a flag to guard against recursion does not work, still crash) + + On OS attaching to wxEVT_LEFT_DOWN leads to occasional crashes, especially when double-clicking + */ +#endif + +#ifdef ZEN_LINUX + //file drag and drop directly into the text control unhelpfully inserts in format "file://.." + //1. this format's implementation is a mess: http://www.lephpfacile.com/manuel-php-gtk/tutorials.filednd.urilist.php + //2. even if we handle "drag-data-received" for "text/uri-list" this doesn't consider logic in dirname.cpp + //=> disable all drop events on the text control (disables text drop, too, but not a big loss) + //=> all drops are nicely propagated as regular file drop events like they should have been in the first place! + if (GtkWidget* widget = GetConnectWidget()) + ::gtk_drag_dest_unset(widget); +#endif +} + + +void FolderHistoryBox::OnRequireHistoryUpdate(wxEvent& event) +{ + setValueAndUpdateList(GetValue()); + event.Skip(); +} + +//set value and update list are technically entangled: see potential bug description below +void FolderHistoryBox::setValueAndUpdateList(const wxString& dirname) +{ + //populate selection list.... + std::vector dirList; + { + //add some aliases to allow user changing to volume name and back, if possible + std::vector aliases = getDirectoryAliases(toZ(dirname)); //may block when resolving [] + std::transform(aliases.begin(), aliases.end(), std::back_inserter(dirList), [](const Zstring& str) { return utfCvrtTo(str); }); + } + if (sharedHistory_.get()) + { + std::vector tmp = sharedHistory_->getList(); + std::sort(tmp.begin(), tmp.end(), LessFilename()); + + if (!dirList.empty() && !tmp.empty()) + dirList.push_back(FolderHistory::separationLine()); + + std::transform(tmp.begin(), tmp.end(), std::back_inserter(dirList), [](const Zstring& str) { return utfCvrtTo(str); }); + } + + //########################################################################################### + + //attention: if the target value is not part of the dropdown list, SetValue() will look for a string that *starts with* this value: + //e.g. if the dropdown list contains "222" SetValue("22") will erroneously set and select "222" instead, while "111" would be set correctly! + // -> by design on Windows! + if (std::find(dirList.begin(), dirList.end(), dirname) == dirList.end()) + dirList.insert(dirList.begin(), dirname); + + Clear(); //emits yet another wxEVT_COMMAND_TEXT_UPDATED on Suse/X11/wxWidgets 2.9.4!!! + std::for_each(dirList.begin(), dirList.end(), [&](const wxString& dir) { this->Append(dir); }); + //this->SetSelection(wxNOT_FOUND); //don't select anything + this->SetValue(dirname); //preserve main text! +} + + +void FolderHistoryBox::OnKeyEvent(wxKeyEvent& event) +{ + const int keyCode = event.GetKeyCode(); + if (keyCode == WXK_DELETE || keyCode == WXK_NUMPAD_DELETE) + { + //try to delete the currently selected config history item + int pos = this->GetCurrentSelection(); + if (0 <= pos && pos < static_cast(this->GetCount()) && + //what a mess...: + (GetValue() != GetString(pos) || //avoid problems when a character shall be deleted instead of list item + GetValue() == wxEmptyString)) //exception: always allow removing empty entry + { + //save old (selected) value: deletion seems to have influence on this + const wxString currentVal = this->GetValue(); + //this->SetSelection(wxNOT_FOUND); + + //delete selected row + if (sharedHistory_.get()) + sharedHistory_->delItem(toZ(GetString(pos))); + SetString(pos, wxString()); //in contrast to Delete(), this one does not kill the drop-down list and gives a nice visual feedback! + //Delete(pos); + + //(re-)set value + this->SetValue(currentVal); + + //eat up key event + return; + } + } + event.Skip(); +} diff --git a/FreeFileSync/Source/ui/folder_history_box.h b/FreeFileSync/Source/ui/folder_history_box.h new file mode 100644 index 00000000..9ffa2d74 --- /dev/null +++ b/FreeFileSync/Source/ui/folder_history_box.h @@ -0,0 +1,97 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef CUSTOMCOMBOBOX_H_INCLUDED +#define CUSTOMCOMBOBOX_H_INCLUDED + +#include +#include +#include +#include +#include + +//combobox with history function + functionality to delete items (DEL) + + +class FolderHistory +{ +public: + FolderHistory() : maxSize_(0) {} + + FolderHistory(const std::vector& dirnames, size_t maxSize) : + maxSize_(maxSize), + dirnames_(dirnames) + { + if (dirnames_.size() > maxSize_) //keep maximal size of history list + dirnames_.resize(maxSize_); + } + + const std::vector& getList() const { return dirnames_; } + + static const wxString separationLine() { return L"---------------------------------------------------------------------------------------------------------------"; } + + void addItem(const Zstring& dirname) + { + if (dirname.empty() || dirname == zen::utfCvrtTo(separationLine())) + return; + + Zstring nameTmp = dirname; + zen::trim(nameTmp); + + //insert new folder or put it to the front if already existing + auto it = std::find_if(dirnames_.begin(), dirnames_.end(), + [&](const Zstring& entry) { return ::EqualFilename()(entry, nameTmp); }); + + if (it != dirnames_.end()) + dirnames_.erase(it); + dirnames_.insert(dirnames_.begin(), nameTmp); + + if (dirnames_.size() > maxSize_) //keep maximal size of history list + dirnames_.resize(maxSize_); + } + + void delItem(const Zstring& dirname) { zen::vector_remove_if(dirnames_, [&](const Zstring& entry) { return ::EqualFilename()(entry, dirname); }); } + +private: + + size_t maxSize_; + std::vector dirnames_; +}; + + +class FolderHistoryBox : public wxComboBox +{ +public: + FolderHistoryBox(wxWindow* parent, + wxWindowID id, + const wxString& value = wxEmptyString, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + int n = 0, + const wxString choices[] = nullptr, + long style = 0, + const wxValidator& validator = wxDefaultValidator, + const wxString& name = wxComboBoxNameStr); + + void init(const std::shared_ptr& sharedHistory) { sharedHistory_ = sharedHistory; } + + void setValue(const wxString& dirname) + { + setValueAndUpdateList(dirname); //required for setting value correctly; Linux: ensure the dropdown is shown as being populated + } + + // GetValue + +private: + void OnKeyEvent(wxKeyEvent& event); + void OnRequireHistoryUpdate(wxEvent& event); + void setValueAndUpdateList(const wxString& dirname); + + std::shared_ptr sharedHistory_; +}; + + +#endif // CUSTOMCOMBOBOX_H_INCLUDED diff --git a/FreeFileSync/Source/ui/folder_history_types.h b/FreeFileSync/Source/ui/folder_history_types.h new file mode 100644 index 00000000..23766413 --- /dev/null +++ b/FreeFileSync/Source/ui/folder_history_types.h @@ -0,0 +1,25 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef FOLDER_HIST_TYPES_32481457137432143214 +#define FOLDER_HIST_TYPES_32481457137432143214 + +//#include +#include + + +namespace zen +{ +struct ConfigHistoryItem +{ + explicit ConfigHistoryItem(const Zstring& name) : configFile(name) {} + ConfigHistoryItem() {} + Zstring configFile; + //time_t lastSyncTime; +}; +} + +#endif //FOLDER_HIST_TYPES_32481457137432143214 diff --git a/FreeFileSync/Source/ui/folder_pair.h b/FreeFileSync/Source/ui/folder_pair.h new file mode 100644 index 00000000..be01e79d --- /dev/null +++ b/FreeFileSync/Source/ui/folder_pair.h @@ -0,0 +1,221 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef FOLDERPAIR_H_89341750847252345 +#define FOLDERPAIR_H_89341750847252345 + +#include +#include +#include +#include +#include +#include +#include "dir_name.h" +#include "small_dlgs.h" +#include "sync_cfg.h" +#include "../lib/norm_filter.h" +#include "../structures.h" + +namespace zen +{ +//basic functionality for handling alternate folder pair configuration: change sync-cfg/filter cfg, right-click context menu, button icons... + +template +class FolderPairPanelBasic : private wxEvtHandler +{ +public: + typedef std::shared_ptr AltCompCfgPtr; + typedef std::shared_ptr AltSyncCfgPtr; + + void setConfig(AltCompCfgPtr compConfig, AltSyncCfgPtr syncCfg, const FilterConfig& filter) + { + altCompConfig = compConfig; + altSyncConfig = syncCfg; + localFilter = filter; + refreshButtons(); + } + + AltCompCfgPtr getAltCompConfig() const { return altCompConfig; } + AltSyncCfgPtr getAltSyncConfig() const { return altSyncConfig; } + FilterConfig getAltFilterConfig() const { return localFilter; } + + + FolderPairPanelBasic(GuiPanel& basicPanel) : //takes reference on basic panel to be enhanced + basicPanel_(basicPanel) + { + //register events for removal of alternate configuration + basicPanel_.m_bpButtonAltCompCfg ->Connect(wxEVT_RIGHT_DOWN, wxCommandEventHandler(FolderPairPanelBasic::OnAltCompCfgContext ), nullptr, this); + basicPanel_.m_bpButtonAltSyncCfg ->Connect(wxEVT_RIGHT_DOWN, wxCommandEventHandler(FolderPairPanelBasic::OnAltSyncCfgContext ), nullptr, this); + basicPanel_.m_bpButtonLocalFilter->Connect(wxEVT_RIGHT_DOWN, wxCommandEventHandler(FolderPairPanelBasic::OnLocalFilterCfgContext), nullptr, this); + + basicPanel_.m_bpButtonAltCompCfg-> Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FolderPairPanelBasic::OnAltCompCfg ), nullptr, this); + basicPanel_.m_bpButtonAltSyncCfg-> Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FolderPairPanelBasic::OnAltSyncCfg ), nullptr, this); + basicPanel_.m_bpButtonLocalFilter->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FolderPairPanelBasic::OnLocalFilterCfg), nullptr, this); + + basicPanel_.m_bpButtonRemovePair->SetBitmapLabel(getResourceImage(L"item_remove")); + } + +private: + void refreshButtons() + { + if (altCompConfig.get()) + { + setImage(*basicPanel_.m_bpButtonAltCompCfg, getResourceImage(L"cfg_compare_small")); + basicPanel_.m_bpButtonAltCompCfg->SetToolTip(_("Alternate comparison settings") + L" (" + getVariantName(altCompConfig->compareVar) + L")"); + } + else + { + setImage(*basicPanel_.m_bpButtonAltCompCfg, greyScale(getResourceImage(L"cfg_compare_small"))); + basicPanel_.m_bpButtonAltCompCfg->SetToolTip(_("Alternate comparison settings")); + } + + if (altSyncConfig.get()) + { + setImage(*basicPanel_.m_bpButtonAltSyncCfg, getResourceImage(L"cfg_sync_small")); + basicPanel_.m_bpButtonAltSyncCfg->SetToolTip(_("Alternate synchronization settings") + L" (" + getVariantName(altSyncConfig->directionCfg.var) + L")"); + } + else + { + setImage(*basicPanel_.m_bpButtonAltSyncCfg, greyScale(getResourceImage(L"cfg_sync_small"))); + basicPanel_.m_bpButtonAltSyncCfg->SetToolTip(_("Alternate synchronization settings")); + } + + if (!isNullFilter(localFilter)) + { + setImage(*basicPanel_.m_bpButtonLocalFilter, getResourceImage(L"filter_small")); + basicPanel_.m_bpButtonLocalFilter->SetToolTip(_("Local filter") + L" (" + _("Active") + L")"); + } + else + { + setImage(*basicPanel_.m_bpButtonLocalFilter, greyScale(getResourceImage(L"filter_small"))); + basicPanel_.m_bpButtonLocalFilter->SetToolTip(_("Local filter") + L" (" + _("None") + L")"); + } + } + + void OnAltCompCfgContext(wxCommandEvent& event) + { + auto removeAltCompCfg = [&] + { + this->altCompConfig.reset(); //"this->" galore: workaround GCC compiler bugs + this->refreshButtons(); + this->onAltCompCfgChange(); + }; + + ContextMenu menu; + menu.addItem(_("Remove alternate settings"), removeAltCompCfg, nullptr, altCompConfig.get() != nullptr); + menu.popup(basicPanel_); + } + + void OnAltSyncCfgContext(wxCommandEvent& event) + { + auto removeAltSyncCfg = [&] + { + this->altSyncConfig.reset(); + this->refreshButtons(); + this->onAltSyncCfgChange(); + }; + + ContextMenu menu; + menu.addItem(_("Remove alternate settings"), removeAltSyncCfg, nullptr, altSyncConfig.get() != nullptr); + menu.popup(basicPanel_); + } + + void OnLocalFilterCfgContext(wxCommandEvent& event) + { + auto removeLocalFilterCfg = [&] + { + this->localFilter = FilterConfig(); + this->refreshButtons(); + this->onLocalFilterCfgChange(); + }; + + std::unique_ptr& filterCfgOnClipboard = getFilterCfgOnClipboardRef(); + + auto copyFilter = [&] { filterCfgOnClipboard = make_unique(this->localFilter); }; + auto pasteFilter = [&] + { + if (filterCfgOnClipboard) + { + this->localFilter = *filterCfgOnClipboard; + this->refreshButtons(); + this->onLocalFilterCfgChange(); + } + }; + + ContextMenu menu; + menu.addItem(_("Clear filter settings"), removeLocalFilterCfg, nullptr, !isNullFilter(localFilter)); + menu.addSeparator(); + menu.addItem( _("Copy"), copyFilter, nullptr, !isNullFilter(localFilter)); + menu.addItem( _("Paste"), pasteFilter, nullptr, filterCfgOnClipboard.get() != nullptr); + menu.popup(basicPanel_); + } + + + virtual MainConfiguration getMainConfig() const = 0; + virtual wxWindow* getParentWindow() = 0; + virtual std::unique_ptr& getFilterCfgOnClipboardRef() = 0; + + virtual void onAltCompCfgChange() = 0; + virtual void onAltSyncCfgChange() = 0; + virtual void onLocalFilterCfgChange() = 0; + + void OnAltCompCfg(wxCommandEvent& event) + { + const MainConfiguration mainCfg = getMainConfig(); + + CompConfig cmpCfg = altCompConfig.get() ? *altCompConfig : mainCfg.cmpConfig; + + if (showCompareCfgDialog(getParentWindow(), cmpCfg, _("Alternate Comparison Settings")) == ReturnSmallDlg::BUTTON_OKAY) + { + altCompConfig = std::make_shared(cmpCfg); + refreshButtons(); + onAltCompCfgChange(); + } + } + + void OnAltSyncCfg(wxCommandEvent& event) + { + const MainConfiguration mainCfg = getMainConfig(); + + CompConfig cmpCfg = altCompConfig.get() ? *altCompConfig : mainCfg.cmpConfig; + SyncConfig syncCfg = altSyncConfig.get() ? *altSyncConfig : mainCfg.syncCfg; + + if (showSyncConfigDlg(getParentWindow(), + cmpCfg.compareVar, + syncCfg, + _("Alternate Synchronization Settings"), + nullptr, + nullptr) == ReturnSyncConfig::BUTTON_OKAY) //optional input parameter + { + altSyncConfig = std::make_shared(syncCfg); + refreshButtons(); + onAltSyncCfgChange(); + } + } + + void OnLocalFilterCfg(wxCommandEvent& event) + { + FilterConfig localFiltTmp = localFilter; + + if (showFilterDialog(getParentWindow(), localFiltTmp, _("Local Filter")) == ReturnSmallDlg::BUTTON_OKAY) + { + localFilter = localFiltTmp; + refreshButtons(); + onLocalFilterCfgChange(); + } + } + + GuiPanel& basicPanel_; //panel to be enhanced by this template + + //alternate configuration attached to it + AltCompCfgPtr altCompConfig; //optional + AltSyncCfgPtr altSyncConfig; // + FilterConfig localFilter; +}; +} + + +#endif //FOLDERPAIR_H_89341750847252345 diff --git a/FreeFileSync/Source/ui/grid_view.cpp b/FreeFileSync/Source/ui/grid_view.cpp new file mode 100644 index 00000000..e1e16e47 --- /dev/null +++ b/FreeFileSync/Source/ui/grid_view.cpp @@ -0,0 +1,548 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "grid_view.h" +#include "sorting.h" +#include "../synchronization.h" +#include +//#include + +using namespace zen; + + +template +void getNumbers(const FileSystemObject& fsObj, StatusResult& result) +{ + struct GetValues : public FSObjectVisitor + { + GetValues(StatusResult& res) : result_(res) {} + + virtual void visit(const FilePair& fileObj) + { + if (!fileObj.isEmpty()) + { + result_.filesizeLeftView += fileObj.getFileSize(); + ++result_.filesOnLeftView; + } + if (!fileObj.isEmpty()) + { + result_.filesizeRightView += fileObj.getFileSize(); + ++result_.filesOnRightView; + } + } + + virtual void visit(const SymlinkPair& linkObj) + { + if (!linkObj.isEmpty()) + ++result_.filesOnLeftView; + + if (!linkObj.isEmpty()) + ++result_.filesOnRightView; + } + + virtual void visit(const DirPair& dirObj) + { + if (!dirObj.isEmpty()) + ++result_.foldersOnLeftView; + + if (!dirObj.isEmpty()) + ++result_.foldersOnRightView; + } + StatusResult& result_; + } getVal(result); + fsObj.accept(getVal); +} + + +template +void GridView::updateView(Predicate pred) +{ + viewRef.clear(); + rowPositions.clear(); + rowPositionsFirstChild.clear(); + + std::for_each(sortedRef.begin(), sortedRef.end(), + [&](const RefIndex& ref) + { + if (const FileSystemObject* fsObj = FileSystemObject::retrieve(ref.objId)) + if (pred(*fsObj)) + { + //save row position for direct random access to FilePair or DirPair + this->rowPositions.insert(std::make_pair(ref.objId, viewRef.size())); //costs: 0.28 s per call - MSVC based on std::set + //"this->" required by two-pass lookup as enforced by GCC 4.7 + + //save row position to identify first child *on sorted subview* of DirPair or BaseDirPair in case latter are filtered out + const HierarchyObject* parent = &fsObj->parent(); + for (;;) //map all yet unassociated parents to this row + { + const auto rv = this->rowPositionsFirstChild.insert(std::make_pair(parent, viewRef.size())); + if (!rv.second) + break; + + if (auto dirObj = dynamic_cast(parent)) + parent = &(dirObj->parent()); + else + break; + } + + //build subview + this->viewRef.push_back(ref.objId); + } + }); +} + + +ptrdiff_t GridView::findRowDirect(FileSystemObject::ObjectIdConst objId) const +{ + auto it = rowPositions.find(objId); + return it != rowPositions.end() ? it->second : -1; +} + +ptrdiff_t GridView::findRowFirstChild(const HierarchyObject* hierObj) const +{ + auto it = rowPositionsFirstChild.find(hierObj); + return it != rowPositionsFirstChild.end() ? it->second : -1; +} + + +GridView::StatusCmpResult::StatusCmpResult() : + existsLeftOnly (false), + existsRightOnly (false), + existsLeftNewer (false), + existsRightNewer(false), + existsDifferent (false), + existsEqual (false), + existsConflict (false), + filesOnLeftView (0), + foldersOnLeftView (0), + filesOnRightView (0), + foldersOnRightView(0) {} + + +GridView::StatusCmpResult GridView::updateCmpResult(bool hideFiltered, //maps sortedRef to viewRef + bool leftOnlyFilesActive, + bool rightOnlyFilesActive, + bool leftNewerFilesActive, + bool rightNewerFilesActive, + bool differentFilesActive, + bool equalFilesActive, + bool conflictFilesActive) +{ + StatusCmpResult output; + + updateView([&](const FileSystemObject& fsObj) -> bool + { + if (hideFiltered && !fsObj.isActive()) + return false; + + switch (fsObj.getCategory()) + { + case FILE_LEFT_SIDE_ONLY: + output.existsLeftOnly = true; + if (!leftOnlyFilesActive) return false; + break; + case FILE_RIGHT_SIDE_ONLY: + output.existsRightOnly = true; + if (!rightOnlyFilesActive) return false; + break; + case FILE_LEFT_NEWER: + output.existsLeftNewer = true; + if (!leftNewerFilesActive) return false; + break; + case FILE_RIGHT_NEWER: + output.existsRightNewer = true; + if (!rightNewerFilesActive) return false; + break; + case FILE_DIFFERENT: + output.existsDifferent = true; + if (!differentFilesActive) return false; + break; + case FILE_EQUAL: + case FILE_DIFFERENT_METADATA: //= sub-category of equal + output.existsEqual = true; + if (!equalFilesActive) return false; + break; + case FILE_CONFLICT: + output.existsConflict = true; + if (!conflictFilesActive) return false; + break; + } + //calculate total number of bytes for each side + getNumbers(fsObj, output); + return true; + }); + + return output; +} + + +GridView::StatusSyncPreview::StatusSyncPreview() : + existsSyncCreateLeft (false), + existsSyncCreateRight(false), + existsSyncDeleteLeft (false), + existsSyncDeleteRight(false), + existsSyncDirLeft (false), + existsSyncDirRight (false), + existsSyncDirNone (false), + existsSyncEqual (false), + existsConflict (false), + filesOnLeftView (0), + foldersOnLeftView (0), + filesOnRightView (0), + foldersOnRightView(0) {} + + +GridView::StatusSyncPreview GridView::updateSyncPreview(bool hideFiltered, //maps sortedRef to viewRef + bool syncCreateLeftActive, + bool syncCreateRightActive, + bool syncDeleteLeftActive, + bool syncDeleteRightActive, + bool syncDirOverwLeftActive, + bool syncDirOverwRightActive, + bool syncDirNoneActive, + bool syncEqualActive, + bool conflictFilesActive) +{ + StatusSyncPreview output; + + updateView([&](const FileSystemObject& fsObj) -> bool + { + if (hideFiltered && !fsObj.isActive()) + return false; + + switch (fsObj.getSyncOperation()) //evaluate comparison result and sync direction + { + case SO_CREATE_NEW_LEFT: + output.existsSyncCreateLeft = true; + if (!syncCreateLeftActive) return false; + break; + case SO_CREATE_NEW_RIGHT: + output.existsSyncCreateRight = true; + if (!syncCreateRightActive) return false; + break; + case SO_DELETE_LEFT: + output.existsSyncDeleteLeft = true; + if (!syncDeleteLeftActive) return false; + break; + case SO_DELETE_RIGHT: + output.existsSyncDeleteRight = true; + if (!syncDeleteRightActive) return false; + break; + case SO_OVERWRITE_RIGHT: + case SO_COPY_METADATA_TO_RIGHT: //no extra button on screen + case SO_MOVE_RIGHT_SOURCE: + case SO_MOVE_RIGHT_TARGET: + output.existsSyncDirRight = true; + if (!syncDirOverwRightActive) return false; + break; + case SO_OVERWRITE_LEFT: + case SO_COPY_METADATA_TO_LEFT: //no extra button on screen + case SO_MOVE_LEFT_TARGET: + case SO_MOVE_LEFT_SOURCE: + output.existsSyncDirLeft = true; + if (!syncDirOverwLeftActive) return false; + break; + case SO_DO_NOTHING: + output.existsSyncDirNone = true; + if (!syncDirNoneActive) return false; + break; + case SO_EQUAL: + output.existsSyncEqual = true; + if (!syncEqualActive) return false; + break; + case SO_UNRESOLVED_CONFLICT: + output.existsConflict = true; + if (!conflictFilesActive) return false; + break; + } + + //calculate total number of bytes for each side + getNumbers(fsObj, output); + return true; + }); + + return output; +} + + +std::vector GridView::getAllFileRef(const std::set& rows) +{ + std::vector output; + + auto iterLast = rows.lower_bound(rowsOnView()); //loop over valid rows only! + std::for_each(rows.begin(), iterLast, + [&](size_t pos) + { + if (FileSystemObject* fsObj = FileSystemObject::retrieve(viewRef[pos])) + output.push_back(fsObj); + }); + return output; +} + + +void GridView::removeInvalidRows() +{ + viewRef.clear(); + rowPositions.clear(); + rowPositionsFirstChild.clear(); + + //remove rows that have been deleted meanwhile + vector_remove_if(sortedRef, [&](const RefIndex& refIdx) { return FileSystemObject::retrieve(refIdx.objId) == nullptr; }); +} + + +class GridView::SerializeHierarchy +{ +public: + static void execute(HierarchyObject& hierObj, std::vector& sortedRef, size_t index) { SerializeHierarchy(sortedRef, index).recurse(hierObj); } + +private: + SerializeHierarchy(std::vector& sortedRef, size_t index) : + index_(index), + sortedRef_(sortedRef) {} + + void recurse(HierarchyObject& hierObj) + { + for (FilePair& fileObj : hierObj.refSubFiles()) + sortedRef_.push_back(RefIndex(index_, fileObj.getId())); + for (SymlinkPair& linkObj : hierObj.refSubLinks()) + sortedRef_.push_back(RefIndex(index_, linkObj.getId())); + for (DirPair& dirObj : hierObj.refSubDirs()) + { + sortedRef_.push_back(RefIndex(index_, dirObj.getId())); + recurse(dirObj); //add recursion here to list sub-objects directly below parent! + } + } + + size_t index_; + std::vector& sortedRef_; +}; + + +void GridView::setData(FolderComparison& folderCmp) +{ + //clear everything + std::vector().swap(viewRef); //free mem + std::vector().swap(sortedRef); // + currentSort.reset(); + + folderPairCount = std::count_if(begin(folderCmp), end(folderCmp), + [](const BaseDirPair& baseObj) //count non-empty pairs to distinguish single/multiple folder pair cases + { + return !baseObj.getBaseDirPf().empty() || + !baseObj.getBaseDirPf().empty(); + }); + + for (auto it = begin(folderCmp); it != end(folderCmp); ++it) + SerializeHierarchy::execute(*it, sortedRef, it - begin(folderCmp)); +} + + +//------------------------------------ SORTING TEMPLATES ------------------------------------------------ +template +class GridView::LessRelativeName : public std::binary_function +{ +public: + bool operator()(const RefIndex a, const RefIndex b) const + { + //presort by folder pair + if (a.folderIndex != b.folderIndex) + return ascending ? + a.folderIndex < b.folderIndex : + a.folderIndex > b.folderIndex; + + const FileSystemObject* fsObjA = FileSystemObject::retrieve(a.objId); + const FileSystemObject* fsObjB = FileSystemObject::retrieve(b.objId); + if (!fsObjA) //invalid rows shall appear at the end + return false; + else if (!fsObjB) + return true; + + return lessRelativeName(*fsObjA, *fsObjB); + } +}; + + +template +class GridView::LessShortFileName : public std::binary_function +{ +public: + bool operator()(const RefIndex a, const RefIndex b) const + { + const FileSystemObject* fsObjA = FileSystemObject::retrieve(a.objId); + const FileSystemObject* fsObjB = FileSystemObject::retrieve(b.objId); + if (!fsObjA) //invalid rows shall appear at the end + return false; + else if (!fsObjB) + return true; + + return lessShortFileName(*fsObjA, *fsObjB); + } +}; + + +template +class GridView::LessFilesize : public std::binary_function +{ +public: + bool operator()(const RefIndex a, const RefIndex b) const + { + const FileSystemObject* fsObjA = FileSystemObject::retrieve(a.objId); + const FileSystemObject* fsObjB = FileSystemObject::retrieve(b.objId); + if (!fsObjA) //invalid rows shall appear at the end + return false; + else if (!fsObjB) + return true; + + return lessFilesize(*fsObjA, *fsObjB); + } +}; + + +template +class GridView::LessFiletime : public std::binary_function +{ +public: + bool operator()(const RefIndex a, const RefIndex b) const + { + const FileSystemObject* fsObjA = FileSystemObject::retrieve(a.objId); + const FileSystemObject* fsObjB = FileSystemObject::retrieve(b.objId); + if (!fsObjA) //invalid rows shall appear at the end + return false; + else if (!fsObjB) + return true; + + return lessFiletime(*fsObjA, *fsObjB); + } +}; + + +template +class GridView::LessExtension : public std::binary_function +{ +public: + bool operator()(const RefIndex a, const RefIndex b) const + { + const FileSystemObject* fsObjA = FileSystemObject::retrieve(a.objId); + const FileSystemObject* fsObjB = FileSystemObject::retrieve(b.objId); + if (!fsObjA) //invalid rows shall appear at the end + return false; + else if (!fsObjB) + return true; + + return lessExtension(*fsObjA, *fsObjB); + } +}; + + +template +class GridView::LessCmpResult : public std::binary_function +{ +public: + bool operator()(const RefIndex a, const RefIndex b) const + { + const FileSystemObject* fsObjA = FileSystemObject::retrieve(a.objId); + const FileSystemObject* fsObjB = FileSystemObject::retrieve(b.objId); + if (!fsObjA) //invalid rows shall appear at the end + return false; + else if (!fsObjB) + return true; + + return lessCmpResult(*fsObjA, *fsObjB); + } +}; + + +template +class GridView::LessSyncDirection : public std::binary_function +{ +public: + bool operator()(const RefIndex a, const RefIndex b) const + { + const FileSystemObject* fsObjA = FileSystemObject::retrieve(a.objId); + const FileSystemObject* fsObjB = FileSystemObject::retrieve(b.objId); + if (!fsObjA) //invalid rows shall appear at the end + return false; + else if (!fsObjB) + return true; + + return lessSyncDirection(*fsObjA, *fsObjB); + } +}; + +//------------------------------------------------------------------------------------------------------- +bool GridView::getDefaultSortDirection(ColumnTypeRim type) //true: ascending; false: descending +{ + switch (type) + { + case COL_TYPE_SIZE: + case COL_TYPE_DATE: + return false; + + case COL_TYPE_DIRECTORY: + case COL_TYPE_FULL_PATH: + case COL_TYPE_REL_PATH: + case COL_TYPE_FILENAME: + case COL_TYPE_EXTENSION: + return true; + } + assert(false); + return true; +} + + +void GridView::sortView(ColumnTypeRim type, bool onLeft, bool ascending) +{ + viewRef.clear(); + rowPositions.clear(); + rowPositionsFirstChild.clear(); + currentSort = make_unique(type, onLeft, ascending); + + switch (type) + { + case COL_TYPE_FULL_PATH: + case COL_TYPE_REL_PATH: + if ( ascending) std::sort(sortedRef.begin(), sortedRef.end(), LessRelativeName()); + else if (!ascending) std::sort(sortedRef.begin(), sortedRef.end(), LessRelativeName()); + break; + case COL_TYPE_FILENAME: + if ( ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessShortFileName()); + else if ( ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessShortFileName()); + else if (!ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessShortFileName()); + else if (!ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessShortFileName()); + break; + case COL_TYPE_SIZE: + if ( ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFilesize()); + else if ( ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFilesize()); + else if (!ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFilesize()); + else if (!ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFilesize()); + break; + case COL_TYPE_DATE: + if ( ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFiletime()); + else if ( ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFiletime()); + else if (!ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFiletime()); + else if (!ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFiletime()); + break; + case COL_TYPE_EXTENSION: + if ( ascending && onLeft) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessExtension()); + else if ( ascending && !onLeft) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessExtension()); + else if (!ascending && onLeft) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessExtension()); + else if (!ascending && !onLeft) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessExtension()); + break; + //case SORT_BY_CMP_RESULT: + // if ( ascending) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessCmpResult()); + // else if (!ascending) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessCmpResult()); + // break; + //case SORT_BY_SYNC_DIRECTION: + // if ( ascending) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessSyncDirection()); + // else if (!ascending) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessSyncDirection()); + // break; + case COL_TYPE_DIRECTORY: + if ( ascending) std::stable_sort(sortedRef.begin(), sortedRef.end(), [](const RefIndex a, const RefIndex b) { return a.folderIndex < b.folderIndex; }); + else if (!ascending) std::stable_sort(sortedRef.begin(), sortedRef.end(), [](const RefIndex a, const RefIndex b) { return a.folderIndex > b.folderIndex; }); + break; + } +} diff --git a/FreeFileSync/Source/ui/grid_view.h b/FreeFileSync/Source/ui/grid_view.h new file mode 100644 index 00000000..c01ac240 --- /dev/null +++ b/FreeFileSync/Source/ui/grid_view.h @@ -0,0 +1,202 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef GRIDVIEW_H_INCLUDED +#define GRIDVIEW_H_INCLUDED + +#include +#include "column_attr.h" +#include "../file_hierarchy.h" + + +namespace zen +{ +//grid view of FolderComparison +class GridView +{ +public: + GridView() : folderPairCount(0) {} + + //direct data access via row number + const FileSystemObject* getObject(size_t row) const; //returns nullptr if object is not found; complexity: constant! + /**/ + FileSystemObject* getObject(size_t row); // + size_t rowsOnView() const { return viewRef .size(); } //only visible elements + size_t rowsTotal () const { return sortedRef.size(); } //total rows available + + //get references to FileSystemObject: no nullptr-check needed! Everything's bound. + std::vector getAllFileRef(const std::set& rows); + + struct StatusCmpResult + { + StatusCmpResult(); + + bool existsLeftOnly; + bool existsRightOnly; + bool existsLeftNewer; + bool existsRightNewer; + bool existsDifferent; + bool existsEqual; + bool existsConflict; + + unsigned int filesOnLeftView; + unsigned int foldersOnLeftView; + unsigned int filesOnRightView; + unsigned int foldersOnRightView; + + zen::UInt64 filesizeLeftView; + zen::UInt64 filesizeRightView; + }; + + //comparison results view + StatusCmpResult updateCmpResult(bool hideFiltered, + bool leftOnlyFilesActive, + bool rightOnlyFilesActive, + bool leftNewerFilesActive, + bool rightNewerFilesActive, + bool differentFilesActive, + bool equalFilesActive, + bool conflictFilesActive); + + struct StatusSyncPreview + { + StatusSyncPreview(); + + bool existsSyncCreateLeft; + bool existsSyncCreateRight; + bool existsSyncDeleteLeft; + bool existsSyncDeleteRight; + bool existsSyncDirLeft; + bool existsSyncDirRight; + bool existsSyncDirNone; + bool existsSyncEqual; + bool existsConflict; + + unsigned int filesOnLeftView; + unsigned int foldersOnLeftView; + unsigned int filesOnRightView; + unsigned int foldersOnRightView; + + zen::UInt64 filesizeLeftView; + zen::UInt64 filesizeRightView; + }; + + //synchronization preview + StatusSyncPreview updateSyncPreview(bool hideFiltered, + bool syncCreateLeftActive, + bool syncCreateRightActive, + bool syncDeleteLeftActive, + bool syncDeleteRightActive, + bool syncDirOverwLeftActive, + bool syncDirOverwRightActive, + bool syncDirNoneActive, + bool syncEqualActive, + bool conflictFilesActive); + + void setData(FolderComparison& newData); + void removeInvalidRows(); //remove references to rows that have been deleted meanwhile: call after manual deletion and synchronization! + + //sorting... + bool static getDefaultSortDirection(zen::ColumnTypeRim type); //true: ascending; false: descending + + void sortView(zen::ColumnTypeRim type, bool onLeft, bool ascending); //always call this method for sorting, never sort externally! + + struct SortInfo + { + SortInfo(zen::ColumnTypeRim type, bool onLeft, bool ascending) : type_(type), onLeft_(onLeft), ascending_(ascending) {} + zen::ColumnTypeRim type_; + bool onLeft_; + bool ascending_; + }; + const SortInfo* getSortInfo() const { return currentSort.get(); } //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 HierarchyObject* hierObj) const; // find first child of DirPair or BaseDirPair *on sorted sub view* + //"hierObj" may be invalid, it is NOT dereferenced, return < 0 if not found + + size_t getFolderPairCount() const { return folderPairCount; } //count non-empty pairs to distinguish single/multiple folder pair cases + +private: + struct RefIndex + { + RefIndex(size_t folderInd, FileSystemObject::ObjectId id) : + folderIndex(folderInd), + objId(id) {} + size_t folderIndex; //because of alignment there's no benefit in using "unsigned int" in 64-bit code here! + FileSystemObject::ObjectId objId; + }; + + template void updateView(Predicate pred); + + + zen::hash_map rowPositions; //find row positions on sortedRef directly + zen::hash_map rowPositionsFirstChild; //find first child on sortedRef of a hierarchy object + //void* instead of HierarchyObject*: these are weak pointers and should *never be dereferenced*! + + std::vector viewRef; //partial view on sortedRef + /* /|\ + | (update...) + | */ + std::vector sortedRef; //flat view of weak pointers on folderCmp; may be sorted + /* /|\ + | (setData...) + | */ + //std::shared_ptr folderCmp; //actual comparison data: owned by GridView! + size_t folderPairCount; //number of non-empty folder pairs + + + class SerializeHierarchy; + + //sorting classes + template + class LessRelativeName; + + template + class LessShortFileName; + + template + class LessFilesize; + + template + class LessFiletime; + + template + class LessExtension; + + template + class LessCmpResult; + + template + class LessSyncDirection; + + std::unique_ptr currentSort; +}; + + + + + + + +//##################### implementation ######################################### + +inline +const FileSystemObject* GridView::getObject(size_t row) const +{ + return row < viewRef.size() ? + FileSystemObject::retrieve(viewRef[row]) : nullptr; +} + +inline +FileSystemObject* GridView::getObject(size_t row) +{ + //code re-use of const method: see Meyers Effective C++ + return const_cast(static_cast(*this).getObject(row)); +} +} + + +#endif // GRIDVIEW_H_INCLUDED diff --git a/FreeFileSync/Source/ui/gui_generated.cpp b/FreeFileSync/Source/ui/gui_generated.cpp new file mode 100644 index 00000000..91783b83 --- /dev/null +++ b/FreeFileSync/Source/ui/gui_generated.cpp @@ -0,0 +1,3499 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Oct 8 2012) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "../wx+/bitmap_button.h" +#include "../wx+/graph.h" +#include "../wx+/grid.h" +#include "../wx+/toggle_button.h" +#include "exec_finished_box.h" +#include "folder_history_box.h" +#include "triple_splitter.h" + +#include "gui_generated.h" + +/////////////////////////////////////////////////////////////////////////// + +MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxSize( 640,400 ), wxDefaultSize ); + + m_menubar1 = new wxMenuBar( 0 ); + m_menuFile = new wxMenu(); + m_menuItemNew = new wxMenuItem( m_menuFile, wxID_NEW, wxString( _("&New") ) + wxT('\t') + wxT("Ctrl+N"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItemNew ); + + m_menuItemLoad = new wxMenuItem( m_menuFile, wxID_OPEN, wxString( _("&Open...") ) + wxT('\t') + wxT("Ctrl+O"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItemLoad ); + + m_menuItemSave = new wxMenuItem( m_menuFile, wxID_SAVE, wxString( _("&Save") ) + wxT('\t') + wxT("Ctrl+S"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItemSave ); + + m_menuItemSaveAs = new wxMenuItem( m_menuFile, wxID_SAVEAS, wxString( _("Save &as...") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItemSaveAs ); + + m_menuItem7 = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("Save as &batch job...") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItem7 ); + + m_menuFile->AppendSeparator(); + + m_menuItem10 = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("1. &Compare") ) + wxT('\t') + wxT("F5"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItem10 ); + + m_menuItem11 = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("2. &Synchronize") ) + wxT('\t') + wxT("F8"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItem11 ); + + m_menuFile->AppendSeparator(); + + wxMenuItem* m_menuItem4; + m_menuItem4 = new wxMenuItem( m_menuFile, wxID_EXIT, wxString( _("&Quit") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItem4 ); + + m_menubar1->Append( m_menuFile, _("&Program") ); + + m_menuTools = new wxMenu(); + m_menuItemGlobSett = new wxMenuItem( m_menuTools, wxID_PREFERENCES, wxString( _("&Global settings") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuTools->Append( m_menuItemGlobSett ); + + m_menuTools->AppendSeparator(); + + m_menuLanguages = new wxMenu(); + m_menuTools->Append( -1, _("&Language"), m_menuLanguages ); + + wxMenuItem* m_menuItem15; + m_menuItem15 = new wxMenuItem( m_menuTools, wxID_FIND, wxString( _("&Find...") ) + wxT('\t') + wxT("Ctrl+F"), wxEmptyString, wxITEM_NORMAL ); + m_menuTools->Append( m_menuItem15 ); + + wxMenuItem* m_menuItem5; + m_menuItem5 = new wxMenuItem( m_menuTools, wxID_ANY, wxString( _("&Export file list...") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuTools->Append( m_menuItem5 ); + + m_menubar1->Append( m_menuTools, _("&Tools") ); + + m_menuHelp = new wxMenu(); + m_menuItemManual = new wxMenuItem( m_menuHelp, wxID_HELP, wxString( _("&View help") ) + wxT('\t') + wxT("F1"), wxEmptyString, wxITEM_NORMAL ); + m_menuHelp->Append( m_menuItemManual ); + + m_menuCheckVersion = new wxMenu(); + m_menuItemCheckVersionNow = new wxMenuItem( m_menuCheckVersion, wxID_ANY, wxString( _("&Check now") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuCheckVersion->Append( m_menuItemCheckVersionNow ); + + m_menuItemCheckVersionAuto = new wxMenuItem( m_menuCheckVersion, wxID_ANY, wxString( _("Check &automatically once a week") ) , wxEmptyString, wxITEM_CHECK ); + m_menuCheckVersion->Append( m_menuItemCheckVersionAuto ); + m_menuItemCheckVersionAuto->Check( true ); + + m_menuHelp->Append( -1, _("&Check for new version"), m_menuCheckVersion ); + + m_menuHelp->AppendSeparator(); + + m_menuItemAbout = new wxMenuItem( m_menuHelp, wxID_ABOUT, wxString( _("&About") ) + wxT('\t') + wxT("Shift+F1"), wxEmptyString, wxITEM_NORMAL ); + m_menuHelp->Append( m_menuItemAbout ); + + m_menubar1->Append( m_menuHelp, _("&Help") ); + + this->SetMenuBar( m_menubar1 ); + + bSizerPanelHolder = new wxBoxSizer( wxVERTICAL ); + + m_panelTopButtons = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxRAISED_BORDER|wxTAB_TRAVERSAL ); + bSizerTopButtons = new wxBoxSizer( wxHORIZONTAL ); + + + bSizerTopButtons->Add( 15, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + wxBoxSizer* bSizer1721; + bSizer1721 = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonCompare = new zen::BitmapTextButton( m_panelTopButtons, wxID_ANY, _("Compare"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonCompare->SetDefault(); + m_buttonCompare->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + m_buttonCompare->SetToolTip( _("dummy") ); + + bSizer1721->Add( m_buttonCompare, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_buttonCancel = new zen::BitmapTextButton( m_panelTopButtons, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( 180,-1 ), 0 ); + m_buttonCancel->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + m_buttonCancel->Enable( false ); + m_buttonCancel->Hide(); + + bSizer1721->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_bpButtonCmpConfig = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 44,44 ), wxBU_AUTODRAW ); + m_bpButtonCmpConfig->SetToolTip( _("dummy") ); + + bSizer1721->Add( m_bpButtonCmpConfig, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxEXPAND, 3 ); + + + bSizer1721->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonSyncConfig = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 44,44 ), wxBU_AUTODRAW ); + m_bpButtonSyncConfig->SetToolTip( _("dummy") ); + + bSizer1721->Add( m_bpButtonSyncConfig, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxEXPAND, 3 ); + + m_buttonSync = new zen::BitmapTextButton( m_panelTopButtons, wxID_ANY, _("Synchronize"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonSync->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + m_buttonSync->SetToolTip( _("dummy") ); + + bSizer1721->Add( m_buttonSync, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizerTopButtons->Add( bSizer1721, 1, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 4 ); + + + bSizerTopButtons->Add( 15, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + m_panelTopButtons->SetSizer( bSizerTopButtons ); + m_panelTopButtons->Layout(); + bSizerTopButtons->Fit( m_panelTopButtons ); + bSizerPanelHolder->Add( m_panelTopButtons, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + m_panelDirectoryPairs = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSTATIC_BORDER|wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer1601; + bSizer1601 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer91; + bSizer91 = new wxBoxSizer( wxHORIZONTAL ); + + m_panelTopLeft = new wxPanel( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelTopLeft->SetMinSize( wxSize( 1,-1 ) ); + + wxFlexGridSizer* fgSizer8; + fgSizer8 = new wxFlexGridSizer( 0, 2, 0, 0 ); + fgSizer8->AddGrowableCol( 1 ); + fgSizer8->SetFlexibleDirection( wxBOTH ); + fgSizer8->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_ALL ); + + + fgSizer8->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextResolvedPathL = new wxStaticText( m_panelTopLeft, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextResolvedPathL->Wrap( -1 ); + fgSizer8->Add( m_staticTextResolvedPathL, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); + + wxBoxSizer* bSizer159; + bSizer159 = new wxBoxSizer( wxHORIZONTAL ); + + m_bpButtonAddPair = new wxBitmapButton( m_panelTopLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonAddPair->SetToolTip( _("Add folder pair") ); + + bSizer159->Add( m_bpButtonAddPair, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonRemovePair = new wxBitmapButton( m_panelTopLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonRemovePair->SetToolTip( _("Remove folder pair") ); + + bSizer159->Add( m_bpButtonRemovePair, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + fgSizer8->Add( bSizer159, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + wxBoxSizer* bSizer182; + bSizer182 = new wxBoxSizer( wxHORIZONTAL ); + + m_directoryLeft = new FolderHistoryBox( m_panelTopLeft, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizer182->Add( m_directoryLeft, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonSelectDirLeft = new wxButton( m_panelTopLeft, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonSelectDirLeft->SetToolTip( _("Select a folder") ); + + bSizer182->Add( m_buttonSelectDirLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + fgSizer8->Add( bSizer182, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + m_panelTopLeft->SetSizer( fgSizer8 ); + m_panelTopLeft->Layout(); + fgSizer8->Fit( m_panelTopLeft ); + bSizer91->Add( m_panelTopLeft, 1, wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); + + m_panelTopMiddle = new wxPanel( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer1771; + bSizer1771 = new wxBoxSizer( wxVERTICAL ); + + + bSizer1771->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_bpButtonSwapSides = new wxBitmapButton( m_panelTopMiddle, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW ); + m_bpButtonSwapSides->SetToolTip( _("Swap sides") ); + + bSizer1771->Add( m_bpButtonSwapSides, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + wxBoxSizer* bSizer160; + bSizer160 = new wxBoxSizer( wxHORIZONTAL ); + + m_bpButtonAltCompCfg = new wxBitmapButton( m_panelTopMiddle, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + bSizer160->Add( m_bpButtonAltCompCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonLocalFilter = new wxBitmapButton( m_panelTopMiddle, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + bSizer160->Add( m_bpButtonLocalFilter, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 2 ); + + m_bpButtonAltSyncCfg = new wxBitmapButton( m_panelTopMiddle, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + bSizer160->Add( m_bpButtonAltSyncCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer1771->Add( bSizer160, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer1771->Add( 0, 0, 1, wxEXPAND, 5 ); + + + m_panelTopMiddle->SetSizer( bSizer1771 ); + m_panelTopMiddle->Layout(); + bSizer1771->Fit( m_panelTopMiddle ); + bSizer91->Add( m_panelTopMiddle, 0, wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_panelTopRight = new wxPanel( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelTopRight->SetMinSize( wxSize( 1,-1 ) ); + + wxBoxSizer* bSizer183; + bSizer183 = new wxBoxSizer( wxVERTICAL ); + + m_staticTextResolvedPathR = new wxStaticText( m_panelTopRight, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextResolvedPathR->Wrap( -1 ); + bSizer183->Add( m_staticTextResolvedPathR, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); + + wxBoxSizer* bSizer179; + bSizer179 = new wxBoxSizer( wxHORIZONTAL ); + + m_directoryRight = new FolderHistoryBox( m_panelTopRight, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizer179->Add( m_directoryRight, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonSelectDirRight = new wxButton( m_panelTopRight, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonSelectDirRight->SetToolTip( _("Select a folder") ); + + bSizer179->Add( m_buttonSelectDirRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer183->Add( bSizer179, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + m_panelTopRight->SetSizer( bSizer183 ); + m_panelTopRight->Layout(); + bSizer183->Fit( m_panelTopRight ); + bSizer91->Add( m_panelTopRight, 1, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + + bSizer1601->Add( bSizer91, 0, wxEXPAND, 5 ); + + m_scrolledWindowFolderPairs = new wxScrolledWindow( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), wxHSCROLL|wxVSCROLL ); + m_scrolledWindowFolderPairs->SetScrollRate( 10, 10 ); + m_scrolledWindowFolderPairs->SetMinSize( wxSize( -1,0 ) ); + + bSizerAddFolderPairs = new wxBoxSizer( wxVERTICAL ); + + + m_scrolledWindowFolderPairs->SetSizer( bSizerAddFolderPairs ); + m_scrolledWindowFolderPairs->Layout(); + bSizerAddFolderPairs->Fit( m_scrolledWindowFolderPairs ); + bSizer1601->Add( m_scrolledWindowFolderPairs, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + m_panelDirectoryPairs->SetSizer( bSizer1601 ); + m_panelDirectoryPairs->Layout(); + bSizer1601->Fit( m_panelDirectoryPairs ); + bSizerPanelHolder->Add( m_panelDirectoryPairs, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + m_gridNavi = new zen::Grid( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); + m_gridNavi->SetScrollRate( 5, 5 ); + bSizerPanelHolder->Add( m_gridNavi, 1, wxEXPAND, 5 ); + + m_panelCenter = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer1711; + bSizer1711 = new wxBoxSizer( wxVERTICAL ); + + m_splitterMain = new zen::TripleSplitter( m_panelCenter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer1781; + bSizer1781 = new wxBoxSizer( wxHORIZONTAL ); + + m_gridMainL = new zen::Grid( m_splitterMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); + m_gridMainL->SetScrollRate( 5, 5 ); + bSizer1781->Add( m_gridMainL, 1, wxEXPAND, 5 ); + + m_gridMainC = new zen::Grid( m_splitterMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); + m_gridMainC->SetScrollRate( 5, 5 ); + bSizer1781->Add( m_gridMainC, 0, wxEXPAND, 5 ); + + m_gridMainR = new zen::Grid( m_splitterMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); + m_gridMainR->SetScrollRate( 5, 5 ); + bSizer1781->Add( m_gridMainR, 1, wxEXPAND, 5 ); + + + m_splitterMain->SetSizer( bSizer1781 ); + m_splitterMain->Layout(); + bSizer1781->Fit( m_splitterMain ); + bSizer1711->Add( m_splitterMain, 1, wxEXPAND, 5 ); + + m_panelStatusBar = new wxPanel( m_panelCenter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSTATIC_BORDER|wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer451; + bSizer451 = new wxBoxSizer( wxHORIZONTAL ); + + bSizer451->SetMinSize( wxSize( -1,22 ) ); + bSizerFileStatus = new wxBoxSizer( wxHORIZONTAL ); + + bSizerStatusLeft = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer53; + bSizer53 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer53->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + bSizerStatusLeftDirectories = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapSmallDirectoryLeft = new wxStaticBitmap( m_panelStatusBar, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizerStatusLeftDirectories->Add( m_bitmapSmallDirectoryLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatusLeftDirectories->Add( 2, 0, 0, 0, 5 ); + + m_staticTextStatusLeftDirs = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextStatusLeftDirs->Wrap( -1 ); + bSizerStatusLeftDirectories->Add( m_staticTextStatusLeftDirs, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer53->Add( bSizerStatusLeftDirectories, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + bSizerStatusLeftFiles = new wxBoxSizer( wxHORIZONTAL ); + + + bSizerStatusLeftFiles->Add( 10, 0, 0, 0, 5 ); + + m_bitmapSmallFileLeft = new wxStaticBitmap( m_panelStatusBar, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizerStatusLeftFiles->Add( m_bitmapSmallFileLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatusLeftFiles->Add( 2, 0, 0, 0, 5 ); + + m_staticTextStatusLeftFiles = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextStatusLeftFiles->Wrap( -1 ); + bSizerStatusLeftFiles->Add( m_staticTextStatusLeftFiles, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatusLeftFiles->Add( 4, 0, 0, 0, 5 ); + + m_staticTextStatusLeftBytes = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextStatusLeftBytes->Wrap( -1 ); + bSizerStatusLeftFiles->Add( m_staticTextStatusLeftBytes, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer53->Add( bSizerStatusLeftFiles, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer53->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatusLeft->Add( bSizer53, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticline9 = new wxStaticLine( m_panelStatusBar, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizerStatusLeft->Add( m_staticline9, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxTOP, 2 ); + + + bSizerFileStatus->Add( bSizerStatusLeft, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerFileStatus->Add( 26, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextStatusMiddle = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextStatusMiddle->Wrap( -1 ); + bSizerFileStatus->Add( m_staticTextStatusMiddle, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerFileStatus->Add( 26, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + bSizerStatusRight = new wxBoxSizer( wxHORIZONTAL ); + + m_staticline10 = new wxStaticLine( m_panelStatusBar, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizerStatusRight->Add( m_staticline10, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP, 2 ); + + wxBoxSizer* bSizer52; + bSizer52 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer52->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + bSizerStatusRightDirectories = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapSmallDirectoryRight = new wxStaticBitmap( m_panelStatusBar, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizerStatusRightDirectories->Add( m_bitmapSmallDirectoryRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatusRightDirectories->Add( 2, 0, 0, 0, 5 ); + + m_staticTextStatusRightDirs = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextStatusRightDirs->Wrap( -1 ); + bSizerStatusRightDirectories->Add( m_staticTextStatusRightDirs, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer52->Add( bSizerStatusRightDirectories, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + bSizerStatusRightFiles = new wxBoxSizer( wxHORIZONTAL ); + + + bSizerStatusRightFiles->Add( 10, 0, 0, 0, 5 ); + + m_bitmapSmallFileRight = new wxStaticBitmap( m_panelStatusBar, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizerStatusRightFiles->Add( m_bitmapSmallFileRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatusRightFiles->Add( 2, 0, 0, 0, 5 ); + + m_staticTextStatusRightFiles = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextStatusRightFiles->Wrap( -1 ); + bSizerStatusRightFiles->Add( m_staticTextStatusRightFiles, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatusRightFiles->Add( 4, 0, 0, 0, 5 ); + + m_staticTextStatusRightBytes = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextStatusRightBytes->Wrap( -1 ); + bSizerStatusRightFiles->Add( m_staticTextStatusRightBytes, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer52->Add( bSizerStatusRightFiles, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer52->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatusRight->Add( bSizer52, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerFileStatus->Add( bSizerStatusRight, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer451->Add( bSizerFileStatus, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_staticTextFullStatus = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextFullStatus->Wrap( -1 ); + m_staticTextFullStatus->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer451->Add( m_staticTextFullStatus, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + + m_panelStatusBar->SetSizer( bSizer451 ); + m_panelStatusBar->Layout(); + bSizer451->Fit( m_panelStatusBar ); + bSizer1711->Add( m_panelStatusBar, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + + m_panelCenter->SetSizer( bSizer1711 ); + m_panelCenter->Layout(); + bSizer1711->Fit( m_panelCenter ); + bSizerPanelHolder->Add( m_panelCenter, 1, wxEXPAND, 5 ); + + m_panelSearch = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer1713; + bSizer1713 = new wxBoxSizer( wxHORIZONTAL ); + + m_bpButtonHideSearch = new wxBitmapButton( m_panelSearch, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonHideSearch->SetToolTip( _("Close search bar") ); + + bSizer1713->Add( m_bpButtonHideSearch, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_staticText101 = new wxStaticText( m_panelSearch, wxID_ANY, _("Find:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText101->Wrap( -1 ); + bSizer1713->Add( m_staticText101, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_textCtrlSearchTxt = new wxTextCtrl( m_panelSearch, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 220,-1 ), 0|wxWANTS_CHARS ); + m_textCtrlSearchTxt->SetMaxLength( 0 ); + bSizer1713->Add( m_textCtrlSearchTxt, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); + + m_checkBoxMatchCase = new wxCheckBox( m_panelSearch, wxID_ANY, _("Match case"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer1713->Add( m_checkBoxMatchCase, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + + m_panelSearch->SetSizer( bSizer1713 ); + m_panelSearch->Layout(); + bSizer1713->Fit( m_panelSearch ); + bSizerPanelHolder->Add( m_panelSearch, 0, 0, 5 ); + + m_panelConfig = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + bSizerConfig = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer151; + bSizer151 = new wxBoxSizer( wxHORIZONTAL ); + + m_bpButtonOpen = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonOpen->SetToolTip( _("dummy") ); + + bSizer151->Add( m_bpButtonOpen, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonSave = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonSave->SetToolTip( _("dummy") ); + + bSizer151->Add( m_bpButtonSave, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonBatchJob = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonBatchJob->SetToolTip( _("Save as batch job") ); + + bSizer151->Add( m_bpButtonBatchJob, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerConfig->Add( bSizer151, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_listBoxHistory = new wxListBox( m_panelConfig, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_EXTENDED|wxLB_NEEDED_SB ); + m_listBoxHistory->SetMinSize( wxSize( -1,40 ) ); + + bSizerConfig->Add( m_listBoxHistory, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + m_panelConfig->SetSizer( bSizerConfig ); + m_panelConfig->Layout(); + bSizerConfig->Fit( m_panelConfig ); + bSizerPanelHolder->Add( m_panelConfig, 0, 0, 5 ); + + m_panelFilter = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer171; + bSizer171 = new wxBoxSizer( wxHORIZONTAL ); + + m_bpButtonFilter = new wxBitmapButton( m_panelFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW|wxFULL_REPAINT_ON_RESIZE ); + bSizer171->Add( m_bpButtonFilter, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_checkBoxHideExcluded = new wxCheckBox( m_panelFilter, wxID_ANY, _("Hide excluded items"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxHideExcluded->SetToolTip( _("Show filtered or temporarily excluded files") ); + + bSizer171->Add( m_checkBoxHideExcluded, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + + m_panelFilter->SetSizer( bSizer171 ); + m_panelFilter->Layout(); + bSizer171->Fit( m_panelFilter ); + bSizerPanelHolder->Add( m_panelFilter, 0, 0, 5 ); + + m_panelStatistics = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + bSizer1801 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer1801->Add( 0, 0, 1, wxEXPAND, 5 ); + + bSizerStatistics = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer1712; + bSizer1712 = new wxBoxSizer( wxVERTICAL ); + + m_bitmapCreateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapCreateLeft->SetToolTip( _("Number of files and folders that will be created") ); + + bSizer1712->Add( m_bitmapCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer1712->Add( 5, 2, 0, 0, 5 ); + + + bSizer1712->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextCreateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextCreateLeft->Wrap( -1 ); + m_staticTextCreateLeft->SetToolTip( _("Number of files and folders that will be created") ); + + bSizer1712->Add( m_staticTextCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizerStatistics->Add( bSizer1712, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizerStatistics->Add( 5, 5, 0, 0, 5 ); + + wxBoxSizer* bSizer172; + bSizer172 = new wxBoxSizer( wxVERTICAL ); + + m_bitmapUpdateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapUpdateLeft->SetToolTip( _("Number of files that will be overwritten") ); + + bSizer172->Add( m_bitmapUpdateLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer172->Add( 5, 2, 0, 0, 5 ); + + + bSizer172->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextUpdateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextUpdateLeft->Wrap( -1 ); + m_staticTextUpdateLeft->SetToolTip( _("Number of files that will be overwritten") ); + + bSizer172->Add( m_staticTextUpdateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizerStatistics->Add( bSizer172, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizerStatistics->Add( 5, 5, 0, 0, 5 ); + + wxBoxSizer* bSizer173; + bSizer173 = new wxBoxSizer( wxVERTICAL ); + + m_bitmapDeleteLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") ); + + bSizer173->Add( m_bitmapDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer173->Add( 5, 2, 0, 0, 5 ); + + + bSizer173->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextDeleteLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDeleteLeft->Wrap( -1 ); + m_staticTextDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") ); + + bSizer173->Add( m_staticTextDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatistics->Add( bSizer173, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizerStatistics->Add( 5, 5, 0, 0, 5 ); + + bSizerData = new wxBoxSizer( wxVERTICAL ); + + m_bitmapData = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapData->SetToolTip( _("Total bytes to copy") ); + + bSizerData->Add( m_bitmapData, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizerData->Add( 5, 2, 0, 0, 5 ); + + + bSizerData->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextData = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextData->Wrap( -1 ); + m_staticTextData->SetToolTip( _("Total bytes to copy") ); + + bSizerData->Add( m_staticTextData, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatistics->Add( bSizerData, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizerStatistics->Add( 5, 5, 0, 0, 5 ); + + wxBoxSizer* bSizer176; + bSizer176 = new wxBoxSizer( wxVERTICAL ); + + m_bitmapDeleteRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") ); + + bSizer176->Add( m_bitmapDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer176->Add( 5, 2, 0, 0, 5 ); + + + bSizer176->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextDeleteRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDeleteRight->Wrap( -1 ); + m_staticTextDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") ); + + bSizer176->Add( m_staticTextDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatistics->Add( bSizer176, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizerStatistics->Add( 5, 5, 0, 0, 5 ); + + wxBoxSizer* bSizer177; + bSizer177 = new wxBoxSizer( wxVERTICAL ); + + m_bitmapUpdateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapUpdateRight->SetToolTip( _("Number of files that will be overwritten") ); + + bSizer177->Add( m_bitmapUpdateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer177->Add( 5, 2, 0, 0, 5 ); + + + bSizer177->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextUpdateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextUpdateRight->Wrap( -1 ); + m_staticTextUpdateRight->SetToolTip( _("Number of files that will be overwritten") ); + + bSizer177->Add( m_staticTextUpdateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatistics->Add( bSizer177, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizerStatistics->Add( 5, 5, 0, 0, 5 ); + + wxBoxSizer* bSizer178; + bSizer178 = new wxBoxSizer( wxVERTICAL ); + + m_bitmapCreateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapCreateRight->SetToolTip( _("Number of files and folders that will be created") ); + + bSizer178->Add( m_bitmapCreateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer178->Add( 5, 2, 0, 0, 5 ); + + + bSizer178->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextCreateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextCreateRight->Wrap( -1 ); + m_staticTextCreateRight->SetToolTip( _("Number of files and folders that will be created") ); + + bSizer178->Add( m_staticTextCreateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStatistics->Add( bSizer178, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer1801->Add( bSizerStatistics, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer1801->Add( 0, 0, 1, wxEXPAND, 5 ); + + + m_panelStatistics->SetSizer( bSizer1801 ); + m_panelStatistics->Layout(); + bSizer1801->Fit( m_panelStatistics ); + bSizerPanelHolder->Add( m_panelStatistics, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_panelViewFilter = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + bSizerViewFilter = new wxBoxSizer( wxHORIZONTAL ); + + m_bpButtonViewTypeSyncAction = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonViewTypeSyncAction, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizerViewFilter->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_bpButtonShowCreateLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowUpdateLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowUpdateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowDeleteLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowDeleteLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowLeftOnly = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowLeftOnly, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowLeftNewer = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowLeftNewer, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowEqual = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowEqual, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowDifferent = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowDifferent, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowDoNothing = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowDoNothing, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowRightNewer = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowRightNewer, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowRightOnly = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowRightOnly, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowDeleteRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowDeleteRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowUpdateRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowUpdateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowCreateRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowCreateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonShowConflict = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowConflict, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizerViewFilter->Add( 0, 0, 1, wxEXPAND, 5 ); + + + m_panelViewFilter->SetSizer( bSizerViewFilter ); + m_panelViewFilter->Layout(); + bSizerViewFilter->Fit( m_panelViewFilter ); + bSizerPanelHolder->Add( m_panelViewFilter, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + this->SetSizer( bSizerPanelHolder ); + this->Layout(); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( MainDialogGenerated::OnClose ) ); + this->Connect( m_menuItemNew->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigNew ) ); + this->Connect( m_menuItemLoad->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigLoad ) ); + this->Connect( m_menuItemSave->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigSave ) ); + this->Connect( m_menuItemSaveAs->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigSaveAs ) ); + this->Connect( m_menuItem7->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnSaveAsBatchJob ) ); + this->Connect( m_menuItem10->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnCompare ) ); + this->Connect( m_menuItem11->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnStartSync ) ); + this->Connect( m_menuItem4->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuQuit ) ); + this->Connect( m_menuItemGlobSett->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuGlobalSettings ) ); + this->Connect( m_menuItem15->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuFindItem ) ); + this->Connect( m_menuItem5->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuExportFileList ) ); + this->Connect( m_menuItemManual->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnShowHelp ) ); + this->Connect( m_menuItemCheckVersionNow->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuCheckVersion ) ); + this->Connect( m_menuItemCheckVersionAuto->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuCheckVersionAutomatically ) ); + this->Connect( m_menuItemAbout->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuAbout ) ); + m_buttonCompare->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnCompare ), NULL, this ); + m_bpButtonCmpConfig->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnCmpSettings ), NULL, this ); + m_bpButtonCmpConfig->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnCompSettingsContext ), NULL, this ); + m_bpButtonSyncConfig->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncSettings ), NULL, this ); + m_bpButtonSyncConfig->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnSyncSettingsContext ), NULL, this ); + m_buttonSync->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnStartSync ), NULL, this ); + m_bpButtonAddPair->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnAddFolderPair ), NULL, this ); + m_bpButtonRemovePair->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnRemoveTopFolderPair ), NULL, this ); + m_bpButtonSwapSides->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSwapSides ), NULL, this ); + m_bpButtonHideSearch->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnHideSearchPanel ), NULL, this ); + m_textCtrlSearchTxt->Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( MainDialogGenerated::OnSearchGridEnter ), NULL, this ); + m_bpButtonOpen->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigLoad ), NULL, this ); + m_bpButtonSave->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigSave ), NULL, this ); + m_bpButtonBatchJob->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSaveAsBatchJob ), NULL, this ); + m_listBoxHistory->Connect( wxEVT_CHAR, wxKeyEventHandler( MainDialogGenerated::OnCfgHistoryKeyEvent ), NULL, this ); + m_listBoxHistory->Connect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnLoadFromHistory ), NULL, this ); + m_listBoxHistory->Connect( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, wxCommandEventHandler( MainDialogGenerated::OnLoadFromHistoryDoubleClick ), NULL, this ); + m_listBoxHistory->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnCfgHistoryRightClick ), NULL, this ); + m_bpButtonFilter->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigureFilter ), NULL, this ); + m_bpButtonFilter->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnGlobalFilterContext ), NULL, this ); + m_checkBoxHideExcluded->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnShowExcluded ), NULL, this ); + m_bpButtonViewTypeSyncAction->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewType ), NULL, this ); + m_bpButtonShowCreateLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowCreateLeft->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowUpdateLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowUpdateLeft->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowDeleteLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowDeleteLeft->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowLeftOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowLeftOnly->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowLeftNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowLeftNewer->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowEqual->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowEqual->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowDifferent->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowDifferent->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowDoNothing->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowDoNothing->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowRightNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowRightNewer->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowRightOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowRightOnly->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowDeleteRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowDeleteRight->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowUpdateRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowUpdateRight->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowCreateRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowCreateRight->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowConflict->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowConflict->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); +} + +MainDialogGenerated::~MainDialogGenerated() +{ +} + +CmpCfgDlgGenerated::CmpCfgDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxDefaultSize, wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer136; + bSizer136 = new wxBoxSizer( wxVERTICAL ); + + m_panel36 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panel36->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer159; + bSizer159 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer182; + bSizer182 = new wxBoxSizer( wxVERTICAL ); + + m_staticText91 = new wxStaticText( m_panel36, wxID_ANY, _("Select a variant:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText91->Wrap( -1 ); + bSizer182->Add( m_staticText91, 0, wxALL, 5 ); + + wxFlexGridSizer* fgSizer16; + fgSizer16 = new wxFlexGridSizer( 2, 2, 5, 5 ); + fgSizer16->SetFlexibleDirection( wxBOTH ); + fgSizer16->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_bitmapByTime = new wxStaticBitmap( m_panel36, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapByTime->SetToolTip( _("Identify equal files by comparing modification time and size.") ); + + fgSizer16->Add( m_bitmapByTime, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_toggleBtnTimeSize = new wxToggleButton( m_panel36, wxID_ANY, _("File time and size"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_toggleBtnTimeSize->SetValue( true ); + m_toggleBtnTimeSize->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + m_toggleBtnTimeSize->SetToolTip( _("Identify equal files by comparing modification time and size.") ); + + fgSizer16->Add( m_toggleBtnTimeSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_bitmapByContent = new wxStaticBitmap( m_panel36, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapByContent->SetToolTip( _("Identify equal files by comparing the file content.") ); + + fgSizer16->Add( m_bitmapByContent, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_toggleBtnContent = new wxToggleButton( m_panel36, wxID_ANY, _("File content"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_toggleBtnContent->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + m_toggleBtnContent->SetToolTip( _("Identify equal files by comparing the file content.") ); + + fgSizer16->Add( m_toggleBtnContent, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer182->Add( fgSizer16, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer159->Add( bSizer182, 0, wxALL, 5 ); + + m_staticline33 = new wxStaticLine( m_panel36, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer159->Add( m_staticline33, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer172; + bSizer172 = new wxBoxSizer( wxVERTICAL ); + + m_staticText92 = new wxStaticText( m_panel36, wxID_ANY, _("Symbolic links:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText92->Wrap( -1 ); + bSizer172->Add( m_staticText92, 0, wxBOTTOM, 5 ); + + wxArrayString m_choiceHandleSymlinksChoices; + m_choiceHandleSymlinks = new wxChoice( m_panel36, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceHandleSymlinksChoices, 0 ); + m_choiceHandleSymlinks->SetSelection( -1 ); + bSizer172->Add( m_choiceHandleSymlinks, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_hyperlink24 = new wxHyperlinkCtrl( m_panel36, wxID_ANY, _("More information"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + bSizer172->Add( m_hyperlink24, 0, wxTOP, 5 ); + + + bSizer159->Add( bSizer172, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 10 ); + + + m_panel36->SetSizer( bSizer159 ); + m_panel36->Layout(); + bSizer159->Fit( m_panel36 ); + bSizer136->Add( m_panel36, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + m_staticline14 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer136->Add( m_staticline14, 0, wxEXPAND, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonOkay->SetDefault(); + m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizerStdButtons->Add( m_buttonOkay, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer136->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); + + + this->SetSizer( bSizer136 ); + this->Layout(); + bSizer136->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( CmpCfgDlgGenerated::OnClose ) ); + m_toggleBtnTimeSize->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( CmpCfgDlgGenerated::OnTimeSizeDouble ), NULL, this ); + m_toggleBtnTimeSize->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( CmpCfgDlgGenerated::OnTimeSize ), NULL, this ); + m_toggleBtnContent->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( CmpCfgDlgGenerated::OnContentDouble ), NULL, this ); + m_toggleBtnContent->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( CmpCfgDlgGenerated::OnContent ), NULL, this ); + m_choiceHandleSymlinks->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( CmpCfgDlgGenerated::OnChangeErrorHandling ), NULL, this ); + m_hyperlink24->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( CmpCfgDlgGenerated::OnHelpComparisonSettings ), NULL, this ); + m_buttonOkay->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( CmpCfgDlgGenerated::OnOkay ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( CmpCfgDlgGenerated::OnCancel ), NULL, this ); +} + +CmpCfgDlgGenerated::~CmpCfgDlgGenerated() +{ +} + +SyncCfgDlgGenerated::SyncCfgDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxDefaultSize, wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer7; + bSizer7 = new wxBoxSizer( wxVERTICAL ); + + m_panel37 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panel37->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer181; + bSizer181 = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer29; + bSizer29 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer183; + bSizer183 = new wxBoxSizer( wxVERTICAL ); + + m_staticText86 = new wxStaticText( m_panel37, wxID_ANY, _("Select a variant:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText86->Wrap( -1 ); + bSizer183->Add( m_staticText86, 0, wxALL, 5 ); + + wxFlexGridSizer* fgSizer1; + fgSizer1 = new wxFlexGridSizer( 4, 2, 5, 5 ); + fgSizer1->SetFlexibleDirection( wxBOTH ); + fgSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + wxBoxSizer* bSizer171; + bSizer171 = new wxBoxSizer( wxVERTICAL ); + + + bSizer171->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_toggleBtnTwoWay = new wxToggleButton( m_panel37, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_toggleBtnTwoWay->SetValue( true ); + m_toggleBtnTwoWay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer171->Add( m_toggleBtnTwoWay, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer171->Add( 0, 0, 1, wxEXPAND, 5 ); + + + fgSizer1->Add( bSizer171, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_staticTextAutomatic = new wxStaticText( m_panel37, wxID_ANY, _("Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database."), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextAutomatic->Wrap( 480 ); + fgSizer1->Add( m_staticTextAutomatic, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + wxBoxSizer* bSizer172; + bSizer172 = new wxBoxSizer( wxVERTICAL ); + + + bSizer172->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_toggleBtnMirror = new wxToggleButton( m_panel37, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_toggleBtnMirror->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer172->Add( m_toggleBtnMirror, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer172->Add( 0, 0, 1, wxEXPAND, 5 ); + + + fgSizer1->Add( bSizer172, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextMirror = new wxStaticText( m_panel37, wxID_ANY, _("Create a mirror backup of the left folder which exactly matches the right folder after synchronization."), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextMirror->Wrap( 480 ); + fgSizer1->Add( m_staticTextMirror, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + wxBoxSizer* bSizer173; + bSizer173 = new wxBoxSizer( wxVERTICAL ); + + + bSizer173->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_toggleBtnUpdate = new wxToggleButton( m_panel37, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_toggleBtnUpdate->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer173->Add( m_toggleBtnUpdate, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer173->Add( 0, 0, 1, wxEXPAND, 5 ); + + + fgSizer1->Add( bSizer173, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_staticTextUpdate = new wxStaticText( m_panel37, wxID_ANY, _("Copy new and updated files to the right folder."), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextUpdate->Wrap( 480 ); + fgSizer1->Add( m_staticTextUpdate, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + wxBoxSizer* bSizer1741; + bSizer1741 = new wxBoxSizer( wxVERTICAL ); + + + bSizer1741->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_toggleBtnCustom = new wxToggleButton( m_panel37, wxID_ANY, _("Custom"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_toggleBtnCustom->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer1741->Add( m_toggleBtnCustom, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer1741->Add( 0, 0, 1, wxEXPAND, 5 ); + + + fgSizer1->Add( bSizer1741, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextCustom = new wxStaticText( m_panel37, wxID_ANY, _("Configure your own synchronization rules."), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextCustom->Wrap( 480 ); + fgSizer1->Add( m_staticTextCustom, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer183->Add( fgSizer1, 0, wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizer1751; + bSizer1751 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer1751->Add( 8, 0, 0, 0, 5 ); + + m_checkBoxDetectMove = new wxCheckBox( m_panel37, wxID_ANY, _("Detect moved files"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxDetectMove->SetValue(true); + m_checkBoxDetectMove->SetToolTip( _("Requires database files. Not supported by all file systems.") ); + + bSizer1751->Add( m_checkBoxDetectMove, 1, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 ); + + + bSizer183->Add( bSizer1751, 0, wxEXPAND, 5 ); + + + bSizer29->Add( bSizer183, 0, wxALL, 5 ); + + m_staticline32 = new wxStaticLine( m_panel37, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer29->Add( m_staticline32, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer184; + bSizer184 = new wxBoxSizer( wxVERTICAL ); + + m_staticText87 = new wxStaticText( m_panel37, wxID_ANY, _("Delete files:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText87->Wrap( -1 ); + bSizer184->Add( m_staticText87, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + wxBoxSizer* bSizer180; + bSizer180 = new wxBoxSizer( wxHORIZONTAL ); + + m_toggleBtnPermanent = new wxToggleButton( m_panel37, wxID_ANY, _("Permanent"), wxDefaultPosition, wxDefaultSize, 0 ); + m_toggleBtnPermanent->SetToolTip( _("Delete or overwrite files permanently") ); + + bSizer180->Add( m_toggleBtnPermanent, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_toggleBtnRecycler = new wxToggleButton( m_panel37, wxID_ANY, _("Recycle bin"), wxDefaultPosition, wxDefaultSize, 0 ); + m_toggleBtnRecycler->SetToolTip( _("Back up deleted and overwritten files in the recycle bin") ); + + bSizer180->Add( m_toggleBtnRecycler, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 ); + + m_toggleBtnVersioning = new wxToggleButton( m_panel37, wxID_ANY, _("Versioning"), wxDefaultPosition, wxDefaultSize, 0 ); + m_toggleBtnVersioning->SetToolTip( _("Move files to a user-defined folder") ); + + bSizer180->Add( m_toggleBtnVersioning, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer184->Add( bSizer180, 0, wxTOP, 5 ); + + m_panelVersioning = new wxPanel( m_panel37, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelVersioning->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer191; + bSizer191 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer156; + bSizer156 = new wxBoxSizer( wxHORIZONTAL ); + + m_versioningFolder = new FolderHistoryBox( m_panelVersioning, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizer156->Add( m_versioningFolder, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonSelectDirVersioning = new wxButton( m_panelVersioning, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonSelectDirVersioning->SetToolTip( _("Select a folder") ); + + bSizer156->Add( m_buttonSelectDirVersioning, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer191->Add( bSizer156, 1, wxEXPAND|wxBOTTOM, 5 ); + + bSizer192 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText93 = new wxStaticText( m_panelVersioning, wxID_ANY, _("Naming convention:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText93->Wrap( -1 ); + bSizer192->Add( m_staticText93, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + wxArrayString m_choiceVersioningStyleChoices; + m_choiceVersioningStyle = new wxChoice( m_panelVersioning, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceVersioningStyleChoices, 0 ); + m_choiceVersioningStyle->SetSelection( 0 ); + bSizer192->Add( m_choiceVersioningStyle, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_staticTextNamingCvtPart1 = new wxStaticText( m_panelVersioning, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextNamingCvtPart1->Wrap( -1 ); + m_staticTextNamingCvtPart1->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); + + bSizer192->Add( m_staticTextNamingCvtPart1, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextNamingCvtPart2Bold = new wxStaticText( m_panelVersioning, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextNamingCvtPart2Bold->Wrap( -1 ); + m_staticTextNamingCvtPart2Bold->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + m_staticTextNamingCvtPart2Bold->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); + + bSizer192->Add( m_staticTextNamingCvtPart2Bold, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextNamingCvtPart3 = new wxStaticText( m_panelVersioning, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextNamingCvtPart3->Wrap( -1 ); + m_staticTextNamingCvtPart3->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); + + bSizer192->Add( m_staticTextNamingCvtPart3, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer192->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_hyperlink17 = new wxHyperlinkCtrl( m_panelVersioning, wxID_ANY, _("Show examples"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + bSizer192->Add( m_hyperlink17, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); + + + bSizer191->Add( bSizer192, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + m_panelVersioning->SetSizer( bSizer191 ); + m_panelVersioning->Layout(); + bSizer191->Fit( m_panelVersioning ); + bSizer184->Add( m_panelVersioning, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxTOP, 5 ); + + + bSizer29->Add( bSizer184, 0, wxALL|wxEXPAND, 10 ); + + bSizerExtraConfig = new wxBoxSizer( wxVERTICAL ); + + m_staticline321 = new wxStaticLine( m_panel37, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizerExtraConfig->Add( m_staticline321, 0, wxEXPAND, 5 ); + + bSizer179 = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer174; + bSizer174 = new wxBoxSizer( wxVERTICAL ); + + m_staticText88 = new wxStaticText( m_panel37, wxID_ANY, _("Handle errors:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText88->Wrap( -1 ); + bSizer174->Add( m_staticText88, 0, wxBOTTOM, 5 ); + + wxBoxSizer* bSizer175; + bSizer175 = new wxBoxSizer( wxHORIZONTAL ); + + m_toggleBtnErrorIgnore = new wxToggleButton( m_panel37, wxID_ANY, _("Ignore"), wxDefaultPosition, wxDefaultSize, 0 ); + m_toggleBtnErrorIgnore->SetToolTip( _("Hide all error and warning messages") ); + + bSizer175->Add( m_toggleBtnErrorIgnore, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 ); + + m_toggleBtnErrorPopup = new wxToggleButton( m_panel37, wxID_ANY, _("Pop-up"), wxDefaultPosition, wxDefaultSize, 0 ); + m_toggleBtnErrorPopup->SetToolTip( _("Show pop-up on errors or warnings") ); + + bSizer175->Add( m_toggleBtnErrorPopup, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer174->Add( bSizer175, 0, 0, 5 ); + + + bSizer179->Add( bSizer174, 0, wxALL, 10 ); + + m_staticline36 = new wxStaticLine( m_panel37, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer179->Add( m_staticline36, 0, wxEXPAND, 5 ); + + bSizerOnCompletion = new wxBoxSizer( wxVERTICAL ); + + m_staticText89 = new wxStaticText( m_panel37, wxID_ANY, _("On completion:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText89->Wrap( -1 ); + bSizerOnCompletion->Add( m_staticText89, 0, wxBOTTOM, 5 ); + + m_comboBoxExecFinished = new ExecFinishedBox( m_panel37, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizerOnCompletion->Add( m_comboBoxExecFinished, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer179->Add( bSizerOnCompletion, 1, wxALL, 10 ); + + + bSizerExtraConfig->Add( bSizer179, 0, wxEXPAND, 5 ); + + + bSizer29->Add( bSizerExtraConfig, 0, wxEXPAND, 5 ); + + + bSizer181->Add( bSizer29, 0, wxEXPAND, 5 ); + + m_staticline31 = new wxStaticLine( m_panel37, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer181->Add( m_staticline31, 0, wxEXPAND, 5 ); + + bSizerConfig = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer18011; + bSizer18011 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticTextHeaderCategory1 = new wxStaticText( m_panel37, wxID_ANY, _("Category"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT ); + m_staticTextHeaderCategory1->Wrap( -1 ); + bSizer18011->Add( m_staticTextHeaderCategory1, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer18011->Add( 5, 0, 0, 0, 5 ); + + m_staticTextHeaderAction1 = new wxStaticText( m_panel37, wxID_ANY, _("Action"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT ); + m_staticTextHeaderAction1->Wrap( -1 ); + bSizer18011->Add( m_staticTextHeaderAction1, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizerConfig->Add( bSizer18011, 0, wxEXPAND, 5 ); + + + bSizerConfig->Add( 0, 5, 0, 0, 5 ); + + m_bitmapDatabase = new wxStaticBitmap( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizerConfig->Add( m_bitmapDatabase, 0, wxALIGN_CENTER_HORIZONTAL|wxTOP, 10 ); + + wxBoxSizer* sbSizerKeepWidthStableIfSyncDirsNotShown; + sbSizerKeepWidthStableIfSyncDirsNotShown = new wxBoxSizer( wxHORIZONTAL ); + + + sbSizerKeepWidthStableIfSyncDirsNotShown->Add( 45, 0, 0, 0, 5 ); + + + sbSizerKeepWidthStableIfSyncDirsNotShown->Add( 5, 0, 0, 0, 5 ); + + + sbSizerKeepWidthStableIfSyncDirsNotShown->Add( 46, 0, 0, 0, 5 ); + + + bSizerConfig->Add( sbSizerKeepWidthStableIfSyncDirsNotShown, 0, 0, 5 ); + + sbSizerSyncDirections = new wxBoxSizer( wxVERTICAL ); + + bSizerLeftOnly = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapLeftOnly = new wxStaticBitmap( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapLeftOnly->SetToolTip( _("Item exists on left side only") ); + + bSizerLeftOnly->Add( m_bitmapLeftOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerLeftOnly->Add( 5, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonLeftOnly = new wxBitmapButton( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); + bSizerLeftOnly->Add( m_bpButtonLeftOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + sbSizerSyncDirections->Add( bSizerLeftOnly, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + + bSizerRightOnly = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapRightOnly = new wxStaticBitmap( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapRightOnly->SetToolTip( _("Item exists on right side only") ); + + bSizerRightOnly->Add( m_bitmapRightOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerRightOnly->Add( 5, 0, 0, 0, 5 ); + + m_bpButtonRightOnly = new wxBitmapButton( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); + bSizerRightOnly->Add( m_bpButtonRightOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + sbSizerSyncDirections->Add( bSizerRightOnly, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + + bSizerLeftNewer = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapLeftNewer = new wxStaticBitmap( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapLeftNewer->SetToolTip( _("Left side is newer") ); + + bSizerLeftNewer->Add( m_bitmapLeftNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerLeftNewer->Add( 5, 0, 0, 0, 5 ); + + m_bpButtonLeftNewer = new wxBitmapButton( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); + bSizerLeftNewer->Add( m_bpButtonLeftNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + sbSizerSyncDirections->Add( bSizerLeftNewer, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + + bSizerRightNewer = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapRightNewer = new wxStaticBitmap( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapRightNewer->SetToolTip( _("Right side is newer") ); + + bSizerRightNewer->Add( m_bitmapRightNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerRightNewer->Add( 5, 0, 0, 0, 5 ); + + m_bpButtonRightNewer = new wxBitmapButton( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); + bSizerRightNewer->Add( m_bpButtonRightNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + sbSizerSyncDirections->Add( bSizerRightNewer, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + + bSizerDifferent = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapDifferent = new wxStaticBitmap( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapDifferent->SetToolTip( _("Items have different content") ); + + bSizerDifferent->Add( m_bitmapDifferent, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerDifferent->Add( 5, 0, 0, 0, 5 ); + + m_bpButtonDifferent = new wxBitmapButton( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); + bSizerDifferent->Add( m_bpButtonDifferent, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + sbSizerSyncDirections->Add( bSizerDifferent, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + + bSizerConflict = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapConflict = new wxStaticBitmap( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapConflict->SetToolTip( _("Conflict/item cannot be categorized") ); + + bSizerConflict->Add( m_bitmapConflict, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerConflict->Add( 5, 0, 0, 0, 5 ); + + m_bpButtonConflict = new wxBitmapButton( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); + bSizerConflict->Add( m_bpButtonConflict, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + sbSizerSyncDirections->Add( bSizerConflict, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizerConfig->Add( sbSizerSyncDirections, 0, wxEXPAND, 5 ); + + + bSizer181->Add( bSizerConfig, 0, wxALL|wxEXPAND, 10 ); + + + m_panel37->SetSizer( bSizer181 ); + m_panel37->Layout(); + bSizer181->Fit( m_panel37 ); + bSizer7->Add( m_panel37, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + m_staticline15 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer7->Add( m_staticline15, 0, wxEXPAND, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonOK = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonOK->SetDefault(); + m_buttonOK->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizerStdButtons->Add( m_buttonOK, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer7->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); + + + this->SetSizer( bSizer7 ); + this->Layout(); + bSizer7->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SyncCfgDlgGenerated::OnClose ) ); + m_toggleBtnTwoWay->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( SyncCfgDlgGenerated::OnSyncTwoWayDouble ), NULL, this ); + m_toggleBtnTwoWay->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnSyncTwoWay ), NULL, this ); + m_toggleBtnMirror->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( SyncCfgDlgGenerated::OnSyncMirrorDouble ), NULL, this ); + m_toggleBtnMirror->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnSyncMirror ), NULL, this ); + m_toggleBtnUpdate->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( SyncCfgDlgGenerated::OnSyncUpdateDouble ), NULL, this ); + m_toggleBtnUpdate->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnSyncUpdate ), NULL, this ); + m_toggleBtnCustom->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( SyncCfgDlgGenerated::OnSyncCustomDouble ), NULL, this ); + m_toggleBtnCustom->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnSyncCustom ), NULL, this ); + m_checkBoxDetectMove->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnToggleDetectMovedFiles ), NULL, this ); + m_toggleBtnPermanent->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnDeletionPermanent ), NULL, this ); + m_toggleBtnRecycler->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnDeletionRecycler ), NULL, this ); + m_toggleBtnVersioning->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnDeletionVersioning ), NULL, this ); + m_choiceVersioningStyle->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( SyncCfgDlgGenerated::OnParameterChange ), NULL, this ); + m_hyperlink17->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( SyncCfgDlgGenerated::OnHelpVersioning ), NULL, this ); + m_toggleBtnErrorIgnore->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnErrorIgnore ), NULL, this ); + m_toggleBtnErrorPopup->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnErrorPopup ), NULL, this ); + m_bpButtonLeftOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnExLeftSideOnly ), NULL, this ); + m_bpButtonRightOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnExRightSideOnly ), NULL, this ); + m_bpButtonLeftNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnLeftNewer ), NULL, this ); + m_bpButtonRightNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnRightNewer ), NULL, this ); + m_bpButtonDifferent->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnDifferent ), NULL, this ); + m_bpButtonConflict->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnConflict ), NULL, this ); + m_buttonOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnOkay ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnCancel ), NULL, this ); +} + +SyncCfgDlgGenerated::~SyncCfgDlgGenerated() +{ +} + +SyncConfirmationDlgGenerated::SyncConfirmationDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxDefaultSize, wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer134; + bSizer134 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer72; + bSizer72 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapSync = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizer72->Add( m_bitmapSync, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); + + m_staticTextHeader = new wxStaticText( this, wxID_ANY, _("Start synchronization now?"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextHeader->Wrap( -1 ); + bSizer72->Add( m_staticTextHeader, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 10 ); + + + bSizer134->Add( bSizer72, 0, 0, 5 ); + + m_staticline371 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer134->Add( m_staticline371, 0, wxEXPAND, 5 ); + + m_panelStatistics = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelStatistics->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer185; + bSizer185 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer185->Add( 0, 0, 1, 0, 5 ); + + m_staticline38 = new wxStaticLine( m_panelStatistics, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer185->Add( m_staticline38, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer162; + bSizer162 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer182; + bSizer182 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText84 = new wxStaticText( m_panelStatistics, wxID_ANY, _("Variant:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText84->Wrap( -1 ); + bSizer182->Add( m_staticText84, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); + + + bSizer182->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticTextVariant = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextVariant->Wrap( -1 ); + m_staticTextVariant->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer182->Add( m_staticTextVariant, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + + bSizer182->Add( 0, 0, 1, wxEXPAND, 5 ); + + + bSizer162->Add( bSizer182, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxALL, 5 ); + + m_staticline14 = new wxStaticLine( m_panelStatistics, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer162->Add( m_staticline14, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer181; + bSizer181 = new wxBoxSizer( wxVERTICAL ); + + m_staticText83 = new wxStaticText( m_panelStatistics, wxID_ANY, _("Statistics"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText83->Wrap( -1 ); + bSizer181->Add( m_staticText83, 0, wxALL, 5 ); + + wxFlexGridSizer* fgSizer11; + fgSizer11 = new wxFlexGridSizer( 2, 7, 2, 5 ); + fgSizer11->SetFlexibleDirection( wxBOTH ); + fgSizer11->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_bitmapCreateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapCreateLeft->SetToolTip( _("Number of files and folders that will be created") ); + + fgSizer11->Add( m_bitmapCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bitmapUpdateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapUpdateLeft->SetToolTip( _("Number of files that will be overwritten") ); + + fgSizer11->Add( m_bitmapUpdateLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bitmapDeleteLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") ); + + fgSizer11->Add( m_bitmapDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bitmapData = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapData->SetToolTip( _("Total bytes to copy") ); + + fgSizer11->Add( m_bitmapData, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bitmapDeleteRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") ); + + fgSizer11->Add( m_bitmapDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bitmapUpdateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapUpdateRight->SetToolTip( _("Number of files that will be overwritten") ); + + fgSizer11->Add( m_bitmapUpdateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bitmapCreateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + m_bitmapCreateRight->SetToolTip( _("Number of files and folders that will be created") ); + + fgSizer11->Add( m_bitmapCreateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_staticTextCreateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextCreateLeft->Wrap( -1 ); + m_staticTextCreateLeft->SetToolTip( _("Number of files and folders that will be created") ); + + fgSizer11->Add( m_staticTextCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_staticTextUpdateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextUpdateLeft->Wrap( -1 ); + m_staticTextUpdateLeft->SetToolTip( _("Number of files that will be overwritten") ); + + fgSizer11->Add( m_staticTextUpdateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_staticTextDeleteLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDeleteLeft->Wrap( -1 ); + m_staticTextDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") ); + + fgSizer11->Add( m_staticTextDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextData = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextData->Wrap( -1 ); + m_staticTextData->SetToolTip( _("Total bytes to copy") ); + + fgSizer11->Add( m_staticTextData, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextDeleteRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDeleteRight->Wrap( -1 ); + m_staticTextDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") ); + + fgSizer11->Add( m_staticTextDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextUpdateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextUpdateRight->Wrap( -1 ); + m_staticTextUpdateRight->SetToolTip( _("Number of files that will be overwritten") ); + + fgSizer11->Add( m_staticTextUpdateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextCreateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextCreateRight->Wrap( -1 ); + m_staticTextCreateRight->SetToolTip( _("Number of files and folders that will be created") ); + + fgSizer11->Add( m_staticTextCreateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer181->Add( fgSizer11, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer162->Add( bSizer181, 0, wxEXPAND|wxALL, 5 ); + + + bSizer185->Add( bSizer162, 0, 0, 5 ); + + + m_panelStatistics->SetSizer( bSizer185 ); + m_panelStatistics->Layout(); + bSizer185->Fit( m_panelStatistics ); + bSizer134->Add( m_panelStatistics, 0, wxEXPAND, 5 ); + + m_staticline12 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer134->Add( m_staticline12, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer164; + bSizer164 = new wxBoxSizer( wxVERTICAL ); + + m_checkBoxDontShowAgain = new wxCheckBox( this, wxID_ANY, _("&Don't show this dialog again"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer164->Add( m_checkBoxDontShowAgain, 0, wxALIGN_CENTER_HORIZONTAL|wxTOP|wxRIGHT|wxLEFT, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonStartSync = new wxButton( this, wxID_OK, _("&Start"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonStartSync->SetDefault(); + m_buttonStartSync->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizerStdButtons->Add( m_buttonStartSync, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer164->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); + + + bSizer134->Add( bSizer164, 1, wxEXPAND, 5 ); + + + this->SetSizer( bSizer134 ); + this->Layout(); + bSizer134->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SyncConfirmationDlgGenerated::OnClose ) ); + m_buttonStartSync->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncConfirmationDlgGenerated::OnStartSync ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncConfirmationDlgGenerated::OnCancel ), NULL, this ); +} + +SyncConfirmationDlgGenerated::~SyncConfirmationDlgGenerated() +{ +} + +FolderPairPanelGenerated::FolderPairPanelGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) +{ + wxBoxSizer* bSizer74; + bSizer74 = new wxBoxSizer( wxHORIZONTAL ); + + m_panelLeft = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelLeft->SetMinSize( wxSize( 1,-1 ) ); + + wxBoxSizer* bSizer134; + bSizer134 = new wxBoxSizer( wxHORIZONTAL ); + + m_bpButtonRemovePair = new wxBitmapButton( m_panelLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonRemovePair->SetToolTip( _("Remove folder pair") ); + + bSizer134->Add( m_bpButtonRemovePair, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_directoryLeft = new FolderHistoryBox( m_panelLeft, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizer134->Add( m_directoryLeft, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonSelectDirLeft = new wxButton( m_panelLeft, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonSelectDirLeft->SetToolTip( _("Select a folder") ); + + bSizer134->Add( m_buttonSelectDirLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + m_panelLeft->SetSizer( bSizer134 ); + m_panelLeft->Layout(); + bSizer134->Fit( m_panelLeft ); + bSizer74->Add( m_panelLeft, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxEXPAND, 5 ); + + m_panel20 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer95; + bSizer95 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer95->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_bpButtonAltCompCfg = new wxBitmapButton( m_panel20, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + bSizer95->Add( m_bpButtonAltCompCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonLocalFilter = new wxBitmapButton( m_panel20, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + bSizer95->Add( m_bpButtonLocalFilter, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 2 ); + + m_bpButtonAltSyncCfg = new wxBitmapButton( m_panel20, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + bSizer95->Add( m_bpButtonAltSyncCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer95->Add( 0, 0, 1, wxEXPAND, 5 ); + + + m_panel20->SetSizer( bSizer95 ); + m_panel20->Layout(); + bSizer95->Fit( m_panel20 ); + bSizer74->Add( m_panel20, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT|wxEXPAND, 5 ); + + m_panelRight = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelRight->SetMinSize( wxSize( 1,-1 ) ); + + wxBoxSizer* bSizer135; + bSizer135 = new wxBoxSizer( wxHORIZONTAL ); + + m_directoryRight = new FolderHistoryBox( m_panelRight, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizer135->Add( m_directoryRight, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonSelectDirRight = new wxButton( m_panelRight, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonSelectDirRight->SetToolTip( _("Select a folder") ); + + bSizer135->Add( m_buttonSelectDirRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + m_panelRight->SetSizer( bSizer135 ); + m_panelRight->Layout(); + bSizer135->Fit( m_panelRight ); + bSizer74->Add( m_panelRight, 1, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxEXPAND, 5 ); + + + this->SetSizer( bSizer74 ); + this->Layout(); + bSizer74->Fit( this ); +} + +FolderPairPanelGenerated::~FolderPairPanelGenerated() +{ +} + +CompareProgressDlgGenerated::CompareProgressDlgGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) +{ + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer40; + bSizer40 = new wxBoxSizer( wxVERTICAL ); + + + bSizer40->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_textCtrlStatus = new wxTextCtrl( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, wxTE_READONLY ); + m_textCtrlStatus->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + bSizer40->Add( m_textCtrlStatus, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); + + m_gauge2 = new wxGauge( this, wxID_ANY, 100, wxDefaultPosition, wxSize( -1,14 ), wxGA_HORIZONTAL|wxGA_SMOOTH ); + bSizer40->Add( m_gauge2, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 ); + + bSizer42 = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer162StretchSpeedAndRemTimeIndependently; + bSizer162StretchSpeedAndRemTimeIndependently = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer157; + bSizer157 = new wxBoxSizer( wxVERTICAL ); + + bSizerFilesFound = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText321 = new wxStaticText( this, wxID_ANY, _("Items found:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText321->Wrap( -1 ); + bSizerFilesFound->Add( m_staticText321, 0, wxALIGN_BOTTOM, 5 ); + + m_staticTextScanned = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextScanned->Wrap( -1 ); + m_staticTextScanned->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizerFilesFound->Add( m_staticTextScanned, 0, wxALIGN_BOTTOM|wxLEFT, 5 ); + + + bSizer157->Add( bSizerFilesFound, 0, 0, 5 ); + + bSizerFilesRemaining = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText46 = new wxStaticText( this, wxID_ANY, _("Items remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText46->Wrap( -1 ); + bSizerFilesRemaining->Add( m_staticText46, 0, wxALIGN_BOTTOM, 5 ); + + wxBoxSizer* bSizer154; + bSizer154 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticTextFilesRemaining = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextFilesRemaining->Wrap( -1 ); + m_staticTextFilesRemaining->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer154->Add( m_staticTextFilesRemaining, 0, wxALIGN_BOTTOM, 5 ); + + m_staticTextDataRemaining = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDataRemaining->Wrap( -1 ); + bSizer154->Add( m_staticTextDataRemaining, 0, wxALIGN_BOTTOM|wxLEFT, 5 ); + + + bSizerFilesRemaining->Add( bSizer154, 0, wxALIGN_BOTTOM|wxLEFT, 5 ); + + + bSizer157->Add( bSizerFilesRemaining, 0, 0, 5 ); + + + bSizer162StretchSpeedAndRemTimeIndependently->Add( bSizer157, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer162StretchSpeedAndRemTimeIndependently->Add( 0, 0, 1, wxEXPAND, 5 ); + + sSizerSpeed = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText104 = new wxStaticText( this, wxID_ANY, _("Speed:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText104->Wrap( -1 ); + sSizerSpeed->Add( m_staticText104, 0, wxALIGN_BOTTOM, 5 ); + + m_staticTextSpeed = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextSpeed->Wrap( -1 ); + m_staticTextSpeed->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + sSizerSpeed->Add( m_staticTextSpeed, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); + + + bSizer162StretchSpeedAndRemTimeIndependently->Add( sSizerSpeed, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer42->Add( bSizer162StretchSpeedAndRemTimeIndependently, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer42->Add( 10, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + wxBoxSizer* bSizer163; + bSizer163 = new wxBoxSizer( wxHORIZONTAL ); + + sSizerTimeRemaining = new wxBoxSizer( wxHORIZONTAL ); + + m_staticTextTimeRemFixed = new wxStaticText( this, wxID_ANY, _("Time remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextTimeRemFixed->Wrap( -1 ); + sSizerTimeRemaining->Add( m_staticTextTimeRemFixed, 0, wxALIGN_BOTTOM, 5 ); + + m_staticTextRemTime = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextRemTime->Wrap( -1 ); + m_staticTextRemTime->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + sSizerTimeRemaining->Add( m_staticTextRemTime, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); + + + bSizer163->Add( sSizerTimeRemaining, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer163->Add( 0, 0, 1, wxEXPAND, 5 ); + + sSizerTimeElapsed = new wxBoxSizer( wxHORIZONTAL ); + + wxStaticText* m_staticText37; + m_staticText37 = new wxStaticText( this, wxID_ANY, _("Time elapsed:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText37->Wrap( -1 ); + sSizerTimeElapsed->Add( m_staticText37, 0, wxALIGN_BOTTOM, 5 ); + + m_staticTextTimeElapsed = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextTimeElapsed->Wrap( -1 ); + m_staticTextTimeElapsed->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + sSizerTimeElapsed->Add( m_staticTextTimeElapsed, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); + + + bSizer163->Add( sSizerTimeElapsed, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer42->Add( bSizer163, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer40->Add( bSizer42, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 ); + + + bSizer40->Add( 0, 0, 1, wxEXPAND, 5 ); + + + this->SetSizer( bSizer40 ); + this->Layout(); + bSizer40->Fit( this ); +} + +CompareProgressDlgGenerated::~CompareProgressDlgGenerated() +{ +} + +SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) +{ + bSizerRoot = new wxBoxSizer( wxVERTICAL ); + + bSizer42 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer42->Add( 32, 0, 0, 0, 5 ); + + + bSizer42->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_bitmapStatus = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32,32 ), 0 ); + bSizer42->Add( m_bitmapStatus, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); + + m_staticTextPhase = new wxStaticText( this, wxID_ANY, _("Synchronizing..."), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextPhase->Wrap( -1 ); + m_staticTextPhase->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer42->Add( m_staticTextPhase, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 ); + + m_animCtrlSyncing = new wxAnimationCtrl( this, wxID_ANY, wxNullAnimation, wxDefaultPosition, wxSize( 32,32 ), wxAC_DEFAULT_STYLE ); + bSizer42->Add( m_animCtrlSyncing, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); + + + bSizer42->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_bpButtonMinimizeToTray = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32,32 ), wxBU_AUTODRAW ); + m_bpButtonMinimizeToTray->SetToolTip( _("Minimize to notification area") ); + + bSizer42->Add( m_bpButtonMinimizeToTray, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerRoot->Add( bSizer42, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT|wxEXPAND, 5 ); + + bSizerStatusText = new wxBoxSizer( wxVERTICAL ); + + m_staticTextStatus = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextStatus->Wrap( -1 ); + bSizerStatusText->Add( m_staticTextStatus, 0, wxEXPAND|wxLEFT, 10 ); + + + bSizerStatusText->Add( 0, 5, 0, 0, 5 ); + + + bSizerRoot->Add( bSizerStatusText, 0, wxEXPAND, 5 ); + + wxStaticLine* m_staticlineHeader; + m_staticlineHeader = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizerRoot->Add( m_staticlineHeader, 0, wxEXPAND, 5 ); + + m_panelProgress = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelProgress->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer173; + bSizer173 = new wxBoxSizer( wxVERTICAL ); + + bSizer171 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer171->Add( 10, 0, 0, 0, 5 ); + + wxBoxSizer* bSizer164; + bSizer164 = new wxBoxSizer( wxVERTICAL ); + + m_panelItemsProcessed = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + m_panelItemsProcessed->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer165; + bSizer165 = new wxBoxSizer( wxVERTICAL ); + + + bSizer165->Add( 0, 5, 0, 0, 5 ); + + wxStaticText* m_staticText96; + m_staticText96 = new wxStaticText( m_panelItemsProcessed, wxID_ANY, _("Items processed:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText96->Wrap( -1 ); + bSizer165->Add( m_staticText96, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizer169; + bSizer169 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticTextProcessedObj = new wxStaticText( m_panelItemsProcessed, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_staticTextProcessedObj->Wrap( -1 ); + m_staticTextProcessedObj->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer169->Add( m_staticTextProcessedObj, 0, wxALIGN_BOTTOM, 5 ); + + m_staticTextDataProcessed = new wxStaticText( m_panelItemsProcessed, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDataProcessed->Wrap( -1 ); + bSizer169->Add( m_staticTextDataProcessed, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); + + + bSizer165->Add( bSizer169, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer165->Add( 0, 5, 0, 0, 5 ); + + + m_panelItemsProcessed->SetSizer( bSizer165 ); + m_panelItemsProcessed->Layout(); + bSizer165->Fit( m_panelItemsProcessed ); + bSizer164->Add( m_panelItemsProcessed, 0, wxEXPAND|wxTOP, 7 ); + + m_panelItemsRemaining = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + m_panelItemsRemaining->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer166; + bSizer166 = new wxBoxSizer( wxVERTICAL ); + + + bSizer166->Add( 0, 5, 0, 0, 5 ); + + wxStaticText* m_staticText97; + m_staticText97 = new wxStaticText( m_panelItemsRemaining, wxID_ANY, _("Items remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText97->Wrap( -1 ); + bSizer166->Add( m_staticText97, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizer170; + bSizer170 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticTextRemainingObj = new wxStaticText( m_panelItemsRemaining, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_staticTextRemainingObj->Wrap( -1 ); + m_staticTextRemainingObj->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer170->Add( m_staticTextRemainingObj, 0, wxALIGN_BOTTOM, 5 ); + + m_staticTextDataRemaining = new wxStaticText( m_panelItemsRemaining, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDataRemaining->Wrap( -1 ); + bSizer170->Add( m_staticTextDataRemaining, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); + + + bSizer166->Add( bSizer170, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer166->Add( 0, 5, 0, 0, 5 ); + + + m_panelItemsRemaining->SetSizer( bSizer166 ); + m_panelItemsRemaining->Layout(); + bSizer166->Fit( m_panelItemsRemaining ); + bSizer164->Add( m_panelItemsRemaining, 0, wxTOP|wxEXPAND, 7 ); + + m_panelTimeRemaining = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + m_panelTimeRemaining->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer167; + bSizer167 = new wxBoxSizer( wxVERTICAL ); + + + bSizer167->Add( 0, 5, 0, 0, 5 ); + + wxStaticText* m_staticText98; + m_staticText98 = new wxStaticText( m_panelTimeRemaining, wxID_ANY, _("Time remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText98->Wrap( -1 ); + bSizer167->Add( m_staticText98, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); + + m_staticTextRemTime = new wxStaticText( m_panelTimeRemaining, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextRemTime->Wrap( -1 ); + m_staticTextRemTime->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer167->Add( m_staticTextRemTime, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer167->Add( 0, 5, 0, 0, 5 ); + + + m_panelTimeRemaining->SetSizer( bSizer167 ); + m_panelTimeRemaining->Layout(); + bSizer167->Fit( m_panelTimeRemaining ); + bSizer164->Add( m_panelTimeRemaining, 0, wxTOP|wxEXPAND, 7 ); + + wxPanel* m_panelTimeElapsed; + m_panelTimeElapsed = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + m_panelTimeElapsed->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer168; + bSizer168 = new wxBoxSizer( wxVERTICAL ); + + + bSizer168->Add( 0, 5, 0, 0, 5 ); + + wxStaticText* m_staticText961; + m_staticText961 = new wxStaticText( m_panelTimeElapsed, wxID_ANY, _("Time elapsed:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText961->Wrap( -1 ); + bSizer168->Add( m_staticText961, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); + + m_staticTextTimeElapsed = new wxStaticText( m_panelTimeElapsed, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextTimeElapsed->Wrap( -1 ); + m_staticTextTimeElapsed->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer168->Add( m_staticTextTimeElapsed, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer168->Add( 0, 5, 0, 0, 5 ); + + + m_panelTimeElapsed->SetSizer( bSizer168 ); + m_panelTimeElapsed->Layout(); + bSizer168->Fit( m_panelTimeElapsed ); + bSizer164->Add( m_panelTimeElapsed, 0, wxTOP|wxEXPAND, 7 ); + + + bSizer171->Add( bSizer164, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer171->Add( 10, 0, 0, 0, 5 ); + + wxBoxSizer* bSizer161; + bSizer161 = new wxBoxSizer( wxVERTICAL ); + + + bSizer161->Add( 0, 15, 0, 0, 5 ); + + m_panelGraphBytes = new zen::Graph2D( m_panelProgress, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_panelGraphBytes->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + bSizer161->Add( m_panelGraphBytes, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_panelGraphItems = new zen::Graph2D( m_panelProgress, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_panelGraphItems->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + bSizer161->Add( m_panelGraphItems, 1, wxEXPAND, 5 ); + + + bSizer161->Add( 430, 0, 0, 0, 5 ); + + + bSizer171->Add( bSizer161, 1, wxEXPAND, 5 ); + + + bSizer171->Add( 0, 230, 0, 0, 5 ); + + + bSizer173->Add( bSizer171, 1, wxEXPAND, 5 ); + + + m_panelProgress->SetSizer( bSizer173 ); + m_panelProgress->Layout(); + bSizer173->Fit( m_panelProgress ); + bSizerRoot->Add( m_panelProgress, 1, wxEXPAND, 5 ); + + m_notebookResult = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_FIXEDWIDTH ); + + bSizerRoot->Add( m_notebookResult, 1, wxEXPAND, 5 ); + + m_staticlineFooter = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizerRoot->Add( m_staticlineFooter, 0, wxEXPAND, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer160; + bSizer160 = new wxBoxSizer( wxHORIZONTAL ); + + bSizerExecFinished = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText87 = new wxStaticText( this, wxID_ANY, _("On completion:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText87->Wrap( -1 ); + bSizerExecFinished->Add( m_staticText87, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_comboBoxExecFinished = new ExecFinishedBox( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizerExecFinished->Add( m_comboBoxExecFinished, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer160->Add( bSizerExecFinished, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer160->Add( 0, 0, 0, 0, 5 ); + + + bSizerStdButtons->Add( bSizer160, 1, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); + + m_buttonClose = new wxButton( this, wxID_OK, _("Close"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonClose->SetDefault(); + m_buttonClose->Enable( false ); + + bSizerStdButtons->Add( m_buttonClose, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_buttonPause = new wxButton( this, wxID_ANY, _("&Pause"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + bSizerStdButtons->Add( m_buttonPause, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + m_buttonStop = new wxButton( this, wxID_CANCEL, _("Stop"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + bSizerStdButtons->Add( m_buttonStop, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizerRoot->Add( bSizerStdButtons, 0, wxALIGN_RIGHT|wxEXPAND, 5 ); + + + this->SetSizer( bSizerRoot ); + this->Layout(); + bSizerRoot->Fit( this ); +} + +SyncProgressPanelGenerated::~SyncProgressPanelGenerated() +{ +} + +LogPanelGenerated::LogPanelGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) +{ + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer179; + bSizer179 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer153; + bSizer153 = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer154; + bSizer154 = new wxBoxSizer( wxVERTICAL ); + + m_bpButtonErrors = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49,49 ), wxBU_AUTODRAW ); + bSizer154->Add( m_bpButtonErrors, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonWarnings = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49,49 ), wxBU_AUTODRAW ); + bSizer154->Add( m_bpButtonWarnings, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_bpButtonInfo = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49,49 ), wxBU_AUTODRAW ); + bSizer154->Add( m_bpButtonInfo, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer153->Add( bSizer154, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); + + m_staticline13 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer153->Add( m_staticline13, 0, wxEXPAND, 5 ); + + m_gridMessages = new zen::Grid( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); + m_gridMessages->SetScrollRate( 5, 5 ); + bSizer153->Add( m_gridMessages, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer179->Add( bSizer153, 1, wxEXPAND, 5 ); + + + this->SetSizer( bSizer179 ); + this->Layout(); + bSizer179->Fit( this ); + + // Connect Events + m_bpButtonErrors->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( LogPanelGenerated::OnErrors ), NULL, this ); + m_bpButtonWarnings->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( LogPanelGenerated::OnWarnings ), NULL, this ); + m_bpButtonInfo->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( LogPanelGenerated::OnInfo ), NULL, this ); +} + +LogPanelGenerated::~LogPanelGenerated() +{ +} + +BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer54; + bSizer54 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer72; + bSizer72 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapBatchJob = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizer72->Add( m_bitmapBatchJob, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); + + m_staticTextDescr = new wxStaticText( this, wxID_ANY, _("Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDescr->Wrap( 520 ); + bSizer72->Add( m_staticTextDescr, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 10 ); + + + bSizer54->Add( bSizer72, 0, 0, 5 ); + + m_staticline18 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer54->Add( m_staticline18, 0, wxEXPAND, 5 ); + + m_panel35 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panel35->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer172; + bSizer172 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer180; + bSizer180 = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer171; + bSizer171 = new wxBoxSizer( wxVERTICAL ); + + m_staticText82 = new wxStaticText( m_panel35, wxID_ANY, _("Handle errors:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText82->Wrap( -1 ); + bSizer171->Add( m_staticText82, 0, wxALL, 5 ); + + wxBoxSizer* bSizer169; + bSizer169 = new wxBoxSizer( wxHORIZONTAL ); + + m_toggleBtnErrorIgnore = new wxToggleButton( m_panel35, wxID_ANY, _("Ignore"), wxDefaultPosition, wxDefaultSize, 0 ); + m_toggleBtnErrorIgnore->SetToolTip( _("Hide all error and warning messages") ); + + bSizer169->Add( m_toggleBtnErrorIgnore, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 ); + + m_toggleBtnErrorPopup = new wxToggleButton( m_panel35, wxID_ANY, _("Pop-up"), wxDefaultPosition, wxDefaultSize, 0 ); + m_toggleBtnErrorPopup->SetToolTip( _("Show pop-up on errors or warnings") ); + + bSizer169->Add( m_toggleBtnErrorPopup, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_toggleBtnErrorStop = new wxToggleButton( m_panel35, wxID_ANY, _("Stop"), wxDefaultPosition, wxDefaultSize, 0 ); + m_toggleBtnErrorStop->SetToolTip( _("Stop synchronization at first error") ); + + bSizer169->Add( m_toggleBtnErrorStop, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer171->Add( bSizer169, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer180->Add( bSizer171, 0, wxALL, 5 ); + + m_staticline26 = new wxStaticLine( m_panel35, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer180->Add( m_staticline26, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer170; + bSizer170 = new wxBoxSizer( wxVERTICAL ); + + m_checkBoxShowProgress = new wxCheckBox( m_panel35, wxID_ANY, _("Show progress dialog"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer170->Add( m_checkBoxShowProgress, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxALL, 5 ); + + wxBoxSizer* bSizer179; + bSizer179 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText81 = new wxStaticText( m_panel35, wxID_ANY, _("On completion:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText81->Wrap( -1 ); + bSizer179->Add( m_staticText81, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_comboBoxExecFinished = new ExecFinishedBox( m_panel35, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizer179->Add( m_comboBoxExecFinished, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer170->Add( bSizer179, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer180->Add( bSizer170, 1, wxALL, 5 ); + + + bSizer172->Add( bSizer180, 0, wxEXPAND, 5 ); + + m_staticline25 = new wxStaticLine( m_panel35, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer172->Add( m_staticline25, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer191; + bSizer191 = new wxBoxSizer( wxVERTICAL ); + + m_checkBoxGenerateLogfile = new wxCheckBox( m_panel35, wxID_ANY, _("Save log:"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer191->Add( m_checkBoxGenerateLogfile, 0, wxEXPAND|wxBOTTOM, 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_logfileDir = new FolderHistoryBox( m_panelLogfile, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizer1721->Add( m_logfileDir, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonSelectLogfileDir = new wxButton( m_panelLogfile, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonSelectLogfileDir->SetToolTip( _("Select a folder") ); + + bSizer1721->Add( m_buttonSelectLogfileDir, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_checkBoxLogfilesLimit = new wxCheckBox( m_panelLogfile, wxID_ANY, _("Limit:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxLogfilesLimit->SetToolTip( _("Limit maximum number of log files") ); + + bSizer1721->Add( m_checkBoxLogfilesLimit, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); + + m_spinCtrlLogfileLimit = new wxSpinCtrl( m_panelLogfile, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 60,-1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 ); + m_spinCtrlLogfileLimit->SetToolTip( _("Limit maximum number of log files") ); + + bSizer1721->Add( m_spinCtrlLogfileLimit, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + m_panelLogfile->SetSizer( bSizer1721 ); + m_panelLogfile->Layout(); + bSizer1721->Fit( m_panelLogfile ); + bSizer191->Add( m_panelLogfile, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer172->Add( bSizer191, 0, wxEXPAND|wxALL, 10 ); + + 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, wxALIGN_CENTER_VERTICAL|wxALL, 10 ); + + + m_panel35->SetSizer( bSizer172 ); + m_panel35->Layout(); + bSizer172->Fit( m_panel35 ); + bSizer54->Add( m_panel35, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + m_staticline13 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer54->Add( m_staticline13, 0, wxEXPAND, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + + bSizerStdButtons->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_buttonSaveAs = new wxButton( this, wxID_SAVE, _("Save &as..."), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonSaveAs->SetDefault(); + m_buttonSaveAs->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizerStdButtons->Add( m_buttonSaveAs, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer54->Add( bSizerStdButtons, 0, wxALIGN_RIGHT|wxEXPAND, 5 ); + + + this->SetSizer( bSizer54 ); + this->Layout(); + bSizer54->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( BatchDlgGenerated::OnClose ) ); + m_toggleBtnErrorIgnore->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnErrorIgnore ), NULL, this ); + m_toggleBtnErrorPopup->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnErrorPopup ), NULL, this ); + m_toggleBtnErrorStop->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnErrorStop ), NULL, this ); + m_checkBoxGenerateLogfile->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnToggleGenerateLogfile ), NULL, this ); + m_checkBoxLogfilesLimit->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnToggleLogfilesLimit ), NULL, this ); + 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 ); +} + +BatchDlgGenerated::~BatchDlgGenerated() +{ +} + +DeleteDlgGenerated::DeleteDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer24; + bSizer24 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer72; + bSizer72 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapDeleteType = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizer72->Add( m_bitmapDeleteType, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); + + m_staticTextHeader = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0|wxNO_BORDER ); + m_staticTextHeader->Wrap( -1 ); + bSizer72->Add( m_staticTextHeader, 0, wxALIGN_CENTER_VERTICAL|wxALL, 10 ); + + + bSizer24->Add( bSizer72, 0, 0, 5 ); + + m_staticline91 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer24->Add( m_staticline91, 0, wxEXPAND, 5 ); + + m_panel31 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panel31->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer185; + bSizer185 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer185->Add( 60, 0, 0, 0, 5 ); + + m_staticline42 = new wxStaticLine( m_panel31, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer185->Add( m_staticline42, 0, wxEXPAND, 5 ); + + m_textCtrlFileList = new wxTextCtrl( m_panel31, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 500,200 ), wxTE_DONTWRAP|wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER ); + bSizer185->Add( m_textCtrlFileList, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + m_panel31->SetSizer( bSizer185 ); + m_panel31->Layout(); + bSizer185->Fit( m_panel31 ); + bSizer24->Add( m_panel31, 1, wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_staticline9 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer24->Add( m_staticline9, 0, wxEXPAND, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer99; + bSizer99 = new wxBoxSizer( wxVERTICAL ); + + m_checkBoxUseRecycler = new wxCheckBox( this, wxID_ANY, _("&Recycle bin"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer99->Add( m_checkBoxUseRecycler, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxTOP|wxBOTTOM|wxLEFT, 5 ); + + m_checkBoxDeleteBothSides = new wxCheckBox( this, wxID_ANY, _("Delete on both sides"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxDeleteBothSides->Hide(); + m_checkBoxDeleteBothSides->SetToolTip( _("Delete on both sides even if the file is selected on one side only") ); + + bSizer99->Add( m_checkBoxDeleteBothSides, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxBOTTOM|wxLEFT, 5 ); + + + bSizerStdButtons->Add( bSizer99, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonOK = new wxButton( this, wxID_OK, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonOK->SetDefault(); + m_buttonOK->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizerStdButtons->Add( m_buttonOK, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer24->Add( bSizerStdButtons, 0, wxEXPAND, 5 ); + + + this->SetSizer( bSizer24 ); + this->Layout(); + bSizer24->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DeleteDlgGenerated::OnClose ) ); + m_checkBoxUseRecycler->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DeleteDlgGenerated::OnUseRecycler ), NULL, this ); + m_checkBoxDeleteBothSides->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DeleteDlgGenerated::OnDelOnBothSides ), NULL, this ); + m_buttonOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DeleteDlgGenerated::OnOK ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DeleteDlgGenerated::OnCancel ), NULL, this ); +} + +DeleteDlgGenerated::~DeleteDlgGenerated() +{ +} + +FilterDlgGenerated::FilterDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer21; + bSizer21 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer72; + bSizer72 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapFilter = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizer72->Add( m_bitmapFilter, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); + + m_staticText44 = new wxStaticText( this, wxID_ANY, _("Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair."), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_staticText44->Wrap( 480 ); + bSizer72->Add( m_staticText44, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 10 ); + + + bSizer21->Add( bSizer72, 0, 0, 5 ); + + m_staticline17 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer21->Add( m_staticline17, 0, wxEXPAND, 5 ); + + m_panel38 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panel38->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer159; + bSizer159 = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer166; + bSizer166 = new wxBoxSizer( wxVERTICAL ); + + + bSizer166->Add( 0, 10, 0, 0, 5 ); + + wxBoxSizer* bSizer1661; + bSizer1661 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapInclude = new wxStaticBitmap( m_panel38, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 30,30 ), 0 ); + bSizer1661->Add( m_bitmapInclude, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); + + wxBoxSizer* bSizer173; + bSizer173 = new wxBoxSizer( wxVERTICAL ); + + m_staticText78 = new wxStaticText( m_panel38, wxID_ANY, _("Include:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText78->Wrap( -1 ); + bSizer173->Add( m_staticText78, 0, 0, 5 ); + + m_textCtrlInclude = new wxTextCtrl( m_panel38, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE ); + m_textCtrlInclude->SetMinSize( wxSize( 280,-1 ) ); + + bSizer173->Add( m_textCtrlInclude, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxTOP, 5 ); + + + bSizer1661->Add( bSizer173, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer166->Add( bSizer1661, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxLEFT, 5 ); + + m_staticline22 = new wxStaticLine( m_panel38, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer166->Add( m_staticline22, 0, wxEXPAND, 5 ); + + + bSizer166->Add( 0, 10, 0, 0, 5 ); + + wxBoxSizer* bSizer1651; + bSizer1651 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapExclude = new wxStaticBitmap( m_panel38, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 30,30 ), 0 ); + bSizer1651->Add( m_bitmapExclude, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + wxBoxSizer* bSizer174; + bSizer174 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer189; + bSizer189 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText77 = new wxStaticText( m_panel38, wxID_ANY, _("Exclude:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText77->Wrap( -1 ); + bSizer189->Add( m_staticText77, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer189->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_hyperlink17 = new wxHyperlinkCtrl( m_panel38, wxID_ANY, _("Show examples"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + bSizer189->Add( m_hyperlink17, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); + + + bSizer174->Add( bSizer189, 0, wxEXPAND, 5 ); + + m_textCtrlExclude = new wxTextCtrl( m_panel38, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE ); + bSizer174->Add( m_textCtrlExclude, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxTOP, 5 ); + + + bSizer1651->Add( bSizer174, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer166->Add( bSizer1651, 2, wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxLEFT, 5 ); + + + bSizer159->Add( bSizer166, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_staticline24 = new wxStaticLine( m_panel38, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer159->Add( m_staticline24, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + wxBoxSizer* bSizer160; + bSizer160 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer167; + bSizer167 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapFilterDate = new wxStaticBitmap( m_panel38, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 34,34 ), 0 ); + bSizer167->Add( m_bitmapFilterDate, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + wxBoxSizer* bSizer165; + bSizer165 = new wxBoxSizer( wxVERTICAL ); + + m_staticText79 = new wxStaticText( m_panel38, wxID_ANY, _("Time span:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText79->Wrap( -1 ); + bSizer165->Add( m_staticText79, 0, wxBOTTOM, 5 ); + + m_spinCtrlTimespan = new wxSpinCtrl( m_panel38, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 2000000000, 0 ); + bSizer165->Add( m_spinCtrlTimespan, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + wxArrayString m_choiceUnitTimespanChoices; + m_choiceUnitTimespan = new wxChoice( m_panel38, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceUnitTimespanChoices, 0 ); + m_choiceUnitTimespan->SetSelection( 0 ); + bSizer165->Add( m_choiceUnitTimespan, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer167->Add( bSizer165, 1, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer160->Add( bSizer167, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxALL, 5 ); + + m_staticline23 = new wxStaticLine( m_panel38, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer160->Add( m_staticline23, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer168; + bSizer168 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapFilterSize = new wxStaticBitmap( m_panel38, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32,32 ), 0 ); + bSizer168->Add( m_bitmapFilterSize, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); + + wxBoxSizer* bSizer158; + bSizer158 = new wxBoxSizer( wxVERTICAL ); + + m_staticText80 = new wxStaticText( m_panel38, wxID_ANY, _("File size:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText80->Wrap( -1 ); + bSizer158->Add( m_staticText80, 0, wxBOTTOM, 5 ); + + wxBoxSizer* bSizer162; + bSizer162 = new wxBoxSizer( wxVERTICAL ); + + m_staticText101 = new wxStaticText( m_panel38, wxID_ANY, _("Minimum:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText101->Wrap( -1 ); + bSizer162->Add( m_staticText101, 0, wxBOTTOM, 2 ); + + m_spinCtrlMinSize = new wxSpinCtrl( m_panel38, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 2000000000, 0 ); + bSizer162->Add( m_spinCtrlMinSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + wxArrayString m_choiceUnitMinSizeChoices; + m_choiceUnitMinSize = new wxChoice( m_panel38, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceUnitMinSizeChoices, 0 ); + m_choiceUnitMinSize->SetSelection( 0 ); + bSizer162->Add( m_choiceUnitMinSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer158->Add( bSizer162, 0, wxBOTTOM|wxEXPAND, 5 ); + + wxBoxSizer* bSizer163; + bSizer163 = new wxBoxSizer( wxVERTICAL ); + + m_staticText102 = new wxStaticText( m_panel38, wxID_ANY, _("Maximum:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText102->Wrap( -1 ); + bSizer163->Add( m_staticText102, 0, wxBOTTOM, 2 ); + + m_spinCtrlMaxSize = new wxSpinCtrl( m_panel38, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 2000000000, 0 ); + bSizer163->Add( m_spinCtrlMaxSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + wxArrayString m_choiceUnitMaxSizeChoices; + m_choiceUnitMaxSize = new wxChoice( m_panel38, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceUnitMaxSizeChoices, 0 ); + m_choiceUnitMaxSize->SetSelection( 0 ); + bSizer163->Add( m_choiceUnitMaxSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer158->Add( bSizer163, 0, wxEXPAND, 5 ); + + + bSizer168->Add( bSizer158, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer160->Add( bSizer168, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxALL, 5 ); + + + bSizer159->Add( bSizer160, 0, wxEXPAND, 5 ); + + + m_panel38->SetSizer( bSizer159 ); + m_panel38->Layout(); + bSizer159->Fit( m_panel38 ); + bSizer21->Add( m_panel38, 1, wxEXPAND, 5 ); + + m_staticline16 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer21->Add( m_staticline16, 0, wxEXPAND, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonClear = new wxButton( this, wxID_DEFAULT, _("&Clear"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + bSizerStdButtons->Add( m_buttonClear, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); + + + bSizerStdButtons->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonOk = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonOk->SetDefault(); + m_buttonOk->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizerStdButtons->Add( m_buttonOk, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer21->Add( bSizerStdButtons, 0, wxEXPAND, 5 ); + + + this->SetSizer( bSizer21 ); + this->Layout(); + bSizer21->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( FilterDlgGenerated::OnClose ) ); + m_textCtrlInclude->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( FilterDlgGenerated::OnUpdateNameFilter ), NULL, this ); + m_hyperlink17->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( FilterDlgGenerated::OnHelpShowExamples ), NULL, this ); + m_textCtrlExclude->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( FilterDlgGenerated::OnUpdateNameFilter ), NULL, this ); + m_choiceUnitTimespan->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( FilterDlgGenerated::OnUpdateChoice ), NULL, this ); + m_choiceUnitMinSize->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( FilterDlgGenerated::OnUpdateChoice ), NULL, this ); + m_choiceUnitMaxSize->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( FilterDlgGenerated::OnUpdateChoice ), NULL, this ); + m_buttonClear->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnClear ), NULL, this ); + m_buttonOk->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnOkay ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnCancel ), NULL, this ); +} + +FilterDlgGenerated::~FilterDlgGenerated() +{ +} + +GlobalSettingsDlgGenerated::GlobalSettingsDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer95; + bSizer95 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer72; + bSizer72 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapSettings = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizer72->Add( m_bitmapSettings, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); + + m_staticText44 = new wxStaticText( this, wxID_ANY, _("The following settings are used for all synchronization jobs."), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_staticText44->Wrap( 500 ); + bSizer72->Add( m_staticText44, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 10 ); + + + bSizer95->Add( bSizer72, 0, 0, 5 ); + + m_staticline20 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer95->Add( m_staticline20, 0, wxEXPAND, 5 ); + + m_panel39 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panel39->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer166; + bSizer166 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer186; + bSizer186 = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer160; + bSizer160 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer176; + bSizer176 = new wxBoxSizer( wxHORIZONTAL ); + + m_checkBoxFailSafe = new wxCheckBox( m_panel39, wxID_ANY, _("Fail-safe file copy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxFailSafe->SetValue(true); + m_checkBoxFailSafe->SetToolTip( _("Copy to a temporary file (*.ffs_tmp) before overwriting target.\nThis guarantees a consistent state even in case of a serious error.") ); + + bSizer176->Add( m_checkBoxFailSafe, 1, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticText91 = new wxStaticText( m_panel39, wxID_ANY, _("(recommended)"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText91->Wrap( -1 ); + m_staticText91->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); + + bSizer176->Add( m_staticText91, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer160->Add( bSizer176, 0, wxEXPAND, 5 ); + + bSizerLockedFiles = new wxBoxSizer( wxHORIZONTAL ); + + m_checkBoxCopyLocked = new wxCheckBox( m_panel39, wxID_ANY, _("Copy locked files"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxCopyLocked->SetValue(true); + m_checkBoxCopyLocked->SetToolTip( _("Copy shared or locked files using the Volume Shadow Copy Service.") ); + + bSizerLockedFiles->Add( m_checkBoxCopyLocked, 1, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticText92 = new wxStaticText( m_panel39, wxID_ANY, _("(requires administrator rights)"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText92->Wrap( -1 ); + m_staticText92->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); + + bSizerLockedFiles->Add( m_staticText92, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer160->Add( bSizerLockedFiles, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer178; + bSizer178 = new wxBoxSizer( wxHORIZONTAL ); + + m_checkBoxCopyPermissions = new wxCheckBox( m_panel39, wxID_ANY, _("Copy file access permissions"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxCopyPermissions->SetValue(true); + m_checkBoxCopyPermissions->SetToolTip( _("Transfer file and folder permissions.") ); + + bSizer178->Add( m_checkBoxCopyPermissions, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_staticText93 = new wxStaticText( m_panel39, wxID_ANY, _("(requires administrator rights)"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText93->Wrap( -1 ); + m_staticText93->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); + + bSizer178->Add( m_staticText93, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer160->Add( bSizer178, 0, wxEXPAND, 5 ); + + + bSizer186->Add( bSizer160, 0, wxEXPAND|wxALL, 5 ); + + m_staticline39 = new wxStaticLine( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer186->Add( m_staticline39, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer188; + bSizer188 = new wxBoxSizer( wxVERTICAL ); + + m_staticText95 = new wxStaticText( m_panel39, wxID_ANY, _("Automatic retry on error:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText95->Wrap( -1 ); + bSizer188->Add( m_staticText95, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM, 5 ); + + wxFlexGridSizer* fgSizer6; + fgSizer6 = new wxFlexGridSizer( 0, 2, 5, 5 ); + fgSizer6->SetFlexibleDirection( wxBOTH ); + fgSizer6->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_staticText96 = new wxStaticText( m_panel39, wxID_ANY, _("Retry count:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText96->Wrap( -1 ); + fgSizer6->Add( m_staticText96, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_spinCtrlAutoRetryCount = new wxSpinCtrl( m_panel39, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 60,-1 ), wxSP_ARROW_KEYS, 0, 2000000000, 4 ); + fgSizer6->Add( m_spinCtrlAutoRetryCount, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextAutoRetryDelay = new wxStaticText( m_panel39, wxID_ANY, _("Delay (in seconds):"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextAutoRetryDelay->Wrap( -1 ); + fgSizer6->Add( m_staticTextAutoRetryDelay, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_spinCtrlAutoRetryDelay = new wxSpinCtrl( m_panel39, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 60,-1 ), wxSP_ARROW_KEYS, 0, 2000000000, 0 ); + fgSizer6->Add( m_spinCtrlAutoRetryDelay, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer188->Add( fgSizer6, 0, wxLEFT, 10 ); + + + bSizer186->Add( bSizer188, 0, wxALL, 10 ); + + + bSizer166->Add( bSizer186, 0, wxEXPAND, 5 ); + + m_staticline191 = new wxStaticLine( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer166->Add( m_staticline191, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer181; + bSizer181 = new wxBoxSizer( wxVERTICAL ); + + m_staticText85 = new wxStaticText( m_panel39, wxID_ANY, _("Customize context menu:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText85->Wrap( -1 ); + bSizer181->Add( m_staticText85, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM, 5 ); + + m_gridCustomCommand = new wxGrid( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + + // Grid + m_gridCustomCommand->CreateGrid( 5, 2 ); + m_gridCustomCommand->EnableEditing( true ); + m_gridCustomCommand->EnableGridLines( true ); + m_gridCustomCommand->EnableDragGridSize( false ); + m_gridCustomCommand->SetMargins( 0, 0 ); + + // Columns + m_gridCustomCommand->SetColSize( 0, 165 ); + m_gridCustomCommand->SetColSize( 1, 196 ); + m_gridCustomCommand->EnableDragColMove( false ); + m_gridCustomCommand->EnableDragColSize( true ); + m_gridCustomCommand->SetColLabelSize( 20 ); + m_gridCustomCommand->SetColLabelValue( 0, _("Description") ); + m_gridCustomCommand->SetColLabelValue( 1, _("Command line") ); + m_gridCustomCommand->SetColLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE ); + + // Rows + m_gridCustomCommand->EnableDragRowSize( false ); + m_gridCustomCommand->SetRowLabelSize( 1 ); + m_gridCustomCommand->SetRowLabelAlignment( wxALIGN_LEFT, wxALIGN_CENTRE ); + + // Label Appearance + + // Cell Defaults + m_gridCustomCommand->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_TOP ); + bSizer181->Add( m_gridCustomCommand, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + wxBoxSizer* bSizer193; + bSizer193 = new wxBoxSizer( wxHORIZONTAL ); + + m_bpButtonAddRow = new wxBitmapButton( m_panel39, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + bSizer193->Add( m_bpButtonAddRow, 0, 0, 5 ); + + m_bpButtonRemoveRow = new wxBitmapButton( m_panel39, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + bSizer193->Add( m_bpButtonRemoveRow, 0, 0, 5 ); + + + bSizer193->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_hyperlink17 = new wxHyperlinkCtrl( m_panel39, wxID_ANY, _("Show examples"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + bSizer193->Add( m_hyperlink17, 0, wxLEFT, 5 ); + + + bSizer181->Add( bSizer193, 0, wxTOP|wxEXPAND, 5 ); + + + bSizer166->Add( bSizer181, 1, wxEXPAND|wxALL, 10 ); + + m_staticline192 = new wxStaticLine( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer166->Add( m_staticline192, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer1881; + bSizer1881 = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonResetDialogs = new zen::BitmapTextButton( m_panel39, wxID_ANY, _("Restore hidden windows"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizer1881->Add( m_buttonResetDialogs, 0, wxALL|wxALIGN_CENTER_VERTICAL, 10 ); + + m_staticline40 = new wxStaticLine( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer1881->Add( m_staticline40, 0, wxEXPAND, 5 ); + + + bSizer166->Add( bSizer1881, 0, 0, 5 ); + + + m_panel39->SetSizer( bSizer166 ); + m_panel39->Layout(); + bSizer166->Fit( m_panel39 ); + bSizer95->Add( m_panel39, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + m_staticline36 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer95->Add( m_staticline36, 0, wxEXPAND, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonDefault = new wxButton( this, wxID_DEFAULT, _("&Default"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + bSizerStdButtons->Add( m_buttonDefault, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerStdButtons->Add( 0, 0, 1, 0, 5 ); + + m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonOkay->SetDefault(); + m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizerStdButtons->Add( m_buttonOkay, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer95->Add( bSizerStdButtons, 0, wxEXPAND, 5 ); + + + this->SetSizer( bSizer95 ); + this->Layout(); + bSizer95->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( GlobalSettingsDlgGenerated::OnClose ) ); + m_spinCtrlAutoRetryCount->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnToggleAutoRetryCount ), NULL, this ); + m_bpButtonAddRow->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnAddRow ), NULL, this ); + m_bpButtonRemoveRow->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnRemoveRow ), NULL, this ); + m_hyperlink17->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( GlobalSettingsDlgGenerated::OnHelpShowExamples ), NULL, this ); + m_buttonResetDialogs->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnResetDialogs ), NULL, this ); + m_buttonDefault->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnDefault ), NULL, this ); + m_buttonOkay->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnOkay ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnCancel ), NULL, this ); +} + +GlobalSettingsDlgGenerated::~GlobalSettingsDlgGenerated() +{ +} + +TooltipDialogGenerated::TooltipDialogGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxDefaultSize, wxDefaultSize ); + + wxBoxSizer* bSizer158; + bSizer158 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapLeft = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizer158->Add( m_bitmapLeft, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticTextMain = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextMain->Wrap( 600 ); + bSizer158->Add( m_staticTextMain, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + this->SetSizer( bSizer158 ); + this->Layout(); + bSizer158->Fit( this ); +} + +TooltipDialogGenerated::~TooltipDialogGenerated() +{ +} + +SelectTimespanDlgGenerated::SelectTimespanDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxDefaultSize, wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer96; + bSizer96 = new wxBoxSizer( wxVERTICAL ); + + m_panel35 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panel35->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer98; + bSizer98 = new wxBoxSizer( wxHORIZONTAL ); + + m_calendarFrom = new wxCalendarCtrl( m_panel35, wxID_ANY, wxDefaultDateTime, wxDefaultPosition, wxDefaultSize, wxCAL_SHOW_HOLIDAYS|wxNO_BORDER ); + bSizer98->Add( m_calendarFrom, 0, wxTOP|wxBOTTOM|wxLEFT, 10 ); + + m_calendarTo = new wxCalendarCtrl( m_panel35, wxID_ANY, wxDefaultDateTime, wxDefaultPosition, wxDefaultSize, wxCAL_SHOW_HOLIDAYS|wxNO_BORDER ); + bSizer98->Add( m_calendarTo, 0, wxALL, 10 ); + + + m_panel35->SetSizer( bSizer98 ); + m_panel35->Layout(); + bSizer98->Fit( m_panel35 ); + bSizer96->Add( m_panel35, 0, wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_staticline21 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer96->Add( m_staticline21, 0, wxEXPAND, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonOkay->SetDefault(); + m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizerStdButtons->Add( m_buttonOkay, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer96->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); + + + this->SetSizer( bSizer96 ); + this->Layout(); + bSizer96->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SelectTimespanDlgGenerated::OnClose ) ); + m_calendarFrom->Connect( wxEVT_CALENDAR_SEL_CHANGED, wxCalendarEventHandler( SelectTimespanDlgGenerated::OnChangeSelectionFrom ), NULL, this ); + m_calendarTo->Connect( wxEVT_CALENDAR_SEL_CHANGED, wxCalendarEventHandler( SelectTimespanDlgGenerated::OnChangeSelectionTo ), NULL, this ); + m_buttonOkay->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SelectTimespanDlgGenerated::OnOkay ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SelectTimespanDlgGenerated::OnCancel ), NULL, this ); +} + +SelectTimespanDlgGenerated::~SelectTimespanDlgGenerated() +{ +} + +AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxDefaultSize, wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer31; + bSizer31 = new wxBoxSizer( wxVERTICAL ); + + m_panel41 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panel41->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer162; + bSizer162 = new wxBoxSizer( wxVERTICAL ); + + m_bitmapLogo = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizer162->Add( m_bitmapLogo, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticline341 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer162->Add( m_staticline341, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer174; + bSizer174 = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer181; + bSizer181 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer187; + bSizer187 = new wxBoxSizer( wxVERTICAL ); + + m_staticText96 = new wxStaticText( m_panel41, wxID_ANY, _("Source code written in C++ using:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText96->Wrap( -1 ); + bSizer187->Add( m_staticText96, 0, wxALL, 5 ); + + wxBoxSizer* bSizer171; + bSizer171 = new wxBoxSizer( wxHORIZONTAL ); + + m_hyperlink11 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("MS Visual C++"), wxT("http://msdn.microsoft.com/library/60k1461a.aspx"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink11->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink11->SetToolTip( _("http://msdn.microsoft.com/library/60k1461a.aspx") ); + + bSizer171->Add( m_hyperlink11, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_hyperlink9 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("MinGW"), wxT("http://www.mingw.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink9->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink9->SetToolTip( _("http://www.mingw.org") ); + + bSizer171->Add( m_hyperlink9, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_hyperlink10 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Code::Blocks"), wxT("http://www.codeblocks.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink10->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink10->SetToolTip( _("http://www.codeblocks.org") ); + + bSizer171->Add( m_hyperlink10, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_hyperlink7 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("wxWidgets"), wxT("http://www.wxwidgets.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink7->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink7->SetToolTip( _("http://www.wxwidgets.org") ); + + bSizer171->Add( m_hyperlink7, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_hyperlink14 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("wxFormBuilder"), wxT("http://wxformbuilder.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink14->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink14->SetToolTip( _("http://wxformbuilder.org") ); + + bSizer171->Add( m_hyperlink14, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer187->Add( bSizer171, 0, wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizer172; + bSizer172 = new wxBoxSizer( wxHORIZONTAL ); + + m_hyperlink15 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("zen::Xml"), wxT("http://zenxml.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink15->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink15->SetToolTip( _("http://zenxml.sourceforge.net") ); + + bSizer172->Add( m_hyperlink15, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_hyperlink13 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Boost"), wxT("http://www.boost.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink13->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink13->SetToolTip( _("http://www.boost.org") ); + + bSizer172->Add( m_hyperlink13, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_hyperlink16 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Artistic Style"), wxT("http://astyle.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink16->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink16->SetToolTip( _("http://astyle.sourceforge.net") ); + + bSizer172->Add( m_hyperlink16, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_hyperlink12 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Google Test"), wxT("http://code.google.com/p/googletest"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink12->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink12->SetToolTip( _("http://code.google.com/p/googletest") ); + + bSizer172->Add( m_hyperlink12, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_hyperlink18 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Unicode NSIS"), wxT("http://www.scratchpaper.com"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink18->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink18->SetToolTip( _("http://www.scratchpaper.com") ); + + bSizer172->Add( m_hyperlink18, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer187->Add( bSizer172, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer181->Add( bSizer187, 0, wxALL|wxEXPAND, 5 ); + + m_panelDonate = new wxPanel( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelDonate->SetBackgroundColour( wxColour( 153, 170, 187 ) ); + + wxBoxSizer* bSizer183; + bSizer183 = new wxBoxSizer( wxVERTICAL ); + + m_panel39 = new wxPanel( m_panelDonate, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panel39->SetBackgroundColour( wxColour( 221, 221, 255 ) ); + + wxBoxSizer* bSizer184; + bSizer184 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer184->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_animCtrlWink = new wxAnimationCtrl( m_panel39, wxID_ANY, wxNullAnimation, wxDefaultPosition, wxSize( 48,48 ), wxAC_DEFAULT_STYLE ); + bSizer184->Add( m_animCtrlWink, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + wxBoxSizer* bSizer178; + bSizer178 = new wxBoxSizer( wxVERTICAL ); + + m_staticText83 = new wxStaticText( m_panel39, wxID_ANY, _("If you like FreeFileSync"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText83->Wrap( -1 ); + m_staticText83->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + m_staticText83->SetForegroundColour( wxColour( 0, 0, 0 ) ); + + bSizer178->Add( m_staticText83, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_buttonDonate = new wxButton( m_panel39, wxID_ANY, _("Donate with PayPal"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonDonate->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString ) ); + m_buttonDonate->SetToolTip( _("http://freefilesync.sourceforge.net/donate.php") ); + + bSizer178->Add( m_buttonDonate, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer184->Add( bSizer178, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer184->Add( 0, 0, 1, wxEXPAND, 5 ); + + + m_panel39->SetSizer( bSizer184 ); + m_panel39->Layout(); + bSizer184->Fit( m_panel39 ); + bSizer183->Add( m_panel39, 0, wxEXPAND|wxALL, 5 ); + + + m_panelDonate->SetSizer( bSizer183 ); + m_panelDonate->Layout(); + bSizer183->Fit( m_panelDonate ); + bSizer181->Add( m_panelDonate, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxRIGHT|wxLEFT, 5 ); + + wxBoxSizer* bSizer186; + bSizer186 = new wxBoxSizer( wxVERTICAL ); + + m_staticText94 = new wxStaticText( m_panel41, wxID_ANY, _("Feedback and suggestions are welcome"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText94->Wrap( -1 ); + bSizer186->Add( m_staticText94, 0, wxALL, 5 ); + + wxBoxSizer* bSizer166; + bSizer166 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer166->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_bitmap9 = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_bitmap9->SetToolTip( _("Homepage") ); + + bSizer166->Add( m_bitmap9, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); + + m_hyperlink1 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("freefilesync.sf.net"), wxT("http://freefilesync.sf.net/"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink1->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, true, wxEmptyString ) ); + m_hyperlink1->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + bSizer166->Add( m_hyperlink1, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer166->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_bitmap10 = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_bitmap10->SetToolTip( _("Email") ); + + bSizer166->Add( m_bitmap10, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); + + m_hyperlink2 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("zenju@gmx.de"), wxT("mailto:zenju@gmx.de"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink2->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, true, wxEmptyString ) ); + m_hyperlink2->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + bSizer166->Add( m_hyperlink2, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer166->Add( 0, 0, 1, wxEXPAND, 5 ); + + + bSizer186->Add( bSizer166, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer181->Add( bSizer186, 0, wxALL|wxEXPAND, 5 ); + + m_staticline34 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer181->Add( m_staticline34, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer185; + bSizer185 = new wxBoxSizer( wxVERTICAL ); + + m_staticText93 = new wxStaticText( m_panel41, wxID_ANY, _("Published under the GNU General Public License"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText93->Wrap( -1 ); + bSizer185->Add( m_staticText93, 0, wxALL, 5 ); + + wxBoxSizer* bSizer1671; + bSizer1671 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmap13 = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizer1671->Add( m_bitmap13, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_hyperlink5 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("http://www.gnu.org/licenses/gpl.html"), wxT("http://www.gnu.org/licenses/gpl.html"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink5->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + bSizer1671->Add( m_hyperlink5, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer185->Add( bSizer1671, 0, wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + + bSizer181->Add( bSizer185, 0, wxALL|wxEXPAND, 5 ); + + + bSizer174->Add( bSizer181, 0, 0, 5 ); + + m_staticline37 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer174->Add( m_staticline37, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer177; + bSizer177 = new wxBoxSizer( wxVERTICAL ); + + m_staticText54 = new wxStaticText( m_panel41, wxID_ANY, _("Many thanks for localization:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText54->Wrap( 200 ); + m_staticText54->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer177->Add( m_staticText54, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); + + + bSizer177->Add( 0, 5, 0, 0, 5 ); + + m_scrolledWindowTranslators = new wxScrolledWindow( m_panel41, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), wxVSCROLL ); + m_scrolledWindowTranslators->SetScrollRate( 10, 10 ); + m_scrolledWindowTranslators->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_scrolledWindowTranslators->SetMinSize( wxSize( 220,-1 ) ); + + fgSizerTranslators = new wxFlexGridSizer( 0, 2, 2, 10 ); + fgSizerTranslators->SetFlexibleDirection( wxBOTH ); + fgSizerTranslators->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + + m_scrolledWindowTranslators->SetSizer( fgSizerTranslators ); + m_scrolledWindowTranslators->Layout(); + fgSizerTranslators->Fit( m_scrolledWindowTranslators ); + bSizer177->Add( m_scrolledWindowTranslators, 1, wxALIGN_CENTER_HORIZONTAL|wxLEFT|wxEXPAND, 5 ); + + + bSizer174->Add( bSizer177, 0, wxEXPAND|wxTOP|wxLEFT, 5 ); + + + bSizer162->Add( bSizer174, 0, 0, 5 ); + + + m_panel41->SetSizer( bSizer162 ); + m_panel41->Layout(); + bSizer162->Fit( m_panel41 ); + bSizer31->Add( m_panel41, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + m_staticline36 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer31->Add( m_staticline36, 0, wxEXPAND, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonClose = new wxButton( this, wxID_OK, _("Close"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonClose->SetDefault(); + bSizerStdButtons->Add( m_buttonClose, 0, wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer31->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); + + + this->SetSizer( bSizer31 ); + this->Layout(); + bSizer31->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( AboutDlgGenerated::OnClose ) ); + m_buttonDonate->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AboutDlgGenerated::OnDonate ), NULL, this ); + m_buttonClose->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AboutDlgGenerated::OnOK ), NULL, this ); +} + +AboutDlgGenerated::~AboutDlgGenerated() +{ +} diff --git a/FreeFileSync/Source/ui/gui_generated.h b/FreeFileSync/Source/ui/gui_generated.h new file mode 100644 index 00000000..e87529b1 --- /dev/null +++ b/FreeFileSync/Source/ui/gui_generated.h @@ -0,0 +1,880 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Oct 8 2012) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __GUI_GENERATED_H__ +#define __GUI_GENERATED_H__ + +#include +#include +#include +class ExecFinishedBox; +class FolderHistoryBox; +class ToggleButton; +namespace zen{ class BitmapTextButton; } +namespace zen{ class Graph2D; } +namespace zen{ class Grid; } +namespace zen{ class TripleSplitter; } + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zen/i18n.h" + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class MainDialogGenerated +/////////////////////////////////////////////////////////////////////////////// +class MainDialogGenerated : public wxFrame +{ + private: + + protected: + wxMenuBar* m_menubar1; + wxMenu* m_menuFile; + wxMenuItem* m_menuItemNew; + wxMenuItem* m_menuItemLoad; + wxMenuItem* m_menuItemSave; + wxMenuItem* m_menuItemSaveAs; + wxMenuItem* m_menuItem7; + wxMenuItem* m_menuItem10; + wxMenuItem* m_menuItem11; + wxMenu* m_menuTools; + wxMenuItem* m_menuItemGlobSett; + wxMenu* m_menuLanguages; + wxMenu* m_menuHelp; + wxMenuItem* m_menuItemManual; + wxMenu* m_menuCheckVersion; + wxMenuItem* m_menuItemCheckVersionNow; + wxMenuItem* m_menuItemCheckVersionAuto; + wxMenuItem* m_menuItemAbout; + wxBoxSizer* bSizerPanelHolder; + wxPanel* m_panelTopButtons; + wxBoxSizer* bSizerTopButtons; + zen::BitmapTextButton* m_buttonCompare; + zen::BitmapTextButton* m_buttonCancel; + wxBitmapButton* m_bpButtonCmpConfig; + wxBitmapButton* m_bpButtonSyncConfig; + zen::BitmapTextButton* m_buttonSync; + wxPanel* m_panelDirectoryPairs; + wxStaticText* m_staticTextResolvedPathL; + wxBitmapButton* m_bpButtonAddPair; + wxButton* m_buttonSelectDirLeft; + wxPanel* m_panelTopMiddle; + wxBitmapButton* m_bpButtonSwapSides; + wxStaticText* m_staticTextResolvedPathR; + wxButton* m_buttonSelectDirRight; + wxScrolledWindow* m_scrolledWindowFolderPairs; + wxBoxSizer* bSizerAddFolderPairs; + zen::Grid* m_gridNavi; + wxPanel* m_panelCenter; + zen::TripleSplitter* m_splitterMain; + zen::Grid* m_gridMainL; + zen::Grid* m_gridMainC; + zen::Grid* m_gridMainR; + wxPanel* m_panelStatusBar; + wxBoxSizer* bSizerFileStatus; + wxBoxSizer* bSizerStatusLeft; + wxBoxSizer* bSizerStatusLeftDirectories; + wxStaticBitmap* m_bitmapSmallDirectoryLeft; + wxStaticText* m_staticTextStatusLeftDirs; + wxBoxSizer* bSizerStatusLeftFiles; + wxStaticBitmap* m_bitmapSmallFileLeft; + wxStaticText* m_staticTextStatusLeftFiles; + wxStaticText* m_staticTextStatusLeftBytes; + wxStaticLine* m_staticline9; + wxStaticText* m_staticTextStatusMiddle; + wxBoxSizer* bSizerStatusRight; + wxStaticLine* m_staticline10; + wxBoxSizer* bSizerStatusRightDirectories; + wxStaticBitmap* m_bitmapSmallDirectoryRight; + wxStaticText* m_staticTextStatusRightDirs; + wxBoxSizer* bSizerStatusRightFiles; + wxStaticBitmap* m_bitmapSmallFileRight; + wxStaticText* m_staticTextStatusRightFiles; + wxStaticText* m_staticTextStatusRightBytes; + wxStaticText* m_staticTextFullStatus; + wxPanel* m_panelSearch; + wxBitmapButton* m_bpButtonHideSearch; + wxStaticText* m_staticText101; + wxTextCtrl* m_textCtrlSearchTxt; + wxCheckBox* m_checkBoxMatchCase; + wxPanel* m_panelConfig; + wxBoxSizer* bSizerConfig; + wxBitmapButton* m_bpButtonOpen; + wxBitmapButton* m_bpButtonSave; + wxBitmapButton* m_bpButtonBatchJob; + wxListBox* m_listBoxHistory; + wxPanel* m_panelFilter; + wxBitmapButton* m_bpButtonFilter; + wxCheckBox* m_checkBoxHideExcluded; + wxPanel* m_panelStatistics; + wxBoxSizer* bSizer1801; + wxStaticBitmap* m_bitmapCreateLeft; + wxStaticText* m_staticTextCreateLeft; + wxStaticBitmap* m_bitmapUpdateLeft; + wxStaticText* m_staticTextUpdateLeft; + wxStaticBitmap* m_bitmapDeleteLeft; + wxStaticText* m_staticTextDeleteLeft; + wxStaticBitmap* m_bitmapData; + wxStaticText* m_staticTextData; + wxStaticBitmap* m_bitmapDeleteRight; + wxStaticText* m_staticTextDeleteRight; + wxStaticBitmap* m_bitmapUpdateRight; + wxStaticText* m_staticTextUpdateRight; + wxStaticBitmap* m_bitmapCreateRight; + wxStaticText* m_staticTextCreateRight; + wxPanel* m_panelViewFilter; + wxBoxSizer* bSizerViewFilter; + ToggleButton* m_bpButtonViewTypeSyncAction; + ToggleButton* m_bpButtonShowCreateLeft; + ToggleButton* m_bpButtonShowUpdateLeft; + ToggleButton* m_bpButtonShowDeleteLeft; + ToggleButton* m_bpButtonShowLeftOnly; + ToggleButton* m_bpButtonShowLeftNewer; + ToggleButton* m_bpButtonShowEqual; + ToggleButton* m_bpButtonShowDifferent; + ToggleButton* m_bpButtonShowDoNothing; + ToggleButton* m_bpButtonShowRightNewer; + ToggleButton* m_bpButtonShowRightOnly; + ToggleButton* m_bpButtonShowDeleteRight; + ToggleButton* m_bpButtonShowUpdateRight; + ToggleButton* m_bpButtonShowCreateRight; + ToggleButton* m_bpButtonShowConflict; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnConfigNew( wxCommandEvent& event ) { event.Skip(); } + virtual void OnConfigLoad( wxCommandEvent& event ) { event.Skip(); } + virtual void OnConfigSave( wxCommandEvent& event ) { event.Skip(); } + virtual void OnConfigSaveAs( wxCommandEvent& event ) { event.Skip(); } + virtual void OnSaveAsBatchJob( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCompare( wxCommandEvent& event ) { event.Skip(); } + virtual void OnStartSync( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMenuQuit( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMenuGlobalSettings( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMenuFindItem( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMenuExportFileList( wxCommandEvent& event ) { event.Skip(); } + virtual void OnShowHelp( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMenuCheckVersion( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMenuCheckVersionAutomatically( wxCommandEvent& event ) { event.Skip(); } + virtual void OnMenuAbout( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCmpSettings( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCompSettingsContext( wxMouseEvent& event ) { event.Skip(); } + virtual void OnSyncSettings( wxCommandEvent& event ) { event.Skip(); } + virtual void OnSyncSettingsContext( wxMouseEvent& event ) { event.Skip(); } + virtual void OnAddFolderPair( wxCommandEvent& event ) { event.Skip(); } + virtual void OnRemoveTopFolderPair( wxCommandEvent& event ) { event.Skip(); } + virtual void OnSwapSides( wxCommandEvent& event ) { event.Skip(); } + virtual void OnHideSearchPanel( wxCommandEvent& event ) { event.Skip(); } + virtual void OnSearchGridEnter( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCfgHistoryKeyEvent( wxKeyEvent& event ) { event.Skip(); } + virtual void OnLoadFromHistory( wxCommandEvent& event ) { event.Skip(); } + virtual void OnLoadFromHistoryDoubleClick( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCfgHistoryRightClick( wxMouseEvent& event ) { event.Skip(); } + virtual void OnConfigureFilter( wxCommandEvent& event ) { event.Skip(); } + virtual void OnGlobalFilterContext( wxMouseEvent& event ) { event.Skip(); } + virtual void OnShowExcluded( wxCommandEvent& event ) { event.Skip(); } + virtual void OnToggleViewType( wxCommandEvent& event ) { event.Skip(); } + virtual void OnToggleViewButton( wxCommandEvent& event ) { event.Skip(); } + virtual void OnViewButtonRightClick( wxMouseEvent& event ) { event.Skip(); } + + + public: + wxPanel* m_panelTopLeft; + wxBitmapButton* m_bpButtonRemovePair; + FolderHistoryBox* m_directoryLeft; + wxBitmapButton* m_bpButtonAltCompCfg; + wxBitmapButton* m_bpButtonLocalFilter; + wxBitmapButton* m_bpButtonAltSyncCfg; + wxPanel* m_panelTopRight; + FolderHistoryBox* m_directoryRight; + wxBoxSizer* bSizerStatistics; + wxBoxSizer* bSizerData; + + MainDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 900,600 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL ); + + ~MainDialogGenerated(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class CmpCfgDlgGenerated +/////////////////////////////////////////////////////////////////////////////// +class CmpCfgDlgGenerated : public wxDialog +{ + private: + + protected: + wxPanel* m_panel36; + wxStaticText* m_staticText91; + wxStaticBitmap* m_bitmapByTime; + wxToggleButton* m_toggleBtnTimeSize; + wxStaticBitmap* m_bitmapByContent; + wxToggleButton* m_toggleBtnContent; + wxStaticLine* m_staticline33; + wxStaticText* m_staticText92; + wxChoice* m_choiceHandleSymlinks; + wxHyperlinkCtrl* m_hyperlink24; + wxStaticLine* m_staticline14; + wxBoxSizer* bSizerStdButtons; + wxButton* m_buttonOkay; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnTimeSizeDouble( wxMouseEvent& event ) { event.Skip(); } + virtual void OnTimeSize( wxCommandEvent& event ) { event.Skip(); } + virtual void OnContentDouble( wxMouseEvent& event ) { event.Skip(); } + virtual void OnContent( wxCommandEvent& event ) { event.Skip(); } + virtual void OnChangeErrorHandling( wxCommandEvent& event ) { event.Skip(); } + virtual void OnHelpComparisonSettings( wxHyperlinkEvent& event ) { event.Skip(); } + virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } + + + public: + + CmpCfgDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE ); + ~CmpCfgDlgGenerated(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class SyncCfgDlgGenerated +/////////////////////////////////////////////////////////////////////////////// +class SyncCfgDlgGenerated : public wxDialog +{ + private: + + protected: + wxPanel* m_panel37; + wxStaticText* m_staticText86; + wxToggleButton* m_toggleBtnTwoWay; + wxStaticText* m_staticTextAutomatic; + wxToggleButton* m_toggleBtnMirror; + wxStaticText* m_staticTextMirror; + wxToggleButton* m_toggleBtnUpdate; + wxStaticText* m_staticTextUpdate; + wxToggleButton* m_toggleBtnCustom; + wxStaticText* m_staticTextCustom; + wxCheckBox* m_checkBoxDetectMove; + wxStaticLine* m_staticline32; + wxStaticText* m_staticText87; + wxToggleButton* m_toggleBtnPermanent; + wxToggleButton* m_toggleBtnRecycler; + wxToggleButton* m_toggleBtnVersioning; + wxPanel* m_panelVersioning; + FolderHistoryBox* m_versioningFolder; + wxButton* m_buttonSelectDirVersioning; + wxBoxSizer* bSizer192; + wxStaticText* m_staticText93; + wxChoice* m_choiceVersioningStyle; + wxStaticText* m_staticTextNamingCvtPart1; + wxStaticText* m_staticTextNamingCvtPart2Bold; + wxStaticText* m_staticTextNamingCvtPart3; + wxHyperlinkCtrl* m_hyperlink17; + wxBoxSizer* bSizerExtraConfig; + wxStaticLine* m_staticline321; + wxBoxSizer* bSizer179; + wxStaticText* m_staticText88; + wxToggleButton* m_toggleBtnErrorIgnore; + wxToggleButton* m_toggleBtnErrorPopup; + wxStaticLine* m_staticline36; + wxBoxSizer* bSizerOnCompletion; + wxStaticText* m_staticText89; + ExecFinishedBox* m_comboBoxExecFinished; + wxStaticLine* m_staticline31; + wxBoxSizer* bSizerConfig; + wxStaticText* m_staticTextHeaderCategory1; + wxStaticText* m_staticTextHeaderAction1; + wxStaticBitmap* m_bitmapDatabase; + wxBoxSizer* sbSizerSyncDirections; + wxBoxSizer* bSizerLeftOnly; + wxStaticBitmap* m_bitmapLeftOnly; + wxBitmapButton* m_bpButtonLeftOnly; + wxBoxSizer* bSizerRightOnly; + wxStaticBitmap* m_bitmapRightOnly; + wxBitmapButton* m_bpButtonRightOnly; + wxBoxSizer* bSizerLeftNewer; + wxStaticBitmap* m_bitmapLeftNewer; + wxBitmapButton* m_bpButtonLeftNewer; + wxBoxSizer* bSizerRightNewer; + wxStaticBitmap* m_bitmapRightNewer; + wxBitmapButton* m_bpButtonRightNewer; + wxBoxSizer* bSizerDifferent; + wxStaticBitmap* m_bitmapDifferent; + wxBitmapButton* m_bpButtonDifferent; + wxBoxSizer* bSizerConflict; + wxStaticBitmap* m_bitmapConflict; + wxBitmapButton* m_bpButtonConflict; + wxStaticLine* m_staticline15; + wxBoxSizer* bSizerStdButtons; + wxButton* m_buttonOK; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnSyncTwoWayDouble( wxMouseEvent& event ) { event.Skip(); } + virtual void OnSyncTwoWay( wxCommandEvent& event ) { event.Skip(); } + virtual void OnSyncMirrorDouble( wxMouseEvent& event ) { event.Skip(); } + virtual void OnSyncMirror( wxCommandEvent& event ) { event.Skip(); } + virtual void OnSyncUpdateDouble( wxMouseEvent& event ) { event.Skip(); } + virtual void OnSyncUpdate( wxCommandEvent& event ) { event.Skip(); } + virtual void OnSyncCustomDouble( wxMouseEvent& event ) { event.Skip(); } + virtual void OnSyncCustom( wxCommandEvent& event ) { event.Skip(); } + virtual void OnToggleDetectMovedFiles( wxCommandEvent& event ) { event.Skip(); } + virtual void OnDeletionPermanent( wxCommandEvent& event ) { event.Skip(); } + virtual void OnDeletionRecycler( wxCommandEvent& event ) { event.Skip(); } + virtual void OnDeletionVersioning( wxCommandEvent& event ) { event.Skip(); } + virtual void OnParameterChange( wxCommandEvent& event ) { event.Skip(); } + virtual void OnHelpVersioning( wxHyperlinkEvent& event ) { event.Skip(); } + virtual void OnErrorIgnore( wxCommandEvent& event ) { event.Skip(); } + virtual void OnErrorPopup( wxCommandEvent& event ) { event.Skip(); } + virtual void OnExLeftSideOnly( wxCommandEvent& event ) { event.Skip(); } + virtual void OnExRightSideOnly( wxCommandEvent& event ) { event.Skip(); } + virtual void OnLeftNewer( wxCommandEvent& event ) { event.Skip(); } + virtual void OnRightNewer( wxCommandEvent& event ) { event.Skip(); } + virtual void OnDifferent( wxCommandEvent& event ) { event.Skip(); } + virtual void OnConflict( wxCommandEvent& event ) { event.Skip(); } + virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } + + + public: + + SyncCfgDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); + ~SyncCfgDlgGenerated(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class SyncConfirmationDlgGenerated +/////////////////////////////////////////////////////////////////////////////// +class SyncConfirmationDlgGenerated : public wxDialog +{ + private: + + protected: + wxStaticBitmap* m_bitmapSync; + wxStaticText* m_staticTextHeader; + wxStaticLine* m_staticline371; + wxPanel* m_panelStatistics; + wxStaticLine* m_staticline38; + wxStaticText* m_staticText84; + wxStaticText* m_staticTextVariant; + wxStaticLine* m_staticline14; + wxStaticText* m_staticText83; + wxStaticBitmap* m_bitmapCreateLeft; + wxStaticBitmap* m_bitmapUpdateLeft; + wxStaticBitmap* m_bitmapDeleteLeft; + wxStaticBitmap* m_bitmapData; + wxStaticBitmap* m_bitmapDeleteRight; + wxStaticBitmap* m_bitmapUpdateRight; + wxStaticBitmap* m_bitmapCreateRight; + wxStaticText* m_staticTextCreateLeft; + wxStaticText* m_staticTextUpdateLeft; + wxStaticText* m_staticTextDeleteLeft; + wxStaticText* m_staticTextData; + wxStaticText* m_staticTextDeleteRight; + wxStaticText* m_staticTextUpdateRight; + wxStaticText* m_staticTextCreateRight; + wxStaticLine* m_staticline12; + wxCheckBox* m_checkBoxDontShowAgain; + wxBoxSizer* bSizerStdButtons; + wxButton* m_buttonStartSync; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnStartSync( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } + + + public: + + SyncConfirmationDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("FreeFileSync"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); + ~SyncConfirmationDlgGenerated(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class FolderPairPanelGenerated +/////////////////////////////////////////////////////////////////////////////// +class FolderPairPanelGenerated : public wxPanel +{ + private: + + protected: + wxButton* m_buttonSelectDirLeft; + wxButton* m_buttonSelectDirRight; + + public: + wxPanel* m_panelLeft; + wxBitmapButton* m_bpButtonRemovePair; + FolderHistoryBox* m_directoryLeft; + wxPanel* m_panel20; + wxBitmapButton* m_bpButtonAltCompCfg; + wxBitmapButton* m_bpButtonLocalFilter; + wxBitmapButton* m_bpButtonAltSyncCfg; + wxPanel* m_panelRight; + FolderHistoryBox* m_directoryRight; + + FolderPairPanelGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0 ); + ~FolderPairPanelGenerated(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class CompareProgressDlgGenerated +/////////////////////////////////////////////////////////////////////////////// +class CompareProgressDlgGenerated : public wxPanel +{ + private: + + protected: + wxTextCtrl* m_textCtrlStatus; + wxGauge* m_gauge2; + wxBoxSizer* bSizer42; + wxBoxSizer* bSizerFilesFound; + wxStaticText* m_staticText321; + wxStaticText* m_staticTextScanned; + wxBoxSizer* bSizerFilesRemaining; + wxStaticText* m_staticText46; + wxStaticText* m_staticTextFilesRemaining; + wxStaticText* m_staticTextDataRemaining; + wxBoxSizer* sSizerSpeed; + wxStaticText* m_staticText104; + wxStaticText* m_staticTextSpeed; + wxBoxSizer* sSizerTimeRemaining; + wxStaticText* m_staticTextTimeRemFixed; + wxStaticText* m_staticTextRemTime; + wxBoxSizer* sSizerTimeElapsed; + wxStaticText* m_staticTextTimeElapsed; + + public: + + CompareProgressDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxRAISED_BORDER ); + ~CompareProgressDlgGenerated(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class SyncProgressPanelGenerated +/////////////////////////////////////////////////////////////////////////////// +class SyncProgressPanelGenerated : public wxPanel +{ + private: + + protected: + wxBoxSizer* bSizer42; + wxBoxSizer* bSizer171; + wxStaticText* m_staticText87; + + public: + wxBoxSizer* bSizerRoot; + wxStaticBitmap* m_bitmapStatus; + wxStaticText* m_staticTextPhase; + wxAnimationCtrl* m_animCtrlSyncing; + wxBitmapButton* m_bpButtonMinimizeToTray; + wxBoxSizer* bSizerStatusText; + wxStaticText* m_staticTextStatus; + wxPanel* m_panelProgress; + wxPanel* m_panelItemsProcessed; + wxStaticText* m_staticTextProcessedObj; + wxStaticText* m_staticTextDataProcessed; + wxPanel* m_panelItemsRemaining; + wxStaticText* m_staticTextRemainingObj; + wxStaticText* m_staticTextDataRemaining; + wxPanel* m_panelTimeRemaining; + wxStaticText* m_staticTextRemTime; + wxStaticText* m_staticTextTimeElapsed; + zen::Graph2D* m_panelGraphBytes; + zen::Graph2D* m_panelGraphItems; + wxNotebook* m_notebookResult; + wxStaticLine* m_staticlineFooter; + wxBoxSizer* bSizerStdButtons; + wxBoxSizer* bSizerExecFinished; + ExecFinishedBox* m_comboBoxExecFinished; + wxButton* m_buttonClose; + wxButton* m_buttonPause; + wxButton* m_buttonStop; + + SyncProgressPanelGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL ); + ~SyncProgressPanelGenerated(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class LogPanelGenerated +/////////////////////////////////////////////////////////////////////////////// +class LogPanelGenerated : public wxPanel +{ + private: + + protected: + ToggleButton* m_bpButtonErrors; + ToggleButton* m_bpButtonWarnings; + ToggleButton* m_bpButtonInfo; + wxStaticLine* m_staticline13; + zen::Grid* m_gridMessages; + + // Virtual event handlers, overide them in your derived class + virtual void OnErrors( wxCommandEvent& event ) { event.Skip(); } + virtual void OnWarnings( wxCommandEvent& event ) { event.Skip(); } + virtual void OnInfo( wxCommandEvent& event ) { event.Skip(); } + + + public: + + LogPanelGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxTAB_TRAVERSAL ); + ~LogPanelGenerated(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class BatchDlgGenerated +/////////////////////////////////////////////////////////////////////////////// +class BatchDlgGenerated : public wxDialog +{ + private: + + protected: + wxStaticBitmap* m_bitmapBatchJob; + wxStaticText* m_staticTextDescr; + wxStaticLine* m_staticline18; + wxPanel* m_panel35; + wxStaticText* m_staticText82; + wxToggleButton* m_toggleBtnErrorIgnore; + wxToggleButton* m_toggleBtnErrorPopup; + wxToggleButton* m_toggleBtnErrorStop; + wxStaticLine* m_staticline26; + wxCheckBox* m_checkBoxShowProgress; + wxStaticText* m_staticText81; + ExecFinishedBox* m_comboBoxExecFinished; + wxStaticLine* m_staticline25; + wxCheckBox* m_checkBoxGenerateLogfile; + wxPanel* m_panelLogfile; + wxButton* m_buttonSelectLogfileDir; + wxCheckBox* m_checkBoxLogfilesLimit; + wxSpinCtrl* m_spinCtrlLogfileLimit; + wxHyperlinkCtrl* m_hyperlink17; + wxStaticLine* m_staticline13; + wxBoxSizer* bSizerStdButtons; + wxButton* m_buttonSaveAs; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnErrorIgnore( wxCommandEvent& event ) { event.Skip(); } + virtual void OnErrorPopup( wxCommandEvent& event ) { event.Skip(); } + virtual void OnErrorStop( 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(); } + + + public: + FolderHistoryBox* m_logfileDir; + + BatchDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Save as Batch Job"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~BatchDlgGenerated(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class DeleteDlgGenerated +/////////////////////////////////////////////////////////////////////////////// +class DeleteDlgGenerated : public wxDialog +{ + private: + + protected: + wxStaticBitmap* m_bitmapDeleteType; + wxStaticText* m_staticTextHeader; + wxStaticLine* m_staticline91; + wxPanel* m_panel31; + wxStaticLine* m_staticline42; + wxTextCtrl* m_textCtrlFileList; + wxStaticLine* m_staticline9; + wxBoxSizer* bSizerStdButtons; + wxCheckBox* m_checkBoxUseRecycler; + wxCheckBox* m_checkBoxDeleteBothSides; + wxButton* m_buttonOK; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnUseRecycler( wxCommandEvent& event ) { event.Skip(); } + virtual void OnDelOnBothSides( wxCommandEvent& event ) { event.Skip(); } + virtual void OnOK( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } + + + public: + + DeleteDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Delete Items"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); + ~DeleteDlgGenerated(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class FilterDlgGenerated +/////////////////////////////////////////////////////////////////////////////// +class FilterDlgGenerated : public wxDialog +{ + private: + + protected: + wxStaticBitmap* m_bitmapFilter; + wxStaticText* m_staticText44; + wxStaticLine* m_staticline17; + wxPanel* m_panel38; + wxStaticBitmap* m_bitmapInclude; + wxStaticText* m_staticText78; + wxTextCtrl* m_textCtrlInclude; + wxStaticLine* m_staticline22; + wxStaticBitmap* m_bitmapExclude; + wxStaticText* m_staticText77; + wxHyperlinkCtrl* m_hyperlink17; + wxTextCtrl* m_textCtrlExclude; + wxStaticLine* m_staticline24; + wxStaticBitmap* m_bitmapFilterDate; + wxStaticText* m_staticText79; + wxSpinCtrl* m_spinCtrlTimespan; + wxChoice* m_choiceUnitTimespan; + wxStaticLine* m_staticline23; + wxStaticBitmap* m_bitmapFilterSize; + wxStaticText* m_staticText80; + wxStaticText* m_staticText101; + wxSpinCtrl* m_spinCtrlMinSize; + wxChoice* m_choiceUnitMinSize; + wxStaticText* m_staticText102; + wxSpinCtrl* m_spinCtrlMaxSize; + wxChoice* m_choiceUnitMaxSize; + wxStaticLine* m_staticline16; + wxBoxSizer* bSizerStdButtons; + wxButton* m_buttonClear; + wxButton* m_buttonOk; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnUpdateNameFilter( wxCommandEvent& event ) { event.Skip(); } + virtual void OnHelpShowExamples( wxHyperlinkEvent& event ) { event.Skip(); } + virtual void OnUpdateChoice( wxCommandEvent& event ) { event.Skip(); } + virtual void OnClear( wxCommandEvent& event ) { event.Skip(); } + virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } + + + public: + + FilterDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); + ~FilterDlgGenerated(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class GlobalSettingsDlgGenerated +/////////////////////////////////////////////////////////////////////////////// +class GlobalSettingsDlgGenerated : public wxDialog +{ + private: + + protected: + wxStaticBitmap* m_bitmapSettings; + wxStaticText* m_staticText44; + wxStaticLine* m_staticline20; + wxPanel* m_panel39; + wxCheckBox* m_checkBoxFailSafe; + wxStaticText* m_staticText91; + wxBoxSizer* bSizerLockedFiles; + wxCheckBox* m_checkBoxCopyLocked; + wxStaticText* m_staticText92; + wxCheckBox* m_checkBoxCopyPermissions; + wxStaticText* m_staticText93; + wxStaticLine* m_staticline39; + wxStaticText* m_staticText95; + wxStaticText* m_staticText96; + wxSpinCtrl* m_spinCtrlAutoRetryCount; + wxStaticText* m_staticTextAutoRetryDelay; + wxSpinCtrl* m_spinCtrlAutoRetryDelay; + wxStaticLine* m_staticline191; + wxStaticText* m_staticText85; + wxGrid* m_gridCustomCommand; + wxBitmapButton* m_bpButtonAddRow; + wxBitmapButton* m_bpButtonRemoveRow; + wxHyperlinkCtrl* m_hyperlink17; + wxStaticLine* m_staticline192; + zen::BitmapTextButton* m_buttonResetDialogs; + wxStaticLine* m_staticline40; + wxStaticLine* m_staticline36; + wxBoxSizer* bSizerStdButtons; + wxButton* m_buttonDefault; + wxButton* m_buttonOkay; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnToggleAutoRetryCount( wxCommandEvent& event ) { event.Skip(); } + virtual void OnAddRow( wxCommandEvent& event ) { event.Skip(); } + virtual void OnRemoveRow( wxCommandEvent& event ) { event.Skip(); } + virtual void OnHelpShowExamples( wxHyperlinkEvent& event ) { event.Skip(); } + virtual void OnResetDialogs( wxCommandEvent& event ) { event.Skip(); } + virtual void OnDefault( wxCommandEvent& event ) { event.Skip(); } + virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } + + + public: + + GlobalSettingsDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Global Settings"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~GlobalSettingsDlgGenerated(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class TooltipDialogGenerated +/////////////////////////////////////////////////////////////////////////////// +class TooltipDialogGenerated : public wxDialog +{ + private: + + protected: + + public: + wxStaticBitmap* m_bitmapLeft; + wxStaticText* m_staticTextMain; + + TooltipDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); + ~TooltipDialogGenerated(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class SelectTimespanDlgGenerated +/////////////////////////////////////////////////////////////////////////////// +class SelectTimespanDlgGenerated : public wxDialog +{ + private: + + protected: + wxPanel* m_panel35; + wxCalendarCtrl* m_calendarFrom; + wxCalendarCtrl* m_calendarTo; + wxStaticLine* m_staticline21; + wxBoxSizer* bSizerStdButtons; + wxButton* m_buttonOkay; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnChangeSelectionFrom( wxCalendarEvent& event ) { event.Skip(); } + virtual void OnChangeSelectionTo( wxCalendarEvent& event ) { event.Skip(); } + virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } + + + public: + + SelectTimespanDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Select Time Span"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); + ~SelectTimespanDlgGenerated(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class AboutDlgGenerated +/////////////////////////////////////////////////////////////////////////////// +class AboutDlgGenerated : public wxDialog +{ + private: + + protected: + wxPanel* m_panel41; + wxStaticBitmap* m_bitmapLogo; + wxStaticLine* m_staticline341; + wxStaticText* m_staticText96; + wxHyperlinkCtrl* m_hyperlink11; + wxHyperlinkCtrl* m_hyperlink9; + wxHyperlinkCtrl* m_hyperlink10; + wxHyperlinkCtrl* m_hyperlink7; + wxHyperlinkCtrl* m_hyperlink14; + wxHyperlinkCtrl* m_hyperlink15; + wxHyperlinkCtrl* m_hyperlink13; + wxHyperlinkCtrl* m_hyperlink16; + wxHyperlinkCtrl* m_hyperlink12; + wxHyperlinkCtrl* m_hyperlink18; + wxPanel* m_panelDonate; + wxPanel* m_panel39; + wxAnimationCtrl* m_animCtrlWink; + wxStaticText* m_staticText83; + wxButton* m_buttonDonate; + wxStaticText* m_staticText94; + wxStaticBitmap* m_bitmap9; + wxHyperlinkCtrl* m_hyperlink1; + wxStaticBitmap* m_bitmap10; + wxHyperlinkCtrl* m_hyperlink2; + wxStaticLine* m_staticline34; + wxStaticText* m_staticText93; + wxStaticBitmap* m_bitmap13; + wxHyperlinkCtrl* m_hyperlink5; + wxStaticLine* m_staticline37; + wxStaticText* m_staticText54; + wxScrolledWindow* m_scrolledWindowTranslators; + wxFlexGridSizer* fgSizerTranslators; + wxStaticLine* m_staticline36; + wxBoxSizer* bSizerStdButtons; + wxButton* m_buttonClose; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnDonate( wxCommandEvent& event ) { event.Skip(); } + virtual void OnOK( wxCommandEvent& event ) { event.Skip(); } + + + public: + + AboutDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("About"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); + ~AboutDlgGenerated(); + +}; + +#endif //__GUI_GENERATED_H__ diff --git a/FreeFileSync/Source/ui/gui_status_handler.cpp b/FreeFileSync/Source/ui/gui_status_handler.cpp new file mode 100644 index 00000000..ec856beb --- /dev/null +++ b/FreeFileSync/Source/ui/gui_status_handler.cpp @@ -0,0 +1,477 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "gui_status_handler.h" +#include +#include +#include +#include +#include +#include "main_dlg.h" +#include "exec_finished_box.h" +#include "../lib/generate_logfile.h" +#include "../lib/resolve_path.h" +#include "../lib/status_handler_impl.h" + +using namespace zen; +using namespace xmlAccess; + + +CompareStatusHandler::CompareStatusHandler(MainDialog& dlg) : + mainDlg(dlg), + ignoreErrors(false) +{ + { +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(&mainDlg); //leads to GUI corruption problems on Linux/OS X! +#endif + + //display status panel during compare + mainDlg.compareStatus->init(*this); //clear old values before showing panel + mainDlg.auiMgr.GetPane(mainDlg.compareStatus->getAsWindow()).Show(); + mainDlg.auiMgr.Update(); + } + + mainDlg.Update(); //don't wait until idle event! + + //register keys + mainDlg.Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(CompareStatusHandler::OnKeyPressed), nullptr, this); + mainDlg.m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CompareStatusHandler::OnAbortCompare), nullptr, this); +} + + +CompareStatusHandler::~CompareStatusHandler() +{ + //unregister keys + mainDlg.Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(CompareStatusHandler::OnKeyPressed), nullptr, this); + mainDlg.m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CompareStatusHandler::OnAbortCompare), nullptr, this); + + mainDlg.compareStatus->finalize(); + mainDlg.auiMgr.GetPane(mainDlg.compareStatus->getAsWindow()).Hide(); + mainDlg.auiMgr.Update(); +} + + +void CompareStatusHandler::OnKeyPressed(wxKeyEvent& event) +{ + const int keyCode = event.GetKeyCode(); + if (keyCode == WXK_ESCAPE) + { + wxCommandEvent dummy; + OnAbortCompare(dummy); + } + + event.Skip(); +} + + +void CompareStatusHandler::initNewPhase(int objectsTotal, Int64 dataTotal, Phase phaseID) +{ + StatusHandler::initNewPhase(objectsTotal, dataTotal, phaseID); + + switch (currentPhase()) + { + case PHASE_NONE: + case PHASE_SYNCHRONIZING: + assert(false); + case PHASE_SCANNING: + break; + case PHASE_COMPARING_CONTENT: + { +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(&mainDlg); //leads to GUI corruption problems on Linux/OS X! +#endif + mainDlg.compareStatus->switchToCompareBytewise(); + mainDlg.Layout(); //show progress bar... + mainDlg.Refresh(); //remove distortion... + } + break; + } +} + + +ProcessCallback::Response CompareStatusHandler::reportError(const std::wstring& errorMessage, size_t retryNumber) +{ + //no need to implement auto-retry here: 1. user is watching 2. comparison is fast + //=> similar behavior like "ignoreErrors" which does not honor sync settings + + if (ignoreErrors) + return ProcessCallback::IGNORE_ERROR; + + forceUiRefresh(); + + bool ignoreNextErrors = false; + switch (showConfirmationDialog3(&mainDlg, DialogInfoType::ERROR2, PopupDialogCfg3(). + setDetailInstructions(errorMessage). + setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors"), ConfirmationButton3::DONT_DO_IT), + _("&Ignore"), _("&Retry"))) + { + case ConfirmationButton3::DO_IT: //ignore + ignoreErrors = ignoreNextErrors; + return ProcessCallback::IGNORE_ERROR; + + case ConfirmationButton3::DONT_DO_IT: //retry + return ProcessCallback::RETRY; + + case ConfirmationButton3::CANCEL: + abortProcessNow(); + break; + } + + assert(false); + return ProcessCallback::IGNORE_ERROR; //dummy return value +} + + +void CompareStatusHandler::reportFatalError(const std::wstring& errorMessage) +{ + forceUiRefresh(); + showNotificationDialog(&mainDlg, DialogInfoType::ERROR2, PopupDialogCfg().setTitle(_("Serious Error")).setDetailInstructions(errorMessage)); +} + + +void CompareStatusHandler::reportWarning(const std::wstring& warningMessage, bool& warningActive) +{ + if (!warningActive || ignoreErrors) //if errors are ignored, then warnings should also + return; + + forceUiRefresh(); + + //show pop-up and ask user how to handle warning + bool dontWarnAgain = false; + switch (showConfirmationDialog(&mainDlg, DialogInfoType::WARNING, + PopupDialogCfg().setDetailInstructions(warningMessage). + setCheckBox(dontWarnAgain, _("&Don't show this warning again")), + _("&Ignore"))) + { + case ConfirmationButton::DO_IT: + warningActive = !dontWarnAgain; + break; + case ConfirmationButton::CANCEL: + abortProcessNow(); + break; + } +} + + +void CompareStatusHandler::forceUiRefresh() +{ + mainDlg.compareStatus->updateStatusPanelNow(); +} + + +void CompareStatusHandler::OnAbortCompare(wxCommandEvent& event) +{ + requestAbortion(); +} + + +void CompareStatusHandler::abortProcessNow() +{ + requestAbortion(); //just make sure... + throw GuiAbortProcess(); +} + +//######################################################################################################## + +SyncStatusHandler::SyncStatusHandler(wxFrame* parentDlg, + size_t lastSyncsLogFileSizeMax, + OnGuiError handleError, + size_t automaticRetryCount, + size_t automaticRetryDelay, + const std::wstring& jobName, + const std::wstring& execWhenFinished, + std::vector& execFinishedHistory) : + progressDlg(createProgressDialog(*this, [this] { this->onProgressDialogTerminate(); }, *this, parentDlg, true, jobName, execWhenFinished, execFinishedHistory)), + lastSyncsLogFileSizeMax_(lastSyncsLogFileSizeMax), + handleError_(handleError), + automaticRetryCount_(automaticRetryCount), + automaticRetryDelay_(automaticRetryDelay), + jobName_(jobName), +startTime_(wxGetUTCTimeMillis().GetValue()) {} + + +SyncStatusHandler::~SyncStatusHandler() +{ + //------------ "on completion" command conceptually is part of the sync, not cleanup -------------------------------------- + + //decide whether to stay on status screen or exit immediately... + bool showFinalResults = true; + + if (progressDlg) + { + //execute "on completion" command (even in case of ignored errors) + if (!abortIsRequested()) //if aborted (manually), we don't execute the command + { + const std::wstring finalCommand = progressDlg->getExecWhenFinishedCommand(); //final value (after possible user modification) + if (!finalCommand.empty()) + { + if (isCloseProgressDlgCommand(finalCommand)) + showFinalResults = false; //take precedence over current visibility status + else + try + { + tryReportingError([&] { shellExecute2(expandMacros(utfCvrtTo(finalCommand)), EXEC_TYPE_SYNC); }, //throw FileError, throw X? + *this); + } + catch (...) {} + } + } + } + //------------ end of sync: begin of cleanup -------------------------------------- + + const int totalErrors = errorLog.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR); //evaluate before finalizing log + const int totalWarnings = errorLog.getItemCount(TYPE_WARNING); + + //finalize error log + std::wstring finalStatus; + if (abortIsRequested()) + { + finalStatus = _("Synchronization stopped"); + errorLog.logMsg(finalStatus, TYPE_ERROR); + } + else if (totalErrors > 0) + { + finalStatus = _("Synchronization completed with errors"); + errorLog.logMsg(finalStatus, TYPE_ERROR); + } + else if (totalWarnings > 0) + { + finalStatus = _("Synchronization completed with warnings"); + errorLog.logMsg(finalStatus, TYPE_WARNING); //give status code same warning priority as display category! + } + else + { + if (getObjectsTotal(PHASE_SYNCHRONIZING) == 0 && //we're past "initNewPhase(PHASE_SYNCHRONIZING)" at this point! + getDataTotal (PHASE_SYNCHRONIZING) == 0) + finalStatus = _("Nothing to synchronize"); //even if "ignored conflicts" occurred! + else + finalStatus = _("Synchronization completed successfully"); + errorLog.logMsg(finalStatus, TYPE_INFO); + } + + const SummaryInfo summary = + { + jobName_, finalStatus, + getObjectsCurrent(PHASE_SYNCHRONIZING), getDataCurrent(PHASE_SYNCHRONIZING), + getObjectsTotal (PHASE_SYNCHRONIZING), getDataTotal (PHASE_SYNCHRONIZING), + (wxGetUTCTimeMillis().GetValue() - startTime_) / 1000 + }; + + try + { + saveToLastSyncsLog(summary, errorLog, lastSyncsLogFileSizeMax_); //throw FileError + } + catch (FileError&) {} + + if (progressDlg) + { + //notify to progressDlg that current process has ended + if (showFinalResults) + { + if (abortIsRequested()) + progressDlg->processHasFinished(SyncProgressDialog::RESULT_ABORTED, errorLog); //enable okay and close events + else if (totalErrors > 0) + progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_ERROR, errorLog); + else if (totalWarnings > 0) + progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS, errorLog); + else + progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS, errorLog); + } + else + progressDlg->closeWindowDirectly(); + + //wait until progress dialog notified shutdown via onProgressDialogTerminate() + //-> required since it has our "this" pointer captured in lambda "notifyWindowTerminate"! + //-> nicely manages dialog lifetime + while (progressDlg) + { + wxTheApp->Yield(); //*first* refresh GUI (removing flicker) before sleeping! + boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL)); + } + } +} + + +void SyncStatusHandler::initNewPhase(int objectsTotal, Int64 dataTotal, Phase phaseID) +{ + assert(phaseID == PHASE_SYNCHRONIZING); + StatusHandler::initNewPhase(objectsTotal, dataTotal, phaseID); + if (progressDlg) + progressDlg->initNewPhase(); //call after "StatusHandler::initNewPhase" +} + + +void SyncStatusHandler::updateProcessedData(int objectsDelta, Int64 dataDelta) +{ + StatusHandler::updateProcessedData(objectsDelta, dataDelta); + if (progressDlg) + progressDlg->notifyProgressChange(); //noexcept + //note: this method should NOT throw in order to properly allow undoing setting of statistics! +} + + +void SyncStatusHandler::reportInfo(const std::wstring& text) +{ + StatusHandler::reportInfo(text); + errorLog.logMsg(text, TYPE_INFO); +} + + +ProcessCallback::Response SyncStatusHandler::reportError(const std::wstring& errorMessage, size_t retryNumber) +{ + //auto-retry + if (retryNumber < automaticRetryCount_) + { + errorLog.logMsg(errorMessage + L"\n=> " + + _P("Automatic retry in 1 second...", "Automatic retry in %x seconds...", automaticRetryDelay_), TYPE_INFO); + //delay + const int iterations = static_cast(1000 * automaticRetryDelay_ / UI_UPDATE_INTERVAL); //always round down: don't allow for negative remaining time below + for (int i = 0; i < iterations; ++i) + { + reportStatus(_("Error") + L": " + _P("Automatic retry in 1 second...", "Automatic retry in %x seconds...", + (1000 * automaticRetryDelay_ - i * UI_UPDATE_INTERVAL + 999) / 1000)); //integer round up + boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL)); + } + return ProcessCallback::RETRY; + } + + + //always, except for "retry": + zen::ScopeGuard guardWriteLog = zen::makeGuard([&] { errorLog.logMsg(errorMessage, TYPE_ERROR); }); + + switch (handleError_) + { + case ON_GUIERROR_POPUP: + { + if (!progressDlg) abortProcessNow(); + PauseTimers dummy(*progressDlg); + forceUiRefresh(); + + bool ignoreNextErrors = false; + switch (showConfirmationDialog3(progressDlg->getWindowIfVisible(), DialogInfoType::ERROR2, PopupDialogCfg3(). + setDetailInstructions(errorMessage). + setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors"), ConfirmationButton3::DONT_DO_IT), + _("&Ignore"), _("&Retry"))) + { + case ConfirmationButton3::DO_IT: //ignore + if (ignoreNextErrors) //falsify only + handleError_ = ON_GUIERROR_IGNORE; + return ProcessCallback::IGNORE_ERROR; + + case ConfirmationButton3::DONT_DO_IT: //retry + guardWriteLog.dismiss(); + errorLog.logMsg(errorMessage + L"\n=> " + _("Retrying operation..."), TYPE_INFO); //explain why there are duplicate "doing operation X" info messages in the log! + return ProcessCallback::RETRY; + + case ConfirmationButton3::CANCEL: + abortProcessNow(); + break; + } + } + break; + + case ON_GUIERROR_IGNORE: + return ProcessCallback::IGNORE_ERROR; + } + + assert(false); + return ProcessCallback::IGNORE_ERROR; //dummy value +} + + +void SyncStatusHandler::reportFatalError(const std::wstring& errorMessage) +{ + errorLog.logMsg(errorMessage, TYPE_FATAL_ERROR); + + switch (handleError_) + { + case ON_GUIERROR_POPUP: + { + if (!progressDlg) abortProcessNow(); + PauseTimers dummy(*progressDlg); + forceUiRefresh(); + + bool ignoreNextErrors = false; + switch (showConfirmationDialog(progressDlg->getWindowIfVisible(), DialogInfoType::ERROR2, + PopupDialogCfg().setTitle(_("Serious Error")). + setDetailInstructions(errorMessage). + setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors")), + _("&Ignore"))) + { + case ConfirmationButton::DO_IT: + if (ignoreNextErrors) //falsify only + handleError_ = ON_GUIERROR_IGNORE; + break; + case ConfirmationButton::CANCEL: + abortProcessNow(); + break; + } + } + break; + + case ON_GUIERROR_IGNORE: + break; + } +} + + +void SyncStatusHandler::reportWarning(const std::wstring& warningMessage, bool& warningActive) +{ + errorLog.logMsg(warningMessage, TYPE_WARNING); + + if (!warningActive) + return; + + switch (handleError_) + { + case ON_GUIERROR_POPUP: + { + if (!progressDlg) abortProcessNow(); + PauseTimers dummy(*progressDlg); + forceUiRefresh(); + + bool dontWarnAgain = false; + switch (showConfirmationDialog(progressDlg->getWindowIfVisible(), DialogInfoType::WARNING, + PopupDialogCfg().setDetailInstructions(warningMessage). + setCheckBox(dontWarnAgain, _("&Don't show this warning again")), + _("&Ignore"))) + { + case ConfirmationButton::DO_IT: + warningActive = !dontWarnAgain; + break; + case ConfirmationButton::CANCEL: + abortProcessNow(); + break; + } + } + break; + + case ON_GUIERROR_IGNORE: + break; //if errors are ignored, then warnings should be, too + } +} + + +void SyncStatusHandler::forceUiRefresh() +{ + if (progressDlg) + progressDlg->updateGui(); +} + + +void SyncStatusHandler::abortProcessNow() +{ + requestAbortion(); //just make sure... + throw GuiAbortProcess(); //abort can be triggered by progressDlg +} + + +void SyncStatusHandler::onProgressDialogTerminate() +{ + //it's responsibility of "progressDlg" to call requestAbortion() when closing dialog + progressDlg = nullptr; +} diff --git a/FreeFileSync/Source/ui/gui_status_handler.h b/FreeFileSync/Source/ui/gui_status_handler.h new file mode 100644 index 00000000..5f5e0817 --- /dev/null +++ b/FreeFileSync/Source/ui/gui_status_handler.h @@ -0,0 +1,88 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef GUISTATUSHANDLER_H_INCLUDED +#define GUISTATUSHANDLER_H_INCLUDED + +#include +#include +//#include +#include "progress_indicator.h" +#include "../lib/status_handler.h" +#include "../lib/process_xml.h" +#include "main_dlg.h" + + +//Exception class used to abort the "compare" and "sync" process +class GuiAbortProcess {}; + +//classes handling sync and compare error as well as status information + +//CompareStatusHandler(CompareProgressDialog) will internally process Window messages! disable GUI controls to avoid unexpected callbacks! +class CompareStatusHandler : private wxEvtHandler, public zen::StatusHandler //throw GuiAbortProcess +{ +public: + CompareStatusHandler(MainDialog& dlg); + ~CompareStatusHandler(); + + virtual void initNewPhase(int objectsTotal, zen::Int64 dataTotal, Phase phaseID); + virtual void forceUiRefresh(); + + virtual Response reportError(const std::wstring& text, size_t retryNumber); + virtual void reportFatalError(const std::wstring& errorMessage); + virtual void reportWarning(const std::wstring& warningMessage, bool& warningActive); + + virtual void abortProcessNow(); //throw GuiAbortProcess + +private: + void OnKeyPressed(wxKeyEvent& event); + void OnAbortCompare(wxCommandEvent& event); //handle abort button click + + MainDialog& mainDlg; + bool ignoreErrors; +}; + + +//SyncStatusHandler(SyncProgressDialog) will internally process Window messages! disable GUI controls to avoid unexpected callbacks! +class SyncStatusHandler : public zen::StatusHandler +{ +public: + SyncStatusHandler(wxFrame* parentDlg, + size_t lastSyncsLogFileSizeMax, + xmlAccess::OnGuiError handleError, + size_t automaticRetryCount, + size_t automaticRetryDelay, + const std::wstring& jobName, + const std::wstring& execWhenFinished, + std::vector& execFinishedHistory); + ~SyncStatusHandler(); + + virtual void initNewPhase (int objectsTotal, zen::Int64 dataTotal, Phase phaseID); + virtual void updateProcessedData(int objectsDelta, zen::Int64 dataDelta); + virtual void reportInfo(const std::wstring& text); + virtual void forceUiRefresh(); + + virtual Response reportError(const std::wstring& text, size_t retryNumber); + virtual void reportFatalError(const std::wstring& errorMessage); + virtual void reportWarning(const std::wstring& warningMessage, bool& warningActive); + + virtual void abortProcessNow(); //throw GuiAbortProcess + +private: + void onProgressDialogTerminate(); + + SyncProgressDialog* progressDlg; //managed to have shorter lifetime than this handler! + const size_t lastSyncsLogFileSizeMax_; + xmlAccess::OnGuiError handleError_; + zen::ErrorLog errorLog; + const size_t automaticRetryCount_; + const size_t automaticRetryDelay_; + const std::wstring jobName_; + const int64_t startTime_; //don't use wxStopWatch: may overflow after a few days due to ::QueryPerformanceCounter() +}; + + +#endif // GUISTATUSHANDLER_H_INCLUDED diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp new file mode 100644 index 00000000..2e78152e --- /dev/null +++ b/FreeFileSync/Source/ui/main_dlg.cpp @@ -0,0 +1,4570 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "main_dlg.h" +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "check_version.h" +#include "gui_status_handler.h" +#include "sync_cfg.h" +#include "small_dlgs.h" +#include "progress_indicator.h" +#include "folder_pair.h" +#include "search.h" +#include "batch_config.h" +#include "triple_splitter.h" +#include "app_icon.h" +//#include "config_history.h" +#include "../comparison.h" +#include "../synchronization.h" +#include "../algorithm.h" +#include "../lib/resolve_path.h" +#include "../lib/ffs_paths.h" +#include "../lib/help_provider.h" +#include "../lib/lock_holder.h" +#include "../lib/localization.h" + +#ifdef ZEN_MAC +#include +#endif + +using namespace zen; +using namespace std::rel_ops; + + +namespace +{ +struct wxClientHistoryData : public wxClientData //we need a wxClientData derived class to tell wxWidgets to take object ownership! +{ + wxClientHistoryData(const Zstring& cfgFile, int lastUseIndex) : cfgFile_(cfgFile), lastUseIndex_(lastUseIndex) {} + + Zstring cfgFile_; + int lastUseIndex_; //support sorting history by last usage, the higher the index the more recent the usage +}; + +IconBuffer::IconSize convert(xmlAccess::FileIconSize isize) +{ + using namespace xmlAccess; + switch (isize) + { + case ICON_SIZE_SMALL: + return IconBuffer::SIZE_SMALL; + case ICON_SIZE_MEDIUM: + return IconBuffer::SIZE_MEDIUM; + case ICON_SIZE_LARGE: + return IconBuffer::SIZE_LARGE; + } + return IconBuffer::SIZE_SMALL; +} +} + + +class DirectoryNameMainImpl : public DirectoryName +{ +public: + DirectoryNameMainImpl(MainDialog& mainDlg, + wxWindow& dropWindow1, + Grid& dropGrid, + wxButton& dirSelectButton, + FolderHistoryBox& dirName, + wxStaticText& staticText) : + DirectoryName(dropWindow1, dirSelectButton, dirName, &staticText, &dropGrid.getMainWin()), + mainDlg_(mainDlg) {} + + virtual bool acceptDrop(const std::vector& droppedFiles, const wxPoint& clientPos, const wxWindow& wnd) + { + if (std::any_of(droppedFiles.begin(), droppedFiles.end(), [](const wxString& filename) + { + using namespace xmlAccess; + try + { + switch (getXmlType(utfCvrtTo(filename))) //throw FfsXmlError + { + case XML_TYPE_GUI: + case XML_TYPE_BATCH: + return true; + case XML_TYPE_GLOBAL: + case XML_TYPE_OTHER: + break; + } + } + catch (const FfsXmlError&) {} + + return false; + })) + { + mainDlg_.loadConfiguration(toZ(droppedFiles)); + return false; + } + + //=> return true: change directory selection via drag and drop + return true; + } + +private: + DirectoryNameMainImpl(const DirectoryNameMainImpl&); + DirectoryNameMainImpl& operator=(const DirectoryNameMainImpl&); + + MainDialog& mainDlg_; +}; + +//------------------------------------------------------------------ +/* class hierarchy: + + template<> + FolderPairPanelBasic + /|\ + | + template<> + FolderPairCallback FolderPairPanelGenerated + /|\ /|\ + _________|________ ________| + | | | + FolderPairFirst FolderPairPanel +*/ + +template +class FolderPairCallback : public FolderPairPanelBasic //implements callback functionality to MainDialog as imposed by FolderPairPanelBasic +{ +public: + FolderPairCallback(GuiPanel& basicPanel, MainDialog& mainDialog) : + FolderPairPanelBasic(basicPanel), //pass FolderPairPanelGenerated part... + mainDlg(mainDialog) {} + +private: + virtual MainConfiguration getMainConfig() const { return mainDlg.getConfig().mainCfg; } + virtual wxWindow* getParentWindow() { return &mainDlg; } + virtual std::unique_ptr& getFilterCfgOnClipboardRef() { return mainDlg.filterCfgOnClipboard; } + + virtual void onAltCompCfgChange () { mainDlg.applyCompareConfig(); } + virtual void onAltSyncCfgChange () { mainDlg.applySyncConfig(); } + virtual void onLocalFilterCfgChange() { mainDlg.applyFilterConfig(); } //re-apply filter + + MainDialog& mainDlg; +}; + + +class FolderPairPanel : + public FolderPairPanelGenerated, //FolderPairPanel "owns" FolderPairPanelGenerated! + public FolderPairCallback +{ +public: + FolderPairPanel(wxWindow* parent, MainDialog& mainDialog) : + FolderPairPanelGenerated(parent), + FolderPairCallback(static_cast(*this), mainDialog), //pass FolderPairPanelGenerated part... + dirNameLeft (*m_panelLeft, *m_buttonSelectDirLeft, *m_directoryLeft), + dirNameRight(*m_panelRight, *m_buttonSelectDirRight, *m_directoryRight) + { + dirNameLeft .Connect(EVENT_ON_DIR_SELECTED, wxCommandEventHandler(MainDialog::onDirSelected), nullptr, &mainDialog); + dirNameRight.Connect(EVENT_ON_DIR_SELECTED, wxCommandEventHandler(MainDialog::onDirSelected), nullptr, &mainDialog); + + dirNameLeft .Connect(EVENT_ON_DIR_MANUAL_CORRECTION, wxCommandEventHandler(MainDialog::onDirManualCorrection), nullptr, &mainDialog); + dirNameRight.Connect(EVENT_ON_DIR_MANUAL_CORRECTION, wxCommandEventHandler(MainDialog::onDirManualCorrection), nullptr, &mainDialog); + } + + void setValues(const Zstring& leftDir, + const Zstring& rightDir, + AltCompCfgPtr cmpCfg, + AltSyncCfgPtr syncCfg, + const FilterConfig& filter) + { + setConfig(cmpCfg, syncCfg, filter); + dirNameLeft .setName(toWx(leftDir)); + dirNameRight.setName(toWx(rightDir)); + } + Zstring getLeftDir () const { return toZ(dirNameLeft .getName()); } + Zstring getRightDir() const { return toZ(dirNameRight.getName()); } + +private: + //support for drag and drop + DirectoryName dirNameLeft; + DirectoryName dirNameRight; +}; + + +class FolderPairFirst : public FolderPairCallback +{ +public: + FolderPairFirst(MainDialog& mainDialog) : + FolderPairCallback(mainDialog, mainDialog), + + //prepare drag & drop + dirNameLeft(mainDialog, + *mainDialog.m_panelTopLeft, + *mainDialog.m_gridMainL, + *mainDialog.m_buttonSelectDirLeft, + *mainDialog.m_directoryLeft, + *mainDialog.m_staticTextResolvedPathL), + dirNameRight(mainDialog, + *mainDialog.m_panelTopRight, + *mainDialog.m_gridMainR, + *mainDialog.m_buttonSelectDirRight, + *mainDialog.m_directoryRight, + *mainDialog.m_staticTextResolvedPathR) + { + dirNameLeft .Connect(EVENT_ON_DIR_SELECTED, wxCommandEventHandler(MainDialog::onDirSelected), nullptr, &mainDialog); + dirNameRight.Connect(EVENT_ON_DIR_SELECTED, wxCommandEventHandler(MainDialog::onDirSelected), nullptr, &mainDialog); + + dirNameLeft .Connect(EVENT_ON_DIR_MANUAL_CORRECTION, wxCommandEventHandler(MainDialog::onDirManualCorrection), nullptr, &mainDialog); + dirNameRight.Connect(EVENT_ON_DIR_MANUAL_CORRECTION, wxCommandEventHandler(MainDialog::onDirManualCorrection), nullptr, &mainDialog); + } + + void setValues(const Zstring& leftDir, + const Zstring& rightDir, + AltCompCfgPtr cmpCfg, + AltSyncCfgPtr syncCfg, + const FilterConfig& filter) + { + setConfig(cmpCfg, syncCfg, filter); + dirNameLeft .setName(toWx(leftDir)); + dirNameRight.setName(toWx(rightDir)); + } + Zstring getLeftDir () const { return toZ(dirNameLeft .getName()); } + Zstring getRightDir() const { return toZ(dirNameRight.getName()); } + +private: + //support for drag and drop + DirectoryNameMainImpl dirNameLeft; + DirectoryNameMainImpl dirNameRight; +}; + + +#ifdef ZEN_WIN +class PanelMoveWindow : public MouseMoveWindow +{ +public: + PanelMoveWindow(MainDialog& mainDlg) : + MouseMoveWindow(mainDlg, false), //don't include main dialog itself, thereby prevent various mouse capture lost issues + mainDlg_(mainDlg) {} + + virtual bool allowMove(const wxMouseEvent& event) + { + if (wxPanel* panel = dynamic_cast(event.GetEventObject())) + { + const wxAuiPaneInfo& paneInfo = mainDlg_.auiMgr.GetPane(panel); + if (paneInfo.IsOk() && + paneInfo.IsFloating()) + return false; //prevent main dialog move + } + + return true; //allow dialog move + } + +private: + MainDialog& mainDlg_; +}; +#endif + + +namespace +{ +//workaround for wxWidgets bug failing to update menu item bitmaps (affects Windows- and Linux-build) +void setMenuItemImage(wxMenuItem*& menuItem, const wxBitmap& bmp) +{ + assert(menuItem->GetKind() == wxITEM_NORMAL); + + //support polling + if (isEqual(bmp, menuItem->GetBitmap())) + return; + + if (wxMenu* menu = menuItem->GetMenu()) + { + int pos = menu->GetMenuItems().IndexOf(menuItem); + if (pos != wxNOT_FOUND) + { + /* + menu->Remove(menuItem); ->this simple sequence crashes on Kubuntu x64, wxWidgets 2.9.2 + menu->Insert(pos, menuItem); + */ + const bool enabled = menuItem->IsEnabled(); + wxMenuItem* newItem = new wxMenuItem(menu, menuItem->GetId(), menuItem->GetItemLabel()); + + newItem->SetBitmap(bmp); +#ifdef __WXMSW__ //not availabe on wxGTK or wxOSX + //for some inconceivable reason wxWidgets is not consistent with itself and renders disabled icons + //just greyscale instead of brightened like bitmap buttons; much better: + newItem->SetDisabledBitmap(bmp.ConvertToDisabled()); +#endif + bool isDestroyed = menu->Destroy(menuItem); //actual workaround + assert(isDestroyed); + (void)isDestroyed; + menuItem = menu->Insert(pos, newItem); //don't forget to update input item pointer! + + if (!enabled) + menuItem->Enable(false); //do not enable BEFORE appending item! wxWidgets screws up for yet another crappy reason + } + } +} + + +void updateTopButton(wxBitmapButton& btn, const wxBitmap& bmp, const wxString& variantName, bool makeGrey) +{ + wxImage labelImage = createImageFromText(btn.GetLabel(), btn.GetFont(), wxSystemSettings::GetColour(makeGrey ? wxSYS_COLOUR_GRAYTEXT : wxSYS_COLOUR_BTNTEXT)); + wxImage variantImage = createImageFromText(variantName, wxFont(wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxBOLD), wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); + wxImage descrImage = stackImages(labelImage, variantImage, ImageStackLayout::VERTICAL, ImageStackAlignment::CENTER); + const wxImage& iconImage = makeGrey ? greyScale(bmp.ConvertToImage()) : bmp.ConvertToImage(); + + wxImage dynImage = btn.GetLayoutDirection() != wxLayout_RightToLeft ? + stackImages(iconImage, descrImage, ImageStackLayout::HORIZONTAL, ImageStackAlignment::CENTER, 5) : + stackImages(descrImage, iconImage, ImageStackLayout::HORIZONTAL, ImageStackAlignment::CENTER, 5); + + //SetMinSize() instead of SetSize() is needed here for wxWindows layout determination to work corretly + wxSize minSize = dynImage.GetSize() + wxSize(10, 10); //add border space + minSize.x = std::max(minSize.x, 180); + + btn.SetMinSize(minSize); + btn.SetBitmapLabel(wxBitmap(dynImage)); + //SetLabel() calls confuse wxBitmapButton in the disabled state and it won't show the image! workaround: + btn.SetBitmapDisabled(wxBitmap(dynImage.ConvertToDisabled())); +} + +//################################################################################################################################## + +xmlAccess::XmlGlobalSettings retrieveGlobalCfgFromDisk() //blocks on GUI on errors! +{ + using namespace xmlAccess; + XmlGlobalSettings globalCfg; + try + { + if (fileExists(getGlobalConfigFile())) + readConfig(globalCfg); //throw FfsXmlError + //else: globalCfg already has default values + } + catch (const FfsXmlError& e) + { + if (e.getSeverity() != FfsXmlError::WARNING) //ignore parsing errors: should be migration problems only *cross-fingers* + showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); //no parent window: main dialog not yet created! + assert(false); + } + return globalCfg; +} +} + + +void MainDialog::create() +{ + using namespace xmlAccess; + const XmlGlobalSettings globalSettings = retrieveGlobalCfgFromDisk(); + + std::vector filenames = globalSettings.gui.lastUsedConfigFiles; //2. now try last used files + + //------------------------------------------------------------------------------------------ + //check existence of all files in parallel: + RunUntilFirstHit findFirstMissing; + + std::for_each(filenames.begin(), filenames.end(), [&](const Zstring& filename) + { + findFirstMissing.addJob([=] { return filename.empty() /*ever empty??*/ || !fileExists(filename) ? zen::make_unique() : nullptr; }); + }); + //potentially slow network access: give all checks 500ms to finish + const bool allFilesExist = findFirstMissing.timedWait(boost::posix_time::milliseconds(500)) && //false: time elapsed + !findFirstMissing.get(); //no missing + if (!allFilesExist) + filenames.clear(); //we do NOT want to show an error due to last config file missing on application start! + //------------------------------------------------------------------------------------------ + + if (filenames.empty()) + { + if (zen::fileExists(lastRunConfigName())) //3. try to load auto-save config + filenames.push_back(lastRunConfigName()); + } + + XmlGuiConfig guiCfg; //structure to receive gui settings with default values + + if (filenames.empty()) + { + //add default exclusion filter: this is only ever relevant when creating new configurations! + //a default XmlGuiConfig does not need these user-specific exclusions! + Zstring& excludeFilter = guiCfg.mainCfg.globalFilter.excludeFilter; + if (!excludeFilter.empty() && !endsWith(excludeFilter, Zstr("\n"))) + excludeFilter += Zstr("\n"); + excludeFilter += globalSettings.gui.defaultExclusionFilter; + } + else + try + { + readAnyConfig(filenames, guiCfg); //throw FfsXmlError + } + catch (const FfsXmlError& e) + { + if (e.getSeverity() == FfsXmlError::WARNING) + showNotificationDialog(nullptr, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(e.toString())); + //what about simulating changed config on parsing errors???? + else + showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); + } + + //------------------------------------------------------------------------------------------ + + create(guiCfg, filenames, &globalSettings, false); +} + + +void MainDialog::create(const xmlAccess::XmlGuiConfig& guiCfg, + const std::vector& referenceFiles, + const xmlAccess::XmlGlobalSettings* globalSettings, + bool startComparison) +{ + const xmlAccess::XmlGlobalSettings& globSett = globalSettings ? *globalSettings : retrieveGlobalCfgFromDisk(); + try + { + //we need to set language *before* creating MainDialog! + setLanguage(globSett.programLanguage); //throw FileError + } + catch (const FileError& e) + { + showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); + //continue! + } + + MainDialog* frame = new MainDialog(guiCfg, referenceFiles, globSett, startComparison); + frame->Show(); +#ifdef ZEN_MAC + ProcessSerialNumber psn = { 0, kCurrentProcess }; + ::SetFrontProcess(&psn); //call before TransformProcessType() so that OSX menu is updated correctly + ::TransformProcessType(&psn, kProcessTransformToForegroundApplication); //show dock icon, even if we're not an application bundle + //if the executable is not yet in a bundle or if it is called through a launcher, we need to set focus manually: +#endif +} + + +MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg, + const std::vector& referenceFiles, + const xmlAccess::XmlGlobalSettings& globalSettings, + bool startComparison) : + MainDialogGenerated(nullptr), + folderHistoryLeft (std::make_shared()), //make sure it is always bound + folderHistoryRight(std::make_shared()), // + focusWindowAfterSearch(nullptr) +{ + m_directoryLeft ->init(folderHistoryLeft); + m_directoryRight->init(folderHistoryRight); + + //setup sash: detach + reparent: + m_splitterMain->SetSizer(nullptr); //alas wxFormbuilder doesn't allow us to have child windows without a sizer, so we have to remove it here + m_splitterMain->setupWindows(m_gridMainL, m_gridMainC, m_gridMainR); + +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! +#endif + + setRelativeFontSize(*m_buttonCompare, 1.4); + setRelativeFontSize(*m_buttonSync, 1.4); + setRelativeFontSize(*m_buttonCancel, 1.4); + + //---------------- support for dockable gui style -------------------------------- + bSizerPanelHolder->Detach(m_panelTopButtons); + bSizerPanelHolder->Detach(m_panelDirectoryPairs); + bSizerPanelHolder->Detach(m_gridNavi); + bSizerPanelHolder->Detach(m_panelCenter); + bSizerPanelHolder->Detach(m_panelConfig); + bSizerPanelHolder->Detach(m_panelFilter); + bSizerPanelHolder->Detach(m_panelViewFilter); + bSizerPanelHolder->Detach(m_panelStatistics); + + auiMgr.SetManagedWindow(this); + auiMgr.SetFlags(wxAUI_MGR_DEFAULT | wxAUI_MGR_LIVE_RESIZE); + + compareStatus = make_unique(*this); //integrate the compare status panel (in hidden state) + + //caption required for all panes that can be manipulated by the users => used by context menu + auiMgr.AddPane(m_panelCenter, + wxAuiPaneInfo().Name(L"Panel3").CenterPane().PaneBorder(false)); + + auiMgr.AddPane(m_panelDirectoryPairs, + wxAuiPaneInfo().Name(L"Panel2").Layer(2).Top().Caption(_("Folder Pairs")).CaptionVisible(false).PaneBorder(false).Gripper()); + + auiMgr.AddPane(m_panelSearch, + wxAuiPaneInfo().Name(L"PanelFind").Layer(2).Bottom().Caption(_("Find")).CaptionVisible(false).PaneBorder(false).Gripper().MinSize(200, m_bpButtonHideSearch->GetSize().GetHeight()).Hide()); + + auiMgr.AddPane(m_gridNavi, + wxAuiPaneInfo().Name(L"Panel10").Layer(3).Left().Position(1).Caption(_("Overview")).MinSize(300, m_gridNavi->GetSize().GetHeight())); //MinSize(): just default size, see comment below + + auiMgr.AddPane(m_panelConfig, + wxAuiPaneInfo().Name(L"Panel4").Layer(3).Left().Position(2).Caption(_("Configuration")).MinSize(m_listBoxHistory->GetSize().GetWidth(), m_panelConfig->GetSize().GetHeight())); + + //set comparison button label tentatively for m_panelTopButtons to receive final height: + updateTopButton(*m_buttonCompare, getResourceImage(L"compare"), L"Dummy", false); + m_panelTopButtons->GetSizer()->SetSizeHints(m_panelTopButtons); //~=Fit() + SetMinSize() + + auiMgr.AddPane(m_panelTopButtons, + wxAuiPaneInfo().Name(L"Panel1").Layer(4).Top().Row(1).Caption(_("Main Bar")).CaptionVisible(false).PaneBorder(false).Gripper().MinSize(-1, m_panelTopButtons->GetSize().GetHeight())); + //note: min height is calculated incorrectly by wxAuiManager if panes with and without caption are in the same row => use smaller min-size + + auiMgr.AddPane(compareStatus->getAsWindow(), + wxAuiPaneInfo().Name(L"Panel9").Layer(4).Top().Row(2).CaptionVisible(false).PaneBorder(false).Hide()); + + auiMgr.AddPane(m_panelFilter, + wxAuiPaneInfo().Name(L"Panel5").Layer(4).Bottom().Position(1).Caption(_("Filter Files")).MinSize(m_bpButtonFilter->GetSize().GetWidth(), m_panelFilter->GetSize().GetHeight())); + + auiMgr.AddPane(m_panelViewFilter, + wxAuiPaneInfo().Name(L"Panel6").Layer(4).Bottom().Position(2).Caption(_("Select View")).MinSize(m_bpButtonShowDoNothing->GetSize().GetWidth(), m_panelViewFilter->GetSize().GetHeight())); + + auiMgr.AddPane(m_panelStatistics, + wxAuiPaneInfo().Name(L"Panel7").Layer(4).Bottom().Position(3).Caption(_("Statistics")).MinSize(m_bitmapData->GetSize().GetWidth() + m_staticTextData->GetSize().GetWidth(), m_panelStatistics->GetSize().GetHeight())); + + auiMgr.Update(); + + //give panel captions bold typeface + if (wxAuiDockArt* artProvider = auiMgr.GetArtProvider()) + { + wxFont font = artProvider->GetFont(wxAUI_DOCKART_CAPTION_FONT); + font.SetWeight(wxFONTWEIGHT_BOLD); + font.SetPointSize(wxNORMAL_FONT->GetPointSize()); //= larger than the wxAuiDockArt default; looks better on OS X + artProvider->SetFont(wxAUI_DOCKART_CAPTION_FONT, font); + + //accessibility: fix wxAUI drawing black text on black background on high-contrast color schemes: + artProvider->SetColor(wxAUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); + } + + auiMgr.GetPane(m_gridNavi).MinSize(-1, -1); //we successfully tricked wxAuiManager into setting an initial Window size :> incomplete API anyone?? + auiMgr.Update(); // + + defaultPerspective = auiMgr.SavePerspective(); + //---------------------------------------------------------------------------------- + //register view layout context menu + m_panelTopButtons->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(MainDialog::OnContextSetLayout), nullptr, this); + m_panelConfig ->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(MainDialog::OnContextSetLayout), nullptr, this); + m_panelFilter ->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(MainDialog::OnContextSetLayout), nullptr, this); + m_panelViewFilter->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(MainDialog::OnContextSetLayout), nullptr, this); + m_panelStatistics->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(MainDialog::OnContextSetLayout), nullptr, this); + m_panelStatusBar ->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(MainDialog::OnContextSetLayout), nullptr, this); + //---------------------------------------------------------------------------------- + + //sort grids + m_gridMainL->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickL ), nullptr, this); + m_gridMainC->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickC ), nullptr, this); + m_gridMainR->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickR ), nullptr, this); + + m_gridMainL->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextL ), nullptr, this); + m_gridMainC->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextC ), nullptr, this); + m_gridMainR->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextR ), nullptr, this); + + //grid context menu + m_gridMainL->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onMainGridContextL), nullptr, this); + m_gridMainC->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onMainGridContextC), nullptr, this); + m_gridMainR->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onMainGridContextR), nullptr, this); + m_gridNavi ->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onNaviGridContext ), nullptr, this); + + m_gridMainL->Connect(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEventHandler(MainDialog::onGridDoubleClickL), nullptr, this ); + m_gridMainR->Connect(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEventHandler(MainDialog::onGridDoubleClickR), nullptr, this ); + + m_gridNavi->Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(MainDialog::onNaviSelection), nullptr, this); + //---------------------------------------------------------------------------------- + + m_panelSearch->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::OnSearchPanelKeyPressed), nullptr, this); + + //set tool tips with (non-translated!) short cut hint + m_bpButtonOpen ->SetToolTip(_("Open...") + L" (Ctrl+O)"); + m_bpButtonSave ->SetToolTip(_("Save") + L" (Ctrl+S)"); + m_buttonCompare ->SetToolTip(_("Compare both sides") + L" (F5)"); + m_bpButtonCmpConfig ->SetToolTip(_("Comparison settings") + L" (F6)"); + m_bpButtonSyncConfig->SetToolTip(_("Synchronization settings") + L" (F7)"); + m_buttonSync ->SetToolTip(_("Start synchronization") + L" (F8)"); + + gridDataView = std::make_shared(); + treeDataView = std::make_shared(); + + cleanedUp = false; + processingGlobalKeyEvent = false; + +#ifdef ZEN_WIN + new PanelMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere... //ownership passed to "this" +#endif + + //set icons for this dialog + SetIcon(getFfsIcon()); //set application icon + + m_bpButtonSyncConfig->SetBitmapLabel(getResourceImage(L"cfg_sync")); + m_bpButtonCmpConfig ->SetBitmapLabel(getResourceImage(L"cfg_compare")); + m_bpButtonOpen ->SetBitmapLabel(getResourceImage(L"load")); + m_bpButtonBatchJob ->SetBitmapLabel(getResourceImage(L"batch")); + m_bpButtonAddPair ->SetBitmapLabel(getResourceImage(L"item_add")); + m_bpButtonHideSearch->SetBitmapLabel(getResourceImage(L"close_panel")); + + //we can't use a wxButton for cancel: it's rendered smaller on OS X than a wxBitmapButton! + setBitmapTextLabel(*m_buttonCancel, wxImage(), m_buttonCancel->GetLabel()); + + { + IconBuffer tmp(IconBuffer::SIZE_SMALL); + const wxBitmap& bmpFile = tmp.genericFileIcon(); + const wxBitmap& bmpDir = tmp.genericDirIcon(); + + m_bitmapSmallDirectoryLeft ->SetBitmap(bmpDir); + m_bitmapSmallFileLeft ->SetBitmap(bmpFile); + m_bitmapSmallDirectoryRight->SetBitmap(bmpDir); + m_bitmapSmallFileRight ->SetBitmap(bmpFile); + } + + //menu icons: workaround for wxWidgets: small hack to update menu items: actually this is a wxWidgets bug (affects Windows- and Linux-build) + setMenuItemImage(m_menuItem10, getResourceImage(L"compare_small")); + setMenuItemImage(m_menuItem11, getResourceImage(L"sync_small")); + + setMenuItemImage(m_menuItemLoad, getResourceImage(L"load_small")); + setMenuItemImage(m_menuItemSave, getResourceImage(L"save_small")); + + setMenuItemImage(m_menuItemGlobSett, getResourceImage(L"settings_small")); + setMenuItemImage(m_menuItem7, getResourceImage(L"batch_small")); + + setMenuItemImage(m_menuItemManual, getResourceImage(L"help_small")); + setMenuItemImage(m_menuItemAbout, getResourceImage(L"about_small")); + + if (!manualProgramUpdateRequired()) + { + m_menuItemCheckVersionNow ->Enable(false); + m_menuItemCheckVersionAuto->Enable(false); + + //wxFormbuilder doesn't give us a wxMenuItem for m_menuCheckVersion, so we need this abomination: + wxMenuItemList& items = m_menuHelp->GetMenuItems(); + for (auto it = items.begin(); it != items.end(); ++it) + if ((*it)->GetSubMenu() == m_menuCheckVersion) + (*it)->Enable(false); + } + + //create language selection menu + for (const ExistingTranslations::Entry& entry : ExistingTranslations::get()) + { + wxMenuItem* newItem = new wxMenuItem(m_menuLanguages, wxID_ANY, entry.languageName); + newItem->SetBitmap(getResourceImage(entry.languageFlag)); + + //map menu item IDs with language IDs: evaluated when processing event handler + languageMenuItemMap.insert(std::make_pair(newItem->GetId(), entry.languageID)); + + //connect event + this->Connect(newItem->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnMenuLanguageSwitch)); + m_menuLanguages->Append(newItem); + } + + //notify about (logical) application main window => program won't quit, but stay on this dialog + zen::setMainWindow(this); + + //init handling of first folder pair + firstFolderPair = make_unique(*this); + + initViewFilterButtons(); + + //init grid settings + gridview::init(*m_gridMainL, *m_gridMainC, *m_gridMainR, gridDataView); + treeview::init(*m_gridNavi, treeDataView); + //config_history::init(*m_gridConfigHistory, lastRunConfigName()); + + //initialize and load configuration + setGlobalCfgOnInit(globalSettings); + setConfig(guiCfg, referenceFiles); + + //support for CTRL + C and DEL on grids + m_gridMainL->getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::onGridButtonEventL), nullptr, this); + m_gridMainC->getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::onGridButtonEventC), nullptr, this); + m_gridMainR->getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::onGridButtonEventR), nullptr, this); + + m_gridNavi->getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::onTreeButtonEvent), nullptr, this); + + //register global hotkeys (without explicit menu entry) + wxTheApp->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::OnGlobalKeyEvent), nullptr, this); + wxTheApp->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::OnGlobalKeyEvent), nullptr, this); //capture direction keys + + //drag & drop on navi panel + setupFileDrop(*m_gridNavi); + m_gridNavi->Connect(EVENT_DROP_FILE, FileDropEventHandler(MainDialog::onNaviPanelFilesDropped), nullptr, this); + + timerForAsyncTasks.Connect(wxEVT_TIMER, wxEventHandler(MainDialog::onProcessAsyncTasks), nullptr, this); + + //Connect(wxEVT_SIZE, wxSizeEventHandler(MainDialog::OnResize), nullptr, this); + //Connect(wxEVT_MOVE, wxSizeEventHandler(MainDialog::OnResize), nullptr, this); + + //calculate witdh of folder pair manually (if scrollbars are visible) + m_panelTopLeft->Connect(wxEVT_SIZE, wxEventHandler(MainDialog::OnResizeLeftFolderWidth), nullptr, this); + + //dynamically change sizer direction depending on size + m_panelConfig ->Connect(wxEVT_SIZE, wxEventHandler(MainDialog::OnResizeConfigPanel), nullptr, this); + m_panelViewFilter->Connect(wxEVT_SIZE, wxEventHandler(MainDialog::OnResizeViewPanel), nullptr, this); + m_panelStatistics->Connect(wxEVT_SIZE, wxEventHandler(MainDialog::OnResizeStatisticsPanel), nullptr, this); + wxSizeEvent dummy3; + OnResizeConfigPanel (dummy3); //call once on window creation + OnResizeViewPanel (dummy3); // + OnResizeStatisticsPanel(dummy3); // + + //event handler for manual (un-)checking of rows and setting of sync direction + m_gridMainC->Connect(EVENT_GRID_CHECK_ROWS, CheckRowsEventHandler (MainDialog::onCheckRows), nullptr, this); + m_gridMainC->Connect(EVENT_GRID_SYNC_DIRECTION, SyncDirectionEventHandler(MainDialog::onSetSyncDirection), nullptr, this); + + //mainly to update row label sizes... + updateGui(); + + //register regular check for update on next idle event + Connect(wxEVT_IDLE, wxIdleEventHandler(MainDialog::OnRegularUpdateCheck), nullptr, this); + + //asynchronous call to wxWindow::Layout(): fix superfluous frame on right and bottom when FFS is started in fullscreen mode + Connect(wxEVT_IDLE, wxIdleEventHandler(MainDialog::OnLayoutWindowAsync), nullptr, this); + wxCommandEvent evtDummy; //call once before OnLayoutWindowAsync() + OnResizeLeftFolderWidth(evtDummy); // + + //---------------------------------------------------------------------------------------------------------------------------------------------------------------- + //some convenience: if FFS is started with a *.ffs_gui file as commandline parameter AND all directories contained exist, comparison shall be started right away + if (startComparison) + { + const zen::MainConfiguration currMainCfg = getConfig().mainCfg; + + //------------------------------------------------------------------------------------------ + //check existence of all directories in parallel! + RunUntilFirstHit findFirstMissing; + + //harmonize checks with comparison.cpp:: checkForIncompleteInput() + //we're really doing two checks: 1. check directory existence 2. check config validity -> don't mix them! + bool havePartialPair = false; + bool haveFullPair = false; + + auto addDirCheck = [&](const FolderPairEnh& fp) + { + const Zstring dirLeft = getFormattedDirectoryName(fp.dirnamePhraseLeft ); //should not block!? + const Zstring dirRight = getFormattedDirectoryName(fp.dirnamePhraseRight); // + + if (dirLeft.empty() != dirRight.empty()) //only skip check if both sides are empty! + havePartialPair = true; + else if (!dirLeft.empty()) + haveFullPair = true; + + if (!dirLeft.empty()) + findFirstMissing.addJob([=] { return !dirExists(dirLeft ) ? zen::make_unique() : nullptr; }); + if (!dirRight.empty()) + findFirstMissing.addJob([=] { return !dirExists(dirRight) ? zen::make_unique() : nullptr; }); + }; + + addDirCheck(currMainCfg.firstPair); + std::for_each(currMainCfg.additionalPairs.begin(), currMainCfg.additionalPairs.end(), addDirCheck); + //------------------------------------------------------------------------------------------ + + if (havePartialPair != haveFullPair) //either all pairs full or all half-filled -> validity check! + { + //potentially slow network access: give all checks 500ms to finish + const bool allFilesExist = findFirstMissing.timedWait(boost::posix_time::milliseconds(500)) && //true: have result + !findFirstMissing.get(); //no missing + if (allFilesExist) + if (wxEvtHandler* evtHandler = m_buttonCompare->GetEventHandler()) + { + wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); + evtHandler->AddPendingEvent(dummy2); //simulate button click on "compare" + } + } + } +} + + +MainDialog::~MainDialog() +{ + try //save "GlobalSettings.xml" + { + xmlAccess::writeConfig(getGlobalCfgBeforeExit()); //throw FfsXmlError + } + catch (const xmlAccess::FfsXmlError& e) + { + showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); + } + + try //save "LastRun.ffs_gui" + { + xmlAccess::writeConfig(getConfig(), lastRunConfigName()); //throw FfsXmlError + } + //don't annoy users on read-only drives: it's enough to show a single error message when saving global config + catch (const xmlAccess::FfsXmlError&) {} + + //important! event source wxTheApp is NOT dependent on this instance -> disconnect! + wxTheApp->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::OnGlobalKeyEvent), nullptr, this); + wxTheApp->Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::OnGlobalKeyEvent), nullptr, this); + +#ifdef ZEN_MAC + //more (non-portable) wxWidgets crap: wxListBox leaks wxClientData, both of the following functions fail to clean up: + // src/common/ctrlsub.cpp:: wxItemContainer::~wxItemContainer() -> empty function body!!! + // src/osx/listbox_osx.cpp: wxListBox::~wxListBox() + //=> finally a manual wxItemContainer::Clear() will render itself useful: + m_listBoxHistory->Clear(); +#endif + + auiMgr.UnInit(); + + //no need for wxEventHandler::Disconnect() here; event sources are components of this window and are destroyed, too +} + +//------------------------------------------------------------------------------------------------------------------------------------- + +void MainDialog::onQueryEndSession() +{ + try { xmlAccess::writeConfig(getGlobalCfgBeforeExit()); } + catch (const xmlAccess::FfsXmlError&) {} //we try our best do to something useful in this extreme situation - no reason to notify or even log errors here! + + try { xmlAccess::writeConfig(getConfig(), lastRunConfigName()); } + catch (const xmlAccess::FfsXmlError&) {} +} + + +void MainDialog::setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSettings) +{ + globalCfg = globalSettings; + + //caveat set/get language asymmmetry! setLanguage(globalSettings.programLanguage); //throw FileError + //we need to set langugabe before creating this class! + + //set dialog size and position: + // - width/height are invalid if the window is minimized (eg x,y == -32000; height = 28, width = 160) + // - multi-monitor setups: dialog may be placed on second monitor which is currently turned off + if (globalSettings.gui.dlgSize.GetWidth () > 0 && + globalSettings.gui.dlgSize.GetHeight() > 0) + { + //calculate how much of the dialog will be visible on screen + const int dialogAreaTotal = globalSettings.gui.dlgSize.GetWidth() * globalSettings.gui.dlgSize.GetHeight(); + int dialogAreaVisible = 0; + + const int monitorCount = wxDisplay::GetCount(); + for (int i = 0; i < monitorCount; ++i) + { + wxRect intersection = wxDisplay(i).GetClientArea().Intersect(wxRect(globalSettings.gui.dlgPos, globalSettings.gui.dlgSize)); + dialogAreaVisible = std::max(dialogAreaVisible, intersection.GetWidth() * intersection.GetHeight()); + } + + if (dialogAreaVisible > 0.1 * dialogAreaTotal) //at least 10% of the dialog should be visible! + SetSize(wxRect(globalSettings.gui.dlgPos, globalSettings.gui.dlgSize)); + else + { + SetSize(wxRect(globalSettings.gui.dlgSize)); + Centre(); + } + } + else + Centre(); + + Maximize(globalSettings.gui.isMaximized); + + //set column attributes + m_gridMainL ->setColumnConfig(gridview::convertConfig(globalSettings.gui.columnAttribLeft)); + m_gridMainR ->setColumnConfig(gridview::convertConfig(globalSettings.gui.columnAttribRight)); + m_splitterMain->setSashOffset(globalSettings.gui.sashOffset); + + m_gridNavi->setColumnConfig(treeview::convertConfig(globalSettings.gui.columnAttribNavi)); + treeview::setShowPercentage(*m_gridNavi, globalSettings.gui.showPercentBar); + + treeDataView->setSortDirection(globalSettings.gui.naviLastSortColumn, globalSettings.gui.naviLastSortAscending); + + //-------------------------------------------------------------------------------- + //load list of last used configuration files + //config_history::setItems(*m_gridConfigHistory, globalSettings.gui.lastUsedConfigFiles2); + + std::vector cfgFileNames; + std::transform(globalSettings.gui.cfgFileHistory.rbegin(), globalSettings.gui.cfgFileHistory.rend(), std::back_inserter(cfgFileNames), + [](const ConfigHistoryItem& item) { return item.configFile; }); + //list is stored with last used files first in xml, however addFileToCfgHistory() needs them last!!! + + cfgFileNames.push_back(lastRunConfigName()); //make sure is always part of history list (if existing) + addFileToCfgHistory(cfgFileNames); + + removeObsoleteCfgHistoryItems(cfgFileNames); //remove non-existent items (we need this only on startup) + //-------------------------------------------------------------------------------- + + //load list of last used folders + *folderHistoryLeft = FolderHistory(globalSettings.gui.folderHistoryLeft, globalSettings.gui.folderHistMax); + *folderHistoryRight = FolderHistory(globalSettings.gui.folderHistoryRight, globalSettings.gui.folderHistMax); + + //show/hide file icons + gridview::setupIcons(*m_gridMainL, *m_gridMainC, *m_gridMainR, globalSettings.gui.showIcons, convert(globalSettings.gui.iconSize)); + + //------------------------------------------------------------------------------------------------ + m_checkBoxMatchCase->SetValue(globalCfg.gui.textSearchRespectCase); + + //wxAuiManager erroneously loads panel captions, we don't want that + typedef std::vector> CaptionNameMapping; + CaptionNameMapping captionNameMap; + const wxAuiPaneInfoArray& paneArray = auiMgr.GetAllPanes(); + for (size_t i = 0; i < paneArray.size(); ++i) + captionNameMap.push_back(std::make_pair(paneArray[i].caption, paneArray[i].name)); + + auiMgr.LoadPerspective(globalSettings.gui.guiPerspectiveLast); + + //restore original captions + for (auto it = captionNameMap.begin(); it != captionNameMap.end(); ++it) + auiMgr.GetPane(it->second).Caption(it->first); + + //if MainDialog::onQueryEndSession() is called while comparison is active, this panel is saved and restored as "visible" + auiMgr.GetPane(compareStatus->getAsWindow()).Hide(); + + auiMgr.GetPane(m_panelSearch).Hide(); //no need to show it on startup + + m_menuItemCheckVersionAuto->Check(globalCfg.gui.lastUpdateCheck != -1); + + auiMgr.Update(); +} + + +xmlAccess::XmlGlobalSettings MainDialog::getGlobalCfgBeforeExit() +{ + Freeze(); //no need to Thaw() again!! + + xmlAccess::XmlGlobalSettings globalSettings = globalCfg; + + globalSettings.programLanguage = getLanguage(); + + //retrieve column attributes + globalSettings.gui.columnAttribLeft = gridview::convertConfig(m_gridMainL->getColumnConfig()); + globalSettings.gui.columnAttribRight = gridview::convertConfig(m_gridMainR->getColumnConfig()); + globalSettings.gui.sashOffset = m_splitterMain->getSashOffset(); + + globalSettings.gui.columnAttribNavi = treeview::convertConfig(m_gridNavi->getColumnConfig()); + globalSettings.gui.showPercentBar = treeview::getShowPercentage(*m_gridNavi); + + const std::pair sortInfo = treeDataView->getSortDirection(); + globalSettings.gui.naviLastSortColumn = sortInfo.first; + globalSettings.gui.naviLastSortAscending = sortInfo.second; + + //-------------------------------------------------------------------------------- + //write list of last used configuration files + std::map historyDetail; //(cfg-file/last use index) + for (unsigned int i = 0; i < m_listBoxHistory->GetCount(); ++i) + if (auto clientString = dynamic_cast(m_listBoxHistory->GetClientObject(i))) + historyDetail.insert(std::make_pair(clientString->lastUseIndex_, clientString->cfgFile_)); + else + assert(false); + + //sort by last use; put most recent items *first* (looks better in xml than the reverse) + std::vector history; + std::transform(historyDetail.rbegin(), historyDetail.rend(), std::back_inserter(history), [](const std::pair& item) { return ConfigHistoryItem(item.second); }); + + if (history.size() > globalSettings.gui.cfgFileHistMax) //erase oldest elements + history.resize(globalSettings.gui.cfgFileHistMax); + + globalSettings.gui.cfgFileHistory = history; + //-------------------------------------------------------------------------------- + globalSettings.gui.lastUsedConfigFiles = activeConfigFiles; + //globalSettings.gui.lastUsedConfigFiles2 = config_history::getItems(*m_gridConfigHistory); + + //write list of last used folders + globalSettings.gui.folderHistoryLeft = folderHistoryLeft ->getList(); + globalSettings.gui.folderHistoryRight = folderHistoryRight->getList(); + + globalSettings.gui.textSearchRespectCase = m_checkBoxMatchCase->GetValue(); + + globalSettings.gui.guiPerspectiveLast = auiMgr.SavePerspective(); + + //we need to portably retrieve non-iconized, non-maximized size and position (non-portable: GetWindowPlacement()) + //call *after* wxAuiManager::SavePerspective()! + if (IsIconized()) + Iconize(false); + + globalSettings.gui.isMaximized = IsMaximized(); //evaluate AFTER uniconizing! + + if (IsMaximized()) + Maximize(false); + + globalSettings.gui.dlgSize = GetSize(); + globalSettings.gui.dlgPos = GetPosition(); + + return globalSettings; +} + + +void MainDialog::setSyncDirManually(const std::vector& selection, SyncDirection direction) +{ + if (!selection.empty()) + { + std::for_each(selection.begin(), selection.end(), + [&](FileSystemObject* fsObj) + { + setSyncDirectionRec(direction, *fsObj); //set new direction (recursively) + zen::setActiveStatus(true, *fsObj); //works recursively for directories + }); + + updateGui(); + } +} + + +void MainDialog::setFilterManually(const std::vector& selection, bool setIncluded) +{ + //if hidefiltered is active, there should be no filtered elements on screen => current element was filtered out + assert(!currentCfg.hideExcludedItems || !setIncluded); + + if (!selection.empty()) + { + std::for_each(selection.begin(), selection.end(), + [&](FileSystemObject* fsObj) { zen::setActiveStatus(setIncluded, *fsObj); }); //works recursively for directories + + updateGuiDelayedIf(currentCfg.hideExcludedItems); //show update GUI before removing rows + } +} + + +namespace +{ +//perf: wxString doesn't model exponential growth and so is unusable for large data sets +typedef Zbase zxString; //guaranteed exponential growth +} + +void MainDialog::copySelectionToClipboard(const std::vector& gridRefs) +{ + try + { + zxString clipboardString; + + auto addSelection = [&](const Grid& grid) + { + if (auto prov = grid.getDataProvider()) + { + std::vector colAttr = grid.getColumnConfig(); + vector_remove_if(colAttr, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); + if (!colAttr.empty()) + { + const std::vector selection = grid.getSelectedRows(); + std::for_each(selection.begin(), selection.end(), + [&](size_t row) + { + std::for_each(colAttr.begin(), colAttr.end() - 1, + [&](const Grid::ColumnAttribute& ca) + { + clipboardString += copyStringTo(prov->getValue(row, ca.type_)); + clipboardString += L'\t'; + }); + clipboardString += copyStringTo(prov->getValue(row, colAttr.back().type_)); + clipboardString += L'\n'; + }); + } + } + }; + + for (const Grid* gr : gridRefs) + addSelection(*gr); + + //finally write to clipboard + if (!clipboardString.empty()) + if (wxClipboard::Get()->Open()) + { + ZEN_ON_SCOPE_EXIT(wxClipboard::Get()->Close()); + wxClipboard::Get()->SetData(new wxTextDataObject(copyStringTo(clipboardString))); //ownership passed + } + } + catch (const std::bad_alloc& e) + { + showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setMainInstructions(_("Out of memory.") + L" " + utfCvrtTo(e.what()))); + } +} + + +std::vector MainDialog::getGridSelection(bool fromLeft, bool fromRight) const +{ + std::set selectedRows; + + auto addSelection = [&](const Grid& grid) + { + const std::vector& sel = grid.getSelectedRows(); + selectedRows.insert(sel.begin(), sel.end()); + }; + + if (fromLeft) + addSelection(*m_gridMainL); + + if (fromRight) + addSelection(*m_gridMainR); + + return gridDataView->getAllFileRef(selectedRows); +} + + +std::vector MainDialog::getTreeSelection() const +{ + std::vector output; + + const std::vector& sel = m_gridNavi->getSelectedRows(); + std::for_each(sel.begin(), sel.end(), + [&](size_t row) + { + if (std::unique_ptr node = treeDataView->getLine(row)) + { + if (auto root = dynamic_cast(node.get())) + { + //select first level of child elements + for (auto& fsObj : root->baseDirObj_.refSubDirs ()) output.push_back(&fsObj); + for (auto& fsObj : root->baseDirObj_.refSubFiles()) output.push_back(&fsObj); + for (auto& fsObj : root->baseDirObj_.refSubLinks()) output.push_back(&fsObj); + } + else if (auto dir = dynamic_cast(node.get())) + output.push_back(&(dir->dirObj_)); + else if (auto file = dynamic_cast(node.get())) + output.insert(output.end(), file->filesAndLinks_.begin(), file->filesAndLinks_.end()); + } + }); + return output; +} + + +//Exception class used to abort the "compare" and "sync" process +class AbortDeleteProcess {}; + +class ManualDeletionHandler : private wxEvtHandler, public DeleteFilesHandler //throw AbortDeleteProcess +{ +public: + ManualDeletionHandler(MainDialog& main) : + mainDlg(main), + abortRequested(false), + ignoreErrors(false) + { + mainDlg.disableAllElements(true); //disable everything except abort button + + //register abort button + mainDlg.m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ManualDeletionHandler::OnAbortDeletion), nullptr, this ); + mainDlg.Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(ManualDeletionHandler::OnKeyPressed), nullptr, this); + } + + ~ManualDeletionHandler() + { + //de-register abort button + mainDlg.Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(ManualDeletionHandler::OnKeyPressed), nullptr, this); + mainDlg.m_buttonCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ManualDeletionHandler::OnAbortDeletion ), nullptr, this ); + + mainDlg.enableAllElements(); + } + + virtual Response reportError(const std::wstring& msg) + { + if (ignoreErrors) + return DeleteFilesHandler::IGNORE_ERROR; + + forceUiRefresh(); + bool ignoreNextErrors = false; + switch (showConfirmationDialog3(&mainDlg, DialogInfoType::ERROR2, PopupDialogCfg3(). + setDetailInstructions(msg). + setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors"), ConfirmationButton3::DONT_DO_IT), + _("&Ignore"), _("&Retry"))) + { + case ConfirmationButton3::DO_IT: //ignore + ignoreErrors = ignoreNextErrors; + return DeleteFilesHandler::IGNORE_ERROR; + + case ConfirmationButton3::DONT_DO_IT: //retry + return DeleteFilesHandler::RETRY; + + case ConfirmationButton3::CANCEL: + throw AbortDeleteProcess(); + } + + assert (false); + return DeleteFilesHandler::IGNORE_ERROR; //dummy return value + } + + virtual void reportWarning(const std::wstring& msg, bool& warningActive) + { + if (!warningActive || ignoreErrors) + return; + + forceUiRefresh(); + bool dontWarnAgain = false; + switch (showConfirmationDialog(&mainDlg, DialogInfoType::WARNING, PopupDialogCfg(). + setDetailInstructions(msg). + setCheckBox(dontWarnAgain, _("&Don't show this warning again")), _("&Ignore"))) + { + case ConfirmationButton::DO_IT: + warningActive = !dontWarnAgain; + break; + case ConfirmationButton::CANCEL: + throw AbortDeleteProcess(); + } + } + + virtual void reportStatus (const std::wstring& msg) + { + statusMsg = msg; + requestUiRefresh(); + } + +private: + virtual void requestUiRefresh() + { + if (updateUiIsAllowed()) //test if specific time span between ui updates is over + forceUiRefresh(); + + if (abortRequested) //test after (implicit) call to wxApp::Yield() + throw AbortDeleteProcess(); + } + + void forceUiRefresh() + { + //std::wstring msg = toGuiString(deletionCount) + + mainDlg.setStatusBarFullText(statusMsg); + wxTheApp->Yield(); + } + + //context: C callstack message loop => throw()! + void OnAbortDeletion(wxCommandEvent& event) //handle abort button click + { + abortRequested = true; //don't throw exceptions across a C call stack! + } + + void OnKeyPressed(wxKeyEvent& event) + { + const int keyCode = event.GetKeyCode(); + if (keyCode == WXK_ESCAPE) + { + abortRequested = true; //don't throw exceptions across a C call stack! + return; + } + + event.Skip(); + } + + MainDialog& mainDlg; + + bool abortRequested; + bool ignoreErrors; + //size_t deletionCount; // + std::wstring statusMsg; //status reporting +}; + + +void MainDialog::deleteSelectedFiles(const std::vector& selectionLeft, + const std::vector& selectionRight) +{ + bool deleteOnBothSides = false; //let's keep this disabled by default -> don't save + //=> clenup empty selection on either side: + std::vector selectionLeftTmp; + std::vector selectionRightTmp; + std::copy_if(selectionLeft .begin(), selectionLeft .end(), std::back_inserter(selectionLeftTmp ), [](const FileSystemObject* fsObj) { return !fsObj->isEmpty(); }); + std::copy_if(selectionRight.begin(), selectionRight.end(), std::back_inserter(selectionRightTmp), [](const FileSystemObject* fsObj) { return !fsObj->isEmpty(); }); + + if (!selectionLeftTmp.empty() || !selectionRightTmp.empty()) + { + wxWindow* oldFocus = wxWindow::FindFocus(); + ZEN_ON_SCOPE_EXIT(if (oldFocus) oldFocus->SetFocus();) + + if (zen::showDeleteDialog(this, + selectionLeftTmp, + selectionRightTmp, + deleteOnBothSides, + globalCfg.gui.useRecyclerForManualDeletion) == ReturnSmallDlg::BUTTON_OKAY) + { + //wxBusyCursor dummy; -> redundant: progress already shown in status bar! + try + { + //handle errors when deleting files/folders + ManualDeletionHandler statusHandler(*this); + + zen::deleteFromGridAndHD(selectionLeftTmp, + selectionRightTmp, + folderCmp, + extractDirectionCfg(getConfig().mainCfg), + deleteOnBothSides, + globalCfg.gui.useRecyclerForManualDeletion, + statusHandler, + globalCfg.optDialogs.warningRecyclerMissing); + + m_gridMainL->clearSelection(ALLOW_GRID_EVENT); + m_gridMainC->clearSelection(ALLOW_GRID_EVENT); + m_gridMainR->clearSelection(ALLOW_GRID_EVENT); + } + catch (AbortDeleteProcess&) {} //do not clear grids, if aborted! + + //remove rows that are empty: just a beautification, invalid rows shouldn't cause issues + gridDataView->removeInvalidRows(); + + updateGui(); + } + } +} + + +namespace +{ +template +Zstring getExistingParentFolder(const FileSystemObject& fsObj) +{ + const DirPair* dirObj = dynamic_cast(&fsObj); + if (!dirObj) + dirObj = dynamic_cast(&fsObj.parent()); + + while (dirObj) + { + if (!dirObj->isEmpty()) + return dirObj->getFullName(); + + dirObj = dynamic_cast(&dirObj->parent()); + } + return fsObj.getBaseDirPf(); +} +} + +void MainDialog::openExternalApplication(const wxString& commandline, const std::vector& selection, bool leftSide) +{ + if (commandline.empty()) + return; + + auto selectionTmp = selection; + + const bool openFileBrowserRequested = [&]() -> bool + { + xmlAccess::XmlGlobalSettings::Gui dummy; + return !dummy.externelApplications.empty() && dummy.externelApplications[0].second == commandline; + }(); + + //support fallback instead of an error in this special case + if (openFileBrowserRequested) + { + if (selectionTmp.size() > 1) //do not open more than one explorer instance! + selectionTmp.resize(1); // + + if (selectionTmp.empty() || + (leftSide && selectionTmp[0]->isEmpty()) || + (!leftSide && selectionTmp[0]->isEmpty())) + { + Zstring fallbackDir; + if (selectionTmp.empty()) + fallbackDir = leftSide ? + getFormattedDirectoryName(firstFolderPair->getLeftDir()) : + getFormattedDirectoryName(firstFolderPair->getRightDir()); + + else + fallbackDir = leftSide ? + getExistingParentFolder(*selectionTmp[0]) : + getExistingParentFolder(*selectionTmp[0]); + + try + { +#ifdef ZEN_WIN + shellExecute2(L"\"" + fallbackDir + L"\"", EXEC_TYPE_ASYNC); //throw FileError +#elif defined ZEN_LINUX + shellExecute2("xdg-open \"" + fallbackDir + "\"", EXEC_TYPE_ASYNC); // +#elif defined ZEN_MAC + shellExecute2("open \"" + fallbackDir + "\"", EXEC_TYPE_ASYNC); // +#endif + } + catch (const FileError& e) { showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); } + return; + } + } + + const size_t massInvokeThreshold = 10; //more than this is likely a user mistake + + if (selectionTmp.size() > massInvokeThreshold) + if (globalCfg.optDialogs.confirmExternalCommandMassInvoke) + { + bool dontAskAgain = false; + switch (showConfirmationDialog(this, DialogInfoType::WARNING, PopupDialogCfg(). + setTitle(_("Confirm")). + setMainInstructions(replaceCpy(_P("Do you really want to execute the command %y for one item?", + "Do you really want to execute the command %y for %x items?", selectionTmp.size()), + L"%y", L'\"' + commandline + L'\"')). + setCheckBox(dontAskAgain, _("&Don't show this warning again")), + _("&Execute"))) + { + case ConfirmationButton::DO_IT: + globalCfg.optDialogs.confirmExternalCommandMassInvoke = !dontAskAgain; + break; + case ConfirmationButton::CANCEL: + return; + } + } + + //regular command evaluation + for (const FileSystemObject* fsObj : selectionTmp) //context menu calls this function only if selection is not empty! + { + Zstring path1 = fsObj->getBaseDirPf() + fsObj->getObjRelativeName(); //full path, even if item is not existing! + Zstring dir1 = beforeLast(path1, FILE_NAME_SEPARATOR); //Win: wrong for root paths like "C:\file.txt" + + Zstring path2 = fsObj->getBaseDirPf() + fsObj->getObjRelativeName(); + Zstring dir2 = beforeLast(path2, FILE_NAME_SEPARATOR); + + if (!leftSide) + { + std::swap(path1, path2); + std::swap(dir1, dir2); + } + + Zstring command = utfCvrtTo(commandline); + replace(command, Zstr("%item_path%"), path1); + replace(command, Zstr("%item2_path%"), path2); + replace(command, Zstr("%item_folder%"), dir1 ); + replace(command, Zstr("%item2_folder%"), dir2 ); + + auto cmdExp = expandMacros(command); + try + { + //caveat: spawning too many threads asynchronously can easily kill a user's desktop session on Ubuntu! + shellExecute2(cmdExp, selectionTmp.size() > massInvokeThreshold ? EXEC_TYPE_SYNC : EXEC_TYPE_ASYNC); //throw FileError + } + catch (const FileError& e) { showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); } + } +} + + +void MainDialog::setStatusBarFileStatistics(size_t filesOnLeftView, + size_t foldersOnLeftView, + size_t filesOnRightView, + size_t foldersOnRightView, + zen::UInt64 filesizeLeftView, + zen::UInt64 filesizeRightView) +{ +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(m_panelStatusBar); //leads to GUI corruption problems on Linux/OS X! +#endif + + //select state + bSizerFileStatus->Show(true); + m_staticTextFullStatus->Hide(); + + //update status information + bSizerStatusLeftDirectories->Show(foldersOnLeftView > 0); + bSizerStatusLeftFiles ->Show(filesOnLeftView > 0); + + setText(*m_staticTextStatusLeftDirs, _P("1 directory", "%x directories", foldersOnLeftView)); + setText(*m_staticTextStatusLeftFiles, _P("1 file", "%x files", filesOnLeftView)); + setText(*m_staticTextStatusLeftBytes, L"(" + filesizeToShortString(to(filesizeLeftView)) + L")"); + //------------------------------------------------------------------------------ + bSizerStatusRightDirectories->Show(foldersOnRightView > 0); + bSizerStatusRightFiles ->Show(filesOnRightView > 0); + + setText(*m_staticTextStatusRightDirs, _P("1 directory", "%x directories", foldersOnRightView)); + setText(*m_staticTextStatusRightFiles, _P("1 file", "%x files", filesOnRightView)); + setText(*m_staticTextStatusRightBytes, L"(" + filesizeToShortString(to(filesizeRightView)) + L")"); + //------------------------------------------------------------------------------ + wxString statusMiddleNew; + if (gridDataView->rowsTotal() > 0) + { + statusMiddleNew = _P("%y of 1 row in view", "%y of %x rows in view", gridDataView->rowsTotal()); + replace(statusMiddleNew, L"%y", toGuiString(gridDataView->rowsOnView()), false); //%x is already used as plural form placeholder! + } + + //fill middle text (considering flashStatusInformation()) + if (oldStatusMsgs.empty()) + setText(*m_staticTextStatusMiddle, statusMiddleNew); + else + oldStatusMsgs.front() = statusMiddleNew; + + m_panelStatusBar->Layout(); +} + + +void MainDialog::setStatusBarFullText(const wxString& msg) +{ + const bool needLayoutUpdate = !m_staticTextFullStatus->IsShown(); + //select state + bSizerFileStatus->Show(false); + m_staticTextFullStatus->Show(); + + //update status information + setText(*m_staticTextFullStatus, msg); + m_panelStatusBar->Layout(); + + if (needLayoutUpdate) + auiMgr.Update(); //fix status bar height (needed on OS X) +} + + +void MainDialog::flashStatusInformation(const wxString& text) +{ + oldStatusMsgs.push_back(m_staticTextStatusMiddle->GetLabel()); + + m_staticTextStatusMiddle->SetLabel(text); + m_staticTextStatusMiddle->SetForegroundColour(wxColour(31, 57, 226)); //highlight color: blue + m_staticTextStatusMiddle->SetFont(m_staticTextStatusMiddle->GetFont().Bold()); + + m_panelStatusBar->Layout(); + //if (needLayoutUpdate) auiMgr.Update(); -> not needed here, this is called anyway in updateGui() + + processAsync2([] { boost::this_thread::sleep(boost::posix_time::millisec(2500)); }, + [this] { this->restoreStatusInformation(); }); +} + + +void MainDialog::restoreStatusInformation() +{ + if (!oldStatusMsgs.empty()) + { + wxString oldMsg = oldStatusMsgs.back(); + oldStatusMsgs.pop_back(); + + if (oldStatusMsgs.empty()) //restore original status text + { + m_staticTextStatusMiddle->SetLabel(oldMsg); + m_staticTextStatusMiddle->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); //reset color + + wxFont fnt = m_staticTextStatusMiddle->GetFont(); + fnt.SetWeight(wxFONTWEIGHT_NORMAL); + m_staticTextStatusMiddle->SetFont(fnt); + + m_panelStatusBar->Layout(); + } + } +} + + +void MainDialog::onProcessAsyncTasks(wxEvent& event) +{ + //schedule and run long-running tasks asynchronously + asyncTasks.evalResults(); //process results on GUI queue + if (asyncTasks.empty()) + timerForAsyncTasks.Stop(); +} + + +void MainDialog::disableAllElements(bool enableAbort) +{ + //disables all elements (except abort button) that might receive user input during long-running processes: + //when changing consider: comparison, synchronization, manual deletion + + EnableCloseButton(false); //not allowed for synchronization! progress indicator is top window! -> not honored on OS X! + + //OS X: wxWidgets portability promise is again a mess: http://wxwidgets.10942.n7.nabble.com/Disable-panel-and-appropriate-children-windows-linux-macos-td35357.html + + m_menubar1->EnableTop(0, false); + m_menubar1->EnableTop(1, false); + m_menubar1->EnableTop(2, false); + m_bpButtonCmpConfig ->Disable(); + m_bpButtonSyncConfig ->Disable(); + m_buttonSync ->Disable(); + m_panelDirectoryPairs->Disable(); + m_splitterMain ->Disable(); //includes m_panelCenter, but not m_panelStatusBar! + m_panelViewFilter ->Disable(); + m_panelFilter ->Disable(); + m_panelConfig ->Disable(); + m_panelStatistics ->Disable(); + m_gridNavi ->Disable(); + + if (enableAbort) + { + //show abort button + m_buttonCancel->Enable(); + m_buttonCancel->Show(); + if (m_buttonCancel->IsShownOnScreen()) + m_buttonCancel->SetFocus(); + m_buttonCompare->Disable(); + m_buttonCompare->Hide(); + m_panelTopButtons->Layout(); + } + else + m_panelTopButtons->Disable(); +} + + +void MainDialog::enableAllElements() +{ + //wxGTK, yet another QOI issue: some stupid bug, keeps moving main dialog to top!! + + EnableCloseButton(true); + + m_menubar1->EnableTop(0, true); + m_menubar1->EnableTop(1, true); + m_menubar1->EnableTop(2, true); + m_bpButtonCmpConfig ->Enable(); + m_bpButtonSyncConfig ->Enable(); + m_buttonSync ->Enable(); + m_panelDirectoryPairs->Enable(); + m_splitterMain ->Enable(); + m_panelViewFilter ->Enable(); + m_panelFilter ->Enable(); + m_panelConfig ->Enable(); + m_panelStatistics ->Enable(); + m_gridNavi ->Enable(); + + //show compare button + m_buttonCancel->Disable(); + m_buttonCancel->Hide(); + m_buttonCompare->Enable(); + m_buttonCompare->Show(); + + m_panelTopButtons->Enable(); + m_panelTopButtons->Layout(); + + //at least wxWidgets on OS X fails to do this after enabling: + Refresh(); +} + + +namespace +{ +void updateSizerOrientation(wxBoxSizer& sizer, wxWindow& window, double horizontalWeight) +{ + const int newOrientation = window.GetSize().GetWidth() * horizontalWeight > window.GetSize().GetHeight() ? wxHORIZONTAL : wxVERTICAL; //check window NOT sizer width! + if (sizer.GetOrientation() != newOrientation) + { + sizer.SetOrientation(newOrientation); + window.Layout(); + } +} +} + + +void MainDialog::OnResizeConfigPanel(wxEvent& event) +{ + updateSizerOrientation(*bSizerConfig, *m_panelConfig, 0.5); + event.Skip(); +} + + +void MainDialog::OnResizeViewPanel(wxEvent& event) +{ + updateSizerOrientation(*bSizerViewFilter, *m_panelViewFilter, 1.0); + event.Skip(); +} + + +void MainDialog::OnResizeStatisticsPanel(wxEvent& event) +{ + //updateSizerOrientation(*bSizerStatistics, *m_panelStatistics); + + //we need something more fancy: + const int parentOrient = m_panelStatistics->GetSize().GetWidth() > m_panelStatistics->GetSize().GetHeight() ? wxHORIZONTAL : wxVERTICAL; //check window NOT sizer width! + if (bSizerStatistics->GetOrientation() != parentOrient) + { + bSizerStatistics->SetOrientation(parentOrient); + + //apply opposite orientation for child sizers + const int childOrient = parentOrient == wxHORIZONTAL ? wxVERTICAL : wxHORIZONTAL; + wxSizerItemList& sl = bSizerStatistics->GetChildren(); + for (auto it = sl.begin(); it != sl.end(); ++it) //yet another wxWidgets bug keeps us from using std::for_each + { + wxSizerItem& szItem = **it; + if (auto sizerChild = dynamic_cast(szItem.GetSizer())) + if (sizerChild->GetOrientation() != childOrient) + sizerChild->SetOrientation(childOrient); + } + m_panelStatistics->Layout(); + } + + event.Skip(); +} + + +void MainDialog::OnResizeLeftFolderWidth(wxEvent& event) +{ + //adapt left-shift display distortion caused by scrollbars for multiple folder pairs + const int width = m_panelTopLeft->GetSize().GetWidth(); + std::for_each(additionalFolderPairs.begin(), additionalFolderPairs.end(), + [&](FolderPairPanel* panel) + { + panel->m_panelLeft->SetMinSize(wxSize(width, -1)); + }); + + event.Skip(); +} + + +void MainDialog::onTreeButtonEvent(wxKeyEvent& event) +{ + int keyCode = event.GetKeyCode(); + if (m_gridNavi->GetLayoutDirection() == wxLayout_RightToLeft) + { + if (keyCode == WXK_LEFT) + keyCode = WXK_RIGHT; + else if (keyCode == WXK_RIGHT) + keyCode = WXK_LEFT; + else if (keyCode == WXK_NUMPAD_LEFT) + keyCode = WXK_NUMPAD_RIGHT; + else if (keyCode == WXK_NUMPAD_RIGHT) + keyCode = WXK_NUMPAD_LEFT; + } + + if (event.ControlDown()) + switch (keyCode) + { + case 'C': + case WXK_INSERT: //CTRL + C || CTRL + INS + { + std::vector gridRefs; + gridRefs.push_back(m_gridNavi); + copySelectionToClipboard(gridRefs); + } + return; + } + else if (event.AltDown()) + switch (keyCode) + { + case WXK_NUMPAD_LEFT: + case WXK_LEFT: //ALT + <- + setSyncDirManually(getTreeSelection(), SyncDirection::LEFT); + return; + + case WXK_NUMPAD_RIGHT: + case WXK_RIGHT: //ALT + -> + setSyncDirManually(getTreeSelection(), SyncDirection::RIGHT); + return; + + case WXK_NUMPAD_UP: + case WXK_NUMPAD_DOWN: + case WXK_UP: /* ALT + /|\ */ + case WXK_DOWN: /* ALT + \|/ */ + setSyncDirManually(getTreeSelection(), SyncDirection::NONE); + return; + } + + else + switch (keyCode) + { + case WXK_SPACE: + case WXK_NUMPAD_SPACE: + { + const std::vector& selection = getTreeSelection(); + if (!selection.empty()) + setFilterManually(selection, !selection[0]->isActive()); + } + return; + + case WXK_DELETE: + case WXK_NUMPAD_DELETE: + deleteSelectedFiles(getTreeSelection(), getTreeSelection()); + return; + } + + event.Skip(); //unknown keypress: propagate +} + + +void MainDialog::onGridButtonEventL(wxKeyEvent& event) +{ + onGridButtonEvent(event, *m_gridMainL, true); +} +void MainDialog::onGridButtonEventC(wxKeyEvent& event) +{ + onGridButtonEvent(event, *m_gridMainC, true); +} +void MainDialog::onGridButtonEventR(wxKeyEvent& event) +{ + onGridButtonEvent(event, *m_gridMainR, false); +} + +void MainDialog::onGridButtonEvent(wxKeyEvent& event, Grid& grid, bool leftSide) +{ + int keyCode = event.GetKeyCode(); + if (grid.GetLayoutDirection() == wxLayout_RightToLeft) + { + if (keyCode == WXK_LEFT) + keyCode = WXK_RIGHT; + else if (keyCode == WXK_RIGHT) + keyCode = WXK_LEFT; + else if (keyCode == WXK_NUMPAD_LEFT) + keyCode = WXK_NUMPAD_RIGHT; + else if (keyCode == WXK_NUMPAD_RIGHT) + keyCode = WXK_NUMPAD_LEFT; + } + + if (event.ControlDown()) + switch (keyCode) + { + case 'C': + case WXK_INSERT: //CTRL + C || CTRL + INS + { + std::vector gridRefs; + gridRefs.push_back(m_gridMainL); + gridRefs.push_back(m_gridMainR); + copySelectionToClipboard(gridRefs); + } + return; // -> swallow event! don't allow default grid commands! + } + + else if (event.AltDown()) + switch (keyCode) + { + case WXK_NUMPAD_LEFT: + case WXK_LEFT: //ALT + <- + setSyncDirManually(getGridSelection(), SyncDirection::LEFT); + return; + + case WXK_NUMPAD_RIGHT: + case WXK_RIGHT: //ALT + -> + setSyncDirManually(getGridSelection(), SyncDirection::RIGHT); + return; + + case WXK_NUMPAD_UP: + case WXK_NUMPAD_DOWN: + case WXK_UP: /* ALT + /|\ */ + case WXK_DOWN: /* ALT + \|/ */ + setSyncDirManually(getGridSelection(), SyncDirection::NONE); + return; + } + + else + switch (keyCode) + { + case WXK_DELETE: + case WXK_NUMPAD_DELETE: + deleteSelectedFiles(getGridSelection(true, false), + getGridSelection(false, true)); + return; + + case WXK_SPACE: + case WXK_NUMPAD_SPACE: + { + const std::vector& selection = getGridSelection(); + if (!selection.empty()) + setFilterManually(selection, !selection[0]->isActive()); + } + return; + + case WXK_RETURN: + case WXK_NUMPAD_ENTER: + if (!globalCfg.gui.externelApplications.empty()) + openExternalApplication(globalCfg.gui.externelApplications[0].second, //open with first external application + getGridSelection(), leftSide); + return; + } + + event.Skip(); //unknown keypress: propagate +} + + +//pretty much the same like "bool wxWindowBase::IsDescendant(wxWindowBase* child) const" but without the obvious misnaming +bool isComponentOf(const wxWindow* child, const wxWindow* top) +{ + for (const wxWindow* wnd = child; wnd != nullptr; wnd = wnd->GetParent()) + if (wnd == top) + return true; + return false; +} + + +void MainDialog::OnGlobalKeyEvent(wxKeyEvent& event) //process key events without explicit menu entry :) +{ + const wxWindow* focus = wxWindow::FindFocus(); + + //avoid recursion!!! -> this ugly construct seems to be the only (portable) way to avoid re-entrancy + //recursion may happen in multiple situations: e.g. modal dialogs, Grid::ProcessEvent()! + if (processingGlobalKeyEvent || + !isComponentOf(focus, this) || + !IsEnabled() || //only handle if main window is in use and no modal dialog is shown: + !IsActive()) //thanks to wxWidgets non-portability we need both checks: + //first is sufficient for Windows, second is needed on OS X since it does NOT disable the parent when showing a modal dialog + { + event.Skip(); + return; + } + processingGlobalKeyEvent = true; + ZEN_ON_SCOPE_EXIT(processingGlobalKeyEvent = false;) + //---------------------------------------------------- + + const int keyCode = event.GetKeyCode(); + + //CTRL + X + //if (event.ControlDown()) + // switch (keyCode) + // { + // case 'F': //CTRL + F + // showFindPanel(); + // return; //-> swallow event! + // } + + switch (keyCode) + { + case WXK_F3: + case WXK_NUMPAD_F3: + startFindNext(); + return; //-> swallow event! + + case WXK_F6: + { + wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); //simulate button click + if (wxEvtHandler* evtHandler = m_bpButtonCmpConfig->GetEventHandler()) + evtHandler->ProcessEvent(dummy2); //synchronous call + } + return; //-> swallow event! + + case WXK_F7: + { + wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); //simulate button click + if (wxEvtHandler* evtHandler = m_bpButtonSyncConfig->GetEventHandler()) + evtHandler->ProcessEvent(dummy2); //synchronous call + } + return; //-> swallow event! + + case WXK_F9: + setViewTypeSyncAction(!m_bpButtonViewTypeSyncAction->isActive()); + return; //-> swallow event! + + case WXK_F10: + { + wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); //simulate button click + if (wxEvtHandler* evtHandler = m_bpButtonFilter->GetEventHandler()) + evtHandler->ProcessEvent(dummy2); //synchronous call + } + return; //-> swallow event! + + //redirect certain (unhandled) keys directly to grid! + case WXK_UP: + case WXK_DOWN: + case WXK_LEFT: + case WXK_RIGHT: + case WXK_NUMPAD_UP: + case WXK_NUMPAD_DOWN: + case WXK_NUMPAD_LEFT: + case WXK_NUMPAD_RIGHT: + + case WXK_PAGEUP: + case WXK_PAGEDOWN: + case WXK_HOME: + case WXK_END: + case WXK_NUMPAD_PAGEUP: + case WXK_NUMPAD_PAGEDOWN: + case WXK_NUMPAD_HOME: + case WXK_NUMPAD_END: + if (!isComponentOf(focus, m_gridMainL ) && // + !isComponentOf(focus, m_gridMainC ) && //don't propagate keyboard commands if grid is already in focus + !isComponentOf(focus, m_gridMainR ) && // + !isComponentOf(focus, m_gridNavi ) && + !isComponentOf(focus, m_listBoxHistory) && //don't propagate if selecting config + !isComponentOf(focus, m_directoryLeft ) && //don't propagate if changing directory field + !isComponentOf(focus, m_directoryRight) && + !isComponentOf(focus, m_panelSearch ) && + !isComponentOf(focus, m_scrolledWindowFolderPairs) && + m_gridMainL->IsEnabled()) + if (wxEvtHandler* evtHandler = m_gridMainL->getMainWin().GetEventHandler()) + { + m_gridMainL->SetFocus(); + + event.SetEventType(wxEVT_KEY_DOWN); //the grid event handler doesn't expect wxEVT_CHAR_HOOK! + evtHandler->ProcessEvent(event); //propagating event catched at wxTheApp to child leads to recursion, but we prevented it... + event.Skip(false); //definitively handled now! + return; + } + break; + } + + event.Skip(); +} + + +void MainDialog::onNaviSelection(GridRangeSelectEvent& event) +{ + //scroll m_gridMain to user's new selection on m_gridNavi + ptrdiff_t leadRow = -1; + if (event.positive_ && event.rowFirst_ != event.rowLast_) + if (std::unique_ptr node = treeDataView->getLine(event.rowFirst_)) + { + if (const TreeView::RootNode* root = dynamic_cast(node.get())) + leadRow = gridDataView->findRowFirstChild(&(root->baseDirObj_)); + else if (const TreeView::DirNode* dir = dynamic_cast(node.get())) + { + leadRow = gridDataView->findRowDirect(&(dir->dirObj_)); + if (leadRow < 0) //directory was filtered out! still on tree view (but NOT on grid view) + leadRow = gridDataView->findRowFirstChild(&(dir->dirObj_)); + } + else if (const TreeView::FilesNode* files = dynamic_cast(node.get())) + { + assert(!files->filesAndLinks_.empty()); + if (!files->filesAndLinks_.empty()) + leadRow = gridDataView->findRowDirect(files->filesAndLinks_[0]->getId()); + } + } + + if (leadRow >= 0) + { + leadRow = std::max(0, leadRow - 1); //scroll one more row + + m_gridMainL->scrollTo(leadRow); //scroll all of them (includes the "scroll master") + m_gridMainC->scrollTo(leadRow); // + m_gridMainR->scrollTo(leadRow); // + + m_gridNavi->getMainWin().Update(); //draw cursor immediately rather than on next idle event (required for slow CPUs, netbook) + } + + //get selection on navigation tree and set corresponding markers on main grid + hash_set markedFilesAndLinks; //mark files/symlinks directly + hash_set markedContainer; //mark full container including child-objects + + const std::vector& selection = m_gridNavi->getSelectedRows(); + std::for_each(selection.begin(), selection.end(), + [&](size_t row) + { + if (std::unique_ptr node = treeDataView->getLine(row)) + { + if (const TreeView::RootNode* root = dynamic_cast(node.get())) + markedContainer.insert(&(root->baseDirObj_)); + else if (const TreeView::DirNode* dir = dynamic_cast(node.get())) + markedContainer.insert(&(dir->dirObj_)); + else if (const TreeView::FilesNode* files = dynamic_cast(node.get())) + markedFilesAndLinks.insert(files->filesAndLinks_.begin(), files->filesAndLinks_.end()); + } + }); + + gridview::setNavigationMarker(*m_gridMainL, std::move(markedFilesAndLinks), std::move(markedContainer)); + + event.Skip(); +} + + +void MainDialog::onNaviGridContext(GridClickEvent& event) +{ + const auto& selection = getTreeSelection(); //referenced by lambdas! + ContextMenu menu; + + //---------------------------------------------------------------------------------------------------- + if (!selection.empty()) + //std::any_of(selection.begin(), selection.end(), [](const FileSystemObject* fsObj){ return fsObj->getSyncOperation() != SO_EQUAL; })) -> doesn't consider directories + { + auto getImage = [&](SyncDirection dir, SyncOperation soDefault) + { + return mirrorIfRtl(getSyncOpImage(selection[0]->getSyncOperation() != SO_EQUAL ? + selection[0]->testSyncOperation(dir) : soDefault)); + }; + const wxBitmap opRight = getImage(SyncDirection::RIGHT, SO_OVERWRITE_RIGHT); + const wxBitmap opNone = getImage(SyncDirection::NONE, SO_DO_NOTHING ); + const wxBitmap opLeft = getImage(SyncDirection::LEFT, SO_OVERWRITE_LEFT ); + + wxString shortCutLeft = L"\tAlt+Left"; + wxString shortCutRight = L"\tAlt+Right"; + if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft) + std::swap(shortCutLeft, shortCutRight); + + menu.addItem(_("Set direction:") + L" ->" + shortCutRight, [this, &selection] { setSyncDirManually(selection, SyncDirection::RIGHT); }, &opRight); + menu.addItem(_("Set direction:") + L" -" L"\tAlt+Down", [this, &selection] { setSyncDirManually(selection, SyncDirection::NONE); }, &opNone); + menu.addItem(_("Set direction:") + L" <-" + shortCutLeft, [this, &selection] { setSyncDirManually(selection, SyncDirection::LEFT); }, &opLeft); + //Gtk needs a direction, "<-", because it has no context menu icons! + //Gtk requires "no spaces" for shortcut identifiers! + menu.addSeparator(); + } + + //---------------------------------------------------------------------------------------------------- + //FILE FILTER + auto addFilterMenu = [&](const std::wstring& label, const wxString& iconName, bool include) + { + if (selection.size() == 1) + { + ContextMenu submenu; + + const bool isDir = dynamic_cast(selection[0]) != nullptr; + + //by short name + Zstring labelShort = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + selection[0]->getObjShortName(); + if (isDir) + labelShort += Zstring(FILE_NAME_SEPARATOR) + Zstr("*"); + submenu.addItem(utfCvrtTo(labelShort), [this, &selection, include] { filterShortname(*selection[0], include); }); + + //by relative path + Zstring labelRel = FILE_NAME_SEPARATOR + selection[0]->getObjRelativeName(); + if (isDir) + labelRel += Zstring(FILE_NAME_SEPARATOR) + Zstr("*"); + submenu.addItem(utfCvrtTo(labelRel), [this, &selection, include] { filterItems(selection, include); }); + + menu.addSubmenu(label, submenu, &getResourceImage(iconName)); + } + else if (selection.size() > 1) + { + //by relative path + menu.addItem(label + L" <" + _("multiple selection") + L">", + [this, &selection, include] { filterItems(selection, include); }, &getResourceImage(iconName)); + } + }; + addFilterMenu(_("Include via filter:"), L"filter_include_small", true); + addFilterMenu(_("Exclude via filter:"), L"filter_exclude_small", false); + + //---------------------------------------------------------------------------------------------------- + if (!selection.empty()) + { + if (selection[0]->isActive()) + menu.addItem(_("Exclude temporarily") + L"\tSpace", [this, &selection] { setFilterManually(selection, false); }, &getResourceImage(L"checkboxFalse")); + else + menu.addItem(_("Include temporarily") + L"\tSpace", [this, &selection] { setFilterManually(selection, true); }, &getResourceImage(L"checkboxTrue")); + } + else + menu.addItem(_("Exclude temporarily") + L"\tSpace", [] {}, nullptr, false); + + //---------------------------------------------------------------------------------------------------- + //CONTEXT_DELETE_FILES + menu.addSeparator(); + + menu.addItem(_("Delete") + L"\tDel", [&] { deleteSelectedFiles(selection, selection); }, nullptr, !selection.empty()); + + menu.popup(*this); +} + + +void MainDialog::onMainGridContextC(GridClickEvent& event) +{ + ContextMenu menu; + + menu.addItem(_("Include all"), [&] + { + zen::setActiveStatus(true, folderCmp); + updateGui(); + }, nullptr, gridDataView->rowsTotal() > 0); + + menu.addItem(_("Exclude all"), [&] + { + zen::setActiveStatus(false, folderCmp); + updateGuiDelayedIf(currentCfg.hideExcludedItems); //show update GUI before removing rows + }, nullptr, gridDataView->rowsTotal() > 0); + + menu.popup(*this); +} + +void MainDialog::onMainGridContextL(GridClickEvent& event) +{ + onMainGridContextRim(true); +} + +void MainDialog::onMainGridContextR(GridClickEvent& event) +{ + onMainGridContextRim(false); +} + +void MainDialog::onMainGridContextRim(bool leftSide) +{ + const auto& selection = getGridSelection(); //referenced by lambdas! + ContextMenu menu; + + if (!selection.empty()) + { + auto getImage = [&](SyncDirection dir, SyncOperation soDefault) + { + return mirrorIfRtl(getSyncOpImage(selection[0]->getSyncOperation() != SO_EQUAL ? + selection[0]->testSyncOperation(dir) : soDefault)); + }; + const wxBitmap opRight = getImage(SyncDirection::RIGHT, SO_OVERWRITE_RIGHT); + const wxBitmap opNone = getImage(SyncDirection::NONE, SO_DO_NOTHING ); + const wxBitmap opLeft = getImage(SyncDirection::LEFT, SO_OVERWRITE_LEFT ); + + wxString shortCutLeft = L"\tAlt+Left"; + wxString shortCutRight = L"\tAlt+Right"; + if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft) + std::swap(shortCutLeft, shortCutRight); + + menu.addItem(_("Set direction:") + L" ->" + shortCutRight, [this, &selection] { setSyncDirManually(selection, SyncDirection::RIGHT); }, &opRight); + menu.addItem(_("Set direction:") + L" -" L"\tAlt+Down", [this, &selection] { setSyncDirManually(selection, SyncDirection::NONE); }, &opNone); + menu.addItem(_("Set direction:") + L" <-" + shortCutLeft, [this, &selection] { setSyncDirManually(selection, SyncDirection::LEFT); }, &opLeft); + //Gtk needs a direction, "<-", because it has no context menu icons! + //Gtk requires "no spaces" for shortcut identifiers! + menu.addSeparator(); + } + + //---------------------------------------------------------------------------------------------------- + //FILE FILTER + auto addFilterMenu = [&](const wxString& label, const wxString& iconName, bool include) + { + if (selection.size() == 1) + { + ContextMenu submenu; + + const bool isDir = dynamic_cast(selection[0]) != nullptr; + + //by extension + if (!isDir) + { + const Zstring filename = afterLast(selection[0]->getObjRelativeName(), FILE_NAME_SEPARATOR); + if (contains(filename, Zchar('.'))) //be careful: afterLast returns the whole string if '.' is not found! + { + const Zstring extension = afterLast(filename, Zchar('.')); + submenu.addItem(L"*." + utfCvrtTo(extension), + [this, extension, include] { filterExtension(extension, include); }); + } + } + + //by short name + Zstring labelShort = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + selection[0]->getObjShortName(); + if (isDir) + labelShort += Zstring(FILE_NAME_SEPARATOR) + Zstr("*"); + submenu.addItem(utfCvrtTo(labelShort), [this, &selection, include] { filterShortname(*selection[0], include); }); + + //by relative path + Zstring labelRel = FILE_NAME_SEPARATOR + selection[0]->getObjRelativeName(); + if (isDir) + labelRel += Zstring(FILE_NAME_SEPARATOR) + Zstr("*"); + submenu.addItem(utfCvrtTo(labelRel), [this, &selection, include] { filterItems(selection, include); }); + + menu.addSubmenu(label, submenu, &getResourceImage(iconName)); + } + else if (selection.size() > 1) + { + //by relative path + menu.addItem(label + L" <" + _("multiple selection") + L">", + [this, &selection, include] { filterItems(selection, include); }, &getResourceImage(iconName)); + } + }; + addFilterMenu(_("Include via filter:"), L"filter_include_small", true); + addFilterMenu(_("Exclude via filter:"), L"filter_exclude_small", false); + + //---------------------------------------------------------------------------------------------------- + if (!selection.empty()) + { + if (selection[0]->isActive()) + menu.addItem(_("Exclude temporarily") + L"\tSpace", [this, &selection] { setFilterManually(selection, false); }, &getResourceImage(L"checkboxFalse")); + else + menu.addItem(_("Include temporarily") + L"\tSpace", [this, &selection] { setFilterManually(selection, true); }, &getResourceImage(L"checkboxTrue")); + } + else + menu.addItem(_("Exclude temporarily") + L"\tSpace", [] {}, nullptr, false); + + //---------------------------------------------------------------------------------------------------- + //CONTEXT_EXTERNAL_APP + if (!globalCfg.gui.externelApplications.empty()) + { + menu.addSeparator(); + + for (auto it = globalCfg.gui.externelApplications.begin(); + it != globalCfg.gui.externelApplications.end(); + ++it) + { + //translate default external apps on the fly: 1. "open in explorer" 2. "start directly" + wxString description = zen::implementation::translate(it->first); + if (description.empty()) + description = L" "; //wxWidgets doesn't like empty items + + const wxString command = it->second; //COPY into lambda + + auto openApp = [this, &selection, leftSide, command] { openExternalApplication(command, selection, leftSide); }; + + if (it == globalCfg.gui.externelApplications.begin()) + description += L"\tEnter"; + + menu.addItem(description, openApp, nullptr, !selection.empty()); + } + } + //---------------------------------------------------------------------------------------------------- + //CONTEXT_DELETE_FILES + menu.addSeparator(); + + menu.addItem(_("Delete") + L"\tDel", [this] + { + deleteSelectedFiles( + getGridSelection(true, false), + getGridSelection(false, true)); + }, nullptr, !selection.empty()); + + menu.popup(*this); +} + + + +void MainDialog::filterPhrase(const Zstring& phrase, bool include, bool addNewLine) +{ + Zstring& filterString = [&]() -> Zstring& + { + if (include) + { + Zstring& includeFilter = currentCfg.mainCfg.globalFilter.includeFilter; + if (NameFilter::isNull(includeFilter, FilterConfig().excludeFilter)) //fancy way of checking for "*" include + includeFilter.clear(); + return includeFilter; + } + else + return currentCfg.mainCfg.globalFilter.excludeFilter; + }(); + + if (addNewLine) + { + if (!filterString.empty() && !endsWith(filterString, Zstr("\n"))) + filterString += Zstr("\n"); + filterString += phrase; + } + else + { + if (!filterString.empty() && !endsWith(filterString, Zstr("\n")) && !endsWith(filterString, Zstr(";"))) + filterString += Zstr("\n"); + filterString += phrase + Zstr(";"); //';' is appended to 'mark' that next exclude extension entry won't write to new line + } + + updateGlobalFilterButton(); + if (include) + applyFilterConfig(); //user's temporary exclusions lost! + else //do not fully apply filter, just exclude new items: preserve user's temporary exclusions + { + std::for_each(begin(folderCmp), end(folderCmp), [&](BaseDirPair& baseDirObj) { addHardFiltering(baseDirObj, phrase); }); + updateGui(); + } +} + + +void MainDialog::filterExtension(const Zstring& extension, bool include) +{ + assert(!extension.empty()); + filterPhrase(Zstr("*.") + extension, include, false); +} + + +void MainDialog::filterShortname(const FileSystemObject& fsObj, bool include) +{ + Zstring phrase = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + fsObj.getObjShortName(); + const bool isDir = dynamic_cast(&fsObj) != nullptr; + if (isDir) + phrase += Zstring(FILE_NAME_SEPARATOR) + Zstr("*"); //include filter: * required; exclude filter: * optional, but let's still apply it! + + filterPhrase(phrase, include, true); +} + + +void MainDialog::filterItems(const std::vector& selection, bool include) +{ + if (!selection.empty()) + { + Zstring phrase; + for (auto it = selection.begin(); it != selection.end(); ++it) + { + FileSystemObject* fsObj = *it; + + if (it != selection.begin()) + phrase += Zstr("\n"); + + //#pragma warning(suppress: 6011) -> fsObj bound in this context! + phrase += FILE_NAME_SEPARATOR + fsObj->getObjRelativeName(); + + const bool isDir = dynamic_cast(fsObj) != nullptr; + if (isDir) + phrase += Zstring(FILE_NAME_SEPARATOR) + Zstr("*"); //include filter: * required; exclude filter: * optional, but let's still apply it! + } + filterPhrase(phrase, include, true); + } +} + + +void MainDialog::onGridLabelContextC(GridClickEvent& event) +{ + ContextMenu menu; + + const bool actionView = m_bpButtonViewTypeSyncAction->isActive(); + menu.addRadio(_("Category") + (actionView ? L"\tF9" : L""), [&] { setViewTypeSyncAction(false); }, !actionView); + menu.addRadio(_("Action") + (!actionView ? L"\tF9" : L""), [&] { setViewTypeSyncAction(true ); }, actionView); + + //menu.addItem(_("Category") + L"\tF9", [&] { setViewTypeSyncAction(false); }, m_bpButtonViewTypeSyncAction->isActive() ? nullptr : &getResourceImage(L"compare_small")); + //menu.addItem(_("Action"), [&] { setViewTypeSyncAction(true ); }, m_bpButtonViewTypeSyncAction->isActive() ? &getResourceImage(L"sync_small") : nullptr); + menu.popup(*this); +} + + +void MainDialog::onGridLabelContextL(GridClickEvent& event) +{ + onGridLabelContext(*m_gridMainL, static_cast(event.colType_), getDefaultColumnAttributesLeft()); +} +void MainDialog::onGridLabelContextR(GridClickEvent& event) +{ + onGridLabelContext(*m_gridMainR, static_cast(event.colType_), getDefaultColumnAttributesRight()); +} + + +void MainDialog::onGridLabelContext(Grid& grid, ColumnTypeRim type, const std::vector& defaultColumnAttributes) +{ + ContextMenu menu; + + auto toggleColumn = [&](ColumnType ct) + { + auto colAttr = grid.getColumnConfig(); + + for (Grid::ColumnAttribute& ca : colAttr) + if (ca.type_ == ct) + { + ca.visible_ = !ca.visible_; + grid.setColumnConfig(colAttr); + return; + } + }; + + if (const GridData* prov = grid.getDataProvider()) + for (const Grid::ColumnAttribute& ca : grid.getColumnConfig()) + menu.addCheckBox(prov->getColumnLabel(ca.type_), [ca, toggleColumn] { toggleColumn(ca.type_); }, + ca.visible_, ca.type_ != static_cast(COL_TYPE_FILENAME)); //do not allow user to hide file name column! + //---------------------------------------------------------------------------------------------- + menu.addSeparator(); + + auto setDefault = [&] + { + grid.setColumnConfig(gridview::convertConfig(defaultColumnAttributes)); + }; + menu.addItem(_("&Default"), setDefault); //'&' -> reuse text from "default" buttons elsewhere + //---------------------------------------------------------------------------------------------- + menu.addSeparator(); + menu.addCheckBox(_("Show icons:"), [&] + { + globalCfg.gui.showIcons = !globalCfg.gui.showIcons; + gridview::setupIcons(*m_gridMainL, *m_gridMainC, *m_gridMainR, globalCfg.gui.showIcons, convert(globalCfg.gui.iconSize)); + + }, globalCfg.gui.showIcons); + + auto setIconSize = [&](xmlAccess::FileIconSize sz) + { + globalCfg.gui.iconSize = sz; + gridview::setupIcons(*m_gridMainL, *m_gridMainC, *m_gridMainR, globalCfg.gui.showIcons, convert(sz)); + }; + auto addSizeEntry = [&](const wxString& label, xmlAccess::FileIconSize sz) + { + auto setIconSize2 = setIconSize; //bring into scope + menu.addRadio(label, [sz, setIconSize2] { setIconSize2(sz); }, globalCfg.gui.iconSize == sz, globalCfg.gui.showIcons); + }; + addSizeEntry(L" " + _("Small" ), xmlAccess::ICON_SIZE_SMALL ); + addSizeEntry(L" " + _("Medium"), xmlAccess::ICON_SIZE_MEDIUM); + addSizeEntry(L" " + _("Large" ), xmlAccess::ICON_SIZE_LARGE ); + //---------------------------------------------------------------------------------------------- + if (type == COL_TYPE_DATE) + { + menu.addSeparator(); + + auto selectTimeSpan = [&] + { + if (showSelectTimespanDlg(this, manualTimeSpanFrom, manualTimeSpanTo) == ReturnSmallDlg::BUTTON_OKAY) + { + applyTimeSpanFilter(folderCmp, manualTimeSpanFrom, manualTimeSpanTo); //overwrite current active/inactive settings + //updateGuiDelayedIf(currentCfg.hideExcludedItems); //show update GUI before removing rows + updateGui(); + } + }; + menu.addItem(_("Select time span..."), selectTimeSpan); + } + + menu.popup(*this); +} + + +void MainDialog::OnContextSetLayout(wxMouseEvent& event) +{ + ContextMenu menu; + + menu.addItem(_("Default view"), [&] + { + m_splitterMain->setSashOffset(0); + auiMgr.LoadPerspective(defaultPerspective); + updateGuiForFolderPair(); + }); + //---------------------------------------------------------------------------------------- + + bool addedSeperator = false; + + const wxAuiPaneInfoArray& paneArray = auiMgr.GetAllPanes(); + for (size_t i = 0; i < paneArray.size(); ++i) + { + wxAuiPaneInfo& paneInfo = paneArray[i]; + if (!paneInfo.IsShown() && + paneInfo.window != compareStatus->getAsWindow() && + paneInfo.window != m_panelSearch) + { + if (!addedSeperator) + { + menu.addSeparator(); + addedSeperator = true; + } + + menu.addItem(replaceCpy(_("Show \"%x\""), L"%x", paneInfo.caption), + [this, &paneInfo] + { + paneInfo.Show(); + this->auiMgr.Update(); + }); + } + } + + menu.popup(*this); +} + + +void MainDialog::OnCompSettingsContext(wxMouseEvent& event) +{ + ContextMenu menu; + + auto setVariant = [&](CompareVariant var) + { + currentCfg.mainCfg.cmpConfig.compareVar = var; + applyCompareConfig(true); //true: switchMiddleGrid + }; + + auto currentVar = getConfig().mainCfg.cmpConfig.compareVar; + + menu.addRadio(_("File time and size"), [&] { setVariant(CMP_BY_TIME_SIZE); }, currentVar == CMP_BY_TIME_SIZE); + menu.addRadio(_("File content" ), [&] { setVariant(CMP_BY_CONTENT); }, currentVar == CMP_BY_CONTENT); + + menu.popup(*this); +} + + +void MainDialog::OnSyncSettingsContext(wxMouseEvent& event) +{ + ContextMenu menu; + + auto setVariant = [&](DirectionConfig::Variant var) + { + currentCfg.mainCfg.syncCfg.directionCfg.var = var; + applySyncConfig(); + }; + + const auto currentVar = getConfig().mainCfg.syncCfg.directionCfg.var; + + menu.addRadio(L"<- " + _("Two way") + L" ->" , [&] { setVariant(DirectionConfig::TWOWAY); }, currentVar == DirectionConfig::TWOWAY); + menu.addRadio( _("Mirror") + L" ->>", [&] { setVariant(DirectionConfig::MIRROR); }, currentVar == DirectionConfig::MIRROR); + menu.addRadio( _("Update") + L" ->" , [&] { setVariant(DirectionConfig::UPDATE); }, currentVar == DirectionConfig::UPDATE); + menu.addRadio( _("Custom") , [&] { setVariant(DirectionConfig::CUSTOM); }, currentVar == DirectionConfig::CUSTOM); + + menu.popup(*this); +} + + +void MainDialog::onNaviPanelFilesDropped(FileDropEvent& event) +{ + loadConfiguration(toZ(event.getFiles())); + event.Skip(); +} + + +void MainDialog::onDirSelected(wxCommandEvent& event) +{ + //left and right directory text-control and dirpicker are synchronized by MainFolderDragDrop automatically + clearGrid(); //disable the sync button + event.Skip(); +} + + +void MainDialog::onDirManualCorrection(wxCommandEvent& event) +{ + updateUnsavedCfgStatus(); + event.Skip(); +} + + +wxString getFormattedHistoryElement(const Zstring& filename) +{ + Zstring output = afterLast(filename, FILE_NAME_SEPARATOR); + if (endsWith(output, Zstr(".ffs_gui"))) + output = beforeLast(output, Zstr('.')); + return utfCvrtTo(output); +} + + +void MainDialog::addFileToCfgHistory(const std::vector& filenames) +{ + //determine highest "last use" index number of m_listBoxHistory + int lastUseIndexMax = 0; + for (unsigned int i = 0; i < m_listBoxHistory->GetCount(); ++i) + if (auto histData = dynamic_cast(m_listBoxHistory->GetClientObject(i))) + lastUseIndexMax = std::max(lastUseIndexMax, histData->lastUseIndex_); + else + assert(false); + + std::deque selections(m_listBoxHistory->GetCount()); //items to select after update of history list + + for (const Zstring& filename : filenames) + { + //Do we need to additionally check for aliases of the same physical files here? (and aliases for lastRunConfigName?) + + const auto itemPos = [&]() -> std::pair + { + for (unsigned int i = 0; i < m_listBoxHistory->GetCount(); ++i) + if (auto histData = dynamic_cast(m_listBoxHistory->GetClientObject(i))) + { + if (EqualFilename()(filename, histData->cfgFile_)) + return std::make_pair(histData, i); + } + else + assert(false); + return std::make_pair(nullptr, 0); + }(); + + if (itemPos.first) //update + { + itemPos.first->lastUseIndex_ = ++lastUseIndexMax; + selections[itemPos.second] = true; + } + else //insert + { + wxString label; + unsigned int newPos = 0; + + if (EqualFilename()(filename, lastRunConfigName())) + label = L"<" + _("Last session") + L">"; + else + { + //workaround wxWidgets 2.9 bug on GTK screwing up the client data if the list box is sorted: + label = getFormattedHistoryElement(filename); + //"linear-time insertion sort": + for (; newPos < m_listBoxHistory->GetCount(); ++newPos) + if (label.CmpNoCase(m_listBoxHistory->GetString(newPos)) < 0) + break; + } + + assert(!m_listBoxHistory->IsSorted()); + m_listBoxHistory->Insert(label, newPos, new wxClientHistoryData(filename, ++lastUseIndexMax)); + + selections.insert(selections.begin() + newPos, true); + } + } + + assert(selections.size() == m_listBoxHistory->GetCount()); + + //do not apply selections immediately but only when needed! + //this prevents problems with m_listBoxHistory losing keyboard selection focus if identical selection is redundantly reapplied + for (int pos = 0; pos < static_cast(selections.size()); ++pos) + if (m_listBoxHistory->IsSelected(pos) != selections[pos]) + m_listBoxHistory->SetSelection(pos, selections[pos]); +} + + +void MainDialog::removeObsoleteCfgHistoryItems(const std::vector& filenames) +{ + //don't use wxString: NOT thread-safe! (e.g. non-atomic ref-count) + + auto getMissingFilesAsync = [filenames]() -> std::vector + { + //boost::this_thread::sleep(boost::posix_time::millisec(5000)); + + //check existence of all config files in parallel! + std::list> fileEx; + + for (const Zstring& filename : filenames) + fileEx.push_back(zen::async2([=] { return fileExists(filename); })); + + //potentially slow network access => limit maximum wait time! + wait_for_all_timed(fileEx.begin(), fileEx.end(), boost::posix_time::milliseconds(1000)); + + std::vector missingFiles; + + auto itFut = fileEx.begin(); + for (auto it = filenames.begin(); it != filenames.end(); ++it, (void)++itFut) //void: prevent ADL from dragging in boost's ,-overload: "MSVC warning C4913: user defined binary operator ',' exists but no overload could convert all operands" + if (itFut->is_ready() && !itFut->get()) //remove only files that are confirmed to be non-existent + missingFiles.push_back(*it); + + return missingFiles; + }; + + processAsync(getMissingFilesAsync, [this](const std::vector& files) { removeCfgHistoryItems(files); }); +} + + +void MainDialog::removeCfgHistoryItems(const std::vector& filenames) +{ + std::for_each(filenames.begin(), filenames.end(), [&](const Zstring& filename) + { + const int histSize = m_listBoxHistory->GetCount(); + for (int i = 0; i < histSize; ++i) + if (auto histData = dynamic_cast(m_listBoxHistory->GetClientObject(i))) + if (EqualFilename()(filename, histData->cfgFile_)) + { + m_listBoxHistory->Delete(i); + break; + } + }); +} + + +void MainDialog::updateUnsavedCfgStatus() +{ + const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilename()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); + + const bool haveUnsavedCfg = lastConfigurationSaved != getConfig(); + + //update save config button + const bool allowSave = haveUnsavedCfg || + activeConfigFiles.size() > 1; + + auto makeBrightGrey = [](const wxBitmap& bmp) -> wxBitmap + { + wxImage img = bmp.ConvertToImage().ConvertToGreyscale(1.0/3, 1.0/3, 1.0/3); //treat all channels equally! + brighten(img, 80); + return img; + }; + //setImage(*m_bpButtonSave, greyScale(getResourceImage(L"save"))); + + setImage(*m_bpButtonSave, allowSave ? getResourceImage(L"save") : makeBrightGrey(getResourceImage(L"save"))); + m_bpButtonSave->Enable(allowSave); + m_menuItemSave->Enable(allowSave); //bitmap is automatically greyscaled on Win7 (introducing a crappy looking shift), but not on XP + + //set main dialog title + wxString title; + if (haveUnsavedCfg) + title += L'*'; + + if (!activeCfgFilename.empty()) + title += toWx(activeCfgFilename); + else if (activeConfigFiles.size() > 1) + { + const wchar_t* EM_DASH = L" \u2014 "; + title += xmlAccess::extractJobName(activeConfigFiles[0]); + std::for_each(activeConfigFiles.begin() + 1, activeConfigFiles.end(), [&](const Zstring& filename) { title += EM_DASH + xmlAccess::extractJobName(filename); }); + } + else + title += L"FreeFileSync - " + _("Folder Comparison and Synchronization"); + + SetTitle(title); +} + + +void MainDialog::OnConfigSave(wxCommandEvent& event) +{ + const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilename()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); + + using namespace xmlAccess; + + //if we work on a single named configuration document: save directly if changed + //else: always show file dialog + if (activeCfgFilename.empty()) + trySaveConfig(nullptr); + else + try + { + switch (getXmlType(activeCfgFilename)) //throw FfsXmlError + { + case XML_TYPE_GUI: + trySaveConfig(&activeCfgFilename); + break; + case XML_TYPE_BATCH: + trySaveBatchConfig(&activeCfgFilename); + break; + case XML_TYPE_GLOBAL: + case XML_TYPE_OTHER: + showNotificationDialog(this, DialogInfoType::ERROR2, + PopupDialogCfg().setDetailInstructions(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(activeCfgFilename)))); + break; + } + } + catch (const FfsXmlError& e) + { + showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); + } +} + + +void MainDialog::OnConfigSaveAs(wxCommandEvent& event) +{ + trySaveConfig(nullptr); +} + + +void MainDialog::OnSaveAsBatchJob(wxCommandEvent& event) +{ + trySaveBatchConfig(nullptr); +} + + +bool MainDialog::trySaveConfig(const Zstring* fileNameGui) //return true if saved successfully +{ + Zstring targetFilename; + + if (fileNameGui) + { + targetFilename = *fileNameGui; + assert(endsWith(targetFilename, Zstr(".ffs_gui"))); + } + else + { + Zstring defaultFileName = activeConfigFiles.size() == 1 && !EqualFilename()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstr("SyncSettings.ffs_gui"); + //attention: activeConfigFiles may be an imported *.ffs_batch file! We don't want to overwrite it with a GUI config! + if (endsWith(defaultFileName, Zstr(".ffs_batch"))) + replace(defaultFileName, Zstr(".ffs_batch"), Zstr(".ffs_gui"), false); + + wxFileDialog filePicker(this, //put modal dialog on stack: creating this on freestore leads to memleak! + wxEmptyString, + //OS X really needs dir/file separated like this: + utfCvrtTo(beforeLast(defaultFileName, FILE_NAME_SEPARATOR)), //default dir; empty string if / not found + utfCvrtTo(afterLast (defaultFileName, FILE_NAME_SEPARATOR)), //default file; whole string if / not found + wxString(L"FreeFileSync (*.ffs_gui)|*.ffs_gui") + L"|" +_("All files") + L" (*.*)|*", + wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + if (filePicker.ShowModal() != wxID_OK) + return false; + targetFilename = toZ(filePicker.GetPath()); + } + + const xmlAccess::XmlGuiConfig guiCfg = getConfig(); + + try + { + xmlAccess::writeConfig(guiCfg, targetFilename); //throw FfsXmlError + setLastUsedConfig(targetFilename, guiCfg); + + flashStatusInformation(_("Configuration saved")); + return true; + } + catch (const xmlAccess::FfsXmlError& e) + { + showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); + return false; + } +} + + +bool MainDialog::trySaveBatchConfig(const Zstring* fileNameBatch) +{ + //essentially behave like trySaveConfig(): the collateral damage of not saving GUI-only settings "hideExcludedItems, m_bpButtonViewTypeSyncAction" is negliable + + const xmlAccess::XmlGuiConfig guiCfg = getConfig(); + + Zstring targetFilename; + xmlAccess::XmlBatchConfig batchCfg; + + if (fileNameBatch) + { + targetFilename = *fileNameBatch; + batchCfg = convertGuiToBatchPreservingExistingBatch(guiCfg, *fileNameBatch); + assert(endsWith(targetFilename, Zstr(".ffs_batch"))); + } + else + { + const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilename()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); + batchCfg = convertGuiToBatchPreservingExistingBatch(guiCfg, activeCfgFilename); + + //let user change batch config: this should change batch-exclusive settings only, else the "setLastUsedConfig" below would be somewhat of a lie + if (!customizeBatchConfig(this, + batchCfg, //in/out + globalCfg.gui.onCompletionHistory, + globalCfg.gui.onCompletionHistoryMax)) + return false; + + Zstring defaultFileName = !activeCfgFilename.empty() ? activeCfgFilename : Zstr("BatchRun.ffs_batch"); + //attention: activeConfigFiles may be a *.ffs_gui file! We don't want to overwrite it with a BATCH config! + if (endsWith(defaultFileName, Zstr(".ffs_gui"))) + replace(defaultFileName, Zstr(".ffs_gui"), Zstr(".ffs_batch")); + + wxFileDialog filePicker(this, //put modal dialog on stack: creating this on freestore leads to memleak! + wxEmptyString, + //OS X really needs dir/file separated like this: + utfCvrtTo(beforeLast(defaultFileName, FILE_NAME_SEPARATOR)), //default dir; empty string if / not found + utfCvrtTo(afterLast (defaultFileName, FILE_NAME_SEPARATOR)), //default file; whole string if / not found + _("FreeFileSync batch") + L" (*.ffs_batch)|*.ffs_batch" + L"|" +_("All files") + L" (*.*)|*", + wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + if (filePicker.ShowModal() != wxID_OK) + return false; + targetFilename = toZ(filePicker.GetPath()); + } + + try + { + xmlAccess::writeConfig(batchCfg, targetFilename); //throw FfsXmlError + + setLastUsedConfig(targetFilename, guiCfg); //[!] behave as if we had saved guiCfg + flashStatusInformation(_("Configuration saved")); + return true; + } + catch (const xmlAccess::FfsXmlError& e) + { + showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); + return false; + } +} + + +bool MainDialog::saveOldConfig() //return false on user abort +{ + if (lastConfigurationSaved != getConfig()) + { + const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilename()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); + + //notify user about changed settings + if (globalCfg.optDialogs.popupOnConfigChange) + if (!activeCfgFilename.empty()) + //only if check is active and non-default config file loaded + { + bool neverSaveChanges = false; + switch (showConfirmationDialog3(this, DialogInfoType::INFO, PopupDialogCfg3(). + setTitle(toWx(activeCfgFilename)). + setMainInstructions(replaceCpy(_("Do you want to save changes to %x?"), L"%x", fmtFileName(afterLast(activeCfgFilename, FILE_NAME_SEPARATOR)))). + setCheckBox(neverSaveChanges, _("Never save &changes"), ConfirmationButton3::DO_IT), + _("&Save"), _("Do&n't save"))) + { + case ConfirmationButton3::DO_IT: //save + using namespace xmlAccess; + + try + { + switch (getXmlType(activeCfgFilename)) //throw FfsXmlError + { + case XML_TYPE_GUI: + return trySaveConfig(&activeCfgFilename); + case XML_TYPE_BATCH: + return trySaveBatchConfig(&activeCfgFilename); + case XML_TYPE_GLOBAL: + case XML_TYPE_OTHER: + showNotificationDialog(this, DialogInfoType::ERROR2, + PopupDialogCfg().setDetailInstructions(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(activeCfgFilename)))); + return false; + } + } + catch (const FfsXmlError& e) + { + showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); + return false; + } + break; + + case ConfirmationButton3::DONT_DO_IT: //don't save + globalCfg.optDialogs.popupOnConfigChange = !neverSaveChanges; + break; + + case ConfirmationButton3::CANCEL: + return false; + } + } + + //discard current reference file(s), this ensures next app start will load instead of the original non-modified config selection + setLastUsedConfig(std::vector(), lastConfigurationSaved); + //this seems to make theoretical sense also: the job of this function is to make sure current (volatile) config and reference file name are in sync + // => if user does not save cfg, it is not attached to a physical file names anymore! + } + return true; +} + + +void MainDialog::OnConfigLoad(wxCommandEvent& event) +{ + const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilename()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); + + wxFileDialog filePicker(this, + wxEmptyString, + utfCvrtTo(beforeLast(activeCfgFilename, FILE_NAME_SEPARATOR)), //set default dir: empty string if "activeConfigFiles" is empty or has no path separator + wxEmptyString, + wxString(L"FreeFileSync (*.ffs_gui; *.ffs_batch)|*.ffs_gui;*.ffs_batch") + L"|" +_("All files") + L" (*.*)|*", + wxFD_OPEN | wxFD_MULTIPLE); + + if (filePicker.ShowModal() == wxID_OK) + { + wxArrayString tmp; + filePicker.GetPaths(tmp); + std::vector filenames(tmp.begin(), tmp.end()); + + loadConfiguration(toZ(filenames)); + } +} + + +void MainDialog::OnConfigNew(wxCommandEvent& event) +{ + if (!saveOldConfig()) //notify user about changed settings + return; + + xmlAccess::XmlGuiConfig newConfig; + + //add default exclusion filter: this is only ever relevant when creating new configurations! + //a default XmlGuiConfig does not need these user-specific exclusions! + Zstring& excludeFilter = newConfig.mainCfg.globalFilter.excludeFilter; + if (!excludeFilter.empty() && !endsWith(excludeFilter, Zstr("\n"))) + excludeFilter += Zstr("\n"); + excludeFilter += globalCfg.gui.defaultExclusionFilter; + + setConfig(newConfig, std::vector()); +} + + +void MainDialog::OnLoadFromHistory(wxCommandEvent& event) +{ + wxArrayInt selections; + m_listBoxHistory->GetSelections(selections); + + std::vector filenames; + std::for_each(selections.begin(), selections.end(), + [&](int pos) + { + if (auto histData = dynamic_cast(m_listBoxHistory->GetClientObject(pos))) + filenames.push_back(histData->cfgFile_); + else + assert(false); + }); + + if (!filenames.empty()) + loadConfiguration(filenames); + + //user changed m_listBoxHistory selection so it's this method's responsibility to synchronize with activeConfigFiles: + //- if user cancelled saving old config + //- there's an error loading new config + //- filenames is empty and user tried to unselect the current config + addFileToCfgHistory(activeConfigFiles); +} + + +void MainDialog::OnLoadFromHistoryDoubleClick(wxCommandEvent& event) +{ + wxArrayInt selections; + m_listBoxHistory->GetSelections(selections); + + std::vector filenames; + std::for_each(selections.begin(), selections.end(), [&](int pos) + { + if (auto histData = dynamic_cast(m_listBoxHistory->GetClientObject(pos))) + filenames.push_back(histData->cfgFile_); + else + assert(false); + }); + + if (!filenames.empty()) + if (loadConfiguration(filenames)) + { + //simulate button click on "compare" + wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); + if (wxEvtHandler* evtHandler = m_buttonCompare->GetEventHandler()) + evtHandler->ProcessEvent(dummy2); //synchronous call + } + + //synchronize m_listBoxHistory and activeConfigFiles, see OnLoadFromHistory() + addFileToCfgHistory(activeConfigFiles); +} + + +bool MainDialog::loadConfiguration(const std::vector& filenames) +{ + if (filenames.empty()) + return true; + + if (!saveOldConfig()) + return false; //cancelled by user + + //load XML + xmlAccess::XmlGuiConfig newGuiCfg; //structure to receive gui settings, already defaulted!! + try + { + //allow reading batch configurations also + xmlAccess::readAnyConfig(filenames, newGuiCfg); //throw FfsXmlError + + setConfig(newGuiCfg, filenames); + //flashStatusInformation(("Configuration loaded")); -> irrelevant!? + return true; + } + catch (const xmlAccess::FfsXmlError& e) + { + if (e.getSeverity() == xmlAccess::FfsXmlError::WARNING) + { + showNotificationDialog(this, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(e.toString())); + setConfig(newGuiCfg, filenames); + setLastUsedConfig(filenames, xmlAccess::XmlGuiConfig()); //simulate changed config due to parsing errors + } + else + showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); + return false; + } +} + + +void MainDialog::deleteSelectedCfgHistoryItems() +{ + wxArrayInt tmp; + m_listBoxHistory->GetSelections(tmp); + + std::set selections(tmp.begin(), tmp.end()); //sort ascending! + //delete starting with high positions: + std::for_each(selections.rbegin(), selections.rend(), [&](int pos) { m_listBoxHistory->Delete(pos); }); + + //set active selection on next element to allow "batch-deletion" by holding down DEL key + if (!selections.empty() && m_listBoxHistory->GetCount() > 0) + { + int newSelection = *selections.begin(); + if (newSelection >= static_cast(m_listBoxHistory->GetCount())) + newSelection = m_listBoxHistory->GetCount() - 1; + m_listBoxHistory->SetSelection(newSelection); + } +} + + +void MainDialog::OnCfgHistoryRightClick(wxMouseEvent& event) +{ + ContextMenu menu; + menu.addItem(_("Delete") + L"\tDel", [this] { deleteSelectedCfgHistoryItems(); }); + menu.popup(*this); +} + + +void MainDialog::OnCfgHistoryKeyEvent(wxKeyEvent& event) +{ + const int keyCode = event.GetKeyCode(); + if (keyCode == WXK_DELETE || keyCode == WXK_NUMPAD_DELETE) + { + deleteSelectedCfgHistoryItems(); + return; //"swallow" event + } + event.Skip(); +} + + +void MainDialog::OnClose(wxCloseEvent& event) +{ + //attention: system shutdown: is handled in onQueryEndSession()! + + //regular destruction handling + if (event.CanVeto()) + { + const bool cancelled = !saveOldConfig(); //notify user about changed settings + if (cancelled) + { + //attention: this Veto() will NOT cancel system shutdown since saveOldConfig() blocks on modal dialog + + event.Veto(); + return; + } + } + + Destroy(); +} + + +void MainDialog::onCheckRows(CheckRowsEvent& event) +{ + std::set selectedRows; + + const size_t rowLast = std::min(event.rowLast_, gridDataView->rowsOnView()); //consider dummy rows + for (size_t i = event.rowFirst_; i < rowLast; ++i) + selectedRows.insert(i); + + if (!selectedRows.empty()) + { + std::vector objects = gridDataView->getAllFileRef(selectedRows); + setFilterManually(objects, event.setIncluded_); + } +} + + +void MainDialog::onSetSyncDirection(SyncDirectionEvent& event) +{ + std::set selectedRows; + + const size_t rowLast = std::min(event.rowLast_, gridDataView->rowsOnView()); //consider dummy rows + for (size_t i = event.rowFirst_; i < rowLast; ++i) + selectedRows.insert(i); + + if (!selectedRows.empty()) + { + std::vector objects = gridDataView->getAllFileRef(selectedRows); + setSyncDirManually(objects, event.direction_); + } +} + + +void MainDialog::setLastUsedConfig(const Zstring& filename, const xmlAccess::XmlGuiConfig& guiConfig) +{ + std::vector filenames; + filenames.push_back(filename); + setLastUsedConfig(filenames, guiConfig); +} + + +void MainDialog::setLastUsedConfig(const std::vector& filenames, + const xmlAccess::XmlGuiConfig& guiConfig) +{ + activeConfigFiles = filenames; + lastConfigurationSaved = guiConfig; + + addFileToCfgHistory(activeConfigFiles); //put filename on list of last used config files + + updateUnsavedCfgStatus(); +} + + +void MainDialog::setConfig(const xmlAccess::XmlGuiConfig& newGuiCfg, const std::vector& referenceFiles) +{ + currentCfg = newGuiCfg; + + //evaluate new settings... + + //(re-)set view filter buttons + setViewFilterDefault(); + + updateGlobalFilterButton(); + + //set first folder pair + firstFolderPair->setValues(currentCfg.mainCfg.firstPair.dirnamePhraseLeft, + currentCfg.mainCfg.firstPair.dirnamePhraseRight, + currentCfg.mainCfg.firstPair.altCmpConfig, + currentCfg.mainCfg.firstPair.altSyncConfig, + currentCfg.mainCfg.firstPair.localFilter); + + //folderHistoryLeft->addItem(currentCfg.mainCfg.firstPair.leftDirectory); + //folderHistoryRight->addItem(currentCfg.mainCfg.firstPair.rightDirectory); + + setAddFolderPairs(currentCfg.mainCfg.additionalPairs); + + //read GUI layout + m_checkBoxHideExcluded->SetValue(currentCfg.hideExcludedItems); + + setViewTypeSyncAction(currentCfg.highlightSyncAction); + + clearGrid(); //+ update GUI! + + setLastUsedConfig(referenceFiles, newGuiCfg); +} + + +inline +FolderPairEnh getEnhancedPair(const FolderPairPanel* panel) +{ + return FolderPairEnh(panel->getLeftDir(), + panel->getRightDir(), + panel->getAltCompConfig(), + panel->getAltSyncConfig(), + panel->getAltFilterConfig()); +} + + +xmlAccess::XmlGuiConfig MainDialog::getConfig() const +{ + xmlAccess::XmlGuiConfig guiCfg = currentCfg; + + //load settings whose ownership lies not in currentCfg: + + //first folder pair + guiCfg.mainCfg.firstPair = FolderPairEnh(firstFolderPair->getLeftDir(), + firstFolderPair->getRightDir(), + firstFolderPair->getAltCompConfig(), + firstFolderPair->getAltSyncConfig(), + firstFolderPair->getAltFilterConfig()); + + //add additional pairs + guiCfg.mainCfg.additionalPairs.clear(); + std::transform(additionalFolderPairs.begin(), additionalFolderPairs.end(), + std::back_inserter(guiCfg.mainCfg.additionalPairs), getEnhancedPair); + + //sync preview + guiCfg.highlightSyncAction = m_bpButtonViewTypeSyncAction->isActive(); + + return guiCfg; +} + + +const Zstring& MainDialog::lastRunConfigName() +{ + static Zstring instance = zen::getConfigDir() + Zstr("LastRun.ffs_gui"); + return instance; +} + + +void MainDialog::updateGuiDelayedIf(bool condition) +{ + const int delay = 400; + + if (condition) + { + gridview::refresh(*m_gridMainL, *m_gridMainC, *m_gridMainR); + m_gridMainL->Update(); + m_gridMainC->Update(); + m_gridMainR->Update(); + + wxMilliSleep(delay); //some delay to show the changed GUI before removing rows from sight + } + + updateGui(); +} + + +void MainDialog::OnShowExcluded(wxCommandEvent& event) +{ + //toggle showing filtered rows + currentCfg.hideExcludedItems = !currentCfg.hideExcludedItems; + //make sure, checkbox and value are in sync + m_checkBoxHideExcluded->SetValue(currentCfg.hideExcludedItems); + + updateGui(); +} + + +void MainDialog::OnConfigureFilter(wxCommandEvent& event) +{ + if (showFilterDialog(this, currentCfg.mainCfg.globalFilter, _("Filter")) == ReturnSmallDlg::BUTTON_OKAY) + { + updateGlobalFilterButton(); //refresh global filter icon + applyFilterConfig(); //re-apply filter + } + + //event.Skip() +} + + +void MainDialog::OnGlobalFilterContext(wxMouseEvent& event) +{ + auto clearFilter = [&] + { + currentCfg.mainCfg.globalFilter = FilterConfig(); + updateGlobalFilterButton(); //refresh global filter icon + applyFilterConfig(); //re-apply filter + }; + auto copyFilter = [&] { filterCfgOnClipboard = make_unique(currentCfg.mainCfg.globalFilter); }; + auto pasteFilter = [&] + { + if (filterCfgOnClipboard) + { + currentCfg.mainCfg.globalFilter = *filterCfgOnClipboard; + updateGlobalFilterButton(); //refresh global filter icon + applyFilterConfig(); //re-apply filter + } + }; + + ContextMenu menu; + menu.addItem( _("Clear filter settings"), clearFilter, nullptr, !isNullFilter(currentCfg.mainCfg.globalFilter)); + menu.addSeparator(); + menu.addItem( _("Copy"), copyFilter, nullptr, !isNullFilter(currentCfg.mainCfg.globalFilter)); + menu.addItem( _("Paste"), pasteFilter, nullptr, filterCfgOnClipboard.get() != nullptr); + menu.popup(*this); +} + + +void MainDialog::OnToggleViewType(wxCommandEvent& event) +{ + setViewTypeSyncAction(!m_bpButtonViewTypeSyncAction->isActive()); //toggle view +} + + +void MainDialog::OnToggleViewButton(wxCommandEvent& event) +{ + if (auto button = dynamic_cast(event.GetEventObject())) + { + button->toggle(); + updateGui(); + } + else + assert(false); +} + + +inline +wxBitmap buttonPressed(const std::string& name) +{ + wxBitmap background = getResourceImage(L"buttonPressed"); + return mirrorIfRtl( + layOver(getResourceImage(utfCvrtTo(name)), background)); +} + + +inline +wxBitmap buttonReleased(const std::string& name) +{ + wxImage output = getResourceImage(utfCvrtTo(name)).ConvertToImage().ConvertToGreyscale(1.0/3, 1.0/3, 1.0/3); //treat all channels equally! + //zen::moveImage(output, 1, 0); //move image right one pixel + + brighten(output, 80); + return mirrorIfRtl(output); +} + + +void MainDialog::initViewFilterButtons() +{ + m_bpButtonViewTypeSyncAction->init(getResourceImage(L"viewtype_sync_action"), getResourceImage(L"viewtype_cmp_result")); + //tooltip is updated dynamically in setViewTypeSyncAction() + + auto initButton = [](ToggleButton& btn, const char* imgName, const wxString& tooltip) { btn.init(buttonPressed(imgName), buttonReleased(imgName)); btn.SetToolTip(tooltip); }; + + //compare result buttons + initButton(*m_bpButtonShowLeftOnly, "cat_left_only", _("Show files that exist on left side only")); + initButton(*m_bpButtonShowRightOnly, "cat_right_only", _("Show files that exist on right side only")); + initButton(*m_bpButtonShowLeftNewer, "cat_left_newer", _("Show files that are newer on left")); + initButton(*m_bpButtonShowRightNewer, "cat_right_newer", _("Show files that are newer on right")); + initButton(*m_bpButtonShowEqual, "cat_equal", _("Show files that are equal")); + initButton(*m_bpButtonShowDifferent, "cat_different", _("Show files that are different")); + initButton(*m_bpButtonShowConflict, "cat_conflict", _("Show conflicts")); + + //sync preview buttons + initButton(*m_bpButtonShowCreateLeft, "so_create_left", _("Show files that will be created on the left side")); + initButton(*m_bpButtonShowCreateRight, "so_create_right", _("Show files that will be created on the right side")); + initButton(*m_bpButtonShowDeleteLeft, "so_delete_left", _("Show files that will be deleted on the left side")); + initButton(*m_bpButtonShowDeleteRight, "so_delete_right", _("Show files that will be deleted on the right side")); + initButton(*m_bpButtonShowUpdateLeft, "so_update_left", _("Show files that will be overwritten on left side")); + initButton(*m_bpButtonShowUpdateRight, "so_update_right", _("Show files that will be overwritten on right side")); + initButton(*m_bpButtonShowDoNothing, "so_none", _("Show files that won't be copied")); +} + + +void MainDialog::setViewFilterDefault() +{ + auto setButton = [](ToggleButton* tb, bool value) { tb->setActive(value); }; + + const auto& def = globalCfg.gui.viewFilterDefault; + setButton(m_bpButtonShowLeftOnly, def.leftOnly); + setButton(m_bpButtonShowRightOnly, def.rightOnly); + setButton(m_bpButtonShowLeftNewer, def.leftNewer); + setButton(m_bpButtonShowRightNewer, def.rightNewer); + setButton(m_bpButtonShowEqual, def.equal); + setButton(m_bpButtonShowDifferent, def.different); + setButton(m_bpButtonShowConflict, def.conflict); + + setButton(m_bpButtonShowCreateLeft, def.createLeft); + setButton(m_bpButtonShowCreateRight,def.createRight); + setButton(m_bpButtonShowUpdateLeft, def.updateLeft); + setButton(m_bpButtonShowUpdateRight,def.updateRight); + setButton(m_bpButtonShowDeleteLeft, def.deleteLeft); + setButton(m_bpButtonShowDeleteRight,def.deleteRight); + setButton(m_bpButtonShowDoNothing, def.doNothing); +} + + +void MainDialog::OnViewButtonRightClick(wxMouseEvent& event) +{ + auto setButtonDefault = [](const ToggleButton* tb, bool& defaultValue) + { + if (tb->IsShown()) + defaultValue = tb->isActive(); + }; + + auto setDefault = [&] + { + auto& def = globalCfg.gui.viewFilterDefault; + setButtonDefault(m_bpButtonShowLeftOnly, def.leftOnly); + setButtonDefault(m_bpButtonShowRightOnly, def.rightOnly); + setButtonDefault(m_bpButtonShowLeftNewer, def.leftNewer); + setButtonDefault(m_bpButtonShowRightNewer, def.rightNewer); + setButtonDefault(m_bpButtonShowEqual, def.equal); + setButtonDefault(m_bpButtonShowDifferent, def.different); + setButtonDefault(m_bpButtonShowConflict, def.conflict); + + setButtonDefault(m_bpButtonShowCreateLeft, def.createLeft); + setButtonDefault(m_bpButtonShowCreateRight, def.createRight); + setButtonDefault(m_bpButtonShowUpdateLeft, def.updateLeft); + setButtonDefault(m_bpButtonShowUpdateRight, def.updateRight); + setButtonDefault(m_bpButtonShowDeleteLeft, def.deleteLeft); + setButtonDefault(m_bpButtonShowDeleteRight, def.deleteRight); + setButtonDefault(m_bpButtonShowDoNothing, def.doNothing); + }; + + ContextMenu menu; + menu.addItem( _("Set as default"), setDefault); + menu.popup(*this); +} + + +void MainDialog::updateGlobalFilterButton() +{ + //global filter: test for Null-filter + if (!isNullFilter(currentCfg.mainCfg.globalFilter)) + { + setImage(*m_bpButtonFilter, getResourceImage(L"filter")); + m_bpButtonFilter->SetToolTip(_("Filter") + L" (F10) (" + _("Active") + L")"); + } + else + { + setImage(*m_bpButtonFilter, greyScale(getResourceImage(L"filter"))); + m_bpButtonFilter->SetToolTip(_("Filter") + L" (F10) (" + _("None") + L")"); + } +} + + +void MainDialog::OnCompare(wxCommandEvent& event) +{ + //PERF_START; + + //wxBusyCursor dummy; -> redundant: progress already shown in progress dialog! + + wxWindow* oldFocus = wxWindow::FindFocus(); + ZEN_ON_SCOPE_EXIT(if (oldFocus) oldFocus->SetFocus();); //e.g. keep focus on main grid after pressing F5 + + int scrollPosX = 0; + int scrollPosY = 0; + m_gridMainL->GetViewStart(&scrollPosX, &scrollPosY); //preserve current scroll position + ZEN_ON_SCOPE_EXIT( + m_gridMainL->Scroll(scrollPosX, scrollPosY); // + m_gridMainR->Scroll(scrollPosX, scrollPosY); //restore + m_gridMainC->Scroll(-1, scrollPosY); ) // + + clearGrid(); //avoid memory peak by clearing old data first + + disableAllElements(true); //CompareStatusHandler will internally process Window messages, so avoid unexpected callbacks! + auto app = wxTheApp; //fix lambda/wxWigets/VC fuck up + ZEN_ON_SCOPE_EXIT(app->Yield(); enableAllElements()); //ui update before enabling buttons again: prevent strange behaviour of delayed button clicks + + try + { + //class handling status display and error messages + CompareStatusHandler statusHandler(*this); + + const std::vector cmpConfig = zen::extractCompareCfg(getConfig().mainCfg); + + //GUI mode: place directory locks on directories isolated(!) during both comparison and synchronization + std::unique_ptr dirLocks; + + //COMPARE DIRECTORIES + compare(globalCfg.fileTimeTolerance, + globalCfg.optDialogs, + true, //allow pw prompt + globalCfg.runWithBackgroundPriority, + globalCfg.createLockFile, + dirLocks, + cmpConfig, + folderCmp, + statusHandler); //throw GuiAbortProcess + } + catch (GuiAbortProcess&) + { + // if (m_buttonCompare->IsShownOnScreen()) m_buttonCompare->SetFocus(); + updateGui(); //refresh grid in ANY case! (also on abort) + return; + } + + gridDataView->setData(folderCmp); //update view on data + treeDataView->setData(folderCmp); // + updateGui(); + + // if (m_buttonSync->IsShownOnScreen()) m_buttonSync->SetFocus(); + + m_gridMainL->clearSelection(ALLOW_GRID_EVENT); + m_gridMainC->clearSelection(ALLOW_GRID_EVENT); + m_gridMainR->clearSelection(ALLOW_GRID_EVENT); + + m_gridNavi->clearSelection(ALLOW_GRID_EVENT); + + //play (optional) sound notification after sync has completed (GUI and batch mode) + //const Zstring soundFile = zen::getResourceDir() + Zstr("Compare_Complete.wav"); + //if (fileExists(soundFile)) + // wxSound::Play(toWx(soundFile), wxSOUND_ASYNC); + + //add to folder history after successful comparison only + folderHistoryLeft ->addItem(toZ(m_directoryLeft ->GetValue())); + folderHistoryRight->addItem(toZ(m_directoryRight->GetValue())); + + //prepare status information + if (allElementsEqual(folderCmp)) + flashStatusInformation(_("All folders are in sync")); +} + + +void MainDialog::updateTopButtonImages() +{ + updateTopButton(*m_buttonCompare, getResourceImage(L"compare"), getConfig().mainCfg.getCompVariantName(), false); + updateTopButton(*m_buttonSync, getResourceImage(L"sync"), getConfig().mainCfg.getSyncVariantName(), folderCmp.empty()); + + m_panelTopButtons->Layout(); +} + + +void MainDialog::updateGui() +{ + updateGridViewData(); //update gridDataView and write status information + + updateStatistics(); + + updateUnsavedCfgStatus(); + + updateTopButtonImages(); + + auiMgr.Update(); //fix small display distortion, if view filter panel is empty +} + + +void MainDialog::clearGrid() +{ + folderCmp.clear(); + gridDataView->setData(folderCmp); + treeDataView->setData(folderCmp); + + updateGui(); +} + + +void MainDialog::updateStatistics() +{ + //update preview of item count and bytes to be transferred: + const SyncStatistics st(folderCmp); + + setText(*m_staticTextData, filesizeToShortString(st.getDataToProcess())); + if (st.getDataToProcess() == 0) + m_bitmapData->SetBitmap(greyScale(getResourceImage(L"data"))); + else + m_bitmapData->SetBitmap(getResourceImage(L"data")); + + auto setValue = [](wxStaticText& txtControl, int value, wxStaticBitmap& bmpControl, const wchar_t* bmpName) + { + setText(txtControl, toGuiString(value)); + + if (value == 0) + bmpControl.SetBitmap(greyScale(mirrorIfRtl(getResourceImage(bmpName)))); + else + bmpControl.SetBitmap(mirrorIfRtl(getResourceImage(bmpName))); + }; + + setValue(*m_staticTextCreateLeft, st.getCreate(), *m_bitmapCreateLeft, L"so_create_left_small"); + setValue(*m_staticTextUpdateLeft, st.getUpdate(), *m_bitmapUpdateLeft, L"so_update_left_small"); + setValue(*m_staticTextDeleteLeft, st.getDelete(), *m_bitmapDeleteLeft, L"so_delete_left_small"); + setValue(*m_staticTextCreateRight, st.getCreate(), *m_bitmapCreateRight, L"so_create_right_small"); + setValue(*m_staticTextUpdateRight, st.getUpdate(), *m_bitmapUpdateRight, L"so_update_right_small"); + setValue(*m_staticTextDeleteRight, st.getDelete(), *m_bitmapDeleteRight, L"so_delete_right_small"); + + m_panelStatistics->Layout(); + m_panelStatistics->Refresh(); //fix small mess up on RTL layout +} + + +void MainDialog::OnSyncSettings(wxCommandEvent& event) +{ + ExecWhenFinishedCfg ewfCfg = { ¤tCfg.mainCfg.onCompletion, + &globalCfg.gui.onCompletionHistory, + globalCfg.gui.onCompletionHistoryMax + }; + + if (showSyncConfigDlg(this, + currentCfg.mainCfg.cmpConfig.compareVar, + currentCfg.mainCfg.syncCfg, + _("Synchronization Settings"), + ¤tCfg.handleError, + &ewfCfg) == ReturnSyncConfig::BUTTON_OKAY) //optional input parameter + { + applySyncConfig(); + } +} + + +void MainDialog::applyCompareConfig(bool switchMiddleGrid) +{ + clearGrid(); //+ GUI update + + //convenience: change sync view + if (switchMiddleGrid) + switch (currentCfg.mainCfg.cmpConfig.compareVar) + { + case CMP_BY_TIME_SIZE: + setViewTypeSyncAction(true); + break; + case CMP_BY_CONTENT: + setViewTypeSyncAction(false); + break; + } +} + + +void MainDialog::OnCmpSettings(wxCommandEvent& event) +{ + //show window right next to the compare-config button + //wxPoint windowPos = m_bpButtonCmpConfig->GetScreenPosition(); + //windowPos.x += m_bpButtonCmpConfig->GetSize().GetWidth() + 5; + + CompConfig cmpConfigNew = currentCfg.mainCfg.cmpConfig; + + if (zen::showCompareCfgDialog(this, cmpConfigNew, _("Comparison Settings")) == ReturnSmallDlg::BUTTON_OKAY && + //check if settings were changed at all + cmpConfigNew != currentCfg.mainCfg.cmpConfig) + { + currentCfg.mainCfg.cmpConfig = cmpConfigNew; + + applyCompareConfig(true); //true: switchMiddleGrid + } +} + + +void MainDialog::OnStartSync(wxCommandEvent& event) +{ + if (folderCmp.empty()) + { + //quick sync: simulate button click on "compare" + wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); + if (wxEvtHandler* evtHandler = m_buttonCompare->GetEventHandler()) + evtHandler->ProcessEvent(dummy2); //synchronous call + + if (folderCmp.empty()) //check if user aborted or error occurred, ect... + return; + } + + //show sync preview/confirmation dialog + if (globalCfg.optDialogs.confirmSyncStart) + { + bool dontShowAgain = false; + + if (zen::showSyncConfirmationDlg(this, + getConfig().mainCfg.getSyncVariantName(), + zen::SyncStatistics(folderCmp), + dontShowAgain) != ReturnSmallDlg::BUTTON_OKAY) + return; + + globalCfg.optDialogs.confirmSyncStart = !dontShowAgain; + } + + try + { + //PERF_START; + const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && !EqualFilename()(activeConfigFiles[0], lastRunConfigName()) ? activeConfigFiles[0] : Zstring(); + + const auto& guiCfg = getConfig(); + + disableAllElements(false); //SyncStatusHandler will internally process Window messages, so avoid unexpected callbacks! + ZEN_ON_SCOPE_EXIT(enableAllElements()); + + //class handling status updates and error messages + SyncStatusHandler statusHandler(this, //throw GuiAbortProcess + globalCfg.lastSyncsLogFileSizeMax, + currentCfg.handleError, + globalCfg.automaticRetryCount, + globalCfg.automaticRetryDelay, + xmlAccess::extractJobName(activeCfgFilename), + guiCfg.mainCfg.onCompletion, + globalCfg.gui.onCompletionHistory); + + //wxBusyCursor dummy; -> redundant: progress already shown in progress dialog! + + //GUI mode: place directory locks on directories isolated(!) during both comparison and synchronization + std::unique_ptr dirLocks; + if (globalCfg.createLockFile) + { + std::set dirnamesExisting; + for (auto it = begin(folderCmp); it != end(folderCmp); ++it) + { + if (it->isExisting()) //do NOT check directory existence again! + dirnamesExisting.insert(it->getBaseDirPf()); + if (it->isExisting()) + dirnamesExisting.insert(it->getBaseDirPf()); + } + dirLocks = make_unique(dirnamesExisting, globalCfg.optDialogs.warningDirectoryLockFailed, statusHandler); + } + + //START SYNCHRONIZATION + const std::vector syncProcessCfg = zen::extractSyncCfg(guiCfg.mainCfg); + if (syncProcessCfg.size() != folderCmp.size()) + throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo(__LINE__)); + //should never happen: sync button is deactivated if they are not in sync + + synchronize(localTime(), + globalCfg.optDialogs, + globalCfg.verifyFileCopy, + globalCfg.copyLockedFiles, + globalCfg.copyFilePermissions, + globalCfg.failsafeFileCopy, + globalCfg.runWithBackgroundPriority, + syncProcessCfg, + folderCmp, + statusHandler); + } + catch (GuiAbortProcess&) + { + //do NOT disable the sync button: user might want to try to sync the REMAINING rows + } //enableSynchronization(false); + + //remove empty rows: just a beautification, invalid rows shouldn't cause issues + gridDataView->removeInvalidRows(); + + updateGui(); +} + + +void MainDialog::onGridDoubleClickL(GridClickEvent& event) +{ + onGridDoubleClickRim(event.row_, true); +} +void MainDialog::onGridDoubleClickR(GridClickEvent& event) +{ + onGridDoubleClickRim(event.row_, false); +} + +void MainDialog::onGridDoubleClickRim(size_t row, bool leftSide) +{ + if (!globalCfg.gui.externelApplications.empty()) + { + std::vector selection; + if (FileSystemObject* fsObj = gridDataView->getObject(row)) //selection must be a list of BOUND pointers! + selection.push_back(fsObj); + + openExternalApplication(globalCfg.gui.externelApplications[0].second, selection, leftSide); + } +} + + +void MainDialog::onGridLabelLeftClick(bool onLeft, ColumnTypeRim type) +{ + auto sortInfo = gridDataView->getSortInfo(); + + bool sortAscending = GridView::getDefaultSortDirection(type); + if (sortInfo && sortInfo->onLeft_ == onLeft && sortInfo->type_ == type) + sortAscending = !sortInfo->ascending_; + + gridDataView->sortView(type, onLeft, sortAscending); + + m_gridMainL->clearSelection(ALLOW_GRID_EVENT); + m_gridMainC->clearSelection(ALLOW_GRID_EVENT); + m_gridMainR->clearSelection(ALLOW_GRID_EVENT); + + updateGui(); //refresh gridDataView +} + +void MainDialog::onGridLabelLeftClickL(GridClickEvent& event) +{ + onGridLabelLeftClick(true, static_cast(event.colType_)); +} +void MainDialog::onGridLabelLeftClickR(GridClickEvent& event) +{ + onGridLabelLeftClick(false, static_cast(event.colType_)); +} + + +void MainDialog::onGridLabelLeftClickC(GridClickEvent& event) +{ + //sorting middle grid is more or less useless: therefore let's toggle view instead! + setViewTypeSyncAction(!m_bpButtonViewTypeSyncAction->isActive()); //toggle view +} + + +void MainDialog::OnSwapSides(wxCommandEvent& event) +{ + //swap directory names: first pair + firstFolderPair->setValues(firstFolderPair->getRightDir(), // swap directories + firstFolderPair->getLeftDir(), // + firstFolderPair->getAltCompConfig(), + firstFolderPair->getAltSyncConfig(), + firstFolderPair->getAltFilterConfig()); + + //additional pairs + for (FolderPairPanel* panel : additionalFolderPairs) + panel->setValues(panel->getRightDir(), // swap directories + panel->getLeftDir(), // + panel->getAltCompConfig(), + panel->getAltSyncConfig(), + panel->getAltFilterConfig()); + + //swap view filter + bool tmp = m_bpButtonShowLeftOnly->isActive(); + m_bpButtonShowLeftOnly->setActive(m_bpButtonShowRightOnly->isActive()); + m_bpButtonShowRightOnly->setActive(tmp); + + tmp = m_bpButtonShowLeftNewer->isActive(); + m_bpButtonShowLeftNewer->setActive(m_bpButtonShowRightNewer->isActive()); + m_bpButtonShowRightNewer->setActive(tmp); + + /* for sync preview and "mirror" variant swapping may create strange effect: + tmp = m_bpButtonShowCreateLeft->isActive(); + m_bpButtonShowCreateLeft->setActive(m_bpButtonShowCreateRight->isActive()); + m_bpButtonShowCreateRight->setActive(tmp); + + tmp = m_bpButtonShowDeleteLeft->isActive(); + m_bpButtonShowDeleteLeft->setActive(m_bpButtonShowDeleteRight->isActive()); + m_bpButtonShowDeleteRight->setActive(tmp); + + tmp = m_bpButtonShowUpdateLeft->isActive(); + m_bpButtonShowUpdateLeft->setActive(m_bpButtonShowUpdateRight->isActive()); + m_bpButtonShowUpdateRight->setActive(tmp); + */ + + //swap grid information + zen::swapGrids(getConfig().mainCfg, folderCmp); + + updateGui(); +} + + +void MainDialog::updateGridViewData() +{ + size_t filesOnLeftView = 0; + size_t foldersOnLeftView = 0; + size_t filesOnRightView = 0; + size_t foldersOnRightView = 0; + zen::UInt64 filesizeLeftView; + zen::UInt64 filesizeRightView; + + //disable all buttons per default + m_bpButtonShowLeftOnly ->Show(false); + m_bpButtonShowRightOnly ->Show(false); + m_bpButtonShowLeftNewer ->Show(false); + m_bpButtonShowRightNewer->Show(false); + m_bpButtonShowDifferent ->Show(false); + m_bpButtonShowEqual ->Show(false); + m_bpButtonShowConflict ->Show(false); + + m_bpButtonShowCreateLeft ->Show(false); + m_bpButtonShowCreateRight->Show(false); + m_bpButtonShowDeleteLeft ->Show(false); + m_bpButtonShowDeleteRight->Show(false); + m_bpButtonShowUpdateLeft ->Show(false); + m_bpButtonShowUpdateRight->Show(false); + m_bpButtonShowDoNothing ->Show(false); + + if (m_bpButtonViewTypeSyncAction->isActive()) + { + const GridView::StatusSyncPreview result = gridDataView->updateSyncPreview(currentCfg.hideExcludedItems, + m_bpButtonShowCreateLeft ->isActive(), + m_bpButtonShowCreateRight->isActive(), + m_bpButtonShowDeleteLeft ->isActive(), + m_bpButtonShowDeleteRight->isActive(), + m_bpButtonShowUpdateLeft ->isActive(), + m_bpButtonShowUpdateRight->isActive(), + m_bpButtonShowDoNothing ->isActive(), + m_bpButtonShowEqual ->isActive(), + m_bpButtonShowConflict ->isActive()); + filesOnLeftView = result.filesOnLeftView; + foldersOnLeftView = result.foldersOnLeftView; + filesOnRightView = result.filesOnRightView; + foldersOnRightView = result.foldersOnRightView; + filesizeLeftView = result.filesizeLeftView; + filesizeRightView = result.filesizeRightView; + + + //sync preview buttons + m_bpButtonShowCreateLeft ->Show(result.existsSyncCreateLeft); + m_bpButtonShowCreateRight ->Show(result.existsSyncCreateRight); + m_bpButtonShowDeleteLeft ->Show(result.existsSyncDeleteLeft); + m_bpButtonShowDeleteRight ->Show(result.existsSyncDeleteRight); + m_bpButtonShowUpdateLeft ->Show(result.existsSyncDirLeft); + m_bpButtonShowUpdateRight ->Show(result.existsSyncDirRight); + m_bpButtonShowDoNothing ->Show(result.existsSyncDirNone); + m_bpButtonShowEqual ->Show(result.existsSyncEqual); + m_bpButtonShowConflict ->Show(result.existsConflict); + + const bool anyViewFilterButtonShown = m_bpButtonShowCreateLeft ->IsShown() || + m_bpButtonShowCreateRight->IsShown() || + m_bpButtonShowDeleteLeft ->IsShown() || + m_bpButtonShowDeleteRight->IsShown() || + m_bpButtonShowUpdateLeft ->IsShown() || + m_bpButtonShowUpdateRight->IsShown() || + m_bpButtonShowDoNothing ->IsShown() || + m_bpButtonShowEqual ->IsShown() || + m_bpButtonShowConflict ->IsShown(); + m_bpButtonViewTypeSyncAction->Show(anyViewFilterButtonShown); + + if (anyViewFilterButtonShown) + { + m_panelViewFilter->Show(); + m_panelViewFilter->Layout(); + } + else + m_panelViewFilter->Hide(); + } + else + { + const GridView::StatusCmpResult result = gridDataView->updateCmpResult(currentCfg.hideExcludedItems, + m_bpButtonShowLeftOnly ->isActive(), + m_bpButtonShowRightOnly ->isActive(), + m_bpButtonShowLeftNewer ->isActive(), + m_bpButtonShowRightNewer->isActive(), + m_bpButtonShowDifferent ->isActive(), + m_bpButtonShowEqual ->isActive(), + m_bpButtonShowConflict ->isActive()); + filesOnLeftView = result.filesOnLeftView; + foldersOnLeftView = result.foldersOnLeftView; + filesOnRightView = result.filesOnRightView; + foldersOnRightView = result.foldersOnRightView; + filesizeLeftView = result.filesizeLeftView; + filesizeRightView = result.filesizeRightView; + + //comparison result view buttons + m_bpButtonShowLeftOnly ->Show(result.existsLeftOnly); + m_bpButtonShowRightOnly ->Show(result.existsRightOnly); + m_bpButtonShowLeftNewer ->Show(result.existsLeftNewer); + m_bpButtonShowRightNewer->Show(result.existsRightNewer); + m_bpButtonShowDifferent ->Show(result.existsDifferent); + m_bpButtonShowEqual ->Show(result.existsEqual); + m_bpButtonShowConflict ->Show(result.existsConflict); + + const bool anyViewFilterButtonShown = m_bpButtonShowLeftOnly ->IsShown() || + m_bpButtonShowRightOnly ->IsShown() || + m_bpButtonShowLeftNewer ->IsShown() || + m_bpButtonShowRightNewer->IsShown() || + m_bpButtonShowDifferent ->IsShown() || + m_bpButtonShowEqual ->IsShown() || + m_bpButtonShowConflict ->IsShown(); + m_bpButtonViewTypeSyncAction->Show(anyViewFilterButtonShown); + + if (anyViewFilterButtonShown) + { + m_panelViewFilter->Show(); + m_panelViewFilter->Layout(); + } + else + m_panelViewFilter->Hide(); + } + //all three grids retrieve their data directly via gridDataView + gridview::refresh(*m_gridMainL, *m_gridMainC, *m_gridMainR); + + //navigation tree + if (m_bpButtonViewTypeSyncAction->isActive()) + treeDataView->updateSyncPreview(currentCfg.hideExcludedItems, + m_bpButtonShowCreateLeft ->isActive(), + m_bpButtonShowCreateRight->isActive(), + m_bpButtonShowDeleteLeft ->isActive(), + m_bpButtonShowDeleteRight->isActive(), + m_bpButtonShowUpdateLeft ->isActive(), + m_bpButtonShowUpdateRight->isActive(), + m_bpButtonShowDoNothing ->isActive(), + m_bpButtonShowEqual ->isActive(), + m_bpButtonShowConflict ->isActive()); + else + treeDataView->updateCmpResult(currentCfg.hideExcludedItems, + m_bpButtonShowLeftOnly ->isActive(), + m_bpButtonShowRightOnly ->isActive(), + m_bpButtonShowLeftNewer ->isActive(), + m_bpButtonShowRightNewer->isActive(), + m_bpButtonShowDifferent ->isActive(), + m_bpButtonShowEqual ->isActive(), + m_bpButtonShowConflict ->isActive()); + m_gridNavi->Refresh(); + + //update status bar information + setStatusBarFileStatistics(filesOnLeftView, + foldersOnLeftView, + filesOnRightView, + foldersOnRightView, + filesizeLeftView, + filesizeRightView); +} + + +void MainDialog::applyFilterConfig() +{ + applyFiltering(folderCmp, getConfig().mainCfg); + updateGui(); + //updateGuiDelayedIf(currentCfg.hideExcludedItems); //show update GUI before removing rows +} + + +void MainDialog::applySyncConfig() +{ + zen::redetermineSyncDirection(getConfig().mainCfg, folderCmp, + [&](const std::wstring& warning) + { + bool& warningActive = globalCfg.optDialogs.warningDatabaseError; + if (warningActive) + { + bool dontWarnAgain = false; + + showNotificationDialog(this, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(warning).setCheckBox(dontWarnAgain, _("&Don't show this warning again"))); + warningActive = !dontWarnAgain; + } + }); + + updateGui(); +} + + +void MainDialog::OnMenuFindItem(wxCommandEvent& event) //CTRL + F +{ + showFindPanel(); +} + + +void MainDialog::OnSearchGridEnter(wxCommandEvent& event) +{ + startFindNext(); +} + + +void MainDialog::OnHideSearchPanel(wxCommandEvent& event) +{ + hideFindPanel(); +} + + +void MainDialog::OnSearchPanelKeyPressed(wxKeyEvent& event) +{ + switch (event.GetKeyCode()) + { + case WXK_RETURN: + case WXK_NUMPAD_ENTER: //catches ENTER keys while focus is on *any* part of m_panelSearch! Seems to obsolete OnHideSearchPanel()! + startFindNext(); + return; + case WXK_ESCAPE: + hideFindPanel(); + return; + } + event.Skip(); +} + + +void MainDialog::showFindPanel() //CTRL + F or F3 with empty search phrase +{ + auiMgr.GetPane(m_panelSearch).Show(); + auiMgr.Update(); + + 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) + m_textCtrlSearchTxt->SetFocus(); +} + + +void MainDialog::hideFindPanel() +{ + auiMgr.GetPane(m_panelSearch).Hide(); + auiMgr.Update(); + + if (focusWindowAfterSearch) + { + focusWindowAfterSearch->SetFocus(); + focusWindowAfterSearch = nullptr; + } +} + + +void MainDialog::startFindNext() //F3 or ENTER in m_textCtrlSearchTxt +{ + const wxString& searchString = m_textCtrlSearchTxt->GetValue(); + + if (searchString.empty()) + showFindPanel(); + else + { + Grid* grid1 = m_gridMainL; + Grid* grid2 = m_gridMainR; + + wxWindow* focus = wxWindow::FindFocus(); + if ((isComponentOf(focus, m_panelSearch) ? focusWindowAfterSearch : focus) == &m_gridMainR->getMainWin()) + std::swap(grid1, grid2); //select side to start search at grid cursor position + + wxBeginBusyCursor(wxHOURGLASS_CURSOR); + const std::pair result = findGridMatch(*grid1, *grid2, searchString, + m_checkBoxMatchCase->GetValue()); //parameter owned by GUI, *not* globalCfg structure! => we should better implement a getGlocalCfg()! + wxEndBusyCursor(); + + if (Grid* grid = const_cast(result.first)) //grid wasn't const when passing to findAndSelectNext(), so this is safe + { + assert(result.second >= 0); + + gridview::setScrollMaster(*grid); + grid->setGridCursor(result.second); + + focusWindowAfterSearch = &grid->getMainWin(); + + if (!isComponentOf(wxWindow::FindFocus(), m_panelSearch)) + grid->getMainWin().SetFocus(); + } + else + { + showFindPanel(); + showNotificationDialog(this, DialogInfoType::INFO, PopupDialogCfg(). + setTitle(_("Find")). + setMainInstructions(replaceCpy(_("Cannot find %x"), L"%x", L"\"" + searchString + L"\"", false))); + } + } +} + + +void MainDialog::OnAddFolderPair(wxCommandEvent& event) +{ +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! +#endif + + std::vector newPairs; + newPairs.push_back(getConfig().mainCfg.firstPair); + + //clear first pair + const FolderPairEnh cfgEmpty; + firstFolderPair->setValues(cfgEmpty.dirnamePhraseLeft, + cfgEmpty.dirnamePhraseRight, + cfgEmpty.altCmpConfig, + cfgEmpty.altSyncConfig, + cfgEmpty.localFilter); + + //keep sequence to update GUI as last step + addAddFolderPair(newPairs, true); //add pair in front of additonal pairs +} + + +void MainDialog::OnRemoveTopFolderPair(wxCommandEvent& event) +{ + if (!additionalFolderPairs.empty()) + { +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! +#endif + + //get settings from second folder pair + const FolderPairEnh cfgSecond = getEnhancedPair(additionalFolderPairs[0]); + + //reset first pair + firstFolderPair->setValues(cfgSecond.dirnamePhraseLeft, + cfgSecond.dirnamePhraseRight, + cfgSecond.altCmpConfig, + cfgSecond.altSyncConfig, + cfgSecond.localFilter); + + removeAddFolderPair(0); //remove second folder pair (first of additional folder pairs) + } +} + + +void MainDialog::OnRemoveFolderPair(wxCommandEvent& event) +{ +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! +#endif + + const wxObject* const eventObj = event.GetEventObject(); //find folder pair originating the event + for (auto it = additionalFolderPairs.begin(); it != additionalFolderPairs.end(); ++it) + if (eventObj == (*it)->m_bpButtonRemovePair) + { + removeAddFolderPair(it - additionalFolderPairs.begin()); + break; + } +} + + +void MainDialog::updateGuiForFolderPair() +{ +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! +#endif + + //adapt delete top folder pair button + m_bpButtonRemovePair->Show(!additionalFolderPairs.empty()); + m_panelTopLeft->Layout(); + + //adapt local filter and sync cfg for first folder pair + const bool showLocalCfgFirstPair = !additionalFolderPairs.empty() || + firstFolderPair->getAltCompConfig().get() != nullptr || + firstFolderPair->getAltSyncConfig().get() != nullptr || + !isNullFilter(firstFolderPair->getAltFilterConfig()); + + m_bpButtonAltCompCfg ->Show(showLocalCfgFirstPair); + m_bpButtonAltSyncCfg ->Show(showLocalCfgFirstPair); + m_bpButtonLocalFilter->Show(showLocalCfgFirstPair); + setImage(*m_bpButtonSwapSides, getResourceImage(showLocalCfgFirstPair ? L"swap_slim" : L"swap")); + + //update sub-panel sizes for calculations below!!! + m_panelTopMiddle->GetSizer()->SetSizeHints(m_panelTopMiddle); //~=Fit() + SetMinSize() + + int addPairMinimalHeight = 0; + int addPairOptimalHeight = 0; + if (!additionalFolderPairs.empty()) + { + const int pairHeight = additionalFolderPairs[0]->GetSize().GetHeight(); + addPairMinimalHeight = std::min(1.5, additionalFolderPairs.size()) * pairHeight; //have 1.5 * height indicate that more folders are there + addPairOptimalHeight = std::min(globalCfg.gui.maxFolderPairsVisible - 1 + 0.5, //subtract first/main folder pair and add 0.5 to indicate additional folders + additionalFolderPairs.size()) * pairHeight; + + addPairOptimalHeight = std::max(addPairOptimalHeight, addPairMinimalHeight); //implicitly handle corrupted values for "maxFolderPairsVisible" + } + + const int firstPairHeight = std::max(m_panelDirectoryPairs->ClientToWindowSize(m_panelTopLeft ->GetSize()).GetHeight(), //include m_panelDirectoryPairs window borders! + m_panelDirectoryPairs->ClientToWindowSize(m_panelTopMiddle->GetSize()).GetHeight()); // + + //######################################################################################################################## + //wxAUI hack: set minimum height to desired value, then call wxAuiPaneInfo::Fixed() to apply it + auiMgr.GetPane(m_panelDirectoryPairs).MinSize(-1, firstPairHeight + addPairOptimalHeight); + auiMgr.GetPane(m_panelDirectoryPairs).Fixed(); + auiMgr.Update(); + + //now make resizable again + auiMgr.GetPane(m_panelDirectoryPairs).Resizable(); + auiMgr.Update(); + //######################################################################################################################## + + //make sure user cannot fully shrink additional folder pairs + auiMgr.GetPane(m_panelDirectoryPairs).MinSize(-1, firstPairHeight + addPairMinimalHeight); + auiMgr.Update(); + + //it seems there is no GetSizer()->SetSizeHints(this)/Fit() required due to wxAui "magic" + //=> *massive* perf improvement on OS X! +} + + +void MainDialog::addAddFolderPair(const std::vector& newPairs, bool addFront) +{ +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(m_panelDirectoryPairs); //leads to GUI corruption problems on Linux/OS X! +#endif + + std::vector newEntries; + + std::for_each(newPairs.begin(), newPairs.end(), + [&](const FolderPairEnh& enhPair) + { + //add new folder pair + FolderPairPanel* newPair = new FolderPairPanel(m_scrolledWindowFolderPairs, *this); + + //init dropdown history + newPair->m_directoryLeft ->init(folderHistoryLeft); + newPair->m_directoryRight->init(folderHistoryRight); + + //set width of left folder panel + const int width = m_panelTopLeft->GetSize().GetWidth(); + newPair->m_panelLeft->SetMinSize(wxSize(width, -1)); + + if (addFront) + { + bSizerAddFolderPairs->Insert(0, newPair, 0, wxEXPAND); + additionalFolderPairs.insert(additionalFolderPairs.begin(), newPair); + } + else + { + bSizerAddFolderPairs->Add(newPair, 0, wxEXPAND); + additionalFolderPairs.push_back(newPair); + } + newEntries.push_back(newPair); + + //register events + newPair->m_bpButtonRemovePair->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MainDialog::OnRemoveFolderPair), nullptr, this); + }); + + updateGuiForFolderPair(); + + //wxComboBox screws up miserably if width/height is smaller than the magic number 4! Problem occurs when trying to set tooltip + //so we have to update window sizes before setting configuration: + for (auto it = newPairs.begin(); it != newPairs.end(); ++it)//set alternate configuration + newEntries[it - newPairs.begin()]->setValues(it->dirnamePhraseLeft, + it->dirnamePhraseRight, + it->altCmpConfig, + it->altSyncConfig, + it->localFilter); + clearGrid(); //+ GUI update +} + + +void MainDialog::removeAddFolderPair(size_t pos) +{ +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(m_panelDirectoryPairs); //leads to GUI corruption problems on Linux/OS X! +#endif + + if (pos < additionalFolderPairs.size()) + { + FolderPairPanel* panel = additionalFolderPairs[pos]; + + bSizerAddFolderPairs->Detach(panel); //Remove() does not work on Window*, so do it manually + additionalFolderPairs.erase(additionalFolderPairs.begin() + pos); + //more (non-portable) wxWidgets bullshit: on OS X wxWindow::Destroy() screws up and calls "operator delete" directly rather than + //the deferred deletion it is expected to do (and which is implemented correctly on Windows and Linux) + //http://bb10.com/python-wxpython-devel/2012-09/msg00004.html + //=> since we're in a mouse button callback of a sub-component of "panel" we need to delay deletion ourselves: + processAsync2([] {}, [panel] { panel->Destroy(); }); + } + + updateGuiForFolderPair(); + + clearGrid(); //+ GUI update +} + + +void MainDialog::setAddFolderPairs(const std::vector& newPairs) +{ +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(m_panelDirectoryPairs); //leads to GUI corruption problems on Linux/OS X! +#endif + + additionalFolderPairs.clear(); + bSizerAddFolderPairs->Clear(true); + + //updateGuiForFolderPair(); -> already called in addAddFolderPair() + + addAddFolderPair(newPairs); +} + + +//######################################################################################################## + + +//menu events +void MainDialog::OnMenuGlobalSettings(wxCommandEvent& event) +{ + zen::showGlobalSettingsDlg(this, globalCfg); +} + + +void MainDialog::OnMenuExportFileList(wxCommandEvent& event) +{ + //get a filename + wxFileDialog filePicker(this, //creating this on freestore leads to memleak! + wxEmptyString, + wxEmptyString, + L"FileList.csv", //default file name + _("Comma-separated values") + L" (*.csv)|*.csv" + L"|" +_("All files") + L" (*.*)|*", + wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + if (filePicker.ShowModal() != wxID_OK) + return; + + wxBusyCursor dummy; + + const Zstring filename = utfCvrtTo(filePicker.GetPath()); + + //http://en.wikipedia.org/wiki/Comma-separated_values + const lconv* localInfo = ::localeconv(); //always bound according to doc + const bool haveCommaAsDecimalSep = std::string(localInfo->decimal_point) == ","; + + const char CSV_SEP = haveCommaAsDecimalSep ? ';' : ','; + + auto fmtValue = [&](const wxString& val) -> Utf8String + { + Utf8String&& tmp = utfCvrtTo(val); + + if (contains(tmp, CSV_SEP)) + return '\"' + tmp + '\"'; + else + return tmp; + }; + + Utf8String header; //perf: wxString doesn't model exponential growth and so is out, std::string doesn't give performance guarantee! + header += BYTE_ORDER_MARK_UTF8; + + //base folders + header += fmtValue(_("Folder Pairs")) + '\n' ; + std::for_each(begin(folderCmp), end(folderCmp), + [&](BaseDirPair& baseDirObj) + { + header += utfCvrtTo(baseDirObj.getBaseDirPf()) + CSV_SEP; + header += utfCvrtTo(baseDirObj.getBaseDirPf()) + '\n'; + }); + header += '\n'; + + //write header + auto provLeft = m_gridMainL->getDataProvider(); + auto provMiddle = m_gridMainC->getDataProvider(); + auto provRight = m_gridMainR->getDataProvider(); + + auto colAttrLeft = m_gridMainL->getColumnConfig(); + auto colAttrMiddle = m_gridMainC->getColumnConfig(); + auto colAttrRight = m_gridMainR->getColumnConfig(); + + vector_remove_if(colAttrLeft , [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); + vector_remove_if(colAttrMiddle, [](const Grid::ColumnAttribute& ca) { return !ca.visible_ || static_cast(ca.type_) == COL_TYPE_CHECKBOX; }); + vector_remove_if(colAttrRight , [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); + + if (provLeft && provMiddle && provRight) + { + std::for_each(colAttrLeft.begin(), colAttrLeft.end(), + [&](const Grid::ColumnAttribute& ca) + { + header += fmtValue(provLeft->getColumnLabel(ca.type_)); + header += CSV_SEP; + }); + std::for_each(colAttrMiddle.begin(), colAttrMiddle.end(), + [&](const Grid::ColumnAttribute& ca) + { + header += fmtValue(provMiddle->getColumnLabel(ca.type_)); + header += CSV_SEP; + }); + if (!colAttrRight.empty()) + { + std::for_each(colAttrRight.begin(), colAttrRight.end() - 1, + [&](const Grid::ColumnAttribute& ca) + { + header += fmtValue(provRight->getColumnLabel(ca.type_)); + header += CSV_SEP; + }); + header += fmtValue(provRight->getColumnLabel(colAttrRight.back().type_)); + } + header += '\n'; + + try + { + //write file + FileOutput fileOut(filename, zen::FileOutput::ACC_OVERWRITE); //throw FileError + + replace(header, '\n', LINE_BREAK); + fileOut.write(&*header.begin(), header.size()); //throw FileError + + //main grid: write rows one after the other instead of creating one big string: memory allocation might fail; think 1 million rows! + /* + performance test case "export 600.000 rows" to CSV: + aproach 1. assemble single temporary string, then write file: 4.6s + aproach 2. write to buffered file output directly for each row: 6.4s + */ + const size_t rowCount = m_gridMainL->getRowCount(); + for (size_t row = 0; row < rowCount; ++row) + { + Utf8String tmp; + + std::for_each(colAttrLeft.begin(), colAttrLeft.end(), + [&](const Grid::ColumnAttribute& ca) + { + tmp += fmtValue(provLeft->getValue(row, ca.type_)); + tmp += CSV_SEP; + }); + std::for_each(colAttrMiddle.begin(), colAttrMiddle.end(), + [&](const Grid::ColumnAttribute& ca) + { + tmp += fmtValue(provMiddle->getValue(row, ca.type_)); + tmp += CSV_SEP; + }); + std::for_each(colAttrRight.begin(), colAttrRight.end(), + [&](const Grid::ColumnAttribute& ca) + { + tmp += fmtValue(provRight->getValue(row, ca.type_)); + tmp += CSV_SEP; + }); + tmp += '\n'; + + replace(tmp, '\n', LINE_BREAK); + fileOut.write(&*tmp.begin(), tmp.size()); //throw FileError + } + + flashStatusInformation(_("File list exported")); + } + catch (const FileError& e) + { + showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); + } + } +} + + +void MainDialog::OnMenuCheckVersion(wxCommandEvent& event) +{ + zen::checkForUpdateNow(this); +} + + +void MainDialog::OnMenuCheckVersionAutomatically(wxCommandEvent& event) +{ + globalCfg.gui.lastUpdateCheck = globalCfg.gui.lastUpdateCheck == -1 ? 0 : -1; + m_menuItemCheckVersionAuto->Check(globalCfg.gui.lastUpdateCheck != -1); + zen::checkForUpdatePeriodically(this, globalCfg.gui.lastUpdateCheck, [&] { flashStatusInformation(_("Searching for program updates...")); }); +} + + +void MainDialog::OnRegularUpdateCheck(wxIdleEvent& event) +{ + //execute just once per startup! + Disconnect(wxEVT_IDLE, wxIdleEventHandler(MainDialog::OnRegularUpdateCheck), nullptr, this); + + if (manualProgramUpdateRequired()) + zen::checkForUpdatePeriodically(this, globalCfg.gui.lastUpdateCheck, [&] { flashStatusInformation(_("Searching for program updates...")); }); +} + + +void MainDialog::OnLayoutWindowAsync(wxIdleEvent& event) +{ + //execute just once per startup! + Disconnect(wxEVT_IDLE, wxIdleEventHandler(MainDialog::OnLayoutWindowAsync), nullptr, this); + +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! +#endif + + //adjust folder pair distortion on startup + std::for_each(additionalFolderPairs.begin(), additionalFolderPairs.end(), + [](FolderPairPanel* panel) { panel->Layout(); }); + + m_panelTopButtons->Layout(); + Layout(); //strangely this layout call works if called in next idle event only + auiMgr.Update(); //fix view filter distortion +} + + +void MainDialog::OnMenuAbout(wxCommandEvent& event) +{ + zen::showAboutDialog(this); +} + + +void MainDialog::OnShowHelp(wxCommandEvent& event) +{ + zen::displayHelpEntry(this); +} + +//######################################################################################################### + +//language selection +void MainDialog::switchProgramLanguage(int langID) +{ + //create new dialog with respect to new language + xmlAccess::XmlGlobalSettings newGlobalCfg = getGlobalCfgBeforeExit(); + newGlobalCfg.programLanguage = langID; + + //show new dialog, then delete old one + MainDialog::create(getConfig(), activeConfigFiles, &newGlobalCfg, false); + + //we don't use Close(): + //1. we don't want to show the prompt to save current config in OnClose() + //2. after getGlobalCfgBeforeExit() the old main dialog is invalid so we want to force deletion + Destroy(); +} + + +void MainDialog::OnMenuLanguageSwitch(wxCommandEvent& event) +{ + std::map::const_iterator it = languageMenuItemMap.find(event.GetId()); + if (it != languageMenuItemMap.end()) + switchProgramLanguage(it->second); +} + +//######################################################################################################### + +void MainDialog::setViewTypeSyncAction(bool value) +{ + //if (m_bpButtonViewTypeSyncAction->isActive() == value) return; support polling -> what about initialization? + + m_bpButtonViewTypeSyncAction->setActive(value); + m_bpButtonViewTypeSyncAction->SetToolTip((value ? _("Action") : _("Category")) + L" (F9)"); + + //toggle display of sync preview in middle grid + gridview::highlightSyncAction(*m_gridMainC, value); + + updateGui(); +} diff --git a/FreeFileSync/Source/ui/main_dlg.h b/FreeFileSync/Source/ui/main_dlg.h new file mode 100644 index 00000000..110f42b6 --- /dev/null +++ b/FreeFileSync/Source/ui/main_dlg.h @@ -0,0 +1,314 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef MAINDIALOG_H +#define MAINDIALOG_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "gui_generated.h" +#include "custom_grid.h" +#include "tree_view.h" +#include "folder_history_box.h" +#include "../lib/process_xml.h" + +class FolderPairFirst; +class FolderPairPanel; +class CompareProgressDialog; + + +class MainDialog : public MainDialogGenerated +{ +public: + //default behavior, application start, restores last used config + static void create(); + + //when loading dynamically assembled config, + //when switching language, + //or switching from batch run to GUI on warnings + static void create(const xmlAccess::XmlGuiConfig& guiCfg, + const std::vector& referenceFiles, + const xmlAccess::XmlGlobalSettings* globalSettings, //optional: take over ownership => save on exit + bool startComparison); + + void disableAllElements(bool enableAbort); //dis-/enables all elements (except abort button) that might receive user input + void enableAllElements(); //during long-running processes: comparison, deletion + + void onQueryEndSession(); //last chance to do something useful before killing the application! + +private: + MainDialog(const xmlAccess::XmlGuiConfig& guiCfg, + const std::vector& referenceFiles, + const xmlAccess::XmlGlobalSettings& globalSettings, //take over ownership => save on exit + bool startComparison); + ~MainDialog(); + + friend class CompareStatusHandler; + friend class SyncStatusHandler; + friend class ManualDeletionHandler; + friend class FolderPairFirst; + friend class FolderPairPanel; + friend class DirectoryNameMainImpl; + template + friend class FolderPairCallback; + friend class PanelMoveWindow; + + //configuration load/save + void setLastUsedConfig(const Zstring& filename, const xmlAccess::XmlGuiConfig& guiConfig); + void setLastUsedConfig(const std::vector& filenames, const xmlAccess::XmlGuiConfig& guiConfig); + + xmlAccess::XmlGuiConfig getConfig() const; + void setConfig(const xmlAccess::XmlGuiConfig& newGuiCfg, const std::vector& referenceFiles); + + void setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSettings); //messes with Maximize(), window sizes, so call just once! + xmlAccess::XmlGlobalSettings getGlobalCfgBeforeExit(); //destructive "get" thanks to "Iconize(false), Maximize(false)" + + bool loadConfiguration(const std::vector& filenames); //return true if loaded successfully + + bool trySaveConfig (const Zstring* fileNameGui); //return true if saved successfully + bool trySaveBatchConfig(const Zstring* fileNameBatch); // + bool saveOldConfig(); //return false on user abort + + static const Zstring& lastRunConfigName(); + + xmlAccess::XmlGuiConfig lastConfigurationSaved; //support for: "Save changed configuration?" dialog + //used when saving configuration + std::vector activeConfigFiles; //name of currently loaded config file (may be more than 1) + + void updateGlobalFilterButton(); + + void initViewFilterButtons(); + void setViewFilterDefault(); + + void addFileToCfgHistory(const std::vector& filenames); //= update/insert + apply selection + void removeObsoleteCfgHistoryItems(const std::vector& filenames); + void removeCfgHistoryItems(const std::vector& filenames); + + void addAddFolderPair(const std::vector& newPairs, bool addFront = false); + void removeAddFolderPair(size_t pos); + void setAddFolderPairs(const std::vector& newPairs); + + void updateGuiForFolderPair(); //helper method: add usability by showing/hiding buttons related to folder pairs + + //main method for putting gridDataView on UI: updates data respecting current view settings + void updateGui(); //kitchen-sink update + void updateGuiDelayedIf(bool condition); // 400 ms delay + + void updateGridViewData(); // + void updateStatistics(); // more fine-grained updaters + void updateUnsavedCfgStatus(); // + void updateTopButtonImages(); // + + //context menu functions + std::vector getGridSelection(bool fromLeft = true, bool fromRight = true) const; + std::vector getTreeSelection() const; + + void setSyncDirManually(const std::vector& selection, zen::SyncDirection direction); + void setFilterManually(const std::vector& selection, bool setIncluded); + void copySelectionToClipboard(const std::vector& gridRefs); + void deleteSelectedFiles(const std::vector& selectionLeft, + const std::vector& selectionRight); + + void openExternalApplication(const wxString& commandline, const std::vector& selection, bool leftSide); //selection may be empty + + //don't use wxWidgets idle handling => repeated idle requests/consumption hogs 100% cpu! + void onProcessAsyncTasks(wxEvent& event); + + template + void processAsync(Fun doAsync, Fun2 evalOnGui) { asyncTasks.add(doAsync, evalOnGui); timerForAsyncTasks.Start(50); /*timer interval in [ms] */ } + template + void processAsync2(Fun doAsync, Fun2 evalOnGui) { asyncTasks.add2(doAsync, evalOnGui); timerForAsyncTasks.Start(50); /*timer interval in [ms] */ } + + //status bar supports one of the following two states at a time: + void setStatusBarFullText(const wxString& msg); + void setStatusBarFileStatistics(size_t filesOnLeftView, size_t foldersOnLeftView, size_t filesOnRightView, size_t foldersOnRightView, zen::UInt64 filesizeLeftView, zen::UInt64 filesizeRightView); + + void flashStatusInformation(const wxString& msg); //temporarily show different status (only valid for setStatusBarFullText) + void restoreStatusInformation(); //called automatically after a few seconds + + //events + void onGridButtonEventL(wxKeyEvent& event); + void onGridButtonEventC(wxKeyEvent& event); + void onGridButtonEventR(wxKeyEvent& event); + void onGridButtonEvent (wxKeyEvent& event, zen::Grid& grid, bool leftSide); + + void onTreeButtonEvent (wxKeyEvent& event); + void OnContextSetLayout(wxMouseEvent& event); + void OnGlobalKeyEvent (wxKeyEvent& event); + + virtual void OnCompSettingsContext(wxMouseEvent& event) override; + virtual void OnSyncSettingsContext(wxMouseEvent& event) override; + virtual void OnGlobalFilterContext(wxMouseEvent& event) override; + virtual void OnViewButtonRightClick(wxMouseEvent& event) override; + + void applyCompareConfig(bool switchMiddleGrid = false); + + //context menu handler methods + void onMainGridContextL(zen::GridClickEvent& event); + void onMainGridContextC(zen::GridClickEvent& event); + void onMainGridContextR(zen::GridClickEvent& event); + void onMainGridContextRim(bool leftSide); + + void onNaviGridContext(zen::GridClickEvent& event); + + void onNaviSelection(zen::GridRangeSelectEvent& event); + + void onNaviPanelFilesDropped(zen::FileDropEvent& event); + + void onDirSelected(wxCommandEvent& event); + void onDirManualCorrection(wxCommandEvent& event); + + void onCheckRows (zen::CheckRowsEvent& event); + void onSetSyncDirection(zen::SyncDirectionEvent& event); + + void onGridDoubleClickL(zen::GridClickEvent& event); + void onGridDoubleClickR(zen::GridClickEvent& event); + void onGridDoubleClickRim(size_t row, bool leftSide); + + void onGridLabelLeftClickC(zen::GridClickEvent& event); + void onGridLabelLeftClickL(zen::GridClickEvent& event); + void onGridLabelLeftClickR(zen::GridClickEvent& event); + void onGridLabelLeftClick(bool onLeft, zen::ColumnTypeRim type); + + void onGridLabelContextL(zen::GridClickEvent& event); + void onGridLabelContextC(zen::GridClickEvent& event); + void onGridLabelContextR(zen::GridClickEvent& event); + void onGridLabelContext(zen::Grid& grid, zen::ColumnTypeRim type, const std::vector& defaultColumnAttributes); + + virtual void OnToggleViewType (wxCommandEvent& event) override; + virtual void OnToggleViewButton(wxCommandEvent& event) override; + + virtual void OnConfigNew (wxCommandEvent& event) override; + virtual void OnConfigSave (wxCommandEvent& event) override; + virtual void OnConfigSaveAs (wxCommandEvent& event) override; + virtual void OnSaveAsBatchJob (wxCommandEvent& event) override; + virtual void OnConfigLoad (wxCommandEvent& event) override; + virtual void OnLoadFromHistory(wxCommandEvent& event) override; + virtual void OnLoadFromHistoryDoubleClick(wxCommandEvent& event); + + void deleteSelectedCfgHistoryItems(); + + virtual void OnCfgHistoryRightClick(wxMouseEvent& event) override; + void OnCfgHistoryKeyEvent (wxKeyEvent& event) override; + void OnRegularUpdateCheck (wxIdleEvent& event); + void OnLayoutWindowAsync (wxIdleEvent& event); + + void OnResizeLeftFolderWidth(wxEvent& event); + void OnResizeConfigPanel (wxEvent& event); + void OnResizeViewPanel (wxEvent& event); + void OnResizeStatisticsPanel(wxEvent& event); + virtual void OnShowExcluded (wxCommandEvent& event) override; + virtual void OnConfigureFilter (wxCommandEvent& event) override; + virtual void OnSwapSides (wxCommandEvent& event) override; + virtual void OnCompare (wxCommandEvent& event) override; + virtual void OnSyncSettings (wxCommandEvent& event) override; + virtual void OnCmpSettings (wxCommandEvent& event) override; + virtual void OnStartSync (wxCommandEvent& event) override; + virtual void OnClose (wxCloseEvent& event) override; + + void filterExtension(const Zstring& extension, bool include); + void filterShortname(const zen::FileSystemObject& fsObj, bool include); + void filterItems(const std::vector& selection, bool include); + void filterPhrase(const Zstring& phrase, bool include, bool addNewLine); + + virtual void OnAddFolderPair (wxCommandEvent& event) override; + void OnRemoveFolderPair (wxCommandEvent& event); + virtual void OnRemoveTopFolderPair(wxCommandEvent& event) override; + + void applyFilterConfig(); + void applySyncConfig(); + + void showFindPanel(); //CTRL + F + void hideFindPanel(); + void startFindNext(); //F3 + + virtual void OnSearchGridEnter(wxCommandEvent& event) override; + virtual void OnHideSearchPanel(wxCommandEvent& event) override; + void OnSearchPanelKeyPressed(wxKeyEvent& event); + + //menu events + virtual void OnMenuGlobalSettings(wxCommandEvent& event) override; + virtual void OnMenuExportFileList(wxCommandEvent& event) override; + virtual void OnMenuFindItem (wxCommandEvent& event) override; + virtual void OnMenuCheckVersion (wxCommandEvent& event) override; + virtual void OnMenuCheckVersionAutomatically(wxCommandEvent& event) override; + virtual void OnMenuAbout (wxCommandEvent& event) override; + virtual void OnShowHelp (wxCommandEvent& event) override; + virtual void OnMenuQuit (wxCommandEvent& event) override { Close(); } + + void OnMenuLanguageSwitch(wxCommandEvent& event); + + void switchProgramLanguage(int langID); + + typedef int MenuItemID; + typedef int LanguageID; + std::map languageMenuItemMap; //needed to attach menu item events + + //*********************************************** + //application variables are stored here: + + //global settings shared by GUI and batch mode + xmlAccess::XmlGlobalSettings globalCfg; + + //UI view of FolderComparison structure (partially owns folderCmp) + std::shared_ptr gridDataView; //always bound! + std::shared_ptr treeDataView; // + + //the prime data structure of this tool *bling*: + zen::FolderComparison folderCmp; //optional!: sync button not available if empty + + void clearGrid(); + + //------------------------------------- + //program configuration + xmlAccess::XmlGuiConfig currentCfg; + + //folder pairs: + std::unique_ptr firstFolderPair; //always bound!!! + std::vector additionalFolderPairs; //additional pairs to the first pair + //------------------------------------- + + //*********************************************** + //status information + std::list oldStatusMsgs; //the first one is the original/non-flash status message + + //compare status panel (hidden on start, shown when comparing) + std::unique_ptr compareStatus; //always bound + + bool cleanedUp; + + bool processingGlobalKeyEvent; //indicator to notify recursion in OnGlobalKeyEvent() + + //toggle to display configuration preview instead of comparison result: + //for read access use: m_bpButtonViewTypeSyncAction->isActive() + //when changing value use: + void setViewTypeSyncAction(bool value); + + wxAuiManager auiMgr; //implement dockable GUI design + + wxString defaultPerspective; + + zen::Int64 manualTimeSpanFrom, manualTimeSpanTo; //buffer manual time span selection at session level + + std::shared_ptr folderHistoryLeft; //shared by all wxComboBox dropdown controls + std::shared_ptr folderHistoryRight; //always bound! + + //schedule and run long-running tasks asynchronously, but process results on GUI queue + zen::AsyncTasks asyncTasks; + wxTimer timerForAsyncTasks; //don't use wxWidgets idle handling => repeated idle requests/consumption hogs 100% cpu! + + std::unique_ptr filterCfgOnClipboard; //copy/paste of filter config + + wxWindow* focusWindowAfterSearch; //used to restore focus after search panel is closed +}; + +#endif // MAINDIALOG_H diff --git a/FreeFileSync/Source/ui/osx_dock.h b/FreeFileSync/Source/ui/osx_dock.h new file mode 100644 index 00000000..62237524 --- /dev/null +++ b/FreeFileSync/Source/ui/osx_dock.h @@ -0,0 +1,17 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef OSX_DOCK_837210847312534 +#define OSX_DOCK_837210847312534 + +#include + +namespace osx +{ +void dockIconSetText(const char* str); //throw SysError +} + +#endif //OSX_DOCK_837210847312534 diff --git a/FreeFileSync/Source/ui/osx_dock.mm b/FreeFileSync/Source/ui/osx_dock.mm new file mode 100644 index 00000000..8f42ae88 --- /dev/null +++ b/FreeFileSync/Source/ui/osx_dock.mm @@ -0,0 +1,24 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "osx_dock.h" +#include +#include + + +void osx::dockIconSetText(const char* str) //throw SysError +{ + @try + { + NSString* label = [NSString stringWithCString:str encoding:NSUTF8StringEncoding]; + //stringWithCString returns string which is already set to autorelease! + [[NSApp dockTile] setBadgeLabel:label]; //label may be nil + } + @catch (NSException* e) + { + throwSysError(e); //throw SysError + } +} diff --git a/FreeFileSync/Source/ui/progress_indicator.cpp b/FreeFileSync/Source/ui/progress_indicator.cpp new file mode 100644 index 00000000..cec13836 --- /dev/null +++ b/FreeFileSync/Source/ui/progress_indicator.cpp @@ -0,0 +1,2147 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "progress_indicator.h" +#include +#include +//#include +//#include +#include +#include +#include +#include +#include //wxTextDataObject +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gui_generated.h" +#include "../lib/ffs_paths.h" +#include "../lib/perf_check.h" +#include "tray_icon.h" +#include "taskbar.h" +#include "exec_finished_box.h" +#include "app_icon.h" +#ifdef ZEN_MAC +#include +#endif + +using namespace zen; + + +namespace +{ +const int GAUGE_FULL_RANGE = 50000; + +//window size used for statistics in milliseconds +const int WINDOW_REMAINING_TIME = 60000; //some scenarios have dropouts of 40 seconds -> 60 sec. window size handles them well +const int WINDOW_BYTES_PER_SEC = 5000; // + +//don't use wxStopWatch for long-running measurements: internally it uses ::QueryPerformanceCounter() which can overflow after only a few days: +//https://sourceforge.net/p/freefilesync/discussion/help/thread/5d62339e + +class StopWatch +{ +public: + StopWatch() : startTime(wxGetUTCTimeMillis().GetValue()), paused(false), elapsedUntilPause(0) {} //start running + + void restart() + { + startTime = wxGetUTCTimeMillis().GetValue(); //uses ::GetSystemTimeAsFileTime() + paused = false; + } + + void pause() + { + if (!paused) + { + paused = true; + elapsedUntilPause = wxGetUTCTimeMillis().GetValue() - startTime; + } + } + + void resume() + { + if (paused) + { + paused = false; + startTime = wxGetUTCTimeMillis().GetValue() - elapsedUntilPause; + } + } + + int64_t timeMs() const { return paused ? elapsedUntilPause : wxGetUTCTimeMillis().GetValue() - startTime; } + +private: + int64_t startTime; + bool paused; + int64_t elapsedUntilPause; +}; +} + + +class CompareProgressDialog::Pimpl : public CompareProgressDlgGenerated +{ +public: + Pimpl(wxFrame& parentWindow); + + void init(const Statistics& syncStat); //constructor/destructor semantics, but underlying Window is reused + void finalize(); // + + void switchToCompareBytewise(); + void updateStatusPanelNow(); + +private: + wxFrame& parentWindow_; + wxString titleTextBackup; + + StopWatch timeElapsed; + int64_t binCompStartMs; //begin of binary comparison phase in [ms] + + const Statistics* syncStat_; //only bound while sync is running + + std::unique_ptr taskbar_; + std::unique_ptr perf; //estimate remaining time + + int64_t lastStatCallSpeed; //used for calculating intervals between showing and collecting perf samples +}; + + +CompareProgressDialog::Pimpl::Pimpl(wxFrame& parentWindow) : + CompareProgressDlgGenerated(&parentWindow), + parentWindow_(parentWindow), + binCompStartMs(0), + syncStat_(nullptr), + lastStatCallSpeed(-1000000) //some big number +{ + //init(); -> needed? +} + + +void CompareProgressDialog::Pimpl::init(const Statistics& syncStat) +{ + syncStat_ = &syncStat; + titleTextBackup = parentWindow_.GetTitle(); + + try //try to get access to Windows 7/Ubuntu taskbar + { + taskbar_ = make_unique(parentWindow_); + } + catch (const TaskbarNotAvailable&) {} + + //initialize gauge + m_gauge2->SetRange(GAUGE_FULL_RANGE); + m_gauge2->SetValue(0); + + perf.reset(); + timeElapsed.restart(); //measure total time + + //initially hide status that's relevant for comparing bytewise only + bSizerFilesFound ->Show(true); + bSizerFilesRemaining->Show(false); + sSizerSpeed ->Show(false); + sSizerTimeRemaining ->Show(false); + + updateStatusPanelNow(); + + m_gauge2->Hide(); + bSizer42->Layout(); + Layout(); +} + + +void CompareProgressDialog::Pimpl::finalize() +{ + syncStat_ = nullptr; + parentWindow_.SetTitle(titleTextBackup); + taskbar_.reset(); +} + + +void CompareProgressDialog::Pimpl::switchToCompareBytewise() +{ + //start to measure perf + perf = make_unique(WINDOW_REMAINING_TIME, WINDOW_BYTES_PER_SEC); + lastStatCallSpeed = -1000000; //some big number + + binCompStartMs = timeElapsed.timeMs(); + + //show status for comparing bytewise + bSizerFilesFound ->Show(false); + bSizerFilesRemaining->Show(true); + sSizerSpeed ->Show(true); + sSizerTimeRemaining ->Show(true); + + m_gauge2->Show(); + bSizer42->Layout(); + Layout(); +} + + +void CompareProgressDialog::Pimpl::updateStatusPanelNow() +{ + if (!syncStat_) //no comparison running!! + return; + + const wxString& scannedObjects = toGuiString(syncStat_->getObjectsCurrent(ProcessCallback::PHASE_SCANNING)); + + auto setTitle = [&](const wxString& title) + { + if (parentWindow_.GetTitle() != title) + parentWindow_.SetTitle(title); + }; + + bool layoutChanged = false; //avoid screen flicker by calling layout() only if necessary + const int64_t timeNow = timeElapsed.timeMs(); + + //status texts + setText(*m_textCtrlStatus, replaceCpy(syncStat_->currentStatusText(), L'\n', L' ')); //no layout update for status texts! + + //write status information to taskbar, parent title ect. + switch (syncStat_->currentPhase()) + { + case ProcessCallback::PHASE_NONE: + case ProcessCallback::PHASE_SCANNING: + //dialog caption, taskbar + setTitle(scannedObjects + L" - " + _("Scanning...")); + if (taskbar_.get()) //support Windows 7 taskbar + taskbar_->setStatus(Taskbar::STATUS_INDETERMINATE); + break; + + case ProcessCallback::PHASE_COMPARING_CONTENT: + { + auto objectsCurrent = syncStat_->getObjectsCurrent(ProcessCallback::PHASE_COMPARING_CONTENT); + auto objectsTotal = syncStat_->getObjectsTotal (ProcessCallback::PHASE_COMPARING_CONTENT); + auto dataCurrent = syncStat_->getDataCurrent (ProcessCallback::PHASE_COMPARING_CONTENT); + auto dataTotal = syncStat_->getDataTotal (ProcessCallback::PHASE_COMPARING_CONTENT); + + //add both data + obj-count, to handle "deletion-only" cases + const double fraction = dataTotal + objectsTotal == 0 ? 0 : std::max(0.0, to(dataCurrent + objectsCurrent) / to(dataTotal + objectsTotal)); + + //dialog caption, taskbar + setTitle(fractionToString(fraction) + wxT(" - ") + _("Comparing content...")); + if (taskbar_.get()) + { + taskbar_->setProgress(fraction); + taskbar_->setStatus(Taskbar::STATUS_NORMAL); + } + + //progress indicator, shown for binary comparison only + m_gauge2->SetValue(numeric::round(fraction * GAUGE_FULL_RANGE)); + + //remaining objects and bytes for file comparison + setText(*m_staticTextFilesRemaining, toGuiString(objectsTotal - objectsCurrent), &layoutChanged); + setText(*m_staticTextDataRemaining, L"(" + filesizeToShortString(dataTotal - dataCurrent) + L")", &layoutChanged); + + //remaining time and speed: only visible during binary comparison + assert(perf); + if (perf) + if (numeric::dist(lastStatCallSpeed, timeNow) >= 500) + { + lastStatCallSpeed = timeNow; + + if (numeric::dist(binCompStartMs, timeNow) >= 1000) //discard stats for first second: probably messy + perf->addSample(objectsCurrent, to(dataCurrent), timeNow); + + //current speed -> Win 7 copy uses 1 sec update interval instead + Opt bps = perf->getBytesPerSecond(); + setText(*m_staticTextSpeed, bps ? *bps : L"-", &layoutChanged); + + //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 rt = perf->getRemainingTime(to(dataTotal - dataCurrent)); + setText(*m_staticTextRemTime, rt ? *rt : L"-", &layoutChanged); + } + } + break; + + case ProcessCallback::PHASE_SYNCHRONIZING: + assert(false); + break; + } + + //nr of scanned objects + setText(*m_staticTextScanned, scannedObjects, &layoutChanged); + + //time elapsed + const int64_t timeElapSec = timeNow / 1000; + setText(*m_staticTextTimeElapsed, + timeElapSec < 3600 ? + wxTimeSpan::Seconds(timeElapSec).Format( L"%M:%S") : + wxTimeSpan::Seconds(timeElapSec).Format(L"%H:%M:%S"), &layoutChanged); + + if (layoutChanged) + bSizer42->Layout(); + + //do the ui update + wxTheApp->Yield(); +} + +//######################################################################################## + +//redirect to implementation +CompareProgressDialog::CompareProgressDialog(wxFrame& parentWindow) : + pimpl(new Pimpl(parentWindow)) {} //owned by parentWindow + +wxWindow* CompareProgressDialog::getAsWindow() +{ + return pimpl; +} + +void CompareProgressDialog::init(const Statistics& syncStat) +{ + pimpl->init(syncStat); +} + +void CompareProgressDialog::finalize() +{ + pimpl->finalize(); +} + +void CompareProgressDialog::switchToCompareBytewise() +{ + pimpl->switchToCompareBytewise(); +} + +void CompareProgressDialog::updateStatusPanelNow() +{ + pimpl->updateStatusPanelNow(); +} +//######################################################################################## + +namespace +{ +inline +wxBitmap buttonPressed(const std::string& name) +{ + wxBitmap background = getResourceImage(L"log button pressed"); + return layOver(getResourceImage(utfCvrtTo(name)), background); +} + + +inline +wxBitmap buttonReleased(const std::string& name) +{ + wxImage output = greyScale(getResourceImage(utfCvrtTo(name))).ConvertToImage(); + //getResourceImage(utfCvrtTo(name)).ConvertToImage().ConvertToGreyscale(1.0/3, 1.0/3, 1.0/3); //treat all channels equally! + //brighten(output, 30); + + //zen::moveImage(output, 1, 0); //move image right one pixel + return output; +} + + +//a vector-view on ErrorLog considering multi-line messages: prepare consumption by Grid +class MessageView +{ +public: + MessageView(const ErrorLog& log) : log_(log) {} + + size_t rowsOnView() const { return viewRef.size(); } + + struct LogEntryView + { + time_t time; + MessageType type; + MsgString messageLine; + bool firstLine; //if LogEntry::message spans multiple rows + }; + bool getEntry(size_t row, LogEntryView& out) const + { + if (row < viewRef.size()) + { + const Line& line = viewRef[row]; + out.time = line.entry_->time; + out.type = line.entry_->type; + out.messageLine = extractLine(line.entry_->message, line.rowNumber_); + out.firstLine = line.rowNumber_ == 0; //this is virtually always correct, unless first line of the original message is empty! + return true; + } + return false; + } + + void updateView(int includedTypes) //TYPE_INFO | TYPE_WARNING, ect. see error_log.h + { + viewRef.clear(); + + for (auto it = log_.begin(); it != log_.end(); ++it) + if (it->type & includedTypes) + { + assert_static((IsSameType::Type, wchar_t>::value)); + assert(!startsWith(it->message, L'\n')); + + size_t rowNumber = 0; + bool lastCharNewline = true; + for (const wchar_t c : it->message) + if (c == L'\n') + { + if (!lastCharNewline) //do not reference empty lines! + viewRef.push_back(Line(&*it, rowNumber)); + ++rowNumber; + lastCharNewline = true; + } + else + lastCharNewline = false; + + if (!lastCharNewline) + viewRef.push_back(Line(&*it, rowNumber)); + } + } + +private: + static MsgString extractLine(const MsgString& message, size_t textRow) + { + auto iter1 = message.begin(); + for (;;) + { + auto iter2 = std::find_if(iter1, message.end(), [](wchar_t c) { return c == L'\n'; }); + if (textRow == 0) + return iter1 == message.end() ? MsgString() : MsgString(&*iter1, iter2 - iter1); //must not dereference iterator pointing to "end"! + + if (iter2 == message.end()) + { + assert(false); + return MsgString(); + } + + iter1 = iter2 + 1; //skip newline + --textRow; + } + } + + struct Line + { + Line(const LogEntry* entry, size_t rowNumber) : entry_(entry), rowNumber_(rowNumber) {} + const LogEntry* entry_; //always bound! + size_t rowNumber_; //LogEntry::message may span multiple rows + }; + + std::vector viewRef; //partial view on log_ + /* /|\ + | updateView() + | */ + const ErrorLog log_; +}; + +//----------------------------------------------------------------------------- + +enum ColumnTypeMsg +{ + COL_TYPE_MSG_TIME, + COL_TYPE_MSG_CATEGORY, + COL_TYPE_MSG_TEXT, +}; + +//Grid data implementation referencing MessageView +class GridDataMessages : public GridData +{ +public: + GridDataMessages(const std::shared_ptr& msgView) : msgView_(msgView) {} + + virtual size_t getRowCount() const { return msgView_ ? msgView_->rowsOnView() : 0; } + + virtual wxString getValue(size_t row, ColumnType colType) const + { + MessageView::LogEntryView entry = {}; + if (msgView_ && msgView_->getEntry(row, entry)) + switch (static_cast(colType)) + { + case COL_TYPE_MSG_TIME: + if (entry.firstLine) + return formatTime(FORMAT_TIME, localTime(entry.time)); + break; + + case COL_TYPE_MSG_CATEGORY: + if (entry.firstLine) + switch (entry.type) + { + case TYPE_INFO: + return _("Info"); + case TYPE_WARNING: + return _("Warning"); + case TYPE_ERROR: + return _("Error"); + case TYPE_FATAL_ERROR: + return _("Serious Error"); + } + break; + + case COL_TYPE_MSG_TEXT: + return copyStringTo(entry.messageLine); + } + return wxEmptyString; + } + + virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool selected) override + { + wxRect rectTmp = rect; + + const wxColor colorGridLine = wxColour(192, 192, 192); //light grey + + wxDCPenChanger dummy2(dc, wxPen(colorGridLine, 1, wxSOLID)); + const bool drawBottomLine = [&]() -> bool //don't separate multi-line messages + { + MessageView::LogEntryView nextEntry = {}; + if (msgView_ && msgView_->getEntry(row + 1, nextEntry)) + return nextEntry.firstLine; + return true; + }(); + + if (drawBottomLine) + { + dc.DrawLine(rect.GetBottomLeft(), rect.GetBottomRight() + wxPoint(1, 0)); + --rectTmp.height; + } + + //-------------------------------------------------------- + + MessageView::LogEntryView entry = {}; + if (msgView_ && msgView_->getEntry(row, entry)) + switch (static_cast(colType)) + { + case COL_TYPE_MSG_TIME: + drawCellText(dc, rectTmp, getValue(row, colType), true, wxALIGN_CENTER); + break; + + case COL_TYPE_MSG_CATEGORY: + if (entry.firstLine) + switch (entry.type) + { + case TYPE_INFO: + dc.DrawLabel(wxString(), getResourceImage(L"msg_info_small"), rectTmp, wxALIGN_CENTER); + break; + case TYPE_WARNING: + dc.DrawLabel(wxString(), getResourceImage(L"msg_warning_small"), rectTmp, wxALIGN_CENTER); + break; + case TYPE_ERROR: + case TYPE_FATAL_ERROR: + dc.DrawLabel(wxString(), getResourceImage(L"msg_error_small"), rectTmp, wxALIGN_CENTER); + break; + } + break; + + case COL_TYPE_MSG_TEXT: + { + rectTmp.x += COLUMN_GAP_LEFT; + rectTmp.width -= COLUMN_GAP_LEFT; + drawCellText(dc, rectTmp, getValue(row, colType), true); + } + break; + } + } + + virtual int getBestSize(wxDC& dc, size_t row, ColumnType colType) override + { + // -> synchronize renderCell() <-> getBestSize() + + MessageView::LogEntryView entry = {}; + if (msgView_ && msgView_->getEntry(row, entry)) + switch (static_cast(colType)) + { + case COL_TYPE_MSG_TIME: + return 2 * COLUMN_GAP_LEFT + dc.GetTextExtent(getValue(row, colType)).GetWidth(); + + case COL_TYPE_MSG_CATEGORY: + return getResourceImage(L"msg_info_small").GetWidth(); + + case COL_TYPE_MSG_TEXT: + return COLUMN_GAP_LEFT + dc.GetTextExtent(getValue(row, colType)).GetWidth(); + } + return 0; + } + + static int getColumnTimeDefaultWidth(Grid& grid) + { + wxClientDC dc(&grid.getMainWin()); + dc.SetFont(grid.getMainWin().GetFont()); + return 2 * COLUMN_GAP_LEFT + dc.GetTextExtent(formatTime(FORMAT_TIME)).GetWidth(); + } + + static int getColumnCategoryDefaultWidth() + { + return getResourceImage(L"msg_info_small").GetWidth(); + } + + static int getRowDefaultHeight(const Grid& grid) + { + return std::max(getResourceImage(L"msg_info_small").GetHeight(), grid.getMainWin().GetCharHeight() + 2) + 1; //+ some space + bottom border + } + + virtual wxString getToolTip(size_t row, ColumnType colType) const override + { + MessageView::LogEntryView entry = {}; + if (msgView_ && msgView_->getEntry(row, entry)) + switch (static_cast(colType)) + { + case COL_TYPE_MSG_TIME: + case COL_TYPE_MSG_TEXT: + break; + + case COL_TYPE_MSG_CATEGORY: + return getValue(row, colType); + } + return wxEmptyString; + } + + virtual wxString getColumnLabel(ColumnType colType) const { return wxEmptyString; } + +private: + const std::shared_ptr msgView_; +}; + +bool isComponentOf(const wxWindow* child, const wxWindow* top) +{ + for (const wxWindow* wnd = child; wnd != nullptr; wnd = wnd->GetParent()) + if (wnd == top) + return true; + return false; +} + +const wxTopLevelWindow* getTopLevelWindow(const wxWindow* child) +{ + for (const wxWindow* wnd = child; wnd != nullptr; wnd = wnd->GetParent()) + if (auto tlw = dynamic_cast(wnd)) + return tlw; + return nullptr; +} +} + + +class LogPanel : public LogPanelGenerated +{ +public: + LogPanel(wxWindow* parent, const ErrorLog& log) : LogPanelGenerated(parent), + msgView(std::make_shared(log)), + processingGlobalKeyEvent(false) + { + const int errorCount = log.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR); + const int warningCount = log.getItemCount(TYPE_WARNING); + const int infoCount = log.getItemCount(TYPE_INFO); + + auto initButton = [](ToggleButton& btn, const char* imgName, const wxString& tooltip) { btn.init(buttonPressed(imgName), buttonReleased(imgName)); btn.SetToolTip(tooltip); }; + + initButton(*m_bpButtonErrors, "msg_error", _("Error" ) + wxString::Format(L" (%d)", errorCount )); + initButton(*m_bpButtonWarnings, "msg_warning", _("Warning") + wxString::Format(L" (%d)", warningCount)); + initButton(*m_bpButtonInfo, "msg_info", _("Info" ) + wxString::Format(L" (%d)", infoCount )); + + m_bpButtonErrors ->setActive(true); + m_bpButtonWarnings->setActive(true); + m_bpButtonInfo ->setActive(errorCount + warningCount == 0); + + m_bpButtonErrors ->Show(errorCount != 0); + m_bpButtonWarnings->Show(warningCount != 0); + m_bpButtonInfo ->Show(infoCount != 0); + + //init grid, determine default sizes + const int rowHeight = GridDataMessages::getRowDefaultHeight(*m_gridMessages); + const int colMsgTimeWidth = GridDataMessages::getColumnTimeDefaultWidth(*m_gridMessages); + const int colMsgCategoryWidth = GridDataMessages::getColumnCategoryDefaultWidth(); + + m_gridMessages->setDataProvider(std::make_shared(msgView)); + m_gridMessages->setColumnLabelHeight(0); + m_gridMessages->showRowLabel(false); + m_gridMessages->setRowHeight(rowHeight); + std::vector attr; + attr.push_back(Grid::ColumnAttribute(static_cast(COL_TYPE_MSG_TIME ), colMsgTimeWidth, 0)); + attr.push_back(Grid::ColumnAttribute(static_cast(COL_TYPE_MSG_CATEGORY), colMsgCategoryWidth, 0)); + attr.push_back(Grid::ColumnAttribute(static_cast(COL_TYPE_MSG_TEXT ), -colMsgTimeWidth - colMsgCategoryWidth, 1)); + m_gridMessages->setColumnConfig(attr); + + //support for CTRL + C + m_gridMessages->getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(LogPanel::onGridButtonEvent), nullptr, this); + + m_gridMessages->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(LogPanel::onMsgGridContext), nullptr, this); + + wxTheApp->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(LogPanel::onGlobalKeyEvent), nullptr, this); + wxTheApp->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(LogPanel::onGlobalKeyEvent), nullptr, this); //capture direction keys + + updateGrid(); + } + + ~LogPanel() + { + //important! event source wxTheApp is NOT dependent on this instance -> disconnect! + wxTheApp->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(LogPanel::onGlobalKeyEvent), nullptr, this); + wxTheApp->Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(LogPanel::onGlobalKeyEvent), nullptr, this); + } + +private: + virtual void OnErrors(wxCommandEvent& event) + { + m_bpButtonErrors->toggle(); + updateGrid(); + } + + virtual void OnWarnings(wxCommandEvent& event) + { + m_bpButtonWarnings->toggle(); + updateGrid(); + } + + virtual void OnInfo(wxCommandEvent& event) + { + m_bpButtonInfo->toggle(); + updateGrid(); + } + + void updateGrid() + { + int includedTypes = 0; + if (m_bpButtonErrors->isActive()) + includedTypes |= TYPE_ERROR | TYPE_FATAL_ERROR; + + if (m_bpButtonWarnings->isActive()) + includedTypes |= TYPE_WARNING; + + if (m_bpButtonInfo->isActive()) + includedTypes |= TYPE_INFO; + + msgView->updateView(includedTypes); //update MVC "model" + m_gridMessages->Refresh(); //update MVC "view" + } + + void onGridButtonEvent(wxKeyEvent& event) + { + int keyCode = event.GetKeyCode(); + + if (event.ControlDown()) + switch (keyCode) + { + //case 'A': -> "select all" is already implemented by Grid! + + case 'C': + case WXK_INSERT: //CTRL + C || CTRL + INS + copySelectionToClipboard(); + return; // -> swallow event! don't allow default grid commands! + } + + event.Skip(); //unknown keypress: propagate + } + + void onMsgGridContext(GridClickEvent& event) + { + const std::vector selection = m_gridMessages->getSelectedRows(); + + const size_t rowCount = [&]() -> size_t + { + if (auto prov = m_gridMessages->getDataProvider()) + return prov->getRowCount(); + return 0; + }(); + + ContextMenu menu; + menu.addItem(_("Select all") + L"\tCtrl+A", [this] { m_gridMessages->selectAllRows(ALLOW_GRID_EVENT); }, nullptr, rowCount > 0); + menu.addSeparator(); + + menu.addItem(_("Copy") + L"\tCtrl+C", [this] { copySelectionToClipboard(); }, nullptr, !selection.empty()); + menu.popup(*this); + } + + void onGlobalKeyEvent(wxKeyEvent& event) //process key events without explicit menu entry :) + { + const wxWindow* focus = wxWindow::FindFocus(); + const wxTopLevelWindow* tlw = getTopLevelWindow(this); + + //avoid recursion!!! -> this ugly construct seems to be the only (portable) way to avoid re-entrancy + //recursion may happen in multiple situations: e.g. modal dialogs, Grid::ProcessEvent()! + if (processingGlobalKeyEvent || + !isComponentOf(focus, this) || + !IsEnabled() || //only handle if main window is in use and no modal dialog is shown: + !tlw || !const_cast(tlw)->IsActive()) //thanks to wxWidgets non-portability we need both checks: + //first is sufficient for Windows, second is needed on OS X since it does NOT disable the parent when showing a modal dialog + { + event.Skip(); + return; + } + processingGlobalKeyEvent = true; + ZEN_ON_SCOPE_EXIT(processingGlobalKeyEvent = false;) + //---------------------------------------------------- + + const int keyCode = event.GetKeyCode(); + + if (event.ControlDown()) + switch (keyCode) + { + case 'A': + m_gridMessages->SetFocus(); + m_gridMessages->selectAllRows(ALLOW_GRID_EVENT); + return; // -> swallow event! don't allow default grid commands! + } + else + switch (keyCode) + { + //redirect certain (unhandled) keys directly to grid! + case WXK_UP: + case WXK_DOWN: + case WXK_LEFT: + case WXK_RIGHT: + case WXK_NUMPAD_UP: + case WXK_NUMPAD_DOWN: + case WXK_NUMPAD_LEFT: + case WXK_NUMPAD_RIGHT: + + case WXK_PAGEUP: + case WXK_PAGEDOWN: + case WXK_HOME: + case WXK_END: + case WXK_NUMPAD_PAGEUP: + case WXK_NUMPAD_PAGEDOWN: + case WXK_NUMPAD_HOME: + case WXK_NUMPAD_END: + if (!isComponentOf(focus, m_gridMessages) && //don't propagate keyboard commands if grid is already in focus + m_gridMessages->IsEnabled()) + if (wxEvtHandler* evtHandler = m_gridMessages->getMainWin().GetEventHandler()) + { + m_gridMessages->SetFocus(); + + event.SetEventType(wxEVT_KEY_DOWN); //the grid event handler doesn't expect wxEVT_CHAR_HOOK! + evtHandler->ProcessEvent(event); //propagating event catched at wxTheApp to child leads to recursion, but we prevented it... + event.Skip(false); //definitively handled now! + return; + } + break; + } + + event.Skip(); + } + + void copySelectionToClipboard() + { + try + { + typedef Zbase zxString; //guaranteed exponential growth, unlike wxString + zxString clipboardString; + + if (auto prov = m_gridMessages->getDataProvider()) + { + std::vector colAttr = m_gridMessages->getColumnConfig(); + vector_remove_if(colAttr, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); + if (!colAttr.empty()) + for (size_t row : m_gridMessages->getSelectedRows()) + { + std::for_each(colAttr.begin(), --colAttr.end(), + [&](const Grid::ColumnAttribute& ca) + { + clipboardString += copyStringTo(prov->getValue(row, ca.type_)); + clipboardString += L'\t'; + }); + clipboardString += copyStringTo(prov->getValue(row, colAttr.back().type_)); + clipboardString += L'\n'; + } + } + + //finally write to clipboard + if (!clipboardString.empty()) + if (wxClipboard::Get()->Open()) + { + ZEN_ON_SCOPE_EXIT(wxClipboard::Get()->Close()); + wxClipboard::Get()->SetData(new wxTextDataObject(copyStringTo(clipboardString))); //ownership passed + } + } + catch (const std::bad_alloc& e) + { + showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setMainInstructions(_("Out of memory.") + L" " + utfCvrtTo(e.what()))); + } + } + + std::shared_ptr msgView; //bound! + + bool processingGlobalKeyEvent; +}; + +//######################################################################################## + +namespace +{ +class CurveDataStatistics : public SparseCurveData +{ +public: + CurveDataStatistics() : SparseCurveData(true), /*true: add steps*/ timeNow(0) {} + + void clear() { samples.clear(); timeNow = 0; } + + void addRecord(int64_t timeNowMs, double value) + { + //samples.clear(); + //samples.insert(std::make_pair(-1000, 0)); + //samples.insert(std::make_pair(0, 0)); + //samples.insert(std::make_pair(1, 1)); + //samples.insert(std::make_pair(1000, 0)); + //return; + + timeNow = timeNowMs; + + //allow for at most one sample per 100ms (handles duplicate inserts, too!) => this is unrelated to UI_UPDATE_INTERVAL! + if (!samples.empty() && timeNowMs / 100 == samples.rbegin()->first / 100) + { + samples.rbegin()->second = value; + return; + } + + samples.insert(samples.end(), std::make_pair(timeNowMs, value)); //time is "expected" to be monotonously ascending + //documentation differs about whether "hint" should be before or after the to be inserted element! + //however "std::map<>::end()" is interpreted correctly by GCC and VS2010 + + if (samples.size() > MAX_BUFFER_SIZE) //limit buffer size + samples.erase(samples.begin()); + } + +private: + virtual std::pair getRangeX() const override + { + if (samples.empty()) return std::make_pair(0.0, 0.0); + + double upperEndMs = std::max(timeNow, samples.rbegin()->first); + + //report some additional width by 5% elapsed time to make graph recalibrate before hitting the right border + //caveat: graph for batch mode binary comparison does NOT start at elapsed time 0!! PHASE_COMPARING_CONTENT and PHASE_SYNCHRONIZING! + //=> consider width of current sample set! + upperEndMs += 0.05 *(upperEndMs - samples.begin()->first); + + return std::make_pair(samples.begin()->first / 1000.0, //need not start with 0, e.g. "binary comparison, graph reset, followed by sync" + upperEndMs / 1000.0); + } + + virtual Opt getLessEq(double x) const override //x: seconds since begin + { + const int64_t timex = std::floor(x * 1000); + //------ add artifical last sample value ------- + if (!samples.empty() && samples.rbegin()->first < timeNow) + if (timeNow <= timex) + return CurvePoint(timeNow / 1000.0, samples.rbegin()->second); + //-------------------------------------------------- + + //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(); + //=> samples not empty in this context + --it; + return CurvePoint(it->first / 1000.0, it->second); + } + + virtual Opt getGreaterEq(double x) const override + { + const int64_t timex = std::ceil(x * 1000); + //------ add artifical last sample value ------- + if (!samples.empty() && samples.rbegin()->first < timeNow) + if (samples.rbegin()->first < timex && timex <= timeNow) + return CurvePoint(timeNow / 1000.0, samples.rbegin()->second); + //-------------------------------------------------- + + auto it = samples.lower_bound(timex); + if (it == samples.end()) + return NoValue(); + return CurvePoint(it->first / 1000.0, it->second); + } + + static const size_t MAX_BUFFER_SIZE = 2500000; //sizeof(single node) worst case ~ 3 * 8 byte ptr + 16 byte key/value = 40 byte + + std::map samples; //time, unit: [ms] !don't use std::multimap, see getLessEq() + int64_t timeNow; //help create an artificial record at the end of samples to visualize current time! +}; + + +class CurveDataCurrentValue : public CurveData +{ +public: + CurveDataCurrentValue() : x(0), yCurrent_(0), yTotal_(0) {} + + void setValue(int64_t xTimeNowMs, double yCurrent, double yTotal) { x = xTimeNowMs / 1000.0; yCurrent_ = yCurrent; yTotal_ = yTotal; } + +private: + virtual std::pair getRangeX() const override { return std::make_pair(x, x); } //conceptually just a vertical line! + + virtual void getPoints(double minX, double maxX, int pixelWidth, std::vector& points) const override + { + //points.push_back(CurvePoint(-1, 0)); + //points.push_back(CurvePoint(0, 0)); + //points.push_back(CurvePoint(0.0001, 1)); + //points.push_back(CurvePoint(1, 0)); + //return; + + if (x <= maxX) + { + points.push_back(CurvePoint(x, 0)); + points.push_back(CurvePoint(x, yCurrent_)); + points.push_back(CurvePoint(maxX, yCurrent_)); + points.push_back(CurvePoint(x, yCurrent_)); + points.push_back(CurvePoint(x, yTotal_)); + } + } + + double x; //time elapsed in seconds + double yCurrent_; //current bytes/items + double yTotal_; +}; + + +class CurveDataTotalValue : public CurveData +{ +public: + CurveDataTotalValue () : x(0), yTotal_(0) {} + + void setValue(int64_t xTimeNowMs, double yTotal) { x = xTimeNowMs / 1000.0; yTotal_ = yTotal; } + +private: + virtual std::pair getRangeX() const override { return std::make_pair(x, x); } //conceptually just a vertical line! + + virtual void getPoints(double minX, double maxX, int pixelWidth, std::vector& points) const override + { + if (x <= maxX) + { + points.push_back(CurvePoint(x, yTotal_)); + points.push_back(CurvePoint(maxX, yTotal_)); + } + } + + double x; //time elapsed in seconds + double yTotal_; +}; + + +const double stretchDefaultBlockSize = 1.4; //enlarge block default size + + +struct LabelFormatterBytes : public LabelFormatter +{ + virtual double getOptimalBlockSize(double bytesProposed) const + { + bytesProposed *= stretchDefaultBlockSize; //enlarge block default size + + if (bytesProposed <= 1) //never smaller than 1 byte + return 1; + + //round to next number which is a convenient to read block size + const double k = std::floor(std::log(bytesProposed) / std::log(2.0)); + const double e = std::pow(2.0, k); + if (numeric::isNull(e)) + return 0; + const double a = bytesProposed / e; //bytesProposed = a * 2^k with a in [1, 2) + assert(1 <= a && a < 2); + const double steps[] = { 1, 2 }; + return e * numeric::nearMatch(a, std::begin(steps), std::end(steps)); + } + + virtual wxString formatText(double value, double optimalBlockSize) const { return filesizeToShortString(Int64(value)); }; +}; + + +struct LabelFormatterItemCount : public LabelFormatter +{ + virtual double getOptimalBlockSize(double itemsProposed) const + { + itemsProposed *= stretchDefaultBlockSize; //enlarge block default size + + const double steps[] = { 1, 2, 5, 10 }; + if (itemsProposed <= 10) + return numeric::nearMatch(itemsProposed, std::begin(steps), std::end(steps)); //similar to nextNiceNumber(), but without the 2.5 step! + return nextNiceNumber(itemsProposed); + } + + virtual wxString formatText(double value, double optimalBlockSize) const + { + return toGuiString(numeric::round(value)); //not enough room for a "%x items" representation + }; +}; + + +struct LabelFormatterTimeElapsed : public LabelFormatter +{ + LabelFormatterTimeElapsed(bool drawLabel) : drawLabel_(drawLabel) {} + + virtual double getOptimalBlockSize(double secProposed) const + { + const double stepsSec[] = { 20, 30, 60 }; //20 sec: minimum block size; (no 15: avoid flicker between 10<->15<->20 sec blocks) + if (secProposed <= 60) + return numeric::nearMatch(secProposed, std::begin(stepsSec), std::end(stepsSec)); + + const double stepsMin[] = { 1, 2, 5, 10, 15, 20, 30, 60 }; //nice numbers for minutes + if (secProposed <= 3600) + return 60.0 * numeric::nearMatch(secProposed / 60, std::begin(stepsMin), std::end(stepsMin)); + + if (secProposed <= 3600 * 24) + return nextNiceNumber(secProposed / 3600) * 3600; //round up to full hours + + return nextNiceNumber(secProposed / (24 * 3600)) * 24 * 3600; //round to full days + } + + virtual wxString formatText(double timeElapsed, double optimalBlockSize) const + { + if (!drawLabel_) return wxString(); + return timeElapsed < 60 ? + _P("1 sec", "%x sec", numeric::round(timeElapsed)) : + timeElapsed < 3600 ? + wxTimeSpan::Seconds(timeElapsed).Format( L"%M:%S") : + wxTimeSpan::Seconds(timeElapsed).Format(L"%H:%M:%S"); + } + +private: + bool drawLabel_; +}; +} + + +template //can be a wxFrame or wxDialog +class SyncProgressDialogImpl : public TopLevelDialog, public SyncProgressDialog +/*we need derivation, not composition! + 1. SyncProgressDialogImpl IS a wxFrame/wxDialog + 2. implement virtual ~wxFrame() + 3. event handling below assumes lifetime is larger-equal than wxFrame's +*/ +{ +public: + SyncProgressDialogImpl(long style, //wxFrame/wxDialog style + const std::function& getTaskbarFrame, + AbortCallback& abortCb, + const std::function& notifyWindowTerminate, + const Statistics& syncStat, + wxFrame* parentFrame, + bool showProgress, + const wxString& jobName, + const std::wstring& execWhenFinished, + std::vector& execFinishedHistory); + virtual ~SyncProgressDialogImpl(); + + //call this in StatusUpdater derived class destructor at the LATEST(!) to prevent access to currentStatusUpdater + virtual void processHasFinished(SyncResult resultId, const ErrorLog& log); + virtual void closeWindowDirectly(); + + virtual wxWindow* getWindowIfVisible() { return this->IsShown() ? this : nullptr; } + //workaround OS X bug: if "this" is used as parent window for a modal dialog then this dialog will erroneously un-hide its parent! + + virtual void initNewPhase(); + virtual void notifyProgressChange(); + virtual void updateGui() { updateGuiInt(true); } + + virtual std::wstring getExecWhenFinishedCommand() const { return pnl.m_comboBoxExecFinished->getValue(); } + + virtual void stopTimer() //halt all internal counters! + { + pnl.m_animCtrlSyncing->Stop(); + timeElapsed.pause(); + } + virtual void resumeTimer() + { + pnl.m_animCtrlSyncing->Play(); + timeElapsed.resume(); + } + +private: + void updateGuiInt(bool allowYield); + + void OnKeyPressed(wxKeyEvent& event); + void OnOkay (wxCommandEvent& event); + void OnPause (wxCommandEvent& event); + void OnCancel (wxCommandEvent& event); + void OnClose (wxCloseEvent& event); + void OnIconize(wxIconizeEvent& event); + void OnMinimizeToTray(wxCommandEvent& event) { minimizeToTray(); } + + void minimizeToTray(); + void resumeFromSystray(); + + void updateDialogStatus(); + void setExternalStatus(const wxString& status, const wxString& progress); //progress may be empty! + + SyncProgressPanelGenerated& pnl; //wxPanel containing the GUI controls of *this + + const wxString jobName_; + StopWatch timeElapsed; + + wxFrame* parentFrame_; //optional + + std::function notifyWindowTerminate_; //call once in OnClose(), NOT in destructor which is called far too late somewhere in wxWidgets main loop! + + bool wereDead; //after wxWindow::Delete, which is equal to "delete this" on OS X! + + //status variables + const Statistics* syncStat_; // + AbortCallback* abortCb_; //valid only while sync is running + bool paused_; //valid only while sync is running + SyncResult finalResult; //set after sync + + //remaining time + std::unique_ptr perf; + int64_t lastStatCallSpeed; //used for calculating intervals between collecting perf samples + //help calculate total speed + int64_t phaseStartMs; //begin of current phase in [ms] + + std::shared_ptr curveDataBytes; + std::shared_ptr curveDataItems; + + std::shared_ptr curveDataBytesTotal; + std::shared_ptr curveDataBytesCurrent; + std::shared_ptr curveDataItemsTotal; + std::shared_ptr curveDataItemsCurrent; + + wxString parentFrameTitleBackup; + std::unique_ptr trayIcon; //optional: if filled all other windows should be hidden and conversely + std::unique_ptr taskbar_; +}; + + +template +SyncProgressDialogImpl::SyncProgressDialogImpl(long style, //wxFrame/wxDialog style + const std::function& getTaskbarFrame, + AbortCallback& abortCb, + const std::function& notifyWindowTerminate, + const Statistics& syncStat, + wxFrame* parentFrame, + bool showProgress, + const wxString& jobName, + const std::wstring& execWhenFinished, + std::vector& execFinishedHistory) : + TopLevelDialog(parentFrame, wxID_ANY, wxString(), wxDefaultPosition, wxDefaultSize, style), //title is overwritten anyway in setExternalStatus() + pnl(*new SyncProgressPanelGenerated(this)), //ownership passed to "this" + jobName_ (jobName), + parentFrame_(parentFrame), + notifyWindowTerminate_(notifyWindowTerminate), + wereDead(false), + syncStat_ (&syncStat), + abortCb_ (&abortCb), + paused_ (false), + finalResult(RESULT_ABORTED), //dummy value + lastStatCallSpeed(-1000000), //some big number + phaseStartMs(0) +{ + assert_static((IsSameType::value || + IsSameType::value)); + assert((IsSameType::value == !parentFrame)); + + //finish construction of this dialog: + this->SetMinSize(wxSize(470, 280)); //== minimum size! no idea why SetMinSize() is not used... + wxBoxSizer* bSizer170 = new wxBoxSizer(wxVERTICAL); + bSizer170->Add(&pnl, 1, wxEXPAND); + this->SetSizer(bSizer170); //pass ownership + //this->Layout(); + //bSizer170->Fit(this); + this->Centre(wxBOTH); + + //lifetime of event sources is subset of this instance's lifetime => no wxEvtHandler::Disconnect() needed + this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler (SyncProgressDialogImpl::OnClose)); + this->Connect(wxEVT_ICONIZE, wxIconizeEventHandler(SyncProgressDialogImpl::OnIconize)); + this->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(SyncProgressDialogImpl::OnKeyPressed), nullptr, this); + pnl.m_buttonClose->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SyncProgressDialogImpl::OnOkay ), NULL, this); + pnl.m_buttonPause->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SyncProgressDialogImpl::OnPause ), NULL, this); + pnl.m_buttonStop ->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SyncProgressDialogImpl::OnCancel), NULL, this); + pnl.m_bpButtonMinimizeToTray->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SyncProgressDialogImpl::OnMinimizeToTray), NULL, this); + +#ifdef ZEN_WIN + new MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" +#endif + + assert(pnl.m_buttonClose->GetId() == wxID_OK); //we cannot use wxID_CLOSE else Esc key won't work: yet another wxWidgets bug?? + + setRelativeFontSize(*pnl.m_staticTextPhase, 1.5); + + if (parentFrame_) + parentFrameTitleBackup = parentFrame_->GetTitle(); //save old title (will be used as progress indicator) + + pnl.m_animCtrlSyncing->SetAnimation(getResourceAnimation(L"working")); + pnl.m_animCtrlSyncing->Play(); + + this->EnableCloseButton(false); //this is NOT honored on OS X or during system shutdown on Windows! + + timeElapsed.restart(); //measure total time + + if (wxFrame* frame = getTaskbarFrame(*this)) + try //try to get access to Windows 7/Ubuntu taskbar + { + taskbar_ = make_unique(*frame); //throw TaskbarNotAvailable + } + catch (const TaskbarNotAvailable&) {} + + //hide "processed" statistics until end of process + pnl.m_notebookResult ->Hide(); + pnl.m_panelItemsProcessed->Hide(); + pnl.m_buttonClose ->Show(false); + //set std order after button visibility was set + setStandardButtonOrder(*pnl.bSizerStdButtons, StdButtons().setAffirmative(pnl.m_buttonPause).setCancel(pnl.m_buttonStop)); + + pnl.m_bpButtonMinimizeToTray->SetBitmapLabel(getResourceImage(L"minimize_to_tray")); + + //init graph + curveDataBytes = std::make_shared(); + curveDataItems = std::make_shared(); + curveDataBytesTotal = std::make_shared(); + curveDataBytesCurrent = std::make_shared(); + curveDataItemsTotal = std::make_shared(); + curveDataItemsCurrent = std::make_shared(); + + const int xLabelHeight = 18; //we need to use the same height for both graphs to make sure they stretch evenly + const int yLabelWidth = 70; + pnl.m_panelGraphBytes->setAttributes(Graph2D::MainAttributes(). + setLabelX(Graph2D::X_LABEL_BOTTOM, xLabelHeight, std::make_shared(true)). + setLabelY(Graph2D::Y_LABEL_RIGHT, yLabelWidth, std::make_shared()). + setSelectionMode(Graph2D::SELECT_NONE)); + + pnl.m_panelGraphItems->setAttributes(Graph2D::MainAttributes(). + setLabelX(Graph2D::X_LABEL_BOTTOM, xLabelHeight, std::make_shared(false)). + setLabelY(Graph2D::Y_LABEL_RIGHT, yLabelWidth, std::make_shared()). + setSelectionMode(Graph2D::SELECT_NONE)); + + //pnl.m_panelGraphBytes->setAttributes(Graph2D::MainAttributes().setMinX(-1).setMaxX(1).setMinY(-1).setMaxY(1)); + + pnl.m_panelGraphBytes->setCurve(curveDataBytes, Graph2D::CurveAttributes().setLineWidth(2). + fillCurveArea(wxColor(205, 255, 202)). //faint green + setColor (wxColor( 20, 200, 0))); //medium green + + pnl.m_panelGraphItems->setCurve(curveDataItems, Graph2D::CurveAttributes().setLineWidth(2). + fillCurveArea(wxColor(198, 206, 255)). //faint blue + setColor (wxColor( 90, 120, 255))); //medium blue + + pnl.m_panelGraphBytes->addCurve(curveDataBytesTotal, Graph2D::CurveAttributes().setLineWidth(2).setColor(wxColor(12, 128, 0))); //dark green + pnl.m_panelGraphBytes->addCurve(curveDataBytesCurrent, Graph2D::CurveAttributes().setLineWidth(1).setColor(wxColor(12, 128, 0))); // + + pnl.m_panelGraphItems->addCurve(curveDataItemsTotal, Graph2D::CurveAttributes().setLineWidth(2).setColor(wxColor(53, 25, 255))); //dark blue + pnl.m_panelGraphItems->addCurve(curveDataItemsCurrent, Graph2D::CurveAttributes().setLineWidth(1).setColor(wxColor(53, 25, 255))); // + + //allow changing on completion command + pnl.m_comboBoxExecFinished->initHistory(execFinishedHistory, execFinishedHistory.size()); //-> we won't use addItemHistory() later + pnl.m_comboBoxExecFinished->setValue(execWhenFinished); + + updateDialogStatus(); //null-status will be shown while waiting for dir locks + + this->GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() + pnl.Layout(); + + if (showProgress) + { + this->Show(); +#ifdef ZEN_MAC + ProcessSerialNumber psn = { 0, kCurrentProcess }; + ::TransformProcessType(&psn, kProcessTransformToForegroundApplication); //show dock icon (consider non-silent batch mode) + ::SetFrontProcess(&psn); //call before TransformProcessType() so that OSX menu is updated correctly +#endif + + pnl.m_buttonStop->SetFocus(); //don't steal focus when starting in sys-tray! + + //clear gui flicker, remove dummy texts: window must be visible to make this work! + updateGuiInt(true); //at least on OS X a real Yield() is required to flush pending GUI updates; Update() is not enough + } + else + minimizeToTray(); +} + + +template +SyncProgressDialogImpl::~SyncProgressDialogImpl() +{ + if (parentFrame_) + { + parentFrame_->SetTitle(parentFrameTitleBackup); //restore title text + + //make sure main dialog is shown again if still "minimized to systray"! see SyncProgressDialog::closeWindowDirectly() + parentFrame_->Show(); +#ifdef ZEN_MAC + ProcessSerialNumber psn = { 0, kCurrentProcess }; + //why isn't this covered by wxWindows::Raise()?? + ::TransformProcessType(&psn, kProcessTransformToForegroundApplication); //show dock icon (consider GUI mode with "close progress dialog") + ::SetFrontProcess(&psn); //call before TransformProcessType() so that OSX menu is updated correctly +#endif + //if (parentFrame_->IsIconized()) //caveat: if window is maximized calling Iconize(false) will erroneously un-maximize! + // parentFrame_->Iconize(false); + } + + //our client is NOT expecting a second call via notifyWindowTerminate_()! +} + + +template +void SyncProgressDialogImpl::OnKeyPressed(wxKeyEvent& event) +{ + const int keyCode = event.GetKeyCode(); + if (keyCode == WXK_ESCAPE) + { + wxCommandEvent dummy(wxEVT_COMMAND_BUTTON_CLICKED); + + //simulate click on abort button + if (pnl.m_buttonStop->IsShown()) //delegate to "cancel" button if available + { + if (wxEvtHandler* handler = pnl.m_buttonStop->GetEventHandler()) + handler->ProcessEvent(dummy); + return; + } + else if (pnl.m_buttonClose->IsShown()) + { + if (wxEvtHandler* handler = pnl.m_buttonClose->GetEventHandler()) + handler->ProcessEvent(dummy); + return; + } + } + + event.Skip(); +} + + +template +void SyncProgressDialogImpl::initNewPhase() +{ + updateDialogStatus(); //evaluates "syncStat_->currentPhase()" + + //reset graphs (e.g. after binary comparison) + curveDataBytesTotal ->setValue(0, 0); + curveDataBytesCurrent->setValue(0, 0, 0); + curveDataItemsTotal ->setValue(0, 0); + curveDataItemsCurrent->setValue(0, 0, 0); + curveDataBytes->clear(); + curveDataItems->clear(); + + notifyProgressChange(); + + //start new measurement + perf = make_unique(WINDOW_REMAINING_TIME, WINDOW_BYTES_PER_SEC); + lastStatCallSpeed = -1000000; //some big number + + phaseStartMs = timeElapsed.timeMs(); + + updateGuiInt(false); +} + + +template +void SyncProgressDialogImpl::notifyProgressChange() //noexcept! +{ + if (syncStat_) //sync running + switch (syncStat_->currentPhase()) + { + case ProcessCallback::PHASE_NONE: + assert(false); + case ProcessCallback::PHASE_SCANNING: + break; + case ProcessCallback::PHASE_COMPARING_CONTENT: + case ProcessCallback::PHASE_SYNCHRONIZING: + curveDataBytes->addRecord(timeElapsed.timeMs(), to(syncStat_->getDataCurrent (syncStat_->currentPhase()))); + curveDataItems->addRecord(timeElapsed.timeMs(), syncStat_->getObjectsCurrent(syncStat_->currentPhase())); + break; + } +} + + +namespace +{ +#ifdef ZEN_WIN +enum Zorder +{ + ZORDER_CORRECT, + ZORDER_WRONG, + ZORDER_INDEFINITE, +}; + +Zorder evaluateZorder(const wxWindow& top, const wxWindow& bottom) +{ + HWND hTop = static_cast(top.GetHWND()); + HWND hBottom = static_cast(bottom.GetHWND()); + assert(hTop && hBottom); + + for (HWND hAbove = hBottom; hAbove; hAbove = ::GetNextWindow(hAbove, GW_HWNDPREV)) //GW_HWNDPREV means "to foreground" + if (hAbove == hTop) + return ZORDER_CORRECT; + + for (HWND hAbove = hTop; hAbove; hAbove = ::GetNextWindow(hAbove, GW_HWNDPREV)) + if (hAbove == hBottom) + return ZORDER_WRONG; + + return ZORDER_INDEFINITE; +} +#endif + + +std::wstring getDialogPhaseText(const Statistics* syncStat, bool paused, SyncProgressDialog::SyncResult finalResult) +{ + if (syncStat) //sync running + { + if (paused) + return _("Paused"); + else + switch (syncStat->currentPhase()) + { + case ProcessCallback::PHASE_NONE: + return _("Initializing..."); //dialog is shown *before* sync starts, so this text may be visible! + case ProcessCallback::PHASE_SCANNING: + return _("Scanning..."); + case ProcessCallback::PHASE_COMPARING_CONTENT: + return _("Comparing content..."); + case ProcessCallback::PHASE_SYNCHRONIZING: + return _("Synchronizing..."); + } + } + else //sync finished + switch (finalResult) + { + case SyncProgressDialog::RESULT_ABORTED: + return _("Stopped"); + case SyncProgressDialog::RESULT_FINISHED_WITH_ERROR: + case SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS: + case SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS: + return _("Completed"); + } + return std::wstring(); +} +} + + +template +void SyncProgressDialogImpl::setExternalStatus(const wxString& status, const wxString& progress) //progress may be empty! +{ + //sys tray: order "top-down": jobname, status, progress + wxString systrayTooltip = jobName_.empty() ? status : L"\"" + jobName_ + L"\"\n" + status; + if (!progress.empty()) + systrayTooltip += L" " + progress; + + //window caption/taskbar; inverse order: progress, status, jobname + wxString title = progress.empty() ? status : progress + L" - " + status; + if (!jobName_.empty()) + title += L" - \"" + jobName_ + L"\""; + + //systray tooltip, if window is minimized + if (trayIcon.get()) + trayIcon->setToolTip(systrayTooltip); + + //show text in dialog title (and at the same time in taskbar) + if (parentFrame_) + { + if (parentFrame_->GetTitle() != title) + parentFrame_->SetTitle(title); + } + + //always set a title: we don't wxGTK to show "nameless window" instead + if (this->GetTitle() != title) + this->SetTitle(title); +} + + +template +void SyncProgressDialogImpl::updateGuiInt(bool allowYield) +{ + if (!syncStat_) //sync not running + return; + + bool layoutChanged = false; //avoid screen flicker by calling layout() only if necessary + const int64_t timeNow = timeElapsed.timeMs(); + + //sync status text + setText(*pnl.m_staticTextStatus, replaceCpy(syncStat_->currentStatusText(), L'\n', L' ')); //no layout update for status texts! + + switch (syncStat_->currentPhase()) //no matter if paused or not + { + case ProcessCallback::PHASE_NONE: + case ProcessCallback::PHASE_SCANNING: + //dialog caption, taskbar, systray tooltip + setExternalStatus(getDialogPhaseText(syncStat_, paused_, finalResult), toGuiString(syncStat_->getObjectsCurrent(ProcessCallback::PHASE_SCANNING))); //status text may be "paused"! + + //progress indicators + if (trayIcon.get()) trayIcon->setProgress(1); //100% = regular FFS logo + + //ignore graphs: should already have been cleared in initNewPhase() + + //remaining objects and data + setText(*pnl.m_staticTextRemainingObj , L"-", &layoutChanged); + setText(*pnl.m_staticTextDataRemaining, L"", &layoutChanged); + + //remaining time and speed + setText(*pnl.m_staticTextRemTime, L"-", &layoutChanged); + pnl.m_panelGraphBytes->setAttributes(pnl.m_panelGraphBytes->getAttributes().setCornerText(wxString(), Graph2D::CORNER_TOP_LEFT)); + pnl.m_panelGraphItems->setAttributes(pnl.m_panelGraphItems->getAttributes().setCornerText(wxString(), Graph2D::CORNER_TOP_LEFT)); + break; + + case ProcessCallback::PHASE_COMPARING_CONTENT: + case ProcessCallback::PHASE_SYNCHRONIZING: + { + const int itemsCurrent = syncStat_->getObjectsCurrent(syncStat_->currentPhase()); + const int itemsTotal = syncStat_->getObjectsTotal (syncStat_->currentPhase()); + const Int64 dataCurrent = syncStat_->getDataCurrent (syncStat_->currentPhase()); + const Int64 dataTotal = syncStat_->getDataTotal (syncStat_->currentPhase()); + + //add both data + obj-count, to handle "deletion-only" cases + const double fraction = dataTotal + itemsTotal == 0 ? 1 : std::max(0.0, to(dataCurrent + itemsCurrent) / to(dataTotal + itemsTotal)); + //yes, this may legitimately become < 0: failed rename operation falls-back to copy + delete, reducing "dataCurrent" to potentially < 0! + //---------------------------------------------------------------------------------------------------- + + //dialog caption, taskbar, systray tooltip + setExternalStatus(getDialogPhaseText(syncStat_, paused_, finalResult), fractionToString(fraction)); //status text may be "paused"! + + //progress indicators + if (trayIcon.get()) trayIcon->setProgress(fraction); + if (taskbar_.get()) taskbar_->setProgress(fraction); + + //constant line graph + curveDataBytesTotal ->setValue(timeNow, to(dataTotal)); + curveDataBytesCurrent->setValue(timeNow, to(dataCurrent), to(dataTotal)); + curveDataItemsTotal ->setValue(timeNow, itemsTotal); + curveDataItemsCurrent->setValue(timeNow, itemsCurrent, itemsTotal); + //even though notifyProgressChange() already set the latest data, let's add another sample to have all curves consider "timeNow" + //no problem with adding too many records: CurveDataStatistics will remove duplicate entries! + curveDataBytes->addRecord(timeNow, to(dataCurrent)); + curveDataItems->addRecord(timeNow, itemsCurrent); + + //remaining objects and data + setText(*pnl.m_staticTextRemainingObj, toGuiString(itemsTotal - itemsCurrent), &layoutChanged); + setText(*pnl.m_staticTextDataRemaining, L"(" + filesizeToShortString(dataTotal - dataCurrent) + L")", &layoutChanged); + //it's possible data remaining becomes shortly negative if last file synced has ADS data and the dataTotal was not yet corrected! + + //remaining time and speed + assert(perf); + if (perf) + if (numeric::dist(lastStatCallSpeed, timeNow) >= 500) + { + lastStatCallSpeed = timeNow; + + if (numeric::dist(phaseStartMs, timeNow) >= 1000) //discard stats for first second: probably messy + perf->addSample(itemsCurrent, to(dataCurrent), timeNow); + + //current speed -> Win 7 copy uses 1 sec update interval instead + Opt bps = perf->getBytesPerSecond(); + Opt 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)); + //setText(*pnl.m_staticTextSpeed, perf->getBytesPerSecond(), &layoutChanged); + + //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 rt = perf->getRemainingTime(to(dataTotal - dataCurrent)); + setText(*pnl.m_staticTextRemTime, rt ? *rt : L"-", &layoutChanged); + } + } + break; + } + + pnl.m_panelGraphBytes->Refresh(); + pnl.m_panelGraphItems->Refresh(); + + //time elapsed + const int64_t timeElapSec = timeNow / 1000; + setText(*pnl.m_staticTextTimeElapsed, + timeElapSec < 3600 ? + wxTimeSpan::Seconds(timeElapSec).Format( L"%M:%S") : + wxTimeSpan::Seconds(timeElapSec).Format(L"%H:%M:%S"), &layoutChanged); + + //adapt layout after content changes above + if (layoutChanged) + { + pnl.m_panelProgress->Layout(); + //small statistics panels: + //pnl.m_panelItemsProcessed->Layout(); + pnl.m_panelItemsRemaining->Layout(); + pnl.m_panelTimeRemaining ->Layout(); + //pnl.m_panelTimeElapsed->Layout(); -> needed? + } + +#ifdef ZEN_WIN + //workaround Windows 7 bug messing up z-order after temporary application hangs: https://sourceforge.net/tracker/index.php?func=detail&aid=3376523&group_id=234430&atid=1093080 + //This is still needed no matter if wxDialog or wxPanel is used! (2013-07) + if (parentFrame_) + if (evaluateZorder(*this, *parentFrame_) == ZORDER_WRONG) + { + HWND hProgress = static_cast(this->GetHWND()); + if (::IsWindowVisible(hProgress)) + { + ::ShowWindow(hProgress, SW_HIDE); //make Windows recalculate z-order + ::ShowWindow(hProgress, SW_SHOW); // + } + } +#endif + + if (allowYield) + { + //support for pause button + if (paused_) + { + /* + ZEN_ON_SCOPE_EXIT(resumeTimer()); -> crashes on Fedora; WHY??? + => likely compiler bug!!! + 1. no crash on Fedora for: ZEN_ON_SCOPE_EXIT(this->resumeTimer()); + 1. no crash if we derive from wxFrame instead of template "TopLevelDialog" + 2. no crash on Ubuntu GCC + 3. following makes GCC crash already during compilation: auto dfd = zen::makeGuard([this]{ resumeTimer(); }); + */ + + stopTimer(); + + while (paused_) + { + wxTheApp->Yield(); //receive UI message that end pause OR forceful termination! + //*first* refresh GUI (removing flicker) before sleeping! + boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL)); + } + //if SyncProgressDialogImpl::OnClose() already wxWindow::Destroy() on OS X then this instance is already toast! + if (wereDead) + return; //GTFO and don't call this->resumeTimer() + + resumeTimer(); + } + else + /* + /|\ + | keep this sequence to ensure one full progress update before entering pause mode! + \|/ + */ + wxTheApp->Yield(); //receive UI message that sets pause status OR forceful termination! + } + else + this->Update(); //don't wait until next idle event (who knows what blocking process comes next?) +} + + +template +void SyncProgressDialogImpl::updateDialogStatus() //depends on "syncStat_, paused_, finalResult" +{ + const wxString dlgStatusTxt = getDialogPhaseText(syncStat_, paused_, finalResult); + + pnl.m_staticTextPhase->SetLabel(dlgStatusTxt); + + //status bitmap + if (syncStat_) //sync running + { + auto setStatusBitmap = [&](const wchar_t* bmpName) + { + pnl.m_animCtrlSyncing->Hide(); + pnl.m_bitmapStatus->SetBitmap(getResourceImage(bmpName)); + pnl.m_bitmapStatus->SetToolTip(dlgStatusTxt); + pnl.m_bitmapStatus->Show(); + }; + + if (paused_) + setStatusBitmap(L"status_pause"); + else + switch (syncStat_->currentPhase()) + { + case ProcessCallback::PHASE_NONE: + pnl.m_animCtrlSyncing->Hide(); + pnl.m_bitmapStatus->Hide(); + break; + + case ProcessCallback::PHASE_SCANNING: + setStatusBitmap(L"status_scanning"); + break; + + case ProcessCallback::PHASE_COMPARING_CONTENT: + setStatusBitmap(L"status_binary_compare"); + break; + + case ProcessCallback::PHASE_SYNCHRONIZING: + pnl.m_bitmapStatus->SetBitmap(getResourceImage(L"status_syncing")); + pnl.m_bitmapStatus->SetToolTip(dlgStatusTxt); + pnl.m_bitmapStatus->Show(); + pnl.m_animCtrlSyncing->Show(); + pnl.m_animCtrlSyncing->SetToolTip(dlgStatusTxt); + break; + } + } + else //sync finished + { + auto setStatusBitmap = [&](const wchar_t* bmpName, const std::wstring& tooltip) + { + pnl.m_animCtrlSyncing->Hide(); + pnl.m_bitmapStatus->SetBitmap(getResourceImage(bmpName)); + pnl.m_bitmapStatus->Show(); + pnl.m_bitmapStatus->SetToolTip(tooltip); + }; + switch (finalResult) + { + case RESULT_ABORTED: + setStatusBitmap(L"status_aborted", _("Synchronization stopped")); + break; + + case RESULT_FINISHED_WITH_ERROR: + setStatusBitmap(L"status_finished_errors", _("Synchronization completed with errors")); + break; + + case RESULT_FINISHED_WITH_WARNINGS: + setStatusBitmap(L"status_finished_warnings", _("Synchronization completed with warnings")); + break; + + case RESULT_FINISHED_WITH_SUCCESS: + setStatusBitmap(L"status_finished_success", _("Synchronization completed successfully")); + break; + } + } + + //show status on Windows 7 taskbar + if (taskbar_.get()) + { + if (syncStat_) //sync running + { + if (paused_) + taskbar_->setStatus(Taskbar::STATUS_PAUSED); + else + switch (syncStat_->currentPhase()) + { + case ProcessCallback::PHASE_NONE: + case ProcessCallback::PHASE_SCANNING: + taskbar_->setStatus(Taskbar::STATUS_INDETERMINATE); + break; + + case ProcessCallback::PHASE_COMPARING_CONTENT: + case ProcessCallback::PHASE_SYNCHRONIZING: + taskbar_->setStatus(Taskbar::STATUS_NORMAL); + break; + } + } + else //sync finished + switch (finalResult) + { + case RESULT_ABORTED: + case RESULT_FINISHED_WITH_ERROR: + taskbar_->setStatus(Taskbar::STATUS_ERROR); + break; + + case RESULT_FINISHED_WITH_WARNINGS: + case RESULT_FINISHED_WITH_SUCCESS: + taskbar_->setStatus(Taskbar::STATUS_NORMAL); + break; + } + } + + //pause button + if (syncStat_) //sync running + pnl.m_buttonPause->SetLabel(paused_ ? _("&Continue") : _("&Pause")); + + pnl.Layout(); + this->Refresh(); //a few pixels below the status text need refreshing +} + + +template +void SyncProgressDialogImpl::closeWindowDirectly() //this should really be called: do not call back + schedule deletion +{ + paused_ = false; //you never know? + //ATTENTION: dialog may live a little longer, so watch callbacks! + //e.g. wxGTK calls OnIconize after wxWindow::Close() (better not ask why) and before physical destruction! => indirectly calls updateDialogStatus(), which reads syncStat_!!! + syncStat_ = nullptr; + abortCb_ = nullptr; + //resumeFromSystray(); -> NO, instead ~SyncProgressDialogImpl() makes sure that main dialog is shown again! + + this->Close(); //generate close event: do NOT destroy window unconditionally! +} + + +template +void SyncProgressDialogImpl::processHasFinished(SyncResult resultId, const ErrorLog& log) //essential to call this in StatusHandler derived class destructor +{ + //at the LATEST(!) to prevent access to currentStatusHandler + //enable okay and close events; may be set in this method ONLY + +#if (defined __WXGTK__ || defined __WXOSX__) + //In wxWidgets 2.9.3 upwards, the wxWindow::Reparent() below fails on GTK and OS X if window is frozen! http://forums.codeblocks.org/index.php?topic=13388.45 +#else + wxWindowUpdateLocker dummy(this); //badly needed on Windows +#endif + + paused_ = false; //you never know? + + //update numbers one last time (as if sync were still running) + notifyProgressChange(); //make one last graph entry at the *current* time + updateGuiInt(false); + + switch (syncStat_->currentPhase()) //no matter if paused or not + { + case ProcessCallback::PHASE_NONE: + case ProcessCallback::PHASE_SCANNING: + //set overall speed -> not needed + //items processed -> not needed + break; + + case ProcessCallback::PHASE_COMPARING_CONTENT: + case ProcessCallback::PHASE_SYNCHRONIZING: + { + const int itemsCurrent = syncStat_->getObjectsCurrent(syncStat_->currentPhase()); + const int itemsTotal = syncStat_->getObjectsTotal (syncStat_->currentPhase()); + const Int64 dataCurrent = syncStat_->getDataCurrent (syncStat_->currentPhase()); + const Int64 dataTotal = syncStat_->getDataTotal (syncStat_->currentPhase()); + assert(dataCurrent <= dataTotal); + + //set overall speed (instead of current speed) + const int64_t timeDelta = timeElapsed.timeMs() - phaseStartMs; //we need to consider "time within current phase" not total "timeElapsed"! + + const wxString overallBytesPerSecond = timeDelta == 0 ? wxString() : filesizeToShortString(dataCurrent * 1000 / timeDelta) + _("/sec"); + const wxString overallItemsPerSecond = timeDelta == 0 ? wxString() : replaceCpy(_("%x items/sec"), L"%x", formatThreeDigitPrecision(itemsCurrent * 1000.0 / timeDelta)); + + pnl.m_panelGraphBytes->setAttributes(pnl.m_panelGraphBytes->getAttributes().setCornerText(overallBytesPerSecond, Graph2D::CORNER_TOP_LEFT)); + pnl.m_panelGraphItems->setAttributes(pnl.m_panelGraphItems->getAttributes().setCornerText(overallItemsPerSecond, Graph2D::CORNER_TOP_LEFT)); + + //show new element "items processed" + pnl.m_panelItemsProcessed->Show(); + pnl.m_staticTextProcessedObj ->SetLabel(toGuiString(itemsCurrent)); + pnl.m_staticTextDataProcessed->SetLabel(L"(" + filesizeToShortString(dataCurrent) + L")"); + + //hide remaining elements... + if (itemsCurrent == itemsTotal && //...if everything was processed successfully + dataCurrent == dataTotal) + pnl.m_panelItemsRemaining->Hide(); + } + break; + } + + //------- change class state ------- + finalResult = resultId; + + syncStat_ = nullptr; + abortCb_ = nullptr; + //---------------------------------- + + updateDialogStatus(); + setExternalStatus(getDialogPhaseText(syncStat_, paused_, finalResult), wxString()); + + resumeFromSystray(); //if in tray mode... + + this->EnableCloseButton(true); + + pnl.m_bpButtonMinimizeToTray->Hide(); + pnl.m_buttonStop->Disable(); + pnl.m_buttonStop->Hide(); + pnl.m_buttonPause->Disable(); + pnl.m_buttonPause->Hide(); + pnl.m_buttonClose->Show(); + pnl.m_buttonClose->Enable(); + + pnl.m_buttonClose->SetFocus(); + + pnl.bSizerExecFinished->Show(false); + + //set std order after button visibility was set + setStandardButtonOrder(*pnl.bSizerStdButtons, StdButtons().setAffirmative(pnl.m_buttonClose)); + + //hide current operation status + pnl.bSizerStatusText->Show(false); + + //show and prepare final statistics + pnl.m_notebookResult->Show(); + +#if defined ZEN_WIN || defined ZEN_LINUX + pnl.m_staticlineFooter->Hide(); //win: m_notebookResult already has a window frame +#endif + + //hide remaining time + pnl.m_panelTimeRemaining->Hide(); + + //1. re-arrange graph into results listbook + const bool wasDetached = pnl.bSizerRoot->Detach(pnl.m_panelProgress); + assert(wasDetached); + (void)wasDetached; + pnl.m_panelProgress->Reparent(pnl.m_notebookResult); + pnl.m_notebookResult->AddPage(pnl.m_panelProgress, _("Statistics"), true); + + //2. log file + const size_t posLog = 1; + assert(pnl.m_notebookResult->GetPageCount() == 1); + LogPanel* logPanel = new LogPanel(pnl.m_notebookResult, log); //owned by m_notebookResult + pnl.m_notebookResult->AddPage(logPanel, _("Log"), false); + //bSizerHoldStretch->Insert(0, logPanel, 1, wxEXPAND); + + //show log instead of graph if errors occurred! (not required for ignored warnings) + if (log.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR) > 0) + pnl.m_notebookResult->ChangeSelection(posLog); + + //GetSizer()->SetSizeHints(this); //~=Fit() //not a good idea: will shrink even if window is maximized or was enlarged by the user + pnl.Layout(); + + pnl.m_panelProgress->Layout(); + //small statistics panels: + pnl.m_panelItemsProcessed->Layout(); + pnl.m_panelItemsRemaining->Layout(); + //pnl.m_panelTimeRemaining->Layout(); + //pnl.m_panelTimeElapsed->Layout(); -> needed? + + //play (optional) sound notification after sync has completed -> only play when waiting on results dialog, seems to be pointless otherwise! + switch (finalResult) + { + case SyncProgressDialog::RESULT_ABORTED: + break; + case SyncProgressDialog::RESULT_FINISHED_WITH_ERROR: + case SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS: + case SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS: + { + const Zstring soundFile = getResourceDir() + Zstr("Sync_Complete.wav"); + if (fileExists(soundFile)) + wxSound::Play(utfCvrtTo(soundFile), wxSOUND_ASYNC); //warning: this may fail and show a wxWidgets error message! => must not play when running FFS as a service! + } + break; + } + + //Raise(); -> don't! user may be watching a movie in the meantime ;) note: resumeFromSystray() also calls Raise()! +} + + +template +void SyncProgressDialogImpl::OnOkay(wxCommandEvent& event) +{ + this->Close(); //generate close event: do NOT destroy window unconditionally! +} + + +template +void SyncProgressDialogImpl::OnCancel(wxCommandEvent& event) +{ + paused_ = false; + updateDialogStatus(); //update status + pause button + + //no Layout() or UI-update here to avoid cascaded Yield()-call + if (abortCb_) + abortCb_->requestAbortion(); +} + + +template +void SyncProgressDialogImpl::OnPause(wxCommandEvent& event) +{ + paused_ = !paused_; + updateDialogStatus(); //update status + pause button +} + + +template +void SyncProgressDialogImpl::OnClose(wxCloseEvent& event) +{ + //this event handler may be called *during* sync, e.g. due to a system shutdown (Windows), anytime (OS X) + //try to stop sync gracefully and cross fingers: + if (abortCb_) + abortCb_->requestAbortion(); + //Note: we must NOT veto dialog destruction, else we will cancel system shutdown if this dialog is application main window (like in batch mode) + + notifyWindowTerminate_(); //don't wait until delayed "Destroy()" finally calls destructor -> avoid calls to processHasFinished()/closeWindowDirectly() + + paused_ = false; //[!] we could be pausing here! + + //now that we notified window termination prematurely, and since processHasFinished()/closeWindowDirectly() won't be called, make sure we don't call back, too! + //e.g. the second notifyWindowTerminate_() in ~SyncProgressDialogImpl()!!! + syncStat_ = nullptr; + abortCb_ = nullptr; + + wereDead = true; + this->Destroy(); //wxWidgets OS X: simple "delete"!!!!!!! +} + + +template +void SyncProgressDialogImpl::OnIconize(wxIconizeEvent& event) +{ + /* + propagate progress dialog minimize/maximize to parent + ----------------------------------------------------- + Fedora/Debian/Ubuntu: + - wxDialog cannot be minimized + - worse, wxGTK sends stray iconize events *after* wxDialog::Destroy() + - worse, on Fedora an iconize event is issued directly after calling Close() + - worse, even wxDialog::Hide() causes iconize event! + => nothing to do + SUSE: + - wxDialog can be minimized (it just vanishes!) and in general also minimizes parent: except for our progress wxDialog!!! + - worse, wxDialog::Hide() causes iconize event + - probably the same issues with stray iconize events like Fedora/Debian/Ubuntu + - minimize button is always shown, even if wxMINIMIZE_BOX is omitted! + => nothing to do + Mac OS X: + - wxDialog can be minimized and automatically minimizes parent + - no iconize events seen by wxWidgets! + => nothing to do + Windows: + - wxDialog can be minimized but does not also minimize parent + - iconize events only seen for manual minimize + => propagate event to parent + */ +#ifdef ZEN_WIN + if (parentFrame_) + if (parentFrame_->IsIconized() != event.IsIconized()) //caveat: if window is maximized calling Iconize(false) will erroneously un-maximize! + parentFrame_->Iconize(event.IsIconized()); +#endif + event.Skip(); +} + + +template +void SyncProgressDialogImpl::minimizeToTray() +{ + if (!trayIcon.get()) + { + trayIcon = make_unique([this] { this->resumeFromSystray(); }); //FfsTrayIcon lifetime is a subset of "this"'s lifetime! + //we may destroy FfsTrayIcon even while in the FfsTrayIcon callback!!!! + + updateGuiInt(false); //set tray tooltip + progress: e.g. no updates while paused + + this->Hide(); + if (parentFrame_) + parentFrame_->Hide(); +#ifdef ZEN_MAC + //hide dock icon: else user is able to forcefully show the hidden main dialog by clicking on the icon!! + ProcessSerialNumber psn = { 0, kCurrentProcess }; + ::TransformProcessType(&psn, kProcessTransformToUIElementApplication); +#endif + } +} + + +template +void SyncProgressDialogImpl::resumeFromSystray() +{ + if (trayIcon) + { + trayIcon.reset(); + + if (parentFrame_) + { + //if (parentFrame_->IsIconized()) //caveat: if window is maximized calling Iconize(false) will erroneously un-maximize! + // parentFrame_->Iconize(false); + parentFrame_->Show(); + parentFrame_->Raise(); + } + + //if (IsIconized()) //caveat: if window is maximized calling Iconize(false) will erroneously un-maximize! + // Iconize(false); + this->Show(); + this->Raise(); + this->SetFocus(); + + updateDialogStatus(); //restore Windows 7 task bar status (e.g. required in pause mode) + updateGuiInt(false); //restore Windows 7 task bar progress (e.g. required in pause mode) + +#ifdef ZEN_MAC + ProcessSerialNumber psn = { 0, kCurrentProcess }; + //why isn't this covered by wxWindows::Raise()?? + ::TransformProcessType(&psn, kProcessTransformToForegroundApplication); //show dock icon again + ::SetFrontProcess(&psn); //call before TransformProcessType() so that OSX menu is updated correctly +#endif + } +} + +//######################################################################################## + +SyncProgressDialog* createProgressDialog(zen::AbortCallback& abortCb, + const std::function& notifyWindowTerminate, //note: user closing window cannot be prevented on OS X! (And neither on Windows during system shutdown!) + const zen::Statistics& syncStat, + wxFrame* parentWindow, //may be nullptr + bool showProgress, + const wxString& jobName, + const std::wstring& execWhenFinished, + std::vector& execFinishedHistory) +{ + if (parentWindow) //sync from GUI + return new SyncProgressDialogImpl(wxDEFAULT_DIALOG_STYLE | wxMAXIMIZE_BOX | wxMINIMIZE_BOX | wxRESIZE_BORDER, + [&](wxDialog& progDlg) { return parentWindow; }, abortCb, + notifyWindowTerminate, syncStat, parentWindow, showProgress, jobName, execWhenFinished, execFinishedHistory); + else //FFS batch job + { + auto dlg = new SyncProgressDialogImpl(wxDEFAULT_FRAME_STYLE, + [](wxFrame& progDlg) { return &progDlg; }, abortCb, + notifyWindowTerminate, syncStat, parentWindow, showProgress, jobName, execWhenFinished, execFinishedHistory); + + //only top level windows should have an icon: + dlg->SetIcon(getFfsIcon()); + return dlg; + } +} diff --git a/FreeFileSync/Source/ui/progress_indicator.h b/FreeFileSync/Source/ui/progress_indicator.h new file mode 100644 index 00000000..e2dbb99c --- /dev/null +++ b/FreeFileSync/Source/ui/progress_indicator.h @@ -0,0 +1,92 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef PROGRESSINDICATOR_H_INCLUDED +#define PROGRESSINDICATOR_H_INCLUDED + +#include +#include +//#include +#include +#include "../lib/status_handler.h" +//#include "main_dlg.h" + + +class CompareProgressDialog +{ +public: + CompareProgressDialog(wxFrame& parentWindow); //CompareProgressDialog will be owned by parentWindow! + + wxWindow* getAsWindow(); //convenience! don't abuse! + + void init(const zen::Statistics& syncStat); //begin of sync: make visible, set pointer to "syncStat", initialize all status values + void finalize(); //end of sync: hide again, clear pointer to "syncStat" + + void switchToCompareBytewise(); + void updateStatusPanelNow(); + +private: + class Pimpl; + Pimpl* const pimpl; +}; + + +//SyncStatusHandler will internally process Window messages => disable GUI controls to avoid unexpected callbacks! + +struct SyncProgressDialog +{ + enum SyncResult + { + RESULT_ABORTED, + RESULT_FINISHED_WITH_ERROR, + RESULT_FINISHED_WITH_WARNINGS, + RESULT_FINISHED_WITH_SUCCESS + }; + //essential to call one of these two methods in StatusUpdater derived class' destructor at the LATEST(!) + //to prevent access to callback to updater (e.g. request abort) + virtual void processHasFinished(SyncResult resultId, const zen::ErrorLog& log) = 0; //sync finished, still dialog may live on + virtual void closeWindowDirectly() = 0; //don't wait for user + + //--------------------------------------------------------------------------- + + virtual wxWindow* getWindowIfVisible() = 0; //may be nullptr; don't abuse, use as parent for modal dialogs only! + + virtual void initNewPhase() = 0; //call after "StatusHandler::initNewPhase" + virtual void notifyProgressChange() = 0; //throw (), required by graph! + virtual void updateGui() = 0; //update GUI and process Window messages + + virtual std::wstring getExecWhenFinishedCommand() const = 0; //final value (after possible user modification) + + virtual void stopTimer() = 0; //halt all internal timers! + virtual void resumeTimer() = 0; // + +protected: + ~SyncProgressDialog() {} +}; + + +SyncProgressDialog* createProgressDialog(zen::AbortCallback& abortCb, + const std::function& notifyWindowTerminate, //note: user closing window cannot be prevented on OS X! (And neither on Windows during system shutdown!) + const zen::Statistics& syncStat, + wxFrame* parentWindow, //may be nullptr + bool showProgress, + const wxString& jobName, + const std::wstring& execWhenFinished, + std::vector& execFinishedHistory); //changing parameter! +//DON'T delete the pointer! it will be deleted by the user clicking "OK/Cancel"/wxWindow::Destroy() after processHasFinished() or closeWindowDirectly() + + +class PauseTimers +{ +public: + PauseTimers(SyncProgressDialog& ss) : ss_(ss) { ss_.stopTimer(); } + ~PauseTimers() { ss_.resumeTimer(); } +private: + SyncProgressDialog& ss_; +}; + + +#endif // PROGRESSINDICATOR_H_INCLUDED diff --git a/FreeFileSync/Source/ui/search.cpp b/FreeFileSync/Source/ui/search.cpp new file mode 100644 index 00000000..416d2c9e --- /dev/null +++ b/FreeFileSync/Source/ui/search.cpp @@ -0,0 +1,99 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "search.h" +#include + + +using namespace zen; + +namespace +{ +template +class ContainsMatch +{ +public: + ContainsMatch(const wxString& textToFind) : textToFind_(textToFind) {} + bool operator()(const wxString& phrase) const { return contains(phrase, textToFind_); } + +private: + wxString textToFind_; +}; + + +template <> +class ContainsMatch +{ +public: + ContainsMatch(const wxString& textToFind) : textToFind_(textToFind) { textToFind_.MakeUpper(); } + bool operator()(wxString&& phrase) const + { + //wxWidgets::MakeUpper() is inefficient! But performance is not THAT important for this high-level search functionality + phrase.MakeUpper(); + return contains(phrase, textToFind_); + } + +private: + wxString textToFind_; +}; + +//########################################################################################### + +template +ptrdiff_t findRow(const Grid& grid, //return -1 if no matching row found + const wxString& searchString, + size_t rowFirst, //specify area to search: + size_t rowLast) // [rowFirst, rowLast) +{ + if (auto prov = grid.getDataProvider()) + { + std::vector colAttr = grid.getColumnConfig(); + vector_remove_if(colAttr, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); + if (!colAttr.empty()) + { + const ContainsMatch containsMatch(searchString); + + for (size_t row = rowFirst; row < rowLast; ++row) + for (auto iterCol = colAttr.begin(); iterCol != colAttr.end(); ++iterCol) + if (containsMatch(prov->getValue(row, iterCol->type_))) + return row; + } + } + return -1; +} +} + + +std::pair zen::findGridMatch(const Grid& grid1, const Grid& grid2, const wxString& searchString, bool respectCase) +{ + const size_t rowCountL = grid1.getRowCount(); + const size_t rowCountR = grid2.getRowCount(); + + std::pair result(nullptr, -1); + + size_t cursorRowL = grid1.getGridCursor(); + if (cursorRowL >= rowCountL) + cursorRowL = 0; + { + auto finishSearch = [&](const Grid& grid, size_t rowFirst, size_t rowLast) -> bool + { + const ptrdiff_t targetRow = respectCase ? + findRow( grid, searchString, rowFirst, rowLast) : + findRow(grid, searchString, rowFirst, rowLast); + if (targetRow >= 0) + { + result = std::make_pair(&grid, targetRow); + return true; + } + return false; + }; + + if (!finishSearch(grid1, cursorRowL + 1, rowCountL)) + if (!finishSearch(grid2, 0, rowCountR)) + finishSearch(grid1, 0, cursorRowL + 1); + } + return result; +} \ No newline at end of file diff --git a/FreeFileSync/Source/ui/search.h b/FreeFileSync/Source/ui/search.h new file mode 100644 index 00000000..1adf3d01 --- /dev/null +++ b/FreeFileSync/Source/ui/search.h @@ -0,0 +1,18 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef SEARCH_H_423905762345342526587 +#define SEARCH_H_423905762345342526587 + +#include + +namespace zen +{ +std::pair findGridMatch(const Grid& grid1, const Grid& grid2, const wxString& searchString, bool respectCase); +//returns (grid/row) where the value was found, (nullptr, -1) if not found +} + +#endif //SEARCH_H_423905762345342526587 diff --git a/FreeFileSync/Source/ui/small_dlgs.cpp b/FreeFileSync/Source/ui/small_dlgs.cpp new file mode 100644 index 00000000..785f558d --- /dev/null +++ b/FreeFileSync/Source/ui/small_dlgs.cpp @@ -0,0 +1,1044 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "small_dlgs.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gui_generated.h" +#include "custom_grid.h" +#include "../algorithm.h" +#include "../synchronization.h" +#include "../lib/help_provider.h" +#include "../lib/hard_filter.h" +#include "../version/version.h" + +using namespace zen; + + +class AboutDlg : public AboutDlgGenerated +{ +public: + AboutDlg(wxWindow* parent); + +private: + virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + virtual void OnOK (wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_OKAY); } + virtual void OnDonate(wxCommandEvent& event) { wxLaunchDefaultBrowser(L"http://freefilesync.sourceforge.net/donate.php"); } +}; + + +AboutDlg::AboutDlg(wxWindow* parent) : AboutDlgGenerated(parent) +{ + setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonClose)); + + setRelativeFontSize(*m_buttonDonate, 1.25); + + assert(m_buttonClose->GetId() == wxID_OK); //we cannot use wxID_CLOSE else Esc key won't work: yet another wxWidgets bug?? + + m_bitmap9 ->SetBitmap(getResourceImage(L"website")); + m_bitmap10->SetBitmap(getResourceImage(L"email")); + m_bitmap13->SetBitmap(getResourceImage(L"gpl")); + //m_bitmapSmiley->SetBitmap(getResourceImage(L"smiley")); + + m_animCtrlWink->SetAnimation(getResourceAnimation(L"wink")); + m_animCtrlWink->Play(); + + //create language credits + for (const ExistingTranslations::Entry& trans : ExistingTranslations::get()) + { + //flag + wxStaticBitmap* staticBitmapFlag = new wxStaticBitmap(m_scrolledWindowTranslators, wxID_ANY, getResourceImage(trans.languageFlag), wxDefaultPosition, wxSize(-1, 11), 0 ); + fgSizerTranslators->Add(staticBitmapFlag, 0, wxALIGN_CENTER); + + //translator name + wxStaticText* staticTextTranslator = new wxStaticText(m_scrolledWindowTranslators, wxID_ANY, trans.translatorName, wxDefaultPosition, wxDefaultSize, 0 ); + staticTextTranslator->Wrap(-1); + fgSizerTranslators->Add(staticTextTranslator, 0, wxALIGN_CENTER_VERTICAL); + + staticBitmapFlag ->SetToolTip(trans.languageName); + staticTextTranslator->SetToolTip(trans.languageName); + } + fgSizerTranslators->Fit(m_scrolledWindowTranslators); + +#ifdef ZEN_WIN + new zen::MouseMoveWindow(*this); //-> put *after* creating credits +#endif + + //build information + wxString build = __TDATE__; + build += L" - Unicode"; +#ifndef wxUSE_UNICODE +#error what is going on? +#endif + + build += zen::is64BitBuild ? L" x64" : L" x86"; + assert_static(zen::is32BitBuild || zen::is64BitBuild); + + GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() + + //generate logo: put *after* first Fit() + Layout(); //make sure m_panelLogo has final width (required by wxGTK) + + wxBitmap bmpLogo; + { + wxImage tmp = getResourceImage(L"logo").ConvertToImage(); + tmp.Resize(wxSize(GetClientSize().GetWidth(), tmp.GetHeight()), wxPoint(0, 0), 255, 255, 255); //enlarge to fit full width + bmpLogo = wxBitmap(tmp); + } + { + wxMemoryDC dc(bmpLogo); + + wxImage labelImage = createImageFromText(wxString(L"FreeFileSync ") + zen::currentVersion, + wxFont(wxNORMAL_FONT->GetPointSize() * 1.8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, L"Tahoma"), + *wxBLACK); //accessibility: align foreground/background colors! + wxImage buildImage = createImageFromText(replaceCpy(_("Build: %x"), L"%x", build), + *wxNORMAL_FONT, + *wxBLACK); + wxImage dynImage = stackImages(labelImage, buildImage, ImageStackLayout::VERTICAL, ImageStackAlignment::CENTER, 0); + + dc.DrawBitmap(dynImage, wxPoint((bmpLogo.GetWidth () - dynImage.GetWidth ()) / 2, + (bmpLogo.GetHeight() - dynImage.GetHeight()) / 2)); + } + m_bitmapLogo->SetBitmap(bmpLogo); + + GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() + //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! + + m_buttonClose->SetFocus(); //on GTK ESC is only associated with wxID_OK correctly if we set at least *any* focus at all!!! +} + + +void zen::showAboutDialog(wxWindow* parent) +{ + AboutDlg aboutDlg(parent); + aboutDlg.ShowModal(); +} + +//######################################################################################## + +class FilterDlg : public FilterDlgGenerated +{ +public: + FilterDlg(wxWindow* parent, + FilterConfig& filter, + const wxString& title); + ~FilterDlg() {} + +private: + virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + virtual void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + virtual void OnHelpShowExamples(wxHyperlinkEvent& event) { displayHelpEntry(L"html/Exclude Items.html", this); } + virtual void OnClear (wxCommandEvent& event); + virtual void OnOkay (wxCommandEvent& event); + virtual void OnUpdateChoice(wxCommandEvent& event) { updateGui(); } + virtual void OnUpdateNameFilter(wxCommandEvent& event) { updateGui(); } + + void updateGui(); + void setFilter(const FilterConfig& filter); + FilterConfig getFilter() const; + void onKeyEvent(wxKeyEvent& event); + + FilterConfig& outputRef; + + EnumDescrList enumTimeDescr; + EnumDescrList enumSizeDescr; +}; + + +FilterDlg::FilterDlg(wxWindow* parent, + FilterConfig& filter, + const wxString& title) : + FilterDlgGenerated(parent), + outputRef(filter) //just hold reference +{ +#ifdef ZEN_WIN + new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" +#endif + setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonOk).setCancel(m_buttonCancel)); + + SetTitle(title); + +#ifndef __WXGTK__ //wxWidgets holds portability promise by not supporting for multi-line controls...not + m_textCtrlInclude->SetMaxLength(0); //allow large filter entries! + m_textCtrlExclude->SetMaxLength(0); // +#endif + + m_textCtrlInclude->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(FilterDlg::onKeyEvent), nullptr, this); + m_textCtrlExclude->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(FilterDlg::onKeyEvent), nullptr, this); + + enumTimeDescr. + add(UTIME_NONE, L"(" + _("None") + L")"). //meta options should be enclosed in parentheses + add(UTIME_TODAY, _("Today")). + // add(UTIME_THIS_WEEK, _("This week")). + add(UTIME_THIS_MONTH, _("This month")). + add(UTIME_THIS_YEAR, _("This year")). + add(UTIME_LAST_X_DAYS, _("Last x days")); + + enumSizeDescr. + add(USIZE_NONE, L"(" + _("None") + L")"). //meta options should be enclosed in parentheses + add(USIZE_BYTE, _("Byte")). + add(USIZE_KB, _("KB")). + add(USIZE_MB, _("MB")); + + m_bitmapFilter->SetBitmap(getResourceImage(L"filter")); + + setFilter(filter); + + m_buttonOk->SetFocus(); + + GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() + //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! + + Layout(); +} + + +void FilterDlg::onKeyEvent(wxKeyEvent& event) +{ + const int keyCode = event.GetKeyCode(); + + if (event.ControlDown()) + switch (keyCode) + { + case 'A': //CTRL + A + if (auto textCtrl = dynamic_cast(event.GetEventObject())) + textCtrl->SetSelection(-1, -1); //select all + return; + } + event.Skip(); +} + + +void FilterDlg::updateGui() +{ + FilterConfig activeCfg = getFilter(); + + auto setStatusBitmap = [&](wxStaticBitmap& staticBmp, const wxString& bmpName, bool active) + { + if (active) + staticBmp.SetBitmap(getResourceImage(bmpName)); + else + staticBmp.SetBitmap(greyScale(getResourceImage(bmpName))); + }; + setStatusBitmap(*m_bitmapInclude, L"filter_include", !NameFilter::isNull(activeCfg.includeFilter, FilterConfig().excludeFilter)); + setStatusBitmap(*m_bitmapExclude, L"filter_exclude", !NameFilter::isNull(FilterConfig().includeFilter, activeCfg.excludeFilter)); + setStatusBitmap(*m_bitmapFilterDate, L"clock", activeCfg.unitTimeSpan != UTIME_NONE); + setStatusBitmap(*m_bitmapFilterSize, L"size", activeCfg.unitSizeMin != USIZE_NONE || activeCfg.unitSizeMax != USIZE_NONE); + + m_spinCtrlTimespan->Enable(activeCfg.unitTimeSpan == UTIME_LAST_X_DAYS); + m_spinCtrlMinSize ->Enable(activeCfg.unitSizeMin != USIZE_NONE); + m_spinCtrlMaxSize ->Enable(activeCfg.unitSizeMax != USIZE_NONE); + + m_buttonClear->Enable(!(activeCfg == FilterConfig())); +} + + +void FilterDlg::setFilter(const FilterConfig& filter) +{ + m_textCtrlInclude->ChangeValue(utfCvrtTo(filter.includeFilter)); + m_textCtrlExclude->ChangeValue(utfCvrtTo(filter.excludeFilter)); + + setEnumVal(enumTimeDescr, *m_choiceUnitTimespan, filter.unitTimeSpan); + setEnumVal(enumSizeDescr, *m_choiceUnitMinSize, filter.unitSizeMin); + setEnumVal(enumSizeDescr, *m_choiceUnitMaxSize, filter.unitSizeMax); + + m_spinCtrlTimespan->SetValue(static_cast(filter.timeSpan)); + m_spinCtrlMinSize ->SetValue(static_cast(filter.sizeMin)); + m_spinCtrlMaxSize ->SetValue(static_cast(filter.sizeMax)); + + updateGui(); +} + + +FilterConfig FilterDlg::getFilter() const +{ + return FilterConfig(utfCvrtTo(m_textCtrlInclude->GetValue()), + utfCvrtTo(m_textCtrlExclude->GetValue()), + m_spinCtrlTimespan->GetValue(), + getEnumVal(enumTimeDescr, *m_choiceUnitTimespan), + m_spinCtrlMinSize->GetValue(), + getEnumVal(enumSizeDescr, *m_choiceUnitMinSize), + m_spinCtrlMaxSize->GetValue(), + getEnumVal(enumSizeDescr, *m_choiceUnitMaxSize)); +} + + +void FilterDlg::OnClear(wxCommandEvent& event) +{ + setFilter(FilterConfig()); +} + + +void FilterDlg::OnOkay(wxCommandEvent& event) +{ + FilterConfig cfg = getFilter(); + + //parameter validation: + + //include filter must not be empty: + { + Zstring tmp = cfg.includeFilter; + trim(tmp); + if (tmp.empty()) + cfg.includeFilter = FilterConfig().includeFilter; //no need to show error message, just correct user input + } + + //apply config: + outputRef = cfg; + + //when leaving dialog: filter and redraw grid, if filter is active + EndModal(ReturnSmallDlg::BUTTON_OKAY); +} + + +ReturnSmallDlg::ButtonPressed zen::showFilterDialog(wxWindow* parent, FilterConfig& filter, const wxString& title) +{ + FilterDlg filterDlg(parent, + filter, + title); + return static_cast(filterDlg.ShowModal()); +} + +//######################################################################################## + +class DeleteDialog : public DeleteDlgGenerated +{ +public: + DeleteDialog(wxWindow* parent, + const std::vector& rowsOnLeft, + const std::vector& rowsOnRight, + bool& deleteOnBothSides, + bool& useRecycleBin); + +private: + virtual void OnOK(wxCommandEvent& event); + virtual void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + virtual void OnDelOnBothSides(wxCommandEvent& event); + virtual void OnUseRecycler(wxCommandEvent& event); + + void updateGui(); + + const std::vector& rowsToDeleteOnLeft; + const std::vector& rowsToDeleteOnRight; + bool& outRefdeleteOnBothSides; + bool& outRefuseRecycleBin; + const TickVal tickCountStartup; +}; + + +DeleteDialog::DeleteDialog(wxWindow* parent, + const std::vector& rowsOnLeft, + const std::vector& rowsOnRight, + bool& deleteOnBothSides, + bool& useRecycleBin) : + DeleteDlgGenerated(parent), + rowsToDeleteOnLeft(rowsOnLeft), + rowsToDeleteOnRight(rowsOnRight), + outRefdeleteOnBothSides(deleteOnBothSides), + outRefuseRecycleBin(useRecycleBin), + tickCountStartup(getTicks()) +{ +#ifdef ZEN_WIN + new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" +#endif + setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonOK).setCancel(m_buttonCancel)); + + setMainInstructionFont(*m_staticTextHeader); + + m_checkBoxDeleteBothSides->SetValue(deleteOnBothSides); + m_checkBoxUseRecycler->SetValue(useRecycleBin); + + //if both sides contain same rows this checkbox is superfluous + if (rowsToDeleteOnLeft == rowsToDeleteOnRight) + { + m_checkBoxDeleteBothSides->Show(false); + m_checkBoxDeleteBothSides->SetValue(true); + } + +#ifndef __WXGTK__ //wxWidgets holds portability promise by not supporting for multi-line controls...not + m_textCtrlFileList->SetMaxLength(0); //allow large entries! +#endif + + updateGui(); + + GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() + //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! + + Layout(); + + m_buttonOK->SetFocus(); +} + + +void DeleteDialog::updateGui() +{ +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! +#endif + + const std::pair delInfo = zen::deleteFromGridAndHDPreview(rowsToDeleteOnLeft, + rowsToDeleteOnRight, + m_checkBoxDeleteBothSides->GetValue()); + wxString header; + if (m_checkBoxUseRecycler->GetValue()) + { + header = _P("Do you really want to move the following item to the recycle bin?", + "Do you really want to move the following %x items to the recycle bin?", delInfo.second); + m_bitmapDeleteType->SetBitmap(getResourceImage(L"delete_recycler")); + m_buttonOK->SetLabel(_("Move")); //no access key needed: use ENTER! + } + else + { + header = _P("Do you really want to delete the following item?", + "Do you really want to delete the following %x items?", delInfo.second); + m_bitmapDeleteType->SetBitmap(getResourceImage(L"delete_permanently")); + m_buttonOK->SetLabel(_("Delete")); + } + m_staticTextHeader->SetLabel(header); + //it seems like Wrap() needs to be reapplied after SetLabel() + m_staticTextHeader->Wrap(460); + + const wxString& fileList = utfCvrtTo(delInfo.first); + m_textCtrlFileList->ChangeValue(fileList); + /* + There is a nasty bug on wxGTK under Ubuntu: If a multi-line wxTextCtrl contains so many lines that scrollbars are shown, + it re-enables all windows that are supposed to be disabled during the current modal loop! + This only affects Ubuntu/wxGTK! No such issue on Debian/wxGTK or Suse/wxGTK + => another Unity problem like the following? + http://trac.wxwidgets.org/ticket/14823 "Menu not disabled when showing modal dialogs in wxGTK under Unity" + */ + + Layout(); + Refresh(); //needed after m_buttonOK label change +} + + +void DeleteDialog::OnOK(wxCommandEvent& event) +{ + //additional safety net, similar to Windows Explorer: time delta between DEL and ENTER must be at least 50ms to avoid accidental deletion! + const TickVal now = getTicks(); //0 on error + std::int64_t tps = ticksPerSec(); // + if (now.isValid() && tickCountStartup.isValid() && tps != 0) + if (dist(tickCountStartup, now) * 1000 / tps < 50) + return; + + outRefuseRecycleBin = m_checkBoxUseRecycler->GetValue(); + if (rowsToDeleteOnLeft != rowsToDeleteOnRight) + outRefdeleteOnBothSides = m_checkBoxDeleteBothSides->GetValue(); + + EndModal(ReturnSmallDlg::BUTTON_OKAY); +} + +void DeleteDialog::OnDelOnBothSides(wxCommandEvent& event) +{ + updateGui(); +} + +void DeleteDialog::OnUseRecycler(wxCommandEvent& event) +{ + updateGui(); +} + + +ReturnSmallDlg::ButtonPressed zen::showDeleteDialog(wxWindow* parent, + const std::vector& rowsOnLeft, + const std::vector& rowsOnRight, + bool& deleteOnBothSides, + bool& useRecycleBin) +{ + DeleteDialog confirmDeletion(parent, + rowsOnLeft, + rowsOnRight, + deleteOnBothSides, + useRecycleBin); + return static_cast(confirmDeletion.ShowModal()); +} + +//######################################################################################## + +class SyncConfirmationDlg : public SyncConfirmationDlgGenerated +{ +public: + SyncConfirmationDlg(wxWindow* parent, + const wxString& variantName, + const zen::SyncStatistics& st, + bool& dontShowAgain); +private: + virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + virtual void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + virtual void OnStartSync(wxCommandEvent& event); + + bool& m_dontShowAgain; +}; + + +SyncConfirmationDlg::SyncConfirmationDlg(wxWindow* parent, + const wxString& variantName, + const SyncStatistics& st, + bool& dontShowAgain) : + SyncConfirmationDlgGenerated(parent), + m_dontShowAgain(dontShowAgain) +{ +#ifdef ZEN_WIN + new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" +#endif + setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonStartSync).setCancel(m_buttonCancel)); + + setMainInstructionFont(*m_staticTextHeader); + m_bitmapSync->SetBitmap(getResourceImage(L"sync")); + + m_staticTextVariant->SetLabel(variantName); + m_checkBoxDontShowAgain->SetValue(dontShowAgain); + + //update preview of item count and bytes to be transferred: + setText(*m_staticTextData, filesizeToShortString(st.getDataToProcess())); + if (st.getDataToProcess() == 0) + m_bitmapData->SetBitmap(greyScale(getResourceImage(L"data"))); + else + m_bitmapData->SetBitmap(getResourceImage(L"data")); + + auto setValue = [](wxStaticText& txtControl, int value, wxStaticBitmap& bmpControl, const wchar_t* bmpName) + { + setText(txtControl, toGuiString(value)); + + if (value == 0) + bmpControl.SetBitmap(greyScale(mirrorIfRtl(getResourceImage(bmpName)))); + else + bmpControl.SetBitmap(mirrorIfRtl(getResourceImage(bmpName))); + }; + + setValue(*m_staticTextCreateLeft, st.getCreate(), *m_bitmapCreateLeft, L"so_create_left_small"); + setValue(*m_staticTextUpdateLeft, st.getUpdate(), *m_bitmapUpdateLeft, L"so_update_left_small"); + setValue(*m_staticTextDeleteLeft, st.getDelete(), *m_bitmapDeleteLeft, L"so_delete_left_small"); + setValue(*m_staticTextCreateRight, st.getCreate(), *m_bitmapCreateRight, L"so_create_right_small"); + setValue(*m_staticTextUpdateRight, st.getUpdate(), *m_bitmapUpdateRight, L"so_update_right_small"); + setValue(*m_staticTextDeleteRight, st.getDelete(), *m_bitmapDeleteRight, L"so_delete_right_small"); + + m_panelStatistics->Layout(); + + GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() + //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! + + m_buttonStartSync->SetFocus(); +} + + +void SyncConfirmationDlg::OnStartSync(wxCommandEvent& event) +{ + m_dontShowAgain = m_checkBoxDontShowAgain->GetValue(); + EndModal(ReturnSmallDlg::BUTTON_OKAY); +} + + +ReturnSmallDlg::ButtonPressed zen::showSyncConfirmationDlg(wxWindow* parent, + const wxString& variantName, + const zen::SyncStatistics& statistics, + bool& dontShowAgain) +{ + SyncConfirmationDlg dlg(parent, + variantName, + statistics, + dontShowAgain); + return static_cast(dlg.ShowModal()); +} + +//######################################################################################## + +class CompareCfgDialog : public CmpCfgDlgGenerated +{ +public: + CompareCfgDialog(wxWindow* parent, CompConfig& cmpConfig, const wxString& caption); + +private: + virtual void OnOkay(wxCommandEvent& event); + virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + virtual void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + virtual void OnHelpComparisonSettings(wxHyperlinkEvent& event) { displayHelpEntry(L"html/Comparison Settings.html", this); } + + virtual void OnTimeSize(wxCommandEvent& event) { compareVar = CMP_BY_TIME_SIZE; updateGui(); } + virtual void OnContent (wxCommandEvent& event) { compareVar = CMP_BY_CONTENT; updateGui(); } + + virtual void OnTimeSizeDouble(wxMouseEvent& event); + virtual void OnContentDouble(wxMouseEvent& event); + + void updateGui(); + + CompConfig& cmpConfigOut; //for output only + CompareVariant compareVar; + zen::EnumDescrList enumDescrHandleSyml; +}; + + +CompareCfgDialog::CompareCfgDialog(wxWindow* parent, + CompConfig& cmpConfig, const wxString& title) : + CmpCfgDlgGenerated(parent), + cmpConfigOut(cmpConfig), + compareVar(cmpConfig.compareVar) +{ +#ifdef ZEN_WIN + new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" +#endif + setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonOkay).setCancel(m_buttonCancel)); + + setRelativeFontSize(*m_toggleBtnTimeSize, 1.25); + setRelativeFontSize(*m_toggleBtnContent, 1.25); + + SetTitle(title); + + enumDescrHandleSyml. + add(SYMLINK_EXCLUDE, _("Exclude")). + add(SYMLINK_USE_DIRECTLY, _("Direct")). + add(SYMLINK_FOLLOW_LINK, _("Follow")); + + setEnumVal(enumDescrHandleSyml, *m_choiceHandleSymlinks, cmpConfig.handleSymlinks); + + //move dialog up so that compare-config button and first config-variant are on same level + // Move(wxPoint(position.x, std::max(0, position.y - (m_buttonTimeSize->GetScreenPosition() - GetScreenPosition()).y))); + + updateGui(); + + GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() + //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! + + m_buttonOkay->SetFocus(); +} + + +void CompareCfgDialog::updateGui() +{ + //update toggle buttons -> they have no parameter-ownership at all! + m_toggleBtnTimeSize->SetValue(false); + m_toggleBtnContent ->SetValue(false); + + switch (compareVar) + { + case CMP_BY_TIME_SIZE: + m_toggleBtnTimeSize->SetValue(true); + break; + case CMP_BY_CONTENT: + m_toggleBtnContent->SetValue(true); + break; + } + + auto setBitmap = [](wxStaticBitmap& bmpCtrl, bool active, const wxBitmap& bmp) + { + if (active) + bmpCtrl.SetBitmap(bmp); + else + bmpCtrl.SetBitmap(greyScale(bmp)); + }; + setBitmap(*m_bitmapByTime, compareVar == CMP_BY_TIME_SIZE, getResourceImage(L"clock")); + setBitmap(*m_bitmapByContent, compareVar == CMP_BY_CONTENT, getResourceImage(L"cmpByContent")); +} + + +void CompareCfgDialog::OnOkay(wxCommandEvent& event) +{ + cmpConfigOut.compareVar = compareVar; + cmpConfigOut.handleSymlinks = getEnumVal(enumDescrHandleSyml, *m_choiceHandleSymlinks); + + EndModal(ReturnSmallDlg::BUTTON_OKAY); +} + + +void CompareCfgDialog::OnTimeSizeDouble(wxMouseEvent& event) +{ + wxCommandEvent dummy; + OnTimeSize(dummy); + OnOkay(dummy); +} + + +void CompareCfgDialog::OnContentDouble(wxMouseEvent& event) +{ + wxCommandEvent dummy; + OnContent(dummy); + OnOkay(dummy); +} + + +ReturnSmallDlg::ButtonPressed zen::showCompareCfgDialog(wxWindow* parent, CompConfig& cmpConfig, const wxString& title) +{ + CompareCfgDialog syncDlg(parent, cmpConfig, title); + return static_cast(syncDlg.ShowModal()); +} + +//######################################################################################## + +class GlobalSettingsDlg : public GlobalSettingsDlgGenerated +{ +public: + GlobalSettingsDlg(wxWindow* parent, xmlAccess::XmlGlobalSettings& globalSettings); + +private: + virtual void OnOkay(wxCommandEvent& event); + virtual void OnResetDialogs(wxCommandEvent& event); + virtual void OnDefault(wxCommandEvent& event); + virtual void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + virtual void OnAddRow(wxCommandEvent& event); + virtual void OnRemoveRow(wxCommandEvent& event); + virtual void OnHelpShowExamples(wxHyperlinkEvent& event) { displayHelpEntry(L"html/External Applications.html", this); } + void onResize(wxSizeEvent& event); + void updateGui(); + + virtual void OnToggleAutoRetryCount(wxCommandEvent& event) { updateGui(); } + + void setExtApp(const xmlAccess::ExternalApps& extApp); + xmlAccess::ExternalApps getExtApp(); + + xmlAccess::XmlGlobalSettings& settings; +}; + + +GlobalSettingsDlg::GlobalSettingsDlg(wxWindow* parent, xmlAccess::XmlGlobalSettings& globalSettings) : + GlobalSettingsDlgGenerated(parent), + settings(globalSettings) +{ +#ifdef ZEN_WIN + new zen::MouseMoveWindow(*this); //allow moving dialog by clicking (nearly) anywhere...; ownership passed to "this" +#endif + setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonOkay).setCancel(m_buttonCancel)); + + //setMainInstructionFont(*m_staticTextHeader); + + m_bitmapSettings ->SetBitmap (getResourceImage(L"settings")); + m_bpButtonAddRow ->SetBitmapLabel(getResourceImage(L"item_add")); + m_bpButtonRemoveRow ->SetBitmapLabel(getResourceImage(L"item_remove")); + setBitmapTextLabel(*m_buttonResetDialogs, getResourceImage(L"reset_dialogs").ConvertToImage(), m_buttonResetDialogs->GetLabel()); + + m_checkBoxFailSafe ->SetValue(globalSettings.failsafeFileCopy); + m_checkBoxCopyLocked ->SetValue(globalSettings.copyLockedFiles); + m_checkBoxCopyPermissions->SetValue(globalSettings.copyFilePermissions); + + m_spinCtrlAutoRetryCount->SetValue(globalSettings.automaticRetryCount); + m_spinCtrlAutoRetryDelay->SetValue(globalSettings.automaticRetryDelay); + + setExtApp(globalSettings.gui.externelApplications); + + updateGui(); + +#ifdef ZEN_WIN + m_checkBoxCopyPermissions->SetLabel(_("Copy NTFS permissions")); +#elif defined ZEN_LINUX || defined ZEN_MAC + bSizerLockedFiles->Show(false); +#endif + + const wxString toolTip = wxString(_("Integrate external applications into context menu. The following macros are available:")) + L"\n\n" + + L"%item_path% \t" + _("- full file or folder name") + L"\n" + + L"%item_folder% \t" + _("- folder part only") + L"\n" + + L"%item2_path% \t" + _("- Other side's counterpart to %item_path%") + L"\n" + + L"%item2_folder% \t" + _("- Other side's counterpart to %item_folder%"); + + m_gridCustomCommand->GetGridWindow()->SetToolTip(toolTip); + m_gridCustomCommand->GetGridColLabelWindow()->SetToolTip(toolTip); + m_gridCustomCommand->SetMargins(0, 0); + + GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() + //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! + + Layout(); + + //automatically fit column width to match total grid width + Connect(wxEVT_SIZE, wxSizeEventHandler(GlobalSettingsDlg::onResize), nullptr, this); + wxSizeEvent dummy; + onResize(dummy); + + m_buttonOkay->SetFocus(); +} + + +void GlobalSettingsDlg::onResize(wxSizeEvent& event) +{ + const int widthTotal = m_gridCustomCommand->GetGridWindow()->GetClientSize().GetWidth(); + + if (widthTotal >= 0 && m_gridCustomCommand->GetNumberCols() == 2) + { + const int w0 = widthTotal * 2 / 5; //ratio 2 : 3 + const int w1 = widthTotal - w0; + m_gridCustomCommand->SetColSize(0, w0); + m_gridCustomCommand->SetColSize(1, w1); + + m_gridCustomCommand->Refresh(); //required on Ubuntu + } + + event.Skip(); +} + + +void GlobalSettingsDlg::updateGui() +{ + const bool autoRetryActive = m_spinCtrlAutoRetryCount->GetValue() > 0; + m_staticTextAutoRetryDelay->Enable(autoRetryActive); + m_spinCtrlAutoRetryDelay->Enable(autoRetryActive); +} + + +void GlobalSettingsDlg::OnOkay(wxCommandEvent& event) +{ + //write global settings only when okay-button is pressed! + settings.failsafeFileCopy = m_checkBoxFailSafe->GetValue(); + settings.copyLockedFiles = m_checkBoxCopyLocked->GetValue(); + settings.copyFilePermissions = m_checkBoxCopyPermissions->GetValue(); + + settings.automaticRetryCount = m_spinCtrlAutoRetryCount->GetValue(); + settings.automaticRetryDelay = m_spinCtrlAutoRetryDelay->GetValue(); + + settings.gui.externelApplications = getExtApp(); + + EndModal(ReturnSmallDlg::BUTTON_OKAY); +} + + +void GlobalSettingsDlg::OnResetDialogs(wxCommandEvent& event) +{ + switch (showConfirmationDialog(this, DialogInfoType::INFO, + PopupDialogCfg().setMainInstructions(_("Restore all hidden windows and warnings?")), + _("&Restore"))) + { + case ConfirmationButton::DO_IT: + settings.optDialogs.resetDialogs(); + break; + case ConfirmationButton::CANCEL: + break; + } +} + + +void GlobalSettingsDlg::OnDefault(wxCommandEvent& event) +{ + const xmlAccess::XmlGlobalSettings defaultCfg; + + m_checkBoxFailSafe ->SetValue(defaultCfg.failsafeFileCopy); + m_checkBoxCopyLocked ->SetValue(defaultCfg.copyLockedFiles); + m_checkBoxCopyPermissions->SetValue(defaultCfg.copyFilePermissions); + + m_spinCtrlAutoRetryCount->SetValue(defaultCfg.automaticRetryCount); + m_spinCtrlAutoRetryDelay->SetValue(defaultCfg.automaticRetryDelay); + + setExtApp(defaultCfg.gui.externelApplications); + + updateGui(); +} + + +void GlobalSettingsDlg::setExtApp(const xmlAccess::ExternalApps& extApp) +{ + auto extAppTmp = extApp; + vector_remove_if(extAppTmp, [](decltype(extAppTmp[0])& entry) { return entry.first.empty() && entry.second.empty(); }); + + extAppTmp.resize(extAppTmp.size() + 1); //append empty row to facilitate insertions + + const int rowCount = m_gridCustomCommand->GetNumberRows(); + if (rowCount > 0) + m_gridCustomCommand->DeleteRows(0, rowCount); + + m_gridCustomCommand->AppendRows(static_cast(extAppTmp.size())); + for (auto it = extAppTmp.begin(); it != extAppTmp.end(); ++it) + { + const int row = it - extAppTmp.begin(); + m_gridCustomCommand->SetCellValue(row, 0, it->first); //description + m_gridCustomCommand->SetCellValue(row, 1, it->second); //commandline + } +} + + +xmlAccess::ExternalApps GlobalSettingsDlg::getExtApp() +{ + xmlAccess::ExternalApps output; + for (int i = 0; i < m_gridCustomCommand->GetNumberRows(); ++i) + { + auto description = copyStringTo(m_gridCustomCommand->GetCellValue(i, 0)); + auto commandline = copyStringTo(m_gridCustomCommand->GetCellValue(i, 1)); + + if (!description.empty() || !commandline.empty()) + output.push_back(std::make_pair(description, commandline)); + } + return output; +} + + +void GlobalSettingsDlg::OnAddRow(wxCommandEvent& event) +{ +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! +#endif + + const int selectedRow = m_gridCustomCommand->GetGridCursorRow(); + if (0 <= selectedRow && selectedRow < m_gridCustomCommand->GetNumberRows()) + m_gridCustomCommand->InsertRows(selectedRow); + else + m_gridCustomCommand->AppendRows(); +} + + +void GlobalSettingsDlg::OnRemoveRow(wxCommandEvent& event) +{ + if (m_gridCustomCommand->GetNumberRows() > 0) + { +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! +#endif + + const int selectedRow = m_gridCustomCommand->GetGridCursorRow(); + if (0 <= selectedRow && selectedRow < m_gridCustomCommand->GetNumberRows()) + m_gridCustomCommand->DeleteRows(selectedRow); + else + m_gridCustomCommand->DeleteRows(m_gridCustomCommand->GetNumberRows() - 1); + } +} + + +ReturnSmallDlg::ButtonPressed zen::showGlobalSettingsDlg(wxWindow* parent, xmlAccess::XmlGlobalSettings& globalSettings) +{ + GlobalSettingsDlg settingsDlg(parent, globalSettings); + return static_cast(settingsDlg.ShowModal()); +} + +//######################################################################################## + +class SelectTimespanDlg : public SelectTimespanDlgGenerated +{ +public: + SelectTimespanDlg(wxWindow* parent, Int64& timeFrom, Int64& timeTo); + +private: + virtual void OnOkay(wxCommandEvent& event); + virtual void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + + virtual void OnChangeSelectionFrom(wxCalendarEvent& event) + { + if (m_calendarFrom->GetDate() > m_calendarTo->GetDate()) + m_calendarTo->SetDate(m_calendarFrom->GetDate()); + } + virtual void OnChangeSelectionTo(wxCalendarEvent& event) + { + if (m_calendarFrom->GetDate() > m_calendarTo->GetDate()) + m_calendarFrom->SetDate(m_calendarTo->GetDate()); + } + + Int64& timeFrom_; + Int64& timeTo_; +}; + + +wxDateTime utcToLocalDateTime(time_t utcTime) +{ + //wxDateTime models local(!) time (in contrast to what documentation says), but this constructor takes time_t UTC + return wxDateTime(utcTime); +} + +time_t localDateTimeToUtc(const wxDateTime& localTime) +{ + return localTime.GetTicks(); +} + + +SelectTimespanDlg::SelectTimespanDlg(wxWindow* parent, Int64& timeFrom, Int64& timeTo) : + SelectTimespanDlgGenerated(parent), + timeFrom_(timeFrom), + timeTo_(timeTo) +{ +#ifdef ZEN_WIN + new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" +#endif + setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonOkay).setCancel(m_buttonCancel)); + + long style = wxCAL_SHOW_HOLIDAYS | wxCAL_SHOW_SURROUNDING_WEEKS; + +#ifdef ZEN_WIN + DWORD firstDayOfWeek = 0; + if (::GetLocaleInfo(LOCALE_USER_DEFAULT, //__in LCID Locale, + LOCALE_IFIRSTDAYOFWEEK | // first day of week specifier, 0-6, 0=Monday, 6=Sunday + LOCALE_RETURN_NUMBER, //__in LCTYPE LCType, + reinterpret_cast(&firstDayOfWeek), //__out LPTSTR lpLCData, + sizeof(firstDayOfWeek) / sizeof(TCHAR)) > 0 && //__in int cchData + firstDayOfWeek == 6) + style |= wxCAL_SUNDAY_FIRST; + else //default +#endif + style |= wxCAL_MONDAY_FIRST; + + m_calendarFrom->SetWindowStyleFlag(style); + m_calendarTo ->SetWindowStyleFlag(style); + + //set default values + if (timeTo_ == 0) + timeTo_ = wxGetUTCTime(); // + if (timeFrom_ == 0) + timeFrom_ = timeTo_ - 7 * 24 * 3600; //default time span: one week from "now" + + m_calendarFrom->SetDate(utcToLocalDateTime(to(timeFrom_))); + m_calendarTo ->SetDate(utcToLocalDateTime(to(timeTo_))); + +#if wxCHECK_VERSION(2, 9, 5) + //doesn't seem to be a problem here: +#else + //wxDatePickerCtrl::BestSize() does not respect year field and trims it, both wxMSW/wxGTK - why isn't there anybody testing this wxWidgets stuff??? + wxSize minSz = m_calendarFrom->GetBestSize(); + minSz.x += 30; + m_calendarFrom->SetMinSize(minSz); + m_calendarTo ->SetMinSize(minSz); +#endif + + GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() + //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! + + m_buttonOkay->SetFocus(); +} + + +void SelectTimespanDlg::OnOkay(wxCommandEvent& event) +{ + wxDateTime from = m_calendarFrom->GetDate(); + wxDateTime to = m_calendarTo ->GetDate(); + + //align to full days + from.ResetTime(); + to += wxTimeSpan::Day(); + to.ResetTime(); //reset local(!) time + to -= wxTimeSpan::Second(); //go back to end of previous day + + timeFrom_ = localDateTimeToUtc(from); + timeTo_ = localDateTimeToUtc(to); + + /* + { + time_t current = zen::to(timeFrom_); + struct tm* tdfewst = ::localtime(¤t); + int budfk = 3; + } + { + time_t current = zen::to(timeTo_); + struct tm* tdfewst = ::localtime(¤t); + int budfk = 3; + } + */ + + EndModal(ReturnSmallDlg::BUTTON_OKAY); +} + + +ReturnSmallDlg::ButtonPressed zen::showSelectTimespanDlg(wxWindow* parent, Int64& timeFrom, Int64& timeTo) +{ + SelectTimespanDlg timeSpanDlg(parent, timeFrom, timeTo); + return static_cast(timeSpanDlg.ShowModal()); +} diff --git a/FreeFileSync/Source/ui/small_dlgs.h b/FreeFileSync/Source/ui/small_dlgs.h new file mode 100644 index 00000000..311a7d14 --- /dev/null +++ b/FreeFileSync/Source/ui/small_dlgs.h @@ -0,0 +1,52 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef SMALLDIALOGS_H_INCLUDED +#define SMALLDIALOGS_H_INCLUDED + +#include +//#include "../file_hierarchy.h" +#include "../lib/process_xml.h" +#include "../synchronization.h" + +namespace zen +{ +//parent window, optional: support correct dialog placement above parent on multiple monitor systems + +struct ReturnSmallDlg +{ + enum ButtonPressed + { + BUTTON_CANCEL, + BUTTON_OKAY = 1 + }; +}; + +void showAboutDialog(wxWindow* parent); + +ReturnSmallDlg::ButtonPressed showFilterDialog(wxWindow* parent, FilterConfig& filter, const wxString& caption); + +ReturnSmallDlg::ButtonPressed showDeleteDialog(wxWindow* parent, + const std::vector& rowsOnLeft, + const std::vector& rowsOnRight, + bool& deleteOnBothSides, + bool& useRecycleBin); + +ReturnSmallDlg::ButtonPressed showSyncConfirmationDlg(wxWindow* parent, + const wxString& variantName, + const SyncStatistics& statistics, + bool& dontShowAgain); + +ReturnSmallDlg::ButtonPressed showCompareCfgDialog(wxWindow* parent, CompConfig& cmpConfig, const wxString& title); + +ReturnSmallDlg::ButtonPressed showGlobalSettingsDlg(wxWindow* parent, xmlAccess::XmlGlobalSettings& globalSettings); + +ReturnSmallDlg::ButtonPressed showSelectTimespanDlg(wxWindow* parent, Int64& timeFrom, Int64& timeTo); +} + +#endif // SMALLDIALOGS_H_INCLUDED + + diff --git a/FreeFileSync/Source/ui/sorting.h b/FreeFileSync/Source/ui/sorting.h new file mode 100644 index 00000000..46477d0f --- /dev/null +++ b/FreeFileSync/Source/ui/sorting.h @@ -0,0 +1,193 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef SORTING_H_INCLUDED +#define SORTING_H_INCLUDED + +#include +#include +#include "../file_hierarchy.h" + +namespace zen +{ +namespace +{ +struct CompileTimeReminder : public FSObjectVisitor +{ + virtual void visit(const FilePair& fileObj) {} + virtual void visit(const SymlinkPair& linkObj) {} + virtual void visit(const DirPair& dirObj ) {} +} checkDymanicCasts; //just a compile-time reminder to manually check dynamic casts in this file when needed +} + +inline +bool isDirectoryPair(const FileSystemObject& fsObj) +{ + return dynamic_cast(&fsObj) != nullptr; +} + + +template inline +bool lessShortFileName(const FileSystemObject& a, const FileSystemObject& b) +{ + //presort types: first files, then directories then empty rows + if (a.isEmpty()) + return false; //empty rows always last + else if (b.isEmpty()) + return true; //empty rows always last + + + if (isDirectoryPair(a)) //sort directories by relative name + { + if (isDirectoryPair(b)) + return LessFilename()(a.getRelativeName(), b.getRelativeName()); + else + return false; + } + else + { + if (isDirectoryPair(b)) + return true; + else + return makeSortDirection(LessFilename(), Int2Type())(a.getShortName(), b.getShortName()); + } +} + + +template //side currently unused! +bool lessRelativeName(const FileSystemObject& a, const FileSystemObject& b) +{ + const bool isDirectoryA = isDirectoryPair(a); + const Zstring& relDirNameA = isDirectoryA ? + a.getObjRelativeName() : //directory + beforeLast(a.getObjRelativeName(), FILE_NAME_SEPARATOR); //returns empty string if ch not found + + const bool isDirectoryB = isDirectoryPair(b); + const Zstring& relDirNameB = isDirectoryB ? + b.getObjRelativeName() : //directory + beforeLast(b.getObjRelativeName(), FILE_NAME_SEPARATOR); //returns empty string if ch not found + + //compare relative names without filenames first + const int rv = cmpFileName(relDirNameA, relDirNameB); + if (rv != 0) + return makeSortDirection(std::less(), Int2Type())(rv, 0); + else //compare the filenames + { + if (isDirectoryB) //directories shall appear before files + return false; + else if (isDirectoryA) + return true; + + return LessFilename()(a.getObjShortName(), b.getObjShortName()); + } +} + + +template inline +bool lessFilesize(const FileSystemObject& a, const FileSystemObject& b) +{ + //empty rows always last + if (a.isEmpty()) + return false; + else if (b.isEmpty()) + return true; + + const bool isDirA = dynamic_cast(&a) != nullptr; + const bool isDirB = dynamic_cast(&b) != nullptr; + + //directories second last + if (isDirA) + return false; + else if (isDirB) + return true; + + const FilePair* fileObjA = dynamic_cast(&a); + const FilePair* fileObjB = dynamic_cast(&b); + + //then symlinks + if (!fileObjA) + return false; + else if (!fileObjB) + return true; + + //return list beginning with largest files first + return makeSortDirection(std::less(), Int2Type())(fileObjA->getFileSize(), fileObjB->getFileSize()); +} + + +template inline +bool lessFiletime(const FileSystemObject& a, const FileSystemObject& b) +{ + if (a.isEmpty()) + return false; //empty rows always last + else if (b.isEmpty()) + return true; //empty rows always last + + + const FilePair* fileObjA = dynamic_cast(&a); + const FilePair* fileObjB = dynamic_cast(&b); + + const SymlinkPair* linkObjA = dynamic_cast(&a); + const SymlinkPair* linkObjB = dynamic_cast(&b); + + if (!fileObjA && !linkObjA) + return false; //directories last + else if (!fileObjB && !linkObjB) + return true; //directories last + + zen::Int64 dateA = fileObjA ? fileObjA->getLastWriteTime() : linkObjA->getLastWriteTime(); + zen::Int64 dateB = fileObjB ? fileObjB->getLastWriteTime() : linkObjB->getLastWriteTime(); + + //return list beginning with newest files first + return makeSortDirection(std::less(), Int2Type())(dateA, dateB); +} + + +template inline +bool lessExtension(const FileSystemObject& a, const FileSystemObject& b) +{ + if (a.isEmpty()) + return false; //empty rows always last + else if (b.isEmpty()) + return true; //empty rows always last + + if (dynamic_cast(&a)) + return false; //directories last + else if (dynamic_cast(&b)) + return true; //directories last + + auto getExtension = [&](const FileSystemObject& fsObj) -> Zstring + { + const Zstring& shortName = fsObj.getShortName(); + const size_t pos = shortName.rfind(Zchar('.')); + return pos == Zstring::npos ? Zstring() : Zstring(shortName.c_str() + pos + 1); + }; + + return makeSortDirection(LessFilename(), Int2Type())(getExtension(a), getExtension(b)); +} + + +template inline +bool lessCmpResult(const FileSystemObject& a, const FileSystemObject& b) +{ + //presort result: equal shall appear at end of list + if (a.getCategory() == FILE_EQUAL) + return false; + if (b.getCategory() == FILE_EQUAL) + return true; + + return makeSortDirection(std::less(), Int2Type())(a.getCategory(), b.getCategory()); +} + + +template inline +bool lessSyncDirection(const FileSystemObject& a, const FileSystemObject& b) +{ + return makeSortDirection(std::less(), Int2Type())(a.getSyncOperation(), b.getSyncOperation()); +} +} + +#endif // SORTING_H_INCLUDED diff --git a/FreeFileSync/Source/ui/switch_to_gui.h b/FreeFileSync/Source/ui/switch_to_gui.h new file mode 100644 index 00000000..20fe81de --- /dev/null +++ b/FreeFileSync/Source/ui/switch_to_gui.h @@ -0,0 +1,40 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef SWITCHTOGUI_H_INCLUDED +#define SWITCHTOGUI_H_INCLUDED + +#include "../lib/process_xml.h" +#include "main_dlg.h" //in "application.cpp" we have this dependency anyway! + +namespace zen +{ +//switch from FreeFileSync Batch to GUI modus: opens a new FreeFileSync GUI session asynchronously +class SwitchToGui +{ +public: + SwitchToGui(const Zstring& referenceFile, + const xmlAccess::XmlBatchConfig& batchCfg, + xmlAccess::XmlGlobalSettings& globalSettings) : + guiCfg(xmlAccess::convertBatchToGui(batchCfg)), + globalSettings_(globalSettings) + { + referenceFiles.push_back(referenceFile); + } + + void execute() const + { + MainDialog::create(guiCfg, referenceFiles, &globalSettings_, true); //new toplevel window + } + +private: + std::vector referenceFiles; + const xmlAccess::XmlGuiConfig guiCfg; + xmlAccess::XmlGlobalSettings& globalSettings_; +}; +} + +#endif // SWITCHTOGUI_H_INCLUDED diff --git a/FreeFileSync/Source/ui/sync_cfg.cpp b/FreeFileSync/Source/ui/sync_cfg.cpp new file mode 100644 index 00000000..38e583fc --- /dev/null +++ b/FreeFileSync/Source/ui/sync_cfg.cpp @@ -0,0 +1,640 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "sync_cfg.h" +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gui_generated.h" +#include "exec_finished_box.h" +#include "dir_name.h" +#include "../file_hierarchy.h" +#include "../lib/help_provider.h" + +using namespace zen; +using namespace xmlAccess; + + +class SyncCfgDialog : public SyncCfgDlgGenerated +{ +public: + SyncCfgDialog(wxWindow* parent, + CompareVariant compareVar, + SyncConfig& syncCfg, + const wxString& caption, + xmlAccess::OnGuiError* handleError, // + ExecWhenFinishedCfg* execWhenFinished); //optional input parameter + +private: + virtual void OnSyncTwoWay(wxCommandEvent& event) { directionCfg.var = DirectionConfig::TWOWAY; updateGui(); } + virtual void OnSyncMirror(wxCommandEvent& event) { directionCfg.var = DirectionConfig::MIRROR; updateGui(); } + virtual void OnSyncUpdate(wxCommandEvent& event) { directionCfg.var = DirectionConfig::UPDATE; updateGui(); } + virtual void OnSyncCustom(wxCommandEvent& event) { directionCfg.var = DirectionConfig::CUSTOM; updateGui(); } + + virtual void OnToggleDetectMovedFiles(wxCommandEvent& event) { directionCfg.detectMovedFiles = !directionCfg.detectMovedFiles; updateGui(); } + + virtual void OnSyncTwoWayDouble(wxMouseEvent& event); + virtual void OnSyncMirrorDouble(wxMouseEvent& event); + virtual void OnSyncUpdateDouble(wxMouseEvent& event); + virtual void OnSyncCustomDouble(wxMouseEvent& event); + + virtual void OnExLeftSideOnly (wxCommandEvent& event); + virtual void OnExRightSideOnly(wxCommandEvent& event); + virtual void OnLeftNewer (wxCommandEvent& event); + virtual void OnRightNewer (wxCommandEvent& event); + virtual void OnDifferent (wxCommandEvent& event); + virtual void OnConflict (wxCommandEvent& event); + + virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSyncConfig::BUTTON_CANCEL); } + virtual void OnCancel(wxCommandEvent& event) { EndModal(ReturnSyncConfig::BUTTON_CANCEL); } + virtual void OnOkay (wxCommandEvent& event); + + virtual void OnParameterChange(wxCommandEvent& event) { updateGui(); } + + virtual void OnDeletionPermanent (wxCommandEvent& event) { handleDeletion = DELETE_PERMANENTLY; updateGui(); } + virtual void OnDeletionRecycler (wxCommandEvent& event) { handleDeletion = DELETE_TO_RECYCLER; updateGui(); } + virtual void OnDeletionVersioning (wxCommandEvent& event) { handleDeletion = DELETE_TO_VERSIONING; updateGui(); } + + virtual void OnErrorPopup (wxCommandEvent& event) { onGuiError = ON_GUIERROR_POPUP; updateGui(); } + virtual void OnErrorIgnore(wxCommandEvent& event) { onGuiError = ON_GUIERROR_IGNORE; updateGui(); } + + virtual void OnHelpVersioning(wxHyperlinkEvent& event) { displayHelpEntry(L"html/Versioning.html", this); } + + struct Config + { + SyncConfig syncCfg; + xmlAccess::OnGuiError onGuiError; + std::wstring onCompletion; + }; + void setConfig(const Config& cfg); + Config getConfig() const; + + void updateGui(); + + //parameters with ownership NOT within GUI controls! + DirectionConfig directionCfg; + DeletionPolicy handleDeletion; //use Recycler, delete permanently or move to user-defined location + OnGuiError onGuiError; + + //output data + SyncConfig& outSyncCfg; + xmlAccess::OnGuiError* outOptOnGuiError; + ExecWhenFinishedCfg* outOptExecWhenFinished; + + CompareVariant compareVar_; + DirectoryName versioningFolder; + + EnumDescrList enumVersioningStyle; +}; + + +void updateConfigIcons(const DirectionConfig& directionCfg, + wxBitmapButton* buttonLeftOnly, + wxBitmapButton* buttonRightOnly, + wxBitmapButton* buttonLeftNewer, + wxBitmapButton* buttonRightNewer, + wxBitmapButton* buttonDifferent, + wxBitmapButton* buttonConflict) +{ + if (directionCfg.var != DirectionConfig::TWOWAY) //automatic mode needs no sync-directions + { + const DirectionSet dirCfg = extractDirections(directionCfg); + + switch (dirCfg.exLeftSideOnly) + { + case SyncDirection::RIGHT: + buttonLeftOnly->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_create_right"))); + buttonLeftOnly->SetToolTip(getSyncOpDescription(SO_CREATE_NEW_RIGHT)); + break; + case SyncDirection::LEFT: + buttonLeftOnly->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_delete_left"))); + buttonLeftOnly->SetToolTip(getSyncOpDescription(SO_DELETE_LEFT)); + break; + case SyncDirection::NONE: + buttonLeftOnly->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_none"))); + buttonLeftOnly->SetToolTip(getSyncOpDescription(SO_DO_NOTHING)); + break; + } + + switch (dirCfg.exRightSideOnly) + { + case SyncDirection::RIGHT: + buttonRightOnly->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_delete_right"))); + buttonRightOnly->SetToolTip(getSyncOpDescription(SO_DELETE_RIGHT)); + break; + case SyncDirection::LEFT: + buttonRightOnly->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_create_left"))); + buttonRightOnly->SetToolTip(getSyncOpDescription(SO_CREATE_NEW_LEFT)); + break; + case SyncDirection::NONE: + buttonRightOnly->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_none"))); + buttonRightOnly->SetToolTip(getSyncOpDescription(SO_DO_NOTHING)); + break; + } + + switch (dirCfg.leftNewer) + { + case SyncDirection::RIGHT: + buttonLeftNewer->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_right"))); + buttonLeftNewer->SetToolTip(getSyncOpDescription(SO_OVERWRITE_RIGHT)); + break; + case SyncDirection::LEFT: + buttonLeftNewer->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_left"))); + buttonLeftNewer->SetToolTip(getSyncOpDescription(SO_OVERWRITE_LEFT)); + break; + case SyncDirection::NONE: + buttonLeftNewer->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_none"))); + buttonLeftNewer->SetToolTip(getSyncOpDescription(SO_DO_NOTHING)); + break; + } + + switch (dirCfg.rightNewer) + { + case SyncDirection::RIGHT: + buttonRightNewer->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_right"))); + buttonRightNewer->SetToolTip(getSyncOpDescription(SO_OVERWRITE_RIGHT)); + break; + case SyncDirection::LEFT: + buttonRightNewer->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_left"))); + buttonRightNewer->SetToolTip(getSyncOpDescription(SO_OVERWRITE_LEFT)); + break; + case SyncDirection::NONE: + buttonRightNewer->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_none"))); + buttonRightNewer->SetToolTip(getSyncOpDescription(SO_DO_NOTHING)); + break; + } + + switch (dirCfg.different) + { + case SyncDirection::RIGHT: + buttonDifferent->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_right"))); + buttonDifferent->SetToolTip(getSyncOpDescription(SO_OVERWRITE_RIGHT)); + break; + case SyncDirection::LEFT: + buttonDifferent->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_left"))); + buttonDifferent->SetToolTip(getSyncOpDescription(SO_OVERWRITE_LEFT)); + break; + case SyncDirection::NONE: + buttonDifferent->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_none"))); + buttonDifferent->SetToolTip(getSyncOpDescription(SO_DO_NOTHING)); + break; + } + + switch (dirCfg.conflict) + { + case SyncDirection::RIGHT: + buttonConflict->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_right"))); + buttonConflict->SetToolTip(getSyncOpDescription(SO_OVERWRITE_RIGHT)); + break; + case SyncDirection::LEFT: + buttonConflict->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_left"))); + buttonConflict->SetToolTip(getSyncOpDescription(SO_OVERWRITE_LEFT)); + break; + case SyncDirection::NONE: + buttonConflict->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"cat_conflict"))); //silent dependency to algorithm.cpp::Redetermine!!! + buttonConflict->SetToolTip(_("Leave as unresolved conflict")); + break; + } + } +} + + +SyncCfgDialog::SyncCfgDialog(wxWindow* parent, + CompareVariant compareVar, + SyncConfig& syncCfg, + const wxString& title, + xmlAccess::OnGuiError* handleError, + ExecWhenFinishedCfg* execWhenFinished) : + SyncCfgDlgGenerated(parent), + handleDeletion(DELETE_TO_RECYCLER), // + onGuiError(ON_GUIERROR_POPUP), //dummy init + outSyncCfg(syncCfg), + outOptOnGuiError(handleError), + outOptExecWhenFinished(execWhenFinished), + compareVar_(compareVar), + versioningFolder(*m_panelVersioning, *m_buttonSelectDirVersioning, *m_versioningFolder/*, m_staticTextResolvedPath*/) +{ +#ifdef ZEN_WIN + new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" +#endif + setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonOK).setCancel(m_buttonCancel)); + + SetTitle(title); + + //set icons for this dialog + m_bitmapLeftOnly ->SetBitmap(mirrorIfRtl(greyScale(getResourceImage(L"cat_left_only" )))); + m_bitmapRightOnly ->SetBitmap(mirrorIfRtl(greyScale(getResourceImage(L"cat_right_only" )))); + m_bitmapLeftNewer ->SetBitmap(mirrorIfRtl(greyScale(getResourceImage(L"cat_left_newer" )))); + m_bitmapRightNewer->SetBitmap(mirrorIfRtl(greyScale(getResourceImage(L"cat_right_newer")))); + m_bitmapDifferent ->SetBitmap(mirrorIfRtl(greyScale(getResourceImage(L"cat_different" )))); + m_bitmapConflict ->SetBitmap(mirrorIfRtl(greyScale(getResourceImage(L"cat_conflict" )))); + m_bitmapDatabase ->SetBitmap(getResourceImage(L"database")); + + m_toggleBtnTwoWay->SetLabel(L"<- " + _("Two way") + L" ->"); + m_toggleBtnMirror->SetLabel( _("Mirror") + L" ->>"); + m_toggleBtnUpdate->SetLabel( _("Update") + L" ->"); + + setRelativeFontSize(*m_toggleBtnTwoWay, 1.25); + setRelativeFontSize(*m_toggleBtnMirror, 1.25); + setRelativeFontSize(*m_toggleBtnUpdate, 1.25); + setRelativeFontSize(*m_toggleBtnCustom, 1.25); + + enumVersioningStyle. + add(VER_STYLE_REPLACE, _("Replace"), _("Move files and replace if existing")). + add(VER_STYLE_ADD_TIMESTAMP, _("Time stamp"), _("Append a timestamp to each file name")); + + //hide controls for optional parameters + if (!handleError && !execWhenFinished) //currently either both or neither are bound! + { + bSizerExtraConfig->Show(false); + Layout(); + } + + if (execWhenFinished) + m_comboBoxExecFinished->initHistory(*execWhenFinished->history, execWhenFinished->historyMax); + + Config newCfg = { syncCfg, + handleError ?* handleError : ON_GUIERROR_POPUP, + execWhenFinished ?* execWhenFinished->command : std::wstring() + }; + setConfig(newCfg); + + GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() + //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! + + m_buttonOK->SetFocus(); +} + +//################################################################################################################# + +void SyncCfgDialog::setConfig(const Config& cfg) +{ + directionCfg = cfg.syncCfg.directionCfg; //make working copy; ownership *not* on GUI + handleDeletion = cfg.syncCfg.handleDeletion; + + versioningFolder.setName(utfCvrtTo(cfg.syncCfg.versioningDirectory)); + setEnumVal(enumVersioningStyle, *m_choiceVersioningStyle, cfg.syncCfg.versioningStyle); + + ////map single parameter "version limit" to both checkbox and spin ctrl: + //m_checkBoxVersionsLimit->SetValue(cfg.syncCfg.versionCountLimit >= 0); + //m_spinCtrlVersionsLimit->SetValue(cfg.syncCfg.versionCountLimit >= 0 ? cfg.syncCfg.versionCountLimit : 10 /*SyncConfig().versionCountLimit*/); + + onGuiError = cfg.onGuiError; + + m_comboBoxExecFinished->setValue(cfg.onCompletion); + + updateGui(); +} + + +SyncCfgDialog::Config SyncCfgDialog::getConfig() const +{ + Config output; + + //write configuration to main dialog + output.syncCfg.directionCfg = directionCfg; + output.syncCfg.handleDeletion = handleDeletion; + output.syncCfg.versioningDirectory = utfCvrtTo(versioningFolder.getName()); + output.syncCfg.versioningStyle = getEnumVal(enumVersioningStyle, *m_choiceVersioningStyle), + + //get single parameter "version limit" from both checkbox and spin ctrl: + // output.syncCfg.versionCountLimit = m_checkBoxVersionsLimit->GetValue() ? m_spinCtrlVersionsLimit->GetValue() : -1; + + output.onGuiError = onGuiError; + + output.onCompletion = m_comboBoxExecFinished->getValue(); + return output; +} + + +void SyncCfgDialog::updateGui() +{ +#ifdef ZEN_WIN + wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! + wxWindowUpdateLocker dummy2(m_panelVersioning); + wxWindowUpdateLocker dummy3(m_bpButtonLeftOnly); + wxWindowUpdateLocker dummy4(m_bpButtonRightOnly); + wxWindowUpdateLocker dummy5(m_bpButtonLeftNewer); + wxWindowUpdateLocker dummy6(m_bpButtonRightNewer); + wxWindowUpdateLocker dummy7(m_bpButtonDifferent); + wxWindowUpdateLocker dummy8(m_bpButtonConflict); +#endif + + const Config cfg = getConfig(); //resolve parameter ownership: some on GUI controls, others member variables + + updateConfigIcons(cfg.syncCfg.directionCfg, + m_bpButtonLeftOnly, + m_bpButtonRightOnly, + m_bpButtonLeftNewer, + m_bpButtonRightNewer, + m_bpButtonDifferent, + m_bpButtonConflict); + + //selecting "detect move files" does not always make sense: + m_checkBoxDetectMove->Enable(detectMovedFilesSelectable(directionCfg)); + m_checkBoxDetectMove->SetValue(detectMovedFilesEnabled(directionCfg)); //parameter NOT owned by checkbox! + + //display only relevant sync options + m_bitmapDatabase ->Show(cfg.syncCfg.directionCfg.var == DirectionConfig::TWOWAY); + sbSizerSyncDirections->Show(cfg.syncCfg.directionCfg.var != DirectionConfig::TWOWAY); + + switch (compareVar_) //sbSizerSyncDirections->Show resets child sizers! + { + case CMP_BY_TIME_SIZE: + bSizerDifferent ->Show(false); + break; + + case CMP_BY_CONTENT: + bSizerLeftNewer ->Show(false); + bSizerRightNewer->Show(false); + break; + } + bSizerConfig->Layout(); //[!] + + //update toggle buttons -> they have no parameter-ownership at all! + m_staticTextAutomatic->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); + m_staticTextMirror ->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); + m_staticTextUpdate ->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); + m_staticTextCustom ->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); + + m_toggleBtnTwoWay->SetValue(false); + m_toggleBtnMirror->SetValue(false); + m_toggleBtnUpdate->SetValue(false); + m_toggleBtnCustom->SetValue(false); + + switch (cfg.syncCfg.directionCfg.var) + { + case DirectionConfig::TWOWAY: + m_toggleBtnTwoWay->SetValue(true); + m_staticTextAutomatic->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); + break; + case DirectionConfig::MIRROR: + m_toggleBtnMirror->SetValue(true); + m_staticTextMirror->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); + break; + case DirectionConfig::UPDATE: + m_toggleBtnUpdate->SetValue(true); + m_staticTextUpdate->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); + break; + case DirectionConfig::CUSTOM: + m_toggleBtnCustom->SetValue(true); + m_staticTextCustom->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); + break; + } + + m_toggleBtnPermanent ->SetValue(false); + m_toggleBtnRecycler ->SetValue(false); + m_toggleBtnVersioning->SetValue(false); + switch (cfg.syncCfg.handleDeletion) + { + case DELETE_PERMANENTLY: + m_toggleBtnPermanent->SetValue(true); + break; + case DELETE_TO_RECYCLER: + m_toggleBtnRecycler->SetValue(true); + break; + case DELETE_TO_VERSIONING: + m_toggleBtnVersioning->SetValue(true); + break; + } + + const bool versioningSelected = cfg.syncCfg.handleDeletion == DELETE_TO_VERSIONING; + m_panelVersioning->Show(versioningSelected); + + if (versioningSelected) + { + updateTooltipEnumVal(enumVersioningStyle, *m_choiceVersioningStyle); + + const std::wstring pathSep = utfCvrtTo(FILE_NAME_SEPARATOR); + switch (cfg.syncCfg.versioningStyle) + { + case VER_STYLE_REPLACE: + setText(*m_staticTextNamingCvtPart1, pathSep + _("Folder") + pathSep + _("File") + L".doc"); + setText(*m_staticTextNamingCvtPart2Bold, L""); + setText(*m_staticTextNamingCvtPart3, L""); + break; + + case VER_STYLE_ADD_TIMESTAMP: + setText(*m_staticTextNamingCvtPart1, pathSep + _("Folder") + pathSep + _("File") + L".doc "); + setText(*m_staticTextNamingCvtPart2Bold, _("YYYY-MM-DD hhmmss")); + setText(*m_staticTextNamingCvtPart3, L".doc"); + break; + } + } + + //m_spinCtrlVersionsLimit->Enable(m_checkBoxVersionsLimit->GetValue()); //enabled status is *not* directly dependent from resolved config! (but transitively) + + m_toggleBtnErrorIgnore->SetValue(false); + m_toggleBtnErrorPopup ->SetValue(false); + switch (cfg.onGuiError) + { + case ON_GUIERROR_IGNORE: + m_toggleBtnErrorIgnore->SetValue(true); + break; + case ON_GUIERROR_POPUP: + m_toggleBtnErrorPopup->SetValue(true); + break; + } + + Layout(); + Refresh(); //removes a few artifacts when toggling display of versioning folder + + GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() + //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! +} + + +void SyncCfgDialog::OnOkay(wxCommandEvent& event) +{ + const Config cfg = getConfig(); + + //parameter validation: + + //check if user-defined directory for deletion was specified + if (cfg.syncCfg.handleDeletion == zen::DELETE_TO_VERSIONING) + { + Zstring versioningDir = cfg.syncCfg.versioningDirectory; + trim(versioningDir); + if (versioningDir.empty()) + { + showNotificationDialog(this, DialogInfoType::INFO, PopupDialogCfg().setMainInstructions(_("Please enter a target folder for versioning."))); + //don't show error icon to follow "Windows' encouraging tone" + m_panelVersioning->SetFocus(); + return; + } + } + + //apply config: + outSyncCfg = cfg.syncCfg; + + if (outOptOnGuiError) + *outOptOnGuiError = cfg.onGuiError; + + if (outOptExecWhenFinished) + { + *outOptExecWhenFinished->command = cfg.onCompletion; + //a good place to commit current "on completion" history item + m_comboBoxExecFinished->addItemHistory(); + } + + EndModal(ReturnSyncConfig::BUTTON_OKAY); +} + + +void SyncCfgDialog::OnSyncTwoWayDouble(wxMouseEvent& event) +{ + wxCommandEvent dummy; + OnSyncTwoWay(dummy); + OnOkay(dummy); +} + +void SyncCfgDialog::OnSyncMirrorDouble(wxMouseEvent& event) +{ + wxCommandEvent dummy; + OnSyncMirror(dummy); + OnOkay(dummy); +} + +void SyncCfgDialog::OnSyncUpdateDouble(wxMouseEvent& event) +{ + wxCommandEvent dummy; + OnSyncUpdate(dummy); + OnOkay(dummy); +} + +void SyncCfgDialog::OnSyncCustomDouble(wxMouseEvent& event) +{ + wxCommandEvent dummy; + OnSyncCustom(dummy); + OnOkay(dummy); +} + +namespace +{ +void toggleSyncDirection(SyncDirection& current) +{ + switch (current) + { + case SyncDirection::RIGHT: + current = SyncDirection::LEFT; + break; + case SyncDirection::LEFT: + current = SyncDirection::NONE; + break; + case SyncDirection::NONE: + current = SyncDirection::RIGHT; + break; + } +} + + +void pressCustomDir(DirectionConfig& directionCfg, SyncDirection& syncdir) +{ + switch (directionCfg.var) + { + case DirectionConfig::TWOWAY: + assert(false); + break; + case DirectionConfig::MIRROR: + case DirectionConfig::UPDATE: + directionCfg.custom = extractDirections(directionCfg); + directionCfg.var = DirectionConfig::CUSTOM; + toggleSyncDirection(syncdir); + break; + case DirectionConfig::CUSTOM: + toggleSyncDirection(syncdir); + + //some config optimization: if custom settings happen to match "mirror" or "update", just switch variant + const DirectionSet setMirror = [] + { + DirectionConfig mirrorCfg; + mirrorCfg.var = DirectionConfig::MIRROR; + return extractDirections(mirrorCfg); + }(); + + const DirectionSet setUpdate = [] + { + DirectionConfig updateCfg; + updateCfg.var = DirectionConfig::UPDATE; + return extractDirections(updateCfg); + }(); + + const DirectionSet currentSet = extractDirections(directionCfg); + if (currentSet == setMirror) + directionCfg.var = DirectionConfig::MIRROR; + else if (currentSet == setUpdate) + directionCfg.var = DirectionConfig::UPDATE; + break; + } +} +} + +void SyncCfgDialog::OnExLeftSideOnly(wxCommandEvent& event ) +{ + pressCustomDir(directionCfg, directionCfg.custom.exLeftSideOnly); + updateGui(); +} + + +void SyncCfgDialog::OnExRightSideOnly(wxCommandEvent& event ) +{ + pressCustomDir(directionCfg, directionCfg.custom.exRightSideOnly); + updateGui(); +} + + +void SyncCfgDialog::OnLeftNewer(wxCommandEvent& event ) +{ + pressCustomDir(directionCfg, directionCfg.custom.leftNewer); + updateGui(); +} + + +void SyncCfgDialog::OnRightNewer(wxCommandEvent& event ) +{ + pressCustomDir(directionCfg, directionCfg.custom.rightNewer); + updateGui(); +} + + +void SyncCfgDialog::OnDifferent(wxCommandEvent& event ) +{ + pressCustomDir(directionCfg, directionCfg.custom.different); + updateGui(); +} + + +void SyncCfgDialog::OnConflict(wxCommandEvent& event) +{ + pressCustomDir(directionCfg, directionCfg.custom.conflict); + updateGui(); +} + + +ReturnSyncConfig::ButtonPressed zen::showSyncConfigDlg(wxWindow* parent, + CompareVariant compareVar, + SyncConfig& syncCfg, + const wxString& title, + xmlAccess::OnGuiError* handleError, // + ExecWhenFinishedCfg* execWhenFinished) //optional input parameter +{ + SyncCfgDialog syncDlg(parent, + compareVar, + syncCfg, + title, + handleError, + execWhenFinished); + return static_cast(syncDlg.ShowModal()); +} diff --git a/FreeFileSync/Source/ui/sync_cfg.h b/FreeFileSync/Source/ui/sync_cfg.h new file mode 100644 index 00000000..e56a533b --- /dev/null +++ b/FreeFileSync/Source/ui/sync_cfg.h @@ -0,0 +1,41 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef SYNCCONFIG_H_INCLUDED +#define SYNCCONFIG_H_INCLUDED + +#include +#include "../lib/process_xml.h" + + +namespace zen +{ +struct ReturnSyncConfig +{ + enum ButtonPressed + { + BUTTON_CANCEL, + BUTTON_OKAY + }; +}; + +struct ExecWhenFinishedCfg +{ + std::wstring* command; //*must* be bound! + std::vector* history; // + size_t historyMax; +}; + + +ReturnSyncConfig::ButtonPressed showSyncConfigDlg(wxWindow* parent, + CompareVariant compareVar, + SyncConfig& syncCfg, + const wxString& title, + xmlAccess::OnGuiError* handleError, // + ExecWhenFinishedCfg* execWhenFinished); //optional input parameter +} + +#endif // SYNCCONFIG_H_INCLUDED diff --git a/FreeFileSync/Source/ui/taskbar.cpp b/FreeFileSync/Source/ui/taskbar.cpp new file mode 100644 index 00000000..d65d1fbc --- /dev/null +++ b/FreeFileSync/Source/ui/taskbar.cpp @@ -0,0 +1,176 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "taskbar.h" + +#ifdef ZEN_WIN +#include +#include +#include "../dll/Taskbar_Seven/taskbar.h" + +#elif defined HAVE_UBUNTU_UNITY +#include + +#elif defined ZEN_MAC +#include +#include +#include "osx_dock.h" +#endif + +using namespace zen; + + +#ifdef ZEN_WIN +using namespace tbseven; + + +class Taskbar::Pimpl //throw TaskbarNotAvailable +{ +public: + Pimpl(const wxFrame& window) : + assocWindow(window.GetHWND()), + setStatus_ (getDllName(), funName_setStatus), + setProgress_(getDllName(), funName_setProgress) + { + if (!assocWindow || !setProgress_ || !setStatus_) + throw TaskbarNotAvailable(); + + if (!zen::win7OrLater()) + throw TaskbarNotAvailable(); + } + + ~Pimpl() { setStatus_(assocWindow, tbseven::STATUS_NOPROGRESS); } + + void setStatus(Status status) + { + TaskBarStatus tbSevenStatus = tbseven::STATUS_NORMAL; + switch (status) + { + case Taskbar::STATUS_INDETERMINATE: + tbSevenStatus = tbseven::STATUS_INDETERMINATE; + break; + case Taskbar::STATUS_NORMAL: + tbSevenStatus = tbseven::STATUS_NORMAL; + break; + case Taskbar::STATUS_ERROR: + tbSevenStatus = tbseven::STATUS_ERROR; + break; + case Taskbar::STATUS_PAUSED: + tbSevenStatus = tbseven::STATUS_PAUSED; + break; + } + + setStatus_(assocWindow, tbSevenStatus); + } + + void setProgress(double fraction) + { + setProgress_(assocWindow, fraction * 100000, 100000); + } + +private: + void* assocWindow; //HWND + const DllFun setStatus_; + const DllFun setProgress_; +}; + +#elif defined HAVE_UBUNTU_UNITY //Ubuntu unity +namespace +{ +const char FFS_DESKTOP_FILE[] = "freefilesync.desktop"; +} + +class Taskbar::Pimpl //throw (TaskbarNotAvailable) +{ +public: + Pimpl(const wxFrame& window) : + tbEntry(unity_launcher_entry_get_for_desktop_id(FFS_DESKTOP_FILE)) + //tbEntry(unity_launcher_entry_get_for_app_uri("application://freefilesync.desktop")) + { + if (!tbEntry) + throw TaskbarNotAvailable(); + } + + ~Pimpl() { setStatus(STATUS_INDETERMINATE); } //it seems UnityLauncherEntry* does not need destruction + + void setStatus(Status status) + { + switch (status) + { + case Taskbar::STATUS_ERROR: + unity_launcher_entry_set_urgent(tbEntry, true); + break; + + case Taskbar::STATUS_INDETERMINATE: + unity_launcher_entry_set_urgent(tbEntry, false); + unity_launcher_entry_set_progress_visible(tbEntry, false); + break; + + case Taskbar::STATUS_NORMAL: + unity_launcher_entry_set_urgent(tbEntry, false); + unity_launcher_entry_set_progress_visible(tbEntry, true); + break; + + case Taskbar::STATUS_PAUSED: + unity_launcher_entry_set_urgent(tbEntry, false); + break; + } + } + + void setProgress(double fraction) + { + unity_launcher_entry_set_progress(tbEntry, fraction); + } + +private: + UnityLauncherEntry* tbEntry; +}; + +#elif defined ZEN_MAC +class Taskbar::Pimpl +{ +public: + Pimpl(const wxFrame& window) {} + + ~Pimpl() { setDockText(""); } + + void setStatus(Status status) {} + + void setProgress(double fraction) + { + //no decimal places to make output less noisy + setDockText((numberTo(numeric::round(fraction * 100.0)) + '%').c_str()); //no need to internationalize fraction!? + } + +private: + void setDockText(const char* str) + { + try + { + osx::dockIconSetText(str); //throw SysError + } + catch (const zen::SysError& e) { assert(false); } + } +}; + + +#else //no taskbar support +class Taskbar::Pimpl +{ +public: + Pimpl(const wxFrame& window) { throw TaskbarNotAvailable(); } + void setStatus(Status status) {} + void setProgress(double fraction) {} +}; +#endif + +//######################################################################################################## + +Taskbar::Taskbar(const wxFrame& window) : pimpl_(new Pimpl(window)) {} //throw TaskbarNotAvailable +Taskbar::~Taskbar() {} + +void Taskbar::setStatus(Status status) { pimpl_->setStatus(status); } +void Taskbar::setProgress(double fraction) { pimpl_->setProgress(fraction); } diff --git a/FreeFileSync/Source/ui/taskbar.h b/FreeFileSync/Source/ui/taskbar.h new file mode 100644 index 00000000..82e08656 --- /dev/null +++ b/FreeFileSync/Source/ui/taskbar.h @@ -0,0 +1,51 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef TASKBARPROGRESS_H_INCLUDED +#define TASKBARPROGRESS_H_INCLUDED + +#include +#include + +/* +Windows 7; show progress in windows superbar via ITaskbarList3 Interface: http://msdn.microsoft.com/en-us/library/dd391692(VS.85).aspx + +Ubuntu: use Unity interface (optional) + +Define HAVE_UBUNTU_UNITY and set: + Compiler flag: `pkg-config --cflags unity` + Linker flag: `pkg-config --libs unity` +*/ + +namespace zen +{ +class TaskbarNotAvailable {}; + +class Taskbar +{ +public: + Taskbar(const wxFrame& window); //throw TaskbarNotAvailable + ~Taskbar(); + + enum Status + { + STATUS_INDETERMINATE, + STATUS_NORMAL, + STATUS_ERROR, + STATUS_PAUSED + }; + + void setStatus(Status status); + void setProgress(double fraction); //between [0, 1] + +private: + class Pimpl; + std::unique_ptr pimpl_; +}; + +} + +#endif // TASKBARPROGRESS_H_INCLUDED diff --git a/FreeFileSync/Source/ui/tray_icon.cpp b/FreeFileSync/Source/ui/tray_icon.cpp new file mode 100644 index 00000000..28842a80 --- /dev/null +++ b/FreeFileSync/Source/ui/tray_icon.cpp @@ -0,0 +1,236 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "tray_icon.h" +#include +#include +#include +#include +#include //req. by Linux +#include +#include + +using namespace zen; + + +namespace +{ +void fillRange(wxImage& img, int pixelFirst, int pixelLast, const wxColor& col) //tolerant input range +{ + if (img.IsOk()) + { + const int width = img.GetWidth (); + const int height = img.GetHeight(); + + if (width > 0 && height > 0) + { + pixelFirst = std::max(pixelFirst, 0); + pixelLast = std::min(pixelLast, width * height); + + if (pixelFirst < pixelLast) + { + unsigned char* const bytesBegin = img.GetData() + pixelFirst * 3; + unsigned char* const bytesEnd = img.GetData() + pixelLast * 3; + + for (unsigned char* bytePos = bytesBegin; bytePos < bytesEnd; bytePos += 3) + { + bytePos[0] = col.Red (); + bytePos[1] = col.Green(); + bytePos[2] = col.Blue (); + } + + if (img.HasAlpha()) //make progress indicator fully opaque: + std::fill(img.GetAlpha() + pixelFirst, img.GetAlpha() + pixelLast, wxIMAGE_ALPHA_OPAQUE); + } + } + } +} + + +wxIcon generateProgressIcon(const wxImage& logo, double fraction) //generate icon with progress indicator +{ + if (!logo.IsOk() || logo.GetWidth() <= 0 || logo.GetHeight() <= 0) + return wxIcon(); + + const int pixelCount = logo.GetWidth() * logo.GetHeight(); + const int startFillPixel = numeric::confineCpy(numeric::round(fraction * pixelCount), 0, pixelCount); + + //minor optimization + static std::pair buffer = std::make_pair(-1, wxNullIcon); + + if (buffer.first != startFillPixel) + { + wxImage genImage(logo.Copy()); //workaround wxWidgets' screwed-up design from hell: their copy-construction implements reference-counting WITHOUT copy-on-write! + + //gradually make FFS icon brighter while nearing completion + zen::brighten(genImage, -200 * (1 - fraction)); + + //fill black border row + if (startFillPixel <= pixelCount - genImage.GetWidth()) + { + /* + -------- + ---bbbbb + bbbbSyyy S : start yellow remainder + yyyyyyyy + */ + int bStart = startFillPixel - genImage.GetWidth(); + if (bStart % genImage.GetWidth() != 0) //add one more black pixel, see ascii-art + --bStart; + fillRange(genImage, bStart, startFillPixel, *wxBLACK); + } + else if (startFillPixel < pixelCount) + { + //special handling for last row + /* + -------- + -------- + ---bbbbb + ---bSyyy S : start yellow remainder + */ + int bStart = startFillPixel - genImage.GetWidth() - 1; + int bEnd = (bStart / genImage.GetWidth() + 1) * genImage.GetWidth(); + + fillRange(genImage, bStart, bEnd, *wxBLACK); + fillRange(genImage, startFillPixel - 1, startFillPixel, *wxBLACK); + } + + //fill yellow remainder + fillRange(genImage, startFillPixel, pixelCount, wxColour(240, 200, 0)); + + buffer.second.CopyFromBitmap(wxBitmap(genImage)); + } + + return buffer.second; +} + +//------------------------------------------------------------------------------------------------ + +enum Selection +{ + CONTEXT_RESTORE = 1 //wxWidgets: "A MenuItem ID of zero does not work under Mac" +}; +} + + +class FfsTrayIcon::TaskBarImpl : public wxTaskBarIcon +{ +public: + TaskBarImpl(const std::function& onRequestResume) : onRequestResume_(onRequestResume) + { + Connect(wxEVT_TASKBAR_LEFT_DCLICK, wxEventHandler(TaskBarImpl::OnDoubleClick), nullptr, this); + + //Windows User Experience Guidelines: show the context menu rather than doing *nothing* on single left clicks; however: + //MSDN: "Double-clicking the left mouse button actually generates a sequence of four messages: WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, and WM_LBUTTONUP." + //Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/ms645606%28v=vs.85%29.aspx + //=> the only way to distinguish single left click and double-click is to wait wxSystemSettings::GetMetric(wxSYS_DCLICK_MSEC) (480ms) which is way too long! + } + + //virtual ~TaskBarImpl(){} + + void dontCallbackAnymore() { onRequestResume_ = nullptr; } + +private: + virtual wxMenu* CreatePopupMenu() + { + if (!onRequestResume_) + return nullptr; + + wxMenu* contextMenu = new wxMenu; + + wxMenuItem* defaultItem = new wxMenuItem(contextMenu, CONTEXT_RESTORE, _("&Restore")); + //wxWidgets font messup: + //1. font must be set *before* wxMenu::Append()! + //2. don't use defaultItem->GetFont(); making it bold creates a huge font size for some reason +#ifdef ZEN_WIN //no wxMenuItem::SetFont() on Linux and OS X: wasn't wxWidgets supposed to be *portable* at some point in time????? + defaultItem->SetFont(wxNORMAL_FONT->Bold()); //make default selection bold/align with double-click +#endif + contextMenu->Append(defaultItem); + + //event handling + contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TaskBarImpl::OnContextMenuSelection), nullptr, this); + + return contextMenu; //ownership transferred to caller + } + + void OnContextMenuSelection(wxCommandEvent& event) + { + switch (static_cast(event.GetId())) + { + case CONTEXT_RESTORE: + if (onRequestResume_) + onRequestResume_(); + break; + } + } + + void OnDoubleClick(wxEvent& event) + { + if (onRequestResume_) + onRequestResume_(); + } + + //void OnLeftDownClick(wxEvent& event) + //{ + // //copied from wxTaskBarIconBase::OnRightButtonDown() + // if (wxMenu* menu = CreatePopupMenu()) + // { + // PopupMenu(menu); + // delete menu; + // } + //} + + std::function onRequestResume_; +}; + + +FfsTrayIcon::FfsTrayIcon(const std::function& onRequestResume) : + trayIcon(new TaskBarImpl(onRequestResume)), + activeFraction(1), //show FFS logo by default +#if defined ZEN_WIN || defined ZEN_MAC //16x16 seems to be the only size that is shown correctly on OS X + logo(getResourceImage(L"FFS_tray_16x16").ConvertToImage()) +#elif defined ZEN_LINUX + logo(getResourceImage(L"FFS_tray_24x24").ConvertToImage()) +#endif +{ + trayIcon->SetIcon(generateProgressIcon(logo, activeFraction), L"FreeFileSync"); +} + + +FfsTrayIcon::~FfsTrayIcon() +{ + trayIcon->dontCallbackAnymore(); //TaskBarImpl has longer lifetime than FfsTrayIcon: avoid callback! + + /* + This is not working correctly on OS X! It seems both wxTaskBarIcon::RemoveIcon() and ~wxTaskBarIcon() are broken and do NOT immediately + remove the icon from the system tray! Only some time later in the event loop which called these functions they will be removed. + Maybe some system component has still shared ownership? Objective C auto release pools are freed at the end of the current event loop... + Anyway, wxWidgets fails to disconnect the wxTaskBarIcon event handlers before calling "[m_statusitem release]"! + + => !!!clicking on the icon after ~wxTaskBarIcon ran crashes the application!!! + + - if ~wxTaskBarIcon() ran from the SyncProgressDialog::updateGui() event loop (e.g. user manually clicking the icon) => icon removed on return + - if ~wxTaskBarIcon() ran from SyncProgressDialog::closeWindowDirectly() => leaves the icon dangling until user closes this dialog and outter event loop runs! + */ + + trayIcon->RemoveIcon(); //required on Windows: unlike on OS X, wxPendingDelete does not kick in before main event loop! + //use wxWidgets delayed destruction: delete during next idle loop iteration (handle late window messages, e.g. when double-clicking) + wxPendingDelete.Append(trayIcon); //identical to wxTaskBarIconBase::Destroy() in wxWidgets 2.9.5 +} + + +void FfsTrayIcon::setToolTip(const wxString& toolTip) +{ + activeToolTip = toolTip; + trayIcon->SetIcon(generateProgressIcon(logo, activeFraction), activeToolTip); //another wxWidgets design bug: non-orthogonal method! +} + + +void FfsTrayIcon::setProgress(double fraction) +{ + activeFraction = fraction; + trayIcon->SetIcon(generateProgressIcon(logo, activeFraction), activeToolTip); +} diff --git a/FreeFileSync/Source/ui/tray_icon.h b/FreeFileSync/Source/ui/tray_icon.h new file mode 100644 index 00000000..24c97eb0 --- /dev/null +++ b/FreeFileSync/Source/ui/tray_icon.h @@ -0,0 +1,45 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef TRAYICON_H_84217830427534285 +#define TRAYICON_H_84217830427534285 + +#include +#include + +/* +show tray icon with progress during lifetime of this instance + +ATTENTION: wxWidgets never assumes that an object indirectly destroys itself while processing an event! + this includes wxEvtHandler-derived objects!!! + it seems ProcessEvent() works (on Windows), but AddPendingEvent() will crash since it uses "this" after the event processing! + +=> don't derive from wxEvtHandler or any other wxWidgets object here!!!!!! +=> use simple std::function as callback instead => instance may now be safely deleted in callback! +*/ + +class FfsTrayIcon +{ +public: + FfsTrayIcon(const std::function& onRequestResume); //callback only held during lifetime of this instance + ~FfsTrayIcon(); + + void setToolTip(const wxString& toolTip); + void setProgress(double fraction); //number between [0, 1], for small progress indicator + +private: + FfsTrayIcon(const FfsTrayIcon&); //=delete + FfsTrayIcon& operator=(const FfsTrayIcon&); //=delete + + class TaskBarImpl; + TaskBarImpl* trayIcon; + + wxString activeToolTip; + double activeFraction; + wxImage logo; +}; + +#endif //TRAYICON_H_84217830427534285 diff --git a/FreeFileSync/Source/ui/tree_view.cpp b/FreeFileSync/Source/ui/tree_view.cpp new file mode 100644 index 00000000..f3c728c4 --- /dev/null +++ b/FreeFileSync/Source/ui/tree_view.cpp @@ -0,0 +1,1339 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include +#include "tree_view.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../lib/icon_buffer.h" + +using namespace zen; + + +inline +void TreeView::compressNode(Container& cont) //remove single-element sub-trees -> gain clarity + usability (call *after* inclusion check!!!) +{ + if (cont.subDirs.empty() || //single files node or... + (cont.firstFileId == nullptr && //single dir node... + cont.subDirs.size() == 1 && // + cont.subDirs[0].firstFileId == nullptr && //...that is empty + cont.subDirs[0].subDirs.empty())) // + { + cont.subDirs.clear(); + cont.firstFileId = nullptr; + } +} + + +template //(const FileSystemObject&) -> bool +void TreeView::extractVisibleSubtree(HierarchyObject& hierObj, //in + TreeView::Container& cont, //out + Function pred) +{ + auto getBytes = [](const FilePair& fileObj) -> UInt64 //MSVC screws up miserably if we put this lambda into std::for_each + { + //give accumulated bytes the semantics of a sync preview! + if (fileObj.isActive()) + switch (fileObj.getSyncDir()) + { + case SyncDirection::LEFT: + return fileObj.getFileSize(); + case SyncDirection::RIGHT: + return fileObj.getFileSize(); + case SyncDirection::NONE: + break; + } + return std::max(fileObj.getFileSize(), fileObj.getFileSize()); + }; + + cont.firstFileId = nullptr; + for (FilePair& fileObj : hierObj.refSubFiles()) + if (pred(fileObj)) + { + cont.bytesNet += getBytes(fileObj); + ++cont.itemCountNet; + + if (!cont.firstFileId) + cont.firstFileId = fileObj.getId(); + } + + for (SymlinkPair& linkObj : hierObj.refSubLinks()) + if (pred(linkObj)) + { + ++cont.itemCountNet; + + if (!cont.firstFileId) + cont.firstFileId = linkObj.getId(); + } + + cont.bytesGross += cont.bytesNet; + cont.itemCountGross += cont.itemCountNet; + + cont.subDirs.reserve(hierObj.refSubDirs().size()); //avoid expensive reallocations! + + for (DirPair& subDirObj : hierObj.refSubDirs()) + { + const bool included = pred(subDirObj); + + cont.subDirs.push_back(TreeView::DirNodeImpl()); // + auto& subDirView = cont.subDirs.back(); + TreeView::extractVisibleSubtree(subDirObj, subDirView, pred); + if (included) + ++subDirView.itemCountGross; + + cont.bytesGross += subDirView.bytesGross; + cont.itemCountGross += subDirView.itemCountGross; + + if (!included && !subDirView.firstFileId && subDirView.subDirs.empty()) + cont.subDirs.pop_back(); + else + { + subDirView.objId = subDirObj.getId(); + compressNode(subDirView); + } + } +} + + +namespace +{ +//generate nice percentage numbers which precisely sum up to 100 +void calcPercentage(std::vector>& workList) +{ + const UInt64 total = std::accumulate(workList.begin(), workList.end(), UInt64(), + [](UInt64 sum, const std::pair& pair) { return sum + pair.first; }); + + if (total == 0U) //this case doesn't work with the error minimizing algorithm below + { + for (std::pair& pair : workList) + *pair.second = 0; + return; + } + + int remainingPercent = 100; + for (std::pair& pair : workList) + { + *pair.second = to(pair.first * 100U / total); //round down + remainingPercent -= *pair.second; + } + assert(remainingPercent >= 0); + assert(remainingPercent < static_cast(workList.size())); + + //distribute remaining percent so that overall error is minimized as much as possible: + remainingPercent = std::min(remainingPercent, static_cast(workList.size())); + if (remainingPercent > 0) + { + std::nth_element(workList.begin(), workList.begin() + remainingPercent - 1, workList.end(), + [total](const std::pair& lhs, const std::pair& rhs) + { + return lhs.first * 100U % total > rhs.first * 100U % total; + }); + + std::for_each(workList.begin(), workList.begin() + remainingPercent, [&](std::pair& pair) { ++*pair.second; }); + } +} + + +Zstring getShortDisplayNameForFolderPair(const Zstring& dirLeftPf, const Zstring& dirRightPf) //post-fixed with separator +{ + assert(endsWith(dirLeftPf, FILE_NAME_SEPARATOR) || dirLeftPf .empty()); + assert(endsWith(dirRightPf, FILE_NAME_SEPARATOR) || dirRightPf.empty()); + + auto itL = dirLeftPf .end(); + auto itR = dirRightPf.end(); + + for (;;) + { + auto itLPrev = find_last(dirLeftPf .begin(), itL, FILE_NAME_SEPARATOR); + auto itRPrev = find_last(dirRightPf.begin(), itR, FILE_NAME_SEPARATOR); + + if (itLPrev == itL || + itRPrev == itR) + { + if (itLPrev == itL) + itLPrev = dirLeftPf.begin(); + else + ++itLPrev; //skip separator + if (itRPrev == itR) + itRPrev = dirRightPf.begin(); + else + ++itRPrev; + + if (equal(itLPrev, itL, itRPrev, itR)) + { + itL = itLPrev; + itR = itRPrev; + } + break; + } + + if (!equal(itLPrev, itL, itRPrev, itR)) + break; + itL = itLPrev; + itR = itRPrev; + } + + Zstring commonPostfix(itL, dirLeftPf.end()); + if (startsWith(commonPostfix, FILE_NAME_SEPARATOR)) + commonPostfix = afterFirst(commonPostfix, FILE_NAME_SEPARATOR); + if (endsWith(commonPostfix, FILE_NAME_SEPARATOR)) + commonPostfix.resize(commonPostfix.size() - 1); + + if (commonPostfix.empty()) + { + auto getLastComponent = [](const Zstring& dirPf) { return afterLast(beforeLast(dirPf, FILE_NAME_SEPARATOR), FILE_NAME_SEPARATOR); }; //returns the whole string if term not found + if (dirLeftPf.empty()) + return getLastComponent(dirRightPf); + else if (dirRightPf.empty()) + return getLastComponent(dirLeftPf); + else + return getLastComponent(dirLeftPf) + utfCvrtTo(L" \u2212 ") + //= unicode minus + getLastComponent(dirRightPf); + } + return commonPostfix; +} +} + + +template +struct TreeView::LessShortName +{ + bool operator()(const TreeLine& lhs, const TreeLine& rhs) const + { + //files last (irrespective of sort direction) + if (lhs.type_ == TreeView::TYPE_FILES) + return false; + else if (rhs.type_ == TreeView::TYPE_FILES) + return true; + + if (lhs.type_ != rhs.type_) // + return lhs.type_ < rhs.type_; //shouldn't happen! root nodes not mixed with files or directories + + switch (lhs.type_) + { + case TreeView::TYPE_ROOT: + return makeSortDirection(LessFilename(), Int2Type())(static_cast(lhs.node_)->displayName, + static_cast(rhs.node_)->displayName); + + case TreeView::TYPE_DIRECTORY: + { + const auto* dirObjL = dynamic_cast(FileSystemObject::retrieve(static_cast(lhs.node_)->objId)); + const auto* dirObjR = dynamic_cast(FileSystemObject::retrieve(static_cast(rhs.node_)->objId)); + + if (!dirObjL) //might be pathologic, but it's covered + return false; + else if (!dirObjR) + return true; + + return makeSortDirection(LessFilename(), Int2Type())(dirObjL->getObjShortName(), dirObjR->getObjShortName()); + } + + case TreeView::TYPE_FILES: + break; + } + assert(false); + return false; //:= all equal + } +}; + + +template +void TreeView::sortSingleLevel(std::vector& items, ColumnTypeNavi columnType) +{ + auto getBytes = [](const TreeLine& line) -> UInt64 + { + switch (line.type_) + { + case TreeView::TYPE_ROOT: + case TreeView::TYPE_DIRECTORY: + return line.node_->bytesGross; + case TreeView::TYPE_FILES: + return line.node_->bytesNet; + } + assert(false); + return 0U; + }; + + auto getCount = [](const TreeLine& line) -> int + { + switch (line.type_) + { + case TreeView::TYPE_ROOT: + case TreeView::TYPE_DIRECTORY: + return line.node_->itemCountGross; + + case TreeView::TYPE_FILES: + return line.node_->itemCountNet; + } + assert(false); + return 0; + }; + + const auto lessBytes = [&](const TreeLine& lhs, const TreeLine& rhs) { return getBytes(lhs) < getBytes(rhs); }; + const auto lessCount = [&](const TreeLine& lhs, const TreeLine& rhs) { return getCount(lhs) < getCount(rhs); }; + + switch (columnType) + { + case COL_TYPE_NAVI_BYTES: + std::sort(items.begin(), items.end(), makeSortDirection(lessBytes, Int2Type())); + break; + + case COL_TYPE_NAVI_DIRECTORY: + std::sort(items.begin(), items.end(), LessShortName()); + break; + + case COL_TYPE_NAVI_ITEM_COUNT: + std::sort(items.begin(), items.end(), makeSortDirection(lessCount, Int2Type())); + break; + } +} + + +void TreeView::getChildren(const Container& cont, unsigned int level, std::vector& output) +{ + output.clear(); + output.reserve(cont.subDirs.size() + 1); //keep pointers in "workList" valid + std::vector> workList; + + for (const DirNodeImpl& subDir : cont.subDirs) + { + output.push_back(TreeView::TreeLine(level, 0, &subDir, TreeView::TYPE_DIRECTORY)); + workList.push_back(std::make_pair(subDir.bytesGross, &output.back().percent_)); + } + + if (cont.firstFileId) + { + output.push_back(TreeLine(level, 0, &cont, TreeView::TYPE_FILES)); + workList.push_back(std::make_pair(cont.bytesNet, &output.back().percent_)); + } + calcPercentage(workList); + + if (sortAscending) + sortSingleLevel(output, sortColumn); + else + sortSingleLevel(output, sortColumn); +} + + +void TreeView::applySubView(std::vector&& newView) +{ + //preserve current node expansion status + auto getHierAlias = [](const TreeView::TreeLine& tl) -> const HierarchyObject* + { + switch (tl.type_) + { + case TreeView::TYPE_ROOT: + return static_cast(tl.node_)->baseDirObj.get(); + + case TreeView::TYPE_DIRECTORY: + if (auto dirObj = dynamic_cast(FileSystemObject::retrieve(static_cast(tl.node_)->objId))) + return dirObj; + break; + + case TreeView::TYPE_FILES: + break; //none!!! + } + return nullptr; + }; + + zen::hash_set expandedNodes; + if (!flatTree.empty()) + { + auto it = flatTree.begin(); + for (auto iterNext = flatTree.begin() + 1; iterNext != flatTree.end(); ++iterNext, ++it) + if (it->level_ < iterNext->level_) + if (auto hierObj = getHierAlias(*it)) + expandedNodes.insert(hierObj); + } + + //update view on full data + folderCmpView.swap(newView); //newView may be an alias for folderCmpView! see sorting! + + //set default flat tree + flatTree.clear(); + + if (folderCmp.size() == 1) //single folder pair case (empty pairs were already removed!) do NOT use folderCmpView for this check! + { + if (!folderCmpView.empty()) //it may really be! + getChildren(folderCmpView[0], 0, flatTree); //do not show root + } + else + { + //following is almost identical with TreeView::getChildren(): however we *cannot* reuse code here; + //this were only possible if we replaced "std::vector" with "Container"! + + flatTree.reserve(folderCmpView.size()); //keep pointers in "workList" valid + std::vector> workList; + + for (const RootNodeImpl& root : folderCmpView) + { + flatTree.push_back(TreeView::TreeLine(0, 0, &root, TreeView::TYPE_ROOT)); + workList.push_back(std::make_pair(root.bytesGross, &flatTree.back().percent_)); + } + + calcPercentage(workList); + + if (sortAscending) + sortSingleLevel(flatTree, sortColumn); + else + sortSingleLevel(flatTree, sortColumn); + } + + //restore node expansion status + for (size_t row = 0; row < flatTree.size(); ++row) //flatTree size changes during loop! + { + const TreeLine& line = flatTree[row]; + + if (auto hierObj = getHierAlias(line)) + if (expandedNodes.find(hierObj) != expandedNodes.end()) + { + std::vector newLines; + getChildren(*line.node_, line.level_ + 1, newLines); + + flatTree.insert(flatTree.begin() + row + 1, newLines.begin(), newLines.end()); + } + } +} + + +template +void TreeView::updateView(Predicate pred) +{ + //update view on full data + std::vector newView; + newView.reserve(folderCmp.size()); //avoid expensive reallocations! + + for (const std::shared_ptr& baseObj : folderCmp) + { + newView.push_back(TreeView::RootNodeImpl()); + RootNodeImpl& root = newView.back(); + this->extractVisibleSubtree(*baseObj, root, pred); //"this->" is bogus for a static method, but GCC screws this one up + + //warning: the following lines are almost 1:1 copy from extractVisibleSubtree: + //however we *cannot* reuse code here; this were only possible if we replaced "std::vector" with "Container"! + if (!root.firstFileId && root.subDirs.empty()) + newView.pop_back(); + else + { + root.baseDirObj = baseObj; + root.displayName = getShortDisplayNameForFolderPair(baseObj->getBaseDirPf(), + baseObj->getBaseDirPf()); + + this->compressNode(root); //"this->" required by two-pass lookup as enforced by GCC 4.7 + } + } + + lastViewFilterPred = pred; + applySubView(std::move(newView)); +} + + +void TreeView::setSortDirection(ColumnTypeNavi colType, bool ascending) //apply permanently! +{ + sortColumn = colType; + sortAscending = ascending; + + //reapply current view + applySubView(std::move(folderCmpView)); +} + + +bool TreeView::getDefaultSortDirection(ColumnTypeNavi colType) +{ + switch (colType) + { + case COL_TYPE_NAVI_BYTES: + return false; + case COL_TYPE_NAVI_DIRECTORY: + return true; + case COL_TYPE_NAVI_ITEM_COUNT: + return false; + } + assert(false); + return true; +} + + +TreeView::NodeStatus TreeView::getStatus(size_t row) const +{ + if (row < flatTree.size()) + { + if (row + 1 < flatTree.size() && flatTree[row + 1].level_ > flatTree[row].level_) + return TreeView::STATUS_EXPANDED; + + //it's either reduced or empty + switch (flatTree[row].type_) + { + case TreeView::TYPE_DIRECTORY: + case TreeView::TYPE_ROOT: + return flatTree[row].node_->firstFileId || !flatTree[row].node_->subDirs.empty() ? TreeView::STATUS_REDUCED : TreeView::STATUS_EMPTY; + + case TreeView::TYPE_FILES: + return TreeView::STATUS_EMPTY; + } + } + return TreeView::STATUS_EMPTY; +} + + +void TreeView::expandNode(size_t row) +{ + if (getStatus(row) != TreeView::STATUS_REDUCED) + { + assert(false); + return; + } + + if (row < flatTree.size()) + { + std::vector newLines; + + switch (flatTree[row].type_) + { + case TreeView::TYPE_ROOT: + case TreeView::TYPE_DIRECTORY: + getChildren(*flatTree[row].node_, flatTree[row].level_ + 1, newLines); + break; + case TreeView::TYPE_FILES: + break; + } + flatTree.insert(flatTree.begin() + row + 1, newLines.begin(), newLines.end()); + } +} + + +void TreeView::reduceNode(size_t row) +{ + if (row < flatTree.size()) + { + const unsigned int parentLevel = flatTree[row].level_; + + bool done = false; + flatTree.erase(std::remove_if(flatTree.begin() + row + 1, flatTree.end(), + [&](const TreeLine& line) -> bool + { + if (done) + return false; + if (line.level_ > parentLevel) + return true; + else + { + done = true; + return false; + } + }), flatTree.end()); + } +} + + +ptrdiff_t TreeView::getParent(size_t row) const +{ + if (row < flatTree.size()) + { + const auto level = flatTree[row].level_; + + while (row-- > 0) + if (flatTree[row].level_ < level) + return row; + } + return -1; +} + + +void TreeView::updateCmpResult(bool hideFiltered, + bool leftOnlyFilesActive, + bool rightOnlyFilesActive, + bool leftNewerFilesActive, + bool rightNewerFilesActive, + bool differentFilesActive, + bool equalFilesActive, + bool conflictFilesActive) +{ + updateView([hideFiltered, //make sure the predicate can be stored safely! + leftOnlyFilesActive, + rightOnlyFilesActive, + leftNewerFilesActive, + rightNewerFilesActive, + differentFilesActive, + equalFilesActive, + conflictFilesActive](const FileSystemObject& fsObj) -> bool + { + if (hideFiltered && !fsObj.isActive()) + return false; + + switch (fsObj.getCategory()) + { + case FILE_LEFT_SIDE_ONLY: + return leftOnlyFilesActive; + case FILE_RIGHT_SIDE_ONLY: + return rightOnlyFilesActive; + case FILE_LEFT_NEWER: + return leftNewerFilesActive; + case FILE_RIGHT_NEWER: + return rightNewerFilesActive; + case FILE_DIFFERENT: + return differentFilesActive; + case FILE_EQUAL: + case FILE_DIFFERENT_METADATA: //= sub-category of equal + return equalFilesActive; + case FILE_CONFLICT: + return conflictFilesActive; + } + assert(false); + return true; + }); +} + + +void TreeView::updateSyncPreview(bool hideFiltered, + bool syncCreateLeftActive, + bool syncCreateRightActive, + bool syncDeleteLeftActive, + bool syncDeleteRightActive, + bool syncDirOverwLeftActive, + bool syncDirOverwRightActive, + bool syncDirNoneActive, + bool syncEqualActive, + bool conflictFilesActive) +{ + updateView([hideFiltered, //make sure the predicate can be stored safely! + syncCreateLeftActive, + syncCreateRightActive, + syncDeleteLeftActive, + syncDeleteRightActive, + syncDirOverwLeftActive, + syncDirOverwRightActive, + syncDirNoneActive, + syncEqualActive, + conflictFilesActive](const FileSystemObject& fsObj) -> bool + { + if (hideFiltered && !fsObj.isActive()) + return false; + + switch (fsObj.getSyncOperation()) + { + case SO_CREATE_NEW_LEFT: + return syncCreateLeftActive; + case SO_CREATE_NEW_RIGHT: + return syncCreateRightActive; + case SO_DELETE_LEFT: + return syncDeleteLeftActive; + case SO_DELETE_RIGHT: + return syncDeleteRightActive; + case SO_OVERWRITE_RIGHT: + case SO_COPY_METADATA_TO_RIGHT: + case SO_MOVE_RIGHT_SOURCE: + case SO_MOVE_RIGHT_TARGET: + return syncDirOverwRightActive; + case SO_OVERWRITE_LEFT: + case SO_COPY_METADATA_TO_LEFT: + case SO_MOVE_LEFT_SOURCE: + case SO_MOVE_LEFT_TARGET: + return syncDirOverwLeftActive; + case SO_DO_NOTHING: + return syncDirNoneActive; + case SO_EQUAL: + return syncEqualActive; + case SO_UNRESOLVED_CONFLICT: + return conflictFilesActive; + } + assert(false); + return true; + }); +} + + +void TreeView::setData(FolderComparison& newData) +{ + std::vector().swap(flatTree); //free mem + std::vector().swap(folderCmpView); // + folderCmp = newData; + + //remove truly empty folder pairs as early as this: we want to distinguish single/multiple folder pair cases by looking at "folderCmp" + vector_remove_if(folderCmp, [](const std::shared_ptr& baseObj) + { + return baseObj->getBaseDirPf().empty() && + baseObj->getBaseDirPf().empty(); + }); +} + + +std::unique_ptr TreeView::getLine(size_t row) const +{ + if (row < flatTree.size()) + { + const auto level = flatTree[row].level_; + const int percent = flatTree[row].percent_; + + switch (flatTree[row].type_) + { + case TreeView::TYPE_ROOT: + { + const auto* root = static_cast(flatTree[row].node_); + return make_unique(percent, root->bytesGross, root->itemCountGross, getStatus(row), *(root->baseDirObj), root->displayName); + } + break; + + case TreeView::TYPE_DIRECTORY: + { + const auto* dir = static_cast(flatTree[row].node_); + if (auto dirObj = dynamic_cast(FileSystemObject::retrieve(dir->objId))) + return make_unique(percent, dir->bytesGross, dir->itemCountGross, level, getStatus(row), *dirObj); + } + break; + + case TreeView::TYPE_FILES: + { + const auto* parentDir = flatTree[row].node_; + if (auto firstFile = FileSystemObject::retrieve(parentDir->firstFileId)) + { + std::vector filesAndLinks; + HierarchyObject& parent = firstFile->parent(); + + //lazy evaluation: recheck "lastViewFilterPred" again rather than buffer and bloat "lastViewFilterPred" + for (FileSystemObject& fsObj : parent.refSubFiles()) + if (lastViewFilterPred(fsObj)) + filesAndLinks.push_back(&fsObj); + + for (FileSystemObject& fsObj : parent.refSubLinks()) + if (lastViewFilterPred(fsObj)) + filesAndLinks.push_back(&fsObj); + + return make_unique(percent, parentDir->bytesNet, parentDir->itemCountNet, level, filesAndLinks); + } + } + break; + } + } + return nullptr; +} + +//########################################################################################################## + +namespace +{ +const wxColour COLOR_LEVEL0(0xcc, 0xcc, 0xff); +const wxColour COLOR_LEVEL1(0xcc, 0xff, 0xcc); +const wxColour COLOR_LEVEL2(0xff, 0xff, 0x99); + +const wxColour COLOR_LEVEL3(0xcc, 0xcc, 0xcc); +const wxColour COLOR_LEVEL4(0xff, 0xcc, 0xff); +const wxColour COLOR_LEVEL5(0x99, 0xff, 0xcc); + +const wxColour COLOR_LEVEL6(0xcc, 0xcc, 0x99); +const wxColour COLOR_LEVEL7(0xff, 0xcc, 0xcc); +const wxColour COLOR_LEVEL8(0xcc, 0xff, 0x99); + +const wxColour COLOR_LEVEL9 (0xff, 0xff, 0xcc); +const wxColour COLOR_LEVEL10(0xcc, 0xff, 0xff); +const wxColour COLOR_LEVEL11(0xff, 0xcc, 0x99); + +const wxColour COLOR_PERCENTAGE_BORDER (198, 198, 198); +const wxColour COLOR_PERCENTAGE_BACKGROUND(0xf8, 0xf8, 0xf8); + +//const wxColor COLOR_TREE_SELECTION_GRADIENT_FROM = wxColor( 89, 255, 99); //green: HSV: 88, 255, 172 +//const wxColor COLOR_TREE_SELECTION_GRADIENT_TO = wxColor(225, 255, 227); // HSV: 88, 255, 240 +const wxColor COLOR_TREE_SELECTION_GRADIENT_FROM = getColorSelectionGradientFrom(); +const wxColor COLOR_TREE_SELECTION_GRADIENT_TO = getColorSelectionGradientTo (); + +const int iconSizeSmall = IconBuffer::getSize(IconBuffer::SIZE_SMALL); + +class GridDataNavi : private wxEvtHandler, public GridData +{ +public: + GridDataNavi(Grid& grid, const std::shared_ptr& treeDataView) : treeDataView_(treeDataView), + fileIcon(IconBuffer(IconBuffer::SIZE_SMALL).genericFileIcon()), + dirIcon (IconBuffer(IconBuffer::SIZE_SMALL).genericDirIcon ()), + rootBmp(getResourceImage(L"rootFolder").ConvertToImage().Scale(iconSizeSmall, iconSizeSmall, wxIMAGE_QUALITY_HIGH)), + widthNodeIcon(iconSizeSmall), + widthLevelStep(widthNodeIcon), + widthNodeStatus(getResourceImage(L"nodeExpanded").GetWidth()), + grid_(grid), + showPercentBar(true) + { + grid.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(GridDataNavi::onKeyDown), nullptr, this); + grid.Connect(EVENT_GRID_MOUSE_LEFT_DOWN, GridClickEventHandler(GridDataNavi::onMouseLeft ), nullptr, this); + grid.Connect(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEventHandler(GridDataNavi::onMouseLeftDouble ), nullptr, this); + grid.Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(GridDataNavi::onGridLabelContext), nullptr, this ); + grid.Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(GridDataNavi::onGridLabelLeftClick ), nullptr, this ); + } + + void setShowPercentage(bool value) { showPercentBar = value; grid_.Refresh(); } + bool getShowPercentage() const { return showPercentBar; } + +private: + virtual size_t getRowCount() const { return treeDataView_ ? treeDataView_->linesTotal() : 0; } + + virtual wxString getToolTip(size_t row, ColumnType colType) const override + { + switch (static_cast(colType)) + { + case COL_TYPE_NAVI_BYTES: + case COL_TYPE_NAVI_ITEM_COUNT: + break; + + case COL_TYPE_NAVI_DIRECTORY: + if (treeDataView_) + if (std::unique_ptr node = treeDataView_->getLine(row)) + if (const TreeView::RootNode* root = dynamic_cast(node.get())) + { + const wxString& dirLeft = utfCvrtTo(root->baseDirObj_.getBaseDirPf()); + const wxString& dirRight = utfCvrtTo(root->baseDirObj_.getBaseDirPf()); + if (dirLeft.empty()) + return dirRight; + else if (dirRight.empty()) + return dirLeft; + return dirLeft + L" \u2212 \n" + dirRight; //\u2212 = unicode minus + } + break; + } + return wxString(); + } + + virtual wxString getValue(size_t row, ColumnType colType) const + { + if (treeDataView_) + { + if (std::unique_ptr node = treeDataView_->getLine(row)) + switch (static_cast(colType)) + { + case COL_TYPE_NAVI_BYTES: + return filesizeToShortString(to(node->bytes_)); + + case COL_TYPE_NAVI_DIRECTORY: + if (const TreeView::RootNode* root = dynamic_cast(node.get())) + return utfCvrtTo(root->displayName_); + else if (const TreeView::DirNode* dir = dynamic_cast(node.get())) + return utfCvrtTo(dir->dirObj_.getObjShortName()); + else if (dynamic_cast(node.get())) + return _("Files"); + break; + + case COL_TYPE_NAVI_ITEM_COUNT: + return toGuiString(node->itemCount_); + } + } + return wxString(); + } + + virtual void renderColumnLabel(Grid& tree, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted) override + { + wxRect rectInside = drawColumnLabelBorder(dc, rect); + drawColumnLabelBackground(dc, rectInside, highlighted); + + rectInside.x += COLUMN_GAP_LEFT; + rectInside.width -= COLUMN_GAP_LEFT; + drawColumnLabelText(dc, rectInside, getColumnLabel(colType)); + + if (treeDataView_) //draw sort marker + { + auto sortInfo = treeDataView_->getSortDirection(); + if (colType == static_cast(sortInfo.first)) + { + const wxBitmap& marker = getResourceImage(sortInfo.second ? L"sortAscending" : L"sortDescending"); + wxPoint markerBegin = rectInside.GetTopLeft() + wxPoint((rectInside.width - marker.GetWidth()) / 2, 0); + dc.DrawBitmap(marker, markerBegin, true); //respect 2-pixel gap + } + } + } + + static const int GAP_SIZE = 2; + + virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) override + { + if (enabled) + { + if (selected) + dc.GradientFillLinear(rect, COLOR_TREE_SELECTION_GRADIENT_FROM, COLOR_TREE_SELECTION_GRADIENT_TO, wxEAST); + //ignore focus + else + clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + } + else + clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); + } + + virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool selected) override + { + //wxRect rectTmp= drawCellBorder(dc, rect); + wxRect rectTmp = rect; + + // Partitioning: + // ________________________________________________________________________________ + // | space | gap | percentage bar | 2 x gap | node status | gap |icon | gap | rest | + // -------------------------------------------------------------------------------- + // -> synchronize renderCell() <-> getBestSize() <-> onMouseLeft() + + if (static_cast(colType) == COL_TYPE_NAVI_DIRECTORY && treeDataView_) + { + if (std::unique_ptr node = treeDataView_->getLine(row)) + { + ////clear first secion: + //clearArea(dc, wxRect(rect.GetTopLeft(), wxSize( + // node->level_ * widthLevelStep + GAP_SIZE + //width + // (showPercentBar ? widthPercentBar + 2 * GAP_SIZE : 0) + // + // widthNodeStatus + GAP_SIZE + widthNodeIcon + GAP_SIZE, // + // rect.height)), wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + + //consume space + rectTmp.x += static_cast(node->level_) * widthLevelStep; + rectTmp.width -= static_cast(node->level_) * widthLevelStep; + + rectTmp.x += GAP_SIZE; + rectTmp.width -= GAP_SIZE; + + if (rectTmp.width > 0) + { + //percentage bar + if (showPercentBar) + { + const wxColour brushCol = [&]() -> wxColour + { + switch (node->level_ % 12) + { + case 0: + return COLOR_LEVEL0; + case 1: + return COLOR_LEVEL1; + case 2: + return COLOR_LEVEL2; + case 3: + return COLOR_LEVEL3; + case 4: + return COLOR_LEVEL4; + case 5: + return COLOR_LEVEL5; + case 6: + return COLOR_LEVEL6; + case 7: + return COLOR_LEVEL7; + case 8: + return COLOR_LEVEL8; + case 9: + return COLOR_LEVEL9; + case 10: + return COLOR_LEVEL10; + default: + return COLOR_LEVEL11; + } + }(); + + const wxRect areaPerc(rectTmp.x, rectTmp.y + 2, widthPercentBar, rectTmp.height - 4); + { + //clear background + wxDCPenChanger dummy (dc, COLOR_PERCENTAGE_BORDER); + wxDCBrushChanger dummy2(dc, COLOR_PERCENTAGE_BACKGROUND); + dc.DrawRectangle(areaPerc); + + //inner area + dc.SetPen (brushCol); + dc.SetBrush(brushCol); + + wxRect areaPercTmp = areaPerc; + areaPercTmp.Deflate(1); //do not include border + areaPercTmp.width = numeric::round(areaPercTmp.width * node->percent_ / 100.0); + dc.DrawRectangle(areaPercTmp); + } + + wxDCTextColourChanger dummy3(dc, *wxBLACK); //accessibility: always set both foreground AND background colors! + dc.DrawLabel(numberTo(node->percent_) + L"%", areaPerc, wxALIGN_CENTER); + + rectTmp.x += widthPercentBar + 2 * GAP_SIZE; + rectTmp.width -= widthPercentBar + 2 * GAP_SIZE; + } + if (rectTmp.width > 0) + { + //node status + auto drawStatus = [&](const wchar_t* image) + { + const wxBitmap& bmp = getResourceImage(image); + + wxRect rectStat(rectTmp.GetTopLeft(), wxSize(bmp.GetWidth(), bmp.GetHeight())); + rectStat.y += (rectTmp.height - rectStat.height) / 2; + + //clearArea(dc, rectStat, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + clearArea(dc, rectStat, *wxWHITE); //accessibility: always set both foreground AND background colors! + drawBitmapRtlMirror(dc, bmp, rectStat, wxALIGN_CENTER, buffer); + }; + + switch (node->status_) + { + case TreeView::STATUS_EXPANDED: + drawStatus(L"nodeExpanded"); + break; + case TreeView::STATUS_REDUCED: + drawStatus(L"nodeReduced"); + break; + case TreeView::STATUS_EMPTY: + break; + } + + rectTmp.x += widthNodeStatus + GAP_SIZE; + rectTmp.width -= widthNodeStatus + GAP_SIZE; + if (rectTmp.width > 0) + { + wxBitmap nodeIcon; + bool isActive = true; + //icon + if (dynamic_cast(node.get())) + nodeIcon = rootBmp; + else if (auto dir = dynamic_cast(node.get())) + { + nodeIcon = dirIcon; + isActive = dir->dirObj_.isActive(); + } + else if (dynamic_cast(node.get())) + nodeIcon = fileIcon; + + if (isActive) + drawBitmapRtlNoMirror(dc, nodeIcon, rectTmp, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, buffer); + + else + drawBitmapRtlNoMirror(dc, wxBitmap(nodeIcon.ConvertToImage().ConvertToGreyscale(1.0 / 3, 1.0 / 3, 1.0 / 3)), //treat all channels equally! + rectTmp, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, buffer); + + rectTmp.x += widthNodeIcon + GAP_SIZE; + rectTmp.width -= widthNodeIcon + GAP_SIZE; + + if (rectTmp.width > 0) + drawCellText(dc, rectTmp, getValue(row, colType), isActive, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); + } + } + } + } + } + else + { + int alignment = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL; + + //have file size and item count right-justified (but don't change for RTL languages) + if ((static_cast(colType) == COL_TYPE_NAVI_BYTES || + static_cast(colType) == COL_TYPE_NAVI_ITEM_COUNT) && grid_.GetLayoutDirection() != wxLayout_RightToLeft) + { + rectTmp.width -= 2 * GAP_SIZE; + alignment = wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL; + } + else //left-justified + { + rectTmp.x += 2 * GAP_SIZE; + rectTmp.width -= 2 * GAP_SIZE; + } + + drawCellText(dc, rectTmp, getValue(row, colType), true, alignment); + } + } + + virtual int getBestSize(wxDC& dc, size_t row, ColumnType colType) override + { + // -> synchronize renderCell() <-> getBestSize() <-> onMouseLeft() + + if (static_cast(colType) == COL_TYPE_NAVI_DIRECTORY && treeDataView_) + { + if (std::unique_ptr node = treeDataView_->getLine(row)) + return node->level_ * widthLevelStep + GAP_SIZE + (showPercentBar ? widthPercentBar + 2 * GAP_SIZE : 0) + widthNodeStatus + GAP_SIZE + + widthNodeIcon + GAP_SIZE + dc.GetTextExtent(getValue(row, colType)).GetWidth() + + GAP_SIZE; //additional gap from right + else + return 0; + } + else + return 2 * GAP_SIZE + dc.GetTextExtent(getValue(row, colType)).GetWidth() + + 2 * GAP_SIZE; //include gap from right! + } + + virtual wxString getColumnLabel(ColumnType colType) const + { + switch (static_cast(colType)) + { + case COL_TYPE_NAVI_BYTES: + return _("Size"); + case COL_TYPE_NAVI_DIRECTORY: + return _("Name"); + case COL_TYPE_NAVI_ITEM_COUNT: + return _("Items"); + } + return wxEmptyString; + } + + void onMouseLeft(GridClickEvent& event) + { + if (treeDataView_) + { + bool clickOnNodeStatus = false; + if (static_cast(event.colType_) == COL_TYPE_NAVI_DIRECTORY) + if (std::unique_ptr node = treeDataView_->getLine(event.row_)) + { + const int absX = grid_.CalcUnscrolledPosition(event.GetPosition()).x; + const wxRect cellArea = grid_.getCellArea(event.row_, event.colType_); + if (cellArea.width > 0 && cellArea.height > 0) + { + const int tolerance = 1; + const int xNodeStatusFirst = -tolerance + cellArea.x + static_cast(node->level_) * widthLevelStep + GAP_SIZE + (showPercentBar ? widthPercentBar + 2 * GAP_SIZE : 0); + const int xNodeStatusLast = (xNodeStatusFirst + tolerance) + widthNodeStatus + tolerance; + // -> synchronize renderCell() <-> getBestSize() <-> onMouseLeft() + + if (xNodeStatusFirst <= absX && absX < xNodeStatusLast) + clickOnNodeStatus = true; + } + } + //-------------------------------------------------------------------------------------------------- + + if (clickOnNodeStatus) + switch (treeDataView_->getStatus(event.row_)) + { + case TreeView::STATUS_EXPANDED: + return reduceNode(event.row_); + case TreeView::STATUS_REDUCED: + return expandNode(event.row_); + case TreeView::STATUS_EMPTY: + break; + } + } + event.Skip(); + } + + void onMouseLeftDouble(GridClickEvent& event) + { + if (treeDataView_) + switch (treeDataView_->getStatus(event.row_)) + { + case TreeView::STATUS_EXPANDED: + return reduceNode(event.row_); + case TreeView::STATUS_REDUCED: + return expandNode(event.row_); + case TreeView::STATUS_EMPTY: + break; + } + event.Skip(); + } + + void onKeyDown(wxKeyEvent& event) + { + int keyCode = event.GetKeyCode(); + if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft) + { + if (keyCode == WXK_LEFT) + keyCode = WXK_RIGHT; + else if (keyCode == WXK_RIGHT) + keyCode = WXK_LEFT; + else if (keyCode == WXK_NUMPAD_LEFT) + keyCode = WXK_NUMPAD_RIGHT; + else if (keyCode == WXK_NUMPAD_RIGHT) + keyCode = WXK_NUMPAD_LEFT; + } + + const size_t rowCount = grid_.getRowCount(); + if (rowCount == 0) return; + + size_t row = grid_.getGridCursor(); + if (event.ShiftDown()) + ; + else if (event.ControlDown()) + ; + else + switch (keyCode) + { + case WXK_LEFT: + case WXK_NUMPAD_LEFT: + case WXK_NUMPAD_SUBTRACT: //http://msdn.microsoft.com/en-us/library/ms971323.aspx#atg_keyboardshortcuts_windows_shortcut_keys + if (treeDataView_) + switch (treeDataView_->getStatus(row)) + { + case TreeView::STATUS_EXPANDED: + return reduceNode(row); + case TreeView::STATUS_REDUCED: + case TreeView::STATUS_EMPTY: + + const int parentRow = treeDataView_->getParent(row); + if (parentRow >= 0) + grid_.setGridCursor(parentRow); + break; + } + return; //swallow event + + case WXK_RIGHT: + case WXK_NUMPAD_RIGHT: + case WXK_NUMPAD_ADD: + if (treeDataView_) + switch (treeDataView_->getStatus(row)) + { + case TreeView::STATUS_EXPANDED: + grid_.setGridCursor(std::min(rowCount - 1, row + 1)); + break; + case TreeView::STATUS_REDUCED: + return expandNode(row); + case TreeView::STATUS_EMPTY: + break; + } + return; //swallow event + } + + event.Skip(); + } + + void onGridLabelContext(GridClickEvent& event) + { + ContextMenu menu; + + //-------------------------------------------------------------------------------------------------------- + menu.addCheckBox(_("Percentage"), [this] { setShowPercentage(!getShowPercentage()); }, getShowPercentage()); + //-------------------------------------------------------------------------------------------------------- + auto toggleColumn = [&](const Grid::ColumnAttribute& ca) + { + auto colAttr = grid_.getColumnConfig(); + + for (auto it = colAttr.begin(); it != colAttr.end(); ++it) + if (it->type_ == ca.type_) + { + it->visible_ = !ca.visible_; + grid_.setColumnConfig(colAttr); + return; + } + }; + + for (const Grid::ColumnAttribute& ca : grid_.getColumnConfig()) + { + menu.addCheckBox(getColumnLabel(ca.type_), [ca, toggleColumn]() { toggleColumn(ca); }, + ca.visible_, ca.type_ != static_cast(COL_TYPE_NAVI_DIRECTORY)); //do not allow user to hide file name column! + } + //-------------------------------------------------------------------------------------------------------- + menu.addSeparator(); + + auto setDefaultColumns = [&] + { + setShowPercentage(defaultValueShowPercentage); + grid_.setColumnConfig(treeview::convertConfig(getDefaultColumnAttributesNavi())); + }; + menu.addItem(_("&Default"), setDefaultColumns); //'&' -> reuse text from "default" buttons elsewhere + + menu.popup(grid_); + + event.Skip(); + } + + void onGridLabelLeftClick(GridClickEvent& event) + { + if (treeDataView_) + { + const auto colTypeNavi = static_cast(event.colType_); + bool sortAscending = TreeView::getDefaultSortDirection(colTypeNavi); + + const auto sortInfo = treeDataView_->getSortDirection(); + if (sortInfo.first == colTypeNavi) + sortAscending = !sortInfo.second; + + treeDataView_->setSortDirection(colTypeNavi, sortAscending); + grid_.clearSelection(ALLOW_GRID_EVENT); + grid_.Refresh(); + } + } + + void expandNode(size_t row) + { + treeDataView_->expandNode(row); + grid_.Refresh(); //implicitly clears selection (changed row count after expand) + grid_.setGridCursor(row); + //grid_.autoSizeColumns(); -> doesn't look as good as expected + } + + void reduceNode(size_t row) + { + treeDataView_->reduceNode(row); + grid_.Refresh(); + grid_.setGridCursor(row); + } + + std::shared_ptr treeDataView_; + const wxBitmap fileIcon; + const wxBitmap dirIcon; + const wxBitmap rootBmp; + std::unique_ptr buffer; //avoid costs of recreating this temporal variable + const int widthNodeIcon; + const int widthLevelStep; + const int widthNodeStatus; + static const int widthPercentBar = 60; + Grid& grid_; + bool showPercentBar; +}; +} + + +void treeview::init(Grid& grid, const std::shared_ptr& treeDataView) +{ + grid.setDataProvider(std::make_shared(grid, treeDataView)); + grid.showRowLabel(false); + + const int rowHeight = std::max(IconBuffer::getSize(IconBuffer::SIZE_SMALL), grid.getMainWin().GetCharHeight()) + 2; //allow 1 pixel space on top and bottom; dearly needed on OS X! + grid.setRowHeight(rowHeight); +} + + +void treeview::setShowPercentage(Grid& grid, bool value) +{ + if (auto* prov = dynamic_cast(grid.getDataProvider())) + prov->setShowPercentage(value); + else + assert(false); +} + + +bool treeview::getShowPercentage(const Grid& grid) +{ + if (auto* prov = dynamic_cast(grid.getDataProvider())) + return prov->getShowPercentage(); + assert(false); + return true; +} + + +namespace +{ +std::vector makeConsistent(const std::vector& attribs) +{ + std::set usedTypes; + + std::vector output; + //remove duplicates + std::copy_if(attribs.begin(), attribs.end(), std::back_inserter(output), + [&](const ColumnAttributeNavi& a) { return usedTypes.insert(a.type_).second; }); + + //make sure each type is existing! + const auto& defAttr = getDefaultColumnAttributesNavi(); + std::copy_if(defAttr.begin(), defAttr.end(), std::back_inserter(output), + [&](const ColumnAttributeNavi& a) { return usedTypes.insert(a.type_).second; }); + + return output; +} +} + +std::vector treeview::convertConfig(const std::vector& attribs) +{ + const auto& attribClean = makeConsistent(attribs); + + std::vector output; + std::transform(attribClean.begin(), attribClean.end(), std::back_inserter(output), + [&](const ColumnAttributeNavi& ca) { return Grid::ColumnAttribute(static_cast(ca.type_), ca.offset_, ca.stretch_, ca.visible_); }); + + return output; +} + + +std::vector treeview::convertConfig(const std::vector& attribs) +{ + std::vector output; + + std::transform(attribs.begin(), attribs.end(), std::back_inserter(output), + [&](const Grid::ColumnAttribute& ca) { return ColumnAttributeNavi(static_cast(ca.type_), ca.offset_, ca.stretch_, ca.visible_); }); + + return makeConsistent(output); +} diff --git a/FreeFileSync/Source/ui/tree_view.h b/FreeFileSync/Source/ui/tree_view.h new file mode 100644 index 00000000..d42d898f --- /dev/null +++ b/FreeFileSync/Source/ui/tree_view.h @@ -0,0 +1,188 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef TREE_H_INCLUDED_841703190201835280256673425 +#define TREE_H_INCLUDED_841703190201835280256673425 + +#include +#include +#include +#include "column_attr.h" +#include "../file_hierarchy.h" + +namespace zen +{ +//tree view of FolderComparison +class TreeView +{ +public: + TreeView() : + sortColumn(defaultValueLastSortColumn), + sortAscending(defaultValueLastSortAscending) {} + + void setData(FolderComparison& newData); //set data, taking (partial) ownership + + //apply view filter: comparison results + void updateCmpResult(bool hideFiltered, + bool leftOnlyFilesActive, + bool rightOnlyFilesActive, + bool leftNewerFilesActive, + bool rightNewerFilesActive, + bool differentFilesActive, + bool equalFilesActive, + bool conflictFilesActive); + + //apply view filter: synchronization preview + void updateSyncPreview(bool hideFiltered, + bool syncCreateLeftActive, + bool syncCreateRightActive, + bool syncDeleteLeftActive, + bool syncDeleteRightActive, + bool syncDirOverwLeftActive, + bool syncDirOverwRightActive, + bool syncDirNoneActive, + bool syncEqualActive, + bool conflictFilesActive); + + enum NodeStatus + { + STATUS_EXPANDED, + STATUS_REDUCED, + STATUS_EMPTY + }; + + //--------------------------------------------------------------------- + struct Node + { + Node(int percent, UInt64 bytes, int itemCount, unsigned int level, NodeStatus status) : + percent_(percent), level_(level), status_(status), bytes_(bytes), itemCount_(itemCount) {} + virtual ~Node() {} + + const int percent_; //[0, 100] + const unsigned int level_; + const NodeStatus status_; + const UInt64 bytes_; + const int itemCount_; + }; + + struct FilesNode : public Node + { + FilesNode(int percent, UInt64 bytes, int itemCount, unsigned int level, const std::vector& filesAndLinks) : Node(percent, bytes, itemCount, level, STATUS_EMPTY), filesAndLinks_(filesAndLinks) {} + std::vector filesAndLinks_; //files or symlinks; pointers are bound! + }; + + struct DirNode : public Node + { + DirNode(int percent, UInt64 bytes, int itemCount, unsigned int level, NodeStatus status, DirPair& dirObj) : Node(percent, bytes, itemCount, level, status), dirObj_(dirObj) {} + DirPair& dirObj_; + }; + + struct RootNode : public Node + { + RootNode(int percent, UInt64 bytes, int itemCount, NodeStatus status, BaseDirPair& baseDirObj, const Zstring displayName) : Node(percent, bytes, itemCount, 0, status), baseDirObj_(baseDirObj), displayName_(displayName) {} + BaseDirPair& baseDirObj_; + Zstring displayName_; + }; + + std::unique_ptr getLine(size_t row) const; //return nullptr on error + size_t linesTotal() const { return flatTree.size(); } + + void expandNode(size_t row); + void reduceNode(size_t row); + NodeStatus getStatus(size_t row) const; + ptrdiff_t getParent(size_t row) const; //return < 0 if none + + void setSortDirection(ColumnTypeNavi colType, bool ascending); //apply permanently! + std::pair getSortDirection() { return std::make_pair(sortColumn, sortAscending); } + static bool getDefaultSortDirection(ColumnTypeNavi colType); //ascending? + +private: + struct DirNodeImpl; + + struct Container + { + Container() : itemCountGross(), itemCountNet(), firstFileId(nullptr) {} + UInt64 bytesGross; + UInt64 bytesNet; //bytes for files on view in this directory only + int itemCountGross; + int itemCountNet; //number of files on view for in this directory only + + std::vector subDirs; + FileSystemObject::ObjectId firstFileId; //weak pointer to first FilePair or SymlinkPair + //- "compress" algorithm may hide file nodes for directories with a single included file, i.e. itemCountGross == itemCountNet == 1 + //- a HierarchyObject* would a better fit, but we need weak pointer semantics! + //- a std::vector would be a better design, but we don't want a second memory structure as large as custom grid! + }; + + struct DirNodeImpl : public Container + { + DirNodeImpl() : objId(nullptr) {} + FileSystemObject::ObjectId objId; //weak pointer to DirPair + }; + + struct RootNodeImpl : public Container + { + RootNodeImpl() {} + std::shared_ptr baseDirObj; + Zstring displayName; + }; + + enum NodeType + { + TYPE_ROOT, //-> RootNodeImpl + TYPE_DIRECTORY, //-> DirNodeImpl + TYPE_FILES //-> Container + }; + + struct TreeLine + { + TreeLine(unsigned int level, int percent, const Container* node, enum NodeType type) : level_(level), percent_(percent), node_(node), type_(type) {} + + unsigned int level_; + int percent_; //[0, 100] + const Container* node_; // + NodeType type_; //we increase size of "flatTree" using C-style types rather than have a polymorphic "folderCmpView" + }; + + static void compressNode(Container& cont); + template + static void extractVisibleSubtree(HierarchyObject& hierObj, Container& cont, Function includeObject); + void getChildren(const Container& cont, unsigned int level, std::vector& output); + template void updateView(Predicate pred); + void applySubView(std::vector&& newView); + + template static void sortSingleLevel(std::vector& items, ColumnTypeNavi columnType); + template struct LessShortName; + + std::vector flatTree; //collapsable/expandable sub-tree of folderCmpView -> always sorted! + /* /|\ + | (update...) + | */ + std::vector folderCmpView; //partial view on folderCmp -> unsorted (cannot be, because files are not a separate entity) + std::function lastViewFilterPred; //buffer view filter predicate for lazy evaluation of files/symlinks corresponding to a TYPE_FILES node + /* /|\ + | (update...) + | */ + std::vector> folderCmp; //full raw data + + ColumnTypeNavi sortColumn; + bool sortAscending; +}; + + +namespace treeview +{ +void init(Grid& grid, const std::shared_ptr& treeDataView); + +void setShowPercentage(Grid& grid, bool value); +bool getShowPercentage(const Grid& grid); + +std::vector convertConfig(const std::vector& attribs); //+ make consistent +std::vector convertConfig(const std::vector& attribs); // +} +} + +#endif //TREE_H_INCLUDED_841703190201835280256673425 diff --git a/FreeFileSync/Source/ui/triple_splitter.cpp b/FreeFileSync/Source/ui/triple_splitter.cpp new file mode 100644 index 00000000..fbdd22d7 --- /dev/null +++ b/FreeFileSync/Source/ui/triple_splitter.cpp @@ -0,0 +1,241 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "triple_splitter.h" +#include + +using namespace zen; + + +namespace +{ +//------------ Grid Constants ------------------------------- +const int SASH_HIT_TOLERANCE = 5; //currently only a placebo! +const int SASH_SIZE = 10; +const double SASH_GRAVITY = 0.5; //value within [0, 1]; 1 := resize left only, 0 := resize right only +const int CHILD_WINDOW_MIN_SIZE = 50; //min. size of managed windows + +const wxColor COLOR_SASH_GRADIENT_FROM = wxColour(192, 192, 192); //light grey +const wxColor COLOR_SASH_GRADIENT_TO = *wxWHITE; +} + +TripleSplitter::TripleSplitter(wxWindow* parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style) : wxWindow(parent, id, pos, size, style | wxTAB_TRAVERSAL), //tab between windows + centerOffset(0), + windowL(nullptr), + windowC(nullptr), + windowR(nullptr) +{ + Connect(wxEVT_PAINT, wxPaintEventHandler(TripleSplitter::onPaintEvent ), nullptr, this); + Connect(wxEVT_SIZE, wxSizeEventHandler (TripleSplitter::onSizeEvent ), nullptr, this); + //http://wiki.wxwidgets.org/Flicker-Free_Drawing + Connect(wxEVT_ERASE_BACKGROUND, wxEraseEventHandler(TripleSplitter::onEraseBackGround), nullptr, this); + + SetBackgroundStyle(wxBG_STYLE_PAINT); + + Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(TripleSplitter::onMouseLeftDown ), nullptr, this); + Connect(wxEVT_LEFT_UP, wxMouseEventHandler(TripleSplitter::onMouseLeftUp ), nullptr, this); + Connect(wxEVT_MOTION, wxMouseEventHandler(TripleSplitter::onMouseMovement ), nullptr, this); + Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(TripleSplitter::onLeaveWindow ), nullptr, this); + Connect(wxEVT_MOUSE_CAPTURE_LOST, wxMouseCaptureLostEventHandler(TripleSplitter::onMouseCaptureLost), nullptr, this); + Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(TripleSplitter::onMouseLeftDouble), nullptr, this); +} + + +TripleSplitter::~TripleSplitter() {} //make sure correct destructor gets created for std::unique_ptr + + +void TripleSplitter::updateWindowSizes() +{ + if (windowL && windowC && windowR) + { + const int centerPosX = getCenterPosX(); + const int centerWidth = getCenterWidth(); + + const wxRect clientRect = GetClientRect(); + + const int widthL = centerPosX; + const int windowRposX = widthL + centerWidth; + const int widthR = clientRect.width - windowRposX; + + windowL->SetSize(0, 0, widthL, clientRect.height); + windowC->SetSize(widthL + SASH_SIZE, 0, windowC->GetSize().GetWidth(), clientRect.height); + windowR->SetSize(windowRposX, 0, widthR, clientRect.height); + + wxClientDC dc(this); + drawSash(dc); + } +} + + +class TripleSplitter::SashMove +{ +public: + SashMove(wxWindow& wnd, int mousePosX, int centerOffset) : wnd_(wnd), mousePosX_(mousePosX), centerOffset_(centerOffset) + { + wnd_.SetCursor(wxCURSOR_SIZEWE); + wnd_.CaptureMouse(); + } + ~SashMove() + { + wnd_.SetCursor(*wxSTANDARD_CURSOR); + if (wnd_.HasCapture()) + wnd_.ReleaseMouse(); + } + int getMousePosXStart () const { return mousePosX_; } + int getCenterOffsetStart() const { return centerOffset_; } + +private: + wxWindow& wnd_; + const int mousePosX_; + const int centerOffset_; +}; + + +inline +int TripleSplitter::getCenterWidth() const +{ + return 2 * SASH_SIZE + (windowC ? windowC->GetSize().GetWidth() : 0); +} + + +int TripleSplitter::getCenterPosXOptimal() const +{ + const wxRect clientRect = GetClientRect(); + const int centerWidth = getCenterWidth(); + return (clientRect.width - centerWidth) * SASH_GRAVITY; //allowed to be negative for extreme client widths! +} + + +int TripleSplitter::getCenterPosX() const +{ + const wxRect clientRect = GetClientRect(); + const int centerWidth = getCenterWidth(); + const int centerPosXOptimal = getCenterPosXOptimal(); + + //normalize "centerPosXOptimal + centerOffset" + if (clientRect.width < 2 * CHILD_WINDOW_MIN_SIZE + centerWidth) + //use fixed "centeroffset" when "clientRect.width == 2 * CHILD_WINDOW_MIN_SIZE + centerWidth" + return centerPosXOptimal + CHILD_WINDOW_MIN_SIZE - static_cast(2 * CHILD_WINDOW_MIN_SIZE * SASH_GRAVITY); //avoid rounding error + //make sure transition between conditional branches is continuous! + return std::max(CHILD_WINDOW_MIN_SIZE, //make sure centerPosXOptimal + offset is within bounds + std::min(centerPosXOptimal + centerOffset, clientRect.width - CHILD_WINDOW_MIN_SIZE - centerWidth)); +} + + +void TripleSplitter::drawSash(wxDC& dc) +{ + const int centerPosX = getCenterPosX(); + const int centerWidth = getCenterWidth(); + + auto draw = [&](wxRect rect) + { + const int sash2ndHalf = 3; + rect.width -= sash2ndHalf; + dc.GradientFillLinear(rect, COLOR_SASH_GRADIENT_FROM, COLOR_SASH_GRADIENT_TO, wxEAST); + + rect.x += rect.width; + rect.width = sash2ndHalf; + dc.GradientFillLinear(rect, COLOR_SASH_GRADIENT_FROM, COLOR_SASH_GRADIENT_TO, wxWEST); + + static_assert(SASH_SIZE > sash2ndHalf, ""); + }; + + const wxRect rectSashL(centerPosX, 0, SASH_SIZE, GetClientRect().height); + const wxRect rectSashR(centerPosX + centerWidth - SASH_SIZE, 0, SASH_SIZE, GetClientRect().height); + + draw(rectSashL); + draw(rectSashR); +} + + +bool TripleSplitter::hitOnSashLine(int posX) const +{ + const int centerPosX = getCenterPosX(); + const int centerWidth = getCenterWidth(); + + //we don't get events outside of sash, so SASH_HIT_TOLERANCE is currently *useless* + auto hitSash = [&](int sashX) { return sashX - SASH_HIT_TOLERANCE <= posX && posX < sashX + SASH_SIZE + SASH_HIT_TOLERANCE; }; + + return hitSash(centerPosX) || hitSash(centerPosX + centerWidth - SASH_SIZE); //hit one of the two sash lines +} + + +void TripleSplitter::onMouseLeftDown(wxMouseEvent& event) +{ + activeMove.reset(); + + const int posX = event.GetPosition().x; + if (hitOnSashLine(posX)) + activeMove.reset(new SashMove(*this, posX, centerOffset)); + event.Skip(); +} + + +void TripleSplitter::onMouseLeftUp(wxMouseEvent& event) +{ + activeMove.reset(); //nothing else to do, actual work done by onMouseMovement() + event.Skip(); +} + + +void TripleSplitter::onMouseMovement(wxMouseEvent& event) +{ + if (activeMove) + { + centerOffset = activeMove->getCenterOffsetStart() + event.GetPosition().x - activeMove->getMousePosXStart(); + + //CAVEAT: function getCenterPosX() normalizes centerPosX *not* centerOffset! + //This can lead to the strange effect of window not immediately resizing when centerOffset is extremely off limits + //=> normalize centerOffset right here + centerOffset = getCenterPosX() - getCenterPosXOptimal(); + + updateWindowSizes(); + Update(); //no time to wait until idle event! + } + else + { + //we receive those only while above the sash, not the managed windows (except when the managed windows are disabled!) + const int posX = event.GetPosition().x; + if (hitOnSashLine(posX)) + SetCursor(wxCURSOR_SIZEWE); //set window-local only! + else + SetCursor(*wxSTANDARD_CURSOR); + } + event.Skip(); +} + + +void TripleSplitter::onLeaveWindow(wxMouseEvent& event) +{ + //even called when moving from sash over to managed windows! + if (!activeMove) + SetCursor(*wxSTANDARD_CURSOR); + event.Skip(); +} + + +void TripleSplitter::onMouseCaptureLost(wxMouseCaptureLostEvent& event) +{ + activeMove.reset(); + updateWindowSizes(); + //event.Skip(); -> we DID handle it! +} + + +void TripleSplitter::onMouseLeftDouble(wxMouseEvent& event) +{ + const int posX = event.GetPosition().x; + if (hitOnSashLine(posX)) + { + centerOffset = 0; //reset sash according to gravity + updateWindowSizes(); + } + event.Skip(); +} diff --git a/FreeFileSync/Source/ui/triple_splitter.h b/FreeFileSync/Source/ui/triple_splitter.h new file mode 100644 index 00000000..0577dabf --- /dev/null +++ b/FreeFileSync/Source/ui/triple_splitter.h @@ -0,0 +1,89 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef TRIPPLE_SPLIT_HEADER_8257804292846842573942534254 +#define TRIPPLE_SPLIT_HEADER_8257804292846842573942534254 + +#include +#include +#include +#include + +//a not-so-crappy splitter window + +/* manage three contained windows: + 1. left and right window are stretched + 2. middle window is fixed size + 3. middle window position can be changed via mouse with two sash lines + ----------------- + | | | | + | | | | + | | | | + ----------------- +*/ + +namespace zen +{ +class TripleSplitter : public wxWindow +{ +public: + TripleSplitter(wxWindow* parent, + wxWindowID id = wxID_ANY, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = 0); + + ~TripleSplitter(); + + void setupWindows(wxWindow* winL, wxWindow* winC, wxWindow* winR) + { + assert(winL->GetParent() == this && winC->GetParent() == this && winR->GetParent() == this && !GetSizer()); + windowL = winL; + windowC = winC; + windowR = winR; + updateWindowSizes(); + } + + int getSashOffset() const { return centerOffset; } + void setSashOffset(int off) { centerOffset = off; updateWindowSizes(); } + +private: + void onEraseBackGround(wxEraseEvent& event) {} + void onSizeEvent(wxSizeEvent& event) { updateWindowSizes(); event.Skip(); } + + void onPaintEvent(wxPaintEvent& event) + { + wxPaintDC dc(this); + drawSash(dc); + } + + void updateWindowSizes(); + int getCenterWidth() const; + int getCenterPosX() const; //return normalized posX + int getCenterPosXOptimal() const; + + void drawSash(wxDC& dc); + bool hitOnSashLine(int posX) const; + + void onMouseLeftDown(wxMouseEvent& event); + void onMouseLeftUp(wxMouseEvent& event); + void onMouseMovement(wxMouseEvent& event); + void onLeaveWindow(wxMouseEvent& event); + void onMouseCaptureLost(wxMouseCaptureLostEvent& event); + void onMouseLeftDouble(wxMouseEvent& event); + + class SashMove; + std::unique_ptr activeMove; + + int centerOffset; //offset to add after "gravity" stretching + + wxWindow* windowL; + wxWindow* windowC; + wxWindow* windowR; +}; +} + +#endif //TRIPPLE_SPLIT_HEADER_8257804292846842573942534254 diff --git a/FreeFileSync/Source/ui/wx_form_build_hide_warnings.h b/FreeFileSync/Source/ui/wx_form_build_hide_warnings.h new file mode 100644 index 00000000..71f28340 --- /dev/null +++ b/FreeFileSync/Source/ui/wx_form_build_hide_warnings.h @@ -0,0 +1,22 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef WX_FORM_BUILD_230948324234234 +#define WX_FORM_BUILD_230948324234234 + +//pamper over wxFormBuilder "sub-optimal" code + +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wunused-variable" +#ifndef __clang__ //clang seems to define __GNUC__, but doesn't support this warning +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#endif + +#elif defined _MSC_VER +#pragma warning(disable: 4189) +#endif + +#endif //WX_FORM_BUILD_230948324234234 diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h new file mode 100644 index 00000000..b3dca56b --- /dev/null +++ b/FreeFileSync/Source/version/version.h @@ -0,0 +1,9 @@ +#ifndef VERSION_HEADER_434343489702544325 +#define VERSION_HEADER_434343489702544325 + +namespace zen +{ +const wchar_t currentVersion[] = L"5.23"; //internal linkage! +} + +#endif diff --git a/Makefile b/Makefile deleted file mode 100644 index 49bb22a4..00000000 --- a/Makefile +++ /dev/null @@ -1,173 +0,0 @@ -BLAH_BLUBB_123= -#for some buggy reason the first row in the make file has no effect on Suse Linux! => make sure there's no important command -APPNAME = FreeFileSync -prefix = /usr -BINDIR = $(DESTDIR)$(prefix)/bin -SHAREDIR = $(DESTDIR)$(prefix)/share -APPSHAREDIR = $(SHAREDIR)/$(APPNAME) -DOCSHAREDIR = $(SHAREDIR)/doc/$(APPNAME) - -CXXFLAGS = -std=c++11 -Wall -pipe -O3 -DNDEBUG -DwxUSE_UNICODE -DWXINTL_NO_GETTEXT_MACRO -I. -include "zen/i18n.h" -include "zen/warn_static.h" -LINKFLAGS = - -#distinguish Linux/OSX builds -OPERATING_SYSTEM_NAME := $(shell uname) - -#################### Linux ############################ -ifeq ($(OPERATING_SYSTEM_NAME), Linux) -COMPILER_BIN=g++ -pthread -CXXFLAGS += -DZEN_LINUX - -#Gtk - support recycler/icon loading/no button border/grid scrolling -CXXFLAGS += `pkg-config --cflags gtk+-2.0` -LINKFLAGS += `pkg-config --libs gtk+-2.0` - -#support for SELinux (optional) -SELINUX_EXISTING=$(shell pkg-config --exists libselinux && echo YES) -ifeq ($(SELINUX_EXISTING),YES) -CXXFLAGS += `pkg-config --cflags libselinux` -DHAVE_SELINUX -LINKFLAGS += `pkg-config --libs libselinux` -endif - -#support for Ubuntu Unity (optional) -UNITY_EXISTING=$(shell pkg-config --exists unity && echo YES) -ifeq ($(UNITY_EXISTING),YES) -CXXFLAGS += `pkg-config --cflags unity` -DHAVE_UBUNTU_UNITY -LINKFLAGS += `pkg-config --libs unity` -endif - -ifeq ($(BUILD),Launchpad) -#default build/Launchpad -CXXFLAGS += `wx-config --cxxflags --debug=no` -LINKFLAGS += `wx-config --libs std, aui --debug=no` -lboost_thread -lboost_system -lz -else -#static wxWidgets and boost library linkage for precompiled release -WX_CONFIG_BIN =$(HOME)/Desktop/wxWidgets-2.9.5/lib/release/bin/wx-config -CXXFLAGS += -I$(HOME)/Desktop/boost_1_54_0 -BOOST_LIB_DIR =$(HOME)/Desktop/boost_1_54_0/stage/lib - -CXXFLAGS += `$(WX_CONFIG_BIN) --cxxflags --debug=no --static=yes` -LINKFLAGS += `$(WX_CONFIG_BIN) --libs std, aui --debug=no --static=yes` $(BOOST_LIB_DIR)/libboost_thread.a $(BOOST_LIB_DIR)/libboost_system.a -lX11 -endif - -endif -#################### OS X ############################ -ifeq ($(OPERATING_SYSTEM_NAME), Darwin) -COMPILER_BIN=clang++ -stdlib=libc++ -CXXFLAGS += -DZEN_MAC - -WX_CONFIG_BIN =$(HOME)/Desktop/wxWidgets-2.9.5/lib/release/bin/wx-config -CXXFLAGS += -I$(HOME)/Desktop/boost_1_54_0 -BOOST_LIB_DIR =$(HOME)/Desktop/boost_1_54_0/stage/lib -MACOS_SDK =-mmacosx-version-min=10.7 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk - -#-Wl,-Bstatic not supported on OSX! - -# link wxWidgets and boost statically -> check dependencies with: otool -L FreeFileSync -CXXFLAGS += $(MACOS_SDK) `$(WX_CONFIG_BIN) --cxxflags --debug=no --static=yes` -LINKFLAGS += $(MACOS_SDK) `$(WX_CONFIG_BIN) --libs std, aui --debug=no --static=yes` $(BOOST_LIB_DIR)/libboost_thread.a $(BOOST_LIB_DIR)/libboost_system.a - -endif -###################################################### - -CPP_LIST= #internal list of all *.cpp files needed for compilation -CPP_LIST+=algorithm.cpp -CPP_LIST+=application.cpp -CPP_LIST+=comparison.cpp -CPP_LIST+=structures.cpp -CPP_LIST+=synchronization.cpp -CPP_LIST+=file_hierarchy.cpp -CPP_LIST+=ui/custom_grid.cpp -CPP_LIST+=ui/folder_history_box.cpp -CPP_LIST+=ui/exec_finished_box.cpp -CPP_LIST+=ui/dir_name.cpp -CPP_LIST+=ui/batch_config.cpp -CPP_LIST+=ui/batch_status_handler.cpp -CPP_LIST+=ui/check_version.cpp -CPP_LIST+=ui/grid_view.cpp -CPP_LIST+=ui/tree_view.cpp -CPP_LIST+=ui/gui_generated.cpp -CPP_LIST+=ui/gui_status_handler.cpp -CPP_LIST+=ui/main_dlg.cpp -CPP_LIST+=ui/progress_indicator.cpp -CPP_LIST+=ui/search.cpp -CPP_LIST+=ui/small_dlgs.cpp -CPP_LIST+=ui/sync_cfg.cpp -CPP_LIST+=ui/taskbar.cpp -CPP_LIST+=ui/triple_splitter.cpp -CPP_LIST+=ui/tray_icon.cpp -CPP_LIST+=lib/binary.cpp -CPP_LIST+=lib/db_file.cpp -CPP_LIST+=lib/dir_lock.cpp -CPP_LIST+=lib/hard_filter.cpp -CPP_LIST+=lib/icon_buffer.cpp -CPP_LIST+=lib/localization.cpp -CPP_LIST+=lib/parallel_scan.cpp -CPP_LIST+=lib/process_xml.cpp -CPP_LIST+=lib/resolve_path.cpp -CPP_LIST+=lib/perf_check.cpp -CPP_LIST+=lib/status_handler.cpp -CPP_LIST+=lib/versioning.cpp -CPP_LIST+=lib/ffs_paths.cpp -CPP_LIST+=lib/xml_base.cpp -CPP_LIST+=zen/recycler.cpp -CPP_LIST+=zen/file_handling.cpp -CPP_LIST+=zen/file_id.cpp -CPP_LIST+=zen/file_io.cpp -CPP_LIST+=zen/file_traverser.cpp -CPP_LIST+=zen/zstring.cpp -CPP_LIST+=zen/format_unit.cpp -CPP_LIST+=zen/process_priority.cpp -CPP_LIST+=wx+/grid.cpp -CPP_LIST+=wx+/image_tools.cpp -CPP_LIST+=wx+/graph.cpp -CPP_LIST+=wx+/tooltip.cpp -CPP_LIST+=wx+/image_resources.cpp -CPP_LIST+=wx+/popup_dlg.cpp -CPP_LIST+=wx+/popup_dlg_generated.cpp -CPP_LIST+=wx+/zlib_wrap.cpp - -# OS X -ifeq ($(OPERATING_SYSTEM_NAME), Darwin) -MM_LIST= #objective C files -MM_LIST+=ui/osx_dock.mm -MM_LIST+=lib/osx_file_icon.mm -endif - -#list of all *.o files -OBJECT_LIST = $(CPP_LIST:%.cpp=OBJ/FFS_GCC_Make_Release/%.o) -OBJECT_LIST += $(MM_LIST:%.mm=OBJ/FFS_GCC_Make_Release/%.mm.o) - -all: FreeFileSync - -OBJ/FFS_GCC_Make_Release/%.mm.o : %.mm - mkdir -p $(dir $@) - $(COMPILER_BIN) $(CXXFLAGS) -c $< -o $@ - -OBJ/FFS_GCC_Make_Release/%.o : %.cpp - mkdir -p $(dir $@) - $(COMPILER_BIN) $(CXXFLAGS) -c $< -o $@ - -FreeFileSync: $(OBJECT_LIST) - $(COMPILER_BIN) -o ./BUILD/$(APPNAME) $(OBJECT_LIST) $(LINKFLAGS) - -clean: - rm -rf OBJ/FFS_GCC_Make_Release - rm -f BUILD/$(APPNAME) - rm -f wx+/pch.h.gch - -install: - mkdir -p $(BINDIR) - cp BUILD/$(APPNAME) $(BINDIR) - - mkdir -p $(APPSHAREDIR) - cp -R BUILD/Languages/ \ - BUILD/Help/ \ - BUILD/Sync_Complete.wav \ - BUILD/Resources.zip \ - BUILD/styles.gtk_rc \ - $(APPSHAREDIR) - - mkdir -p $(DOCSHAREDIR) - cp BUILD/Changelog.txt $(DOCSHAREDIR)/changelog - gzip $(DOCSHAREDIR)/changelog diff --git a/RealtimeSync/RealtimeSync.ico b/RealtimeSync/RealtimeSync.ico deleted file mode 100644 index f9012b12..00000000 Binary files a/RealtimeSync/RealtimeSync.ico and /dev/null differ diff --git a/RealtimeSync/RealtimeSync.vcxproj b/RealtimeSync/RealtimeSync.vcxproj deleted file mode 100644 index 99f64740..00000000 --- a/RealtimeSync/RealtimeSync.vcxproj +++ /dev/null @@ -1,264 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {A80B97E9-807C-47A1-803A-27565A1BD526} - Win32Proj - RealtimeSync - $(VCTargetsPath11) - - - - Application - true - Unicode - v120_xp - - - Application - true - Unicode - v120_xp - - - Application - false - true - Unicode - v120_xp - - - Application - false - true - Unicode - v120_xp - - - - - - - - - - - - - - - - - - - ..\BUILD\Bin\ - ..\OBJ\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ - false - $(ProjectName)_$(PlatformName) - - - ..\BUILD\Bin\ - ..\OBJ\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ - false - $(ProjectName)_$(PlatformName) - - - false - ..\BUILD\Bin\ - ..\OBJ\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ - false - $(ProjectName)_$(PlatformName) - - - false - ..\BUILD\Bin\ - ..\OBJ\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ - false - $(ProjectName)_$(PlatformName) - - - - Use - Level4 - Disabled - _SCL_SECURE_NO_WARNINGS;wxUSE_UNICODE;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;__WXDEBUG__;WXUSINGDLL;_DEBUG;%(PreprocessorDefinitions) - C:\Data\C++\Boost;C:\Data\C++\wxWidgets\include;C:\Data\C++\wxWidgets\lib\vc12_x86_debug_dll\mswud;.. - wx+/pch.h - 4100;4512 - true - $(IntDir)pch.obj - zen/warn_static.h;wx+/pch.h - EditAndContinue - false - true - - - Windows - true - $(OutDir)$(TargetName)$(TargetExt) - wxmsw29ud_adv.lib;wxmsw29ud_core.lib;wxbase29ud.lib;wxpngd.lib;wxzlibd.lib;wxbase29ud_net.lib;comctl32.lib;ws2_32.lib;Rpcrt4.lib;winmm.lib;%(AdditionalDependencies) - C:\Data\C++\Boost\stage\lib;C:\Data\C++\wxWidgets\lib\vc12_x86_debug_dll - - - C:\Data\C++\wxWidgets\include - %(PreprocessorDefinitions);ZEN_ARCHITECTURE_X86 - - - - - Use - Level4 - Disabled - _SCL_SECURE_NO_WARNINGS;wxUSE_UNICODE;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;__WXDEBUG__;WXUSINGDLL;_DEBUG;%(PreprocessorDefinitions) - C:\Data\C++\Boost;C:\Data\C++\wxWidgets\include;C:\Data\C++\wxWidgets\lib\vc12_x64_debug_dll\mswud;.. - wx+/pch.h - 4100;4512 - true - $(IntDir)pch.obj - zen/warn_static.h;wx+/pch.h - ProgramDatabase - true - false - false - true - - - Windows - true - $(OutDir)$(TargetName)$(TargetExt) - wxmsw29ud_adv.lib;wxmsw29ud_core.lib;wxbase29ud.lib;wxpngd.lib;wxzlibd.lib;wxbase29ud_net.lib;comctl32.lib;ws2_32.lib;Rpcrt4.lib;winmm.lib;%(AdditionalDependencies) - C:\Data\C++\Boost\stage_x64\lib;C:\Data\C++\wxWidgets\lib\vc12_x64_debug_dll - - - - - C:\Data\C++\wxWidgets\include - %(PreprocessorDefinitions);ZEN_ARCHITECTURE_X64 - - - - - Level4 - NotUsing - MaxSpeed - true - _SCL_SECURE_NO_WARNINGS;wxUSE_UNICODE;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;%(PreprocessorDefinitions) - C:\Data\C++\Boost;C:\Data\C++\wxWidgets\include;C:\Data\C++\wxWidgets\lib\vc12_x86_release_lib\mswu;.. - Speed - 4100;4512;4996 - MultiThreaded - true - zen/warn_static.h - - - Windows - false - true - true - wxbase29u.lib;wxmsw29u_adv.lib;wxmsw29u_core.lib;wxpng.lib;wxzlib.lib;wxbase29u_net.lib;comctl32.lib;ws2_32.lib;winmm.lib;Rpcrt4.lib;%(AdditionalDependencies) - $(OutDir)$(TargetName)$(TargetExt) - C:\Data\C++\Boost\stage\lib;C:\Data\C++\wxWidgets\lib\vc12_x86_release_lib - - - C:\Data\C++\wxWidgets\include - %(PreprocessorDefinitions);ZEN_ARCHITECTURE_X86 - - - "C:\Data\C++\CodeSigning\SignCode.cmd" "$(TargetPath)" - - - - - Level4 - NotUsing - MaxSpeed - true - _SCL_SECURE_NO_WARNINGS;wxUSE_UNICODE;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;%(PreprocessorDefinitions) - C:\Data\C++\Boost;C:\Data\C++\wxWidgets\include;C:\Data\C++\wxWidgets\lib\vc12_x64_release_lib\mswu;.. - Speed - 4100;4512;4996 - MultiThreaded - true - zen/warn_static.h - - - Windows - false - true - true - wxmsw29u_adv.lib;wxmsw29u_core.lib;wxbase29u.lib;wxpng.lib;wxzlib.lib;wxbase29u_net.lib;comctl32.lib;ws2_32.lib;winmm.lib;Rpcrt4.lib;%(AdditionalDependencies) - $(OutDir)$(TargetName)$(TargetExt) - C:\Data\C++\Boost\stage_x64\lib;C:\Data\C++\wxWidgets\lib\vc12_x64_release_lib - - - C:\Data\C++\wxWidgets\include - %(PreprocessorDefinitions);ZEN_ARCHITECTURE_X64 - - - "C:\Data\C++\CodeSigning\SignCode.cmd" "$(TargetPath)" - - - - - - - - - - - - - Create - Create - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/RealtimeSync/app_icon.h b/RealtimeSync/app_icon.h deleted file mode 100644 index fcd2b548..00000000 --- a/RealtimeSync/app_icon.h +++ /dev/null @@ -1,40 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef RTS_APP_ICON_8914578394545 -#define RTS_APP_ICON_8914578394545 - -#include -#include - -namespace zen -{ -inline -wxIcon getRtsIcon() -{ - //wxWidgets' bitmap to icon conversion on OS X can only deal with very specific sizes => check on all platforms! - assert(getResourceImage(L"RealtimeSync").GetWidth () == getResourceImage(L"RealtimeSync").GetHeight() && - getResourceImage(L"RealtimeSync").GetWidth() % 128 == 0); -#ifdef ZEN_WIN - //for compatibility it seems we need to stick with a "real" icon - return wxIcon(L"A_RTS_ICON"); - -#elif defined ZEN_LINUX - //attention: make sure to not implicitly call "instance()" again => deadlock on Linux - wxIcon icon; - icon.CopyFromBitmap(getResourceImage(L"RealtimeSync")); //use big logo bitmap for better quality - return icon; - -#elif defined ZEN_MAC - wxIcon icon; - icon.CopyFromBitmap(getResourceImage(L"RealtimeSync").ConvertToImage().Scale(128, 128, wxIMAGE_QUALITY_HIGH)); //"von hinten durch die Brust ins Auge" - return icon; -#endif -} -} - - -#endif //RTS_APP_ICON_8914578394545 diff --git a/RealtimeSync/application.cpp b/RealtimeSync/application.cpp deleted file mode 100644 index a0d693d6..00000000 --- a/RealtimeSync/application.cpp +++ /dev/null @@ -1,172 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "application.h" -#include "main_dlg.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "xml_ffs.h" -#include "../lib/localization.h" -#include "../lib/ffs_paths.h" -#include "../lib/return_codes.h" -#include "lib/error_log.h" - -#ifdef ZEN_WIN -#include - -#elif defined ZEN_LINUX -#include -#endif - -using namespace zen; - - -IMPLEMENT_APP(Application); - -namespace -{ -/* -boost::thread::id mainThreadId = boost::this_thread::get_id(); - -void onTerminationRequested() -{ -std::wstring msg = boost::this_thread::get_id() == mainThreadId ? - L"Termination requested in main thread!\n\n" : - L"Termination requested in worker thread!\n\n"; -msg += L"Please file a bug report at: http://sourceforge.net/projects/freefilesync"; - -wxSafeShowMessage(_("An exception occurred"), msg); -std::abort(); -} -*/ -#ifdef _MSC_VER -void crtInvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved) { assert(false); } -#endif - -const wxEventType EVENT_ENTER_EVENT_LOOP = wxNewEventType(); -} - - -bool Application::OnInit() -{ - //std::set_terminate(onTerminationRequested); //unlike wxWidgets uncaught exception handling, this works for all worker threads - -#ifdef ZEN_WIN -#ifdef _MSC_VER - _set_invalid_parameter_handler(crtInvalidParameterHandler); //see comment in -#endif - //Quote: "Best practice is that all applications call the process-wide SetErrorMode function with a parameter of - //SEM_FAILCRITICALERRORS at startup. This is to prevent error mode dialogs from hanging the application." - ::SetErrorMode(SEM_FAILCRITICALERRORS); - -#elif defined ZEN_LINUX - ::gtk_rc_parse((zen::getResourceDir() + "styles.gtk_rc").c_str()); //remove inner border from bitmap buttons -#endif - -#ifdef ZEN_WIN - wxToolTip::SetMaxWidth(-1); //disable tooltip wrapping -> Windows only -#endif - //Windows User Experience Interaction Guidelines: tool tips should have 5s timeout, info tips no timeout => compromise: - wxToolTip::SetAutoPop(7000); //http://msdn.microsoft.com/en-us/library/windows/desktop/aa511495.aspx - - SetAppName(L"RealtimeSync"); - - initResourceImages(getResourceDir() + Zstr("Resources.zip")); - - //do not call wxApp::OnInit() to avoid using default commandline parser - - //Note: app start is deferred: -> see FreeFileSync - Connect(EVENT_ENTER_EVENT_LOOP, wxEventHandler(Application::onEnterEventLoop), nullptr, this); - wxCommandEvent scrollEvent(EVENT_ENTER_EVENT_LOOP); - AddPendingEvent(scrollEvent); - - return true; //true: continue processing; false: exit immediately. -} - - -int Application::OnExit() -{ - releaseWxLocale(); - return wxApp::OnExit(); -} - - -void Application::onEnterEventLoop(wxEvent& event) -{ - Disconnect(EVENT_ENTER_EVENT_LOOP, wxEventHandler(Application::onEnterEventLoop), nullptr, this); - - try - { - setLanguage(rts::getProgramLanguage()); //throw FileError - } - catch (const FileError& e) - { - showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); - //continue! - } - - //try to set config/batch-filename set by %1 parameter - std::vector commandArgs; - for (int i = 1; i < argc; ++i) - { - Zstring filename = toZ(argv[i]); - - if (!fileExists(filename)) //be a little tolerant - { - if (fileExists(filename + Zstr(".ffs_real"))) - filename += Zstr(".ffs_real"); - else if (fileExists(filename + Zstr(".ffs_batch"))) - filename += Zstr(".ffs_batch"); - else - { - showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setMainInstructions(replaceCpy(_("Cannot open file %x."), L"%x", fmtFileName(filename)))); - return; - } - } - commandArgs.push_back(filename); - } - - Zstring cfgFilename; - if (!commandArgs.empty()) - cfgFilename = commandArgs[0]; - - MainDialog::create(cfgFilename); -} - - -int Application::OnRun() -{ - - auto processException = [](const std::wstring& msg) - { - //it's not always possible to display a message box, e.g. corrupted stack, however low-level file output works! - logError(utfCvrtTo(msg)); - wxSafeShowMessage(_("An exception occurred"), msg); - }; - - try - { - wxApp::OnRun(); - } - catch (const std::exception& e) //catch all STL exceptions - { - processException(utfCvrtTo(e.what())); - return FFS_RC_EXCEPTION; - } - catch (...) //catch the rest - { - processException(L"Unknown error."); - return FFS_RC_EXCEPTION; - } - - return FFS_RC_SUCCESS; //program's return code -} diff --git a/RealtimeSync/application.h b/RealtimeSync/application.h deleted file mode 100644 index 2edef90c..00000000 --- a/RealtimeSync/application.h +++ /dev/null @@ -1,25 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef REALTIMESYNCAPP_H -#define REALTIMESYNCAPP_H - -#include - -class Application : public wxApp -{ -public: - virtual bool OnInit(); - virtual int OnExit(); - virtual int OnRun(); - virtual bool OnExceptionInMainLoop() { throw; } //just re-throw and avoid display of additional messagebox: it will be caught in OnRun() - -private: - void onEnterEventLoop(wxEvent& event); - //virtual wxLayoutDirection GetLayoutDirection() const { return wxLayout_LeftToRight; } -}; - -#endif // REALTIMESYNCAPP_H diff --git a/RealtimeSync/gui_generated.cpp b/RealtimeSync/gui_generated.cpp deleted file mode 100644 index 3b3ad3fe..00000000 --- a/RealtimeSync/gui_generated.cpp +++ /dev/null @@ -1,283 +0,0 @@ -/////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Oct 8 2012) -// http://www.wxformbuilder.org/ -// -// PLEASE DO "NOT" EDIT THIS FILE! -/////////////////////////////////////////////////////////////////////////// - -#include "../wx+/bitmap_button.h" - -#include "gui_generated.h" - -/////////////////////////////////////////////////////////////////////////// - -MainDlgGenerated::MainDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style ) -{ - this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - m_menubar1 = new wxMenuBar( 0 ); - m_menuFile = new wxMenu(); - wxMenuItem* m_menuItem13; - m_menuItem13 = new wxMenuItem( m_menuFile, wxID_OPEN, wxString( _("&Open...") ) + wxT('\t') + wxT("CTRL+O"), wxEmptyString, wxITEM_NORMAL ); - m_menuFile->Append( m_menuItem13 ); - - wxMenuItem* m_menuItem14; - m_menuItem14 = new wxMenuItem( m_menuFile, wxID_SAVEAS, wxString( _("Save &as...") ) , wxEmptyString, wxITEM_NORMAL ); - m_menuFile->Append( m_menuItem14 ); - - m_menuFile->AppendSeparator(); - - wxMenuItem* m_menuItem4; - m_menuItem4 = new wxMenuItem( m_menuFile, wxID_EXIT, wxString( _("&Quit") ) , wxEmptyString, wxITEM_NORMAL ); - m_menuFile->Append( m_menuItem4 ); - - m_menubar1->Append( m_menuFile, _("&Program") ); - - m_menuHelp = new wxMenu(); - wxMenuItem* m_menuItemContent; - m_menuItemContent = new wxMenuItem( m_menuHelp, wxID_HELP, wxString( _("&View help") ) + wxT('\t') + wxT("F1"), wxEmptyString, wxITEM_NORMAL ); - m_menuHelp->Append( m_menuItemContent ); - - m_menuHelp->AppendSeparator(); - - m_menuItemAbout = new wxMenuItem( m_menuHelp, wxID_ABOUT, wxString( _("&About") ) + wxT('\t') + wxT("SHIFT+F1"), wxEmptyString, wxITEM_NORMAL ); - m_menuHelp->Append( m_menuItemAbout ); - - m_menubar1->Append( m_menuHelp, _("&Help") ); - - this->SetMenuBar( m_menubar1 ); - - bSizerMain = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer161; - bSizer161 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer16; - bSizer16 = new wxBoxSizer( wxHORIZONTAL ); - - m_staticText9 = new wxStaticText( this, wxID_ANY, _("Usage:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText9->Wrap( -1 ); - m_staticText9->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer16->Add( m_staticText9, 0, wxALL, 5 ); - - wxBoxSizer* bSizer15; - bSizer15 = new wxBoxSizer( wxVERTICAL ); - - m_staticText3 = new wxStaticText( this, wxID_ANY, _("1. Select folders to watch."), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText3->Wrap( -1 ); - bSizer15->Add( m_staticText3, 0, 0, 5 ); - - m_staticText4 = new wxStaticText( this, wxID_ANY, _("2. Enter a command line."), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText4->Wrap( -1 ); - bSizer15->Add( m_staticText4, 0, 0, 5 ); - - m_staticText5 = new wxStaticText( this, wxID_ANY, _("3. Press 'Start'."), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText5->Wrap( -1 ); - bSizer15->Add( m_staticText5, 0, 0, 5 ); - - - bSizer16->Add( bSizer15, 0, wxALL, 5 ); - - - 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->Wrap( -1 ); - m_staticText811->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); - - bSizer161->Add( m_staticText811, 0, wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizerMain->Add( bSizer161, 0, wxALL|wxEXPAND, 5 ); - - m_staticline2 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizerMain->Add( m_staticline2, 0, wxEXPAND, 5 ); - - m_panelMain = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelMain->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer1; - bSizer1 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer151; - bSizer151 = new wxBoxSizer( wxVERTICAL ); - - m_staticText7 = new wxStaticText( m_panelMain, wxID_ANY, _("Folders to watch:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText7->Wrap( -1 ); - bSizer151->Add( m_staticText7, 0, wxALL, 5 ); - - m_panelMainFolder = new wxPanel( m_panelMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelMainFolder->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxFlexGridSizer* fgSizer1; - fgSizer1 = new wxFlexGridSizer( 0, 2, 0, 0 ); - fgSizer1->AddGrowableCol( 1 ); - fgSizer1->SetFlexibleDirection( wxBOTH ); - fgSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_ALL ); - - - fgSizer1->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextFinalPath = new wxStaticText( m_panelMainFolder, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextFinalPath->Wrap( -1 ); - fgSizer1->Add( m_staticTextFinalPath, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); - - wxBoxSizer* bSizer20; - bSizer20 = new wxBoxSizer( wxHORIZONTAL ); - - m_bpButtonAddFolder = new wxBitmapButton( m_panelMainFolder, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - m_bpButtonAddFolder->SetToolTip( _("Add folder") ); - - bSizer20->Add( m_bpButtonAddFolder, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_bpButtonRemoveTopFolder = new wxBitmapButton( m_panelMainFolder, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - m_bpButtonRemoveTopFolder->SetToolTip( _("Remove folder") ); - - bSizer20->Add( m_bpButtonRemoveTopFolder, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - fgSizer1->Add( bSizer20, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - wxBoxSizer* bSizer19; - bSizer19 = new wxBoxSizer( wxHORIZONTAL ); - - m_txtCtrlDirectoryMain = new wxTextCtrl( m_panelMainFolder, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 300,-1 ), 0 ); - m_txtCtrlDirectoryMain->SetMaxLength( 0 ); - bSizer19->Add( m_txtCtrlDirectoryMain, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - m_buttonSelectDirMain = new wxButton( m_panelMainFolder, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); - m_buttonSelectDirMain->SetToolTip( _("Select a folder") ); - - bSizer19->Add( m_buttonSelectDirMain, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - fgSizer1->Add( bSizer19, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - - m_panelMainFolder->SetSizer( fgSizer1 ); - m_panelMainFolder->Layout(); - fgSizer1->Fit( m_panelMainFolder ); - bSizer151->Add( m_panelMainFolder, 0, wxRIGHT|wxLEFT|wxEXPAND, 5 ); - - m_scrolledWinFolders = new wxScrolledWindow( m_panelMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); - m_scrolledWinFolders->SetScrollRate( 10, 10 ); - m_scrolledWinFolders->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - bSizerFolders = new wxBoxSizer( wxVERTICAL ); - - - m_scrolledWinFolders->SetSizer( bSizerFolders ); - m_scrolledWinFolders->Layout(); - bSizerFolders->Fit( m_scrolledWinFolders ); - bSizer151->Add( m_scrolledWinFolders, 1, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer1->Add( bSizer151, 1, wxALL|wxEXPAND, 5 ); - - m_staticline212 = new wxStaticLine( m_panelMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer1->Add( m_staticline212, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer14; - bSizer14 = new wxBoxSizer( wxHORIZONTAL ); - - m_staticText8 = new wxStaticText( m_panelMain, wxID_ANY, _("Idle time (in seconds):"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText8->Wrap( -1 ); - bSizer14->Add( m_staticText8, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); - - m_spinCtrlDelay = new wxSpinCtrl( m_panelMain, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 2000000000, 0 ); - m_spinCtrlDelay->SetToolTip( _("Idle time between last detected change and execution of command") ); - - bSizer14->Add( m_spinCtrlDelay, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - - bSizer1->Add( bSizer14, 0, wxALIGN_RIGHT|wxEXPAND|wxALL, 5 ); - - m_staticline211 = new wxStaticLine( m_panelMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer1->Add( m_staticline211, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer141; - bSizer141 = new wxBoxSizer( wxVERTICAL ); - - m_staticText6 = new wxStaticText( m_panelMain, wxID_ANY, _("Command line:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText6->Wrap( -1 ); - bSizer141->Add( m_staticText6, 0, wxALL, 5 ); - - m_textCtrlCommand = new wxTextCtrl( m_panelMain, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - m_textCtrlCommand->SetMaxLength( 0 ); - m_textCtrlCommand->SetToolTip( _("The command is triggered if:\n- files or subfolders change\n- new folders arrive (e.g. USB stick insert)") ); - - bSizer141->Add( m_textCtrlCommand, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 ); - - - bSizer1->Add( bSizer141, 0, wxALL|wxEXPAND, 5 ); - - - m_panelMain->SetSizer( bSizer1 ); - m_panelMain->Layout(); - bSizer1->Fit( m_panelMain ); - bSizerMain->Add( m_panelMain, 1, wxEXPAND, 5 ); - - m_staticline5 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizerMain->Add( m_staticline5, 0, wxEXPAND, 5 ); - - m_buttonStart = new zen::BitmapTextButton( this, wxID_OK, _("&Start"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_buttonStart->SetDefault(); - m_buttonStart->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizerMain->Add( m_buttonStart, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); - - - this->SetSizer( bSizerMain ); - this->Layout(); - bSizerMain->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( MainDlgGenerated::OnClose ) ); - this->Connect( m_menuItem13->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnConfigLoad ) ); - this->Connect( m_menuItem14->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnConfigSave ) ); - this->Connect( m_menuItem4->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnMenuQuit ) ); - this->Connect( m_menuItemContent->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnShowHelp ) ); - this->Connect( m_menuItemAbout->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnMenuAbout ) ); - m_bpButtonAddFolder->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDlgGenerated::OnAddFolder ), NULL, this ); - m_bpButtonRemoveTopFolder->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDlgGenerated::OnRemoveTopFolder ), NULL, this ); - m_buttonStart->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDlgGenerated::OnStart ), NULL, this ); -} - -MainDlgGenerated::~MainDlgGenerated() -{ -} - -FolderGenerated::FolderGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) -{ - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer114; - bSizer114 = new wxBoxSizer( wxHORIZONTAL ); - - m_bpButtonRemoveFolder = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - m_bpButtonRemoveFolder->SetToolTip( _("Remove folder") ); - - bSizer114->Add( m_bpButtonRemoveFolder, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_txtCtrlDirectory = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - m_txtCtrlDirectory->SetMaxLength( 0 ); - bSizer114->Add( m_txtCtrlDirectory, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - m_buttonSelectDir = new wxButton( this, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); - m_buttonSelectDir->SetToolTip( _("Select a folder") ); - - bSizer114->Add( m_buttonSelectDir, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - this->SetSizer( bSizer114 ); - this->Layout(); - bSizer114->Fit( this ); -} - -FolderGenerated::~FolderGenerated() -{ -} diff --git a/RealtimeSync/gui_generated.h b/RealtimeSync/gui_generated.h deleted file mode 100644 index a816e10a..00000000 --- a/RealtimeSync/gui_generated.h +++ /dev/null @@ -1,117 +0,0 @@ -/////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Oct 8 2012) -// http://www.wxformbuilder.org/ -// -// PLEASE DO "NOT" EDIT THIS FILE! -/////////////////////////////////////////////////////////////////////////// - -#ifndef __GUI_GENERATED_H__ -#define __GUI_GENERATED_H__ - -#include -#include -#include -namespace zen { class BitmapTextButton; } - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "zen/i18n.h" - -/////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// -/// Class MainDlgGenerated -/////////////////////////////////////////////////////////////////////////////// -class MainDlgGenerated : public wxFrame -{ -private: - -protected: - wxMenuBar* m_menubar1; - wxMenu* m_menuFile; - wxMenu* m_menuHelp; - wxMenuItem* m_menuItemAbout; - wxBoxSizer* bSizerMain; - wxStaticText* m_staticText9; - wxStaticText* m_staticText3; - wxStaticText* m_staticText4; - wxStaticText* m_staticText5; - wxStaticText* m_staticText811; - wxStaticLine* m_staticline2; - wxPanel* m_panelMain; - wxStaticText* m_staticText7; - wxPanel* m_panelMainFolder; - wxStaticText* m_staticTextFinalPath; - wxBitmapButton* m_bpButtonAddFolder; - wxBitmapButton* m_bpButtonRemoveTopFolder; - wxTextCtrl* m_txtCtrlDirectoryMain; - wxButton* m_buttonSelectDirMain; - wxScrolledWindow* m_scrolledWinFolders; - wxBoxSizer* bSizerFolders; - wxStaticLine* m_staticline212; - wxStaticText* m_staticText8; - wxSpinCtrl* m_spinCtrlDelay; - wxStaticLine* m_staticline211; - wxStaticText* m_staticText6; - wxTextCtrl* m_textCtrlCommand; - wxStaticLine* m_staticline5; - zen::BitmapTextButton* m_buttonStart; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnConfigLoad( wxCommandEvent& event ) { event.Skip(); } - virtual void OnConfigSave( wxCommandEvent& event ) { event.Skip(); } - virtual void OnMenuQuit( wxCommandEvent& event ) { event.Skip(); } - virtual void OnShowHelp( wxCommandEvent& event ) { event.Skip(); } - virtual void OnMenuAbout( wxCommandEvent& event ) { event.Skip(); } - virtual void OnAddFolder( wxCommandEvent& event ) { event.Skip(); } - virtual void OnRemoveTopFolder( wxCommandEvent& event ) { event.Skip(); } - virtual void OnStart( wxCommandEvent& event ) { event.Skip(); } - - -public: - - MainDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL ); - - ~MainDlgGenerated(); - -}; - -/////////////////////////////////////////////////////////////////////////////// -/// Class FolderGenerated -/////////////////////////////////////////////////////////////////////////////// -class FolderGenerated : public wxPanel -{ -private: - -protected: - wxButton* m_buttonSelectDir; - -public: - wxBitmapButton* m_bpButtonRemoveFolder; - wxTextCtrl* m_txtCtrlDirectory; - - FolderGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = 0 ); - ~FolderGenerated(); - -}; - -#endif //__GUI_GENERATED_H__ diff --git a/RealtimeSync/main_dlg.cpp b/RealtimeSync/main_dlg.cpp deleted file mode 100644 index f29f396b..00000000 --- a/RealtimeSync/main_dlg.cpp +++ /dev/null @@ -1,534 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "main_dlg.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "xml_proc.h" -#include "tray_menu.h" -#include "xml_ffs.h" -#include "app_icon.h" -#include "../lib/help_provider.h" -#include "../lib/process_xml.h" -#include "../lib/ffs_paths.h" -#ifdef ZEN_LINUX -#include -#elif defined ZEN_MAC -#include -#endif - -using namespace zen; - - -class DirectoryPanel : public FolderGenerated -{ -public: - DirectoryPanel(wxWindow* parent) : - FolderGenerated(parent), - dirName(*this, *m_buttonSelectDir, *m_txtCtrlDirectory) - { -#ifdef ZEN_LINUX - //file drag and drop directly into the text control unhelpfully inserts in format "file://.."; see folder_history_box.cpp - if (GtkWidget* widget = m_txtCtrlDirectory->GetConnectWidget()) - ::gtk_drag_dest_unset(widget); -#endif - } - - void setName(const wxString& dirname) { dirName.setName(dirname); } - wxString getName() const { return dirName.getName(); } - -private: - zen::DirectoryName dirName; -}; - - -void MainDialog::create(const Zstring& cfgFile) -{ - /*MainDialog* frame = */ new MainDialog(nullptr, cfgFile); -} - - -MainDialog::MainDialog(wxDialog* dlg, const Zstring& cfgFileName) - : MainDlgGenerated(dlg) -{ -#ifdef ZEN_WIN - new MouseMoveWindow(*this); //ownership passed to "this" - wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! -#endif - -#ifdef ZEN_LINUX - //file drag and drop directly into the text control unhelpfully inserts in format "file://.."; see folder_history_box.cpp - if (GtkWidget* widget = m_txtCtrlDirectoryMain->GetConnectWidget()) - ::gtk_drag_dest_unset(widget); -#endif - - SetIcon(getRtsIcon()); //set application icon - - setRelativeFontSize(*m_buttonStart, 1.5); - - m_bpButtonRemoveTopFolder->Hide(); - m_panelMainFolder->Layout(); - - m_bpButtonAddFolder ->SetBitmapLabel(getResourceImage(L"item_add")); - m_bpButtonRemoveTopFolder->SetBitmapLabel(getResourceImage(L"item_remove")); - setBitmapTextLabel(*m_buttonStart, getResourceImage(L"startRts").ConvertToImage(), m_buttonStart->GetLabel(), 5, 8); - - - //register key event - Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::OnKeyPressed), nullptr, this); - - //prepare drag & drop - dirNameFirst.reset(new DirectoryName(*m_panelMainFolder, *m_buttonSelectDirMain, *m_txtCtrlDirectoryMain, m_staticTextFinalPath)); - - //--------------------------- load config values ------------------------------------ - xmlAccess::XmlRealConfig newConfig; - - const Zstring currentConfigFile = cfgFileName.empty() ? lastConfigFileName() : cfgFileName; - bool loadCfgSuccess = false; - if (!cfgFileName.empty() || fileExists(lastConfigFileName())) - try - { - rts::readRealOrBatchConfig(currentConfigFile, newConfig); //throw FfsXmlError - loadCfgSuccess = true; - } - catch (const xmlAccess::FfsXmlError& e) - { - if (e.getSeverity() == xmlAccess::FfsXmlError::WARNING) - showNotificationDialog(this, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(e.toString())); - else - showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); - } - - const bool startWatchingImmediately = loadCfgSuccess && !cfgFileName.empty(); - - setConfiguration(newConfig); - setLastUsedConfig(currentConfigFile); - //----------------------------------------------------------------------------------------- - - if (startWatchingImmediately) //start watch mode directly - { - wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); - this->OnStart(dummy2); - //don't Show()! - } - else - { - m_buttonStart->SetFocus(); //don't "steal" focus if program is running from sys-tray" - Show(); -#ifdef ZEN_MAC - ProcessSerialNumber psn = { 0, kCurrentProcess }; - ::SetFrontProcess(&psn); //call before TransformProcessType() so that OSX menu is updated correctly - ::TransformProcessType(&psn, kProcessTransformToForegroundApplication); //show dock icon, even if we're not an application bundle - //if the executable is not yet in a bundle or if it is called through a launcher, we need to set focus manually: -#endif - } - - //drag and drop .ffs_real and .ffs_batch on main dialog - setupFileDrop(*m_panelMain); - m_panelMain->Connect(EVENT_DROP_FILE, FileDropEventHandler(MainDialog::onFilesDropped), nullptr, this); - - timerForAsyncTasks.Connect(wxEVT_TIMER, wxEventHandler(MainDialog::onProcessAsyncTasks), nullptr, this); -} - - -MainDialog::~MainDialog() -{ - //save current configuration - const xmlAccess::XmlRealConfig currentCfg = getConfiguration(); - - try //write config to XML - { - writeRealConfig(currentCfg, lastConfigFileName()); //throw FfsXmlError - } - catch (const xmlAccess::FfsXmlError& e) - { - showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); - } -} - - -void MainDialog::onProcessAsyncTasks(wxEvent& event) -{ - //schedule and run long-running tasks asynchronously - asyncTasks.evalResults(); //process results on GUI queue - if (asyncTasks.empty()) - timerForAsyncTasks.Stop(); -} - - -const Zstring& MainDialog::lastConfigFileName() -{ - static Zstring instance = zen::getConfigDir() + Zstr("LastRun.ffs_real"); - return instance; -} - - -void MainDialog::OnShowHelp(wxCommandEvent& event) -{ - zen::displayHelpEntry(L"html/RealtimeSync.html", this); -} - - -void MainDialog::OnMenuAbout(wxCommandEvent& event) -{ - //build information - wxString build = __TDATE__; -#if wxUSE_UNICODE - build += L" - Unicode"; -#else - build += L" - ANSI"; -#endif //wxUSE_UNICODE - - //compile time info about 32/64-bit build - if (zen::is64BitBuild) - build += L" x64"; - else - build += L" x86"; - assert_static(zen::is32BitBuild || zen::is64BitBuild); - - showNotificationDialog(this, DialogInfoType::INFO, PopupDialogCfg(). - setTitle(_("About")). - setMainInstructions(L"RealtimeSync" L"\n\n" + replaceCpy(_("Build: %x"), L"%x", build))); -} - - -void MainDialog::OnKeyPressed(wxKeyEvent& event) -{ - const int keyCode = event.GetKeyCode(); - if (keyCode == WXK_ESCAPE) - { - Close(); - return; - } - event.Skip(); -} - - -void MainDialog::OnStart(wxCommandEvent& event) -{ - xmlAccess::XmlRealConfig currentCfg = getConfiguration(); - - Hide(); -#ifdef ZEN_MAC - //hide dock icon: else user is able to forcefully show the hidden main dialog by clicking on the icon!! - ProcessSerialNumber psn = { 0, kCurrentProcess }; - ::TransformProcessType(&psn, kProcessTransformToUIElementApplication); -#endif - - switch (rts::startDirectoryMonitor(currentCfg, xmlAccess::extractJobName(utfCvrtTo(currentConfigFileName)))) - { - case rts::EXIT_APP: - Close(); - return; - - case rts::SHOW_GUI: - break; - } - Show(); //don't show for EXIT_APP -#ifdef ZEN_MAC - ::SetFrontProcess(&psn); //call before TransformProcessType() so that OSX menu is updated correctly - //why isn't this covered by wxWindows::Raise()?? - ::TransformProcessType(&psn, kProcessTransformToForegroundApplication); //show dock icon again -#endif - Raise(); -} - - -void MainDialog::OnConfigSave(wxCommandEvent& event) -{ - Zstring defaultFileName = currentConfigFileName.empty() ? Zstr("Realtime.ffs_real") : currentConfigFileName; - //attention: currentConfigFileName may be an imported *.ffs_batch file! We don't want to overwrite it with a GUI config! - if (endsWith(defaultFileName, Zstr(".ffs_batch"))) - replace(defaultFileName, Zstr(".ffs_batch"), Zstr(".ffs_real"), false); - - - wxFileDialog filePicker(this, - wxEmptyString, - //OS X really needs dir/file separated like this: - utfCvrtTo(beforeLast(defaultFileName, FILE_NAME_SEPARATOR)), //default dir; empty string if / not found - utfCvrtTo(afterLast (defaultFileName, FILE_NAME_SEPARATOR)), //default file; whole string if / not found - wxString(L"RealtimeSync (*.ffs_real)|*.ffs_real") + L"|" +_("All files") + L" (*.*)|*", - wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - if (filePicker.ShowModal() != wxID_OK) - return; - - const Zstring newFileName = utfCvrtTo(filePicker.GetPath()); - - //write config to XML - const xmlAccess::XmlRealConfig currentCfg = getConfiguration(); - try - { - writeRealConfig(currentCfg, newFileName); //throw FfsXmlError - setLastUsedConfig(newFileName); - } - catch (const xmlAccess::FfsXmlError& e) - { - showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); - } -} - - -void MainDialog::loadConfig(const Zstring& filename) -{ - xmlAccess::XmlRealConfig newConfig; - - try - { - rts::readRealOrBatchConfig(filename, newConfig); - } - catch (const xmlAccess::FfsXmlError& e) - { - if (e.getSeverity() == xmlAccess::FfsXmlError::WARNING) - showNotificationDialog(this, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(e.toString())); - else - { - showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); - return; - } - } - - setConfiguration(newConfig); - setLastUsedConfig(filename); -} - - -void MainDialog::setLastUsedConfig(const Zstring& filename) -{ - //set title - if (filename == lastConfigFileName()) - { - SetTitle(L"RealtimeSync - " + _("Automated Synchronization")); - currentConfigFileName.clear(); - } - else - { - SetTitle(utfCvrtTo(filename)); - currentConfigFileName = filename; - } -} - - -void MainDialog::OnConfigLoad(wxCommandEvent& event) -{ - wxFileDialog filePicker(this, - wxEmptyString, - utfCvrtTo(beforeLast(currentConfigFileName, FILE_NAME_SEPARATOR)), //default dir; empty string if / not found - wxEmptyString, - wxString(L"RealtimeSync (*.ffs_real; *.ffs_batch)|*.ffs_real;*.ffs_batch") + L"|" +_("All files") + L" (*.*)|*", - wxFD_OPEN); - if (filePicker.ShowModal() == wxID_OK) - loadConfig(utfCvrtTo(filePicker.GetPath())); -} - - -void MainDialog::onFilesDropped(FileDropEvent& event) -{ - const auto& files = event.getFiles(); - if (!files.empty()) - loadConfig(utfCvrtTo(files[0])); -} - - -void MainDialog::setConfiguration(const xmlAccess::XmlRealConfig& cfg) -{ - //clear existing folders - dirNameFirst->setName(wxString()); - clearAddFolders(); - - if (!cfg.directories.empty()) - { - //fill top folder - dirNameFirst->setName(utfCvrtTo(*cfg.directories.begin())); - - //fill additional folders - addFolder(std::vector(cfg.directories.begin() + 1, cfg.directories.end())); - } - - //fill commandline - m_textCtrlCommand->SetValue(utfCvrtTo(cfg.commandline)); - - //set delay - m_spinCtrlDelay->SetValue(static_cast(cfg.delay)); -} - - -xmlAccess::XmlRealConfig MainDialog::getConfiguration() -{ - xmlAccess::XmlRealConfig output; - - output.directories.push_back(utfCvrtTo(dirNameFirst->getName())); - for (auto it = dirNamesExtra.begin(); it != dirNamesExtra.end(); ++it) - output.directories.push_back(utfCvrtTo((*it)->getName())); - - output.commandline = utfCvrtTo(m_textCtrlCommand->GetValue()); - output.delay = m_spinCtrlDelay->GetValue(); - - return output; -} - - -void MainDialog::OnAddFolder(wxCommandEvent& event) -{ - const Zstring topFolder = utfCvrtTo(dirNameFirst->getName()); - - //clear existing top folder first - dirNameFirst->setName(wxString()); - - std::vector newFolders; - newFolders.push_back(topFolder); - - addFolder(newFolders, true); //add pair in front of additonal pairs -} - - -void MainDialog::OnRemoveFolder(wxCommandEvent& event) -{ - //find folder pair originating the event - const wxObject* const eventObj = event.GetEventObject(); - for (auto it = dirNamesExtra.begin(); it != dirNamesExtra.end(); ++it) - if (eventObj == static_cast((*it)->m_bpButtonRemoveFolder)) - { - removeAddFolder(it - dirNamesExtra.begin()); - return; - } -} - - -void MainDialog::OnRemoveTopFolder(wxCommandEvent& event) -{ - if (dirNamesExtra.size() > 0) - { - const wxString topDir = (*dirNamesExtra.begin())->getName(); - - dirNameFirst->setName(topDir); - - removeAddFolder(0); //remove first of additional folders - } -} - - -#ifdef ZEN_WIN -static const size_t MAX_ADD_FOLDERS = 8; -#elif defined ZEN_LINUX || defined ZEN_MAC -static const size_t MAX_ADD_FOLDERS = 6; -#endif - - -void MainDialog::addFolder(const std::vector& newFolders, bool addFront) -{ - if (newFolders.size() == 0) - return; - -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! -#endif - - int folderHeight = 0; - for (auto it = newFolders.begin(); it != newFolders.end(); ++it) - { - //add new folder pair - DirectoryPanel* newFolder = new DirectoryPanel(m_scrolledWinFolders); - newFolder->m_bpButtonRemoveFolder->SetBitmapLabel(getResourceImage(L"item_remove")); - - //get size of scrolled window - folderHeight = newFolder->GetSize().GetHeight(); - - if (addFront) - { - bSizerFolders->Insert(0, newFolder, 0, wxEXPAND, 5); - dirNamesExtra.insert(dirNamesExtra.begin(), newFolder); - } - else - { - bSizerFolders->Add(newFolder, 0, wxEXPAND, 5); - dirNamesExtra.push_back(newFolder); - } - - //register events - newFolder->m_bpButtonRemoveFolder->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MainDialog::OnRemoveFolder), nullptr, this ); - - //insert directory name - newFolder->setName(utfCvrtTo(*it)); - } - - //set size of scrolled window - const size_t additionalRows = std::min(dirNamesExtra.size(), MAX_ADD_FOLDERS); //up to MAX_ADD_FOLDERS additional folders shall be shown - m_scrolledWinFolders->SetMinSize(wxSize( -1, folderHeight * static_cast(additionalRows))); - - //adapt delete top folder pair button - m_bpButtonRemoveTopFolder->Show(); - - GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() - Layout(); - Refresh(); //remove a little flicker near the start button -} - - -void MainDialog::removeAddFolder(size_t pos) -{ -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! -#endif - - if (pos < dirNamesExtra.size()) - { - //remove folder pairs from window - DirectoryPanel* pairToDelete = dirNamesExtra[pos]; - const int folderHeight = pairToDelete->GetSize().GetHeight(); - - bSizerFolders->Detach(pairToDelete); //Remove() does not work on Window*, so do it manually - dirNamesExtra.erase(dirNamesExtra.begin() + pos); //remove last element in vector - //more (non-portable) wxWidgets bullshit: on OS X wxWindow::Destroy() screws up and calls "operator delete" directly rather than - //the deferred deletion it is expected to do (and which is implemented correctly on Windows and Linux) - //http://bb10.com/python-wxpython-devel/2012-09/msg00004.html - //=> since we're in a mouse button callback of a sub-component of "pairToDelete" we need to delay deletion ourselves: - processAsync2([] {}, [pairToDelete] { pairToDelete->Destroy(); }); - - //set size of scrolled window - const size_t additionalRows = std::min(dirNamesExtra.size(), MAX_ADD_FOLDERS); //up to MAX_ADD_FOLDERS additional folders shall be shown - m_scrolledWinFolders->SetMinSize(wxSize( -1, folderHeight * static_cast(additionalRows))); - - //adapt delete top folder pair button - if (dirNamesExtra.size() == 0) - { - m_bpButtonRemoveTopFolder->Hide(); - m_panelMainFolder->Layout(); - } - - GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() - Layout(); - Refresh(); //remove a little flicker near the start button - } -} - - -void MainDialog::clearAddFolders() -{ -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! -#endif - - bSizerFolders->Clear(true); - dirNamesExtra.clear(); - - m_scrolledWinFolders->SetMinSize(wxSize(-1, 0)); - - m_bpButtonRemoveTopFolder->Hide(); - m_panelMainFolder->Layout(); - - GetSizer()->SetSizeHints(this); //~=Fit() - Layout(); - Refresh(); //remove a little flicker near the start button -} diff --git a/RealtimeSync/main_dlg.h b/RealtimeSync/main_dlg.h deleted file mode 100644 index 9e5537f0..00000000 --- a/RealtimeSync/main_dlg.h +++ /dev/null @@ -1,80 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef REALTIMESYNCMAIN_H -#define REALTIMESYNCMAIN_H - -#include "gui_generated.h" -#include -#include -#include -#include -#include -#include -#include "../ui/dir_name.h" - -namespace xmlAccess -{ -struct XmlRealConfig; -} -class DirectoryPanel; - - -class MainDialog: public MainDlgGenerated -{ -public: - static void create(const Zstring& cfgFile); - -private: - MainDialog(wxDialog* dlg, const Zstring& cfgFileName); - ~MainDialog(); - - void loadConfig(const Zstring& filename); - - virtual void OnClose (wxCloseEvent& event) { Destroy(); } - virtual void OnShowHelp (wxCommandEvent& event); - virtual void OnMenuAbout (wxCommandEvent& event); - virtual void OnAddFolder (wxCommandEvent& event); - virtual void OnRemoveFolder (wxCommandEvent& event); - virtual void OnRemoveTopFolder(wxCommandEvent& event); - virtual void OnKeyPressed (wxKeyEvent& event); - virtual void OnStart (wxCommandEvent& event); - virtual void OnConfigSave (wxCommandEvent& event); - virtual void OnConfigLoad (wxCommandEvent& event); - virtual void OnMenuQuit (wxCommandEvent& event) { Close(); } - void onFilesDropped(zen::FileDropEvent& event); - - void setConfiguration(const xmlAccess::XmlRealConfig& cfg); - xmlAccess::XmlRealConfig getConfiguration(); - void setLastUsedConfig(const Zstring& filename); - - void layoutAsync(); //call Layout() asynchronously - - //void addFolder(const Zstring& dirname, bool addFront = false); - void addFolder(const std::vector& newFolders, bool addFront = false); - void removeAddFolder(size_t pos); - void clearAddFolders(); - - static const Zstring& lastConfigFileName(); - - std::unique_ptr> dirNameFirst; - std::vector dirNamesExtra; //additional pairs to the standard pair - - Zstring currentConfigFileName; - - void onProcessAsyncTasks(wxEvent& event); - - template - void processAsync(Fun doAsync, Fun2 evalOnGui) { asyncTasks.add(doAsync, evalOnGui); timerForAsyncTasks.Start(50); /*timer interval in [ms] */ } - template - void processAsync2(Fun doAsync, Fun2 evalOnGui) { asyncTasks.add2(doAsync, evalOnGui); timerForAsyncTasks.Start(50); /*timer interval in [ms] */ } - - //schedule and run long-running tasks asynchronously, but process results on GUI queue - zen::AsyncTasks asyncTasks; - wxTimer timerForAsyncTasks; //don't use wxWidgets idle handling => repeated idle requests/consumption hogs 100% cpu! -}; - -#endif // REALTIMESYNCMAIN_H diff --git a/RealtimeSync/makefile b/RealtimeSync/makefile deleted file mode 100644 index 8098d586..00000000 --- a/RealtimeSync/makefile +++ /dev/null @@ -1,101 +0,0 @@ -BLAH_BLUBB_123= -#for some buggy reason the first row in the make file has no effect on Suse Linux! => make sure there's no important command -APPNAME = RealtimeSync -prefix = /usr -BINDIR = $(DESTDIR)$(prefix)/bin - -CXXFLAGS = -std=c++11 -Wall -pipe -O3 -DNDEBUG -DwxUSE_UNICODE -DWXINTL_NO_GETTEXT_MACRO -I.. -include "zen/i18n.h" -include "zen/warn_static.h" -LINKFLAGS = - -#distinguish Linux/Mac builds -OPERATING_SYSTEM_NAME := $(shell uname) - -#################### Linux ############################ -ifeq ($(OPERATING_SYSTEM_NAME), Linux) -COMPILER_BIN=g++ -pthread -CXXFLAGS += -DZEN_LINUX - -#Gtk - support "no button border" -CXXFLAGS += `pkg-config --cflags gtk+-2.0` -LINKFLAGS += `pkg-config --libs gtk+-2.0` - -ifeq ($(BUILD),Launchpad) -#default build/Launchpad -CXXFLAGS += `wx-config --cxxflags --debug=no` -LINKFLAGS += `wx-config --libs --debug=no` -lboost_thread -lboost_system -lz -else -#static wxWidgets and boost library linkage for precompiled release -WX_CONFIG_BIN =$(HOME)/Desktop/wxWidgets-2.9.5/lib/release/bin/wx-config -CXXFLAGS += -I$(HOME)/Desktop/boost_1_54_0 -BOOST_LIB_DIR =$(HOME)/Desktop/boost_1_54_0/stage/lib - -CXXFLAGS += `$(WX_CONFIG_BIN) --cxxflags --debug=no --static=yes` -LINKFLAGS += `$(WX_CONFIG_BIN) --libs --debug=no --static=yes` $(BOOST_LIB_DIR)/libboost_thread.a $(BOOST_LIB_DIR)/libboost_system.a -lX11 -endif - -endif -#################### OS X ############################ -ifeq ($(OPERATING_SYSTEM_NAME), Darwin) -COMPILER_BIN=clang++ -stdlib=libc++ -CXXFLAGS += -DZEN_MAC - -WX_CONFIG_BIN =$(HOME)/Desktop/wxWidgets-2.9.5/lib/release/bin/wx-config -CXXFLAGS += -I$(HOME)/Desktop/boost_1_54_0 -BOOST_LIB_DIR =$(HOME)/Desktop/boost_1_54_0/stage/lib -MACOS_SDK =-mmacosx-version-min=10.7 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk -#-Wl,-Bstatic not supported on OSX! - -# link wxWidgets and boost statically -> check dependencies with: otool -L RealtimeSync -CXXFLAGS += $(MACOS_SDK) `$(WX_CONFIG_BIN) --cxxflags --debug=no --static=yes` -LINKFLAGS += $(MACOS_SDK) `$(WX_CONFIG_BIN) --libs --debug=no --static=yes` $(BOOST_LIB_DIR)/libboost_thread.a $(BOOST_LIB_DIR)/libboost_system.a - -endif -###################################################### - -CPP_LIST= #internal list of all *.cpp files needed for compilation -CPP_LIST+=application.cpp -CPP_LIST+=gui_generated.cpp -CPP_LIST+=main_dlg.cpp -CPP_LIST+=tray_menu.cpp -CPP_LIST+=monitor.cpp -CPP_LIST+=xml_ffs.cpp -CPP_LIST+=xml_proc.cpp -CPP_LIST+=../structures.cpp -CPP_LIST+=../ui/dir_name.cpp -CPP_LIST+=../ui/folder_history_box.cpp -CPP_LIST+=../lib/localization.cpp -CPP_LIST+=../lib/process_xml.cpp -CPP_LIST+=../lib/resolve_path.cpp -CPP_LIST+=../lib/xml_base.cpp -CPP_LIST+=../lib/ffs_paths.cpp -CPP_LIST+=../zen/dir_watcher.cpp -CPP_LIST+=../zen/file_handling.cpp -CPP_LIST+=../zen/file_io.cpp -CPP_LIST+=../zen/file_traverser.cpp -CPP_LIST+=../zen/zstring.cpp -CPP_LIST+=../zen/format_unit.cpp -CPP_LIST+=../wx+/image_tools.cpp -CPP_LIST+=../wx+/image_resources.cpp -CPP_LIST+=../wx+/popup_dlg.cpp -CPP_LIST+=../wx+/popup_dlg_generated.cpp - -#list of all *.o files (we need the "RTS" subdirectory to handle "../*.cpp" files -OBJECT_LIST=$(CPP_LIST:%.cpp=../OBJ/RTS_GCC_Make_Release/RTS/%.o) - -all: RealtimeSync - -../OBJ/RTS_GCC_Make_Release/RTS/%.o : %.cpp - mkdir -p $(dir $@) - $(COMPILER_BIN) $(CXXFLAGS) -c $< -o $@ - -RealtimeSync: $(OBJECT_LIST) - $(COMPILER_BIN) -o ../BUILD/$(APPNAME) $(OBJECT_LIST) $(LINKFLAGS) - -clean: - rm -rf ../OBJ/RTS_GCC_Make_Release - rm -f ../BUILD/$(APPNAME) - rm -f ../wx+/pch.h.gch - -install: - mkdir -p $(BINDIR) - cp ../BUILD/$(APPNAME) $(BINDIR) diff --git a/RealtimeSync/monitor.cpp b/RealtimeSync/monitor.cpp deleted file mode 100644 index 88536281..00000000 --- a/RealtimeSync/monitor.cpp +++ /dev/null @@ -1,281 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "monitor.h" -#include -#include -#include -#include -#include -#include -#include -#include "../lib/resolve_path.h" -//#include "../library/db_file.h" //SYNC_DB_FILE_ENDING -> complete file too much of a dependency; file ending too little to decouple into single header -//#include "../library/lock_holder.h" //LOCK_FILE_ENDING - -using namespace zen; - - -namespace -{ -const int CHECK_DIR_INTERVAL = 1; //unit: [s] - - -std::vector getFormattedDirs(const std::vector& dirs) //throw FileError -{ - std::set tmp; //make unique - - std::transform(dirs.begin(), dirs.end(), std::inserter(tmp, tmp.end()), - [](const Zstring& dirnameNonFmt) { return getFormattedDirectoryName(dirnameNonFmt); }); - - return std::vector(tmp.begin(), tmp.end()); -} - - -//wait until changes are detected or if a directory is not available (anymore) -struct WaitResult -{ - enum ChangeType - { - CHANGE_DETECTED, - CHANGE_DIR_MISSING - }; - - WaitResult(const zen::DirWatcher::Entry& changedItem) : type(CHANGE_DETECTED), changedItem_(changedItem) {} - WaitResult(const Zstring& dirname) : type(CHANGE_DIR_MISSING), dirname_(dirname) {} - - ChangeType type; - zen::DirWatcher::Entry changedItem_; //for type == CHANGE_DETECTED: file or directory - Zstring dirname_; //for type == CHANGE_DIR_MISSING -}; - - -WaitResult waitForChanges(const std::vector& dirNamesNonFmt, //throw FileError - const std::function& onRefreshGui) //bool: readyForSync -{ - const std::vector dirNamesFmt = getFormattedDirs(dirNamesNonFmt); //throw FileError - if (dirNamesFmt.empty()) //pathological case, but we have to check else this function will wait endlessly - throw zen::FileError(_("A folder input field is empty.")); //should have been checked by caller! - - //detect when volumes are removed/are not available anymore - std::vector>> watches; - - for (auto it = dirNamesFmt.begin(); it != dirNamesFmt.end(); ++it) - { - const Zstring& dirnameFmt = *it; - try - { - //a non-existent network path may block, so check existence asynchronously! - auto ftDirExists = async([=] { return zen::dirExists(dirnameFmt); }); - //we need to check dirExists(), not somethingExists(): it's not clear if DirWatcher detects a type clash (file instead of directory!) - while (!ftDirExists.timed_wait(boost::posix_time::milliseconds(rts::UI_UPDATE_INTERVAL / 2))) - onRefreshGui(false); //may throw! - if (!ftDirExists.get()) - return WaitResult(dirnameFmt); - - watches.push_back(std::make_pair(dirnameFmt, std::make_shared(dirnameFmt))); //throw FileError - } - catch (FileError&) - { - if (!somethingExists(dirnameFmt)) //a benign(?) race condition with FileError - return WaitResult(dirnameFmt); - throw; - } - } - - const std::int64_t TICKS_DIR_CHECK_INTERVAL = CHECK_DIR_INTERVAL * ticksPerSec(); //0 on error - TickVal lastCheck = getTicks(); //0 on error - while (true) - { - const bool checkDirExistNow = [&]() -> bool //checking once per sec should suffice - { - const TickVal now = getTicks(); //0 on error - if (dist(lastCheck, now) >= TICKS_DIR_CHECK_INTERVAL) - { - lastCheck = now; - return true; - } - return false; - }(); - - - for (auto it = watches.begin(); it != watches.end(); ++it) - { - const Zstring& dirname = it->first; - DirWatcher& watcher = *(it->second); - - //IMPORTANT CHECK: dirwatcher has problems detecting removal of top watched directories! - if (checkDirExistNow) - if (!dirExists(dirname)) //catch errors related to directory removal, e.g. ERROR_NETNAME_DELETED -> somethingExists() is NOT sufficient here! - return WaitResult(dirname); - try - { - std::vector changedItems = watcher.getChanges([&] { onRefreshGui(false); /*may throw!*/ }); //throw FileError - - //remove to be ignored changes - vector_remove_if(changedItems, [](const DirWatcher::Entry& e) - { - return endsWith(e.filename_, Zstr(".ffs_lock")) || //sync.ffs_lock, sync.Del.ffs_lock - endsWith(e.filename_, Zstr(".ffs_db")); //sync.ffs_db, .sync.tmp.ffs_db - //no need to ignore temporal recycle bin directory: this must be caused by a file deletion anyway - }); - - if (!changedItems.empty()) - { - /* - std::for_each(changedItems.begin(), changedItems.end(), - [](const Zstring& fn) { wxMessageBox(toWx(fn));}); - */ - return WaitResult(changedItems[0]); //directory change detected - } - - } - catch (FileError&) - { - if (!somethingExists(dirname)) //a benign(?) race condition with FileError - return WaitResult(dirname); - throw; - } - } - - boost::this_thread::sleep(boost::posix_time::milliseconds(rts::UI_UPDATE_INTERVAL / 2)); - onRefreshGui(true); //throw ?: may start sync at this presumably idle time - } -} - - -//wait until all directories become available (again) + logs in network share -void waitForMissingDirs(const std::vector& dirNamesNonFmt, //throw FileError - const std::function& onRefreshGui) //Zstring: the directory that is currently being waited for -{ - while (true) - { - //support specifying volume by name => call getFormattedDirectoryName() repeatedly - const std::vector& dirNamesFmt = getFormattedDirs(dirNamesNonFmt); //throw FileError - - bool allExisting = true; - for (auto it = dirNamesFmt.begin(); it != dirNamesFmt.end(); ++it) - { - const Zstring dirnameFmt = *it; - auto ftDirExisting = async([=]() -> bool - { -#ifdef ZEN_WIN - //1. login to network share, if necessary -> we probably do NOT want multiple concurrent runs: GUI!? - loginNetworkShare(dirnameFmt, false); //login networks shares, no PW prompt -> is this really RTS's job? -#endif - //2. check dir existence - return zen::dirExists(dirnameFmt); - }); - while (!ftDirExisting.timed_wait(boost::posix_time::milliseconds(rts::UI_UPDATE_INTERVAL / 2))) - onRefreshGui(dirnameFmt); //may throw! - - if (!ftDirExisting.get()) - { - allExisting = false; - //wait some time... - const int refreshInterval = rts::UI_UPDATE_INTERVAL / 2; - assert_static(CHECK_DIR_INTERVAL * 1000 % refreshInterval == 0); - for (int i = 0; i < CHECK_DIR_INTERVAL * 1000 / refreshInterval; ++i) - { - onRefreshGui(dirnameFmt); //may throw! - boost::this_thread::sleep(boost::posix_time::milliseconds(refreshInterval)); - } - break; - } - } - if (allExisting) - return; - } -} - - -inline -wxString toString(DirWatcher::ActionType type) -{ - switch (type) - { - case DirWatcher::ACTION_CREATE: - return L"CREATE"; - case DirWatcher::ACTION_UPDATE: - return L"UPDATE"; - case DirWatcher::ACTION_DELETE: - return L"DELETE"; - } - return L"ERROR"; -} - -struct ExecCommandNowException {}; -} - - -void rts::monitorDirectories(const std::vector& dirNamesNonFmt, unsigned int delay, rts::MonitorCallback& callback) -{ - if (dirNamesNonFmt.empty()) - { - assert(false); - return; - } - - auto execMonitoring = [&] //throw FileError - { - callback.setPhase(MonitorCallback::MONITOR_PHASE_WAITING); - waitForMissingDirs(dirNamesNonFmt, [&](const Zstring& dirname) { callback.requestUiRefresh(); }); //throw FileError - callback.setPhase(MonitorCallback::MONITOR_PHASE_ACTIVE); - - //schedule initial execution (*after* all directories have arrived, which could take some time which we don't want to include) - time_t nextExecDate = std::time(nullptr) + delay; - - while (true) //loop over command invocations - { - DirWatcher::Entry lastChangeDetected; - try - { - while (true) //loop over detected changes - { - //wait for changes (and for all directories to become available) - WaitResult res = waitForChanges(dirNamesNonFmt, [&](bool readyForSync) //throw FileError, ExecCommandNowException - { - if (readyForSync) - if (nextExecDate <= std::time(nullptr)) - throw ExecCommandNowException(); //abort wait and start sync - callback.requestUiRefresh(); - }); - switch (res.type) - { - case WaitResult::CHANGE_DIR_MISSING: //don't execute the command before all directories are available! - callback.setPhase(MonitorCallback::MONITOR_PHASE_WAITING); - waitForMissingDirs(dirNamesNonFmt, [&](const Zstring& dirname) { callback.requestUiRefresh(); }); //throw FileError - callback.setPhase(MonitorCallback::MONITOR_PHASE_ACTIVE); - break; - - case WaitResult::CHANGE_DETECTED: - lastChangeDetected = res.changedItem_; - break; - } - nextExecDate = std::time(nullptr) + delay; - } - } - catch (ExecCommandNowException&) {} - - ::wxSetEnv(L"change_path", utfCvrtTo(lastChangeDetected.filename_)); //some way to output what file changed to the user - ::wxSetEnv(L"change_action", toString(lastChangeDetected.action_)); // - - //execute command - callback.executeExternalCommand(); - nextExecDate = std::numeric_limits::max(); - } - }; - - while (true) - try - { - execMonitoring(); //throw FileError - } - catch (const zen::FileError& e) - { - callback.reportError(e.toString()); - } -} diff --git a/RealtimeSync/monitor.h b/RealtimeSync/monitor.h deleted file mode 100644 index 0b9dfbc0..00000000 --- a/RealtimeSync/monitor.h +++ /dev/null @@ -1,38 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef MONITOR_HEADER_345087425834253425 -#define MONITOR_HEADER_345087425834253425 - -#include -#include - -namespace rts -{ -const int UI_UPDATE_INTERVAL = 100; //unit: [ms]; perform ui updates not more often than necessary, 100 seems to be a good value with only a minimal performance loss - - -struct MonitorCallback -{ - virtual ~MonitorCallback() {} - - enum WatchPhase - { - MONITOR_PHASE_ACTIVE, - MONITOR_PHASE_WAITING, - }; - virtual void setPhase(WatchPhase mode) = 0; - virtual void executeExternalCommand() = 0; - virtual void requestUiRefresh() = 0; - virtual void reportError(const std::wstring& msg) = 0; //automatically retries after return! -}; -void monitorDirectories(const std::vector& dirNamesNonFmt, - //non-formatted dirnames that yet require call to getFormattedDirectoryName(); empty directories must be checked by caller! - unsigned int delay, - MonitorCallback& callback); -} - -#endif //MONITOR_HEADER_345087425834253425 diff --git a/RealtimeSync/tray_menu.cpp b/RealtimeSync/tray_menu.cpp deleted file mode 100644 index 6e67b5ec..00000000 --- a/RealtimeSync/tray_menu.cpp +++ /dev/null @@ -1,342 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "tray_menu.h" -#include -#include -#include -#include //Linux needs this -#include -#include -#include -#include -#include -#include -#include -#include "monitor.h" -#include "../lib/resolve_path.h" - -using namespace rts; -using namespace zen; - - -namespace -{ -const std::int64_t TICKS_UPDATE_INTERVAL = rts::UI_UPDATE_INTERVAL* ticksPerSec() / 1000; -TickVal lastExec = getTicks(); - -bool updateUiIsAllowed() -{ - const TickVal now = getTicks(); //0 on error - if (dist(lastExec, now) >= TICKS_UPDATE_INTERVAL) //perform ui updates not more often than necessary - { - lastExec = now; - return true; - } - return false; -} - - -enum TrayMode -{ - TRAY_MODE_ACTIVE, - TRAY_MODE_WAITING, - TRAY_MODE_ERROR, -}; - - -class TrayIconObject : public wxTaskBarIcon -{ -public: - TrayIconObject(const wxString& jobname) : - resumeRequested(false), - abortRequested(false), - showErrorMsgRequested(false), - mode(TRAY_MODE_ACTIVE), - iconFlashStatusLast(false), - jobName_(jobname), -#if defined ZEN_WIN || defined ZEN_MAC //16x16 seems to be the only size that is shown correctly on OS X - trayBmp(getResourceImage(L"RTS_tray_16x16")) //use a 16x16 bitmap -#elif defined ZEN_LINUX - trayBmp(getResourceImage(L"RTS_tray_24x24")) //use a 24x24 bitmap for perfect fit -#endif - { - Connect(wxEVT_TASKBAR_LEFT_DCLICK, wxEventHandler(TrayIconObject::OnDoubleClick), nullptr, this); - setMode(mode); - } - - //require polling: - bool resumeIsRequested() const { return resumeRequested; } - bool abortIsRequested () const { return abortRequested; } - - //during TRAY_MODE_ERROR those two functions are available: - void clearShowErrorRequested() { assert(mode == TRAY_MODE_ERROR); showErrorMsgRequested = false; } - bool getShowErrorRequested() const { assert(mode == TRAY_MODE_ERROR); return showErrorMsgRequested; } - - void setMode(TrayMode m) - { - mode = m; - timer.Stop(); - timer.Disconnect(wxEVT_TIMER, wxEventHandler(TrayIconObject::OnErrorFlashIcon), nullptr, this); - switch (m) - { - case TRAY_MODE_ACTIVE: - setTrayIcon(trayBmp, _("Directory monitoring active")); - break; - - case TRAY_MODE_WAITING: - setTrayIcon(greyScale(trayBmp), _("Waiting until all directories are available...")); - break; - - case TRAY_MODE_ERROR: - timer.Connect(wxEVT_TIMER, wxEventHandler(TrayIconObject::OnErrorFlashIcon), nullptr, this); - timer.Start(500); //timer interval in [ms] - break; - } - } - -private: - void OnErrorFlashIcon(wxEvent& event) - { - iconFlashStatusLast = !iconFlashStatusLast; - setTrayIcon(iconFlashStatusLast ? trayBmp : greyScale(trayBmp), _("Error")); - } - - void setTrayIcon(const wxBitmap& bmp, const wxString& statusTxt) - { - wxIcon realtimeIcon; - realtimeIcon.CopyFromBitmap(bmp); - wxString tooltip = L"RealtimeSync\n" + statusTxt; - if (!jobName_.empty()) - tooltip += L"\n\"" + jobName_ + L"\""; - SetIcon(realtimeIcon, tooltip); - } - - enum Selection - { - CONTEXT_RESTORE = 1, //wxWidgets: "A MenuItem ID of zero does not work under Mac" - CONTEXT_SHOW_ERROR, - CONTEXT_ABORT = wxID_EXIT - }; - - virtual wxMenu* CreatePopupMenu() - { - wxMenu* contextMenu = new wxMenu; - - wxMenuItem* defaultItem = nullptr; - switch (mode) - { - case TRAY_MODE_ACTIVE: - case TRAY_MODE_WAITING: - defaultItem = new wxMenuItem(contextMenu, CONTEXT_RESTORE, _("&Restore")); - break; - case TRAY_MODE_ERROR: - defaultItem = new wxMenuItem(contextMenu, CONTEXT_SHOW_ERROR, _("&Show error")); - break; - } -#ifdef ZEN_WIN //no wxMenuItem::SetFont() on Linux and OS X: wasn't wxWidgets supposed to be *portable* at some point in time????? - defaultItem->SetFont(wxNORMAL_FONT->Bold()); -#endif - contextMenu->Append(defaultItem); - - contextMenu->AppendSeparator(); - contextMenu->Append(CONTEXT_ABORT, _("&Exit")); - //event handling - contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TrayIconObject::OnContextMenuSelection), nullptr, this); - - return contextMenu; //ownership transferred to caller - } - - void OnContextMenuSelection(wxCommandEvent& event) - { - switch (static_cast(event.GetId())) - { - case CONTEXT_ABORT: - abortRequested = true; - break; - - case CONTEXT_RESTORE: - resumeRequested = true; - break; - - case CONTEXT_SHOW_ERROR: - showErrorMsgRequested = true; - break; - } - } - - void OnDoubleClick(wxEvent& event) - { - switch (mode) - { - case TRAY_MODE_ACTIVE: - case TRAY_MODE_WAITING: - resumeRequested = true; //never throw exceptions through a C-Layer call stack (GUI)! - break; - case TRAY_MODE_ERROR: - showErrorMsgRequested = true; - break; - } - } - - bool resumeRequested; - bool abortRequested; - bool showErrorMsgRequested; - - TrayMode mode; - - bool iconFlashStatusLast; //flash try icon for TRAY_MODE_ERROR - wxTimer timer; // - - const wxString jobName_; //RTS job name, may be empty - const wxBitmap trayBmp; -}; - - -struct AbortMonitoring //exception class -{ - AbortMonitoring(AbortReason reasonCode) : reasonCode_(reasonCode) {} - AbortReason reasonCode_; -}; - - -//=> don't derive from wxEvtHandler or any other wxWidgets object unless instance is safely deleted (deferred) during idle event!!tray_icon.h -class TrayIconHolder -{ -public: - TrayIconHolder(const wxString& jobname) : - trayObj(new TrayIconObject(jobname)) {} - - ~TrayIconHolder() - { - //harmonize with tray_icon.cpp!!! - trayObj->RemoveIcon(); - //use wxWidgets delayed destruction: delete during next idle loop iteration (handle late window messages, e.g. when double-clicking) - wxPendingDelete.Append(trayObj); - } - - void doUiRefreshNow() //throw AbortMonitoring - { - wxTheApp->Yield(); //yield is UI-layer which is represented by this tray icon - - //advantage of polling vs callbacks: we can throw exceptions! - if (trayObj->resumeIsRequested()) - throw AbortMonitoring(SHOW_GUI); - - if (trayObj->abortIsRequested()) - throw AbortMonitoring(EXIT_APP); - } - - void setMode(TrayMode m) { trayObj->setMode(m); } - - bool getShowErrorRequested() const { return trayObj->getShowErrorRequested(); } - void clearShowErrorRequested() { trayObj->clearShowErrorRequested(); } - -private: - TrayIconObject* trayObj; -}; - -//############################################################################################################## -} - - -rts::AbortReason rts::startDirectoryMonitor(const xmlAccess::XmlRealConfig& config, const wxString& jobname) -{ - std::vector dirNamesNonFmt = config.directories; - vector_remove_if(dirNamesNonFmt, [](Zstring str) -> bool { trim(str); return str.empty(); }); //remove empty entries WITHOUT formatting paths yet! - - if (dirNamesNonFmt.empty()) - { - showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setMainInstructions(_("A folder input field is empty."))); - return SHOW_GUI; - } - - Zstring cmdLine = config.commandline; - trim(cmdLine); - - if (cmdLine.empty()) - { - showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setMainInstructions(_("Incorrect command line:") + L" \"\"")); - return SHOW_GUI; - } - - struct MonitorCallbackImpl : public MonitorCallback - { - MonitorCallbackImpl(const wxString& jobname, - const Zstring& cmdLine) : trayIcon(jobname), cmdLine_(cmdLine) {} - - virtual void setPhase(WatchPhase mode) - { - switch (mode) - { - case MONITOR_PHASE_ACTIVE: - trayIcon.setMode(TRAY_MODE_ACTIVE); - break; - case MONITOR_PHASE_WAITING: - trayIcon.setMode(TRAY_MODE_WAITING); - break; - } - } - - virtual void executeExternalCommand() - { - auto cmdLineExp = expandMacros(cmdLine_); - try - { - shellExecute2(cmdLineExp, EXEC_TYPE_SYNC); //throw FileError - } - catch (const FileError& e) - { - warn_static("fix dialog hiding on OSX !!") - showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); - } - } - - virtual void requestUiRefresh() - { - if (updateUiIsAllowed()) - trayIcon.doUiRefreshNow(); //throw AbortMonitoring - } - - virtual void reportError(const std::wstring& msg) - { - trayIcon.setMode(TRAY_MODE_ERROR); - trayIcon.clearShowErrorRequested(); - - //wait for some time, then return to retry - assert_static(15 * 1000 % UI_UPDATE_INTERVAL == 0); - for (int i = 0; i < 15 * 1000 / UI_UPDATE_INTERVAL; ++i) - { - trayIcon.doUiRefreshNow(); //throw AbortMonitoring - - if (trayIcon.getShowErrorRequested()) - switch (showConfirmationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg(). - setDetailInstructions(msg), _("&Retry"))) - { - case ConfirmationButton::DO_IT: //retry - return; - case ConfirmationButton::CANCEL: - throw AbortMonitoring(SHOW_GUI); - } - boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL)); - } - } - - TrayIconHolder trayIcon; - const Zstring cmdLine_; - } cb(jobname, cmdLine); - - try - { - monitorDirectories(dirNamesNonFmt, config.delay, cb); //cb: throw AbortMonitoring - assert(false); - return SHOW_GUI; - } - catch (const AbortMonitoring& ab) - { - return ab.reasonCode_; - } -} diff --git a/RealtimeSync/tray_menu.h b/RealtimeSync/tray_menu.h deleted file mode 100644 index 1f71a017..00000000 --- a/RealtimeSync/tray_menu.h +++ /dev/null @@ -1,23 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef TRAY_583967857420987534253245 -#define TRAY_583967857420987534253245 - -#include -#include "xml_proc.h" - -namespace rts -{ -enum AbortReason -{ - SHOW_GUI, - EXIT_APP -}; -AbortReason startDirectoryMonitor(const xmlAccess::XmlRealConfig& config, const wxString& jobname); //jobname may be empty -} - -#endif //TRAY_583967857420987534253245 diff --git a/RealtimeSync/xml_ffs.cpp b/RealtimeSync/xml_ffs.cpp deleted file mode 100644 index 149b0bbe..00000000 --- a/RealtimeSync/xml_ffs.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "xml_ffs.h" -#include "../lib/ffs_paths.h" -#include -//#include - -//include FreeFileSync xml headers -#include "../lib/process_xml.h" - -using namespace zen; - - -xmlAccess::XmlRealConfig convertBatchToReal(const xmlAccess::XmlBatchConfig& batchCfg, const Zstring& filename) -{ - std::set uniqueFolders; - - //add main folders - uniqueFolders.insert(batchCfg.mainCfg.firstPair.leftDirectory); - uniqueFolders.insert(batchCfg.mainCfg.firstPair.rightDirectory); - - //additional folders - std::for_each(batchCfg.mainCfg.additionalPairs.begin(), batchCfg.mainCfg.additionalPairs.end(), - [&](const FolderPairEnh & fp) - { - uniqueFolders.insert(fp.leftDirectory); - uniqueFolders.insert(fp.rightDirectory); - }); - - uniqueFolders.erase(Zstring()); - - xmlAccess::XmlRealConfig output; - output.directories.assign(uniqueFolders.begin(), uniqueFolders.end()); - output.commandline = Zstr("\"") + zen::getFreeFileSyncLauncher() + Zstr("\" \"") + filename + Zstr("\""); - return output; -} - - -void rts::readRealOrBatchConfig(const Zstring& filename, xmlAccess::XmlRealConfig& config) //throw xmlAccess::FfsXmlError; -{ - if (xmlAccess::getXmlType(filename) != xmlAccess::XML_TYPE_BATCH) - { - xmlAccess::readRealConfig(filename, config); - return; - } - - //convert batch config to RealtimeSync config - xmlAccess::XmlBatchConfig batchCfg; - try - { - xmlAccess::readConfig(filename, batchCfg); //throw xmlAccess::FfsXmlError; - } - catch (const xmlAccess::FfsXmlError& e) - { - if (e.getSeverity() == xmlAccess::FfsXmlError::WARNING) - config = convertBatchToReal(batchCfg, filename); //do work despite parsing errors, then re-throw - - throw; // - } - config = convertBatchToReal(batchCfg, filename); -} - - -int rts::getProgramLanguage() -{ - xmlAccess::XmlGlobalSettings settings; - - try - { - xmlAccess::readConfig(settings); - } - catch (const xmlAccess::FfsXmlError&) {} //user default language if error occurred - - return settings.programLanguage; -} diff --git a/RealtimeSync/xml_ffs.h b/RealtimeSync/xml_ffs.h deleted file mode 100644 index 90c1c6ca..00000000 --- a/RealtimeSync/xml_ffs.h +++ /dev/null @@ -1,23 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef XMLFREEFILESYNC_H_INCLUDED -#define XMLFREEFILESYNC_H_INCLUDED - -#include "xml_proc.h" -#include - - -//reuse (some of) FreeFileSync's xml files - -namespace rts -{ -void readRealOrBatchConfig(const Zstring& filename, xmlAccess::XmlRealConfig& config); //throw FfsXmlError - -int getProgramLanguage(); -} - -#endif // XMLFREEFILESYNC_H_INCLUDED diff --git a/RealtimeSync/xml_proc.cpp b/RealtimeSync/xml_proc.cpp deleted file mode 100644 index db235ecb..00000000 --- a/RealtimeSync/xml_proc.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "xml_proc.h" -#include -#include -#include - -using namespace zen; -using namespace xmlAccess; - - -namespace -{ -void readConfig(const XmlIn& in, XmlRealConfig& config) -{ - in["Directories"](config.directories); - in["Delay" ](config.delay); - in["Commandline"](config.commandline); -} - - -bool isXmlTypeRTS(const XmlDoc& doc) //throw() -{ - if (doc.root().getNameAs() == "FreeFileSync") - { - std::string type; - if (doc.root().getAttribute("XmlType", type)) - return type == "REAL"; - } - return false; -} -} - - -void xmlAccess::readRealConfig(const Zstring& filename, XmlRealConfig& config) -{ - XmlDoc doc = loadXmlDocument(filename); //throw FfsXmlError - - if (!isXmlTypeRTS(doc)) - throw FfsXmlError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename))); - - XmlIn in(doc); - ::readConfig(in, config); - - if (in.errorsOccured()) - throw FfsXmlError(replaceCpy(_("Configuration file %x loaded partially only."), L"%x", fmtFileName(filename)) + L"\n\n" + - getErrorMessageFormatted(in.getErrorsAs()), FfsXmlError::WARNING); -} - - -namespace -{ -void writeConfig(const XmlRealConfig& config, XmlOut& out) -{ - out["Directories"](config.directories); - out["Delay" ](config.delay); - out["Commandline"](config.commandline); -} -} - - -void xmlAccess::writeRealConfig(const XmlRealConfig& config, const Zstring& filename) -{ - XmlDoc doc("FreeFileSync"); - doc.root().setAttribute("XmlType", "REAL"); - - XmlOut out(doc); - writeConfig(config, out); - - saveXmlDocument(doc, filename); //throw FfsXmlError -} diff --git a/RealtimeSync/xml_proc.h b/RealtimeSync/xml_proc.h deleted file mode 100644 index 671a237f..00000000 --- a/RealtimeSync/xml_proc.h +++ /dev/null @@ -1,30 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef XMLPROCESSING_H_INCLUDED -#define XMLPROCESSING_H_INCLUDED - -#include -//#include -#include -#include "../lib/xml_base.h" - - -namespace xmlAccess -{ -struct XmlRealConfig -{ - XmlRealConfig() : delay(10) {} - std::vector directories; - Zstring commandline; - unsigned int delay; -}; - -void readRealConfig(const Zstring& filename, XmlRealConfig& config); //throw FfsXmlError -void writeRealConfig(const XmlRealConfig& config, const Zstring& filename); //throw FfsXmlError -} - -#endif // XMLPROCESSING_H_INCLUDED diff --git a/algorithm.cpp b/algorithm.cpp deleted file mode 100644 index 44e3f643..00000000 --- a/algorithm.cpp +++ /dev/null @@ -1,1426 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "algorithm.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "lib/norm_filter.h" -#include "lib/db_file.h" -#include "lib/cmp_filetime.h" -#include "lib/norm_filter.h" -#include "process_callback.h" //for UI_UPDATE_INTERVAL - -using namespace zen; -using namespace std::rel_ops; - - -void zen::swapGrids(const MainConfiguration& config, FolderComparison& folderCmp) -{ - std::for_each(begin(folderCmp), end(folderCmp), [](BaseDirPair& baseObj) { baseObj.flip(); }); - redetermineSyncDirection(config, folderCmp, [](const std::wstring&) {}); -} - -//---------------------------------------------------------------------------------------------- - -namespace -{ -class Redetermine -{ -public: - static void execute(const DirectionSet& dirCfgIn, HierarchyObject& hierObj) { Redetermine(dirCfgIn).recurse(hierObj); } - -private: - Redetermine(const DirectionSet& dirCfgIn) : dirCfg(dirCfgIn) {} - - void recurse(HierarchyObject& hierObj) const - { - for (FilePair& fileObj : hierObj.refSubFiles()) - processFile(fileObj); - for (SymlinkPair& linkObj : hierObj.refSubLinks()) - processLink(linkObj); - for (DirPair& dirObj : hierObj.refSubDirs()) - processDir(dirObj); - } - - void processFile(FilePair& fileObj) const - { - const CompareFilesResult cat = fileObj.getCategory(); - - //##################### schedule old temporary files for deletion #################### - if (cat == FILE_LEFT_SIDE_ONLY && endsWith(fileObj.getShortName(), TEMP_FILE_ENDING)) - return fileObj.setSyncDir(SyncDirection::LEFT); - else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(fileObj.getShortName(), TEMP_FILE_ENDING)) - return fileObj.setSyncDir(SyncDirection::RIGHT); - //#################################################################################### - - switch (cat) - { - case FILE_LEFT_SIDE_ONLY: - fileObj.setSyncDir(dirCfg.exLeftSideOnly); - break; - case FILE_RIGHT_SIDE_ONLY: - fileObj.setSyncDir(dirCfg.exRightSideOnly); - break; - case FILE_RIGHT_NEWER: - fileObj.setSyncDir(dirCfg.rightNewer); - break; - case FILE_LEFT_NEWER: - fileObj.setSyncDir(dirCfg.leftNewer); - break; - case FILE_DIFFERENT: - fileObj.setSyncDir(dirCfg.different); - break; - case FILE_CONFLICT: - case FILE_DIFFERENT_METADATA: //use setting from "conflict/cannot categorize" - if (dirCfg.conflict == SyncDirection::NONE) - fileObj.setSyncDirConflict(fileObj.getCatExtraDescription()); //take over category conflict - else - fileObj.setSyncDir(dirCfg.conflict); - break; - case FILE_EQUAL: - fileObj.setSyncDir(SyncDirection::NONE); - break; - } - } - - void processLink(SymlinkPair& linkObj) const - { - switch (linkObj.getLinkCategory()) - { - case SYMLINK_LEFT_SIDE_ONLY: - linkObj.setSyncDir(dirCfg.exLeftSideOnly); - break; - case SYMLINK_RIGHT_SIDE_ONLY: - linkObj.setSyncDir(dirCfg.exRightSideOnly); - break; - case SYMLINK_LEFT_NEWER: - linkObj.setSyncDir(dirCfg.leftNewer); - break; - case SYMLINK_RIGHT_NEWER: - linkObj.setSyncDir(dirCfg.rightNewer); - break; - case SYMLINK_CONFLICT: - case SYMLINK_DIFFERENT_METADATA: //use setting from "conflict/cannot categorize" - if (dirCfg.conflict == SyncDirection::NONE) - linkObj.setSyncDirConflict(linkObj.getCatExtraDescription()); //take over category conflict - else - linkObj.setSyncDir(dirCfg.conflict); - break; - case SYMLINK_DIFFERENT: - linkObj.setSyncDir(dirCfg.different); - break; - case SYMLINK_EQUAL: - linkObj.setSyncDir(SyncDirection::NONE); - break; - } - } - - void processDir(DirPair& dirObj) const - { - const CompareDirResult cat = dirObj.getDirCategory(); - - //########### schedule abandoned temporary recycle bin directory for deletion ########## - if (cat == DIR_LEFT_SIDE_ONLY && endsWith(dirObj.getShortName(), TEMP_FILE_ENDING)) - return setSyncDirectionRec(SyncDirection::LEFT, dirObj); // - else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(dirObj.getShortName(), TEMP_FILE_ENDING)) - return setSyncDirectionRec(SyncDirection::RIGHT, dirObj); //don't recurse below! - //####################################################################################### - - switch (cat) - { - case DIR_LEFT_SIDE_ONLY: - dirObj.setSyncDir(dirCfg.exLeftSideOnly); - break; - case DIR_RIGHT_SIDE_ONLY: - dirObj.setSyncDir(dirCfg.exRightSideOnly); - break; - case DIR_EQUAL: - dirObj.setSyncDir(SyncDirection::NONE); - break; - case DIR_DIFFERENT_METADATA: //use setting from "conflict/cannot categorize" - if (dirCfg.conflict == SyncDirection::NONE) - dirObj.setSyncDirConflict(dirObj.getCatExtraDescription()); //take over category conflict - else - dirObj.setSyncDir(dirCfg.conflict); - break; - } - - recurse(dirObj); - } - - const DirectionSet dirCfg; -}; - -//--------------------------------------------------------------------------------------------------------------- - -//test if non-equal items exist in scanned data -bool allItemsCategoryEqual(const HierarchyObject& hierObj) -{ - return std::all_of(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), - [](const FilePair& fileObj) { return fileObj.getCategory() == FILE_EQUAL; })&& //files - - std::all_of(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(), - [](const SymlinkPair& linkObj) { return linkObj.getLinkCategory() == SYMLINK_EQUAL; })&& //symlinks - - std::all_of(hierObj.refSubDirs(). begin(), hierObj.refSubDirs().end(), - [](const DirPair& dirObj) - { - return dirObj.getDirCategory() == DIR_EQUAL && allItemsCategoryEqual(dirObj); //short circuit-behavior! - }); //directories -} -} - -bool zen::allElementsEqual(const FolderComparison& folderCmp) -{ - return std::all_of(begin(folderCmp), end(folderCmp), [](const BaseDirPair& baseObj) { return allItemsCategoryEqual(baseObj); }); -} - -//--------------------------------------------------------------------------------------------------------------- - -namespace -{ -template inline -const InSyncDescrFile& getDescriptor(const InSyncFile& dbFile) { return dbFile.left; } - -template <> inline -const InSyncDescrFile& getDescriptor(const InSyncFile& dbFile) { return dbFile.right; } - - -//check whether database entry and current item match: *irrespective* of current comparison settings -template inline -bool isEqual(const FilePair& fileObj, const InSyncDir::FileList::value_type* dbFile) -{ - if (fileObj.isEmpty()) - return !dbFile; - else if (!dbFile) - return false; - - const Zstring& shortNameDb = dbFile->first; - const InSyncDescrFile& descrDb = getDescriptor(dbFile->second); - - return fileObj.getShortName() == shortNameDb && //detect changes in case (windows) - //respect 2 second FAT/FAT32 precision! copying a file to a FAT32 drive changes it's modification date by up to 2 seconds - sameFileTime(fileObj.getLastWriteTime(), descrDb.lastWriteTimeRaw, 2) && - fileObj.getFileSize() == dbFile->second.fileSize; - //note: we do *not* consider FileId here, but are only interested in *visual* changes. Consider user moving data to some other medium, this is not a change! -} - - -//check whether database entry is in sync considering *current* comparison settings -inline -bool stillInSync(const InSyncFile& dbFile, CompareVariant compareVar, size_t fileTimeTolerance) -{ - switch (compareVar) - { - case CMP_BY_TIME_SIZE: - if (dbFile.cmpVar == CMP_BY_CONTENT) return true; //special rule: this is already "good enough" for CMP_BY_TIME_SIZE! - - return //case-sensitive short name match is a database invariant! - CmpFileTime::getResult(dbFile.left.lastWriteTimeRaw, dbFile.right.lastWriteTimeRaw, fileTimeTolerance) == CmpFileTime::TIME_EQUAL; - //dbFile.left.fileSize == dbFile.right.fileSize; - - case CMP_BY_CONTENT: - //case-sensitive short name match is a database invariant! - return dbFile.cmpVar == CMP_BY_CONTENT; - //in contrast to comparison, we don't care about modification time here! - } - assert(false); - return false; -} - -//-------------------------------------------------------------------- - -template inline -const InSyncDescrLink& getDescriptor(const InSyncSymlink& dbLink) { return dbLink.left; } - -template <> inline -const InSyncDescrLink& getDescriptor(const InSyncSymlink& dbLink) { return dbLink.right; } - - -//check whether database entry and current item match: *irrespective* of current comparison settings -template inline -bool isEqual(const SymlinkPair& linkObj, const InSyncDir::LinkList::value_type* dbLink) -{ - if (linkObj.isEmpty()) - return !dbLink; - else if (!dbLink) - return false; - - const Zstring& shortNameDb = dbLink->first; - const InSyncDescrLink& descrDb = getDescriptor(dbLink->second); - - return linkObj.getShortName() == shortNameDb && - //respect 2 second FAT/FAT32 precision! copying a file to a FAT32 drive changes its modification date by up to 2 seconds - sameFileTime(linkObj.getLastWriteTime(), descrDb.lastWriteTimeRaw, 2); -} - - -//check whether database entry is in sync considering *current* comparison settings -inline -bool stillInSync(const InSyncSymlink& dbLink, CompareVariant compareVar, size_t fileTimeTolerance) -{ - switch (compareVar) - { - case CMP_BY_TIME_SIZE: - if (dbLink.cmpVar == CMP_BY_CONTENT) return true; //special rule: this is already "good enough" for CMP_BY_TIME_SIZE! - - return //case-sensitive short name match is a database invariant! - CmpFileTime::getResult(dbLink.left.lastWriteTimeRaw, dbLink.right.lastWriteTimeRaw, fileTimeTolerance) == CmpFileTime::TIME_EQUAL; - - case CMP_BY_CONTENT: - //case-sensitive short name match is a database invariant! - return dbLink.cmpVar == CMP_BY_CONTENT; - //in contrast to comparison, we don't care about modification time here! - } - assert(false); - return false; -} - -//-------------------------------------------------------------------- - -//check whether database entry and current item match: *irrespective* of current comparison settings -template inline -bool isEqual(const DirPair& dirObj, const InSyncDir::DirList::value_type* dbDir) -{ - if (dirObj.isEmpty()) - return !dbDir || dbDir->second.status == InSyncDir::DIR_STATUS_STRAW_MAN; - else if (!dbDir || dbDir->second.status == InSyncDir::DIR_STATUS_STRAW_MAN) - return false; - - const Zstring& shortNameDb = dbDir->first; - - return dirObj.getShortName() == shortNameDb; -} - - -inline -bool stillInSync(const InSyncDir& dbDir) -{ - //case-sensitive short name match is a database invariant! - //InSyncDir::DIR_STATUS_STRAW_MAN considered - return true; -} - -//---------------------------------------------------------------------------------------------- - -class DetectMovedFiles -{ -public: - static void execute(BaseDirPair& baseDirectory, const InSyncDir& dbContainer) { DetectMovedFiles(baseDirectory, dbContainer); } - -private: - DetectMovedFiles(BaseDirPair& baseDirectory, const InSyncDir& dbContainer) : - cmpVar(baseDirectory.getCompVariant()), - fileTimeTolerance(baseDirectory.getFileTimeTolerance()) - { - recurse(baseDirectory); - - if (!exLeftOnly.empty() && !exRightOnly.empty()) - detectFilePairs(dbContainer); - } - - void recurse(HierarchyObject& hierObj) - { - for (FilePair& fileObj : hierObj.refSubFiles()) - { - const CompareFilesResult cat = fileObj.getCategory(); - - if (cat == FILE_LEFT_SIDE_ONLY) - { - if (fileObj.getFileId() != FileId()) - { - auto rv = exLeftOnly.insert(std::make_pair(fileObj.getFileId(), &fileObj)); - assert(rv.second); - if (!rv.second) //duplicate file ID! - rv.first->second = nullptr; - } - } - else if (cat == FILE_RIGHT_SIDE_ONLY) - { - if (fileObj.getFileId() != FileId()) - { - auto rv = exRightOnly.insert(std::make_pair(fileObj.getFileId(), &fileObj)); - assert(rv.second); - if (!rv.second) //duplicate file ID! - rv.first->second = nullptr; - } - } - } - for (DirPair& dirObj : hierObj.refSubDirs()) - recurse(dirObj); - } - - void detectFilePairs(const InSyncDir& container) const - { - for (auto& dbFile : container.files) - findAndSetMovePair(dbFile.second); - - for (auto& dbDir : container.dirs) - detectFilePairs(dbDir.second); - } - - static bool sameSizeAndDateLeft(const FilePair& fsObj, const InSyncFile& dbEntry) - { - return fsObj.getFileSize() == dbEntry.fileSize && - sameFileTime(fsObj.getLastWriteTime(), dbEntry.left.lastWriteTimeRaw, 2); //respect 2 second FAT/FAT32 precision! - //PS: *never* allow 2 sec tolerance as container predicate!! - // => no strict weak ordering relation! reason: no transitivity of equivalence! - } - static bool sameSizeAndDateRight(const FilePair& fsObj, const InSyncFile& dbEntry) - { - return fsObj.getFileSize() == dbEntry.fileSize && - sameFileTime(fsObj.getLastWriteTime(), dbEntry.right.lastWriteTimeRaw, 2); - } - - void findAndSetMovePair(const InSyncFile& dbEntry) const - { - const FileId idLeft = dbEntry.left .fileId; - const FileId idRight = dbEntry.right.fileId; - - if (idLeft != FileId() && - idRight != FileId() && - stillInSync(dbEntry, cmpVar, fileTimeTolerance)) - { - auto itL = exLeftOnly.find(idLeft); - if (itL != exLeftOnly.end()) - if (FilePair* fileLeftOnly = itL->second) //= nullptr, if duplicate ID! - if (sameSizeAndDateLeft(*fileLeftOnly, dbEntry)) - { - auto itR = exRightOnly.find(idRight); - if (itR != exRightOnly.end()) - if (FilePair* fileRightOnly = itR->second) //= nullptr, if duplicate ID! - if (sameSizeAndDateRight(*fileRightOnly, dbEntry)) - if (fileLeftOnly ->getMoveRef() == nullptr && //the db may contain duplicate file ids on left or right side: e.g. consider aliasing through symlinks - fileRightOnly->getMoveRef() == nullptr) //=> should not be a problem (same id, size, date => alias!) but don't let a row participate in two move pairs! - { - fileLeftOnly ->setMoveRef(fileRightOnly->getId()); //found a pair, mark it! - fileRightOnly->setMoveRef(fileLeftOnly ->getId()); // - } - } - } - } - - const CompareVariant cmpVar; - const size_t fileTimeTolerance; - - std::map exLeftOnly; //FilePair* == nullptr for duplicate ids! => consider aliasing through symlinks! - std::map exRightOnly; //=> avoid ambiguity for mixtures of files/symlinks on one side and allow 1-1 mapping only! - - /* - detect renamed files: - - X -> |_| Create right - |_| -> Y Delete right - - is detected as: - - Rename Y to X on right - - Algorithm: - ---------- - DB-file left <--- (name, size, date) ---> DB-file right - | | - | (file ID, size, date) | (file ID, size, date) - \|/ \|/ - file left only file right only - - FAT caveat: File Ids are generally not stable when file is either moved or renamed! - => 1. Move/rename operations on FAT cannot be detected reliably. - => 2. database generally contains wrong file ID on FAT after renaming from .ffs_tmp files => correct file Ids in database only after next sync - => 3. even exFAT screws up (but less than FAT) and changes IDs after file move. Did they learn nothing from the past? - - Possible refinement - ------------------- - If the file ID is wrong (FAT) or not available, we could at least allow direct association by name, instead of breaking the chain completely: support NTFS -> FAT - */ -}; - -//---------------------------------------------------------------------------------------------- - -class RedetermineTwoWay -{ -public: - static void execute(BaseDirPair& baseDirectory, const InSyncDir& dbContainer) { RedetermineTwoWay(baseDirectory, dbContainer); } - -private: - RedetermineTwoWay(BaseDirPair& baseDirectory, const InSyncDir& dbContainer) : - txtBothSidesChanged(_("Both sides have changed since last synchronization.")), - txtNoSideChanged(_("Cannot determine sync-direction:") + L" \n" + _("No change since last synchronization.")), - txtDbNotInSync(_("Cannot determine sync-direction:") + L" \n" + _("The database entry is not in sync considering current settings.")), - cmpVar(baseDirectory.getCompVariant()), - fileTimeTolerance(baseDirectory.getFileTimeTolerance()) - { - //-> considering filter not relevant: - //if narrowing filter: all ok; if widening filter (if file ex on both sides -> conflict, fine; if file ex. on one side: copy to other side: fine) - - recurse(baseDirectory, &dbContainer); - } - - void recurse(HierarchyObject& hierObj, const InSyncDir* dbContainer) const - { - for (FilePair& fileObj : hierObj.refSubFiles()) - processFile(fileObj, dbContainer); - for (SymlinkPair& linkObj : hierObj.refSubLinks()) - processSymlink(linkObj, dbContainer); - for (DirPair& dirObj : hierObj.refSubDirs()) - processDir(dirObj, dbContainer); - } - - void processFile(FilePair& fileObj, const InSyncDir* dbContainer) const - { - const CompareFilesResult cat = fileObj.getCategory(); - if (cat == FILE_EQUAL) - return; - - //##################### schedule old temporary files for deletion #################### - if (cat == FILE_LEFT_SIDE_ONLY && endsWith(fileObj.getShortName(), TEMP_FILE_ENDING)) - return fileObj.setSyncDir(SyncDirection::LEFT); - else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(fileObj.getShortName(), TEMP_FILE_ENDING)) - return fileObj.setSyncDir(SyncDirection::RIGHT); - //#################################################################################### - - //try to find corresponding database entry - const InSyncDir::FileList::value_type* dbEntry = nullptr; - if (dbContainer) - { - auto it = dbContainer->files.find(fileObj.getObjShortName()); - if (it != dbContainer->files.end()) - dbEntry = &*it; - } - - //evaluation - const bool changeOnLeft = !isEqual(fileObj, dbEntry); - const bool changeOnRight = !isEqual(fileObj, dbEntry); - - if (changeOnLeft != changeOnRight) - { - //if database entry not in sync according to current settings! -> do not set direction based on async status! - if (dbEntry && !stillInSync(dbEntry->second, cmpVar, fileTimeTolerance)) - fileObj.setSyncDirConflict(txtDbNotInSync); - else - fileObj.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT); - } - else - { - if (changeOnLeft) - fileObj.setSyncDirConflict(txtBothSidesChanged); - else - fileObj.setSyncDirConflict(txtNoSideChanged); - } - } - - void processSymlink(SymlinkPair& linkObj, const InSyncDir* dbContainer) const - { - const CompareSymlinkResult cat = linkObj.getLinkCategory(); - if (cat == SYMLINK_EQUAL) - return; - - //try to find corresponding database entry - const InSyncDir::LinkList::value_type* dbEntry = nullptr; - if (dbContainer) - { - auto it = dbContainer->symlinks.find(linkObj.getObjShortName()); - if (it != dbContainer->symlinks.end()) - dbEntry = &*it; - } - - //evaluation - const bool changeOnLeft = !isEqual(linkObj, dbEntry); - const bool changeOnRight = !isEqual(linkObj, dbEntry); - - if (changeOnLeft != changeOnRight) - { - //if database entry not in sync according to current settings! -> do not set direction based on async status! - if (dbEntry && !stillInSync(dbEntry->second, cmpVar, fileTimeTolerance)) - linkObj.setSyncDirConflict(txtDbNotInSync); - else - linkObj.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT); - } - else - { - if (changeOnLeft) - linkObj.setSyncDirConflict(txtBothSidesChanged); - else - linkObj.setSyncDirConflict(txtNoSideChanged); - } - } - - void processDir(DirPair& dirObj, const InSyncDir* dbContainer) const - { - const CompareDirResult cat = dirObj.getDirCategory(); - - //########### schedule abandoned temporary recycle bin directory for deletion ########## - if (cat == DIR_LEFT_SIDE_ONLY && endsWith(dirObj.getShortName(), TEMP_FILE_ENDING)) - return setSyncDirectionRec(SyncDirection::LEFT, dirObj); // - else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(dirObj.getShortName(), TEMP_FILE_ENDING)) - return setSyncDirectionRec(SyncDirection::RIGHT, dirObj); //don't recurse below! - //####################################################################################### - - //try to find corresponding database entry - const InSyncDir::DirList::value_type* dbEntry = nullptr; - if (dbContainer) - { - auto it = dbContainer->dirs.find(dirObj.getObjShortName()); - if (it != dbContainer->dirs.end()) - dbEntry = &*it; - } - - if (cat != DIR_EQUAL) - { - //evaluation - const bool changeOnLeft = !isEqual(dirObj, dbEntry); - const bool changeOnRight = !isEqual(dirObj, dbEntry); - - if (changeOnLeft != changeOnRight) - { - //if database entry not in sync according to current settings! -> do not set direction based on async status! - if (dbEntry && !stillInSync(dbEntry->second)) - dirObj.setSyncDirConflict(txtDbNotInSync); - else - dirObj.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT); - } - else - { - if (changeOnLeft) - dirObj.setSyncDirConflict(txtBothSidesChanged); - else - dirObj.setSyncDirConflict(txtNoSideChanged); - } - } - - recurse(dirObj, dbEntry ? &dbEntry->second : nullptr); - } - - const std::wstring txtBothSidesChanged; - const std::wstring txtNoSideChanged; - const std::wstring txtDbNotInSync; - - const CompareVariant cmpVar; - const size_t fileTimeTolerance; -}; -} - -//--------------------------------------------------------------------------------------------------------------- - -std::vector zen::extractDirectionCfg(const MainConfiguration& mainCfg) -{ - //merge first and additional pairs - std::vector allPairs; - allPairs.push_back(mainCfg.firstPair); - allPairs.insert(allPairs.end(), - mainCfg.additionalPairs.begin(), //add additional pairs - mainCfg.additionalPairs.end()); - - std::vector output; - std::for_each(allPairs.begin(), allPairs.end(), - [&](const FolderPairEnh& fp) - { - output.push_back(fp.altSyncConfig.get() ? fp.altSyncConfig->directionCfg : mainCfg.syncCfg.directionCfg); - }); - - return output; -} - - -void zen::redetermineSyncDirection(const DirectionConfig& dirCfg, BaseDirPair& baseDirectory, std::function reportWarning) -{ - //try to load sync-database files - std::shared_ptr lastSyncState; - if (dirCfg.var == DirectionConfig::TWOWAY || detectMovedFilesEnabled(dirCfg)) - try - { - if (allItemsCategoryEqual(baseDirectory)) - return; //nothing to do: abort and don't even try to open db files - - lastSyncState = loadLastSynchronousState(baseDirectory); //throw FileError, FileErrorDatabaseNotExisting - } - catch (FileErrorDatabaseNotExisting&) {} //let's ignore this error, there's no value in reporting it other than confuse users - catch (FileError& error) //e.g. incompatible database version - { - reportWarning(error.toString() + - (dirCfg.var == DirectionConfig::TWOWAY ? - L" \n\n" + _("Setting default synchronization directions: Old files will be overwritten with newer files.") : std::wstring())); - } - - //set sync directions - if (dirCfg.var == DirectionConfig::TWOWAY) - { - if (lastSyncState) - RedetermineTwoWay::execute(baseDirectory, *lastSyncState); - else //default fallback - Redetermine::execute(getTwoWayUpdateSet(), baseDirectory); - } - else - Redetermine::execute(extractDirections(dirCfg), baseDirectory); - - //detect renamed files - if (lastSyncState) - DetectMovedFiles::execute(baseDirectory, *lastSyncState); -} - - -void zen::redetermineSyncDirection(const MainConfiguration& mainCfg, FolderComparison& folderCmp, std::function reportWarning) -{ - if (folderCmp.empty()) - return; - - std::vector directCfgs = extractDirectionCfg(mainCfg); - - if (folderCmp.size() != directCfgs.size()) - throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo(__LINE__)); - - for (auto it = folderCmp.begin(); it != folderCmp.end(); ++it) - { - const DirectionConfig& cfg = directCfgs[it - folderCmp.begin()]; - redetermineSyncDirection(cfg, **it, reportWarning); - } -} - -//--------------------------------------------------------------------------------------------------------------- - -struct SetNewDirection -{ - static void execute(FilePair& fileObj, SyncDirection newDirection) - { - if (fileObj.getCategory() != FILE_EQUAL) - fileObj.setSyncDir(newDirection); - } - - static void execute(SymlinkPair& linkObj, SyncDirection newDirection) - { - if (linkObj.getLinkCategory() != SYMLINK_EQUAL) - linkObj.setSyncDir(newDirection); - } - - static void execute(DirPair& dirObj, SyncDirection newDirection) - { - if (dirObj.getDirCategory() != DIR_EQUAL) - dirObj.setSyncDir(newDirection); - - //recurse: - for (FilePair& fileObj : dirObj.refSubFiles()) - execute(fileObj, newDirection); - for (SymlinkPair& linkObj : dirObj.refSubLinks()) - execute(linkObj, newDirection); - for (DirPair& dirObj2 : dirObj.refSubDirs()) - execute(dirObj2, newDirection); - } -}; - - -void zen::setSyncDirectionRec(SyncDirection newDirection, FileSystemObject& fsObj) -{ - //process subdirectories also! - struct Recurse: public FSObjectVisitor - { - Recurse(SyncDirection newDir) : newDir_(newDir) {} - virtual void visit(const FilePair& fileObj) - { - SetNewDirection::execute(const_cast(fileObj), newDir_); //phyiscal object is not const in this method anyway - } - virtual void visit(const SymlinkPair& linkObj) - { - SetNewDirection::execute(const_cast(linkObj), newDir_); // - } - virtual void visit(const DirPair& dirObj) - { - SetNewDirection::execute(const_cast(dirObj), newDir_); // - } - private: - SyncDirection newDir_; - } setDirVisitor(newDirection); - fsObj.accept(setDirVisitor); -} - -//--------------- functions related to filtering ------------------------------------------------------------------------------------ - -namespace -{ -template -void inOrExcludeAllRows(zen::HierarchyObject& hierObj) -{ - for (FilePair& fileObj : hierObj.refSubFiles()) - fileObj.setActive(include); - for (SymlinkPair& linkObj : hierObj.refSubLinks()) - linkObj.setActive(include); - for (DirPair& dirObj : hierObj.refSubDirs()) - { - dirObj.setActive(include); - inOrExcludeAllRows(dirObj); //recurse - } -} -} - - -void zen::setActiveStatus(bool newStatus, zen::FolderComparison& folderCmp) -{ - if (newStatus) - std::for_each(begin(folderCmp), end(folderCmp), [](BaseDirPair& baseDirObj) { inOrExcludeAllRows(baseDirObj); }); //include all rows - else - std::for_each(begin(folderCmp), end(folderCmp), [](BaseDirPair& baseDirObj) { inOrExcludeAllRows(baseDirObj); }); //exclude all rows -} - - -void zen::setActiveStatus(bool newStatus, zen::FileSystemObject& fsObj) -{ - fsObj.setActive(newStatus); - - //process subdirectories also! - struct Recurse: public FSObjectVisitor - { - Recurse(bool newStat) : newStatus_(newStat) {} - virtual void visit(const FilePair& fileObj) {} - virtual void visit(const SymlinkPair& linkObj) {} - virtual void visit(const DirPair& dirObj) - { - if (newStatus_) - inOrExcludeAllRows(const_cast(dirObj)); //object is not physically const here anyway - else - inOrExcludeAllRows(const_cast(dirObj)); // - } - private: - const bool newStatus_; - } recurse(newStatus); - fsObj.accept(recurse); -} - -namespace -{ -enum FilterStrategy -{ - STRATEGY_SET, - STRATEGY_AND, - STRATEGY_OR -}; - -template struct Eval; - -template <> -struct Eval //process all elements -{ - template - bool process(const T& obj) const { return true; } -}; - -template <> -struct Eval -{ - template - bool process(const T& obj) const { return obj.isActive(); } -}; - -template <> -struct Eval -{ - template - bool process(const T& obj) const { return !obj.isActive(); } -}; - - -template -class ApplyHardFilter -{ -public: - static void execute(HierarchyObject& hierObj, const HardFilter& filterProcIn) { ApplyHardFilter(hierObj, filterProcIn); } - -private: - ApplyHardFilter(HierarchyObject& hierObj, const HardFilter& filterProcIn) : filterProc(filterProcIn) { recurse(hierObj); } - - void recurse(HierarchyObject& hierObj) const - { - for (FilePair& fileObj : hierObj.refSubFiles()) - processFile(fileObj); - for (SymlinkPair& linkObj : hierObj.refSubLinks()) - processLink(linkObj); - for (DirPair& dirObj : hierObj.refSubDirs()) - processDir(dirObj); - }; - - void processFile(FilePair& fileObj) const - { - if (Eval().process(fileObj)) - fileObj.setActive(filterProc.passFileFilter(fileObj.getObjRelativeName())); - } - - void processLink(SymlinkPair& linkObj) const - { - if (Eval().process(linkObj)) - linkObj.setActive(filterProc.passFileFilter(linkObj.getObjRelativeName())); - } - - void processDir(DirPair& dirObj) const - { - bool subObjMightMatch = true; - const bool filterPassed = filterProc.passDirFilter(dirObj.getObjRelativeName(), &subObjMightMatch); - - if (Eval().process(dirObj)) - dirObj.setActive(filterPassed); - - if (!subObjMightMatch) //use same logic like directory traversing here: evaluate filter in subdirs only if objects could match - { - inOrExcludeAllRows(dirObj); //exclude all files dirs in subfolders - return; - } - - recurse(dirObj); - } - - const HardFilter& filterProc; -}; - -template <> -class ApplyHardFilter; //usage of InOrExcludeAllRows doesn't allow for strategy "or" - - -template -class ApplySoftFilter //falsify only! -> can run directly after "hard/base filter" -{ -public: - static void execute(HierarchyObject& hierObj, const SoftFilter& timeSizeFilter) { ApplySoftFilter(hierObj, timeSizeFilter); } - -private: - ApplySoftFilter(HierarchyObject& hierObj, const SoftFilter& timeSizeFilter) : timeSizeFilter_(timeSizeFilter) { recurse(hierObj); } - - void recurse(zen::HierarchyObject& hierObj) const - { - for (FilePair& fileObj : hierObj.refSubFiles()) - processFile(fileObj); - for (SymlinkPair& linkObj : hierObj.refSubLinks()) - processLink(linkObj); - for (DirPair& dirObj : hierObj.refSubDirs()) - processDir(dirObj); - }; - - void processFile(FilePair& fileObj) const - { - if (Eval().process(fileObj)) - { - if (fileObj.isEmpty()) - fileObj.setActive(matchSize(fileObj) && - matchTime(fileObj)); - else if (fileObj.isEmpty()) - fileObj.setActive(matchSize(fileObj) && - matchTime(fileObj)); - else - { - //the only case with partially unclear semantics: - //file and time filters may match or not match on each side, leaving a total of 16 combinations for both sides! - /* - ST S T - ST := match size and time - --------- S := match size only - ST |X|X|X|X| T := match time only - ------------ - := no match - S |X|O|?|O| - ------------ X := include row - T |X|?|O|O| O := exclude row - ------------ ? := unclear - - |X|O|O|O| - ------------ - */ - //let's set ? := O - fileObj.setActive((matchSize(fileObj) && - matchTime(fileObj)) || - (matchSize(fileObj) && - matchTime(fileObj))); - } - } - } - - void processLink(SymlinkPair& linkObj) const - { - if (Eval().process(linkObj)) - { - if (linkObj.isEmpty()) - linkObj.setActive(matchTime(linkObj)); - else if (linkObj.isEmpty()) - linkObj.setActive(matchTime(linkObj)); - else - linkObj.setActive(matchTime(linkObj) || - matchTime (linkObj)); - } - } - - void processDir(DirPair& dirObj) const - { - if (Eval().process(dirObj)) - dirObj.setActive(timeSizeFilter_.matchFolder()); //if date filter is active we deactivate all folders: effectively gets rid of empty folders! - - recurse(dirObj); - } - - template - bool matchTime(const T& obj) const - { - return timeSizeFilter_.matchTime(obj.template getLastWriteTime()); - } - - template - bool matchSize(const T& obj) const - { - return timeSizeFilter_.matchSize(obj.template getFileSize()); - } - - const SoftFilter timeSizeFilter_; -}; -} - - -void zen::addHardFiltering(BaseDirPair& baseDirObj, const Zstring& excludeFilter) -{ - ApplyHardFilter::execute(baseDirObj, NameFilter(FilterConfig().includeFilter, excludeFilter)); -} - - -void zen::addSoftFiltering(BaseDirPair& baseDirObj, const SoftFilter& timeSizeFilter) -{ - if (!timeSizeFilter.isNull()) //since we use STRATEGY_AND, we may skip a "null" filter - ApplySoftFilter::execute(baseDirObj, timeSizeFilter); -} - - -void zen::applyFiltering(FolderComparison& folderCmp, const MainConfiguration& mainCfg) -{ - if (folderCmp.empty()) - return; - else if (folderCmp.size() != mainCfg.additionalPairs.size() + 1) - throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo(__LINE__)); - - //merge first and additional pairs - std::vector allPairs; - allPairs.push_back(mainCfg.firstPair); - allPairs.insert(allPairs.end(), - mainCfg.additionalPairs.begin(), //add additional pairs - mainCfg.additionalPairs.end()); - - for (auto it = allPairs.begin(); it != allPairs.end(); ++it) - { - BaseDirPair& baseDirectory = *folderCmp[it - allPairs.begin()]; - - const NormalizedFilter normFilter = normalizeFilters(mainCfg.globalFilter, it->localFilter); - - //"set" hard filter - ApplyHardFilter::execute(baseDirectory, *normFilter.nameFilter); - - //"and" soft filter - addSoftFiltering(baseDirectory, normFilter.timeSizeFilter); - } -} - - -class FilterByTimeSpan -{ -public: - static void execute(HierarchyObject& hierObj, const Int64& timeFrom, const Int64& timeTo) { FilterByTimeSpan(hierObj, timeFrom, timeTo); } - -private: - FilterByTimeSpan(HierarchyObject& hierObj, - const Int64& timeFrom, - const Int64& timeTo) : - timeFrom_(timeFrom), - timeTo_(timeTo) { recurse(hierObj); } - - void recurse(HierarchyObject& hierObj) const - { - for (FilePair& fileObj : hierObj.refSubFiles()) - processFile(fileObj); - for (SymlinkPair& linkObj : hierObj.refSubLinks()) - processLink(linkObj); - for (DirPair& dirObj : hierObj.refSubDirs()) - processDir(dirObj); - }; - - void processFile(FilePair& fileObj) const - { - if (fileObj.isEmpty()) - fileObj.setActive(matchTime(fileObj)); - else if (fileObj.isEmpty()) - fileObj.setActive(matchTime(fileObj)); - else - fileObj.setActive(matchTime(fileObj) || - matchTime(fileObj)); - } - - void processLink(SymlinkPair& linkObj) const - { - if (linkObj.isEmpty()) - linkObj.setActive(matchTime(linkObj)); - else if (linkObj.isEmpty()) - linkObj.setActive(matchTime(linkObj)); - else - linkObj.setActive(matchTime(linkObj) || - matchTime (linkObj)); - } - - void processDir(DirPair& dirObj) const - { - dirObj.setActive(false); - recurse(dirObj); - } - - template - bool matchTime(const T& obj) const - { - return timeFrom_ <= obj.template getLastWriteTime() && - obj.template getLastWriteTime() <= timeTo_; - } - - const Int64 timeFrom_; - const Int64 timeTo_; -}; - - -void zen::applyTimeSpanFilter(FolderComparison& folderCmp, const Int64& timeFrom, const Int64& timeTo) -{ - std::for_each(begin(folderCmp), end(folderCmp), [&](BaseDirPair& baseDirObj) { FilterByTimeSpan::execute(baseDirObj, timeFrom, timeTo); }); -} - - -//############################################################################################################ -std::pair zen::deleteFromGridAndHDPreview(const std::vector& selectionLeft, - const std::vector& selectionRight, - bool deleteOnBothSides) -{ - //don't use wxString here, it's linear allocation strategy would bring perf down to a crawl; Zstring: exponential growth! - Zstring fileList; - int totalDelCount = 0; - - if (deleteOnBothSides) - { - //mix selected rows from left and right (without changing order) - std::vector selection; - { - hash_set objectsUsed; - std::copy_if(selectionLeft .begin(), selectionLeft .end(), std::back_inserter(selection), [&](FileSystemObject* fsObj) { return objectsUsed.insert(fsObj).second; }); - std::copy_if(selectionRight.begin(), selectionRight.end(), std::back_inserter(selection), [&](FileSystemObject* fsObj) { return objectsUsed.insert(fsObj).second; }); - } - - std::for_each(selection.begin(), selection.end(), - [&](const FileSystemObject* fsObj) - { - if (!fsObj->isEmpty()) - { - fileList += fsObj->getFullName() + Zstr('\n'); - ++totalDelCount; - } - - if (!fsObj->isEmpty()) - { - fileList += fsObj->getFullName() + Zstr('\n'); - ++totalDelCount; - } - - fileList += Zstr('\n'); - }); - } - else //delete selected files only - { - std::for_each(selectionLeft.begin(), selectionLeft.end(), - [&](const FileSystemObject* fsObj) - { - if (!fsObj->isEmpty()) - { - fileList += fsObj->getFullName() + Zstr('\n'); - ++totalDelCount; - } - }); - - std::for_each(selectionRight.begin(), selectionRight.end(), - [&](const FileSystemObject* fsObj) - { - if (!fsObj->isEmpty()) - { - fileList += fsObj->getFullName() + Zstr('\n'); - ++totalDelCount; - } - }); - } - - return std::make_pair(fileList, totalDelCount); -} - - -namespace -{ -template inline -bool tryReportingError(Function cmd, DeleteFilesHandler& handler) //return "true" on success, "false" if error was ignored -{ - for (;;) - try - { - cmd(); //throw FileError - return true; - } - catch (FileError& error) - { - switch (handler.reportError(error.toString())) //may throw! - { - case DeleteFilesHandler::IGNORE_ERROR: - return false; - case DeleteFilesHandler::RETRY: - break; //continue with loop - default: - assert(false); - break; - } - } -} - -#ifdef ZEN_WIN -//recycleBinStatus() blocks seriously if recycle bin is really full and drive is slow -StatusRecycler recycleBinStatusUpdating(const Zstring& dirname, DeleteFilesHandler& callback) -{ - const std::wstring msg = replaceCpy(_("Checking recycle bin availability for folder %x..."), L"%x", fmtFileName(dirname), false); - - auto ft = async([=] { return recycleBinStatus(dirname); }); - while (!ft.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL / 2))) - callback.reportStatus(msg); //may throw! - return ft.get(); -} -#endif - - -template -void categorize(const std::set& rowsIn, - std::vector& deletePermanent, - std::vector& deleteRecyler, - bool useRecycleBin, - std::map& hasRecyclerBuffer, - DeleteFilesHandler& callback) -{ - auto hasRecycler = [&](const FileSystemObject& fsObj) -> bool - { -#ifdef ZEN_WIN - const Zstring& baseDirPf = fsObj.root().getBaseDirPf(); - - auto it = hasRecyclerBuffer.find(baseDirPf); - if (it != hasRecyclerBuffer.end()) - return it->second; - return hasRecyclerBuffer.insert(std::make_pair(baseDirPf, recycleBinStatusUpdating(baseDirPf, callback) == STATUS_REC_EXISTS)).first->second; -#elif defined ZEN_LINUX || defined ZEN_MAC - return true; -#endif - }; - - for (auto it = rowsIn.begin(); it != rowsIn.end(); ++it) - if (!(*it)->isEmpty()) - { - if (useRecycleBin && hasRecycler(**it)) //Windows' ::SHFileOperation() will delete permanently anyway, but we have a superior deletion routine - deleteRecyler.push_back(*it); - else - deletePermanent.push_back(*it); - } -} - - -template -struct ItemDeleter : public FSObjectVisitor //throw FileError, but nothrow constructor!!! -{ - ItemDeleter(bool useRecycleBin, DeleteFilesHandler& handler) : - handler_(handler), useRecycleBin_(useRecycleBin), remCallback(*this) - { - if (useRecycleBin_) - { - txtRemovingFile = _("Moving file %x to the recycle bin" ); - txtRemovingDirectory = _("Moving folder %x to the recycle bin" ); - txtRemovingSymlink = _("Moving symbolic link %x to the recycle bin"); - } - else - { - txtRemovingFile = _("Deleting file %x" ); - txtRemovingDirectory = _("Deleting folder %x" ); - txtRemovingSymlink = _("Deleting symbolic link %x"); - } - } - - virtual void visit(const FilePair& fileObj) - { - notifyFileDeletion(fileObj.getFullName()); - - if (useRecycleBin_) - zen::recycleOrDelete(fileObj.getFullName()); //throw FileError - else - zen::removeFile(fileObj.getFullName()); //throw FileError - } - - virtual void visit(const SymlinkPair& linkObj) - { - notifySymlinkDeletion(linkObj.getFullName()); - - if (useRecycleBin_) - zen::recycleOrDelete(linkObj.getFullName()); //throw FileError - else - { - if (dirExists(linkObj.getFullName())) //dir symlink - zen::removeDirectory(linkObj.getFullName()); //throw FileError - else //file symlink, broken symlink - zen::removeFile(linkObj.getFullName()); //throw FileError - } - } - - virtual void visit(const DirPair& dirObj) - { - notifyDirectoryDeletion(dirObj.getFullName()); //notfied twice! see RemoveCallbackImpl -> no big deal - - if (useRecycleBin_) - zen::recycleOrDelete(dirObj.getFullName()); //throw FileError - else - zen::removeDirectory(dirObj.getFullName(), &remCallback); //throw FileError - } - -private: - struct RemoveCallbackImpl : public zen::CallbackRemoveDir - { - RemoveCallbackImpl(ItemDeleter& itemDeleter) : itemDeleter_(itemDeleter) {} - - virtual void onBeforeFileDeletion(const Zstring& filename) { itemDeleter_.notifyFileDeletion (filename); } - virtual void onBeforeDirDeletion (const Zstring& dirname) { itemDeleter_.notifyDirectoryDeletion(dirname ); } - - private: - ItemDeleter& itemDeleter_; - }; - - void notifyFileDeletion (const Zstring& objName) { notifyItemDeletion(txtRemovingFile , objName); } - void notifyDirectoryDeletion(const Zstring& objName) { notifyItemDeletion(txtRemovingDirectory, objName); } - void notifySymlinkDeletion (const Zstring& objName) { notifyItemDeletion(txtRemovingSymlink , objName); } - - void notifyItemDeletion(const std::wstring& statusText, const Zstring& objName) - { - handler_.reportStatus(replaceCpy(statusText, L"%x", fmtFileName(objName))); - } - - DeleteFilesHandler& handler_; - const bool useRecycleBin_; - RemoveCallbackImpl remCallback; - - std::wstring txtRemovingFile; - std::wstring txtRemovingDirectory; - std::wstring txtRemovingSymlink; -}; - - -template -void deleteFromGridAndHDOneSide(std::vector& ptrList, - bool useRecycleBin, - DeleteFilesHandler& handler) -{ - ItemDeleter deleter(useRecycleBin, handler); - - for (auto it = ptrList.begin(); it != ptrList.end(); ++it) //VS 2010 bug prevents replacing this by std::for_each + lamba - { - FileSystemObject& fsObj = **it; //all pointers are required(!) to be bound - if (!fsObj.isEmpty()) //element may be implicitly deleted, e.g. if parent folder was deleted first - tryReportingError([&] - { - fsObj.accept(deleter); //throw FileError - fsObj.removeObject(); //if directory: removes recursively! - }, handler); - } -} -} - -void zen::deleteFromGridAndHD(const std::vector& rowsToDeleteOnLeft, //refresh GUI grid after deletion to remove invalid rows - const std::vector& rowsToDeleteOnRight, //all pointers need to be bound! - FolderComparison& folderCmp, //attention: rows will be physically deleted! - const std::vector& directCfgs, - bool deleteOnBothSides, - bool useRecycleBin, - DeleteFilesHandler& statusHandler, - bool& warningRecyclerMissing) -{ - if (folderCmp.empty()) - return; - else if (folderCmp.size() != directCfgs.size()) - throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo(__LINE__)); - - //build up mapping from base directory to corresponding direction config - hash_map baseDirCfgs; - for (auto it = folderCmp.begin(); it != folderCmp.end(); ++it) - baseDirCfgs[&** it] = directCfgs[it - folderCmp.begin()]; - - std::set deleteLeft (rowsToDeleteOnLeft .begin(), rowsToDeleteOnLeft .end()); - std::set deleteRight(rowsToDeleteOnRight.begin(), rowsToDeleteOnRight.end()); - if (deleteOnBothSides) - { - deleteLeft.insert(deleteRight.begin(), deleteRight.end()); - deleteRight = deleteLeft; - } - - set_remove_if(deleteLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty(); }); //still needed? - set_remove_if(deleteRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty(); }); // - - //ensure cleanup: redetermination of sync-directions and removal of invalid rows - auto updateDirection = [&]() - { - //update sync direction: we cannot do a full redetermination since the user may already have entered manual changes - std::set deletedTotal = deleteLeft; - deletedTotal.insert(deleteRight.begin(), deleteRight.end()); - - for (auto it = deletedTotal.begin(); it != deletedTotal.end(); ++it) - { - FileSystemObject& fsObj = **it; //all pointers are required(!) to be bound - - if (fsObj.isEmpty() != fsObj.isEmpty()) //make sure objects exists on one side only - { - auto cfgIter = baseDirCfgs.find(&fsObj.root()); - if (cfgIter != baseDirCfgs.end()) - { - SyncDirection newDir = SyncDirection::NONE; - - if (cfgIter->second.var == DirectionConfig::TWOWAY) - newDir = fsObj.isEmpty() ? SyncDirection::RIGHT : SyncDirection::LEFT; - else - { - const DirectionSet& dirCfg = extractDirections(cfgIter->second); - newDir = fsObj.isEmpty() ? dirCfg.exRightSideOnly : dirCfg.exLeftSideOnly; - } - - setSyncDirectionRec(newDir, fsObj); //set new direction (recursively) - } - else - assert(!"this should not happen!"); - } - } - - //last step: cleanup empty rows: this one invalidates all pointers! - std::for_each(begin(folderCmp), end(folderCmp), BaseDirPair::removeEmpty); - }; - ZEN_ON_SCOPE_EXIT(updateDirection()); //MSVC: assert is a macro and it doesn't play nice with ZEN_ON_SCOPE_EXIT, surprise... wasn't there something about macros being "evil"? - - //categorize rows into permanent deletion and recycle bin - std::vector deletePermanentLeft; - std::vector deletePermanentRight; - std::vector deleteRecylerLeft; - std::vector deleteRecylerRight; - - std::map hasRecyclerBuffer; - categorize(deleteLeft, deletePermanentLeft, deleteRecylerLeft, useRecycleBin, hasRecyclerBuffer, statusHandler); - categorize(deleteRight, deletePermanentRight, deleteRecylerRight, useRecycleBin, hasRecyclerBuffer, statusHandler); - - //windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong - if (useRecycleBin && - std::any_of(hasRecyclerBuffer.begin(), hasRecyclerBuffer.end(), [](std::pair item) { return !item.second; })) - { - std::wstring msg = _("The recycle bin is not available for the following folders. Files will be deleted permanently instead:") + L"\n"; - - for (auto it = hasRecyclerBuffer.begin(); it != hasRecyclerBuffer.end(); ++it) - if (!it->second) - msg += std::wstring(L"\n") + it->first; - - statusHandler.reportWarning(msg, warningRecyclerMissing); - } - - deleteFromGridAndHDOneSide(deleteRecylerLeft, true, statusHandler); - deleteFromGridAndHDOneSide(deletePermanentLeft, false, statusHandler); - - deleteFromGridAndHDOneSide(deleteRecylerRight, true, statusHandler); - deleteFromGridAndHDOneSide(deletePermanentRight, false, statusHandler); -} diff --git a/algorithm.h b/algorithm.h deleted file mode 100644 index 09adb5ec..00000000 --- a/algorithm.h +++ /dev/null @@ -1,68 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef ALGORITHM_H_INCLUDED -#define ALGORITHM_H_INCLUDED - -#include -#include "file_hierarchy.h" -#include "lib/soft_filter.h" - -namespace zen -{ -void swapGrids(const MainConfiguration& config, FolderComparison& folderCmp); - -std::vector extractDirectionCfg(const MainConfiguration& mainCfg); - -void redetermineSyncDirection(const DirectionConfig& directConfig, BaseDirPair& baseDirectory, std::function reportWarning); -void redetermineSyncDirection(const MainConfiguration& mainCfg, FolderComparison& folderCmp, std::function reportWarning); - -void setSyncDirectionRec(SyncDirection newDirection, FileSystemObject& fsObj); //set new direction (recursively) - -bool allElementsEqual(const FolderComparison& folderCmp); - -//filtering -void applyFiltering (FolderComparison& folderCmp, const MainConfiguration& mainCfg); //full filter apply -void addHardFiltering(BaseDirPair& baseDirObj, const Zstring& excludeFilter); //exclude additional entries only -void addSoftFiltering(BaseDirPair& baseDirObj, const SoftFilter& timeSizeFilter); //exclude additional entries only - -void applyTimeSpanFilter(FolderComparison& folderCmp, const Int64& timeFrom, const Int64& timeTo); //overwrite current active/inactive settings - -void setActiveStatus(bool newStatus, FolderComparison& folderCmp); //activate or deactivate all rows -void setActiveStatus(bool newStatus, FileSystemObject& fsObj); //activate or deactivate row: (not recursively anymore) - - -//manual deletion of files on main grid -std::pair deleteFromGridAndHDPreview( //returns string with elements to be deleted and total count of selected(!) objects, NOT total files/dirs! - const std::vector& selectionLeft, //all pointers need to be bound! - const std::vector& selectionRight, // - bool deleteOnBothSides); - -struct DeleteFilesHandler -{ - virtual ~DeleteFilesHandler() {} - - enum Response - { - IGNORE_ERROR = 10, - RETRY - }; - virtual Response reportError (const std::wstring& msg) = 0; - virtual void reportWarning(const std::wstring& msg, bool& warningActive) = 0; - virtual void reportStatus (const std::wstring& msg) = 0; -}; -void deleteFromGridAndHD(const std::vector& rowsToDeleteOnLeft, //refresh GUI grid after deletion to remove invalid rows - const std::vector& rowsToDeleteOnRight, //all pointers need to be bound! - FolderComparison& folderCmp, //attention: rows will be physically deleted! - const std::vector& directCfgs, - bool deleteOnBothSides, - bool useRecycleBin, - DeleteFilesHandler& statusHandler, - //global warnings: - bool& warningRecyclerMissing); -} - -#endif //ALGORITHM_H_INCLUDED diff --git a/comparison.cpp b/comparison.cpp deleted file mode 100644 index f65d5b26..00000000 --- a/comparison.cpp +++ /dev/null @@ -1,895 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "comparison.h" -#include -#include -#include -#include -#include -#include -#include -#include "algorithm.h" -#include "lib/parallel_scan.h" -#include "lib/resolve_path.h" -#include "lib/dir_exist_async.h" -#include "lib/binary.h" -#include "lib/cmp_filetime.h" -#include "lib/status_handler_impl.h" -#include "lib/parallel_scan.h" - -using namespace zen; - - -std::vector zen::extractCompareCfg(const MainConfiguration& mainCfg) -{ - //merge first and additional pairs - std::vector allPairs; - allPairs.push_back(mainCfg.firstPair); - allPairs.insert(allPairs.end(), - mainCfg.additionalPairs.begin(), //add additional pairs - mainCfg.additionalPairs.end()); - - std::vector output; - std::transform(allPairs.begin(), allPairs.end(), std::back_inserter(output), - [&](const FolderPairEnh& enhPair) -> FolderPairCfg - { - const Zstring leftDirFmt = getFormattedDirectoryName(enhPair.leftDirectory); //ensure they end with FILE_NAME_SEPARATOR and replace macros - const Zstring rightDirFmt = getFormattedDirectoryName(enhPair.rightDirectory); // - - return FolderPairCfg(leftDirFmt, rightDirFmt, - - enhPair.altCmpConfig.get() ? enhPair.altCmpConfig->compareVar : mainCfg.cmpConfig.compareVar, - enhPair.altCmpConfig.get() ? enhPair.altCmpConfig->handleSymlinks : mainCfg.cmpConfig.handleSymlinks, - - normalizeFilters(mainCfg.globalFilter, enhPair.localFilter), - - enhPair.altSyncConfig.get() ? enhPair.altSyncConfig->directionCfg : mainCfg.syncCfg.directionCfg); - }); - return output; -} - -//------------------------------------------------------------------------------------------ -namespace -{ -void checkForIncompleteInput(const std::vector& folderPairsForm, bool& warningInputFieldEmpty, ProcessCallback& callback) -{ - bool havePartialPair = false; - bool haveFullPair = false; - - std::for_each(folderPairsForm.begin(), folderPairsForm.end(), - [&](const FolderPairCfg& fpCfg) - { - if (fpCfg.leftDirectoryFmt.empty() != fpCfg.rightDirectoryFmt.empty()) - havePartialPair = true; - else if (!fpCfg.leftDirectoryFmt.empty()) - haveFullPair = true; - }); - - if (havePartialPair == haveFullPair) //error if: all empty or exist both full and partial pairs -> support single-dir scenario - callback.reportWarning(_("A folder input field is empty.") + L" \n\n" + - _("The corresponding folder will be considered as empty."), warningInputFieldEmpty); -} - -std::set determineExistentDirs(const std::set& dirnames, - bool allowUserInteraction, - ProcessCallback& callback) -{ - std::set dirsEx; - - warn_static("retry klappt nicht fr [] + env vars!") - - tryReportingError([&] - { - std::set missing; - dirsEx = getExistingDirsUpdating(dirnames, missing, allowUserInteraction, callback); //check *all* directories on each try! - if (!missing.empty()) - { - std::wstring msg = _("Cannot find the following folders:") + L"\n"; - for (const Zstring& dirname : missing) - msg += std::wstring(L"\n") + dirname; - throw FileError(msg, _("You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.")); - } - }, callback); - - return dirsEx; -} - - -//check whether one side is subdirectory of other side (folder pair wise!) -//similar check if one directory is read/written by multiple pairs not before beginning of synchronization -void checkFolderDependency(const std::vector& folderPairsFmt, bool& warningDependentFolders, ProcessCallback& callback) //returns warning message, empty if all ok -{ - std::vector> dependentDirs; - - auto dependentDir = [](const Zstring& lhs, const Zstring& rhs) - { - return EqualFilename()(Zstring(lhs.c_str(), std::min(lhs.length(), rhs.length())), //note: this is NOT an equivalence relation! - Zstring(rhs.c_str(), std::min(lhs.length(), rhs.length()))); - }; - - for (const FolderPairCfg& fpCfg : folderPairsFmt) - if (!fpCfg.leftDirectoryFmt.empty() && !fpCfg.rightDirectoryFmt.empty()) //empty folders names may be accepted by user - { - if (dependentDir(fpCfg.leftDirectoryFmt, fpCfg.rightDirectoryFmt)) //test wheter leftDirectory begins with rightDirectory or the other way round - dependentDirs.push_back(std::make_pair(fpCfg.leftDirectoryFmt, fpCfg.rightDirectoryFmt)); - } - - if (!dependentDirs.empty()) - { - std::wstring warningMsg = _("The following folders have dependent paths. Be careful when setting up synchronization rules:"); - for (auto it = dependentDirs.begin(); it != dependentDirs.end(); ++it) - warningMsg += std::wstring(L"\n\n") + - it->first + L"\n" + - it->second; - - callback.reportWarning(warningMsg, warningDependentFolders); - } -} - - -class CmpCallbackImpl : public CompareCallback -{ -public: - CmpCallbackImpl(ProcessCallback& pc, Int64& bytesReported) : - pc_(pc), - bytesReported_(bytesReported) {} - - virtual void updateCompareStatus(Int64 bytesDelta) - { - //inform about the (differential) processed amount of data - pc_.updateProcessedData(0, bytesDelta); //throw()! -> ensure client and service provider are in sync! - bytesReported_ += bytesDelta; // - - pc_.requestUiRefresh(); //may throw - } - -private: - ProcessCallback& pc_; - Int64& bytesReported_; -}; - - -bool filesHaveSameContentUpdating(const Zstring& filename1, const Zstring& filename2, Int64 expectedBytesToCmp, ProcessCallback& pc) //throw FileError -{ - Int64 bytesReported; //amount of bytes that have been compared and communicated to status handler - - //error = unexpected increase of total workload - zen::ScopeGuard guardStatistics = zen::makeGuard([&] { pc.updateTotalData(0, bytesReported); }); - - CmpCallbackImpl callback(pc, bytesReported); - bool sameContent = filesHaveSameContent(filename1, filename2, callback); //throw FileError - - guardStatistics.dismiss(); - - //update statistics to consider the real amount of data processed: consider short-cut behavior if first bytes differ! - if (bytesReported != expectedBytesToCmp) - pc.updateTotalData(0, bytesReported - expectedBytesToCmp); - - return sameContent; -} - -//############################################################################################################################# - -class ComparisonBuffer -{ -public: - ComparisonBuffer(const std::set& keysToRead, size_t fileTimeTol, ProcessCallback& callback); - - //create comparison result table and fill category except for files existing on both sides: undefinedFiles and undefinedLinks are appended! - std::shared_ptr compareByTimeSize(const FolderPairCfg& fpConfig) const; - std::list> compareByContent(const std::vector& workLoad) const; - -private: - ComparisonBuffer(const ComparisonBuffer&); - ComparisonBuffer& operator=(const ComparisonBuffer&); - - std::shared_ptr performComparison(const FolderPairCfg& fpCfg, - std::vector& undefinedFiles, - std::vector& undefinedLinks) const; - - std::map directoryBuffer; //contains only *existing* directories - const size_t fileTimeTolerance; - ProcessCallback& callback_; -}; - - -ComparisonBuffer::ComparisonBuffer(const std::set& keysToRead, - size_t fileTimeTol, - ProcessCallback& callback) : - fileTimeTolerance(fileTimeTol), - callback_(callback) -{ - class CbImpl : public FillBufferCallback - { - public: - CbImpl(ProcessCallback& pcb) : - callback_(pcb), - itemsReported(0) {} - - virtual void reportStatus(const std::wstring& statusMsg, int itemsTotal) - { - callback_.updateProcessedData(itemsTotal - itemsReported, 0); //processed bytes are reported in subfunctions! - itemsReported = itemsTotal; - - callback_.reportStatus(statusMsg); //may throw - //callback_.requestUiRefresh(); //already called by reportStatus() - } - - virtual HandleError reportError(const std::wstring& msg, size_t retryNumber) - { - switch (callback_.reportError(msg, retryNumber)) - { - case ProcessCallback::IGNORE_ERROR: - return ON_ERROR_IGNORE; - - case ProcessCallback::RETRY: - return ON_ERROR_RETRY; - } - - assert(false); - return ON_ERROR_IGNORE; - } - - private: - ProcessCallback& callback_; - int itemsReported; - } cb(callback); - - fillBuffer(keysToRead, //in - directoryBuffer, //out - cb, - UI_UPDATE_INTERVAL / 2); //every ~50 ms -} - - -//--------------------assemble conflict descriptions--------------------------- - -//const wchar_t arrowLeft [] = L"\u2190"; -//const wchar_t arrowRight[] = L"\u2192"; unicode arrows -> too small -const wchar_t arrowLeft [] = L"<--"; -const wchar_t arrowRight[] = L"-->"; - - -//check for very old dates or date2s in the future -std::wstring getConflictInvalidDate(const Zstring& fileNameFull, Int64 utcTime) -{ - return replaceCpy(_("File %x has an invalid date."), L"%x", fmtFileName(fileNameFull)) + L"\n" + - _("Date:") + L" " + utcToLocalTimeString(utcTime); -} - - -//check for changed files with same modification date -std::wstring getConflictSameDateDiffSize(const FilePair& fileObj) -{ - return replaceCpy(_("Files %x have the same date but a different size."), L"%x", fmtFileName(fileObj.getObjRelativeName())) + L"\n" + - L" " + arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.getLastWriteTime()) + L" " + _("Size:") + L" " + toGuiString(fileObj.getFileSize()) + L"\n" + - L" " + arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.getLastWriteTime()) + L" " + _("Size:") + L" " + toGuiString(fileObj.getFileSize()); -} - - -inline -std::wstring getDescrDiffMetaShortnameCase(const FileSystemObject& fsObj) -{ - return _("Items differ in attributes only") + L"\n" + - L" " + arrowLeft + L" " + fmtFileName(fsObj.getShortName()) + L"\n" + - L" " + arrowRight + L" " + fmtFileName(fsObj.getShortName()); -} - - -template inline -std::wstring getDescrDiffMetaDate(const FileOrLinkPair& fileObj) -{ - return _("Items differ in attributes only") + L"\n" + - L" " + arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.template getLastWriteTime()) + L"\n" + - L" " + arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.template getLastWriteTime()); -} - -//----------------------------------------------------------------------------- - -void categorizeSymlinkByTime(SymlinkPair& linkObj, size_t fileTimeTolerance) -{ - //categorize symlinks that exist on both sides - switch (CmpFileTime::getResult(linkObj.getLastWriteTime(), - linkObj.getLastWriteTime(), fileTimeTolerance)) - { - case CmpFileTime::TIME_EQUAL: - //Caveat: - //1. SYMLINK_EQUAL may only be set if short names match in case: InSyncDir's mapping tables use short name as a key! see db_file.cpp - //2. harmonize with "bool stillInSync()" in algorithm.cpp - - if (linkObj.getShortName() == linkObj.getShortName()) - linkObj.setCategory(); - else - linkObj.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(linkObj)); - break; - - case CmpFileTime::TIME_LEFT_NEWER: - linkObj.setCategory(); - break; - - case CmpFileTime::TIME_RIGHT_NEWER: - linkObj.setCategory(); - break; - - case CmpFileTime::TIME_LEFT_INVALID: - linkObj.setCategoryConflict(getConflictInvalidDate(linkObj.getFullName(), linkObj.getLastWriteTime())); - break; - - case CmpFileTime::TIME_RIGHT_INVALID: - linkObj.setCategoryConflict(getConflictInvalidDate(linkObj.getFullName(), linkObj.getLastWriteTime())); - break; - } -} - - -std::shared_ptr ComparisonBuffer::compareByTimeSize(const FolderPairCfg& fpConfig) const -{ - //do basis scan and retrieve files existing on both sides as "compareCandidates" - std::vector uncategorizedFiles; - std::vector uncategorizedLinks; - std::shared_ptr output = performComparison(fpConfig, uncategorizedFiles, uncategorizedLinks); - - //finish symlink categorization - std::for_each(uncategorizedLinks.begin(), uncategorizedLinks.end(), - [&](SymlinkPair* linkObj) { categorizeSymlinkByTime(*linkObj, fileTimeTolerance); }); - - //categorize files that exist on both sides - std::for_each(uncategorizedFiles.begin(), uncategorizedFiles.end(), - [&](FilePair* fileObj) - { - switch (CmpFileTime::getResult(fileObj->getLastWriteTime(), - fileObj->getLastWriteTime(), fileTimeTolerance)) - { - case CmpFileTime::TIME_EQUAL: - //Caveat: - //1. FILE_EQUAL may only be set if short names match in case: InSyncDir's mapping tables use short name as a key! see db_file.cpp - //2. FILE_EQUAL is expected to mean identical file sizes! See InSyncFile - //3. harmonize with "bool stillInSync()" in algorithm.cpp, FilePair::syncTo() in file_hierarchy.cpp - if (fileObj->getFileSize() == fileObj->getFileSize()) - { - if (fileObj->getShortName() == fileObj->getShortName()) - fileObj->setCategory(); - else - fileObj->setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(*fileObj)); - } - else - fileObj->setCategoryConflict(getConflictSameDateDiffSize(*fileObj)); //same date, different filesize - break; - - case CmpFileTime::TIME_LEFT_NEWER: - fileObj->setCategory(); - break; - - case CmpFileTime::TIME_RIGHT_NEWER: - fileObj->setCategory(); - break; - - case CmpFileTime::TIME_LEFT_INVALID: - fileObj->setCategoryConflict(getConflictInvalidDate(fileObj->getFullName(), fileObj->getLastWriteTime())); - break; - - case CmpFileTime::TIME_RIGHT_INVALID: - fileObj->setCategoryConflict(getConflictInvalidDate(fileObj->getFullName(), fileObj->getLastWriteTime())); - break; - } - }); - return output; -} - - -void categorizeSymlinkByContent(SymlinkPair& linkObj, size_t fileTimeTolerance, ProcessCallback& callback) -{ - //categorize symlinks that exist on both sides - Zstring targetPathRawL; - Zstring targetPathRawR; - Opt errMsg = tryReportingError([&] - { - callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtFileName(linkObj.getFullName()))); - targetPathRawL = getSymlinkTargetRaw(linkObj.getFullName()); //throw FileError - - callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtFileName(linkObj.getFullName()))); - targetPathRawR = getSymlinkTargetRaw(linkObj.getFullName()); //throw FileError - }, callback); - - if (errMsg) - linkObj.setCategoryConflict(*errMsg); - else - { - if (targetPathRawL == targetPathRawR -#ifdef ZEN_WIN //type of symbolic link is relevant for Windows only - && - dirExists(linkObj.getFullName()) == //check if dir-symlink - dirExists(linkObj.getFullName()) // -#endif - ) - { - //Caveat: - //1. SYMLINK_EQUAL may only be set if short names match in case: InSyncDir's mapping tables use short name as a key! see db_file.cpp - //2. harmonize with "bool stillInSync()" in algorithm.cpp, FilePair::syncTo() in file_hierarchy.cpp - - //symlinks have same "content" - if (linkObj.getShortName() != linkObj.getShortName()) - linkObj.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(linkObj)); - else if (CmpFileTime::getResult(linkObj.getLastWriteTime(), - linkObj.getLastWriteTime(), fileTimeTolerance) != CmpFileTime::TIME_EQUAL) - linkObj.setCategoryDiffMetadata(getDescrDiffMetaDate(linkObj)); - else - linkObj.setCategory(); - } - else - linkObj.setCategory(); - } -} - - -std::list> ComparisonBuffer::compareByContent(const std::vector& workLoad) const -{ - std::list> output; - if (workLoad.empty()) - return output; - - //PERF_START; - std::vector undefinedFiles; - - //process one folder pair after each other - for (auto it = workLoad.begin(); it != workLoad.end(); ++it) - { - std::vector uncategorizedLinks; - //do basis scan and retrieve candidates for binary comparison (files existing on both sides) - - output.push_back(performComparison(*it, undefinedFiles, uncategorizedLinks)); - - //finish symlink categorization - std::for_each(uncategorizedLinks.begin(), uncategorizedLinks.end(), - [&](SymlinkPair* linkObj) { categorizeSymlinkByContent(*linkObj, fileTimeTolerance, callback_); }); - } - - //finish categorization... - std::vector filesToCompareBytewise; - - //content comparison of file content happens AFTER finding corresponding files - //in order to separate into two processes (scanning and comparing) - - std::for_each(undefinedFiles.begin(), undefinedFiles.end(), - [&](FilePair* fileObj) - { - //pre-check: files have different content if they have a different filesize (must not be FILE_EQUAL: see InSyncFile) - if (fileObj->getFileSize() != fileObj->getFileSize()) - fileObj->setCategory(); - else - filesToCompareBytewise.push_back(fileObj); - }); - - const size_t objectsTotal = filesToCompareBytewise.size(); - const UInt64 bytesTotal = //left and right filesizes are equal - std::accumulate(filesToCompareBytewise.begin(), filesToCompareBytewise.end(), static_cast(0), - [](UInt64 sum, FilePair* fsObj) { return sum + fsObj->getFileSize(); }); - - callback_.initNewPhase(static_cast(objectsTotal), - to(bytesTotal), - ProcessCallback::PHASE_COMPARING_CONTENT); - - const std::wstring txtComparingContentOfFiles = _("Comparing content of files %x"); - - //compare files (that have same size) bytewise... - std::for_each(filesToCompareBytewise.begin(), filesToCompareBytewise.end(), - [&](FilePair* fileObj) - { - callback_.reportStatus(replaceCpy(txtComparingContentOfFiles, L"%x", fmtFileName(fileObj->getObjRelativeName()), false)); - - //check files that exist in left and right model but have different content - - bool haveSameContent = false; - Opt errMsg = tryReportingError([&] - { - haveSameContent = filesHaveSameContentUpdating(fileObj->getFullName(), //throw FileError - fileObj->getFullName(), - to(fileObj->getFileSize()), - callback_); - - callback_.updateProcessedData(1, 0); //processed bytes are reported in subfunctions! - callback_.requestUiRefresh(); //may throw - }, callback_); - - if (errMsg) - fileObj->setCategoryConflict(*errMsg); - else - { - if (haveSameContent) - { - //Caveat: - //1. FILE_EQUAL may only be set if short names match in case: InSyncDir's mapping tables use short name as a key! see db_file.cpp - //2. FILE_EQUAL is expected to mean identical file sizes! See InSyncFile - //3. harmonize with "bool stillInSync()" in algorithm.cpp, FilePair::syncTo() in file_hierarchy.cpp - if (fileObj->getShortName() != fileObj->getShortName()) - fileObj->setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(*fileObj)); - else if (CmpFileTime::getResult(fileObj->getLastWriteTime(), fileObj->getLastWriteTime(), fileTimeTolerance) != CmpFileTime::TIME_EQUAL) - fileObj->setCategoryDiffMetadata(getDescrDiffMetaDate(*fileObj)); - else - fileObj->setCategory(); - } - else - fileObj->setCategory(); - } - }); - return output; -} - - -class MergeSides -{ -public: - MergeSides(std::vector& appendUndefinedFileOut, - std::vector& appendUndefinedLinkOut) : - appendUndefinedFile(appendUndefinedFileOut), - appendUndefinedLink(appendUndefinedLinkOut) {} - - void execute(const DirContainer& leftSide, const DirContainer& rightSide, HierarchyObject& output); - -private: - template - void fillOneSide(const DirContainer& dirCont, HierarchyObject& output); - - std::vector& appendUndefinedFile; - std::vector& appendUndefinedLink; -}; - - -template -void MergeSides::fillOneSide(const DirContainer& dirCont, HierarchyObject& output) -{ - for (const auto& file : dirCont.files) - output.addSubFile(file.first, file.second); - - for (const auto& link : dirCont.links) - output.addSubLink(link.first, link.second); - - for (const auto& dir : dirCont.dirs) - { - DirPair& newDirMap = output.addSubDir(dir.first); - fillOneSide(dir.second, newDirMap); //recurse - } -} - - -//improve merge-perf by over 70% + more natural default sequence -template inline -void linearMerge(const MapType& mapLeft, const MapType& mapRight, ProcessLeftOnly lo, ProcessRightOnly ro, ProcessBoth bo) -{ - const auto lessVal = mapLeft.value_comp(); - - auto iterLeft = mapLeft .begin(); - auto iterRight = mapRight.begin(); - - auto finishLeft = [&] { std::for_each(iterLeft, mapLeft .end(), lo); }; - auto finishRight = [&] { std::for_each(iterRight, mapRight.end(), ro); }; - - if (iterLeft == mapLeft .end()) return finishRight(); - if (iterRight == mapRight.end()) return finishLeft(); - - for (;;) - if (lessVal(*iterLeft, *iterRight)) - { - lo(*iterLeft); - if (++iterLeft == mapLeft.end()) - return finishRight(); - } - else if (lessVal(*iterRight, *iterLeft)) - { - ro(*iterRight); - if (++iterRight == mapRight.end()) - return finishLeft(); - } - else - { - bo(*iterLeft, *iterRight); - ++iterLeft; // - ++iterRight; //increment BOTH before checking for end of range! - if (iterLeft == mapLeft .end()) return finishRight(); - if (iterRight == mapRight.end()) return finishLeft(); - } -} - - -void MergeSides::execute(const DirContainer& leftSide, const DirContainer& rightSide, HierarchyObject& output) -{ - //HierarchyObject::addSubFile() must NOT invalidate references used in "appendUndefined"! - - typedef const DirContainer::FileList::value_type FileData; - - linearMerge(leftSide.files, rightSide.files, - [&](const FileData& fileLeft ) { output.addSubFile(fileLeft .first, fileLeft .second); }, //left only - [&](const FileData& fileRight) { output.addSubFile(fileRight.first, fileRight.second); }, //right only - - [&](const FileData& fileLeft, const FileData& fileRight) //both sides - { - FilePair& newEntry = output.addSubFile(fileLeft.first, - fileLeft.second, - FILE_EQUAL, //FILE_EQUAL is just a dummy-value here - fileRight.first, - fileRight.second); - appendUndefinedFile.push_back(&newEntry); - }); - - //----------------------------------------------------------------------------------------------- - typedef const DirContainer::LinkList::value_type LinkData; - - linearMerge(leftSide.links, rightSide.links, - [&](const LinkData& linkLeft) { output.addSubLink(linkLeft.first, linkLeft.second); }, //left only - [&](const LinkData& linkRight) { output.addSubLink(linkRight.first, linkRight.second); }, //right only - - [&](const LinkData& linkLeft, const LinkData& linkRight) //both sides - { - SymlinkPair& newEntry = output.addSubLink(linkLeft.first, - linkLeft.second, - SYMLINK_EQUAL, //SYMLINK_EQUAL is just a dummy-value here - linkRight.first, - linkRight.second); - appendUndefinedLink.push_back(&newEntry); - }); - - //----------------------------------------------------------------------------------------------- - typedef const DirContainer::DirList::value_type DirData; - - linearMerge(leftSide.dirs, rightSide.dirs, - [&](const DirData& dirLeft) //left only - { - DirPair& newDirMap = output.addSubDir(dirLeft.first); - this->fillOneSide(dirLeft.second, newDirMap); //recurse into subdirectories - }, - [&](const DirData& dirRight) //right only - { - DirPair& newDirMap = output.addSubDir(dirRight.first); - this->fillOneSide(dirRight.second, newDirMap); //recurse into subdirectories - }, - - [&](const DirData& dirLeft, const DirData& dirRight) //both sides - { - DirPair& newDirMap = output.addSubDir(dirLeft.first, dirRight.first, DIR_EQUAL); - if (dirLeft.first != dirRight.first) - newDirMap.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(newDirMap)); - - execute(dirLeft.second, dirRight.second, newDirMap); //recurse into subdirectories - }); -} - -//mark excluded directories (see fillBuffer()) + remove superfluous excluded subdirectories -void removeFilteredDirs(HierarchyObject& hierObj, const HardFilter& filterProc) -{ - //process subdirs recursively - for (DirPair& dirObj : hierObj.refSubDirs()) - { - dirObj.setActive(filterProc.passDirFilter(dirObj.getObjRelativeName(), nullptr)); //subObjMightMatch is always true in this context! - removeFilteredDirs(dirObj, filterProc); - } - - //remove superfluous directories -> note: this does not invalidate "std::vector& undefinedFiles", since we delete folders only - //and there is no side-effect for memory positions of FilePair and SymlinkPair thanks to zen::FixedList! - hierObj.refSubDirs().remove_if([](DirPair& dirObj) - { - return !dirObj.isActive() && - dirObj.refSubDirs ().empty() && - dirObj.refSubLinks().empty() && - dirObj.refSubFiles().empty(); - }); -} - - -//create comparison result table and fill category except for files existing on both sides: undefinedFiles and undefinedLinks are appended! -std::shared_ptr ComparisonBuffer::performComparison(const FolderPairCfg& fpCfg, - std::vector& undefinedFiles, - std::vector& undefinedLinks) const -{ - callback_.reportStatus(_("Generating file list...")); - callback_.forceUiRefresh(); - - auto getDirValue = [&](const Zstring& dirnameFmt) -> const DirectoryValue* - { - auto it = directoryBuffer.find(DirectoryKey(dirnameFmt, fpCfg.filter.nameFilter, fpCfg.handleSymlinks)); - return it != directoryBuffer.end() ? &it->second : nullptr; - }; - - const DirectoryValue* bufValueLeft = getDirValue(fpCfg.leftDirectoryFmt); - const DirectoryValue* bufValueRight = getDirValue(fpCfg.rightDirectoryFmt); - - Zstring filterFailedRead; - auto filterAddFailedDirReads = [&filterFailedRead](const std::set& failedDirReads) //exclude directory child items only! - { - //note: relDirPf is empty for base dir, otherwise postfixed! e.g. "subdir\" - //an empty relDirPf is a pathological case at this point, since determineExistentDirs() already filtered missing base directories! - std::for_each(failedDirReads .begin(), failedDirReads .end(), [&](const Zstring& relDirPf) { filterFailedRead += relDirPf + Zstr("?*\n"); }); - }; - auto filterAddFailedItemReads = [&filterFailedRead](const std::set& failedItemReads) //exclude item AND (potential) child items! - { - std::for_each(failedItemReads.begin(), failedItemReads.end(), [&](const Zstring& relItem ) { filterFailedRead += relItem + Zstr("\n"); }); - }; - - if (bufValueLeft ) filterAddFailedDirReads(bufValueLeft ->failedDirReads); - if (bufValueRight) filterAddFailedDirReads(bufValueRight->failedDirReads); - - if (bufValueLeft ) filterAddFailedItemReads(bufValueLeft ->failedItemReads); - if (bufValueRight) filterAddFailedItemReads(bufValueRight->failedItemReads); - - std::shared_ptr output = std::make_shared(fpCfg.leftDirectoryFmt, - bufValueLeft != nullptr, //dir existence must be checked only once: available iff buffer entry exists! - fpCfg.rightDirectoryFmt, - bufValueRight != nullptr, - fpCfg.filter.nameFilter, - fpCfg.compareVar, - fileTimeTolerance); - //PERF_START; - MergeSides(undefinedFiles, undefinedLinks).execute(bufValueLeft ? bufValueLeft ->dirCont : DirContainer(), - bufValueRight ? bufValueRight->dirCont : DirContainer(), *output); - //PERF_STOP; - - //##################### in/exclude rows according to filtering ##################### - - //attention: some excluded directories are still in the comparison result! (see include filter handling!) - if (!fpCfg.filter.nameFilter->isNull()) - removeFilteredDirs(*output, *fpCfg.filter.nameFilter); //mark excluded directories (see fillBuffer()) + remove superfluous excluded subdirectories - - //apply soft filtering (hard filter already applied during traversal!) - addSoftFiltering(*output, fpCfg.filter.timeSizeFilter); - - //handle (user-ignored) traversing errors: just uncheck them, no need to physically delete them from both sides - if (!filterFailedRead.empty()) - addHardFiltering(*output, filterFailedRead); - - //################################################################################## - return output; -} -} - - -void zen::compare(size_t fileTimeTolerance, - xmlAccess::OptionalDialogs& warnings, - bool allowUserInteraction, - bool runWithBackgroundPriority, - bool createDirLocks, - std::unique_ptr& dirLocks, - const std::vector& cfgList, - FolderComparison& output, - ProcessCallback& callback) -{ - //specify process and resource handling priorities - std::unique_ptr backgroundPrio; - if (runWithBackgroundPriority) - try - { - backgroundPrio = make_unique(); //throw FileError - } - catch (const FileError& e) - { - //not an error in this context - callback.reportInfo(e.toString()); //may throw! - } - - //prevent operating system going into sleep state - std::unique_ptr noStandby; - try - { - noStandby = make_unique(); //throw FileError - } - catch (const FileError& e) - { - //not an error in this context - callback.reportInfo(e.toString()); //may throw! - } - - //PERF_START; - - callback.reportInfo(_("Starting comparison")); //we want some indicator at the very beginning of the log to make sense of "total time" - - //init process: keep at beginning so that all gui elements are initialized properly - callback.initNewPhase(-1, 0, ProcessCallback::PHASE_SCANNING); //it's not known how many files will be scanned => -1 objects - - //-------------------some basic checks:------------------------------------------ - - checkForIncompleteInput(cfgList, warnings.warningInputFieldEmpty, callback); - checkFolderDependency (cfgList, warnings.warningDependentFolders, callback); - - std::set dirnamesExisting; - //list of directories that are *expected* to be existent (and need to be scanned)! - //directory existence only checked *once* to avoid race conditions! - { - std::set dirnames; - std::for_each(cfgList.begin(), cfgList.end(), - [&](const FolderPairCfg& fpCfg) - { - dirnames.insert(fpCfg.leftDirectoryFmt); - dirnames.insert(fpCfg.rightDirectoryFmt); - }); - dirnamesExisting = determineExistentDirs(dirnames, allowUserInteraction, callback); - } - - auto dirAvailable = [&](const Zstring& dirnameFmt) { return dirnamesExisting.find(dirnameFmt) != dirnamesExisting.end(); }; - - //-------------------end of basic checks------------------------------------------ - - try - { - //lock (existing) directories before comparison - if (createDirLocks) - dirLocks = make_unique(dirnamesExisting, warnings.warningDirectoryLockFailed, callback); - - //------------------- fill directory buffer --------------------------------------------------- - std::set dirsToRead; - - std::for_each(cfgList.begin(), cfgList.end(), - [&](const FolderPairCfg& fpCfg) - { - if (dirAvailable(fpCfg.leftDirectoryFmt)) //only request *currently existing* directories: at this point user is aware that non-ex + empty string are seen as empty folder! - dirsToRead.insert(DirectoryKey(fpCfg.leftDirectoryFmt, fpCfg.filter.nameFilter, fpCfg.handleSymlinks)); - if (dirAvailable(fpCfg.rightDirectoryFmt)) - dirsToRead.insert(DirectoryKey(fpCfg.rightDirectoryFmt, fpCfg.filter.nameFilter, fpCfg.handleSymlinks)); - }); - - FolderComparison outputTmp; //write to output as a transaction! - - //reduce peak memory by restricting lifetime of ComparisonBuffer to have ended when loading potentially huge InSyncDir instance in redetermineSyncDirection() - { - //------------ traverse/read folders ----------------------------------------------------- - ComparisonBuffer cmpBuff(dirsToRead, fileTimeTolerance, callback); - - //process binary comparison in one block - std::vector workLoadByContent; - for (const FolderPairCfg& fpCfg : cfgList) - switch (fpCfg.compareVar) - { - case CMP_BY_TIME_SIZE: - break; - case CMP_BY_CONTENT: - workLoadByContent.push_back(fpCfg); - break; - } - std::list> outputByContent = cmpBuff.compareByContent(workLoadByContent); - - //write output in order - for (const FolderPairCfg& fpCfg : cfgList) - switch (fpCfg.compareVar) - { - case CMP_BY_TIME_SIZE: - outputTmp.push_back(cmpBuff.compareByTimeSize(fpCfg)); - break; - case CMP_BY_CONTENT: - assert(!outputByContent.empty()); - if (!outputByContent.empty()) - { - outputTmp.push_back(outputByContent.front()); - outputByContent.pop_front(); - } - break; - } - } - - assert(outputTmp.size() == cfgList.size()); - - //--------- set initial sync-direction -------------------------------------------------- - - for (auto j = begin(outputTmp); j != end(outputTmp); ++j) - { - const FolderPairCfg& fpCfg = cfgList[j - outputTmp.begin()]; - - callback.reportStatus(_("Calculating sync directions...")); - callback.forceUiRefresh(); - zen::redetermineSyncDirection(fpCfg.directionCfg, *j, - [&](const std::wstring& warning) { callback.reportWarning(warning, warnings.warningDatabaseError); }); - } - - //only if everything was processed correctly output is written to! - //note: output mustn't change during this process to be in sync with GUI grid view!!! - outputTmp.swap(output); - } - catch (const std::bad_alloc& e) - { - callback.reportFatalError(_("Out of memory.") + L" " + utfCvrtTo(e.what())); - } - catch (const std::exception& e) - { - callback.reportFatalError(utfCvrtTo(e.what())); - } -} diff --git a/comparison.h b/comparison.h deleted file mode 100644 index 0b92bade..00000000 --- a/comparison.h +++ /dev/null @@ -1,59 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef COMPARISON_H_INCLUDED -#define COMPARISON_H_INCLUDED - -#include "file_hierarchy.h" -#include "lib/process_xml.h" -#include "process_callback.h" -#include "lib/norm_filter.h" -#include "lib/lock_holder.h" - - -namespace zen -{ -struct FolderPairCfg -{ - FolderPairCfg(const Zstring& leftDir, //must be formatted folder pairs! - const Zstring& rightDir, - CompareVariant cmpVar, - SymLinkHandling handleSymlinksIn, - const NormalizedFilter& filterIn, - const DirectionConfig& directCfg) : - leftDirectoryFmt(leftDir), - rightDirectoryFmt(rightDir), - compareVar(cmpVar), - handleSymlinks(handleSymlinksIn), - filter(filterIn), - directionCfg(directCfg) {} - - Zstring leftDirectoryFmt; //resolved directory names! - Zstring rightDirectoryFmt; // - - CompareVariant compareVar; - SymLinkHandling handleSymlinks; - - NormalizedFilter filter; - - DirectionConfig directionCfg; -}; - -std::vector extractCompareCfg(const MainConfiguration& mainCfg); //fill FolderPairCfg and resolve folder pairs - -//FFS core routine: -void compare(size_t fileTimeTolerance, //max allowed file time deviation - xmlAccess::OptionalDialogs& warnings, - bool allowUserInteraction, - bool runWithBackgroundPriority, - bool createDirLocks, - std::unique_ptr& dirLocks, //out - const std::vector& cfgList, - FolderComparison& output, //out - ProcessCallback& callback); -} - -#endif // COMPARISON_H_INCLUDED diff --git a/file_hierarchy.cpp b/file_hierarchy.cpp deleted file mode 100644 index 780f05de..00000000 --- a/file_hierarchy.cpp +++ /dev/null @@ -1,409 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "file_hierarchy.h" -#include -#include -#include - -using namespace zen; - - -void HierarchyObject::removeEmptyRec() -{ - bool emptyExisting = false; - auto isEmpty = [&](const FileSystemObject& fsObj) -> bool - { - const bool objEmpty = fsObj.isEmpty(); - if (objEmpty) - emptyExisting = true; - return objEmpty; - }; - - refSubFiles().remove_if(isEmpty); - refSubLinks().remove_if(isEmpty); - refSubDirs ().remove_if(isEmpty); - - if (emptyExisting) //notify if actual deletion happened - notifySyncCfgChanged(); //mustn't call this in ~FileSystemObject(), since parent, usually a DirPair, is already partially destroyed and existing as a pure HierarchyObject! - - for (DirPair& subDir : refSubDirs()) - subDir.removeEmptyRec(); //recurse -} - -namespace -{ -SyncOperation getIsolatedSyncOperation(CompareFilesResult cmpResult, - bool selectedForSynchronization, - SyncDirection syncDir, - bool hasDirConflict) //perf: std::wstring was wasteful here -{ - assert(!hasDirConflict || syncDir == SyncDirection::NONE); - - if (!selectedForSynchronization) - return cmpResult == FILE_EQUAL ? - SO_EQUAL : - SO_DO_NOTHING; - - switch (cmpResult) - { - case FILE_LEFT_SIDE_ONLY: - switch (syncDir) - { - case SyncDirection::LEFT: - return SO_DELETE_LEFT; //delete files on left - case SyncDirection::RIGHT: - return SO_CREATE_NEW_RIGHT; //copy files to right - case SyncDirection::NONE: - return hasDirConflict ? SO_UNRESOLVED_CONFLICT : SO_DO_NOTHING; - } - break; - - case FILE_RIGHT_SIDE_ONLY: - switch (syncDir) - { - case SyncDirection::LEFT: - return SO_CREATE_NEW_LEFT; //copy files to left - case SyncDirection::RIGHT: - return SO_DELETE_RIGHT; //delete files on right - case SyncDirection::NONE: - return hasDirConflict ? SO_UNRESOLVED_CONFLICT : SO_DO_NOTHING; - } - break; - - case FILE_LEFT_NEWER: - case FILE_RIGHT_NEWER: - case FILE_DIFFERENT: - case FILE_CONFLICT: - switch (syncDir) - { - case SyncDirection::LEFT: - return SO_OVERWRITE_LEFT; //copy from right to left - case SyncDirection::RIGHT: - return SO_OVERWRITE_RIGHT; //copy from left to right - case SyncDirection::NONE: - return hasDirConflict ? SO_UNRESOLVED_CONFLICT : SO_DO_NOTHING; - } - break; - - case FILE_DIFFERENT_METADATA: - switch (syncDir) - { - case SyncDirection::LEFT: - return SO_COPY_METADATA_TO_LEFT; - case SyncDirection::RIGHT: - return SO_COPY_METADATA_TO_RIGHT; - case SyncDirection::NONE: - return hasDirConflict ? SO_UNRESOLVED_CONFLICT : SO_DO_NOTHING; - } - break; - - case FILE_EQUAL: - assert(syncDir == SyncDirection::NONE); - return SO_EQUAL; - } - - assert(false); - return SO_DO_NOTHING; //dummy -} - - -template inline -bool hasDirectChild(const HierarchyObject& hierObj, Predicate p) -{ - return std::any_of(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), p) || - std::any_of(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(), p) || - std::any_of(hierObj.refSubDirs(). begin(), hierObj.refSubDirs(). end(), p); -} -} - - -SyncOperation FileSystemObject::testSyncOperation(SyncDirection testSyncDir) const //semantics: "what if"! assumes "active, no conflict, no recursion (directory)! -{ - return getIsolatedSyncOperation(getCategory(), true, testSyncDir, false); -} - - -SyncOperation FileSystemObject::getSyncOperation() const -{ - return getIsolatedSyncOperation(getCategory(), selectedForSynchronization, getSyncDir(), syncDirConflict.get() != nullptr); - //no *not* make a virtual call to testSyncOperation()! See FilePair::testSyncOperation()! <- better not implement one in terms of the other!!! -} - - -//SyncOperation DirPair::testSyncOperation() const -> no recursion: we do NOT want to consider child elements when testing! - - -SyncOperation DirPair::getSyncOperation() const -{ - if (!syncOpUpToDate) - { - syncOpUpToDate = true; - //redetermine... - - //suggested operation *not* considering child elements - syncOpBuffered = FileSystemObject::getSyncOperation(); - - //action for child elements may occassionally have to overwrite parent task: - switch (syncOpBuffered) - { - case SO_OVERWRITE_LEFT: - case SO_OVERWRITE_RIGHT: - case SO_MOVE_LEFT_SOURCE: - case SO_MOVE_LEFT_TARGET: - case SO_MOVE_RIGHT_SOURCE: - case SO_MOVE_RIGHT_TARGET: - assert(false); - case SO_CREATE_NEW_LEFT: - case SO_CREATE_NEW_RIGHT: - case SO_COPY_METADATA_TO_LEFT: - case SO_COPY_METADATA_TO_RIGHT: - case SO_EQUAL: - break; //take over suggestion, no problem for child-elements - case SO_DELETE_LEFT: - case SO_DELETE_RIGHT: - case SO_DO_NOTHING: - case SO_UNRESOLVED_CONFLICT: - if (isEmpty()) - { - //1. if at least one child-element is to be created, make sure parent folder is created also - //note: this automatically fulfills "create parent folders even if excluded"; - //see http://sourceforge.net/tracker/index.php?func=detail&aid=2628943&group_id=234430&atid=1093080 - if (hasDirectChild(*this, - [](const FileSystemObject& fsObj) -> bool - { - const SyncOperation op = fsObj.getSyncOperation(); - return op == SO_CREATE_NEW_LEFT || - op == SO_MOVE_LEFT_TARGET; - })) - syncOpBuffered = SO_CREATE_NEW_LEFT; - //2. cancel parent deletion if only a single child is not also scheduled for deletion - else if (syncOpBuffered == SO_DELETE_RIGHT && - hasDirectChild(*this, - [](const FileSystemObject& fsObj) -> bool - { - if (fsObj.isEmpty()) - return false; //fsObj may already be empty because it once contained a "move source" - const SyncOperation op = fsObj.getSyncOperation(); - return op != SO_DELETE_RIGHT && - op != SO_MOVE_RIGHT_SOURCE; - })) - syncOpBuffered = SO_DO_NOTHING; - } - else if (isEmpty()) - { - if (hasDirectChild(*this, - [](const FileSystemObject& fsObj) -> bool - { - const SyncOperation op = fsObj.getSyncOperation(); - return op == SO_CREATE_NEW_RIGHT || - op == SO_MOVE_RIGHT_TARGET; - })) - syncOpBuffered = SO_CREATE_NEW_RIGHT; - else if (syncOpBuffered == SO_DELETE_LEFT && - hasDirectChild(*this, - [](const FileSystemObject& fsObj) -> bool - { - if (fsObj.isEmpty()) - return false; - const SyncOperation op = fsObj.getSyncOperation(); - return op != SO_DELETE_LEFT && - op != SO_MOVE_LEFT_SOURCE; - })) - syncOpBuffered = SO_DO_NOTHING; - } - break; - } - } - return syncOpBuffered; -} - - -inline //it's private! -SyncOperation FilePair::applyMoveOptimization(SyncOperation op) const -{ - /* - check whether we can optimize "create + delete" via "move": - 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_TARGET; - else if (op == SO_DELETE_LEFT && - opRef == SO_CREATE_NEW_LEFT) - op = SO_MOVE_LEFT_SOURCE; - else if (op == SO_CREATE_NEW_RIGHT && - opRef == SO_DELETE_RIGHT) - op = SO_MOVE_RIGHT_TARGET; - else if (op == SO_DELETE_RIGHT && - opRef == SO_CREATE_NEW_RIGHT) - op = SO_MOVE_RIGHT_SOURCE; - } - return op; -} - - -SyncOperation FilePair::testSyncOperation(SyncDirection testSyncDir) const -{ - return applyMoveOptimization(FileSystemObject::testSyncOperation(testSyncDir)); -} - - -SyncOperation FilePair::getSyncOperation() const -{ - return applyMoveOptimization(FileSystemObject::getSyncOperation()); -} - - -std::wstring zen::getCategoryDescription(CompareFilesResult cmpRes) -{ - switch (cmpRes) - { - case FILE_LEFT_SIDE_ONLY: - return _("Item exists on left side only"); - case FILE_RIGHT_SIDE_ONLY: - return _("Item exists on right side only"); - case FILE_LEFT_NEWER: - return _("Left side is newer"); - case FILE_RIGHT_NEWER: - return _("Right side is newer"); - case FILE_DIFFERENT: - return _("Items have different content"); - case FILE_EQUAL: - return _("Both sides are equal"); - case FILE_DIFFERENT_METADATA: - return _("Items differ in attributes only"); - case FILE_CONFLICT: - return _("Conflict/item cannot be categorized"); - } - assert(false); - return std::wstring(); -} - - -std::wstring zen::getCategoryDescription(const FileSystemObject& fsObj) -{ - const CompareFilesResult cmpRes = fsObj.getCategory(); - if (cmpRes == FILE_CONFLICT || - cmpRes == FILE_DIFFERENT_METADATA) - return fsObj.getCatExtraDescription(); - - return getCategoryDescription(cmpRes); -} - - -std::wstring zen::getSyncOpDescription(SyncOperation op) -{ - switch (op) - { - case SO_CREATE_NEW_LEFT: - return _("Copy new item to left"); - case SO_CREATE_NEW_RIGHT: - return _("Copy new item to right"); - case SO_DELETE_LEFT: - return _("Delete left item"); - case SO_DELETE_RIGHT: - return _("Delete right item"); - case SO_MOVE_LEFT_SOURCE: - case SO_MOVE_LEFT_TARGET: - return _("Move file on left"); //move only supported for files - case SO_MOVE_RIGHT_SOURCE: - case SO_MOVE_RIGHT_TARGET: - return _("Move file on right"); - case SO_OVERWRITE_LEFT: - return _("Overwrite left item"); - case SO_OVERWRITE_RIGHT: - return _("Overwrite right item"); - case SO_DO_NOTHING: - return _("Do nothing"); - case SO_EQUAL: - return _("Both sides are equal"); - case SO_COPY_METADATA_TO_LEFT: - return _("Update attributes on left"); - case SO_COPY_METADATA_TO_RIGHT: - return _("Update attributes on right"); - case SO_UNRESOLVED_CONFLICT: //not used on GUI, but in .csv - return _("Conflict/item cannot be categorized"); - } - assert(false); - return std::wstring(); -} - - -std::wstring zen::getSyncOpDescription(const FileSystemObject& fsObj) -{ - const SyncOperation op = fsObj.getSyncOperation(); - switch (op) - { - case SO_CREATE_NEW_LEFT: - case SO_CREATE_NEW_RIGHT: - case SO_DELETE_LEFT: - case SO_DELETE_RIGHT: - case SO_OVERWRITE_LEFT: - case SO_OVERWRITE_RIGHT: - case SO_DO_NOTHING: - case SO_EQUAL: - return getSyncOpDescription(op); //use generic description - - case SO_COPY_METADATA_TO_LEFT: - case SO_COPY_METADATA_TO_RIGHT: - //harmonize with synchronization.cpp::SynchronizeFolderPair::synchronizeFileInt, ect!! - { - Zstring shortNameOld = fsObj.getShortName(); - Zstring shortNameNew = fsObj.getShortName(); - if (op == SO_COPY_METADATA_TO_LEFT) - std::swap(shortNameOld, shortNameNew); - - if (shortNameOld != shortNameNew) //detected change in case - return getSyncOpDescription(op) + L"\n" + - fmtFileName(shortNameOld) + L" ->\n" + //show short name only - fmtFileName(shortNameNew); - } - //fallback: - return getSyncOpDescription(op); - - case SO_MOVE_LEFT_SOURCE: - case SO_MOVE_LEFT_TARGET: - case SO_MOVE_RIGHT_SOURCE: - case SO_MOVE_RIGHT_TARGET: - if (const FilePair* sourceFile = dynamic_cast(&fsObj)) - if (const FilePair* targetFile = dynamic_cast(FileSystemObject::retrieve(sourceFile->getMoveRef()))) - { - const bool onLeft = op == SO_MOVE_LEFT_SOURCE || op == SO_MOVE_LEFT_TARGET; - const bool isSource = op == SO_MOVE_LEFT_SOURCE || op == SO_MOVE_RIGHT_SOURCE; - - if (!isSource) - std::swap(sourceFile, targetFile); - - auto getRelName = [&](const FileSystemObject& fso, bool leftSide) { return leftSide ? fso.getRelativeName() : fso.getRelativeName(); }; - - const Zstring relSource = getRelName(*sourceFile, onLeft); - const Zstring relTarget = getRelName(*targetFile, !onLeft); - - return getSyncOpDescription(op) + L"\n" + - (EqualFilename()(beforeLast(relSource, FILE_NAME_SEPARATOR), beforeLast(relTarget, FILE_NAME_SEPARATOR)) ? //returns empty string if ch not found - //detected pure "rename" - fmtFileName(afterLast(relSource, FILE_NAME_SEPARATOR)) + L" ->\n" + //show short name only - fmtFileName(afterLast(relTarget, FILE_NAME_SEPARATOR)) : - //"move" or "move + rename" - fmtFileName(relSource) + L" ->\n" + - fmtFileName(relTarget)); - //attention: ::SetWindowText() doesn't handle tab characters correctly in combination with certain file names, so don't use them - } - break; - - case SO_UNRESOLVED_CONFLICT: - return fsObj.getSyncOpConflict(); - } - - assert(false); - return std::wstring(); -} diff --git a/file_hierarchy.h b/file_hierarchy.h deleted file mode 100644 index ce3a96b0..00000000 --- a/file_hierarchy.h +++ /dev/null @@ -1,1112 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef FILEHIERARCHY_H_INCLUDED -#define FILEHIERARCHY_H_INCLUDED - -#include -#include -#include -#include -#include -#include -#include -#include "structures.h" -#include -#include -#include "structures.h" -#include "lib/hard_filter.h" - -namespace zen -{ -struct FileDescriptor -{ - FileDescriptor() : fileIdx(), devId(), isFollowedSymlink() {} - FileDescriptor(const Int64& lastWriteTimeRawIn, - const UInt64& fileSizeIn, - const FileId& idIn, - bool isSymlink) : - lastWriteTimeRaw(lastWriteTimeRawIn), - fileSize(fileSizeIn), - fileIdx(idIn.second), - devId(idIn.first), - isFollowedSymlink(isSymlink) {} - - Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC, same semantics like time_t (== signed long) - UInt64 fileSize; - FileIndex fileIdx; // == file id: optional! (however, always set on Linux, and *generally* available on Windows) - DeviceId devId; //split into file id into components to avoid padding overhead of a std::pair! - bool isFollowedSymlink; -}; - -inline -FileId getFileId(const FileDescriptor& fd) { return FileId(fd.devId, fd.fileIdx); } - -struct LinkDescriptor -{ - LinkDescriptor() {} - explicit LinkDescriptor(const Int64& lastWriteTimeRawIn) : lastWriteTimeRaw(lastWriteTimeRawIn) {} - - Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC, same semantics like time_t (== signed long) -}; - - -enum SelectedSide -{ - LEFT_SIDE, - RIGHT_SIDE -}; - -template -struct OtherSide; - -template <> -struct OtherSide { static const SelectedSide result = RIGHT_SIDE; }; - -template <> -struct OtherSide { static const SelectedSide result = LEFT_SIDE; }; - - -template -struct SelectParam; - -template <> -struct SelectParam -{ - template - static T& get(T& left, T& right) { return left; } -}; - -template <> -struct SelectParam -{ - template - static T& get(T& left, T& right) { return right; } -}; - - -class BaseDirPair; -class DirPair; -class FilePair; -class SymlinkPair; -class FileSystemObject; - -//------------------------------------------------------------------ - -/* -ERD: - DirContainer 1 --> 0..n DirContainer - DirContainer 1 --> 0..n FileDescriptor - DirContainer 1 --> 0..n LinkDescriptor -*/ - -struct DirContainer -{ - //------------------------------------------------------------------ - typedef std::map DirList; // - typedef std::map FileList; //key: shortName - typedef std::map LinkList; // - //------------------------------------------------------------------ - - DirList dirs; - FileList files; - LinkList links; //non-followed symlinks - - //convenience - DirContainer& addSubDir(const Zstring& shortName) - { - //use C++11 emplace when available - return dirs[shortName]; //value default-construction is okay here - //return dirs.insert(std::make_pair(shortName, DirContainer())).first->second; - } - - void addSubFile(const Zstring& shortName, const FileDescriptor& fileData) - { - files.insert(std::make_pair(shortName, fileData)); - } - - void addSubLink(const Zstring& shortName, const LinkDescriptor& linkData) - { - links.insert(std::make_pair(shortName, linkData)); - } -}; - -/*------------------------------------------------------------------ - inheritance diagram: - - ObjectMgr - /|\ - | - FileSystemObject HierarchyObject - /|\ /|\ - _______________|______________ ______|______ - | | | | | - SymlinkPair FilePair DirPair BaseDirPair - -------------------------------------------------------------------*/ - -class HierarchyObject -{ - friend class DirPair; - friend class FileSystemObject; - -public: - typedef zen::FixedList SubFileVec; //MergeSides::execute() requires a structure that doesn't invalidate pointers after push_back() - typedef zen::FixedList SubLinkVec; //Note: deque<> has circular dependency in VCPP! - typedef zen::FixedList SubDirVec; - - DirPair& addSubDir(const Zstring& shortNameLeft, - const Zstring& shortNameRight, - CompareDirResult defaultCmpResult); - - template - DirPair& addSubDir(const Zstring& shortName); //dir exists on one side only - - - FilePair& addSubFile(const Zstring& shortNameLeft, - const FileDescriptor& left, //file exists on both sides - CompareFilesResult defaultCmpResult, - const Zstring& shortNameRight, - const FileDescriptor& right); - - template - FilePair& addSubFile(const Zstring& shortName, //file exists on one side only - const FileDescriptor& descr); - - SymlinkPair& addSubLink(const Zstring& shortNameLeft, - const LinkDescriptor& left, //link exists on both sides - CompareSymlinkResult defaultCmpResult, - const Zstring& shortNameRight, - const LinkDescriptor& right); - - template - SymlinkPair& addSubLink(const Zstring& shortName, //link exists on one side only - const LinkDescriptor& descr); - - const SubFileVec& refSubFiles() const { return subFiles; } - /**/ SubFileVec& refSubFiles() { return subFiles; } - - const SubLinkVec& refSubLinks() const { return subLinks; } - /**/ SubLinkVec& refSubLinks() { return subLinks; } - - const SubDirVec& refSubDirs() const { return subDirs; } - /**/ SubDirVec& refSubDirs() { return subDirs; } - - BaseDirPair& getRoot() { return root_; } - - const Zstring& getObjRelativeNamePf() const { return objRelNamePf; } //postfixed or empty! - -protected: - HierarchyObject(const Zstring& relativeNamePf, - BaseDirPair& baseDirObj) : - objRelNamePf(relativeNamePf), - root_(baseDirObj) {} - - ~HierarchyObject() {} //don't need polymorphic deletion - - virtual void flip(); - - void removeEmptyRec(); - -private: - virtual void notifySyncCfgChanged() {} - - HierarchyObject(const HierarchyObject&); //this class is referenced by it's child elements => make it non-copyable/movable! - HierarchyObject& operator=(const HierarchyObject&); // - - SubFileVec subFiles; //contained file maps - SubLinkVec subLinks; //contained symbolic link maps - SubDirVec subDirs; //contained directory maps - - Zstring objRelNamePf; //postfixed or empty - BaseDirPair& root_; -}; - -//------------------------------------------------------------------ - -class BaseDirPair : public HierarchyObject //synchronization base directory -{ -public: - BaseDirPair(const Zstring& dirPostfixedLeft, - bool dirExistsLeft, - const Zstring& dirPostfixedRight, - bool dirExistsRight, - const HardFilter::FilterRef& filter, - CompareVariant cmpVar, - size_t fileTimeTolerance) : -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4355) //"The this pointer is valid only within nonstatic member functions. It cannot be used in the initializer list for a base class." -#endif - HierarchyObject(Zstring(), *this), -#ifdef _MSC_VER -#pragma warning(pop) -#endif - filter_(filter), cmpVar_(cmpVar), fileTimeTolerance_(fileTimeTolerance), - baseDirPfL (dirPostfixedLeft ), - baseDirPfR (dirPostfixedRight), - dirExistsLeft_ (dirExistsLeft ), - dirExistsRight_(dirExistsRight) {} - - template const Zstring& getBaseDirPf() const; //base sync directory postfixed with FILE_NAME_SEPARATOR (or empty!) - static void removeEmpty(BaseDirPair& baseDir) { baseDir.removeEmptyRec(); }; //physically remove all invalid entries (where both sides are empty) recursively - - template bool isExisting() const; //status of directory existence at the time of comparison! - template void setExisting(bool value); //update after creating the directory in FFS - - //get settings which were used while creating BaseDirPair - const HardFilter& getFilter() const { return *filter_; } - CompareVariant getCompVariant() const { return cmpVar_; } - size_t getFileTimeTolerance() const { return fileTimeTolerance_; } - - virtual void flip(); - -private: - BaseDirPair(const BaseDirPair&); //this class is referenced by HierarchyObject => make it non-copyable/movable! - BaseDirPair& operator=(const BaseDirPair&); // - - HardFilter::FilterRef filter_; //filter used while scanning directory: represents sub-view of actual files! - CompareVariant cmpVar_; - size_t fileTimeTolerance_; - - Zstring baseDirPfL; //base sync dir postfixed - Zstring baseDirPfR; // - - bool dirExistsLeft_; - bool dirExistsRight_; -}; - - -template <> inline -const Zstring& BaseDirPair::getBaseDirPf() const { return baseDirPfL; } - -template <> inline -const Zstring& BaseDirPair::getBaseDirPf() const { return baseDirPfR; } - - -//get rid of shared_ptr indirection -template //target object type -class DerefIter : public std::iterator -{ -public: - DerefIter() {} - DerefIter(IterTy it) : iter(it) {} - DerefIter(const DerefIter& other) : iter(other.iter) {} - DerefIter& operator++() { ++iter; return *this; } - DerefIter& operator--() { --iter; return *this; } - DerefIter operator++(int) { DerefIter tmp(*this); operator++(); return tmp; } - DerefIter operator--(int) { DerefIter tmp(*this); operator--(); return tmp; } - inline friend ptrdiff_t operator-(const DerefIter& lhs, const DerefIter& rhs) { return lhs.iter - rhs.iter; } - inline friend bool operator==(const DerefIter& lhs, const DerefIter& rhs) { return lhs.iter == rhs.iter; } - inline friend bool operator!=(const DerefIter& lhs, const DerefIter& rhs) { return !(lhs == rhs); } - U& operator* () { return **iter; } - U* operator->() { return &** iter; } -private: - IterTy iter; -}; - -typedef std::vector> FolderComparison; //make sure pointers to sub-elements remain valid -//don't change this back to std::vector too easily: comparison uses push_back to add entries which may result in a full copy! - -DerefIter inline begin(FolderComparison& vect) { return vect.begin(); } -DerefIter inline end (FolderComparison& vect) { return vect.end (); } -DerefIter inline begin(const FolderComparison& vect) { return vect.begin(); } -DerefIter inline end (const FolderComparison& vect) { return vect.end (); } - -//------------------------------------------------------------------ -class FSObjectVisitor -{ -public: - virtual ~FSObjectVisitor() {} - virtual void visit(const FilePair& fileObj) = 0; - virtual void visit(const SymlinkPair& linkObj) = 0; - virtual void visit(const DirPair& dirObj) = 0; -}; - -//inherit from this class to allow safe random access by id instead of unsafe raw pointer -//allow for similar semantics like std::weak_ptr without having to use std::shared_ptr -template -class ObjectMgr -{ -public: - typedef ObjectMgr* ObjectId; - typedef const ObjectMgr* ObjectIdConst; - - ObjectIdConst getId() const { return this; } - /**/ ObjectId getId() { return this; } - - static const T* retrieve(ObjectIdConst id) //returns nullptr if object is not valid anymore - { - auto iter = activeObjects().find(id); - return static_cast(iter == activeObjects().end() ? nullptr : *iter); - } - static T* retrieve(ObjectId id) { return const_cast(retrieve(static_cast(id))); } - -protected: - ObjectMgr () { activeObjects().insert(this); } - ~ObjectMgr() { activeObjects().erase (this); } - -private: - ObjectMgr(const ObjectMgr& rhs); // - ObjectMgr& operator=(const ObjectMgr& rhs); //it's not well-defined what coping an objects means regarding object-identity in this context - - static zen::hash_set& activeObjects() { static zen::hash_set inst; return inst; } //external linkage (even in header file!) -}; - -//------------------------------------------------------------------ - -class FileSystemObject : public ObjectMgr -{ -public: - virtual void accept(FSObjectVisitor& visitor) const = 0; - - Zstring getObjShortName () const; //same as getShortName() but also returns value if either side is empty - Zstring getObjRelativeName() const; //same as getRelativeName() but also returns value if either side is empty - template bool isEmpty() const; - template const Zstring& getShortName() const; //case sensitive! - template Zstring getRelativeName() const; //get name relative to base sync dir without FILE_NAME_SEPARATOR prefix - template const Zstring& getBaseDirPf() const; //base sync directory postfixed with FILE_NAME_SEPARATOR - template Zstring getFullName() const; //getFullName() == getBaseDirPf() + getRelativeName() - - //comparison result - CompareFilesResult getCategory() const { return cmpResult; } - std::wstring getCatExtraDescription() const; //only filled if getCategory() == FILE_CONFLICT or FILE_DIFFERENT_METADATA - - //sync settings - SyncDirection getSyncDir() const; - void setSyncDir(SyncDirection newDir); - void setSyncDirConflict(const std::wstring& description); //set syncDir = SyncDirection::NONE + fill conflict description - - bool isActive() const; - void setActive(bool active); - - //sync operation - virtual SyncOperation testSyncOperation(SyncDirection testSyncDir) const; //semantics: "what if"! assumes "active, no conflict, no recursion (directory)! - 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 - - bool isEmpty() const; //true, if both sides are empty - - const HierarchyObject& parent() const { return parent_; } - /**/ HierarchyObject& parent() { return parent_; } - const BaseDirPair& root() const { return parent_.getRoot(); } - /**/ BaseDirPair& root() { return parent_.getRoot(); } - - //for use during init in "CompareProcess" only: - template void setCategory(); - void setCategoryConflict(const std::wstring& description); - void setCategoryDiffMetadata(const std::wstring& description); - -protected: - FileSystemObject(const Zstring& shortNameLeft, - const Zstring& shortNameRight, - HierarchyObject& parentObj, - CompareFilesResult defaultCmpResult) : - cmpResult(defaultCmpResult), - selectedForSynchronization(true), - syncDir_(SyncDirection::NONE), - shortNameLeft_(shortNameLeft), - shortNameRight_(shortNameRight), - //shortNameRight_(shortNameRight == shortNameLeft ? shortNameLeft : shortNameRight), -> strangely doesn't seem to shrink peak memory consumption at all! - parent_(parentObj) - { - parent_.notifySyncCfgChanged(); - } - - ~FileSystemObject() {} //don't need polymorphic deletion - //mustn't call parent here, it is already partially destroyed and nothing more than a pure HierarchyObject! - - virtual void flip(); - virtual void notifySyncCfgChanged() { parent().notifySyncCfgChanged(); /*propagate!*/ } - - void setSynced(const Zstring& shortName); - -private: - virtual void removeObjectL() = 0; - virtual void removeObjectR() = 0; - - //categorization - std::unique_ptr cmpResultDescr; //only filled if getCategory() == FILE_CONFLICT or FILE_DIFFERENT_METADATA - CompareFilesResult cmpResult; //although this uses 4 bytes there is currently *no* space wasted in class layout! - - bool selectedForSynchronization; - - SyncDirection syncDir_; //1 byte: optimize memory layout! - std::unique_ptr syncDirConflict; //non-empty if we have a conflict setting sync-direction - //get rid of std::wstring small string optimization (consumes 32/48 byte on VS2010 x86/x64!) - - //Note: we model *four* states with last two variables => "syncDirConflict is empty or syncDir == NONE" is a class invariant!!! - - Zstring shortNameLeft_; //slightly redundant under linux, but on windows the "same" filenames can differ in case - Zstring shortNameRight_; //use as indicator: an empty name means: not existing! - - HierarchyObject& parent_; -}; - -//------------------------------------------------------------------ - -class DirPair : public FileSystemObject, public HierarchyObject -{ - friend class HierarchyObject; - -public: - virtual void accept(FSObjectVisitor& visitor) const; - - CompareDirResult getDirCategory() const; //returns actually used subset of CompareFilesResult - - DirPair(const Zstring& shortNameLeft, //use empty shortname if "not existing" - const Zstring& shortNameRight, // - HierarchyObject& parentObj, - CompareDirResult defaultCmpResult) : - FileSystemObject(shortNameLeft, shortNameRight, parentObj, static_cast(defaultCmpResult)), - HierarchyObject(getObjRelativeName() + FILE_NAME_SEPARATOR, parentObj.getRoot()), - syncOpBuffered(SO_DO_NOTHING), - syncOpUpToDate(false) {} - - virtual SyncOperation getSyncOperation() const; - - void setSyncedTo(const Zstring& shortName); //call after sync, sets DIR_EQUAL - -private: - virtual void flip(); - virtual void removeObjectL(); - virtual void removeObjectR(); - virtual void notifySyncCfgChanged() { syncOpUpToDate = false; FileSystemObject::notifySyncCfgChanged(); HierarchyObject::notifySyncCfgChanged(); } - - mutable SyncOperation syncOpBuffered; //determining sync-op for directory may be expensive as it depends on child-objects -> buffer it - mutable bool syncOpUpToDate; // -}; - -//------------------------------------------------------------------ - -class FilePair : public FileSystemObject -{ - friend class HierarchyObject; //construction - -public: - virtual void accept(FSObjectVisitor& visitor) const; - - FilePair(const Zstring& shortNameLeft, //use empty string if "not existing" - const FileDescriptor& left, - CompareFilesResult defaultCmpResult, - const Zstring& shortNameRight, // - const FileDescriptor& right, - HierarchyObject& parentObj) : - FileSystemObject(shortNameLeft, shortNameRight, parentObj, defaultCmpResult), - dataLeft(left), - dataRight(right), - moveFileRef(nullptr) {} - - template Int64 getLastWriteTime () const; - template UInt64 getFileSize () const; - template FileId getFileId () const; - template bool isFollowedSymlink() const; - - void setMoveRef(ObjectId refId) { moveFileRef = refId; } //reference to corresponding renamed file - ObjectId getMoveRef() const { return moveFileRef; } //may be nullptr - - CompareFilesResult getFileCategory() const; - - virtual SyncOperation testSyncOperation(SyncDirection testSyncDir) const; //semantics: "what if"! assumes "active, no conflict, no recursion (directory)! - virtual SyncOperation getSyncOperation() const; - - template - void setSyncedTo(const Zstring& shortName, //call after sync, sets FILE_EQUAL - const UInt64& fileSize, - const Int64& lastWriteTimeTrg, - const Int64& lastWriteTimeSrc, - const FileId& fileIdTrg, - const FileId& fileIdSrc, - bool isSymlinkTrg, - bool isSymlinkSrc); - -private: - SyncOperation applyMoveOptimization(SyncOperation op) const; - - virtual void flip(); - virtual void removeObjectL(); - virtual void removeObjectR(); - - FileDescriptor dataLeft; - FileDescriptor dataRight; - - ObjectId moveFileRef; //optional, filled by redetermineSyncDirection() -}; - -//------------------------------------------------------------------ - -class SymlinkPair : public FileSystemObject //this class models a TRUE symbolic link, i.e. one that is NEVER dereferenced: deref-links should be directly placed in class File/DirPair -{ - friend class HierarchyObject; //construction - -public: - virtual void accept(FSObjectVisitor& visitor) const; - - template zen::Int64 getLastWriteTime() const; //write time of the link, NOT target! - - CompareSymlinkResult getLinkCategory() const; //returns actually used subset of CompareFilesResult - - SymlinkPair(const Zstring& shortNameLeft, //use empty string if "not existing" - const LinkDescriptor& left, - CompareSymlinkResult defaultCmpResult, - const Zstring& shortNameRight, //use empty string if "not existing" - const LinkDescriptor& right, - HierarchyObject& parentObj) : - FileSystemObject(shortNameLeft, shortNameRight, parentObj, static_cast(defaultCmpResult)), - dataLeft(left), - dataRight(right) {} - - template - void setSyncedTo(const Zstring& shortName, //call after sync, sets SYMLINK_EQUAL - const Int64& lastWriteTimeTrg, - const Int64& lastWriteTimeSrc); - -private: - virtual void flip(); - virtual void removeObjectL(); - virtual void removeObjectR(); - - LinkDescriptor dataLeft; - LinkDescriptor dataRight; -}; - -//------------------------------------------------------------------ - -//generic type descriptions (usecase CSV legend, sync config) -std::wstring getCategoryDescription(CompareFilesResult cmpRes); -std::wstring getSyncOpDescription (SyncOperation op); - -//item-specific type descriptions -std::wstring getCategoryDescription(const FileSystemObject& fsObj); -std::wstring getSyncOpDescription (const FileSystemObject& fsObj); - -//------------------------------------------------------------------ - - - - - - - - - - - - - - - - - - - - - - -//---------------Inline Implementation--------------------------------------------------- -//inline virtual... admittedly its use may be limited -inline void FilePair ::accept(FSObjectVisitor& visitor) const { visitor.visit(*this); } -inline void DirPair ::accept(FSObjectVisitor& visitor) const { visitor.visit(*this); } -inline void SymlinkPair::accept(FSObjectVisitor& visitor) const { visitor.visit(*this); } - - -inline -CompareFilesResult FilePair::getFileCategory() const -{ - return getCategory(); -} - - -inline -CompareDirResult DirPair::getDirCategory() const -{ - return static_cast(getCategory()); -} - - -inline -std::wstring FileSystemObject::getCatExtraDescription() const -{ - assert(getCategory() == FILE_CONFLICT || getCategory() == FILE_DIFFERENT_METADATA); - return cmpResultDescr ? *cmpResultDescr : std::wstring(); -} - - -inline -SyncDirection FileSystemObject::getSyncDir() const -{ - return syncDir_; -} - - -inline -void FileSystemObject::setSyncDir(SyncDirection newDir) -{ - syncDir_ = newDir; - syncDirConflict.reset(); - - notifySyncCfgChanged(); -} - - -inline -void FileSystemObject::setSyncDirConflict(const std::wstring& description) -{ - syncDir_ = SyncDirection::NONE; - syncDirConflict.reset(new std::wstring(description)); - - notifySyncCfgChanged(); -} - - -inline -std::wstring FileSystemObject::getSyncOpConflict() const -{ - assert(getSyncOperation() == SO_UNRESOLVED_CONFLICT); - return syncDirConflict ? *syncDirConflict : std::wstring(); -} - - -inline -bool FileSystemObject::isActive() const -{ - return selectedForSynchronization; -} - - -inline -void FileSystemObject::setActive(bool active) -{ - selectedForSynchronization = active; - notifySyncCfgChanged(); -} - - -template inline -bool FileSystemObject::isEmpty() const -{ - return SelectParam::get(shortNameLeft_, shortNameRight_).empty(); -} - - -inline -bool FileSystemObject::isEmpty() const -{ - return isEmpty() && isEmpty(); -} - - -template inline -const Zstring& FileSystemObject::getShortName() const -{ - return SelectParam::get(shortNameLeft_, shortNameRight_); //empty if not existing -} - - -template inline -Zstring FileSystemObject::getRelativeName() const -{ - return isEmpty() ? Zstring() : parent_.getObjRelativeNamePf() + getShortName(); -} - - -inline -Zstring FileSystemObject::getObjRelativeName() const -{ - return parent_.getObjRelativeNamePf() + getObjShortName(); -} - - -inline -Zstring FileSystemObject::getObjShortName() const -{ - return isEmpty() ? getShortName() : getShortName(); -} - - -template inline -Zstring FileSystemObject::getFullName() const -{ - return isEmpty() ? Zstring() : getBaseDirPf() + parent_.getObjRelativeNamePf() + getShortName(); -} - - -template inline -const Zstring& FileSystemObject::getBaseDirPf() const -{ - return root().getBaseDirPf(); -} - - -template <> inline -void FileSystemObject::removeObject() -{ - cmpResult = isEmpty() ? FILE_EQUAL : FILE_RIGHT_SIDE_ONLY; - shortNameLeft_.clear(); - removeObjectL(); - - setSyncDir(SyncDirection::NONE); //calls notifySyncCfgChanged() -} - - -template <> inline -void FileSystemObject::removeObject() -{ - cmpResult = isEmpty() ? FILE_EQUAL : FILE_LEFT_SIDE_ONLY; - shortNameRight_.clear(); - removeObjectR(); - - setSyncDir(SyncDirection::NONE); //calls notifySyncCfgChanged() -} - - -inline -void FileSystemObject::setSynced(const Zstring& shortName) -{ - assert(!isEmpty()); - shortNameRight_ = shortNameLeft_ = shortName; - cmpResult = FILE_EQUAL; - setSyncDir(SyncDirection::NONE); -} - - -template inline -void FileSystemObject::setCategory() -{ - cmpResult = res; -} -template <> void FileSystemObject::setCategory(); // -template <> void FileSystemObject::setCategory(); //not defined! -template <> void FileSystemObject::setCategory(); // -template <> void FileSystemObject::setCategory(); // - -inline -void FileSystemObject::setCategoryConflict(const std::wstring& description) -{ - cmpResult = FILE_CONFLICT; - cmpResultDescr.reset(new std::wstring(description)); -} - -inline -void FileSystemObject::setCategoryDiffMetadata(const std::wstring& description) -{ - cmpResult = FILE_DIFFERENT_METADATA; - cmpResultDescr.reset(new std::wstring(description)); -} - -inline -void FileSystemObject::flip() -{ - std::swap(shortNameLeft_, shortNameRight_); - - switch (cmpResult) - { - case FILE_LEFT_SIDE_ONLY: - cmpResult = FILE_RIGHT_SIDE_ONLY; - break; - case FILE_RIGHT_SIDE_ONLY: - cmpResult = FILE_LEFT_SIDE_ONLY; - break; - case FILE_LEFT_NEWER: - cmpResult = FILE_RIGHT_NEWER; - break; - case FILE_RIGHT_NEWER: - cmpResult = FILE_LEFT_NEWER; - break; - case FILE_DIFFERENT: - case FILE_EQUAL: - case FILE_DIFFERENT_METADATA: - case FILE_CONFLICT: - break; - } - - notifySyncCfgChanged(); -} - - -inline -void HierarchyObject::flip() -{ - for (FilePair& fileObj : refSubFiles()) - fileObj.flip(); - for (SymlinkPair& linkObj : refSubLinks()) - linkObj.flip(); - for (DirPair& dirObj : refSubDirs()) - dirObj.flip(); -} - - -inline -DirPair& HierarchyObject::addSubDir(const Zstring& shortNameLeft, - const Zstring& shortNameRight, - CompareDirResult defaultCmpResult) -{ - subDirs.emplace_back(shortNameLeft, shortNameRight, *this, defaultCmpResult); - return subDirs.back(); -} - - -template <> inline -DirPair& HierarchyObject::addSubDir(const Zstring& shortName) -{ - subDirs.emplace_back(shortName, Zstring(), *this, DIR_LEFT_SIDE_ONLY); - return subDirs.back(); -} - - -template <> inline -DirPair& HierarchyObject::addSubDir(const Zstring& shortName) -{ - subDirs.emplace_back(Zstring(), shortName, *this, DIR_RIGHT_SIDE_ONLY); - return subDirs.back(); -} - - -inline -FilePair& HierarchyObject::addSubFile(const Zstring& shortNameLeft, - const FileDescriptor& left, //file exists on both sides - CompareFilesResult defaultCmpResult, - const Zstring& shortNameRight, - const FileDescriptor& right) -{ - subFiles.emplace_back(shortNameLeft, left, defaultCmpResult, shortNameRight, right, *this); - return subFiles.back(); -} - - -template <> inline -FilePair& HierarchyObject::addSubFile(const Zstring& shortName, const FileDescriptor& descr) -{ - subFiles.emplace_back(shortName, descr, FILE_LEFT_SIDE_ONLY, Zstring(), FileDescriptor(), *this); - return subFiles.back(); -} - - -template <> inline -FilePair& HierarchyObject::addSubFile(const Zstring& shortName, const FileDescriptor& descr) -{ - subFiles.emplace_back(Zstring(), FileDescriptor(), FILE_RIGHT_SIDE_ONLY, shortName, descr, *this); - return subFiles.back(); -} - - -inline -SymlinkPair& HierarchyObject::addSubLink( - const Zstring& shortNameLeft, - const LinkDescriptor& left, //link exists on both sides - CompareSymlinkResult defaultCmpResult, - const Zstring& shortNameRight, - const LinkDescriptor& right) -{ - subLinks.emplace_back(shortNameLeft, left, defaultCmpResult, shortNameRight, right, *this); - return subLinks.back(); -} - - -template <> inline -SymlinkPair& HierarchyObject::addSubLink(const Zstring& shortName, const LinkDescriptor& descr) -{ - subLinks.emplace_back(shortName, descr, SYMLINK_LEFT_SIDE_ONLY, Zstring(), LinkDescriptor(), *this); - return subLinks.back(); -} - - -template <> inline -SymlinkPair& HierarchyObject::addSubLink(const Zstring& shortName, const LinkDescriptor& descr) -{ - subLinks.emplace_back(Zstring(), LinkDescriptor(), SYMLINK_RIGHT_SIDE_ONLY, shortName, descr, *this); - return subLinks.back(); -} - - -inline -void BaseDirPair::flip() -{ - HierarchyObject::flip(); - std::swap(baseDirPfL, baseDirPfR); - std::swap(dirExistsLeft_, dirExistsRight_); -} - - -inline -void DirPair::flip() -{ - HierarchyObject ::flip(); //call base class versions - FileSystemObject::flip(); // -} - - -inline -void DirPair::removeObjectL() -{ - for (FilePair& fileObj : refSubFiles()) - fileObj.removeObject(); - for (SymlinkPair& linkObj : refSubLinks()) - linkObj.removeObject(); - for (DirPair& dirObj : refSubDirs()) - dirObj.removeObject(); -} - - -inline -void DirPair::removeObjectR() -{ - for (FilePair& fileObj : refSubFiles()) - fileObj.removeObject(); - for (SymlinkPair& linkObj : refSubLinks()) - linkObj.removeObject(); - for (DirPair& dirObj : refSubDirs()) - dirObj.removeObject(); -} - - -template inline -bool BaseDirPair::isExisting() const -{ - return SelectParam::get(dirExistsLeft_, dirExistsRight_); -} - - -template inline -void BaseDirPair::setExisting(bool value) -{ - SelectParam::get(dirExistsLeft_, dirExistsRight_) = value; -} - - -inline -void FilePair::flip() -{ - FileSystemObject::flip(); //call base class version - std::swap(dataLeft, dataRight); -} - - -inline -void FilePair::removeObjectL() -{ - dataLeft = FileDescriptor(); -} - - -inline -void FilePair::removeObjectR() -{ - dataRight = FileDescriptor(); -} - - -template inline -zen::Int64 FilePair::getLastWriteTime() const -{ - return SelectParam::get(dataLeft, dataRight).lastWriteTimeRaw; -} - - -template inline -zen::UInt64 FilePair::getFileSize() const -{ - return SelectParam::get(dataLeft, dataRight).fileSize; -} - - -template inline -FileId FilePair::getFileId() const -{ - return FileId(SelectParam::get(dataLeft, dataRight).devId, - SelectParam::get(dataLeft, dataRight).fileIdx); -} - - -template inline -bool FilePair::isFollowedSymlink() const -{ - return SelectParam::get(dataLeft, dataRight).isFollowedSymlink; -} - - -template inline -void FilePair::setSyncedTo(const Zstring& shortName, - const UInt64& fileSize, - const Int64& lastWriteTimeTrg, - const Int64& lastWriteTimeSrc, - const FileId& fileIdTrg, - const FileId& fileIdSrc, - bool isSymlinkTrg, - bool isSymlinkSrc) -{ - //FILE_EQUAL is only allowed for same short name and file size: enforced by this method! - static const SelectedSide sideSrc = OtherSide::result; - - SelectParam::get(dataLeft, dataRight) = FileDescriptor(lastWriteTimeTrg, fileSize, fileIdTrg, isSymlinkTrg); - SelectParam::get(dataLeft, dataRight) = FileDescriptor(lastWriteTimeSrc, fileSize, fileIdSrc, isSymlinkSrc); - - moveFileRef = nullptr; - FileSystemObject::setSynced(shortName); //set FileSystemObject specific part -} - - -template inline -void SymlinkPair::setSyncedTo(const Zstring& shortName, - const Int64& lastWriteTimeTrg, - const Int64& lastWriteTimeSrc) -{ - static const SelectedSide sideSrc = OtherSide::result; - - SelectParam::get(dataLeft, dataRight) = LinkDescriptor(lastWriteTimeTrg); - SelectParam::get(dataLeft, dataRight) = LinkDescriptor(lastWriteTimeSrc); - - FileSystemObject::setSynced(shortName); //set FileSystemObject specific part -} - - -inline -void DirPair::setSyncedTo(const Zstring& shortName) -{ - FileSystemObject::setSynced(shortName); //set FileSystemObject specific part -} - - -template inline -zen::Int64 SymlinkPair::getLastWriteTime() const -{ - return SelectParam::get(dataLeft, dataRight).lastWriteTimeRaw; -} - - -inline -CompareSymlinkResult SymlinkPair::getLinkCategory() const -{ - return static_cast(getCategory()); -} - - -inline -void SymlinkPair::flip() -{ - FileSystemObject::flip(); //call base class versions - std::swap(dataLeft, dataRight); -} - - -inline -void SymlinkPair::removeObjectL() -{ - dataLeft = LinkDescriptor(); -} - - -inline -void SymlinkPair::removeObjectR() -{ - dataRight = LinkDescriptor(); -} -} - -#endif // FILEHIERARCHY_H_INCLUDED diff --git a/lib/Batch.ico b/lib/Batch.ico deleted file mode 100644 index faa2db64..00000000 Binary files a/lib/Batch.ico and /dev/null differ diff --git a/lib/FreeFileSync.ico b/lib/FreeFileSync.ico deleted file mode 100644 index 88f656ee..00000000 Binary files a/lib/FreeFileSync.ico and /dev/null differ diff --git a/lib/ShadowCopy/Shadow_Windows7.vcxproj b/lib/ShadowCopy/Shadow_Windows7.vcxproj deleted file mode 100644 index 86df3453..00000000 --- a/lib/ShadowCopy/Shadow_Windows7.vcxproj +++ /dev/null @@ -1,240 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - Windows7 - {7E217D76-90A5-4B03-A6F8-E7C3ADD22901} - ShadowDll - Win32Proj - - - - DynamicLibrary - Unicode - true - false - v120_xp - - - DynamicLibrary - Unicode - v120_xp - - - DynamicLibrary - Unicode - true - v120_xp - - - DynamicLibrary - Unicode - v120_xp - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - OBJ\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - OBJ\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - false - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - false - Shadow_$(ProjectName)_$(Platform) - Shadow_$(ProjectName)_$(Platform) - Shadow_$(ProjectName)_$(Platform) - Shadow_$(ProjectName)_$(Platform) - - - - $(IntDir)Build.html - - - Disabled - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebugDLL - - - Level4 - true - EditAndContinue - 4100;4996 - ../..;C:\Data\C++\boost - true - true - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - $(IntDir)$(TargetName).pdb - Windows - - - $(IntDir)$(TargetName).lib - MachineX86 - C:\Data\C++\Boost\stage\lib - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - X64 - - - Disabled - false - EnableFastChecks - MultiThreadedDebugDLL - - - Level4 - true - ProgramDatabase - 4100;4996 - ../..;C:\Data\C++\boost - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions) - true - true - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - $(IntDir)$(TargetName).pdb - Windows - - - $(IntDir)$(TargetName).lib - MachineX64 - C:\Data\C++\Boost\stage_x64\lib - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - MaxSpeed - true - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions) - MultiThreaded - true - - - Level4 - true - ProgramDatabase - Speed - 4100;4996 - ../..;C:\Data\C++\boost - true - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - Windows - true - true - UseLinkTimeCodeGeneration - - - $(IntDir)$(TargetName).lib - MachineX86 - C:\Data\C++\Boost\stage\lib - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - X64 - - - MaxSpeed - true - MultiThreaded - true - - - Level4 - true - ProgramDatabase - Speed - 4100;4996 - ../..;C:\Data\C++\boost - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions) - true - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - Windows - true - true - UseLinkTimeCodeGeneration - - - $(IntDir)$(TargetName).lib - MachineX64 - C:\Data\C++\Boost\stage_x64\lib - %(AdditionalDependencies) - - - - - - - - - - - - - \ No newline at end of file diff --git a/lib/ShadowCopy/shadow.cpp b/lib/ShadowCopy/shadow.cpp deleted file mode 100644 index adc7c5c2..00000000 --- a/lib/ShadowCopy/shadow.cpp +++ /dev/null @@ -1,199 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "shadow.h" -#include -#include -#include -#include -#include -#include - -#ifdef USE_SHADOW_XP -#include "xp/inc/vss.h" -#include "xp/inc/vswriter.h" -#include "xp/inc/vsbackup.h" - -#elif defined USE_SHADOW_2003 -#include "Server 2003/inc/vss.h" -#include "Server 2003/inc/vswriter.h" -#include "Server 2003/inc/vsbackup.h" - -#elif defined USE_SHADOW_WINDOWS7 -#include // -#include //part of Windows SDK for Windows 7 -#include // -#pragma comment(lib, "VssApi.lib") - -#else -#error adapt! -#endif - -using namespace zen; - - -struct shadow::ShadowData -{ - ShadowData(const ComPtr& backupComp, - const std::wstring& shadowVolume) : backupComp_(backupComp), shadowVolume_(shadowVolume) {} - - ComPtr backupComp_; - std::wstring shadowVolume_; -}; - - -namespace -{ -std::wstring formatVssError(HRESULT hr) //at least the one's from IVssBackupComponents::AddToSnapshotSet; return empty if no format found -{ - switch (hr) - { - case VSS_E_BAD_STATE: - return L"VSS_E_BAD_STATE"; - case VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED: - return L"VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED"; - case VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED: - return L"VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED"; - case VSS_E_OBJECT_NOT_FOUND: - return L"VSS_E_OBJECT_NOT_FOUND"; - case VSS_E_PROVIDER_NOT_REGISTERED: - return L"VSS_E_PROVIDER_NOT_REGISTERED"; - case VSS_E_PROVIDER_VETO: - return L"VSS_E_PROVIDER_VETO"; - case VSS_E_VOLUME_NOT_SUPPORTED: - return L"VSS_E_VOLUME_NOT_SUPPORTED"; - case VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER: - return L"VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER"; - case VSS_E_UNEXPECTED_PROVIDER_ERROR: - return L"VSS_E_UNEXPECTED_PROVIDER_ERROR"; - default: - return std::wstring(); - } -} - - -shadow::ShadowData createShadowCopy(const wchar_t* volumeName) //throw SysError -{ - ComPtr backupComp; - { - HRESULT hr = ::CreateVssBackupComponents(backupComp.init()); - if (FAILED(hr)) - { - if (hr == E_ACCESSDENIED) - throw SysError(formatComError(L"The caller does not have sufficient backup privileges or is not an administrator.", hr)); - throw SysError(formatComError(L"Error calling \"CreateVssBackupComponents\".", hr)); - } - } - - ZEN_COM_CHECK(backupComp->InitializeForBackup()); //throw SysError - - //SetContext() only required if different than the default, VSS_CTX_BACKUP; not implemented on XP!!! - //ZEN_COM_CHECK(backupComp->SetContext(VSS_CTX_BACKUP)); //throw SysError - - ZEN_COM_CHECK(backupComp->SetBackupState(false, false, VSS_BT_FULL)); //throw SysError - - - //the Shadow Copy Optimization Writer removes items it considers non-essential, - //http://msdn.microsoft.com/en-US/library/bb968827#shadow_copy_optimization_writer - //like the exclusions in HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\BackupRestore\FilesNotToSnapshot - //http://msdn.microsoft.com/en-us/library/aa819132%28v=vs.85%29.aspx - //Outlook *.ost files in particular: - //https://sourceforge.net/p/freefilesync/discussion/help/thread/722dcbfb - const VSS_ID disabledWriters[] = { { 0x4dc3bdd4, 0xab48, 0x4d07, { 0xad, 0xb0, 0x3b, 0xee, 0x29, 0x26, 0xfd, 0x7f } } }; //Shadow Copy Optimization Writer - { - HRESULT hr = backupComp->DisableWriterClasses(disabledWriters, 1); - if (FAILED(hr) && hr != E_NOTIMPL) //DisableWriterClasses() is not implemented on Windows XP, although MSDN documented otherwise! - throw SysError(formatComError(L"Error calling \"backupComp->DisableWriterClasses\".", hr)); - } - - - auto waitForComFuture = [](IVssAsync& fut) - { - ZEN_COM_CHECK(fut.Wait()); - - HRESULT hr = S_OK; - ZEN_COM_CHECK(fut.QueryStatus(&hr, nullptr)); //check if the async operation succeeded... - if (FAILED(hr)) - throw SysError(formatComError(L"Error calling \"fut->QueryStatus\".", hr)); - }; - - ComPtr gatherAsync; - ZEN_COM_CHECK(backupComp->GatherWriterMetadata(gatherAsync.init())); - waitForComFuture(*gatherAsync); //failure can happen if XP-version of VSS is used on Windows Vista (which needs at least VSS-Server2003 build) - - VSS_ID snapshotSetId = {}; - ZEN_COM_CHECK(backupComp->StartSnapshotSet(&snapshotSetId)); - ScopeGuard guardSnapShot = makeGuard([&] { backupComp->AbortBackup(); }); - //Quote: "This method must be called if a backup operation terminates after the creation of a - //shadow copy set with "StartSnapshotSet" and before "DoSnapshotSet" returns." - - VSS_ID SnapShotId = {}; - { - HRESULT hr = backupComp->AddToSnapshotSet(const_cast(volumeName), GUID_NULL, &SnapShotId); - if (FAILED(hr)) - { - if (hr == VSS_E_VOLUME_NOT_SUPPORTED) - throw SysError(L"Volume Shadow Copy Service is not supported on this volume!"); - const std::wstring vssError = formatVssError(hr); - if (!vssError.empty()) - throw SysError(L"Error calling \"backupComp->AddToSnapshotSet\": " + vssError); - else - throw SysError(formatComError(L"Error calling \"backupComp->AddToSnapshotSet\".", hr)); - } - } - - ComPtr prepareAsync; - ZEN_COM_CHECK(backupComp->PrepareForBackup(prepareAsync.init())); - waitForComFuture(*prepareAsync); - - ComPtr snapshotAsync; - ZEN_COM_CHECK(backupComp->DoSnapshotSet(snapshotAsync.init())); - guardSnapShot.dismiss(); - waitForComFuture(*snapshotAsync); - - VSS_SNAPSHOT_PROP props = {}; - ZEN_COM_CHECK(backupComp->GetSnapshotProperties(SnapShotId, &props)); - ZEN_ON_SCOPE_EXIT(::VssFreeSnapshotProperties(&props)); - - //finally: write volume name of newly created shadow copy - return shadow::ShadowData(backupComp, props.m_pwszSnapshotDeviceObject); -} - -boost::thread_specific_ptr lastErrorMessage; //use "thread_local" in C++11 -} - - -shadow::ShadowHandle shadow::createShadowCopy(const wchar_t* volumeName) -{ - try - { - ShadowData result = ::createShadowCopy(volumeName); //throw SysError - return new ShadowData(result); //shadow handle owned by caller! std::bad_alloc? - } - catch (const zen::SysError& e) - { - lastErrorMessage.reset(new std::wstring(e.toString())); - return nullptr; - } -} - - -const wchar_t* shadow::getShadowVolume(shadow::ShadowHandle handle) -{ - return handle ? handle->shadowVolume_.c_str() : nullptr; //better fail in client code than here! -} - - -void shadow::releaseShadowCopy(ShadowHandle handle) -{ - delete handle; -} - - -const wchar_t* shadow::getLastError() -{ - return !lastErrorMessage.get() ? L"" : lastErrorMessage->c_str(); -} diff --git a/lib/ShadowCopy/shadow.h b/lib/ShadowCopy/shadow.h deleted file mode 100644 index e68b2655..00000000 --- a/lib/ShadowCopy/shadow.h +++ /dev/null @@ -1,94 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef SHADOWCOPY_H_14837413434 -#define SHADOWCOPY_H_14837413434 - -#ifdef SHADOWDLL_EXPORTS -#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllexport) -#else -#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllimport) -#endif - -#include -#include - - -class IVssBackupComponents; - -namespace shadow -{ -/*-------------- - |declarations| - --------------*/ - -//COM needs to be initialized before calling any of these functions! CoInitializeEx/CoUninitialize -struct ShadowData; -typedef ShadowData* ShadowHandle; - -//volumeName *must* end with "\" -DLL_FUNCTION_DECLARATION -ShadowHandle createShadowCopy(const wchar_t* volumeName); //returns nullptr on failure! - -//release the backupHandle after shadow copy is not needed anymore! -DLL_FUNCTION_DECLARATION -void releaseShadowCopy(ShadowHandle handle); - -DLL_FUNCTION_DECLARATION -const wchar_t* getShadowVolume(ShadowHandle handle); //never fails, returns shadowVolName, never ending with "\" - -//get last error message if any of the functions above fail -DLL_FUNCTION_DECLARATION -const wchar_t* getLastError(); //no nullptr check required! -//########################################################################################## - - -/*---------- - |typedefs| - ----------*/ -typedef ShadowHandle (*FunType_createShadowCopy )(const wchar_t* volumeName); -typedef void (*FunType_releaseShadowCopy)(ShadowHandle handle); -typedef const wchar_t* (*FunType_getShadowVolume )(ShadowHandle handle); -typedef const wchar_t* (*FunType_getLastError)(); - -/*-------------- - |symbol names| - --------------*/ -//(use const pointers to ensure internal linkage) -const char funName_createShadowCopy [] = "createShadowCopy"; -const char funName_releaseShadowCopy[] = "releaseShadowCopy"; -const char funName_getShadowVolume [] = "getShadowVolume"; -const char funName_getLastError [] = "getLastError"; -/*--------------- - |library names| - ---------------*/ - -inline -const wchar_t* getDllName() -{ - // distinguish a bunch of VSS builds: we use XP, Server 2003 and Server 2008 R2 implementations... - // VSS version and compatibility overview: http://msdn.microsoft.com/en-us/library/aa384627(VS.85).aspx - - if (zen::win7OrLater()) //Windows Server 2008 R2 or Windows 7 - return zen::is64BitBuild ? - L"Shadow_Windows7_x64.dll" : - L"Shadow_Windows7_Win32.dll"; - //else if (vistaOrLater()) -> skip Windows Server 2008 and Windows Vista - // ; - else if (zen::winServer2003orLater()) //Windows Server 2003 and Windows Server 2003 R2 - return zen::is64BitBuild ? - L"Shadow_Server2003_x64.dll" : - L"Shadow_Server2003_Win32.dll"; - else //Windows XP - return zen::is64BitBuild ? - L"Shadow_XP_x64.dll" : - L"Shadow_XP_Win32.dll"; -} -} - -#undef DLL_FUNCTION_DECLARATION - -#endif //SHADOWCOPY_H_14837413434 diff --git a/lib/SyncDB.ico b/lib/SyncDB.ico deleted file mode 100644 index 9740a338..00000000 Binary files a/lib/SyncDB.ico and /dev/null differ diff --git a/lib/Thumbnail/Thumbnail.vcxproj b/lib/Thumbnail/Thumbnail.vcxproj deleted file mode 100644 index a98aadab..00000000 --- a/lib/Thumbnail/Thumbnail.vcxproj +++ /dev/null @@ -1,236 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {70394AEF-5897-4911-AFA1-82EAF0581EFA} - ShadowDll - Win32Proj - - - - DynamicLibrary - Unicode - true - v120_xp - - - DynamicLibrary - Unicode - v120_xp - - - DynamicLibrary - Unicode - true - v120_xp - - - DynamicLibrary - Unicode - v120_xp - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - false - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - false - Thumbnail_$(Platform) - Thumbnail_$(Platform) - Thumbnail_$(Platform) - Thumbnail_$(Platform) - - - - $(IntDir)Build.html - - - Disabled - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;_DEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebugDLL - - - Level4 - true - EditAndContinue - 4100;4996 - ../.. - true - true - NoExtensions - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - $(IntDir)$(TargetName).pdb - Windows - - - $(IntDir)$(TargetName).lib - MachineX86 - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - X64 - - - Disabled - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebugDLL - - - Level4 - true - ProgramDatabase - 4100;4996 - ../.. - true - true - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - $(IntDir)$(TargetName).pdb - Windows - - - $(IntDir)$(TargetName).lib - MachineX64 - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - MaxSpeed - true - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions) - MultiThreaded - true - - - Level4 - true - ProgramDatabase - 4100;4996 - Speed - ../.. - true - NoExtensions - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - Windows - true - true - UseLinkTimeCodeGeneration - - - $(IntDir)$(TargetName).lib - MachineX86 - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - X64 - - - MaxSpeed - true - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;THUMBNAIL_DLL_EXPORTS;%(PreprocessorDefinitions) - MultiThreaded - true - - - Level4 - true - ProgramDatabase - 4100;4996 - Speed - ../.. - true - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - Windows - true - true - UseLinkTimeCodeGeneration - - - $(IntDir)$(TargetName).lib - MachineX64 - %(AdditionalDependencies) - - - - - - - - - - - - - \ No newline at end of file diff --git a/lib/Thumbnail/thumbnail.cpp b/lib/Thumbnail/thumbnail.cpp deleted file mode 100644 index 53c30bc3..00000000 --- a/lib/Thumbnail/thumbnail.cpp +++ /dev/null @@ -1,486 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "thumbnail.h" -#include -#include - -#define WIN32_LEAN_AND_MEAN -#include -#include -#include - -#define STRICT_TYPED_ITEMIDS //better type safety for IDLists -#include - -#include -#include - -#include -#include -#include -#include -#include -//#include - -using namespace zen; - - -namespace -{ -thumb::ImageData* allocImageData(int width, int height) //throw SysError; return value always bound! -{ - ZEN_COM_ASSERT(width >= 0 && height >= 0); //throw SysError - - std::unique_ptr idata = make_unique(); - - idata->width = width; - idata->height = height; - idata->rgb = new unsigned char[width * height * 4]; - idata->alpha = idata->rgb + width * height * 3; - - return idata.release(); -} - -void releaseImageData_impl(const thumb::ImageData* id) -{ - if (id) - { - delete [] id->rgb; - delete id; - } -} - - -//caller takes ownership! -HICON createIconFromBitmap(HBITMAP bitmap) //throw SysError -{ - BITMAP bmpInfo = {}; - ZEN_COM_ASSERT(::GetObject(bitmap, //__in HGDIOBJ hgdiobj, - sizeof(bmpInfo), //__in int cbBuffer, - &bmpInfo)); //__out LPVOID lpvObject - //no documented extended error info - - HDC hScreenDC = ::GetDC(nullptr); - ZEN_COM_ASSERT(hScreenDC); //no documented extended error info - ZEN_ON_SCOPE_EXIT(::ReleaseDC(nullptr, hScreenDC)); - - HBITMAP bitmapMask = ::CreateCompatibleBitmap(hScreenDC, bmpInfo.bmWidth, bmpInfo.bmHeight); - ZEN_COM_ASSERT(bitmapMask); //no documented extended error info - ZEN_ON_SCOPE_EXIT(::DeleteObject(bitmapMask)); - - ICONINFO iconInfo = {}; - iconInfo.fIcon = true; - iconInfo.hbmColor = bitmap; - iconInfo.hbmMask = bitmapMask; - - HICON result = ::CreateIconIndirect(&iconInfo); - if (!result) throw SysError(formatSystemError(L"CreateIconIndirect", getLastError())); - return result; -} - - -//caller takes ownership! -thumb::ImageData* convertToImageData(HBITMAP bmp) //throw SysError -{ - //GetDIBits ???? - - BITMAP bmpInfo = {}; - ZEN_COM_ASSERT(::GetObject(bmp, //__in HGDIOBJ hgdiobj, - sizeof(BITMAP), //__in int cbBuffer, - &bmpInfo)); //__out LPVOID lpvObject - - HDC hScreenDC = ::GetDC(nullptr); - ZEN_COM_ASSERT(hScreenDC); //no documented extended error info - ZEN_ON_SCOPE_EXIT(::ReleaseDC(nullptr, hScreenDC)); - - //32-bit RGB with alpha channel support - BITMAPV5HEADER bi = {}; - bi.bV5Size = sizeof(bi); - bi.bV5Width = bmpInfo.bmWidth; - bi.bV5Height = -bmpInfo.bmHeight; //negative for top left origin - bi.bV5Planes = 1; - bi.bV5BitCount = 32; - bi.bV5Compression = BI_BITFIELDS; - bi.bV5AlphaMask = 0xFF000000; - bi.bV5RedMask = 0x00FF0000; - bi.bV5GreenMask = 0x0000FF00; - bi.bV5BlueMask = 0x000000FF; - unsigned char* bitsRgbBmp = nullptr; - - HBITMAP rgbBmp = ::CreateDIBSection(hScreenDC, //_In_ HDC hdc, - reinterpret_cast(&bi), //_In_ const BITMAPINFO *pbmi, - DIB_RGB_COLORS, //_In_ UINT iUsage, - reinterpret_cast(&bitsRgbBmp), //_Out_ VOID **ppvBits, - nullptr, //_In_ HANDLE hSection, - 0); //_In_ DWORD dwOffset - ZEN_COM_ASSERT(rgbBmp); //no documented extended error info - ZEN_ON_SCOPE_EXIT(::DeleteObject(rgbBmp);); - ZEN_COM_ASSERT(bitsRgbBmp); //check after rgbBmp is owned by us - - HDC memDCSrc = ::CreateCompatibleDC(hScreenDC); - ZEN_COM_ASSERT(memDCSrc); //no documented extended error info - ZEN_ON_SCOPE_EXIT(::DeleteDC(memDCSrc)); - - HDC memDCTrg = ::CreateCompatibleDC(hScreenDC); - ZEN_COM_ASSERT(memDCTrg); //no documented extended error info - ZEN_ON_SCOPE_EXIT(::DeleteDC(memDCTrg)); - - HGDIOBJ hgdiSrcOld = ::SelectObject(memDCSrc, bmp); - ZEN_COM_ASSERT(hgdiSrcOld); //no documented extended error info - ZEN_ON_SCOPE_EXIT(::SelectObject(memDCSrc, hgdiSrcOld)); - - HGDIOBJ hgdiTrgOld = ::SelectObject(memDCTrg, rgbBmp); - ZEN_COM_ASSERT(hgdiTrgOld); //no documented extended error info - ZEN_ON_SCOPE_EXIT(::SelectObject(memDCTrg, hgdiTrgOld)); - - if (!::BitBlt(memDCTrg, //_In_ HDC hdcDest, - 0, //_In_ int nXDest, - 0, //_In_ int nYDest, - bmpInfo.bmWidth, //_In_ int nWidth, - bmpInfo.bmHeight, //_In_ int nHeight, - memDCSrc, //_In_ HDC hdcSrc, - 0, //_In_ int nXSrc, - 0, //_In_ int nYSrc, - SRCCOPY)) //_In_ DWORD dwRop - throw SysError(formatSystemError(L"BitBlt", getLastError())); - - //CreateDIBSection: "Access to the bitmap must be synchronized. [...]. This applies to any use of the pointer to the bitmap bit values." - /*bool rv = */ - ::GdiFlush(); - - thumb::ImageData* imgOut = allocImageData(bmpInfo.bmWidth, bmpInfo.bmHeight); //throw SysError - ScopeGuard guardImgData = zen::makeGuard([&] { releaseImageData_impl(imgOut); }); - - unsigned char* rgbPtr = imgOut->rgb; - unsigned char* alphaPtr = imgOut->alpha; - - for (int i = 0; i < bmpInfo.bmWidth * bmpInfo.bmHeight; ++i) - { - unsigned char b = *bitsRgbBmp++; - unsigned char g = *bitsRgbBmp++; - unsigned char r = *bitsRgbBmp++; - unsigned char a = *bitsRgbBmp++; - - *rgbPtr++ = r; - *rgbPtr++ = g; - *rgbPtr++ = b; - *alphaPtr++ = a; - } - - guardImgData.dismiss(); - return imgOut; -} - - -//caller takes ownership! -const thumb::ImageData* getThumbnail_impl(const wchar_t* filename, int requestedSize) //throw SysError -{ - const std::wstring filenameStr(filename); - - ComPtr desktopFolder; - ZEN_COM_CHECK(::SHGetDesktopFolder(desktopFolder.init())); //throw SysError - ZEN_COM_ASSERT(desktopFolder); //throw SysError -> better safe than sorry? - - PIDLIST_RELATIVE pidlFolder = nullptr; - { - std::wstring pathName = beforeLast(filenameStr, L'\\'); - ZEN_COM_CHECK(desktopFolder->ParseDisplayName(nullptr, // [in] HWND hwnd, - nullptr, // [in] IBindCtx *pbc, - const_cast(pathName.c_str()), // [in] LPWSTR pszDisplayName, - nullptr, // [out] ULONG *pchEaten, - &pidlFolder, // [out] PIDLIST_RELATIVE* ppidl, - nullptr)); // [in, out] ULONG *pdwAttributes - } - ZEN_COM_ASSERT(pidlFolder); - ZEN_ON_SCOPE_EXIT(::ILFree(pidlFolder)); //older version: ::CoTaskMemFree - - ComPtr imageFolder; - ZEN_COM_CHECK(desktopFolder->BindToObject(pidlFolder, // [in] PCUIDLIST_RELATIVE pidl, - nullptr, // [in] IBindCtx *pbc, - IID_PPV_ARGS(imageFolder.init()))); - ZEN_COM_ASSERT(imageFolder); - - PIDLIST_RELATIVE pidImage = nullptr; - { - std::wstring shortName = afterLast(filenameStr, L'\\'); - ZEN_COM_CHECK(imageFolder->ParseDisplayName(nullptr, // [in] HWND hwnd, - nullptr, // [in] IBindCtx *pbc, - const_cast(shortName.c_str()), // [in] LPWSTR pszDisplayName, - nullptr, // [out] ULONG *pchEaten, - &pidImage, // [out] PIDLIST_RELATIVE *ppidl, - nullptr)); // [in, out] ULONG *pdwAttributes - } - ZEN_COM_ASSERT(pidImage); - ZEN_ON_SCOPE_EXIT(::ILFree(pidImage)); //older version: ::CoTaskMemFree - - ComPtr extractImage; - ZEN_COM_CHECK(imageFolder->GetUIObjectOf(nullptr, // [in] HWND hwndOwner, - 1, // [in] UINT cidl, - reinterpret_cast(&pidImage), // [in] PCUITEMID_CHILD_ARRAY apidl, - //this is where STRICT_TYPED_ITEMIDS gets us ;) - IID_IExtractImage, // [in] REFIID riid, - nullptr, // [in, out] UINT *rgfReserved, - reinterpret_cast(extractImage.init()))); // [out] void **ppv - ZEN_COM_ASSERT(extractImage); - - { - wchar_t pathBuffer[MAX_PATH]; - DWORD priority = 0; - const SIZE prgSize = { requestedSize, requestedSize }; - DWORD clrDepth = 32; //"recommended color depth" - DWORD flags = IEIFLAG_SCREEN | IEIFLAG_OFFLINE; - - ZEN_COM_CHECK(extractImage->GetLocation(pathBuffer, // [out] LPWSTR pszPathBuffer, - MAX_PATH, // [in] DWORD cchMax, - &priority, // [out] DWORD *pdwPriority, - &prgSize, // [in] const SIZE *prgSize, - clrDepth, // [in] DWORD dwRecClrDepth, - &flags)); // [in, out] DWORD *pdwFlags - } - - HBITMAP bitmap = nullptr; - ZEN_COM_CHECK(extractImage->Extract(&bitmap)); - ZEN_COM_ASSERT(bitmap); - ZEN_ON_SCOPE_EXIT(::DeleteObject(bitmap)); - - return convertToImageData(bitmap); //throw SysError, pass ownership -} - - -const bool wereVistaOrLater = vistaOrLater(); //thread-safety: init at startup - -//caller takes ownership! -const thumb::ImageData* getIconByIndex_impl(int iconIndex, thumb::IconSizeType st) //throw SysError -{ - //Note: - //- using IExtractIcon::Extract is *no* alternative, just as ::SHGetFileInfo(), it only supports small (16x16) and large (32x32) icons - //- IShellItemImageFactory::GetImage requires Vista or later - - using namespace thumb; - int requestedSize = 16; - int shilIconType = SHIL_SMALL; //16x16, size can be customized by the user. - { - if (!wereVistaOrLater && //XP doesn't have jumbo icons - (st == ICON_SIZE_128 || - st == ICON_SIZE_256)) - st = ICON_SIZE_48; - - switch (st) - { - case ICON_SIZE_16: - break; - case ICON_SIZE_32: - requestedSize = 32; - shilIconType = SHIL_LARGE; //32x32, may be 48x48 if "Use large icon" option is set in Display Properties - break; - case ICON_SIZE_48: - requestedSize = 48; - shilIconType = SHIL_EXTRALARGE; //48x48, size can be customized by the user. - break; - case ICON_SIZE_128: - requestedSize = 128; - shilIconType = SHIL_JUMBO; //256x256 pixels -> scale down! - break; - case ICON_SIZE_256: - requestedSize = 256; - shilIconType = SHIL_JUMBO; //256x256 pixels; Vista and later only - break; - } - } - - ComPtr imageList; //perf: 0,12 s only to get the image list - ZEN_COM_CHECK(::SHGetImageList(shilIconType, //__in int iImageList, - IID_PPV_ARGS(imageList.init()))); - ZEN_COM_ASSERT(imageList); - - int srcWidth = 0; - int srcHeight = 0; - ZEN_COM_CHECK(imageList->GetIconSize(&srcWidth, &srcHeight)); - - int targetWidth = srcWidth; - int targetHeight = srcHeight; - bool needDownScale = false; //scale down if required (e.g Vista Jumbo icons/user-customized icon sizes) - - ZEN_COM_ASSERT(srcWidth > 0 && srcHeight > 0 && requestedSize > 0); - - const int maxExtent = std::max(srcWidth, srcHeight); - if (requestedSize < maxExtent) - { - needDownScale = true; - targetWidth = srcWidth * requestedSize / maxExtent; - targetHeight = srcHeight * requestedSize / maxExtent; - } - - HDC hScreenDC = ::GetDC(nullptr); - ZEN_COM_ASSERT(hScreenDC); //no documented extended error info - ZEN_ON_SCOPE_EXIT(::ReleaseDC(nullptr, hScreenDC)); - - auto createRGBDib = [&](unsigned char** rawBits) -> HBITMAP - { - BITMAPINFO bi = {}; - bi.bmiHeader.biSize = sizeof(bi); - bi.bmiHeader.biWidth = targetWidth; - bi.bmiHeader.biHeight = -targetHeight; //negative for top left origin - bi.bmiHeader.biPlanes = 1; - bi.bmiHeader.biBitCount = 24; //we don't want an alpha channel - bi.bmiHeader.biCompression = BI_RGB; - - return ::CreateDIBSection(hScreenDC, //_In_ HDC hdc, - &bi, //_In_ const BITMAPINFO *pbmi, - DIB_RGB_COLORS, //_In_ UINT iUsage, - reinterpret_cast(rawBits), //_Out_ VOID **ppvBits, - nullptr, //_In_ HANDLE hSection, - 0); //_In_ DWORD dwOffset - }; - - unsigned char* bitsBlackBg = nullptr; - HBITMAP bmpBlackBg = createRGBDib(&bitsBlackBg); - ZEN_COM_ASSERT(bmpBlackBg); //no documented extended error info - ZEN_ON_SCOPE_EXIT(::DeleteObject(bmpBlackBg);); - ZEN_COM_ASSERT(bitsBlackBg); //check after bmpBlackBg is owned by us - - HDC memDC = ::CreateCompatibleDC(hScreenDC); - ZEN_COM_ASSERT(memDC); //no documented extended error info - ZEN_ON_SCOPE_EXIT(::DeleteDC(memDC)); - - HGDIOBJ hgdiOld = ::SelectObject(memDC, bmpBlackBg); - ZEN_COM_ASSERT(hgdiOld); //no documented extended error info - ZEN_ON_SCOPE_EXIT(::SelectObject(memDC, hgdiOld)); - - IMAGELISTDRAWPARAMS drawParams = {}; - drawParams.cbSize = sizeof(drawParams); - drawParams.hdcDst = memDC; - drawParams.i = iconIndex; - drawParams.rgbBk = 0x000000; //black - drawParams.fStyle = ILD_NORMAL; - //other flags: http://msdn.microsoft.com/en-us/library/windows/desktop/bb775230(v=vs.85).aspx - - if (needDownScale) - { - drawParams.fStyle |= ILD_SCALE; - drawParams.cx = targetWidth; - drawParams.cy = targetHeight; - } - - //IDO_SHGIOI_LINK does not draw properly in some cases: - //Win7: draws link overlay *twice* if SHIL_JUMBO is requested, but icon does not have this size - //XP: drawing IDO_SHGIOI_LINK generally draws corrupted icons and links - //if (addShortcutOverlay) - //{ - // int linkOverlay = ::SHGetIconOverlayIndex(nullptr, IDO_SHGIOI_LINK); //-1 on error - // if (linkOverlay != -1) - // { - // //int imgIndex = 0; - // //if (SUCCEEDED(imageList->GetOverlayImage(linkOverlay, &imgIndex))) - // //{ - // // drawParams.i = imgIndex; - // // ZEN_COM_CHECK(imageList->Draw(&drawParams)); - // //} - - // drawParams.fStyle |= INDEXTOOVERLAYMASK(linkOverlay); - // } - //} - - ZEN_COM_CHECK(imageList->Draw(&drawParams)); - - //----------------------------------------------- - //we draw the icon twice on different backgrounds to extract the alpha channel: - //- IImageList::GetIcon doesn't properly render SHIL_JUMBO for icons that don't have jumbo sizes, but IImageList::Draw does! - //- minor: each HICON consumes 3 GDI handles - //- IImageList::Draw does not reliably support alpha channel on device context (Windows XP) - //- wxBitmap created from HBITMAP is very unreliable; often drawn incorrectly by wxDC::DrawBitmap => support wxImage instead - - unsigned char* bitsWhiteBg = nullptr; - HBITMAP bmpWhiteBg = createRGBDib(&bitsWhiteBg); - ZEN_COM_ASSERT(bmpWhiteBg); //no documented extended error info - ZEN_ON_SCOPE_EXIT(::DeleteObject(bmpWhiteBg)); - ZEN_COM_ASSERT(bitsWhiteBg); //check after bmpWhiteBg is owned by us - - HGDIOBJ hgdiOld2 = ::SelectObject(memDC, bmpWhiteBg); - ZEN_COM_ASSERT(hgdiOld2); //no documented extended error info - ZEN_ON_SCOPE_EXIT(::SelectObject(memDC, hgdiOld2)); - - drawParams.rgbBk = 0xFFFFFF; //white - - ZEN_COM_CHECK(imageList->Draw(&drawParams)); - - //##################################################################################### - - //"Access to the bitmap must be synchronized. [...]. This applies to any use of the pointer to the bitmap bit values." - /*bool rv = */ - ::GdiFlush(); - - ImageData* imgOut = allocImageData(targetWidth, targetHeight); //throw SysError - ScopeGuard guardImgData = zen::makeGuard([&] { releaseImageData_impl(imgOut); }); - - unsigned char* rgbPtr = imgOut->rgb; - unsigned char* alphaPtr = imgOut->alpha; - - for (int i = 0; i < targetWidth * targetHeight; ++i) - { - unsigned char b_black = *bitsBlackBg++; - unsigned char g_black = *bitsBlackBg++; - unsigned char r_black = *bitsBlackBg++; - - unsigned char b_white = *bitsWhiteBg++; - unsigned char g_white = *bitsWhiteBg++; - unsigned char r_white = *bitsWhiteBg++; - - const int tmp = 255 + r_black - r_white + //mixed mode arithmetics! - 255 + g_black - g_white + - 255 + b_black - b_white; - unsigned char alpha = static_cast(numeric::confineCpy(tmp / 3, 0, 255)); - - auto calcColor = [&](unsigned char c_black, unsigned char c_white) - { - return static_cast(tmp == 0 ? 0 : numeric::confineCpy - (255 * (3 * (-255 + c_white + c_black) + tmp) / (2 * tmp), //mixed mode arithmetics! - 0, 255)); - }; - - *rgbPtr++ = calcColor(r_black, r_white); - *rgbPtr++ = calcColor(g_black, g_white); - *rgbPtr++ = calcColor(b_black, b_white); - *alphaPtr++ = alpha; - } - - guardImgData.dismiss(); - return imgOut; -} -} - - -const thumb::ImageData* thumb::getThumbnail(const wchar_t* filename, int requestedSize) //return 0 on failure, caller takes ownership! -{ - try - { - return getThumbnail_impl(filename, requestedSize); //throw SysError - } - catch (const SysError&) - { - return nullptr; - } -} - - -const thumb::ImageData* thumb::getIconByIndex(int iconIndex, thumb::IconSizeType st) //return 0 on failure, caller takes ownership! -{ - try - { - return getIconByIndex_impl(iconIndex, st); //throw SysError - } - catch (const SysError&) - { - return nullptr; - } -} - - -void thumb::releaseImageData(const thumb::ImageData* id) -{ - releaseImageData_impl(id); -} diff --git a/lib/Thumbnail/thumbnail.h b/lib/Thumbnail/thumbnail.h deleted file mode 100644 index 307fc7cc..00000000 --- a/lib/Thumbnail/thumbnail.h +++ /dev/null @@ -1,85 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef THUMBNAIL_DLL_HEADER_487108471324 -#define THUMBNAIL_DLL_HEADER_487108471324 - -#ifdef THUMBNAIL_DLL_EXPORTS -#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllexport) -#else -#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllimport) -#endif - -#include -//#include - -namespace thumb -{ -/* -PREREQUISITES: - -1. COM must be initialized for the current thread via ::CoInitialize(nullptr) or ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED), - but NOT ::CoInitializeEx(nullptr, COINIT_MULTITHREADED) -> internal access violation crash! -2. call ::FileIconInit() on app start to remedy obscure errors like SHELL_E_WRONG_BITDEPTH (0x80270102) - for certain file types, e.g. lnk, mpg - required on Windows 7 see http://msdn.microsoft.com/en-us/library/ms683212(v=VS.85).aspx -*/ - -/*-------------- - |declarations| - --------------*/ -struct ImageData //consider alignment! -{ - unsigned char* rgb; //rgb-byte order for use with wxImage - unsigned char* alpha; - int width; - int height; -}; - -DLL_FUNCTION_DECLARATION -const ImageData* getThumbnail(const wchar_t* filename, int requestedSize); //return nullptr on failure, release after use -//Note: not all file types support thumbnails! implement fallback to file icon! - -enum IconSizeType -{ - ICON_SIZE_16, - ICON_SIZE_32, - ICON_SIZE_48, - ICON_SIZE_128, - ICON_SIZE_256, -}; -//"iconIndex" as returned by ::SHGetFileInfo() -DLL_FUNCTION_DECLARATION -const ImageData* getIconByIndex(int iconIndex, IconSizeType st); //return nullptr on failure, release after use - -DLL_FUNCTION_DECLARATION -void releaseImageData(const ImageData* id); - - -/*---------- - |typedefs| - ----------*/ -typedef const ImageData* (*FunType_getThumbnail )(const wchar_t* filename, int requestedSize); -typedef const ImageData* (*FunType_getIconByIndex )(int iconIndex, IconSizeType st); -typedef void (*FunType_releaseImageData)(const ImageData* id); - - -/*-------------- - |symbol names| - --------------*/ -//(use const pointers to ensure internal linkage) -const char funName_getThumbnail [] = "getThumbnail"; -const char funName_getIconByIndex [] = "getIconByIndex"; -const char funName_releaseImageData[] = "releaseImageData"; - -/*--------------- - |library names| - ---------------*/ -inline const wchar_t* getDllName() { return zen::is64BitBuild ? L"Thumbnail_x64.dll" : L"Thumbnail_Win32.dll"; } -} - -#undef DLL_FUNCTION_DECLARATION - -#endif //THUMBNAIL_DLL_HEADER_487108471324 diff --git a/lib/binary.cpp b/lib/binary.cpp deleted file mode 100644 index 0e41f7a6..00000000 --- a/lib/binary.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "binary.h" -#include -#include -#include -#include -#include - -using namespace zen; - - -namespace -{ -inline -void setMinSize(std::vector& buffer, size_t minSize) -{ - if (buffer.size() < minSize) //this is similar to reserve(), but we need a "properly initialized" array here - buffer.resize(minSize); -} - -class BufferSize -{ -public: - BufferSize() : bufSize(BUFFER_SIZE_START) {} - - void inc() - { - if (bufSize < BUFFER_SIZE_MAX) - bufSize *= 2; - } - - void dec() - { - if (bufSize > BUFFER_SIZE_MIN) - bufSize /= 2; - } - - operator size_t() const { return bufSize; } - -private: - static const size_t BUFFER_SIZE_MIN = 64 * 1024; - static const size_t BUFFER_SIZE_START = 128 * 1024; //initial buffer size - static const size_t BUFFER_SIZE_MAX = 16 * 1024 * 1024; - - /*Tests on Win7 x64 show that buffer size does NOT matter if files are located on different physical disks! - Impact of buffer size when files are on same disk: - - buffer MB/s - ------------ - 64 10 - 128 19 - 512 40 - 1024 48 - 2048 56 - 4096 56 - 8192 56 - */ - - size_t bufSize; -}; - - -const std::int64_t TICKS_PER_SEC = ticksPerSec(); -} - - -bool zen::filesHaveSameContent(const Zstring& filename1, const Zstring& filename2, CompareCallback& callback) -{ - static boost::thread_specific_ptr> cpyBuf1; - static boost::thread_specific_ptr> cpyBuf2; - if (!cpyBuf1.get()) - cpyBuf1.reset(new std::vector()); - if (!cpyBuf2.get()) - cpyBuf2.reset(new std::vector()); - - std::vector& memory1 = *cpyBuf1; - std::vector& memory2 = *cpyBuf2; - - FileInput file1(filename1); //throw FileError - FileInput file2(filename2); // - - BufferSize bufferSize; - - TickVal lastDelayViolation = getTicks(); - - do - { - setMinSize(memory1, bufferSize); - setMinSize(memory2, bufferSize); - - const TickVal startTime = getTicks(); - - const size_t length1 = file1.read(&memory1[0], bufferSize); //throw FileError - const size_t length2 = file2.read(&memory2[0], bufferSize); //returns actual number of bytes read - //send progress updates immediately after reading to reliably allow speed calculations for our clients! - callback.updateCompareStatus(to(std::max(length1, length2))); - - if (length1 != length2 || ::memcmp(&memory1[0], &memory2[0], length1) != 0) - return false; - - //-------- dynamically set buffer size to keep callback interval between 100 - 500ms --------------------- - if (TICKS_PER_SEC > 0) - { - const TickVal now = getTicks(); - - const std::int64_t loopTime = dist(startTime, now) * 1000 / TICKS_PER_SEC; //unit: [ms] - if (loopTime < 100) - { - if (dist(lastDelayViolation, now) / TICKS_PER_SEC > 2) //avoid "flipping back": e.g. DVD-Roms read 32MB at once, so first read may be > 500 ms, but second one will be 0ms! - { - lastDelayViolation = now; - bufferSize.inc(); - } - } - else if (loopTime > 500) - { - lastDelayViolation = now; - bufferSize.dec(); - } - } - //------------------------------------------------------------------------------------------------ - } - while (!file1.eof()); - - if (!file2.eof()) //highly unlikely, but possible! (but then again, not in this context where both files have same size...) - return false; - - return true; -} diff --git a/lib/binary.h b/lib/binary.h deleted file mode 100644 index 8a4abe6b..00000000 --- a/lib/binary.h +++ /dev/null @@ -1,25 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef BINARY_H_INCLUDED -#define BINARY_H_INCLUDED - -#include -#include -#include - -namespace zen -{ -struct CompareCallback -{ - virtual ~CompareCallback() {} - virtual void updateCompareStatus(Int64 bytesDelta) = 0; -}; - -bool filesHaveSameContent(const Zstring& filename1, const Zstring& filename2, CompareCallback& callback); //throw FileError -} - -#endif // BINARY_H_INCLUDED diff --git a/lib/cmp_filetime.h b/lib/cmp_filetime.h deleted file mode 100644 index 4e75675b..00000000 --- a/lib/cmp_filetime.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef CMP_FILETIME_H_INCLUDED -#define CMP_FILETIME_H_INCLUDED - -#include -#include - -namespace zen -{ -//--------------------------------------------------------------------------------------------------------------- -inline -bool sameFileTime(const Int64& a, const Int64& b, size_t tolerance) -{ - if (a < b) - return b <= a + static_cast(tolerance); - else - return a <= b + static_cast(tolerance); -} -//--------------------------------------------------------------------------------------------------------------- - -//number of seconds since Jan 1st 1970 + 1 year (needn't be too precise) -static const long oneYearFromNow = wxGetUTCTime() + 365 * 24 * 3600; //init at program startup alas in *each* compilation untit -> avoid MT issues -//refactor when C++11 thread-safe static initialization is availalbe in VS (already in GCC) - -class CmpFileTime -{ -public: - enum Result - { - TIME_EQUAL, - TIME_LEFT_NEWER, - TIME_RIGHT_NEWER, - TIME_LEFT_INVALID, - TIME_RIGHT_INVALID - }; - - static Result getResult(const Int64& lhs, const Int64& rhs, size_t tolerance) - { - if (sameFileTime(lhs, rhs, tolerance)) //last write time may differ by up to 2 seconds (NTFS vs FAT32) - return TIME_EQUAL; - - //check for erroneous dates - if (lhs < 0 || lhs > oneYearFromNow) //earlier than Jan 1st 1970 or more than one year in future - return TIME_LEFT_INVALID; - - if (rhs < 0 || rhs > oneYearFromNow) - return TIME_RIGHT_INVALID; - - //regular time comparison - if (lhs < rhs) - return TIME_RIGHT_NEWER; - else - return TIME_LEFT_NEWER; - } -}; -} - -#endif // CMP_FILETIME_H_INCLUDED diff --git a/lib/db_file.cpp b/lib/db_file.cpp deleted file mode 100644 index 1c2a34f3..00000000 --- a/lib/db_file.cpp +++ /dev/null @@ -1,821 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "db_file.h" -#include -#include -#include -#include -#include -#include - -#ifdef ZEN_WIN -#include //includes "windows.h" -#include -#endif - -using namespace zen; - - -namespace -{ -//------------------------------------------------------------------------------------------------------------------------------- -const char FILE_FORMAT_DESCR[] = "FreeFileSync"; -const int DB_FORMAT_CONTAINER = 9; -const int DB_FORMAT_STREAM = 1; -//------------------------------------------------------------------------------------------------------------------------------- - -typedef std::string UniqueId; -typedef std::map DbStreams; //list of streams ordered by session UUID - -//----------------------------------------------------------------------------------- -//| ensure 32/64 bit portability: use fixed size data types only e.g. std::uint32_t | -//----------------------------------------------------------------------------------- - -template inline -Zstring getDBFilename(const BaseDirPair& baseDirObj, bool tempfile = false) -{ - //Linux and Windows builds are binary incompatible: different file id?, problem with case sensitivity? are UTC file times really compatible? - //what about endianess!? - //however 32 and 64 bit db files *are* designed to be binary compatible! - //Give db files different names. - //make sure they end with ".ffs_db". These files will be excluded from comparison -#ifdef ZEN_WIN - Zstring dbname = Zstring(Zstr("sync")) + (tempfile ? Zstr(".tmp") : Zstr("")) + SYNC_DB_FILE_ENDING; -#elif defined ZEN_LINUX || defined ZEN_MAC - //files beginning with dots are hidden e.g. in Nautilus - Zstring dbname = Zstring(Zstr(".sync")) + (tempfile ? Zstr(".tmp") : Zstr("")) + SYNC_DB_FILE_ENDING; -#endif - return baseDirObj.getBaseDirPf() + dbname; -} - -//####################################################################################################################################### - -void saveStreams(const DbStreams& streamList, const Zstring& filename) //throw FileError -{ - BinStreamOut streamOut; - - //write FreeFileSync file identifier - writeArray(streamOut, FILE_FORMAT_DESCR, sizeof(FILE_FORMAT_DESCR)); - - //save file format version - writeNumber(streamOut, DB_FORMAT_CONTAINER); - - //save stream list - writeNumber(streamOut, static_cast(streamList.size())); //number of streams, one for each sync-pair - - for (const auto& stream : streamList) - { - writeContainer(streamOut, stream.first ); - writeContainer(streamOut, stream.second); - } - - assert(!somethingExists(filename)); //orphan tmp files should be cleaned up already at this point! - saveBinStream(filename, streamOut.get()); //throw FileError - -#ifdef ZEN_WIN - //be careful to avoid CreateFile() + CREATE_ALWAYS on a hidden file -> see file_io.cpp - ::SetFileAttributes(applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_HIDDEN); //(try to) hide database file -#endif -} - - -DbStreams loadStreams(const Zstring& filename) //throw FileError, FileErrorDatabaseNotExisting -{ - try - { - BinStreamIn streamIn = loadBinStream(filename); //throw FileError - - //read FreeFileSync file identifier - char formatDescr[sizeof(FILE_FORMAT_DESCR)] = {}; - readArray(streamIn, formatDescr, sizeof(formatDescr)); //throw UnexpectedEndOfStreamError - - if (!std::equal(FILE_FORMAT_DESCR, FILE_FORMAT_DESCR + sizeof(FILE_FORMAT_DESCR), formatDescr)) - throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filename))); - - const int version = readNumber(streamIn); //throw UnexpectedEndOfStreamError - if (version != DB_FORMAT_CONTAINER) //read file format version number - throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filename))); - - DbStreams output; - - //read stream lists - size_t dbCount = readNumber(streamIn); //number of streams, one for each sync-pair - while (dbCount-- != 0) - { - //DB id of partner databases - std::string sessionID = readContainer(streamIn); //throw UnexpectedEndOfStreamError - BinaryStream stream = readContainer(streamIn); // - - output[sessionID] = std::move(stream); - } - return output; - } - catch (FileError&) - { - if (!somethingExists(filename)) //a benign(?) race condition with FileError - throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" + - replaceCpy(_("Database file %x does not yet exist."), L"%x", fmtFileName(filename))); - throw; - } - catch (UnexpectedEndOfStreamError&) - { - throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filename)); - } - catch (const std::bad_alloc& e) //still required? - { - throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filename), - _("Out of memory.") + L" " + utfCvrtTo(e.what())); - } -} - -//####################################################################################################################################### - -class StreamGenerator //for db-file back-wards compatibility we stick with two output streams until further -{ -public: - static void execute(const InSyncDir& dir, //throw FileError - const Zstring& filenameL, //used for diagnostics only - const Zstring& filenameR, - BinaryStream& streamL, - BinaryStream& streamR) - { - StreamGenerator generator; - - //PERF_START - generator.recurse(dir); - //PERF_STOP - - auto compStream = [](const BinaryStream& stream, const Zstring& filename) -> BinaryStream //throw FileError - { - try - { - /* Zlib: optimal level - testcase 1 million files - level/size [MB]/time [ms] - 0 49.54 272 (uncompressed) - 1 14.53 1013 - 2 14.13 1106 - 3 13.76 1288 - best compromise between speed and compression - 4 13.20 1526 - 5 12.73 1916 - 6 12.58 2765 - 7 12.54 3633 - 8 12.51 9032 - 9 12.50 19698 (maximal compression) */ - return compress(stream, 3); //throw ZlibInternalError - } - catch (ZlibInternalError&) - { - throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename)), L"zlib internal error"); - } - }; - - const BinaryStream tmpL = compStream(generator.outputLeft .get(), filenameL); - const BinaryStream tmpR = compStream(generator.outputRight.get(), filenameR); - const BinaryStream tmpB = compStream(generator.outputBoth .get(), filenameL + Zstr("/") + filenameR); - - BinStreamOut outL; - BinStreamOut outR; - //save format version - writeNumber(outL, DB_FORMAT_STREAM); - writeNumber(outR, DB_FORMAT_STREAM); - - //distribute "outputBoth" over left and right streams: - writeNumber(outL, true); //this side contains first part of "outputBoth" - writeNumber(outR, false); - - const size_t size1stPart = tmpB.size() / 2; - const size_t size2ndPart = tmpB.size() - size1stPart; - - writeNumber(outL, size1stPart); - writeNumber(outR, size2ndPart); - - writeArray(outL, &*tmpB.begin(), size1stPart); - writeArray(outR, &*tmpB.begin() + size1stPart, size2ndPart); - - //write streams corresponding to one side only - writeContainer(outL, tmpL); - writeContainer(outR, tmpR); - - streamL = outL.get(); - streamR = outR.get(); - } - -private: - void recurse(const InSyncDir& container) - { - writeNumber(outputBoth, static_cast(container.files.size())); - for (const auto& dbFile : container.files) - { - writeUtf8(outputBoth, dbFile.first); - writeNumber(outputBoth, dbFile.second.cmpVar); - writeNumber(outputBoth, to(dbFile.second.fileSize)); - - writeFile(outputLeft, dbFile.second.left); - writeFile(outputRight, dbFile.second.right); - } - - writeNumber(outputBoth, static_cast(container.symlinks.size())); - for (const auto& dbSymlink : container.symlinks) - { - writeUtf8(outputBoth, dbSymlink.first); - writeNumber(outputBoth, dbSymlink.second.cmpVar); - - writeLink(outputLeft, dbSymlink.second.left); - writeLink(outputRight, dbSymlink.second.right); - } - - writeNumber(outputBoth, static_cast(container.dirs.size())); - for (const auto& dbDir : container.dirs) - { - writeUtf8(outputBoth, dbDir.first); - writeNumber(outputBoth, dbDir.second.status); - - recurse(dbDir.second); - } - } - - static void writeUtf8(BinStreamOut& output, const Zstring& str) { writeContainer(output, utfCvrtTo>(str)); } - - static void writeFile(BinStreamOut& output, const InSyncDescrFile& descr) - { - writeNumber(output, to(descr.lastWriteTimeRaw)); - writeNumber(output, descr.fileId.first); - writeNumber(output, descr.fileId.second); - assert_static(sizeof(descr.fileId.first ) <= sizeof(std::uint64_t)); - assert_static(sizeof(descr.fileId.second) <= sizeof(std::uint64_t)); - } - - static void writeLink(BinStreamOut& output, const InSyncDescrLink& descr) - { - writeNumber(output, to(descr.lastWriteTimeRaw)); - } - - BinStreamOut outputLeft; //data related to one side only - BinStreamOut outputRight; // - BinStreamOut outputBoth; //data concerning both sides -}; - - -class StreamParser -{ -public: - static std::shared_ptr execute(const BinaryStream& streamL, //throw FileError - const BinaryStream& streamR, - const Zstring& filenameL, //used for diagnostics only - const Zstring& filenameR) - { - auto decompStream = [](const BinaryStream& stream, const Zstring& filename) -> BinaryStream //throw FileError - { - try - { - return decompress(stream); //throw ZlibInternalError - } - catch (ZlibInternalError&) - { - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename)), L"zlib internal error"); - } - }; - - try - { - BinStreamIn inL(streamL); - BinStreamIn inR(streamR); - - const int streamVersion = readNumber(inL); //throw UnexpectedEndOfStreamError - warn_static("remove this case after migration:") - bool migrateStreamFromOldFormat = streamVersion != DB_FORMAT_STREAM; - if (migrateStreamFromOldFormat) - inL = BinStreamIn(streamL); - else - { - const int streamVersionRef = readNumber(inR); //throw UnexpectedEndOfStreamError - if (streamVersionRef != streamVersion) //throw UnexpectedEndOfStreamError - throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filenameR), L"stream format mismatch")); - if (streamVersion != DB_FORMAT_STREAM) - throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filenameL), L"stream format")); - } - - bool has1stPartL = false; - bool has1stPartR = false; - if (migrateStreamFromOldFormat) - { - has1stPartL = readNumber(inL) != 0; //throw UnexpectedEndOfStreamError - has1stPartR = readNumber(inR) != 0; // - } - else - { - has1stPartL = readNumber(inL) != 0; //throw UnexpectedEndOfStreamError - has1stPartR = readNumber(inR) != 0; // - } - - if (has1stPartL == has1stPartR) - throw UnexpectedEndOfStreamError(); - - BinStreamIn& in1stPart = has1stPartL ? inL : inR; - BinStreamIn& in2ndPart = has1stPartL ? inR : inL; - - const size_t size1stPart = static_cast(readNumber(in1stPart)); - const size_t size2ndPart = static_cast(readNumber(in2ndPart)); - - BinaryStream tmpB; - tmpB.resize(size1stPart + size2ndPart); //throw bad_alloc - readArray(in1stPart, &*tmpB.begin(), size1stPart); - readArray(in2ndPart, &*tmpB.begin() + size1stPart, size2ndPart); - - const BinaryStream tmpL = readContainer(inL); - const BinaryStream tmpR = readContainer(inR); - - auto output = std::make_shared(InSyncDir::DIR_STATUS_IN_SYNC); - StreamParser parser(decompStream(tmpL, filenameL), - decompStream(tmpR, filenameR), - decompStream(tmpB, filenameL + Zstr("/") + filenameR), migrateStreamFromOldFormat); - parser.recurse(*output); //throw UnexpectedEndOfStreamError - return output; - } - catch (const UnexpectedEndOfStreamError&) - { - throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filenameL) + L"\n" + fmtFileName(filenameR)); - } - catch (const std::bad_alloc& e) - { - throw FileError(_("Database file is corrupt:") + L"\n" + fmtFileName(filenameL) + L"\n" + fmtFileName(filenameR), - _("Out of memory.") + L" " + utfCvrtTo(e.what())); - } - } - -private: - StreamParser(const BinaryStream& bufferL, - const BinaryStream& bufferR, - const BinaryStream& bufferB, - bool migrateStreamFromOldFormat) : - migrateStreamFromOldFormat_(migrateStreamFromOldFormat), - inputLeft (bufferL), - inputRight(bufferR), - inputBoth (bufferB) {} - - void recurse(InSyncDir& container) - { - size_t fileCount = readNumber(inputBoth); - while (fileCount-- != 0) - { - const Zstring shortName = readUtf8(inputBoth); - - if (migrateStreamFromOldFormat_) - { - const auto inSyncType = readNumber(inputBoth); - const CompareVariant cmpVar = inSyncType == 0 ? CMP_BY_CONTENT : CMP_BY_TIME_SIZE; - - auto lastWriteTimeRawL = readNumber(inputLeft); //throw UnexpectedEndOfStreamError - const UInt64 fileSize = readNumber(inputLeft); - auto devIdL = static_cast(readNumber(inputLeft)); // - auto fileIdxL = static_cast(readNumber(inputLeft)); //silence "loss of precision" compiler warnings - const InSyncDescrFile dataL = InSyncDescrFile(lastWriteTimeRawL, FileId(devIdL, fileIdxL)); - - auto lastWriteTimeRaw = readNumber(inputRight); //throw UnexpectedEndOfStreamError - readNumber(inputRight); - auto devId = static_cast(readNumber(inputRight)); // - auto fileIdx = static_cast(readNumber(inputRight)); //silence "loss of precision" compiler warnings - const InSyncDescrFile dataR = InSyncDescrFile(lastWriteTimeRaw, FileId(devId, fileIdx)); - - container.addFile(shortName, dataL, dataR, cmpVar, fileSize); - } - else - { - const auto cmpVar = static_cast(readNumber(inputBoth)); - const UInt64 fileSize = readNumber(inputBoth); - const InSyncDescrFile dataL = readFile(inputLeft); - const InSyncDescrFile dataR = readFile(inputRight); - container.addFile(shortName, dataL, dataR, cmpVar, fileSize); - } - } - - size_t linkCount = readNumber(inputBoth); - while (linkCount-- != 0) - { - const Zstring shortName = readUtf8(inputBoth); - - if (migrateStreamFromOldFormat_) - { - const CompareVariant cmpVar = CMP_BY_CONTENT; - auto lastWriteTimeRaw = readNumber(inputLeft); - readUtf8(inputLeft);//descr.targetPath = readUtf8(input); - readNumber(inputLeft);//descr.type = static_cast(readNumber(input)); - InSyncDescrLink dataL = InSyncDescrLink(lastWriteTimeRaw); - - auto lastWriteTimeRawR = readNumber(inputRight); - readUtf8(inputRight);//descr.targetPath = readUtf8(input); - readNumber(inputRight);//descr.type = static_cast(readNumber(input)); - InSyncDescrLink dataR = InSyncDescrLink(lastWriteTimeRawR); - - container.addSymlink(shortName, dataL, dataR, cmpVar); - } - else - { - const auto cmpVar = static_cast(readNumber(inputBoth)); - InSyncDescrLink dataL = readLink(inputLeft); - InSyncDescrLink dataR = readLink(inputRight); - container.addSymlink(shortName, dataL, dataR, cmpVar); - } - } - - size_t dirCount = readNumber(inputBoth); - while (dirCount-- != 0) - { - const Zstring shortName = readUtf8(inputBoth); - auto status = static_cast(readNumber(inputBoth)); - - InSyncDir& subDir = container.addDir(shortName, status); - recurse(subDir); - } - } - - static Zstring readUtf8(BinStreamIn& input) { return utfCvrtTo(readContainer>(input)); } //throw UnexpectedEndOfStreamError - - static InSyncDescrFile readFile(BinStreamIn& input) - { - //attention: order of function argument evaluation is undefined! So do it one after the other... - auto lastWriteTimeRaw = readNumber(input); //throw UnexpectedEndOfStreamError - auto devId = static_cast(readNumber(input)); // - auto fileIdx = static_cast(readNumber(input)); //silence "loss of precision" compiler warnings - return InSyncDescrFile(lastWriteTimeRaw, FileId(devId, fileIdx)); - } - - static InSyncDescrLink readLink(BinStreamIn& input) - { - auto lastWriteTimeRaw = readNumber(input); - return InSyncDescrLink(lastWriteTimeRaw); - } - - warn_static("remove after migration") - bool migrateStreamFromOldFormat_; - - BinStreamIn inputLeft; //data related to one side only - BinStreamIn inputRight; // - BinStreamIn inputBoth; //data concerning both sides -}; - -//####################################################################################################################################### - -class UpdateLastSynchronousState -{ - /* - 1. filter by file name does *not* create a new hierarchy, but merely gives a different *view* on the existing file hierarchy - => only update database entries matching this view! - 2. Symlink handling *does* create a new (asymmetric) hierarchy during comparison - => update all database entries! - */ -public: - static void execute(const BaseDirPair& baseDirObj, InSyncDir& dir) - { - UpdateLastSynchronousState updater(baseDirObj.getCompVariant(), baseDirObj.getFilter()); - updater.recurse(baseDirObj, dir); - } - -private: - UpdateLastSynchronousState(CompareVariant activeCmpVar, const HardFilter& filter) : - filter_(filter), - activeCmpVar_(activeCmpVar) {} - - void recurse(const HierarchyObject& hierObj, InSyncDir& dir) - { - process(hierObj.refSubFiles(), hierObj.getObjRelativeNamePf(), dir.files); - process(hierObj.refSubLinks(), hierObj.getObjRelativeNamePf(), dir.symlinks); - process(hierObj.refSubDirs (), hierObj.getObjRelativeNamePf(), dir.dirs); - } - - template - static V& updateItem(M& map, const Zstring& key, const V& value) - { - auto rv = map.insert(typename M::value_type(key, value)); - if (!rv.second) - { -#if defined ZEN_WIN || defined ZEN_MAC //caveat: key must be updated, if there is a change in short name case!!! - if (rv.first->first != key) - { - map.erase(rv.first); - return map.insert(typename M::value_type(key, value)).first->second; - } -#endif - rv.first->second = value; - } - return rv.first->second; - - //www.cplusplus.com claims that hint position for map<>::insert(iterator position, const value_type& val) changed with C++11 -> standard is unclear in [map.modifiers] - // => let's use the more generic and potentially less performant version above! - - /* - //efficient create or update without "default-constructible" requirement (Effective STL, item 24) - - //first check if key already exists (if yes, we're saving a value construction/destruction compared to std::map<>::insert - auto it = map.lower_bound(key); - if (it != map.end() && !(map.key_comp()(key, it->first))) - { - #if defined ZEN_WIN || defined ZEN_MAC //caveat: key might need to be updated, too, if there is a change in short name case!!! - if (it->first != key) - { - map.erase(it); //don't fiddle with decrementing "it"! - you might lose while optimizing pointlessly - return map.insert(typename M::value_type(key, value)).first->second; - } - #endif - it->second = value; - return it->second; - } - return map.insert(it, typename M::value_type(key, value))->second; - */ - } - - void process(const HierarchyObject::SubFileVec& currentFiles, const Zstring& parentRelativeNamePf, InSyncDir::FileList& dbFiles) - { - hash_set toPreserve; //referencing fixed-in-memory std::map elements - for (const FilePair& fileObj : currentFiles) - if (!fileObj.isEmpty()) - { - if (fileObj.getCategory() == FILE_EQUAL) //data in sync: write current state - { - //Caveat: If FILE_EQUAL, we *implicitly* assume equal left and right short names matching case: InSyncDir's mapping tables use short name as a key! - //This makes us silently dependent from code in algorithm.h!!! - assert(fileObj.getShortName() == fileObj.getShortName()); - //this should be taken for granted: - assert(fileObj.getFileSize() == fileObj.getFileSize()); - - //create or update new "in-sync" state - InSyncFile& file = updateItem(dbFiles, fileObj.getObjShortName(), - InSyncFile(InSyncDescrFile(fileObj.getLastWriteTime(), - fileObj.getFileId ()), - InSyncDescrFile(fileObj.getLastWriteTime(), - fileObj.getFileId ()), - activeCmpVar_, - fileObj.getFileSize())); - toPreserve.insert(&file); - } - else //not in sync: preserve last synchronous state - { - auto it = dbFiles.find(fileObj.getObjShortName()); - if (it != dbFiles.end()) - toPreserve.insert(&it->second); - } - } - - warn_static("consider temporarily excluded items due to traveral error just like a fixed file filter here!?") - //delete removed items (= "in-sync") from database - map_remove_if(dbFiles, [&](const InSyncDir::FileList::value_type& v) -> bool - { - if (toPreserve.find(&v.second) != toPreserve.end()) - return false; - //all items not existing in "currentFiles" have either been deleted meanwhile or been excluded via filter: - const Zstring& shortName = v.first; - return filter_.passFileFilter(parentRelativeNamePf + shortName); - }); - } - - void process(const HierarchyObject::SubLinkVec& currentLinks, const Zstring& parentRelativeNamePf, InSyncDir::LinkList& dbLinks) - { - hash_set toPreserve; - for (const SymlinkPair& linkObj : currentLinks) - if (!linkObj.isEmpty()) - { - if (linkObj.getLinkCategory() == SYMLINK_EQUAL) //data in sync: write current state - { - assert(linkObj.getShortName() == linkObj.getShortName()); - - //create or update new "in-sync" state - InSyncSymlink& link = updateItem(dbLinks, linkObj.getObjShortName(), - InSyncSymlink(InSyncDescrLink(linkObj.getLastWriteTime()), - InSyncDescrLink(linkObj.getLastWriteTime()), - activeCmpVar_)); - toPreserve.insert(&link); - } - else //not in sync: preserve last synchronous state - { - auto it = dbLinks.find(linkObj.getObjShortName()); - if (it != dbLinks.end()) - toPreserve.insert(&it->second); - } - } - - //delete removed items (= "in-sync") from database - map_remove_if(dbLinks, [&](const InSyncDir::LinkList::value_type& v) -> bool - { - if (toPreserve.find(&v.second) != toPreserve.end()) - return false; - //all items not existing in "currentLinks" have either been deleted meanwhile or been excluded via filter: - const Zstring& shortName = v.first; - return filter_.passFileFilter(parentRelativeNamePf + shortName); - }); - } - - void process(const HierarchyObject::SubDirVec& currentDirs, const Zstring& parentRelativeNamePf, InSyncDir::DirList& dbDirs) - { - hash_set toPreserve; - for (const DirPair& dirObj : currentDirs) - if (!dirObj.isEmpty()) - switch (dirObj.getDirCategory()) - { - case DIR_EQUAL: - { - assert(dirObj.getShortName() == dirObj.getShortName()); - - //update directory entry only (shallow), but do *not touch* exising child elements!!! - const Zstring& key = dirObj.getObjShortName(); - auto insertResult = dbDirs.insert(std::make_pair(key, InSyncDir(InSyncDir::DIR_STATUS_IN_SYNC))); //get or create - auto it = insertResult.first; - -#if defined ZEN_WIN || defined ZEN_MAC //caveat: key might need to be updated, too, if there is a change in short name case!!! - const bool alreadyExisting = !insertResult.second; - if (alreadyExisting && it->first != key) - { - auto oldValue = std::move(it->second); - dbDirs.erase(it); //don't fiddle with decrementing "it"! - you might lose while optimizing pointlessly - it = dbDirs.insert(InSyncDir::DirList::value_type(key, std::move(oldValue))).first; - } -#endif - InSyncDir& dir = it->second; - dir.status = InSyncDir::DIR_STATUS_IN_SYNC; //update immediate directory entry - toPreserve.insert(&dir); - recurse(dirObj, dir); - } - break; - - case DIR_DIFFERENT_METADATA: - //if DIR_DIFFERENT_METADATA and no old database entry yet: we have to insert a new (bogus) database entry: - //we cannot simply skip the whole directory, since sub-items might be in sync! - //Example: directories on left and right differ in case while sub-files are equal - { - //reuse last "in-sync" if available or insert strawman entry (do not try to update thereby removing child elements!!!) - InSyncDir& dir = dbDirs.insert(std::make_pair(dirObj.getObjShortName(), InSyncDir(InSyncDir::DIR_STATUS_STRAW_MAN))).first->second; - toPreserve.insert(&dir); - recurse(dirObj, dir); - } - break; - - //not in sync: reuse last synchronous state: - case DIR_LEFT_SIDE_ONLY: - case DIR_RIGHT_SIDE_ONLY: - { - auto it = dbDirs.find(dirObj.getObjShortName()); - if (it != dbDirs.end()) - { - toPreserve.insert(&it->second); - recurse(dirObj, it->second); //although existing sub-items cannot be in sync, items deleted on both sides *are* in-sync!!! - } - } - break; - } - - //delete removed items (= "in-sync") from database - map_remove_if(dbDirs, [&](const InSyncDir::DirList::value_type& v) -> bool - { - if (toPreserve.find(&v.second) != toPreserve.end()) - return false; - const Zstring& shortName = v.first; - return filter_.passDirFilter(parentRelativeNamePf + shortName, nullptr); - //if directory is not included in "currentDirs", it is either not existing anymore, in which case it should be deleted from database - //or it was excluded via filter, in which case the database entry should be preserved: - //=> all child db elements are also preserved since they are not recursed in the loop above!!! - //=> no problem with filter logic of excluding complete directory subtrees, if top folder is excluded directly! - }); - } - - const HardFilter& filter_; //filter used while scanning directory: generates view on actual files! - const CompareVariant activeCmpVar_; -}; -} - -//####################################################################################################################################### - -std::shared_ptr zen::loadLastSynchronousState(const BaseDirPair& baseDirObj) //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! -{ - const Zstring fileNameLeft = getDBFilename(baseDirObj); - const Zstring fileNameRight = getDBFilename(baseDirObj); - - if (!baseDirObj.isExisting() || - !baseDirObj.isExisting()) - { - //avoid race condition with directory existence check: reading sync.ffs_db may succeed although first dir check had failed => conflicts! - //https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3531351&group_id=234430 - const Zstring filename = !baseDirObj.isExisting() ? fileNameLeft : fileNameRight; - throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" + //it could be due to a to-be-created target directory not yet existing => FileErrorDatabaseNotExisting - replaceCpy(_("Database file %x does not yet exist."), L"%x", fmtFileName(filename))); - } - - //read file data: list of session ID + DirInfo-stream - const DbStreams streamsLeft = ::loadStreams(fileNameLeft); //throw FileError, FileErrorDatabaseNotExisting - const DbStreams streamsRight = ::loadStreams(fileNameRight); // - - //find associated session: there can be at most one session within intersection of left and right ids - for (const auto& streamLeft : streamsLeft) - { - auto itRight = streamsRight.find(streamLeft.first); - if (itRight != streamsRight.end()) - { - return StreamParser::execute(streamLeft.second, //throw FileError - itRight->second, - fileNameLeft, - fileNameRight); - } - } - throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" + - _("Database files do not share a common session.")); -} - - -void zen::saveLastSynchronousState(const BaseDirPair& baseDirObj) //throw FileError -{ - //transactional behaviour! write to tmp files first - const Zstring dbNameLeftTmp = getDBFilename(baseDirObj, true); - const Zstring dbNameRightTmp = getDBFilename(baseDirObj, true); - - const Zstring dbNameLeft = getDBFilename(baseDirObj); - const Zstring dbNameRight = getDBFilename(baseDirObj); - - //delete old tmp file, if necessary -> throws if deletion fails! - removeFile(dbNameLeftTmp); // - removeFile(dbNameRightTmp); //throw FileError - - //(try to) load old database files... - DbStreams streamsLeft; //list of session ID + DirInfo-stream - DbStreams streamsRight; - - try { streamsLeft = ::loadStreams(dbNameLeft ); } - catch (FileError&) {} - try { streamsRight = ::loadStreams(dbNameRight); } - catch (FileError&) {} - //if error occurs: just overwrite old file! User is already informed about issues right after comparing! - - //find associated session: there can be at most one session within intersection of left and right ids - auto itStreamLeftOld = streamsLeft .cend(); - auto itStreamRightOld = streamsRight.cend(); - for (auto iterLeft = streamsLeft.begin(); iterLeft != streamsLeft.end(); ++iterLeft) - { - auto iterRight = streamsRight.find(iterLeft->first); - if (iterRight != streamsRight.end()) - { - itStreamLeftOld = iterLeft; - itStreamRightOld = iterRight; - break; - } - } - - //load last synchrounous state - std::shared_ptr lastSyncState = std::make_shared(InSyncDir::DIR_STATUS_IN_SYNC); - if (itStreamLeftOld != streamsLeft .end() && - itStreamRightOld != streamsRight.end()) - try - { - lastSyncState = StreamParser::execute(itStreamLeftOld ->second, //throw FileError - itStreamRightOld->second, - dbNameLeft, - dbNameRight); - } - catch (FileError&) {} //if error occurs: just overwrite old file! User is already informed about issues right after comparing! - - //update last synchrounous state - UpdateLastSynchronousState::execute(baseDirObj, *lastSyncState); - - //serialize again - BinaryStream updatedStreamLeft; - BinaryStream updatedStreamRight; - StreamGenerator::execute(*lastSyncState, - dbNameLeft, - dbNameRight, - updatedStreamLeft, - updatedStreamRight); //throw FileError - - //check if there is some work to do at all - if (itStreamLeftOld != streamsLeft .end() && updatedStreamLeft == itStreamLeftOld ->second && - itStreamRightOld != streamsRight.end() && updatedStreamRight == itStreamRightOld->second) - return; //some users monitor the *.ffs_db file with RTS => don't touch the file if it isnt't strictly needed - - //erase old session data - if (itStreamLeftOld != streamsLeft.end()) - streamsLeft.erase(itStreamLeftOld); - if (itStreamRightOld != streamsRight.end()) - streamsRight.erase(itStreamRightOld); - - //create new session data - const std::string sessionID = zen::generateGUID(); - - streamsLeft [sessionID] = std::move(updatedStreamLeft); - streamsRight[sessionID] = std::move(updatedStreamRight); - - //write (temp-) files... - zen::ScopeGuard guardTempFileLeft = zen::makeGuard([&] {zen::removeFile(dbNameLeftTmp); }); - saveStreams(streamsLeft, dbNameLeftTmp); //throw FileError - - zen::ScopeGuard guardTempFileRight = zen::makeGuard([&] {zen::removeFile(dbNameRightTmp); }); - saveStreams(streamsRight, dbNameRightTmp); //throw FileError - - //operation finished: rename temp files -> this should work transactionally: - //if there were no write access, creation of temp files would have failed - removeFile(dbNameLeft); // - removeFile(dbNameRight); //throw FileError - renameFile(dbNameLeftTmp, dbNameLeft); // - renameFile(dbNameRightTmp, dbNameRight); // - - guardTempFileLeft. dismiss(); //no need to delete temp files anymore - guardTempFileRight.dismiss(); // -} diff --git a/lib/db_file.h b/lib/db_file.h deleted file mode 100644 index c432704d..00000000 --- a/lib/db_file.h +++ /dev/null @@ -1,102 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef DBFILE_H_834275398588021574 -#define DBFILE_H_834275398588021574 - -#include -#include "../file_hierarchy.h" - -namespace zen -{ -const Zstring SYNC_DB_FILE_ENDING = Zstr(".ffs_db"); - -struct InSyncDescrFile //subset of FileDescriptor -{ - InSyncDescrFile(const Int64& lastWriteTimeRawIn, - const FileId& idIn) : - lastWriteTimeRaw(lastWriteTimeRawIn), - fileId(idIn) {} - - Int64 lastWriteTimeRaw; - FileId fileId; // == file id: optional! (however, always set on Linux, and *generally* available on Windows) -}; - -struct InSyncDescrLink -{ - explicit InSyncDescrLink(const Int64& lastWriteTimeRawIn) : lastWriteTimeRaw(lastWriteTimeRawIn) {} - Int64 lastWriteTimeRaw; -}; - - -//artificial hierarchy of last synchronous state: -struct InSyncFile -{ - InSyncFile(const InSyncDescrFile& l, const InSyncDescrFile& r, CompareVariant cv, const UInt64& fileSizeIn) : left(l), right(r), cmpVar(cv), fileSize(fileSizeIn) {} - InSyncDescrFile left; - InSyncDescrFile right; - CompareVariant cmpVar; //the one active while finding "file in sync" - UInt64 fileSize; //file size must be identical on both sides! -}; - -struct InSyncSymlink -{ - InSyncSymlink(const InSyncDescrLink& l, const InSyncDescrLink& r, CompareVariant cv) : left(l), right(r), cmpVar(cv) {} - InSyncDescrLink left; - InSyncDescrLink right; - CompareVariant cmpVar; -}; - -struct InSyncDir -{ - //for directories we have a logical problem: we cannot have "not existent" as an indicator for - //"no last synchronous state" since this precludes child elements that may be in sync! - enum InSyncStatus - { - DIR_STATUS_IN_SYNC, - DIR_STATUS_STRAW_MAN //there is no last synchronous state, but used as container only - }; - InSyncDir(InSyncStatus statusIn) : status(statusIn) {} - - InSyncStatus status; - - //------------------------------------------------------------------ - typedef std::map DirList; // - typedef std::map FileList; // key: shortName - typedef std::map LinkList; // - //------------------------------------------------------------------ - - DirList dirs; - FileList files; - LinkList symlinks; //non-followed symlinks - - //convenience - InSyncDir& addDir(const Zstring& shortName, InSyncStatus st) - { - //use C++11 emplace when available - return dirs.insert(std::make_pair(shortName, InSyncDir(st))).first->second; - } - - void addFile(const Zstring& shortName, const InSyncDescrFile& dataL, const InSyncDescrFile& dataR, CompareVariant cmpVar, const UInt64& fileSize) - { - files.insert(std::make_pair(shortName, InSyncFile(dataL, dataR, cmpVar, fileSize))); - } - - void addSymlink(const Zstring& shortName, const InSyncDescrLink& dataL, const InSyncDescrLink& dataR, CompareVariant cmpVar) - { - symlinks.insert(std::make_pair(shortName, InSyncSymlink(dataL, dataR, cmpVar))); - } -}; - - -DEFINE_NEW_FILE_ERROR(FileErrorDatabaseNotExisting); - -std::shared_ptr loadLastSynchronousState(const BaseDirPair& baseDirObj); //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! - -void saveLastSynchronousState(const BaseDirPair& baseDirObj); //throw FileError -} - -#endif //DBFILE_H_834275398588021574 diff --git a/lib/dir_exist_async.h b/lib/dir_exist_async.h deleted file mode 100644 index 19e5f745..00000000 --- a/lib/dir_exist_async.h +++ /dev/null @@ -1,78 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef DIR_EXIST_HEADER_08173281673432158067342132467183267 -#define DIR_EXIST_HEADER_08173281673432158067342132467183267 - -#include -#include -#include -#include "process_callback.h" -#include "resolve_path.h" - -namespace zen -{ -namespace -{ -//directory existence checking may hang for non-existent network drives => run asynchronously and update UI! -//- check existence of all directories in parallel! (avoid adding up search times if multiple network drives are not reachable) -//- add reasonable time-out time! -//- avoid checking duplicate entries by design: set -std::set getExistingDirsUpdating(const std::set& dirnames, - std::set& missing, - bool allowUserInteraction, - ProcessCallback& procCallback) -{ - using namespace zen; - - missing.clear(); - - std::list>> futureInfo; - for (const Zstring& dirname : dirnames) - if (!dirname.empty()) - futureInfo.push_back(std::make_pair(dirname, async2([=]() -> bool - { -#ifdef ZEN_WIN - //1. login to network share, if necessary - loginNetworkShare(dirname, allowUserInteraction); -#endif - //2. check dir existence - return dirExists(dirname); - }))); - - std::set output; - //don't wait (almost) endlessly like win32 would on not existing network shares: - const boost::system_time endTime = boost::get_system_time() + boost::posix_time::seconds(20); //consider CD-rom insert or hard disk spin up time from sleep - - for (auto& fi : futureInfo) - { - procCallback.reportStatus(replaceCpy(_("Searching for folder %x..."), L"%x", fmtFileName(fi.first), false)); //may throw! - - while (boost::get_system_time() < endTime && - !fi.second.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL / 2))) - procCallback.requestUiRefresh(); //may throw! - - if (fi.second.is_ready() && fi.second.get()) - output.insert(fi.first); - else - missing.insert(fi.first); - } - return output; -} -} - -inline //also silences Clang "unused function" for compilation units depending from getExistingDirsUpdating() only -bool dirExistsUpdating(const Zstring& dirname, bool allowUserInteraction, ProcessCallback& procCallback) -{ - if (dirname.empty()) return false; - std::set missing; - std::set dirsEx = getExistingDirsUpdating({ dirname }, missing, allowUserInteraction, procCallback); - assert(dirsEx.empty() != missing.empty()); - return dirsEx.find(dirname) != dirsEx.end(); -} -} - -#endif //DIR_EXIST_HEADER_08173281673432158067342132467183267 diff --git a/lib/dir_lock.cpp b/lib/dir_lock.cpp deleted file mode 100644 index fb016e1e..00000000 --- a/lib/dir_lock.cpp +++ /dev/null @@ -1,685 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** -#include "dir_lock.h" -#include -#include -#include -#include -#include //includes -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef ZEN_WIN -#include -#include //includes "windows.h" -#include -#include //login sid -#include //UNLEN - -#elif defined ZEN_LINUX || defined ZEN_MAC -#include //open() -#include // -#include //getsid() -#include //kill() -#include //getpwuid_r() -#endif - -using namespace zen; -using namespace std::rel_ops; - - -namespace -{ -const int EMIT_LIFE_SIGN_INTERVAL = 5; //show life sign; unit: [s] -const int POLL_LIFE_SIGN_INTERVAL = 4; //poll for life sign; unit: [s] -const int DETECT_ABANDONED_INTERVAL = 30; //assume abandoned lock; unit: [s] - -const char LOCK_FORMAT_DESCR[] = "FreeFileSync"; -const int LOCK_FORMAT_VER = 2; //lock file format version -} - -//worker thread -class LifeSigns -{ -public: - LifeSigns(const Zstring& lockfilename) : //throw()!!! siehe SharedDirLock() - lockfilename_(lockfilename) {} //thread safety: make deep copy! - - void operator()() const //thread entry - { - try - { - while (true) - { - boost::this_thread::sleep(boost::posix_time::seconds(EMIT_LIFE_SIGN_INTERVAL)); //interruption point! - - //actual work - emitLifeSign(); //throw () - } - } - catch (const std::exception& e) //exceptions must be catched per thread - { - wxSafeShowMessage(L"FreeFileSync - " + _("An exception occurred"), utfCvrtTo(e.what()) + L" (Dirlock)"); //simple wxMessageBox won't do for threads - } - } - - void emitLifeSign() const //try to append one byte...; throw() - { - const char buffer[1] = {' '}; -#ifdef ZEN_WIN - //ATTENTION: setting file pointer IS required! => use CreateFile/GENERIC_WRITE + SetFilePointerEx! - //although CreateFile/FILE_APPEND_DATA without SetFilePointerEx works locally, it MAY NOT work on some network shares creating a 4 gig file!!! - - const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilename_).c_str(), - GENERIC_READ | GENERIC_WRITE, //use both when writing over network, see comment in file_io.cpp - FILE_SHARE_READ, - nullptr, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - nullptr); - if (fileHandle == INVALID_HANDLE_VALUE) - return; - ZEN_ON_SCOPE_EXIT(::CloseHandle(fileHandle)); - - const LARGE_INTEGER moveDist = {}; - if (!::SetFilePointerEx(fileHandle, //__in HANDLE hFile, - moveDist, //__in LARGE_INTEGER liDistanceToMove, - nullptr, //__out_opt PLARGE_INTEGER lpNewFilePointer, - FILE_END)) //__in DWORD dwMoveMethod - return; - - DWORD bytesWritten = 0; //this parameter is NOT optional: http://blogs.msdn.com/b/oldnewthing/archive/2013/04/04/10407417.aspx - if (!::WriteFile(fileHandle, //_In_ HANDLE hFile, - buffer, //_In_ LPCVOID lpBuffer, - 1, //_In_ DWORD nNumberOfBytesToWrite, - &bytesWritten, //_Out_opt_ LPDWORD lpNumberOfBytesWritten, - nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped - return; - -#elif defined ZEN_LINUX || defined ZEN_MAC - const int fileHandle = ::open(lockfilename_.c_str(), O_WRONLY | O_APPEND); - if (fileHandle == -1) - return; - ZEN_ON_SCOPE_EXIT(::close(fileHandle)); - - const ssize_t bytesWritten = ::write(fileHandle, buffer, 1); - (void)bytesWritten; -#endif - } - -private: - const Zstring lockfilename_; //thread local! atomic ref-count => binary value-type semantics! -}; - - -namespace -{ -UInt64 getLockFileSize(const Zstring& filename) //throw FileError -{ -#ifdef ZEN_WIN - WIN32_FIND_DATA fileInfo = {}; - const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(filename).c_str(), &fileInfo); - if (searchHandle != INVALID_HANDLE_VALUE) - { - ::FindClose(searchHandle); - return UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); - } - const wchar_t functionName[] = L"FindFirstFile"; -#elif defined ZEN_LINUX || defined ZEN_MAC - struct ::stat fileInfo = {}; - if (::stat(filename.c_str(), &fileInfo) == 0) //follow symbolic links - return UInt64(fileInfo.st_size); - const wchar_t functionName[] = L"stat"; -#endif - - const ErrorCode lastError = getLastError(); - const std::wstring errorMsg = replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)); - const std::wstring errorDescr = formatSystemError(functionName, lastError); - throw FileError(errorMsg, errorDescr); -} - - -Zstring deleteAbandonedLockName(const Zstring& lockfilename) //make sure to NOT change file ending! -{ - const size_t pos = lockfilename.rfind(FILE_NAME_SEPARATOR); //search from end - return pos == Zstring::npos ? Zstr("Del.") + lockfilename : - Zstring(lockfilename.c_str(), pos + 1) + //include path separator - Zstr("Del.") + - afterLast(lockfilename, FILE_NAME_SEPARATOR); //returns the whole string if ch not found -} - - -#ifdef ZEN_WIN -Zstring getLoginSid() //throw FileError -{ - HANDLE hToken = 0; - if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle, - TOKEN_ALL_ACCESS, //__in DWORD DesiredAccess, - &hToken)) //__out PHANDLE TokenHandle - throw FileError(_("Cannot get process information."), formatSystemError(L"OpenProcessToken", getLastError())); - ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken)); - - DWORD bufferSize = 0; - ::GetTokenInformation(hToken, TokenGroups, nullptr, 0, &bufferSize); - - std::vector buffer(bufferSize); - if (!::GetTokenInformation(hToken, //__in HANDLE TokenHandle, - TokenGroups, //__in TOKEN_INFORMATION_CLASS TokenInformationClass, - &buffer[0], //__out_opt LPVOID TokenInformation, - bufferSize, //__in DWORD TokenInformationLength, - &bufferSize)) //__out PDWORD ReturnLength - throw FileError(_("Cannot get process information."), formatSystemError(L"GetTokenInformation", getLastError())); - - auto groups = reinterpret_cast(&buffer[0]); - - for (DWORD i = 0; i < groups->GroupCount; ++i) - if ((groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) != 0) - { - LPTSTR sidStr = nullptr; - if (!::ConvertSidToStringSid(groups->Groups[i].Sid, //__in PSID Sid, - &sidStr)) //__out LPTSTR *StringSid - throw FileError(_("Cannot get process information."), formatSystemError(L"ConvertSidToStringSid", getLastError())); - ZEN_ON_SCOPE_EXIT(::LocalFree(sidStr)); - return sidStr; - } - throw FileError(_("Cannot get process information."), L"no login found"); //shouldn't happen -} -#endif - - -#ifdef ZEN_WIN -typedef DWORD ProcessId; -typedef DWORD SessionId; -#elif defined ZEN_LINUX || defined ZEN_MAC -typedef pid_t ProcessId; -typedef pid_t SessionId; -#endif - -//return ppid on Windows, sid on Linux/Mac, "no value" if process corresponding to "processId" is not existing -Opt getSessionId(ProcessId processId) //throw FileError -{ -#ifdef ZEN_WIN - //note: ::OpenProcess() is no alternative as it may successfully return for crashed processes! -> remark: "WaitForSingleObject" may identify this case! - HANDLE snapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, //__in DWORD dwFlags, - 0); //__in DWORD th32ProcessID - if (snapshot == INVALID_HANDLE_VALUE) - throw FileError(_("Cannot get process information."), formatSystemError(L"CreateToolhelp32Snapshot", getLastError())); - ZEN_ON_SCOPE_EXIT(::CloseHandle(snapshot)); - - PROCESSENTRY32 processEntry = {}; - processEntry.dwSize = sizeof(processEntry); - - if (!::Process32First(snapshot, //__in HANDLE hSnapshot, - &processEntry)) //__inout LPPROCESSENTRY32 lppe - throw FileError(_("Cannot get process information."), formatSystemError(L"Process32First", getLastError())); //ERROR_NO_MORE_FILES not possible - do - { - if (processEntry.th32ProcessID == processId) //yes, MSDN says this is the way: http://msdn.microsoft.com/en-us/library/windows/desktop/ms684868(v=vs.85).aspx - return processEntry.th32ParentProcessID; //parent id is stable, even if parent process has already terminated! - } - while (::Process32Next(snapshot, &processEntry)); - if (::GetLastError() != ERROR_NO_MORE_FILES) //yes, they call it "files" - throw FileError(_("Cannot get process information."), formatSystemError(L"Process32Next", getLastError())); - - return NoValue(); - -#elif defined ZEN_LINUX || defined ZEN_MAC - if (::kill(processId, 0) != 0) //sig == 0: no signal sent, just existence check - return NoValue(); - - pid_t procSid = ::getsid(processId); //NOT to be confused with "login session", e.g. not stable on OS X!!! - if (procSid == -1) - throw FileError(_("Cannot get process information."), formatSystemError(L"getsid", getLastError())); - - return procSid; -#endif -} - - -class FromCurrentProcess {}; //tag - -struct LockInformation //throw FileError -{ - explicit LockInformation(FromCurrentProcess) : - lockId(zen::generateGUID()), - sessionId(), //dummy value -#ifdef ZEN_WIN - processId(::GetCurrentProcessId()) //never fails - { - DWORD bufferSize = 0; - ::GetComputerNameEx(ComputerNameDnsFullyQualified, nullptr, &bufferSize); //get required buffer size - - std::vector buffer(bufferSize); - if (!::GetComputerNameEx(ComputerNameDnsFullyQualified, //__in COMPUTER_NAME_FORMAT NameType, - &buffer[0], //__out LPTSTR lpBuffer, - &bufferSize)) //__inout LPDWORD lpnSize - throw FileError(_("Cannot get process information."), formatSystemError(L"GetComputerNameEx", getLastError())); - - computerName = "Windows." + utfCvrtTo(&buffer[0]); - - bufferSize = UNLEN + 1; - buffer.resize(bufferSize); - if (!::GetUserName(&buffer[0], //__out LPTSTR lpBuffer, - &bufferSize)) //__inout LPDWORD lpnSize - throw FileError(_("Cannot get process information."), formatSystemError(L"GetUserName", getLastError())); - userId = utfCvrtTo(&buffer[0]); - -#elif defined ZEN_LINUX || defined ZEN_MAC - processId(::getpid()) //never fails - { - std::vector buffer(10000); - - if (::gethostname(&buffer[0], buffer.size()) != 0) - throw FileError(_("Cannot get process information."), formatSystemError(L"gethostname", getLastError())); - computerName += "Linux."; //distinguish linux/windows lock files - computerName += &buffer[0]; - - if (::getdomainname(&buffer[0], buffer.size()) != 0) - throw FileError(_("Cannot get process information."), formatSystemError(L"getdomainname", getLastError())); - computerName += "."; - computerName += &buffer[0]; - - const uid_t userIdNo = ::getuid(); //never fails - userId.assign(reinterpret_cast(&userIdNo), sizeof(userIdNo)); - - //the id alone is not very distinctive, e.g. often 1000 on Ubuntu => add name - buffer.resize(std::max(buffer.size(), ::sysconf(_SC_GETPW_R_SIZE_MAX))); //::sysconf may return long(-1) - struct passwd buffer2 = {}; - struct passwd* pwsEntry = nullptr; - if (::getpwuid_r(userIdNo, &buffer2, &buffer[0], buffer.size(), &pwsEntry) != 0) //getlogin() is deprecated and not working on Ubuntu at all!!! - throw FileError(_("Cannot get process information."), formatSystemError(L"getpwuid_r", getLastError())); - if (!pwsEntry) - throw FileError(_("Cannot get process information."), L"no login found"); //should not happen? - userId += pwsEntry->pw_name; -#endif - - Opt sessionIdTmp = getSessionId(processId); //throw FileError - if (!sessionIdTmp) - throw FileError(_("Cannot get process information."), L"no session id found"); //should not happen? - sessionId = *sessionIdTmp; - } - - explicit LockInformation(BinStreamIn& stream) //throw UnexpectedEndOfStreamError - { - char tmp[sizeof(LOCK_FORMAT_DESCR)] = {}; - readArray(stream, &tmp, sizeof(tmp)); //file format header - const int lockFileVersion = readNumber(stream); // - - if (!std::equal(std::begin(tmp), std::end(tmp), std::begin(LOCK_FORMAT_DESCR)) || - lockFileVersion != LOCK_FORMAT_VER) - throw UnexpectedEndOfStreamError(); //well, not really...!? - - lockId = readContainer(stream); // - computerName = readContainer(stream); //UnexpectedEndOfStreamError - userId = readContainer(stream); // - sessionId = static_cast(readNumber(stream)); //[!] conversion - processId = static_cast(readNumber(stream)); //[!] conversion - } - - void toStream(BinStreamOut& stream) const //throw () - { - writeArray(stream, LOCK_FORMAT_DESCR, sizeof(LOCK_FORMAT_DESCR)); - writeNumber(stream, LOCK_FORMAT_VER); - - assert_static(sizeof(processId) <= sizeof(std::uint64_t)); //ensure cross-platform compatibility! - assert_static(sizeof(sessionId) <= sizeof(std::uint64_t)); // - - writeContainer(stream, lockId); - writeContainer(stream, computerName); - writeContainer(stream, userId); - writeNumber(stream, sessionId); - writeNumber(stream, processId); - } - - std::string lockId; //16 byte GUID - a universal identifier for this lock (no matter what the path is, considering symlinks, distributed network, etc.) - - //identify local computer - std::string computerName; //format: HostName.DomainName - std::string userId; - - //identify running process - SessionId sessionId; //Windows: parent process id; Linux/OS X: session of the process, NOT the user - ProcessId processId; -}; - - -//wxGetFullHostName() is a performance killer for some users, so don't touch! - - -LockInformation retrieveLockInfo(const Zstring& lockfilename) //throw FileError -{ - BinStreamIn streamIn = loadBinStream(lockfilename); //throw FileError - try - { - return LockInformation(streamIn); //throw UnexpectedEndOfStreamError - } - catch (UnexpectedEndOfStreamError&) - { - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(lockfilename)), L"unexpected end of stream"); - } -} - - -inline -std::string retrieveLockId(const Zstring& lockfilename) //throw FileError -{ - return retrieveLockInfo(lockfilename).lockId; //throw FileError -} - - -enum ProcessStatus -{ - PROC_STATUS_NOT_RUNNING, - PROC_STATUS_RUNNING, - PROC_STATUS_ITS_US, - PROC_STATUS_CANT_TELL -}; - -ProcessStatus getProcessStatus(const LockInformation& lockInfo) //throw FileError -{ - const LockInformation localInfo((FromCurrentProcess())); //throw FileError - - if (lockInfo.computerName != localInfo.computerName || - lockInfo.userId != localInfo.userId) //another user may run a session right now! - return PROC_STATUS_CANT_TELL; //lock owned by different computer in this network - - if (lockInfo.sessionId == localInfo.sessionId && - lockInfo.processId == localInfo.processId) //obscure, but possible: deletion failed or a lock file is "stolen" and put back while the program is running - return PROC_STATUS_ITS_US; - - if (Opt sessionId = getSessionId(lockInfo.processId)) //throw FileError - return *sessionId == lockInfo.sessionId ? PROC_STATUS_RUNNING : PROC_STATUS_NOT_RUNNING; - return PROC_STATUS_NOT_RUNNING; -} - - -const std::int64_t TICKS_PER_SEC = ticksPerSec(); //= 0 on error - - -void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError -{ - const std::wstring infoMsg = replaceCpy(_("Waiting while directory is locked (%x)..."), L"%x", fmtFileName(lockfilename)); - - if (callback) - callback->reportStatus(infoMsg); - //--------------------------------------------------------------- - try - { - //convenience optimization only: if we know the owning process crashed, we needn't wait DETECT_ABANDONED_INTERVAL sec - bool lockOwnderDead = false; - std::string originalLockId; //empty if it cannot be retrieved - try - { - const LockInformation& lockInfo = retrieveLockInfo(lockfilename); //throw FileError - originalLockId = lockInfo.lockId; - switch (getProcessStatus(lockInfo)) //throw FileError - { - case PROC_STATUS_ITS_US: //since we've already passed LockAdmin, the lock file seems abandoned ("stolen"?) although it's from this process - case PROC_STATUS_NOT_RUNNING: - lockOwnderDead = true; - break; - case PROC_STATUS_RUNNING: - case PROC_STATUS_CANT_TELL: - break; - } - } - catch (FileError&) {} //logfile may be only partly written -> this is no error! - - UInt64 fileSizeOld; - TickVal lastLifeSign = getTicks(); - - while (true) - { - const TickVal now = getTicks(); - const UInt64 fileSizeNew = ::getLockFileSize(lockfilename); //throw FileError - - if (TICKS_PER_SEC <= 0 || !lastLifeSign.isValid() || !now.isValid()) - throw FileError(L"System timer failed."); //no i18n: "should" never throw ;) - - if (fileSizeNew != fileSizeOld) //received life sign from lock - { - fileSizeOld = fileSizeNew; - lastLifeSign = now; - } - - if (lockOwnderDead || //no need to wait any longer... - dist(lastLifeSign, now) / TICKS_PER_SEC > DETECT_ABANDONED_INTERVAL) - { - DirLock dummy(deleteAbandonedLockName(lockfilename), callback); //throw FileError - - //now that the lock is in place check existence again: meanwhile another process may have deleted and created a new lock! - - if (!originalLockId.empty()) - if (retrieveLockId(lockfilename) != originalLockId) //throw FileError -> since originalLockId is filled, we are not expecting errors! - return; //another process has placed a new lock, leave scope: the wait for the old lock is technically over... - - if (::getLockFileSize(lockfilename) != fileSizeOld) //throw FileError - continue; //late life sign - - removeFile(lockfilename); //throw FileError - return; - } - - //wait some time... - assert_static(1000 * POLL_LIFE_SIGN_INTERVAL % GUI_CALLBACK_INTERVAL == 0); - for (size_t i = 0; i < 1000 * POLL_LIFE_SIGN_INTERVAL / GUI_CALLBACK_INTERVAL; ++i) - { - if (callback) callback->requestUiRefresh(); - boost::this_thread::sleep(boost::posix_time::milliseconds(GUI_CALLBACK_INTERVAL)); - - if (callback) - { - //one signal missed: it's likely this is an abandoned lock => show countdown - if (dist(lastLifeSign, now) / TICKS_PER_SEC > EMIT_LIFE_SIGN_INTERVAL) - { - const int remainingSeconds = std::max(0, DETECT_ABANDONED_INTERVAL - dist(lastLifeSign, getTicks()) / TICKS_PER_SEC); - const std::wstring remSecMsg = replaceCpy(_P("1 sec", "%x sec", remainingSeconds), L"%x", numberTo(remainingSeconds)); - callback->reportStatus(infoMsg + L" " + remSecMsg); - } - else - callback->reportStatus(infoMsg); //emit a message in any case (might clear other one) - } - } - } - } - catch (FileError&) - { - if (!somethingExists(lockfilename)) //a benign(?) race condition with FileError - return; //what we are waiting for... - throw; - } -} - - -void releaseLock(const Zstring& lockfilename) //throw () -{ - try - { - removeFile(lockfilename); //throw FileError - } - catch (FileError&) {} -} - - -bool tryLock(const Zstring& lockfilename) //throw FileError -{ -#ifdef ZEN_WIN - const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilename).c_str(), - GENERIC_READ | GENERIC_WRITE, //use both when writing over network, see comment in file_io.cpp - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - nullptr, - CREATE_NEW, - FILE_ATTRIBUTE_NORMAL, - nullptr); - if (fileHandle == INVALID_HANDLE_VALUE) - { - const DWORD lastError = ::GetLastError(); - if (lastError == ERROR_FILE_EXISTS || //confirmed to be used - lastError == ERROR_ALREADY_EXISTS) //comment on msdn claims, this one is used on Windows Mobile 6 - return false; - else - throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(lockfilename)), formatSystemError(L"CreateFile", getLastError())); - } - ScopeGuard guardLockFile = zen::makeGuard([&] { removeFile(lockfilename); }); - FileOutput fileOut(fileHandle, lockfilename); //pass handle ownership - - //be careful to avoid CreateFile() + CREATE_ALWAYS on a hidden file -> see file_io.cpp - //=> we don't need it that badly //::SetFileAttributes(applyLongPathPrefix(lockfilename).c_str(), FILE_ATTRIBUTE_HIDDEN); //(try to) hide it - -#elif defined ZEN_LINUX || defined ZEN_MAC - ::umask(0); //important! -> why? - //O_EXCL contains a race condition on NFS file systems: http://linux.die.net/man/2/open - const int fileHandle = ::open(lockfilename.c_str(), O_CREAT | O_WRONLY | O_EXCL, S_IRWXU | S_IRWXG | S_IRWXO); - if (fileHandle == -1) - { - if (errno == EEXIST) - return false; - else - throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(lockfilename)), formatSystemError(L"open", getLastError())); - } - ScopeGuard guardLockFile = zen::makeGuard([&] { removeFile(lockfilename); }); - FileOutputUnbuffered fileOut(fileHandle, lockfilename); //pass handle ownership -#endif - - //write housekeeping info: user, process info, lock GUID - BinaryStream binStream; - { - BinStreamOut streamOut; - LockInformation(FromCurrentProcess()).toStream(streamOut); - binStream = streamOut.get(); - } - if (!binStream.empty()) - fileOut.write(&*binStream.begin(), binStream.size()); //throw FileError - - guardLockFile.dismiss(); //lockfile created successfully - return true; -} -} - - -class DirLock::SharedDirLock -{ -public: - SharedDirLock(const Zstring& lockfilename, DirLockCallback* callback) : //throw FileError - lockfilename_(lockfilename) - { - while (!::tryLock(lockfilename)) //throw FileError - ::waitOnDirLock(lockfilename, callback); // - - threadObj = boost::thread(LifeSigns(lockfilename)); - } - - ~SharedDirLock() - { - threadObj.interrupt(); //thread lifetime is subset of this instances's life - threadObj.join(); //we assert precondition "threadObj.joinable()"!!! - - ::releaseLock(lockfilename_); //throw () - } - -private: - SharedDirLock(const DirLock&); - SharedDirLock& operator=(const DirLock&); - - const Zstring lockfilename_; - boost::thread threadObj; -}; - - -class DirLock::LockAdmin //administrate all locks held by this process to avoid deadlock by recursion -{ -public: - static LockAdmin& instance() - { - static LockAdmin inst; - return inst; - } - - //create or retrieve a SharedDirLock - std::shared_ptr retrieve(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError - { - tidyUp(); - - //optimization: check if we already own a lock for this path - auto iterGuid = fileToGuid.find(lockfilename); - if (iterGuid != fileToGuid.end()) - if (const std::shared_ptr& activeLock = getActiveLock(iterGuid->second)) //returns null-lock if not found - return activeLock; //SharedDirLock is still active -> enlarge circle of shared ownership - - try //check based on lock GUID, deadlock prevention: "lockfilename" may be an alternative name for a lock already owned by this process - { - const std::string lockId = retrieveLockId(lockfilename); //throw FileError - if (const std::shared_ptr& activeLock = getActiveLock(lockId)) //returns null-lock if not found - { - fileToGuid[lockfilename] = lockId; //found an alias for one of our active locks - return activeLock; - } - } - catch (FileError&) {} //catch everything, let SharedDirLock constructor deal with errors, e.g. 0-sized/corrupted lock files - - //lock not owned by us => create a new one - auto newLock = std::make_shared(lockfilename, callback); //throw FileError - const std::string& newLockGuid = retrieveLockId(lockfilename); //throw FileError - - //update registry - fileToGuid[lockfilename] = newLockGuid; //throw() - guidToLock[newLockGuid] = newLock; // - - return newLock; - } - -private: - LockAdmin() {} - LockAdmin(const LockAdmin&); //=delete - LockAdmin& operator=(const LockAdmin&); //=delete - - typedef std::string UniqueId; - typedef std::map FileToGuidMap; //n:1 handle uppper/lower case correctly - typedef std::map> GuidToLockMap; //1:1 - - std::shared_ptr getActiveLock(const UniqueId& lockId) //returns null if none found - { - auto iterLock = guidToLock.find(lockId); - return iterLock != guidToLock.end() ? iterLock->second.lock() : nullptr; //try to get shared_ptr; throw() - } - - void tidyUp() //remove obsolete lock entries - { - map_remove_if(guidToLock, [ ](const GuidToLockMap::value_type& v) { return !v.second.lock(); }); - map_remove_if(fileToGuid, [&](const FileToGuidMap::value_type& v) { return guidToLock.find(v.second) == guidToLock.end(); }); - } - - FileToGuidMap fileToGuid; //lockname |-> GUID; locks can be referenced by a lockfilename or alternatively a GUID - GuidToLockMap guidToLock; //GUID |-> "shared lock ownership" -}; - - -DirLock::DirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError -{ - if (callback) - callback->reportStatus(replaceCpy(_("Creating file %x"), L"%x", fmtFileName(lockfilename))); - -#ifdef ZEN_WIN - const DWORD bufferSize = 10000; - std::vector volName(bufferSize); - if (::GetVolumePathName(lockfilename.c_str(), //__in LPCTSTR lpszFileName, - &volName[0], //__out LPTSTR lpszVolumePathName, - bufferSize)) //__in DWORD cchBufferLength - { - DWORD dt = ::GetDriveType(&volName[0]); - if (dt == DRIVE_CDROM) - return; //we don't need a lock for a CD ROM - } -#endif - - sharedLock = LockAdmin::instance().retrieve(lockfilename, callback); //throw FileError -} diff --git a/lib/dir_lock.h b/lib/dir_lock.h deleted file mode 100644 index ec2a431a..00000000 --- a/lib/dir_lock.h +++ /dev/null @@ -1,45 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** -#ifndef DIR_LOCK_H_INCLUDED -#define DIR_LOCK_H_INCLUDED - -#include -#include - -namespace zen -{ -const size_t GUI_CALLBACK_INTERVAL = 100; - -struct DirLockCallback //while waiting for the lock -{ - virtual ~DirLockCallback() {} - virtual void requestUiRefresh() = 0; //allowed to throw exceptions - virtual void reportStatus(const std::wstring& text) = 0; -}; - -/* -RAII structure to place a directory lock against other FFS processes: - - recursive locking supported, even with alternate lockfile names, e.g. via symlinks, network mounts etc. - - ownership shared between all object instances refering to a specific lock location(= GUID) - - can be copied safely and efficiently! (ref-counting) - - detects and resolves abandoned locks (instantly if lock is associated with local pc, else after 30 seconds) - - temporary locks created during abandoned lock resolution keep "lockfilename"'s extension - - race-free (Windows, almost on Linux(NFS)) - - NOT thread-safe! (1. static LockAdmin 2. directory name aliases must be resolved sequentially!) -*/ -class DirLock -{ -public: - DirLock(const Zstring& lockfilename, DirLockCallback* callback = nullptr); //throw FileError, callback only used during construction - -private: - class LockAdmin; - class SharedDirLock; - std::shared_ptr sharedLock; -}; -} - -#endif // DIR_LOCK_H_INCLUDED diff --git a/lib/error_log.h b/lib/error_log.h deleted file mode 100644 index 2971f746..00000000 --- a/lib/error_log.h +++ /dev/null @@ -1,43 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef ERROR_LOG_89734181783491324134 -#define ERROR_LOG_89734181783491324134 - -#include -#include -#include -#include "ffs_paths.h" - - -namespace zen -{ -//write error message to a file (even with corrupted stack)- call in desperate situations when no other means of error handling is available -void logError(const std::string& msg); //throw() - - - - - - - - - -//##################### implementation ############################ -inline -void logError(const std::string& msg) //throw() -{ - assert(false); //this is stuff we like to debug - const std::string logEntry = "[" + formatTime(FORMAT_DATE) + " "+ formatTime(FORMAT_TIME) + "] " + msg; - try - { - saveBinStream(getConfigDir() + Zstr("LastError.log"), logEntry); //throw FileError - } - catch (const FileError&) {} -} -} - -#endif //ERROR_LOG_89734181783491324134 diff --git a/lib/ffs_paths.cpp b/lib/ffs_paths.cpp deleted file mode 100644 index 5c775d3e..00000000 --- a/lib/ffs_paths.cpp +++ /dev/null @@ -1,144 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "ffs_paths.h" -#include -#include -#include -#include - -#ifdef ZEN_MAC -#include -#include -#include -//keep in .cpp file to not pollute global namespace! e.g. with UInt64: -#include //LSFindApplicationForInfo -#endif - -using namespace zen; - - -namespace -{ -#if defined ZEN_WIN || defined ZEN_LINUX -inline -Zstring getExecutableDir() //directory containing executable WITH path separator at end -{ - return appendSeparator(beforeLast(utfCvrtTo(wxStandardPaths::Get().GetExecutablePath()), FILE_NAME_SEPARATOR)); -} -#endif - -#ifdef ZEN_WIN -inline -Zstring getInstallDir() //root install directory WITH path separator at end -{ - return appendSeparator(beforeLast(beforeLast(getExecutableDir(), FILE_NAME_SEPARATOR), FILE_NAME_SEPARATOR)); -} -#endif - - -#ifdef ZEN_WIN -inline -bool isPortableVersion() { return !fileExists(getInstallDir() + L"uninstall.exe"); } //this check is a bit lame... -#elif defined ZEN_LINUX -inline -bool isPortableVersion() { return !endsWith(getExecutableDir(), "/bin/"); } //this check is a bit lame... -#endif -} - - -bool zen::manualProgramUpdateRequired() -{ -#if defined ZEN_WIN || defined ZEN_MAC - return true; -#elif defined ZEN_LINUX - return isPortableVersion(); //locally installed version is updated by system -#endif -} - - -Zstring zen::getResourceDir() -{ - //make independent from wxWidgets global variable "appname"; support being called by RealtimeSync - auto appName = wxTheApp->GetAppName(); - wxTheApp->SetAppName(L"FreeFileSync"); - ZEN_ON_SCOPE_EXIT(wxTheApp->SetAppName(appName)); - -#ifdef ZEN_WIN - return getInstallDir(); -#elif defined ZEN_LINUX - if (isPortableVersion()) - return getExecutableDir(); - else //use OS' standard paths - return appendSeparator(toZ(wxStandardPathsBase::Get().GetResourcesDir())); -#elif defined ZEN_MAC - return appendSeparator(toZ(wxStandardPathsBase::Get().GetResourcesDir())); //if packaged, used "Contents/Resources", else the executable directory -#endif -} - - -Zstring zen::getConfigDir() -{ - //make independent from wxWidgets global variable "appname"; support being called by RealtimeSync - auto appName = wxTheApp->GetAppName(); - wxTheApp->SetAppName(L"FreeFileSync"); - ZEN_ON_SCOPE_EXIT(wxTheApp->SetAppName(appName)); - -#ifdef ZEN_WIN - if (isPortableVersion()) - return getInstallDir(); -#elif defined ZEN_LINUX - if (isPortableVersion()) - return getExecutableDir(); -#elif defined ZEN_MAC - //portable apps do not seem common on OS - fine with me: http://theocacao.com/document.page/319 -#endif - //use OS' standard paths - Zstring userDirectory = toZ(wxStandardPathsBase::Get().GetUserDataDir()); - - if (!dirExists(userDirectory)) - try - { - makeDirectory(userDirectory); //throw FileError - } - catch (const FileError&) {} - - return appendSeparator(userDirectory); -} - - -//this function is called by RealtimeSync!!! -Zstring zen::getFreeFileSyncLauncher() -{ -#ifdef ZEN_WIN - return getInstallDir() + Zstr("FreeFileSync.exe"); - -#elif defined ZEN_LINUX - return getExecutableDir() + Zstr("FreeFileSync"); - -#elif defined ZEN_MAC - CFURLRef appURL = nullptr; - ZEN_ON_SCOPE_EXIT(if (appURL) ::CFRelease(appURL)); - - if (::LSFindApplicationForInfo(kLSUnknownCreator, // OSType inCreator, - CFSTR("net.SourceForge.FreeFileSync"),//CFStringRef inBundleID, - nullptr, //CFStringRef inName, - nullptr, //FSRef *outAppRef, - &appURL) == noErr) //CFURLRef *outAppURL - if (appURL) - if (CFURLRef absUrl = ::CFURLCopyAbsoluteURL(appURL)) - { - ZEN_ON_SCOPE_EXIT(::CFRelease(absUrl)); - - if (CFStringRef path = ::CFURLCopyFileSystemPath(absUrl, kCFURLPOSIXPathStyle)) - { - ZEN_ON_SCOPE_EXIT(::CFRelease(path)); - return appendSeparator(osx::cfStringToZstring(path)) + "Contents/MacOS/FreeFileSync"; - } - } - return Zstr("./FreeFileSync"); //fallback: at least give some hint... -#endif -} diff --git a/lib/ffs_paths.h b/lib/ffs_paths.h deleted file mode 100644 index 28516a3f..00000000 --- a/lib/ffs_paths.h +++ /dev/null @@ -1,25 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef STANDARDPATHS_H_84275908342534253425 -#define STANDARDPATHS_H_84275908342534253425 - -#include - -namespace zen -{ -//------------------------------------------------------------------------------ -//global program directories -//------------------------------------------------------------------------------ -Zstring getResourceDir(); //resource directory WITH path separator at end -Zstring getConfigDir (); //config directory WITH path separator at end -//------------------------------------------------------------------------------ - -Zstring getFreeFileSyncLauncher(); //full path to application launcher C:\...\FreeFileSync.exe -bool manualProgramUpdateRequired(); -} - -#endif //STANDARDPATHS_H_84275908342534253425 diff --git a/lib/generate_logfile.h b/lib/generate_logfile.h deleted file mode 100644 index ff97b63a..00000000 --- a/lib/generate_logfile.h +++ /dev/null @@ -1,181 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef GEN_LOGFILE_H_93172643216748973216458732165415 -#define GEN_LOGFILE_H_93172643216748973216458732165415 - -#include -#include -#include -#include -#include "ffs_paths.h" - - -namespace zen -{ -struct SummaryInfo -{ - std::wstring jobName; //may be empty - std::wstring finalStatus; - int itemsSynced; - Int64 dataSynced; - int itemsTotal; - Int64 dataTotal; - long totalTime; //unit: [sec] -}; - -void saveLogToFile(const SummaryInfo& summary, //throw FileError - const ErrorLog& log, - FileOutput& fileOut); - -void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError - const ErrorLog& log, - size_t maxBytesToWrite); - - - - - -//####################### implementation ####################### -namespace -{ -std::wstring generateLogHeader(const SummaryInfo& s) -{ - assert(s.itemsSynced <= s.itemsTotal); - assert(s.dataSynced <= s.dataTotal); - - std::wstring output; - - //write header - std::wstring headerLine = formatTime(FORMAT_DATE); - if (!s.jobName.empty()) - headerLine += L" - " + s.jobName; - headerLine += L": " + s.finalStatus; - - //assemble results box - std::vector results; - results.push_back(headerLine); - results.push_back(L""); - - const wchar_t tabSpace[] = L" "; - - std::wstring itemsProc = tabSpace + _("Items processed:") + L" " + toGuiString(s.itemsSynced); //show always, even if 0! - if (s.itemsSynced != 0 || s.dataSynced != 0) //[!] don't show 0 bytes processed if 0 items were processed - itemsProc += + L" (" + filesizeToShortString(s.dataSynced) + L")"; - results.push_back(itemsProc); - - if (s.itemsTotal != 0 || s.dataTotal != 0) //=: sync phase was reached and there were actual items to sync - { - if (s.itemsSynced != s.itemsTotal || - s.dataSynced != s.dataTotal) - results.push_back(tabSpace + _("Items remaining:") + L" " + toGuiString(s.itemsTotal - s.itemsSynced) + L" (" + filesizeToShortString(s.dataTotal - s.dataSynced) + L")"); - } - - results.push_back(tabSpace + _("Total time:") + L" " + copyStringTo(wxTimeSpan::Seconds(s.totalTime).Format())); - - //calculate max width, this considers UTF-16 only, not true Unicode...but maybe good idea? those 2-char-UTF16 codes are usually wider than fixed width chars anyway! - size_t sepLineLen = 0; - std::for_each(results.begin(), results.end(), [&](const std::wstring& str) { sepLineLen = std::max(sepLineLen, str.size()); }); - - output.resize(output.size() + sepLineLen + 1, L'_'); - output += L'\n'; - - std::for_each(results.begin(), results.end(), [&](const std::wstring& str) { output += L'|'; output += str; output += L'\n'; }); - - output += L'|'; - output.resize(output.size() + sepLineLen, L'_'); - output += L'\n'; - - return output; -} -} - - -inline -void saveLogToFile(const SummaryInfo& summary, //throw FileError - const ErrorLog& log, - FileOutput& fileOut) -{ - Utf8String header = utfCvrtTo(generateLogHeader(summary)); - replace(header, '\n', LINE_BREAK); //don't replace line break any earlier - header += LINE_BREAK; //make sure string is not empty! - - fileOut.write(&*header.begin(), header.size()); //throw FileError - - //write log items one after the other instead of creating one big string: memory allocation might fail; think 1 million entries! - for (auto iter = log.begin(); iter != log.end(); ++iter) - { - Utf8String msg = replaceCpy(utfCvrtTo(formatMessage(*iter)), '\n', LINE_BREAK); - msg += LINE_BREAK; //make sure string is not empty! - - fileOut.write(&*msg.begin(), msg.size()); //throw FileError - } -} - - -inline -void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError - const ErrorLog& log, - size_t maxBytesToWrite) //log may be *huge*, e.g. 1 million items; LastSyncs.log *must not* create performance problems! -{ - const Zstring filename = getConfigDir() + Zstr("LastSyncs.log"); - - Utf8String newStream = utfCvrtTo(generateLogHeader(summary)); - replace(newStream, '\n', LINE_BREAK); //don't replace line break any earlier - newStream += LINE_BREAK; - - //check size of "newStream": memory allocation might fail - think 1 million entries! - for (auto iter = log.begin(); iter != log.end(); ++iter) - { - newStream += replaceCpy(utfCvrtTo(formatMessage(*iter)), '\n', LINE_BREAK); - newStream += LINE_BREAK; - - if (newStream.size() > maxBytesToWrite) - { - newStream += "[...]"; - newStream += LINE_BREAK; - break; - } - } - - //fill up the rest of permitted space by appending old log - if (newStream.size() < maxBytesToWrite) - { - Utf8String oldStream; - try - { - oldStream = loadBinStream(filename); //throw FileError - } - catch (FileError&) {} - - if (!oldStream.empty()) - { - newStream += LINE_BREAK; - newStream += LINE_BREAK; - newStream += oldStream; //impliticly limited by "maxBytesToWrite"! - - //truncate size if required - if (newStream.size() > maxBytesToWrite) - { - //but do not cut in the middle of a row - auto iter = std::search(newStream.cbegin() + maxBytesToWrite, newStream.cend(), std::begin(LINE_BREAK), std::end(LINE_BREAK) - 1); - if (iter != newStream.cend()) - { - newStream.resize(iter - newStream.cbegin()); - newStream += LINE_BREAK; - - newStream += "[...]"; - newStream += LINE_BREAK; - } - } - } - } - - saveBinStream(filename, newStream); //throw FileError -} -} - -#endif //GEN_LOGFILE_H_93172643216748973216458732165415 diff --git a/lib/hard_filter.cpp b/lib/hard_filter.cpp deleted file mode 100644 index 687aecd8..00000000 --- a/lib/hard_filter.cpp +++ /dev/null @@ -1,404 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "hard_filter.h" -#include -#include -#include -#include -#include -//#include "../structures.h" - -using namespace zen; - -//inline bool operator<(const std::type_info& lhs, const std::type_info& rhs) { return lhs.before(rhs) != 0; } -> not working on GCC :( - - -//-------------------------------------------------------------------------------------------------- -bool zen::operator<(const HardFilter& lhs, const HardFilter& rhs) -{ - if (typeid(lhs) != typeid(rhs)) - return typeid(lhs).before(typeid(rhs)) != 0; //note: in worst case, order is guaranteed to be stable only during each program run - - //this and other are same type: - return lhs.cmpLessSameType(rhs); -} - - -//void HardFilter::saveFilter(ZstreamOut& stream) const //serialize derived object -//{ -// //save type information -// writeString(stream, uniqueClassIdentifier()); -// -// //save actual object -// save(stream); -//} - - -//HardFilter::FilterRef HardFilter::loadFilter(ZstreamIn& stream) //throw UnexpectedEndOfStreamError -//{ -// //read type information -// const std::string uniqueClassId = readString(stream); //throw UnexpectedEndOfStreamError -// -// //read actual object -// if (uniqueClassId == "NullFilter") -// return NullFilter::load(stream); -// else if (uniqueClassId == "NameFilter") -// return NameFilter::load(stream); -// else if (uniqueClassId == "CombinedFilter") -// return CombinedFilter::load(stream); -// else -// throw std::logic_error("Programming Error: Unknown filter!"); -//} - - -namespace -{ -//constructing them in addFilterEntry becomes perf issue for large filter lists -const Zstring asterisk(Zstr('*')); -const Zstring sepAsterisk = FILE_NAME_SEPARATOR + asterisk; -const Zstring asteriskSep = asterisk + FILE_NAME_SEPARATOR; -const Zstring asteriskSepAsterisk = asteriskSep + asterisk; -} - - -void addFilterEntry(const Zstring& filterPhrase, std::vector& fileFilter, std::vector& directoryFilter) -{ -#if defined ZEN_WIN || defined ZEN_MAC - //Windows does NOT distinguish between upper/lower-case - Zstring filterFormatted = filterPhrase; - makeUpper(filterFormatted); -#elif defined ZEN_LINUX - const Zstring& filterFormatted = filterPhrase; - //Linux DOES distinguish between upper/lower-case: nothing to do here -#endif - /* - phrase | action - +---------+-------- - | \blah | remove \ - | \*blah | remove \ - | \*\blah | remove \ - | \*\* | remove \ - +---------+-------- - | *blah | - | *\blah | -> add blah - | *\*blah | -> add *blah - +---------+-------- - | blah\ | remove \; directory only - | blah*\ | remove \; directory only - | blah\*\ | remove \; directory only - +---------+-------- - | blah* | - | blah\* | add blah for directory only - | blah*\* | add blah* for directory only - +---------+-------- - */ - auto processTail = [&fileFilter, &directoryFilter](const Zstring& phrase) - { - if (endsWith(phrase, FILE_NAME_SEPARATOR)) //only relevant for directory filtering - { - const Zstring dirPhrase = beforeLast(phrase, FILE_NAME_SEPARATOR); - if (!dirPhrase.empty()) - directoryFilter.push_back(dirPhrase); - } - else if (!phrase.empty()) - { - fileFilter .push_back(phrase); - directoryFilter.push_back(phrase); - if (endsWith(phrase, sepAsterisk)) // abc\* - { - const Zstring dirPhrase = beforeLast(phrase, sepAsterisk); - if (!dirPhrase.empty()) - directoryFilter.push_back(dirPhrase); - } - } - }; - - if (startsWith(filterFormatted, FILE_NAME_SEPARATOR)) // \abc - processTail(afterFirst(filterFormatted, FILE_NAME_SEPARATOR)); - else - { - processTail(filterFormatted); - if (startsWith(filterFormatted, asteriskSep)) // *\abc - processTail(afterFirst(filterFormatted, asteriskSep)); - } -} - - -namespace -{ -template inline -const Char* cStringFind(const Char* str, Char ch) //strchr() -{ - for (;;) - { - const Char s = *str; - if (s == ch) //ch is allowed to be 0 by contract! must return end of string in this case - return str; - - if (s == 0) - return nullptr; - ++str; - } -} - - -bool matchesMask(const Zchar* str, const Zchar* mask) -{ - for (;; ++mask, ++str) - { - Zchar m = *mask; - if (m == 0) - return *str == 0; - - switch (m) - { - case Zstr('?'): - if (*str == 0) - return false; - break; - - case Zstr('*'): - //advance mask to next non-* char - do - { - m = *++mask; - } - while (m == Zstr('*')); - - if (m == 0) //mask ends with '*': - return true; - - //*? - pattern - if (m == Zstr('?')) - { - ++mask; - while (*str++ != 0) - if (matchesMask(str, mask)) - return true; - return false; - } - - //*[letter] - pattern - ++mask; - for (;;) - { - str = cStringFind(str, m); - if (!str) - return false; - - ++str; - if (matchesMask(str, mask)) - return true; - } - - default: - if (*str != m) - return false; - } - } -} - - -//returns true if string matches at least the beginning of mask -inline -bool matchesMaskBegin(const Zchar* str, const Zchar* mask) -{ - for (;; ++mask, ++str) - { - const Zchar m = *mask; - if (m == 0) - return *str == 0; - - switch (m) - { - case Zstr('?'): - if (*str == 0) - return true; - break; - - case Zstr('*'): - return true; - - default: - if (*str != m) - return *str == 0; - } - } -} -} - - -inline -bool matchesFilter(const Zstring& name, const std::vector& filter) -{ - return std::any_of(filter.begin(), filter.end(), [&](const Zstring& mask) { return matchesMask(name.c_str(), mask.c_str()); }); -} - - -inline -bool matchesFilterBegin(const Zstring& name, const std::vector& filter) -{ - return std::any_of(filter.begin(), filter.end(), [&](const Zstring& mask) { return matchesMaskBegin(name.c_str(), mask.c_str()); }); -} - - -std::vector splitByDelimiter(const Zstring& filterString) -{ - //delimiters may be ';' or '\n' - std::vector output; - - const std::vector blocks = split(filterString, Zchar(';')); //split by less common delimiter first - std::for_each(blocks.begin(), blocks.end(), - [&](const Zstring& item) - { - const std::vector blocks2 = split(item, Zchar('\n')); - - std::for_each(blocks2.begin(), blocks2.end(), - [&](Zstring entry) - { - trim(entry); - if (!entry.empty()) - output.push_back(entry); - }); - }); - - return output; -} - -//################################################################################################# -NameFilter::NameFilter(const Zstring& includeFilter, const Zstring& excludeFilter) : - includeFilterTmp(includeFilter), //save constructor arguments for serialization - excludeFilterTmp(excludeFilter) -{ - //no need for regular expressions: In tests wxRegex was by factor of 10 slower than wxString::Matches() - - //load filter into vectors of strings - //delimiters may be ';' or '\n' - const std::vector& includeList = splitByDelimiter(includeFilter); - const std::vector& excludeList = splitByDelimiter(excludeFilter); - - //setup include/exclude filters for files and directories - std::for_each(includeList.begin(), includeList.end(), [&](const Zstring& entry) { addFilterEntry(entry, filterFileIn, filterFolderIn); }); - std::for_each(excludeList.begin(), excludeList.end(), [&](const Zstring& entry) { addFilterEntry(entry, filterFileEx, filterFolderEx); }); - - auto removeDuplicates = [](std::vector& cont) - { - std::vector output; - std::set used; - std::copy_if(cont.begin(), cont.end(), std::back_inserter(output), [&](const Zstring& item) { return used.insert(item).second; }); - output.swap(cont); - }; - - removeDuplicates(filterFileIn); - removeDuplicates(filterFolderIn); - removeDuplicates(filterFileEx); - removeDuplicates(filterFolderEx); -} - - -bool NameFilter::passFileFilter(const Zstring& relFilename) const -{ -#if defined ZEN_WIN || defined ZEN_MAC //Windows does NOT distinguish between upper/lower-case - Zstring nameFormatted = relFilename; - makeUpper(nameFormatted); -#elif defined ZEN_LINUX //Linux DOES distinguish between upper/lower-case - const Zstring& nameFormatted = relFilename; //nothing to do here -#endif - - return matchesFilter(nameFormatted, filterFileIn) && //process include filters - !matchesFilter(nameFormatted, filterFileEx); //process exclude filters -} - - -bool NameFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const -{ - assert(!subObjMightMatch || *subObjMightMatch == true); //check correct usage - -#if defined ZEN_WIN || defined ZEN_MAC //Windows does NOT distinguish between upper/lower-case - Zstring nameFormatted = relDirname; - makeUpper(nameFormatted); -#elif defined ZEN_LINUX //Linux DOES distinguish between upper/lower-case - const Zstring& nameFormatted = relDirname; //nothing to do here -#endif - - if (matchesFilter(nameFormatted, filterFolderEx)) //process exclude filters - { - if (subObjMightMatch) - *subObjMightMatch = false; //exclude subfolders/subfiles as well - return false; - } - - if (!matchesFilter(nameFormatted, filterFolderIn)) //process include filters - { - if (subObjMightMatch) - { - const Zstring& subNameBegin = nameFormatted + FILE_NAME_SEPARATOR; //const-ref optimization - - *subObjMightMatch = matchesFilterBegin(subNameBegin, filterFileIn) || //might match a file in subdirectory - matchesFilterBegin(subNameBegin, filterFolderIn); //or another subdirectory - } - return false; - } - - return true; -} - - -bool NameFilter::isNull(const Zstring& includeFilter, const Zstring& excludeFilter) -{ - Zstring include = includeFilter; - Zstring exclude = excludeFilter; - trim(include); - trim(exclude); - - return include == Zstr("*") && exclude.empty(); - //return NameFilter(includeFilter, excludeFilter).isNull(); -> very expensive for huge lists -} - -bool NameFilter::isNull() const -{ - static NameFilter output(Zstr("*"), Zstring()); - return *this == output; -} - - -bool NameFilter::cmpLessSameType(const HardFilter& other) const -{ - assert(typeid(*this) == typeid(other)); //always given in this context! - - const NameFilter& otherNameFilt = static_cast(other); - - if (filterFileIn != otherNameFilt.filterFileIn) - return filterFileIn < otherNameFilt.filterFileIn; - - if (filterFolderIn != otherNameFilt.filterFolderIn) - return filterFolderIn < otherNameFilt.filterFolderIn; - - if (filterFileEx != otherNameFilt.filterFileEx) - return filterFileEx < otherNameFilt.filterFileEx; - - if (filterFolderEx != otherNameFilt.filterFolderEx) - return filterFolderEx < otherNameFilt.filterFolderEx; - - return false; //vectors equal -} - - -//void NameFilter::save(ZstreamOut& stream) const -//{ -// writeString(stream, includeFilterTmp); -// writeString(stream, excludeFilterTmp); -//} -// -// -//HardFilter::FilterRef NameFilter::load(ZstreamIn& stream) //throw UnexpectedEndOfStreamError -//{ -// const Zstring include = readString(stream); //throw UnexpectedEndOfStreamError -// const Zstring exclude = readString(stream); // -// -// return FilterRef(new NameFilter(include, exclude)); -//} diff --git a/lib/hard_filter.h b/lib/hard_filter.h deleted file mode 100644 index e721fe4f..00000000 --- a/lib/hard_filter.h +++ /dev/null @@ -1,270 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef HARD_FILTER_H_825780275842758345 -#define HARD_FILTER_H_825780275842758345 - -#include -#include -#include -//#include - -namespace zen -{ -//------------------------------------------------------------------ -/* -Semantics of HardFilter: -1. using it creates a NEW folder hierarchy! -> must be considered by -mode! -2. it applies equally to both sides => it always matches either both sides or none! => can be used while traversing a single folder! - - class hierarchy: - - HardFilter (interface) - /|\ - _________|_____________ - | | | -NullFilter NameFilter CombinedFilter -*/ - -class HardFilter //interface for filtering -{ -public: - virtual ~HardFilter() {} - - //filtering - virtual bool passFileFilter(const Zstring& relFilename) const = 0; - virtual bool passDirFilter (const Zstring& relDirname, bool* subObjMightMatch) const = 0; - //subObjMightMatch: file/dir in subdirectories could(!) match - //note: variable is only set if passDirFilter returns false! - - virtual bool isNull() const = 0; //filter is equivalent to NullFilter, but may be technically slower - - typedef std::shared_ptr FilterRef; //always bound by design! - - //serialization - // void saveFilter(ZstreamOut& stream) const; //serialize derived object - // static FilterRef loadFilter(ZstreamIn& stream); //throw UnexpectedEndOfStreamError; CAVEAT!!! adapt this method for each new derivation!!! - -private: - friend bool operator<(const HardFilter& lhs, const HardFilter& rhs); - - // virtual void save(ZstreamOut& stream) const = 0; //serialization - virtual std::string uniqueClassIdentifier() const = 0; //get identifier, used for serialization - virtual bool cmpLessSameType(const HardFilter& other) const = 0; //typeid(*this) == typeid(other) in this context! -}; - -inline bool operator==(const HardFilter& lhs, const HardFilter& rhs) { return !(lhs < rhs) && !(rhs < lhs); } -inline bool operator!=(const HardFilter& lhs, const HardFilter& rhs) { return !(lhs == rhs); } - - -//small helper method: merge two hard filters (thereby remove Null-filters) -HardFilter::FilterRef combineFilters(const HardFilter::FilterRef& first, - const HardFilter::FilterRef& second); - - -class NullFilter : public HardFilter //no filtering at all -{ -public: - virtual bool passFileFilter(const Zstring& relFilename) const; - virtual bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const; - virtual bool isNull() const; - -private: - friend class HardFilter; - // virtual void save(ZstreamOut& stream) const {} - virtual std::string uniqueClassIdentifier() const { return "NullFilter"; } - // static FilterRef load(ZstreamIn& stream); //throw UnexpectedEndOfStreamError - virtual bool cmpLessSameType(const HardFilter& other) const; -}; - - -class NameFilter : public HardFilter //standard filter by filename -{ -public: - NameFilter(const Zstring& includeFilter, const Zstring& excludeFilter); - - virtual bool passFileFilter(const Zstring& relFilename) const; - virtual bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const; - - virtual bool isNull() const; - static bool isNull(const Zstring& includeFilter, const Zstring& excludeFilter); //*fast* check without expensively constructing NameFilter instance! - -private: - friend class HardFilter; - // virtual void save(ZstreamOut& stream) const; - virtual std::string uniqueClassIdentifier() const { return "NameFilter"; } - // static FilterRef load(ZstreamIn& stream); //throw UnexpectedEndOfStreamError - virtual bool cmpLessSameType(const HardFilter& other) const; - - std::vector filterFileIn; // - std::vector filterFolderIn; //upper case (windows) + unique items by construction - std::vector filterFileEx; // - std::vector filterFolderEx; // - - const Zstring includeFilterTmp; //save constructor arguments for serialization - const Zstring excludeFilterTmp; // -}; - - -class CombinedFilter : public HardFilter //combine two filters to match if and only if both match -{ -public: - CombinedFilter(const FilterRef& first, const FilterRef& second) : first_(first), second_(second) {} - - virtual bool passFileFilter(const Zstring& relFilename) const; - virtual bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const; - virtual bool isNull() const; - -private: - friend class HardFilter; - // virtual void save(ZstreamOut& stream) const; - virtual std::string uniqueClassIdentifier() const { return "CombinedFilter"; } - // static FilterRef load(ZstreamIn& stream); //throw UnexpectedEndOfStreamError - virtual bool cmpLessSameType(const HardFilter& other) const; - - const FilterRef first_; - const FilterRef second_; -}; - - - - - - - - - - - - - - - - - - -//---------------Inline Implementation--------------------------------------------------- -//inline -//HardFilter::FilterRef NullFilter::load(ZstreamIn& stream) -//{ -// return FilterRef(new NullFilter); -//} - - -inline -bool NullFilter::passFileFilter(const Zstring& relFilename) const -{ - return true; -} - - -inline -bool NullFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const -{ - assert(!subObjMightMatch || *subObjMightMatch == true); //check correct usage - return true; -} - - -inline -bool NullFilter::isNull() const -{ - return true; -} - - -inline -bool NullFilter::cmpLessSameType(const HardFilter& other) const -{ - assert(typeid(*this) == typeid(other)); //always given in this context! - return false; -} - - -inline -bool CombinedFilter::passFileFilter(const Zstring& relFilename) const -{ - return first_ ->passFileFilter(relFilename) && //short-circuit behavior - second_->passFileFilter(relFilename); -} - - -inline -bool CombinedFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const -{ - if (first_->passDirFilter(relDirname, subObjMightMatch)) - return second_->passDirFilter(relDirname, subObjMightMatch); - else - { - if (subObjMightMatch && *subObjMightMatch) - second_->passDirFilter(relDirname, subObjMightMatch); - return false; - } -} - - -inline -bool CombinedFilter::isNull() const -{ - return first_->isNull() && second_->isNull(); -} - - -inline -bool CombinedFilter::cmpLessSameType(const HardFilter& other) const -{ - assert(typeid(*this) == typeid(other)); //always given in this context! - - const CombinedFilter& otherCombFilt = static_cast(other); - - if (*first_ != *otherCombFilt.first_) - return *first_ < *otherCombFilt.first_; - - return *second_ < *otherCombFilt.second_; -} - - -//inline -//void CombinedFilter::save(ZstreamOut& stream) const -//{ -// first_ ->saveFilter(stream); -// second_->saveFilter(stream); -//} - - -//inline -//HardFilter::FilterRef CombinedFilter::load(ZstreamIn& stream) //throw UnexpectedEndOfStreamError -//{ -// FilterRef first = loadFilter(stream); //throw UnexpectedEndOfStreamError -// FilterRef second = loadFilter(stream); // -// -// return combineFilters(first, second); -//} - - -inline -HardFilter::FilterRef combineFilters(const HardFilter::FilterRef& first, - const HardFilter::FilterRef& second) -{ - if (first->isNull()) - { - if (second->isNull()) - return std::make_shared(); - else - return second; - } - else - { - if (second->isNull()) - return first; - else - return std::make_shared(first, second); - } -} -} - - -#endif //HARD_FILTER_H_825780275842758345 diff --git a/lib/help_provider.h b/lib/help_provider.h deleted file mode 100644 index 8ddc34c7..00000000 --- a/lib/help_provider.h +++ /dev/null @@ -1,98 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef HELPPROVIDER_H_INCLUDED -#define HELPPROVIDER_H_INCLUDED - -#ifdef ZEN_WIN -#include -#include - -#elif defined ZEN_LINUX || defined ZEN_MAC -#include -#endif - -#include "ffs_paths.h" - -namespace zen -{ -//use '/' as path separator! -void displayHelpEntry(wxWindow* parent); -void displayHelpEntry(const wxString& section, wxWindow* parent); - - - - - - - - - -//######################## implementation ######################## -namespace impl -{ -//finish wxWidgets' job -#ifdef ZEN_WIN -class FfsHelpController -{ -public: - FfsHelpController() - { - chmHlp.Initialize(utfCvrtTo(zen::getResourceDir()) + L"FreeFileSync.chm"); - } - - void openSection(const wxString& section, wxWindow* parent) - { - if (section.empty()) - chmHlp.DisplayContents(); - else - chmHlp.DisplaySection(replaceCpy(section, L'/', utfCvrtTo(FILE_NAME_SEPARATOR))); - } -private: - wxCHMHelpController chmHlp; -}; - -#elif defined ZEN_LINUX || defined ZEN_MAC -class FfsHelpController -{ -public: - void openSection(const wxString& section, wxWindow* parent) - { - wxHtmlModalHelp dlg(parent, utfCvrtTo(zen::getResourceDir()) + L"Help/FreeFileSync.hhp", section, - wxHF_DEFAULT_STYLE | wxHF_DIALOG | wxHF_MODAL | wxHF_MERGE_BOOKS); - (void)dlg; - //-> solves modal help craziness on OSX! - //-> Suse Linux: avoids program hang on exit if user closed help parent dialog before the help dialog itself was closed (why is this even possible???) - // avoids ESC key not being recognized by help dialog (but by parent dialog instead) - } -}; -#endif - - -inline -FfsHelpController& getHelpCtrl() -{ - static FfsHelpController ctrl; //external linkage, despite inline definition! - return ctrl; -} -} - - -inline -void displayHelpEntry(const wxString& section, wxWindow* parent) -{ - impl::getHelpCtrl().openSection(section, parent); -} - - -inline -void displayHelpEntry(wxWindow* parent) -{ - impl::getHelpCtrl().openSection(wxString(), parent); -} -} - -#endif //HELPPROVIDER_H_INCLUDED diff --git a/lib/icon_buffer.cpp b/lib/icon_buffer.cpp deleted file mode 100644 index e78b308e..00000000 --- a/lib/icon_buffer.cpp +++ /dev/null @@ -1,674 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "icon_buffer.h" -#include -#include -#include //includes -#include - -#ifdef ZEN_WIN -#include -#include -#include -#include "Thumbnail/thumbnail.h" - -#elif defined ZEN_LINUX -#include - -#elif defined ZEN_MAC -#include "osx_file_icon.h" -#endif - -using namespace zen; - - -namespace -{ -const size_t BUFFER_SIZE_MAX = 600; //maximum number of icons to hold in buffer - -#ifndef NDEBUG -boost::thread::id mainThreadId = boost::this_thread::get_id(); -#endif - -#ifdef ZEN_WIN -const bool isXpOrLater = winXpOrLater(); //VS2010 compiled DLLs are not supported on Win 2000: Popup dialog "DecodePointer not found" - -#define DEF_DLL_FUN(name) const auto name = isXpOrLater ? DllFun(thumb::getDllName(), thumb::funName_##name) : DllFun(); -DEF_DLL_FUN(getIconByIndex); // -DEF_DLL_FUN(getThumbnail); //let's spare the boost::call_once hustle and allocate statically -DEF_DLL_FUN(releaseImageData); // -#endif - -class IconHolder //handle HICON/GdkPixbuf ownership supporting thread-safe usage (in contrast to wxIcon/wxBitmap) -{ -public: -#ifdef ZEN_WIN - typedef const thumb::ImageData* HandleType; -#elif defined ZEN_LINUX - typedef GdkPixbuf* HandleType; -#elif defined ZEN_MAC - typedef osx::ImageData* HandleType; -#endif - - explicit IconHolder(HandleType handle = nullptr) : handle_(handle) {} //take ownership! - - IconHolder(IconHolder&& other) : handle_(other.release()) {} - - IconHolder& operator=(IconHolder other) //unifying assignment - { - other.swap(*this); - return *this; - } - - ~IconHolder() - { - if (handle_ != nullptr) -#ifdef ZEN_WIN - releaseImageData(handle_); //should be checked already before creating IconHolder! -#elif defined ZEN_LINUX - ::g_object_unref(handle_); //superseedes "::gdk_pixbuf_unref"! -#elif defined ZEN_MAC - delete handle_; -#endif - } - - HandleType release() - { - ZEN_ON_SCOPE_EXIT(handle_ = nullptr); - return handle_; - } - - void swap(IconHolder& other) { std::swap(handle_, other.handle_); } //throw() - - //destroys raw icon! Call from GUI thread only! - wxBitmap extractWxBitmap() - { - ZEN_ON_SCOPE_EXIT(assert(!*this)); - assert(boost::this_thread::get_id() == mainThreadId ); - - if (!handle_) - return wxNullBitmap; - -#ifdef ZEN_WIN - ZEN_ON_SCOPE_EXIT(IconHolder().swap(*this)); //destroy after extraction - - //let wxImage reference data without taking ownership: - wxImage fileIcon(handle_->width, handle_->height, handle_->rgb, true); - fileIcon.SetAlpha(handle_->alpha, true); - return wxBitmap(fileIcon); - -#elif defined ZEN_LINUX - return wxBitmap(release()); //ownership passed! - -#elif defined ZEN_MAC - ZEN_ON_SCOPE_EXIT(IconHolder().swap(*this)); //destroy after extraction - - //let wxImage reference data without taking ownership: - if (!handle_->rgb.empty()) - { - wxImage fileIcon(handle_->width, handle_->height, &handle_->rgb[0], true); - if (!handle_->alpha.empty()) - fileIcon.SetAlpha(&handle_->alpha[0], true); - return wxBitmap(fileIcon); - } - assert(false); //rgb and alpha should never be empty - return wxBitmap(); -#endif - } - -private: - HandleType handle_; - - IconHolder(const IconHolder& other); //move semantics! - struct ConversionToBool { int dummy; }; -public: - //use member pointer as implicit conversion to bool (C++ Templates - Vandevoorde/Josuttis; chapter 20) - operator int ConversionToBool::* () const { return handle_ != nullptr ? &ConversionToBool::dummy : nullptr; } -}; - - -#ifdef ZEN_WIN -Zstring getFileExtension(const Zstring& filename) -{ - const Zstring shortName = afterLast(filename, Zchar('\\')); //warning: using windows file name separator! - - return contains(shortName, Zchar('.')) ? - afterLast(filename, Zchar('.')) : - Zstring(); -} - - -std::set priceyExtensions; //thread-safe! -boost::once_flag initExtensionsOnce = BOOST_ONCE_INIT; // - -//test for extension for non-thumbnail icons that physically have to be retrieved from disc -bool isCheapExtension(const Zstring& extension) -{ - boost::call_once(initExtensionsOnce, []() - { - priceyExtensions.insert(L"exe"); - priceyExtensions.insert(L"ico"); - priceyExtensions.insert(L"ani"); - priceyExtensions.insert(L"cur"); - priceyExtensions.insert(L"msc"); - priceyExtensions.insert(L"scr"); - - priceyExtensions.insert(L"lnk"); // - priceyExtensions.insert(L"url"); //make sure shortcuts are pricey to get them to be detected by SHGetFileInfo - priceyExtensions.insert(L"pif"); // - priceyExtensions.insert(L"website"); // - - }); - return priceyExtensions.find(extension) == priceyExtensions.end(); -} - -const bool wereVistaOrLater = vistaOrLater(); //thread-safety: init at startup - - -thumb::IconSizeType getThumbSizeType(IconBuffer::IconSize sz) -{ - using namespace thumb; - switch (sz) - { - case IconBuffer::SIZE_SMALL: - return ICON_SIZE_16; - case IconBuffer::SIZE_MEDIUM: - if (!wereVistaOrLater) return ICON_SIZE_32; //48x48 doesn't look sharp on XP - return ICON_SIZE_48; - case IconBuffer::SIZE_LARGE: - return ICON_SIZE_128; - } - return ICON_SIZE_16; -} - - -IconHolder getIconByAttribute(LPCWSTR pszPath, DWORD dwFileAttributes, IconBuffer::IconSize sz) -{ - //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! - SHFILEINFO fileInfo = {}; //initialize hIcon - DWORD_PTR imgList = ::SHGetFileInfo(pszPath, //Windows 7 doesn't like this parameter to be an empty string - dwFileAttributes, - &fileInfo, - sizeof(fileInfo), - SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES); - if (!imgList) //no need to IUnknown::Release() imgList! - return IconHolder(); - - if (getIconByIndex && releaseImageData) - return IconHolder(getIconByIndex(fileInfo.iIcon, getThumbSizeType(sz))); - - return IconHolder(); -} - - -IconHolder getAssociatedIconByExt(const Zstring& extension, IconBuffer::IconSize sz) -{ - //no read-access to disk! determine icon by extension - return getIconByAttribute((L"dummy." + extension).c_str(), FILE_ATTRIBUTE_NORMAL, sz); -} - -#elif defined ZEN_LINUX -IconHolder iconHolderFromGicon(GIcon* gicon, IconBuffer::IconSize sz) -{ - if (gicon) - if (GtkIconTheme* defaultTheme = ::gtk_icon_theme_get_default()) //not owned! - if (GtkIconInfo* iconInfo = ::gtk_icon_theme_lookup_by_gicon(defaultTheme, gicon, IconBuffer::getSize(sz), GTK_ICON_LOOKUP_USE_BUILTIN)) //this may fail if icon is not installed on system - { - ZEN_ON_SCOPE_EXIT(::gtk_icon_info_free(iconInfo);) - if (GdkPixbuf* pixBuf = ::gtk_icon_info_load_icon(iconInfo, nullptr)) - return IconHolder(pixBuf); //pass ownership - } - return IconHolder(); -} -#endif -} - -//################################################################################################################################################ - -IconHolder getThumbnailIcon(const Zstring& filename, int requestedSize) //return 0 on failure -{ -#ifdef ZEN_WIN - if (getThumbnail && releaseImageData) - return IconHolder(getThumbnail(filename.c_str(), requestedSize)); - -#elif defined ZEN_LINUX - gint width = 0; - gint height = 0; - if (GdkPixbufFormat* fmt = ::gdk_pixbuf_get_file_info(filename.c_str(), &width, &height)) - { - (void)fmt; - if (width > 0 && height > 0 && requestedSize > 0) - { - int trgWidth = width; - int trgHeight = height; - - const int maxExtent = std::max(width, height); //don't stretch small images, but shrink large ones instead! - if (requestedSize < maxExtent) - { - trgWidth = width * requestedSize / maxExtent; - trgHeight = height * requestedSize / maxExtent; - } - if (GdkPixbuf* pixBuf = ::gdk_pixbuf_new_from_file_at_size(filename.c_str(), trgWidth, trgHeight, nullptr)) - return IconHolder(pixBuf); //pass ownership - } - } - -#elif defined ZEN_MAC - try - { - return IconHolder(new osx::ImageData(osx::getThumbnail(filename.c_str(), requestedSize))); //throw SysError - } - catch (zen::SysError&) {} -#endif - return IconHolder(); -} - - -IconHolder getGenericFileIcon(IconBuffer::IconSize sz) -{ - //we're called by getAssociatedIcon()! -> avoid endless recursion! -#ifdef ZEN_WIN - return getIconByAttribute(L"dummy", FILE_ATTRIBUTE_NORMAL, sz); - -#elif defined ZEN_LINUX - const char* mimeFileIcons[] = - { - "application-x-zerosize", //Kubuntu: /usr/share/icons/oxygen/48x48/mimetypes - "text-x-generic", //http://live.gnome.org/GnomeArt/Tutorials/IconThemes - "empty", //Ubuntu: /usr/share/icons/Humanity/mimes/48 - GTK_STOCK_FILE, //"gtk-file", - "gnome-fs-regular", // - }; - - if (GtkIconTheme* defaultTheme = gtk_icon_theme_get_default()) //not owned! - for (auto it = std::begin(mimeFileIcons); it != std::end(mimeFileIcons); ++it) - if (GdkPixbuf* pixBuf = gtk_icon_theme_load_icon(defaultTheme, *it, IconBuffer::getSize(sz), GTK_ICON_LOOKUP_USE_BUILTIN, nullptr)) - return IconHolder(pixBuf); //pass ownership - return IconHolder(); - -#elif defined ZEN_MAC - try - { - return IconHolder(new osx::ImageData(osx::getDefaultFileIcon(IconBuffer::getSize(sz)))); //throw SysError - } - catch (zen::SysError&) {} - return IconHolder(); -#endif -} - - -IconHolder getGenericDirectoryIcon(IconBuffer::IconSize sz) -{ -#ifdef ZEN_WIN - return getIconByAttribute(L"dummy", //Windows 7 doesn't like this parameter to be an empty string! - FILE_ATTRIBUTE_DIRECTORY, sz); -#elif defined ZEN_LINUX - if (GIcon* dirIcon = ::g_content_type_get_icon("inode/directory")) //should contain fallback to GTK_STOCK_DIRECTORY ("gtk-directory") - return iconHolderFromGicon(dirIcon, sz); - return IconHolder(); - -#elif defined ZEN_MAC - try - { - return IconHolder(new osx::ImageData(osx::getDefaultFolderIcon(IconBuffer::getSize(sz)))); //throw SysError - } - catch (zen::SysError&) { return IconHolder(); } -#endif -} - - -IconHolder getAssociatedIcon(const Zstring& filename, IconBuffer::IconSize sz) -{ - //1. try to load thumbnails - switch (sz) - { - case IconBuffer::SIZE_SMALL: - break; - case IconBuffer::SIZE_MEDIUM: - case IconBuffer::SIZE_LARGE: - if (IconHolder ico = getThumbnailIcon(filename, IconBuffer::getSize(sz))) - return ico; - //else: fallback to non-thumbnail icon - break; - } - - warn_static("problem: fr folder links ist getThumbnail erfolgreich => SFGAO_LINK nicht gecheckt!") - - //2. retrieve file icons -#ifdef ZEN_WIN - //perf: optimize fallback case for SIZE_MEDIUM and SIZE_LARGE: - const Zstring& extension = getFileExtension(filename); - if (isCheapExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension - return getAssociatedIconByExt(extension, sz); - //result will not be buffered under extension name, but full filename; this is okay, since we're in SIZE_MEDIUM or SIZE_LARGE context, - //which means the access to get thumbnail failed: thumbnail failure is not dependent from extension in general! - - SHFILEINFO fileInfo = {}; - if (DWORD_PTR imgList = ::SHGetFileInfo(filename.c_str(), //_In_ LPCTSTR pszPath, -> note: ::SHGetFileInfo() can't handle \\?\-prefix! - 0, //DWORD dwFileAttributes, - &fileInfo, //_Inout_ SHFILEINFO *psfi, - sizeof(fileInfo), //UINT cbFileInfo, - SHGFI_SYSICONINDEX /*| SHGFI_ATTRIBUTES*/)) //UINT uFlags - { - (void)imgList; - //imgList->Release(); //empiric study: crash on XP if we release this! Seems we do not own it... -> also no GDI leak on Win7 -> okay - //another comment on http://msdn.microsoft.com/en-us/library/bb762179(v=VS.85).aspx describes exact same behavior on Win7/XP - - //Quote: "The IImageList pointer type, such as that returned in the ppv parameter, can be cast as an HIMAGELIST as needed; - // for example, for use in a list view. Conversely, an HIMAGELIST can be cast as a pointer to an IImageList." - //http://msdn.microsoft.com/en-us/library/windows/desktop/bb762185(v=vs.85).aspx - -#ifdef __MINGW32__ //Shobjidl.h -#define SFGAO_LINK 0x00010000L // Shortcut (link) or symlinks -#endif - - warn_static("support SFGAO_GHOSTED or hidden?") - //requires SHGFI_ATTRIBUTES - //const bool isLink = (fileInfo.dwAttributes & SFGAO_LINK) != 0; - - if (getIconByIndex && releaseImageData) - if (const thumb::ImageData* imgData = getIconByIndex(fileInfo.iIcon, getThumbSizeType(sz))) - return IconHolder(imgData); - } - -#elif defined ZEN_LINUX - GFile* file = ::g_file_new_for_path(filename.c_str()); //documented to "never fail" - ZEN_ON_SCOPE_EXIT(::g_object_unref(file);) - - if (GFileInfo* fileInfo = ::g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_ICON, G_FILE_QUERY_INFO_NONE, nullptr, nullptr)) - { - ZEN_ON_SCOPE_EXIT(::g_object_unref(fileInfo);) - if (GIcon* gicon = ::g_file_info_get_icon(fileInfo)) //not owned! - return iconHolderFromGicon(gicon, sz); - } - //need fallback: icon lookup may fail because some icons are currently not present on system - -#elif defined ZEN_MAC - try - { - return IconHolder(new osx::ImageData(osx::getFileIcon(filename.c_str(), IconBuffer::getSize(sz)))); //throw SysError - } - catch (zen::SysError&) { assert(false); } -#endif - return ::getGenericFileIcon(sz); //make sure this does not internally call getAssociatedIcon("someDefaultFile.txt")!!! => endless recursion! -} - -//################################################################################################################################################ - -//---------------------- Shared Data ------------------------- -class WorkLoad -{ -public: - Zstring extractNextFile() //context of worker thread, blocking - { - assert(boost::this_thread::get_id() != mainThreadId ); - boost::unique_lock dummy(lockFiles); - - while (filesToLoad.empty()) - conditionNewFiles.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point! - - Zstring fileName = filesToLoad.back(); - filesToLoad.pop_back(); - return fileName; - } - - void setWorkload(const std::list& newLoad) //context of main thread - { - assert(boost::this_thread::get_id() == mainThreadId ); - { - boost::lock_guard dummy(lockFiles); - filesToLoad = newLoad; - } - conditionNewFiles.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796 - //condition handling, see: http://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref - } - - void addToWorkload(const Zstring& newEntry) //context of main thread - { - assert(boost::this_thread::get_id() == mainThreadId ); - { - boost::lock_guard dummy(lockFiles); - filesToLoad.push_back(newEntry); //set as next item to retrieve - } - conditionNewFiles.notify_all(); - } - -private: - std::list filesToLoad; //processes last elements of vector first! - boost::mutex lockFiles; - boost::condition_variable conditionNewFiles; //signal event: data for processing available -}; - - -class Buffer -{ -public: - //called by main and worker thread: - bool hasFileIcon(const Zstring& fileName) const - { - boost::lock_guard dummy(lockIconList); - return iconList.find(fileName) != iconList.end(); - } - - //must be called by main thread only! => wxBitmap is NOT thread-safe like an int (non-atomic ref-count!!!) - Opt retrieveFileIcon(const Zstring& fileName) - { - assert(boost::this_thread::get_id() == mainThreadId); - boost::lock_guard dummy(lockIconList); - auto it = iconList.find(fileName); - if (it == iconList.end()) - return NoValue(); - - IconData& idata = it->second; - if (idata.iconRaw) //if not yet converted... - { - idata.iconFmt = make_unique(idata.iconRaw.extractWxBitmap()); //convert in main thread! - assert(!idata.iconRaw); - } - return idata.iconFmt ? *idata.iconFmt : wxNullBitmap; //idata.iconRaw may be inserted as empty from worker thread! - } - - //must be called by main thread only! => ~wxBitmap() is NOT thread-safe! - //call at an appropriate time, e.g. after Workload::setWorkload() - void limitBufferSize() //critical because GDI resources are limited (e.g. 10000 on XP per process) - { - assert(boost::this_thread::get_id() == mainThreadId); - boost::lock_guard dummy(lockIconList); - while (iconList.size() > BUFFER_SIZE_MAX) - { - iconList.erase(iconSequence.front()); //remove oldest element - iconSequence.pop(); - } - } - - //called by main and worker thread: - void moveIntoBuffer(const Zstring& entryName, IconHolder&& icon) - { - boost::lock_guard dummy(lockIconList); - - //thread safety: moving IconHolder is free from side effects, but ~wxBitmap() is NOT! => do NOT delete items from iconList here! - auto rc = iconList.insert(std::make_pair(entryName, IconData(std::move(icon)))); - if (rc.second) //if insertion took place - iconSequence.push(entryName); //note: sharing Zstring with IconDB!!! - - assert(iconList.size() == iconSequence.size()); - } - -private: - struct IconData - { - IconData(IconHolder&& tmp) : iconRaw(std::move(tmp)) {} - IconData(IconData&& tmp) : iconRaw(std::move(tmp.iconRaw)), iconFmt(std::move(tmp.iconFmt)) {} - - IconHolder iconRaw; //native icon representation: may be used by any thread - - std::unique_ptr iconFmt; //use ONLY from main thread! - //wxBitmap is NOT thread-safe: non-atomic ref-count just to begin with... - //- prohibit implicit calls to wxBitmap(const wxBitmap&) - //- prohibit calls to ~wxBitmap() and transitively ~IconData() - //- prohibit even wxBitmap() default constructor - better be safe than sorry! - }; - - mutable boost::mutex lockIconList; - std::map iconList; //shared resource; Zstring is thread-safe like an int - std::queue iconSequence; //save sequence of buffer entry to delete oldest elements -}; - -//################################################################################################################################################ - -class WorkerThread //lifetime is part of icon buffer -{ -public: - WorkerThread(const std::shared_ptr& workload, - const std::shared_ptr& buffer, - IconBuffer::IconSize st) : - workload_(workload), - buffer_(buffer), - iconSizeType(st) {} - - void operator()(); //thread entry - -private: - std::shared_ptr workload_; //main/worker thread may access different shared_ptr instances safely (even though they have the same target!) - std::shared_ptr buffer_; //http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/shared_ptr.htm?sess=8153b05b34d890e02d48730db1ff7ddc#ThreadSafety - const IconBuffer::IconSize iconSizeType; -}; - - -void WorkerThread::operator()() //thread entry -{ - //failure to initialize COM for each thread is a source of hard to reproduce bugs: https://sourceforge.net/tracker/?func=detail&aid=3160472&group_id=234430&atid=1093080 -#ifdef ZEN_WIN - //Prerequisites, see thumbnail.h - - //1. Initialize COM - ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); - ZEN_ON_SCOPE_EXIT(::CoUninitialize()); - - //2. Initialize system image list - typedef BOOL (WINAPI* FileIconInitFun)(BOOL fRestoreCache); - const SysDllFun fileIconInit(L"Shell32.dll", reinterpret_cast(660)); //MS requires and documents this magic number - assert(fileIconInit); - if (fileIconInit) - fileIconInit(false); //TRUE to restore the system image cache from disk; FALSE otherwise. -#endif - - while (true) - { - boost::this_thread::interruption_point(); - - const Zstring fileName = workload_->extractNextFile(); //start work: blocks until next icon to load is retrieved - - if (!buffer_->hasFileIcon(fileName)) //perf: workload may contain duplicate entries? - buffer_->moveIntoBuffer(fileName, getAssociatedIcon(fileName, iconSizeType)); - } -} - -//######################### redirect to impl ##################################################### - -struct IconBuffer::Pimpl -{ - Pimpl() : - workload(std::make_shared()), - buffer (std::make_shared()) {} - - std::shared_ptr workload; - std::shared_ptr buffer; - - boost::thread worker; -}; - - -IconBuffer::IconBuffer(IconSize sz) : pimpl(make_unique()), - iconSizeType(sz), - genDirIcon (::getGenericDirectoryIcon(sz).extractWxBitmap()), - genFileIcon(::getGenericFileIcon (sz).extractWxBitmap()) -{ - pimpl->worker = boost::thread(WorkerThread(pimpl->workload, pimpl->buffer, sz)); -} - - -IconBuffer::~IconBuffer() -{ - setWorkload(std::list()); //make sure interruption point is always reached! - pimpl->worker.interrupt(); - pimpl->worker.join(); //we assume precondition "worker.joinable()"!!! -} - - -int IconBuffer::getSize(IconSize icoSize) -{ - switch (icoSize) - { - case IconBuffer::SIZE_SMALL: -#if defined ZEN_WIN || defined ZEN_MAC - return 16; -#elif defined ZEN_LINUX - return 24; -#endif - case IconBuffer::SIZE_MEDIUM: -#ifdef ZEN_WIN - if (!wereVistaOrLater) return 32; //48x48 doesn't look sharp on XP -#endif - return 48; - - case IconBuffer::SIZE_LARGE: - return 128; - } - assert(false); - return 0; -} - - -bool IconBuffer::readyForRetrieval(const Zstring& filename) -{ -#ifdef ZEN_WIN - if (iconSizeType == IconBuffer::SIZE_SMALL) - if (isCheapExtension(getFileExtension(filename))) - return true; -#endif - return pimpl->buffer->hasFileIcon(filename); -} - - -Opt IconBuffer::retrieveFileIcon(const Zstring& filename) -{ -#ifdef ZEN_WIN - //perf: let's read icons which don't need file access right away! No async delay justified! - if (iconSizeType == IconBuffer::SIZE_SMALL) //non-thumbnail view, we need file type icons only! - { - const Zstring& extension = getFileExtension(filename); - if (isCheapExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension - { - if (Opt ico = pimpl->buffer->retrieveFileIcon(extension)) - return ico; - - //make sure icon is in buffer, even if icon needs not be retrieved! - pimpl->buffer->moveIntoBuffer(extension, getAssociatedIconByExt(extension, iconSizeType)); - - Opt ico = pimpl->buffer->retrieveFileIcon(extension); - assert(ico); - return ico; - } - } -#endif - - if (Opt ico = pimpl->buffer->retrieveFileIcon(filename)) - return ico; - - //since this icon seems important right now, we don't want to wait until next setWorkload() to start retrieving - pimpl->workload->addToWorkload(filename); - pimpl->buffer->limitBufferSize(); - return NoValue(); -} - - -void IconBuffer::setWorkload(const std::list& load) -{ - pimpl->workload->setWorkload(load); //since buffer can only increase due to new workload, - pimpl->buffer->limitBufferSize(); //this is the place to impose the limit from main thread! -} diff --git a/lib/icon_buffer.h b/lib/icon_buffer.h deleted file mode 100644 index efa5179f..00000000 --- a/lib/icon_buffer.h +++ /dev/null @@ -1,52 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef ICONBUFFER_H_INCLUDED -#define ICONBUFFER_H_INCLUDED - -#include -#include -#include -#include -#include - -namespace zen -{ -class IconBuffer -{ -public: - enum IconSize - { - SIZE_SMALL, - SIZE_MEDIUM, - SIZE_LARGE - }; - - IconBuffer(IconSize sz); - ~IconBuffer(); - - static int getSize(IconSize icoSizeType); //expected and *maximum* icon size in pixel - int getSize() const { return getSize(iconSizeType); } // - - const wxBitmap& genericFileIcon() { return genFileIcon; } - const wxBitmap& genericDirIcon () { return genDirIcon; } - - bool readyForRetrieval(const Zstring& filename); - Opt retrieveFileIcon(const Zstring& filename); - - void setWorkload(const std::list& load); //(re-)set new workload of icons to be retrieved; - -private: - struct Pimpl; - std::unique_ptr pimpl; - - const IconSize iconSizeType; - const wxBitmap genDirIcon; - const wxBitmap genFileIcon; -}; -} - -#endif // ICONBUFFER_H_INCLUDED diff --git a/lib/localization.cpp b/lib/localization.cpp deleted file mode 100644 index 3536ba70..00000000 --- a/lib/localization.cpp +++ /dev/null @@ -1,485 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "localization.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "parse_plural.h" -#include "parse_lng.h" -#include "ffs_paths.h" - -#ifdef ZEN_LINUX -#include //wcscasecmp - -#elif defined ZEN_MAC -#include -#endif - -using namespace zen; - - -namespace -{ -class FFSTranslation : public TranslationHandler -{ -public: - FFSTranslation(const Zstring& filename, wxLanguage languageId); //throw lngfile::ParsingError, parse_plural::ParsingError - - wxLanguage langId() const { return langId_; } - - virtual std::wstring translate(const std::wstring& text) override - { - //look for translation in buffer table - auto it = transMapping.find(text); - if (it != transMapping.end() && !it->second.empty()) - return it->second; - return text; //fallback - } - - virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, std::int64_t n) override - { - auto it = transMappingPl.find(std::make_pair(singular, plural)); - if (it != transMappingPl.end()) - { - const size_t formNo = pluralParser->getForm(n); - if (formNo < it->second.size()) - return replaceCpy(it->second[formNo], L"%x", toGuiString(n)); - } - return replaceCpy(std::abs(n) == 1 ? singular : plural, L"%x", toGuiString(n)); //fallback - } - -private: - typedef hash_map Translation; //hash_map is 15% faster than std::map on GCC - typedef std::map, std::vector> TranslationPlural; - - Translation transMapping; //map original text |-> translation - TranslationPlural transMappingPl; - std::unique_ptr pluralParser; //bound! - wxLanguage langId_; -}; - - -FFSTranslation::FFSTranslation(const Zstring& filename, wxLanguage languageId) : langId_(languageId) //throw lngfile::ParsingError, parse_plural::ParsingError -{ - std::string inputStream; - try - { - inputStream = loadBinStream(filename); //throw FileError - } - catch (const FileError& e) - { - throw lngfile::ParsingError(e.toString(), 0, 0); //passing FileError is too high a level for Parsing error, OTOH user is unlikely to see this since file I/O issues are sorted out by ExistingTranslations()! - } - - lngfile::TransHeader header; - lngfile::TranslationMap transInput; - lngfile::TranslationPluralMap transPluralInput; - lngfile::parseLng(inputStream, header, transInput, transPluralInput); //throw ParsingError - - for (const auto& item : transInput) - { - const std::wstring original = utfCvrtTo(item.first); - const std::wstring translation = utfCvrtTo(item.second); - transMapping.insert(std::make_pair(original, translation)); - } - - for (const auto& item : transPluralInput) - { - const std::wstring engSingular = utfCvrtTo(item.first.first); - const std::wstring engPlural = utfCvrtTo(item.first.second); - - std::vector plFormsWide; - for (const std::string& pf : item.second) - plFormsWide.push_back(utfCvrtTo(pf)); - - transMappingPl.insert(std::make_pair(std::make_pair(engSingular, engPlural), plFormsWide)); - } - - pluralParser = make_unique(header.pluralDefinition); //throw parse_plural::ParsingError -} - - -class FindLngfiles : public zen::TraverseCallback -{ -public: - FindLngfiles(std::vector& lngFiles) : lngFiles_(lngFiles) {} - - virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) - { - if (endsWith(fullName, Zstr(".lng"))) - lngFiles_.push_back(fullName); - } - - virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) { return LINK_SKIP; } - virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& fullName) { return nullptr; } - virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) { assert(false); return ON_ERROR_IGNORE; } //errors are not really critical in this context - virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) { assert(false); return ON_ERROR_IGNORE; } // - -private: - std::vector& lngFiles_; -}; - - -struct LessTranslation -{ - bool operator()(const ExistingTranslations::Entry& lhs, const ExistingTranslations::Entry& rhs) const - { - //use a more "natural" sort: ignore case and diacritics -#ifdef ZEN_WIN - const int rv = ::CompareString(LOCALE_USER_DEFAULT, //__in LCID Locale, - NORM_IGNORECASE, //__in DWORD dwCmpFlags, - lhs.languageName.c_str(), //__in LPCTSTR lpString1, - static_cast(lhs.languageName.size()), //__in int cchCount1, - rhs.languageName.c_str(), //__in LPCTSTR lpString2, - static_cast(rhs.languageName.size())); //__in int cchCount2 - if (rv == 0) - throw std::runtime_error("Error comparing strings."); - else - return rv == CSTR_LESS_THAN; //convert to C-style string compare result - -#elif defined ZEN_LINUX - return ::wcscasecmp(lhs.languageName.c_str(), rhs.languageName.c_str()) < 0; //ignores case; locale-dependent! - //return lhs.languageName.CmpNoCase(rhs.languageName) < 0; - -#elif defined ZEN_MAC - auto allocCFStringRef = [](const std::wstring& str) -> CFStringRef //output not owned! - { - return ::CFStringCreateWithCString(nullptr, //CFAllocatorRef alloc, - utfCvrtTo(str).c_str(), //const char *cStr, - kCFStringEncodingUTF8); //CFStringEncoding encoding - }; - - CFStringRef langL = allocCFStringRef(lhs.languageName); - ZEN_ON_SCOPE_EXIT(::CFRelease(langL)); - - CFStringRef langR = allocCFStringRef(rhs.languageName); - ZEN_ON_SCOPE_EXIT(::CFRelease(langR)); - - return::CFStringCompare(langL, langR, kCFCompareLocalized | kCFCompareCaseInsensitive) == kCFCompareLessThan; //no-fail -#endif - } -}; -} - - -ExistingTranslations::ExistingTranslations() -{ - { - //default entry: - ExistingTranslations::Entry newEntry; - newEntry.languageID = wxLANGUAGE_ENGLISH_US; - newEntry.languageName = L"English (US)"; - newEntry.languageFile = L""; - newEntry.translatorName = L"Zenju"; - newEntry.languageFlag = L"flag_usa.png"; - locMapping.push_back(newEntry); - } - - //search language files available - std::vector lngFiles; - FindLngfiles traverseCallback(lngFiles); - - traverseFolder(zen::getResourceDir() + Zstr("Languages"), //throw(); - traverseCallback); - - for (const Zstring& filename : lngFiles) - { - try - { - const std::string stream = loadBinStream(filename); //throw FileError - - lngfile::TransHeader lngHeader; - lngfile::parseHeader(stream, lngHeader); //throw ParsingError - - assert(!lngHeader.languageName .empty()); - assert(!lngHeader.translatorName.empty()); - assert(!lngHeader.localeName .empty()); - assert(!lngHeader.flagFile .empty()); - /* - There is some buggy behavior in wxWidgets which maps "zh_TW" to simplified chinese. - Fortunately locales can be also entered as description. I changed to "Chinese (Traditional)" which works fine. - */ - if (const wxLanguageInfo* locInfo = wxLocale::FindLanguageInfo(utfCvrtTo(lngHeader.localeName))) - { - ExistingTranslations::Entry newEntry; - newEntry.languageID = locInfo->Language; - newEntry.languageName = utfCvrtTo(lngHeader.languageName); - newEntry.languageFile = utfCvrtTo(filename); - newEntry.translatorName = utfCvrtTo(lngHeader.translatorName); - newEntry.languageFlag = utfCvrtTo(lngHeader.flagFile); - locMapping.push_back(newEntry); - } - else assert(false); - } - catch (FileError&) { assert(false); } - catch (lngfile::ParsingError&) { assert(false); } //better not show an error message here; scenario: batch jobs - } - - std::sort(locMapping.begin(), locMapping.end(), LessTranslation()); -} - - -const std::vector& ExistingTranslations::get() -{ - static ExistingTranslations instance; - return instance.locMapping; -} - - -namespace -{ -wxLanguage mapLanguageDialect(wxLanguage language) -{ - switch (static_cast(language)) //avoid enumeration value wxLANGUAGE_*' not handled in switch [-Wswitch-enum] - { - //variants of wxLANGUAGE_ARABIC - case wxLANGUAGE_ARABIC_ALGERIA: - case wxLANGUAGE_ARABIC_BAHRAIN: - case wxLANGUAGE_ARABIC_EGYPT: - case wxLANGUAGE_ARABIC_IRAQ: - case wxLANGUAGE_ARABIC_JORDAN: - case wxLANGUAGE_ARABIC_KUWAIT: - case wxLANGUAGE_ARABIC_LEBANON: - case wxLANGUAGE_ARABIC_LIBYA: - case wxLANGUAGE_ARABIC_MOROCCO: - case wxLANGUAGE_ARABIC_OMAN: - case wxLANGUAGE_ARABIC_QATAR: - case wxLANGUAGE_ARABIC_SAUDI_ARABIA: - case wxLANGUAGE_ARABIC_SUDAN: - case wxLANGUAGE_ARABIC_SYRIA: - case wxLANGUAGE_ARABIC_TUNISIA: - case wxLANGUAGE_ARABIC_UAE: - case wxLANGUAGE_ARABIC_YEMEN: - return wxLANGUAGE_ARABIC; - - //variants of wxLANGUAGE_CHINESE_SIMPLIFIED - case wxLANGUAGE_CHINESE: - case wxLANGUAGE_CHINESE_SINGAPORE: - return wxLANGUAGE_CHINESE_SIMPLIFIED; - - //variants of wxLANGUAGE_CHINESE_TRADITIONAL - case wxLANGUAGE_CHINESE_TAIWAN: - case wxLANGUAGE_CHINESE_HONGKONG: - case wxLANGUAGE_CHINESE_MACAU: - return wxLANGUAGE_CHINESE_TRADITIONAL; - - //variants of wxLANGUAGE_DUTCH - case wxLANGUAGE_DUTCH_BELGIAN: - return wxLANGUAGE_DUTCH; - - //variants of wxLANGUAGE_ENGLISH_UK - case wxLANGUAGE_ENGLISH_AUSTRALIA: - case wxLANGUAGE_ENGLISH_NEW_ZEALAND: - case wxLANGUAGE_ENGLISH_TRINIDAD: - case wxLANGUAGE_ENGLISH_CARIBBEAN: - case wxLANGUAGE_ENGLISH_JAMAICA: - case wxLANGUAGE_ENGLISH_BELIZE: - case wxLANGUAGE_ENGLISH_EIRE: - case wxLANGUAGE_ENGLISH_SOUTH_AFRICA: - case wxLANGUAGE_ENGLISH_ZIMBABWE: - case wxLANGUAGE_ENGLISH_BOTSWANA: - case wxLANGUAGE_ENGLISH_DENMARK: - return wxLANGUAGE_ENGLISH_UK; - - //variants of wxLANGUAGE_ENGLISH_US - case wxLANGUAGE_ENGLISH: - case wxLANGUAGE_ENGLISH_CANADA: - case wxLANGUAGE_ENGLISH_PHILIPPINES: - return wxLANGUAGE_ENGLISH_US; - - //variants of wxLANGUAGE_FRENCH - case wxLANGUAGE_FRENCH_BELGIAN: - case wxLANGUAGE_FRENCH_CANADIAN: - case wxLANGUAGE_FRENCH_LUXEMBOURG: - case wxLANGUAGE_FRENCH_MONACO: - case wxLANGUAGE_FRENCH_SWISS: - return wxLANGUAGE_FRENCH; - - //variants of wxLANGUAGE_GERMAN - case wxLANGUAGE_GERMAN_AUSTRIAN: - case wxLANGUAGE_GERMAN_BELGIUM: - case wxLANGUAGE_GERMAN_LIECHTENSTEIN: - case wxLANGUAGE_GERMAN_LUXEMBOURG: - case wxLANGUAGE_GERMAN_SWISS: - return wxLANGUAGE_GERMAN; - - //variants of wxLANGUAGE_ITALIAN - case wxLANGUAGE_ITALIAN_SWISS: - return wxLANGUAGE_ITALIAN; - - //variants of wxLANGUAGE_NORWEGIAN_BOKMAL - case wxLANGUAGE_NORWEGIAN_NYNORSK: - return wxLANGUAGE_NORWEGIAN_BOKMAL; - - //variants of wxLANGUAGE_ROMANIAN - case wxLANGUAGE_MOLDAVIAN: - return wxLANGUAGE_ROMANIAN; - - //variants of wxLANGUAGE_RUSSIAN - case wxLANGUAGE_RUSSIAN_UKRAINE: - return wxLANGUAGE_RUSSIAN; - - //variants of wxLANGUAGE_SERBIAN - case wxLANGUAGE_SERBIAN_CYRILLIC: - case wxLANGUAGE_SERBIAN_LATIN: - case wxLANGUAGE_SERBO_CROATIAN: - return wxLANGUAGE_SERBIAN; - - //variants of wxLANGUAGE_SPANISH - case wxLANGUAGE_SPANISH_ARGENTINA: - case wxLANGUAGE_SPANISH_BOLIVIA: - case wxLANGUAGE_SPANISH_CHILE: - case wxLANGUAGE_SPANISH_COLOMBIA: - case wxLANGUAGE_SPANISH_COSTA_RICA: - case wxLANGUAGE_SPANISH_DOMINICAN_REPUBLIC: - case wxLANGUAGE_SPANISH_ECUADOR: - case wxLANGUAGE_SPANISH_EL_SALVADOR: - case wxLANGUAGE_SPANISH_GUATEMALA: - case wxLANGUAGE_SPANISH_HONDURAS: - case wxLANGUAGE_SPANISH_MEXICAN: - case wxLANGUAGE_SPANISH_MODERN: - case wxLANGUAGE_SPANISH_NICARAGUA: - case wxLANGUAGE_SPANISH_PANAMA: - case wxLANGUAGE_SPANISH_PARAGUAY: - case wxLANGUAGE_SPANISH_PERU: - case wxLANGUAGE_SPANISH_PUERTO_RICO: - case wxLANGUAGE_SPANISH_URUGUAY: - case wxLANGUAGE_SPANISH_US: - case wxLANGUAGE_SPANISH_VENEZUELA: - return wxLANGUAGE_SPANISH; - - //variants of wxLANGUAGE_SWEDISH - case wxLANGUAGE_SWEDISH_FINLAND: - return wxLANGUAGE_SWEDISH; - - //languages without variants: - //case wxLANGUAGE_CROATIAN: - //case wxLANGUAGE_CZECH: - //case wxLANGUAGE_DANISH: - //case wxLANGUAGE_FINNISH: - //case wxLANGUAGE_GREEK: - //case wxLANGUAGE_HEBREW: - //case wxLANGUAGE_HUNGARIAN: - //case wxLANGUAGE_JAPANESE: - //case wxLANGUAGE_KOREAN: - //case wxLANGUAGE_LITHUANIAN: - //case wxLANGUAGE_POLISH: - //case wxLANGUAGE_PORTUGUESE: - //case wxLANGUAGE_PORTUGUESE_BRAZILIAN: - //case wxLANGUAGE_SCOTS_GAELIC: - //case wxLANGUAGE_SLOVENIAN: - //case wxLANGUAGE_TURKISH: - //case wxLANGUAGE_UKRAINIAN: - default: - return language; - } -} - - -//global wxWidgets localization: sets up C localization runtime as well! -class wxWidgetsLocale -{ -public: - static void init(wxLanguage lng) - { - locale.reset(); //avoid global locale lifetime overlap! wxWidgets cannot handle this and will crash! - locale.reset(new wxLocale); - - const wxLanguageInfo* sysLngInfo = wxLocale::GetLanguageInfo(wxLocale::GetSystemLanguage()); - const wxLanguageInfo* selLngInfo = wxLocale::GetLanguageInfo(lng); - - const bool sysLangIsRTL = sysLngInfo ? sysLngInfo->LayoutDirection == wxLayout_RightToLeft : false; - const bool selectedLangIsRTL = selLngInfo ? selLngInfo->LayoutDirection == wxLayout_RightToLeft : false; - -#ifdef NDEBUG - wxLogNull dummy; //rather than implementing a reasonable error handling wxWidgets decides to shows a modal dialog in wxLocale::Init -> at least we can shut it up! -#endif - if (sysLangIsRTL == selectedLangIsRTL) - locale->Init(wxLANGUAGE_DEFAULT); //use sys-lang to preserve sub-language specific rules (e.g. german swiss number punctation) - else - locale->Init(lng); //have to use the supplied language to enable RTL layout different than user settings - locLng = lng; - } - - static void release() { locale.reset(); locLng = wxLANGUAGE_UNKNOWN; } - - static wxLanguage getLanguage() { return locLng; } - -private: - static std::unique_ptr locale; - static wxLanguage locLng; -}; -std::unique_ptr wxWidgetsLocale::locale; -wxLanguage wxWidgetsLocale::locLng = wxLANGUAGE_UNKNOWN; -} - - -void zen::releaseWxLocale() -{ - wxWidgetsLocale::release(); -} - - -void zen::setLanguage(int language) //throw FileError -{ - if (language == getLanguage() && wxWidgetsLocale::getLanguage() == language) - return; //support polling - - //(try to) retrieve language file - std::wstring languageFile; - - for (auto it = ExistingTranslations::get().begin(); it != ExistingTranslations::get().end(); ++it) - if (it->languageID == language) - { - languageFile = it->languageFile; - break; - } - - //load language file into buffer - if (languageFile.empty()) //if languageFile is empty, texts will be english by default - zen::setTranslator(); - else - try - { - zen::setTranslator(new FFSTranslation(utfCvrtTo(languageFile), static_cast(language))); //throw lngfile::ParsingError, parse_plural::ParsingError - } - catch (lngfile::ParsingError& e) - { - throw FileError(replaceCpy(replaceCpy(replaceCpy(_("Error parsing file %x, row %y, column %z."), - L"%x", fmtFileName(utfCvrtTo(languageFile))), - L"%y", numberTo(e.row_ + 1)), - L"%z", numberTo(e.col_ + 1)) - + L"\n\n" + e.msg_); - } - catch (parse_plural::ParsingError&) - { - throw FileError(L"Invalid plural form definition"); //user should never see this! - } - - //handle RTL swapping: we need wxWidgets to do this - wxWidgetsLocale::init(languageFile.empty() ? wxLANGUAGE_ENGLISH : static_cast(language)); -} - - -int zen::getLanguage() -{ - const FFSTranslation* loc = dynamic_cast(zen::getTranslator()); - return loc ? loc->langId() : wxLANGUAGE_ENGLISH_US; -} - - -int zen::retrieveSystemLanguage() -{ - return mapLanguageDialect(static_cast(wxLocale::GetSystemLanguage())); -} diff --git a/lib/localization.h b/lib/localization.h deleted file mode 100644 index 2d871dd7..00000000 --- a/lib/localization.h +++ /dev/null @@ -1,46 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef LOCALIZATION_H_8917342083178321534 -#define LOCALIZATION_H_8917342083178321534 - -#include -#include -//#include - -namespace zen -{ -class ExistingTranslations -{ -public: - struct Entry - { - int languageID; - std::wstring languageName; - std::wstring languageFile; - std::wstring translatorName; - std::wstring languageFlag; - }; - static const std::vector& get(); - -private: - ExistingTranslations(); - ExistingTranslations(const ExistingTranslations&); - ExistingTranslations& operator=(const ExistingTranslations&); - - std::vector locMapping; -}; - - -void setLanguage(int language); //throw FileError -int getLanguage(); -int retrieveSystemLanguage(); - -void releaseWxLocale(); //wxLocale crashes miserably on wxGTK when destructor runs during global cleanup => call in wxApp::OnExit -//"You should delete all wxWidgets object that you created by the time OnExit finishes. In particular, do not destroy them from application class' destructor!" -} - -#endif //LOCALIZATION_H_8917342083178321534 diff --git a/lib/lock_holder.h b/lib/lock_holder.h deleted file mode 100644 index 81b5632d..00000000 --- a/lib/lock_holder.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef LOCK_HOLDER_H_489572039485723453425 -#define LOCK_HOLDER_H_489572039485723453425 - -#include -#include -#include -#include "dir_lock.h" -#include "status_handler.h" -//#include "dir_exist_async.h" - -namespace zen -{ -const Zstring LOCK_FILE_ENDING = Zstr(".ffs_lock"); //intermediate locks created by DirLock use this extension, too! - -//hold locks for a number of directories without blocking during lock creation -//call after having checked directory existence! -class LockHolder -{ -public: - LockHolder(const std::set& dirnamesExisting, //resolved dirname ending with path separator - bool& warningDirectoryLockFailed, - ProcessCallback& procCallback) - { - for (auto it = dirnamesExisting.begin(); it != dirnamesExisting.end(); ++it) - { - const Zstring& dirnameFmt = *it; - assert(endsWith(dirnameFmt, FILE_NAME_SEPARATOR)); //this is really the contract, formatting does other things as well, e.g. macro substitution - - class WaitOnLockHandler : public DirLockCallback - { - public: - WaitOnLockHandler(ProcessCallback& pc) : pc_(pc) {} - virtual void requestUiRefresh() { pc_.requestUiRefresh(); } //allowed to throw exceptions - virtual void reportStatus(const std::wstring& text) { pc_.reportStatus(text); } - private: - ProcessCallback& pc_; - } callback(procCallback); - - try - { - //lock file creation is synchronous and may block noticeably for very slow devices (usb sticks, mapped cloud storages) - lockHolder.push_back(DirLock(dirnameFmt + Zstr("sync") + LOCK_FILE_ENDING, &callback)); //throw FileError - } - catch (const FileError& e) - { - const std::wstring msg = replaceCpy(_("Cannot set directory lock for %x."), L"%x", fmtFileName(dirnameFmt)) + L"\n\n" + e.toString(); - procCallback.reportWarning(msg, warningDirectoryLockFailed); //may throw! - } - } - } - -private: - std::vector lockHolder; -}; -} - -#endif //LOCK_HOLDER_H_489572039485723453425 diff --git a/lib/norm_filter.h b/lib/norm_filter.h deleted file mode 100644 index 552931e2..00000000 --- a/lib/norm_filter.h +++ /dev/null @@ -1,85 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef NORM_FILTER_H_INCLUDED -#define NORM_FILTER_H_INCLUDED - -#include "hard_filter.h" -#include "soft_filter.h" - -namespace zen -{ -struct NormalizedFilter //grade-a filter: global/local filter settings combined, units resolved, ready for use -{ - NormalizedFilter(const HardFilter::FilterRef& hf, const SoftFilter& sf) : nameFilter(hf), timeSizeFilter(sf) {} - - //"hard" filter: relevant during comparison, physically skips files - HardFilter::FilterRef nameFilter; - //"soft" filter: relevant after comparison; equivalent to user selection - SoftFilter timeSizeFilter; -}; - - -//combine global and local filters via "logical and" -NormalizedFilter normalizeFilters(const FilterConfig& global, const FilterConfig& local); - -inline -bool isNullFilter(const FilterConfig& filterCfg) -{ - return NameFilter::isNull(filterCfg.includeFilter, filterCfg.excludeFilter) && - SoftFilter(filterCfg.timeSpan, filterCfg.unitTimeSpan, - filterCfg.sizeMin, filterCfg.unitSizeMin, - filterCfg.sizeMax, filterCfg.unitSizeMax).isNull(); -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// ----------------------- implementation ----------------------- -inline -NormalizedFilter normalizeFilters(const FilterConfig& global, const FilterConfig& local) -{ - HardFilter::FilterRef globalName = std::make_shared(global.includeFilter, global.excludeFilter); - HardFilter::FilterRef localName = std::make_shared(local .includeFilter, local .excludeFilter); - - SoftFilter globalTimeSize(global.timeSpan, global.unitTimeSpan, - global.sizeMin, global.unitSizeMin, - global.sizeMax, global.unitSizeMax); - - SoftFilter localTimeSize(local.timeSpan, local.unitTimeSpan, - local.sizeMin, local.unitSizeMin, - local.sizeMax, local.unitSizeMax); - - return NormalizedFilter(combineFilters(globalName, localName), - combineFilters(globalTimeSize, localTimeSize)); -} -} - -#endif //NORM_FILTER_H_INCLUDED diff --git a/lib/osx_file_icon.h b/lib/osx_file_icon.h deleted file mode 100644 index 5edfd740..00000000 --- a/lib/osx_file_icon.h +++ /dev/null @@ -1,36 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef OSX_FILE_ICON_8427508422345342 -#define OSX_FILE_ICON_8427508422345342 - -#include -#include - -namespace osx -{ -struct ImageData -{ - ImageData(int w, int h) : width(w), height(h), rgb(w* h * 3), alpha(w* h) {} - ImageData(ImageData&& tmp) : width(tmp.width), height(tmp.height) { rgb.swap(tmp.rgb); alpha.swap(tmp.alpha); } - - const int width; - const int height; - std::vector rgb; //rgb-byte order for use with wxImage - std::vector alpha; - -private: - ImageData(const ImageData&); - ImageData& operator=(const ImageData&); -}; - -ImageData getThumbnail(const char* filename, int requestedSize); //throw SysError -ImageData getFileIcon (const char* filename, int requestedSize); //throw SysError -ImageData getDefaultFileIcon (int requestedSize); //throw SysError -ImageData getDefaultFolderIcon(int requestedSize); //throw SysError -} - -#endif //OSX_FILE_ICON_8427508422345342 diff --git a/lib/osx_file_icon.mm b/lib/osx_file_icon.mm deleted file mode 100644 index fb3c6490..00000000 --- a/lib/osx_file_icon.mm +++ /dev/null @@ -1,179 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "osx_file_icon.h" -#include -#include -#include - -namespace -{ -osx::ImageData extractBytes(NSImage* nsImg, int requestedSize) //throw SysError; NSException? -{ - /* - wxBitmap(NSImage*) is not good enough: it calls "[NSBitmapImageRep imageRepWithData:[img TIFFRepresentation]]" - => inefficient: TIFFRepresentation converts all contained images of an NSImage - => lacking: imageRepWithData extracts the first contained image only! - => wxBitmap(NSImage*) is wxCocoa only, deprecated! - => wxWidgets generally is not thread-safe so care must be taken to use wxBitmap from main thread only! (e.g. race-condition on non-atomic ref-count!!!) - - -> we need only a single bitmap at a specific size => extract raw bytes for use with wxImage in a thread-safe way! - */ - - //we choose the Core Graphics solution; for the equivalent App-Kit way see: http://www.cocoabuilder.com/archive/cocoa/193131-is-lockfocus-main-thread-specific.html#193191 - - ZEN_OSX_ASSERT(requestedSize > 0); - NSRect rectProposed = NSMakeRect(0, 0, requestedSize, requestedSize); //this is merely a hint! - - CGImageRef imgRef = [nsImg CGImageForProposedRect:&rectProposed context:nil hints:nil]; - ZEN_OSX_ASSERT(imgRef != NULL); //can this fail? not documented; ownership?? not documented! - - const size_t width = ::CGImageGetWidth (imgRef); - const size_t height = ::CGImageGetHeight(imgRef); - - ZEN_OSX_ASSERT(width > 0 && height > 0 && requestedSize > 0); - - int trgWidth = width; - int trgHeight = height; - - const int maxExtent = std::max(width, height); //don't stretch small images, but shrink large ones instead! - if (requestedSize < maxExtent) - { - trgWidth = width * requestedSize / maxExtent; - trgHeight = height * requestedSize / maxExtent; - } - - CGColorSpaceRef colorSpace = ::CGColorSpaceCreateDeviceRGB(); - ZEN_OSX_ASSERT(colorSpace != NULL); //may fail - ZEN_ON_SCOPE_EXIT(::CGColorSpaceRelease(colorSpace)); - - std::vector buf(trgWidth* trgHeight * 4); //32-bit ARGB, little endian byte order -> already initialized with 0 = fully transparent - - //supported color spaces: https://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-BCIBHHBB - CGContextRef ctxRef = ::CGBitmapContextCreate(&buf[0], //void *data, - trgWidth, //size_t width, - trgHeight, //size_t height, - 8, //size_t bitsPerComponent, - 4 * trgWidth, //size_t bytesPerRow, - colorSpace, //CGColorSpaceRef colorspace, - kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little); //CGBitmapInfo bitmapInfo - ZEN_OSX_ASSERT(ctxRef != NULL); - ZEN_ON_SCOPE_EXIT(::CGContextRelease(ctxRef)); - - ::CGContextDrawImage(ctxRef, CGRectMake(0, 0, trgWidth, trgHeight), imgRef); //can this fail? not documented - - //::CGContextFlush(ctxRef); //"If you pass [...] a bitmap context, this function does nothing." - - osx::ImageData imgOut(trgWidth, trgHeight); - - auto it = buf.begin(); - auto itOutRgb = imgOut.rgb.begin(); - auto itOutAlpha = imgOut.alpha.begin(); - for (int i = 0; i < trgWidth * trgHeight; ++i) - { - const unsigned char b = *it++; - const unsigned char g = *it++; - const unsigned char r = *it++; - const unsigned char a = *it++; - - //unsigned arithmetics caveat! - auto demultiplex = [&](unsigned char c) { return static_cast(numeric::confineCpy(a == 0 ? 0 : (c * 255 + a - 1) / a, 0, 255)); }; //=ceil(c * 255 / a) - - *itOutRgb++ = demultiplex(r); - *itOutRgb++ = demultiplex(g); - *itOutRgb++ = demultiplex(b); - *itOutAlpha++ = a; - } - - return imgOut; -} -} - - -osx::ImageData osx::getThumbnail(const char* filename, int requestedSize) //throw SysError -{ - @try - { - @autoreleasepool - { - NSString* nsFile = [NSString stringWithCString:filename encoding:NSUTF8StringEncoding]; - ZEN_OSX_ASSERT(nsFile != nil); //throw SysError; can this fail? not documented - //stringWithCString returns string which is already set to autorelease! - - NSImage* nsImg = [[[NSImage alloc] initWithContentsOfFile:nsFile] autorelease]; - ZEN_OSX_ASSERT(nsImg != nil); //may fail - - return extractBytes(nsImg, requestedSize); //throw SysError - } - } - @catch(NSException* e) - { - throwSysError(e); //throw SysError - } -} - - -osx::ImageData osx::getFileIcon(const char* filename, int requestedSize) //throw SysError -{ - @try - { - @autoreleasepool - { - NSString* nsFile = [NSString stringWithCString:filename encoding:NSUTF8StringEncoding]; - ZEN_OSX_ASSERT(nsFile != nil); //throw SysError; can this fail? not documented - //stringWithCString returns string which is already set to autorelease! - - NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFile:nsFile]; - ZEN_OSX_ASSERT(nsImg != nil); //can this fail? not documented - - return extractBytes(nsImg, requestedSize); //throw SysError - } - } - @catch (NSException* e) - { - throwSysError(e); //throw SysError - } -} - - -osx::ImageData osx::getDefaultFileIcon(int requestedSize) //throw SysError -{ - @try - { - @autoreleasepool - { - NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericDocumentIcon)]; - //NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFileType:@"dat"]; - ZEN_OSX_ASSERT(nsImg != nil); //can this fail? not documented - - return extractBytes(nsImg, requestedSize); //throw SysError - } - } - @catch (NSException* e) - { - throwSysError(e); //throw SysError - } -} - - -osx::ImageData osx::getDefaultFolderIcon(int requestedSize) //throw SysError -{ - @try - { - @autoreleasepool - { - NSImage* nsImg = [NSImage imageNamed:NSImageNameFolder]; - //NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericFolderIcon)]; - ZEN_OSX_ASSERT(nsImg != nil); //may fail - - return extractBytes(nsImg, requestedSize); //throw SysError - } - } - @catch (NSException* e) - { - throwSysError(e); //throw SysError - } -} diff --git a/lib/parallel_scan.cpp b/lib/parallel_scan.cpp deleted file mode 100644 index 9c6b16a9..00000000 --- a/lib/parallel_scan.cpp +++ /dev/null @@ -1,588 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "parallel_scan.h" -#include -#include -#include //includes -#include -#include -#include -#include "db_file.h" -#include "lock_holder.h" - -using namespace zen; - - -namespace -{ -/* -#ifdef ZEN_WIN - -struct DiskInfo -{ - DiskInfo() : - driveType(DRIVE_UNKNOWN), - diskID(-1) {} - - UINT driveType; - int diskID; // -1 if id could not be determined, this one is filled if driveType == DRIVE_FIXED or DRIVE_REMOVABLE; -}; - -inline -bool operator<(const DiskInfo& lhs, const DiskInfo& rhs) -{ - if (lhs.driveType != rhs.driveType) - return lhs.driveType < rhs.driveType; - - if (lhs.diskID < 0 || rhs.diskID < 0) - return false; - //consider "same", reason: one volume may be uniquely associated with one disk, while the other volume is associated to the same disk AND another one! - //volume <-> disk is 0..N:1..N - - return lhs.diskID < rhs.diskID ; -} - - -DiskInfo retrieveDiskInfo(const Zstring& pathName) -{ - std::vector volName(std::max(pathName.size(), static_cast(10000))); - - DiskInfo output; - - //full pathName need not yet exist! - if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName, - &volName[0], //__out LPTSTR lpszVolumePathName, - static_cast(volName.size()))) //__in DWORD cchBufferLength - return output; - - const Zstring rootPathName = &volName[0]; - - output.driveType = ::GetDriveType(rootPathName.c_str()); - - if (output.driveType == DRIVE_NO_ROOT_DIR) //these two should be the same error category - output.driveType = DRIVE_UNKNOWN; - - if (output.driveType != DRIVE_FIXED && output.driveType != DRIVE_REMOVABLE) - return output; //no reason to get disk ID - - //go and find disk id: - - //format into form: "\\.\C:" - Zstring volnameFmt = rootPathName; - if (endsWith(volnameFmt, FILE_NAME_SEPARATOR)) - volnameFmt.resize(volnameFmt.size() - 1); - volnameFmt = L"\\\\.\\" + volnameFmt; - - HANDLE hVolume = ::CreateFile(volnameFmt.c_str(), - 0, - FILE_SHARE_READ | FILE_SHARE_WRITE, - nullptr, - OPEN_EXISTING, - 0, - nullptr); - if (hVolume == INVALID_HANDLE_VALUE) - return output; - ZEN_ON_SCOPE_EXIT(::CloseHandle(hVolume)); - - std::vector buffer(sizeof(VOLUME_DISK_EXTENTS) + sizeof(DISK_EXTENT)); //reserve buffer for at most one disk! call below will then fail if volume spans multiple disks! - - DWORD bytesReturned = 0; - if (!::DeviceIoControl(hVolume, // handle to device - IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, // dwIoControlCode - nullptr, // lpInBuffer - 0, // nInBufferSize - &buffer[0], // output buffer - static_cast(buffer.size()), // size of output buffer - &bytesReturned, // number of bytes returned - nullptr)) // OVERLAPPED structure - return output; - - const VOLUME_DISK_EXTENTS& volDisks = *reinterpret_cast(&buffer[0]); - - if (volDisks.NumberOfDiskExtents != 1) - return output; - - output.diskID = volDisks.Extents[0].DiskNumber; - - return output; -} -#endif -*/ - -/* -PERF NOTE - --------------------------------------------- -|Testcase: Reading from two different disks| --------------------------------------------- -Windows 7: - 1st(unbuffered) |2nd (OS buffered) - ---------------------------------- -1 Thread: 57s | 8s -2 Threads: 39s | 7s - --------------------------------------------------- -|Testcase: Reading two directories from same disk| --------------------------------------------------- -Windows 7: Windows XP: - 1st(unbuffered) |2nd (OS buffered) 1st(unbuffered) |2nd (OS buffered) - ---------------------------------- ---------------------------------- -1 Thread: 41s | 13s 1 Thread: 45s | 13s -2 Threads: 42s | 11s 2 Threads: 38s | 8s - -=> Traversing does not take any advantage of file locality so that even multiple threads operating on the same disk impose no performance overhead! (even faster on XP) - -std::vector> separateByDistinctDisk(const std::set& dirkeys) -{ - //use one thread per physical disk: - typedef std::map> DiskKeyMapping; - DiskKeyMapping tmp; - std::for_each(dirkeys.begin(), dirkeys.end(), - [&](const DirectoryKey& key) { tmp[retrieveDiskInfo(key.dirnameFull_)].insert(key); }); - - std::vector> buckets; - std::transform(tmp.begin(), tmp.end(), std::back_inserter(buckets), - [&](const DiskKeyMapping::value_type& diskToKey) { return diskToKey.second; }); - return buckets; -} -*/ - -//------------------------------------------------------------------------------------------ -typedef Zbase BasicWString; //thread safe string class for UI texts - - -class AsyncCallback //actor pattern -{ -public: - AsyncCallback() : - notifyingThreadID(0), - textScanning(_("Scanning:")), - itemsScanned(0), - activeWorker(0) {} - - FillBufferCallback::HandleError reportError(const std::wstring& msg, size_t retryNumber) //blocking call: context of worker thread - { - boost::unique_lock dummy(lockErrorInfo); - while (errorInfo.get() || errorResponse.get()) - conditionCanReportError.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point! - - errorInfo = make_unique>(BasicWString(msg), retryNumber); - - while (!errorResponse.get()) - conditionGotResponse.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point! - - FillBufferCallback::HandleError rv = *errorResponse; - - errorInfo.reset(); - errorResponse.reset(); - - dummy.unlock(); //optimization for condition_variable::notify_all() - conditionCanReportError.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796 - - return rv; - } - - void processErrors(FillBufferCallback& callback) //context of main thread, call repreatedly - { - boost::unique_lock dummy(lockErrorInfo); - if (errorInfo.get() && !errorResponse.get()) - { - FillBufferCallback::HandleError rv = callback.reportError(copyStringTo(errorInfo->first), errorInfo->second); //throw! - errorResponse = make_unique(rv); - - dummy.unlock(); //optimization for condition_variable::notify_all() - conditionGotResponse.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796 - } - } - - void incrementNotifyingThreadId() { ++notifyingThreadID; } //context of main thread - - void reportCurrentFile(const Zstring& filename, long threadID) //context of worker thread - { - if (threadID != notifyingThreadID) return; //only one thread at a time may report status - - boost::lock_guard dummy(lockCurrentStatus); - currentFile = filename; - currentStatus.clear(); - } - - void reportCurrentStatus(const std::wstring& status, long threadID) //context of worker thread - { - if (threadID != notifyingThreadID) return; //only one thread may report status - - boost::lock_guard dummy(lockCurrentStatus); - currentFile.clear(); - currentStatus = BasicWString(status); //we cannot assume std::wstring to be thread safe (yet)! - } - - std::wstring getCurrentStatus() //context of main thread, call repreatedly - { - Zstring filename; - std::wstring statusMsg; - { - boost::lock_guard dummy(lockCurrentStatus); - if (!currentFile.empty()) - filename = currentFile; - else if (!currentStatus.empty()) - statusMsg = copyStringTo(currentStatus); - } - - if (!filename.empty()) - { - std::wstring statusText = copyStringTo(textScanning); - const long activeCount = activeWorker; - if (activeCount >= 2) - statusText += L" [" + replaceCpy(_P("1 thread", "%x threads", activeCount), L"%x", numberTo(activeCount)) + L"]"; - - statusText += L" " + fmtFileName(filename); - return statusText; - } - else - return statusMsg; - } - - void incItemsScanned() { ++itemsScanned; } - long getItemsScanned() const { return itemsScanned; } - - void incActiveWorker() { ++activeWorker; } - void decActiveWorker() { --activeWorker; } - long getActiveWorker() const { return activeWorker; } - -private: - //---- error handling ---- - boost::mutex lockErrorInfo; - boost::condition_variable conditionCanReportError; - boost::condition_variable conditionGotResponse; - std::unique_ptr> errorInfo; //error message + retry number - std::unique_ptr errorResponse; - - //---- status updates ---- - boost::detail::atomic_count notifyingThreadID; - //CAVEAT: do NOT use boost::thread::id as long as this showstopper exists: https://svn.boost.org/trac/boost/ticket/5754 - boost::mutex lockCurrentStatus; //use a different lock for current file: continue traversing while some thread may process an error - Zstring currentFile; //only one of these two is filled at a time! - BasicWString currentStatus; // - - const BasicWString textScanning; //this one is (currently) not shared and could be made a std::wstring, but we stay consistent and use thread-safe variables in this class only! - - //---- status updates II (lock free) ---- - boost::detail::atomic_count itemsScanned; - boost::detail::atomic_count activeWorker; -}; - -//------------------------------------------------------------------------------------------------- - -struct TraverserShared -{ -public: - TraverserShared(long threadID, - SymLinkHandling handleSymlinks, - const HardFilter::FilterRef& filter, - std::set& failedDirReads, - std::set& failedItemReads, - AsyncCallback& acb) : - handleSymlinks_(handleSymlinks), - filterInstance(filter), - failedDirReads_(failedDirReads), - failedItemReads_(failedItemReads), - acb_(acb), - threadID_(threadID) {} - - const SymLinkHandling handleSymlinks_; - const HardFilter::FilterRef filterInstance; //always bound! - - std::set& failedDirReads_; - std::set& failedItemReads_; - - AsyncCallback& acb_; - const long threadID_; -}; - - -class DirCallback : public zen::TraverseCallback -{ -public: - DirCallback(TraverserShared& config, - const Zstring& relNameParentPf, //postfixed with FILE_NAME_SEPARATOR! - DirContainer& output) : - cfg(config), - relNameParentPf_(relNameParentPf), - output_(output) {} - - virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details); - virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details); - virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& fullName); - virtual void releaseDirTraverser(TraverseCallback* trav); - - virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber); - virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName); - -private: - TraverserShared& cfg; - const Zstring relNameParentPf_; - DirContainer& output_; -}; - - -void DirCallback::onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) -{ - boost::this_thread::interruption_point(); - - const Zstring fileNameShort(shortName); - - //do not list the database file(s) sync.ffs_db, sync.x64.ffs_db, etc. or lock files - if (endsWith(fileNameShort, SYNC_DB_FILE_ENDING) || - endsWith(fileNameShort, LOCK_FILE_ENDING)) - return; - - //update status information no matter whether object is excluded or not! - cfg.acb_.reportCurrentFile(fullName, cfg.threadID_); - - //------------------------------------------------------------------------------------ - //apply filter before processing (use relative name!) - if (!cfg.filterInstance->passFileFilter(relNameParentPf_ + fileNameShort)) - return; - - // std::string fileId = details.fileSize >= 1024 * 1024U ? util::retrieveFileID(fullName) : std::string(); - /* - Perf test Windows 7, SSD, 350k files, 50k dirs, files > 1MB: 7000 - regular: 6.9s - ID per file: 43.9s - ID per file > 1MB: 7.2s - ID per dir: 8.4s - - Linux: retrieveFileID takes about 50% longer in VM! (avoidable because of redundant stat() call!) - */ - - output_.addSubFile(fileNameShort, FileDescriptor(details.lastWriteTime, details.fileSize, details.id, details.symlinkInfo != nullptr)); - - cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator -} - - -DirCallback::HandleLink DirCallback::onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) -{ - boost::this_thread::interruption_point(); - - //update status information no matter whether object is excluded or not! - cfg.acb_.reportCurrentFile(fullName, cfg.threadID_); - - switch (cfg.handleSymlinks_) - { - case SYMLINK_EXCLUDE: - return LINK_SKIP; - - case SYMLINK_USE_DIRECTLY: - if (cfg.filterInstance->passFileFilter(relNameParentPf_ + shortName)) //always use file filter: Link type may not be "stable" on Linux! - { - output_.addSubLink(shortName, LinkDescriptor(details.lastWriteTime)); - cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator - } - return LINK_SKIP; - - case SYMLINK_FOLLOW_LINK: - //filter symlinks before trying to follow them: handle user-excluded broken symlinks! - //since we don't know what the symlink will resolve to, only do this when both variants agree: - if (!cfg.filterInstance->passFileFilter(relNameParentPf_ + shortName)) - { - bool subObjMightMatch = true; - if (!cfg.filterInstance->passDirFilter(relNameParentPf_ + shortName, &subObjMightMatch)) - if (!subObjMightMatch) - return LINK_SKIP; - } - return LINK_FOLLOW; - } - - assert(false); - return LINK_SKIP; -} - - -TraverseCallback* DirCallback::onDir(const Zchar* shortName, const Zstring& fullName) -{ - boost::this_thread::interruption_point(); - - //update status information no matter whether object is excluded or not! - cfg.acb_.reportCurrentFile(fullName, cfg.threadID_); - - //------------------------------------------------------------------------------------ - const Zstring& relPath = relNameParentPf_ + shortName; - - //apply filter before processing (use relative name!) - bool subObjMightMatch = true; - const bool passFilter = cfg.filterInstance->passDirFilter(relPath, &subObjMightMatch); - if (!passFilter && !subObjMightMatch) - return nullptr; //do NOT traverse subdirs - //else: attention! ensure directory filtering is applied later to exclude actually filtered directories - - DirContainer& subDir = output_.addSubDir(shortName); - if (passFilter) - cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator - - return new DirCallback(cfg, relPath + FILE_NAME_SEPARATOR, subDir); //releaseDirTraverser() is guaranteed to be called in any case -} - - -void DirCallback::releaseDirTraverser(TraverseCallback* trav) -{ - TraverseCallback::releaseDirTraverser(trav); //no-op, introduce compile-time coupling - delete trav; -} - - -DirCallback::HandleError DirCallback::reportDirError(const std::wstring& msg, size_t retryNumber) -{ - switch (cfg.acb_.reportError(msg, retryNumber)) - { - case FillBufferCallback::ON_ERROR_IGNORE: - cfg.failedDirReads_.insert(relNameParentPf_); - return ON_ERROR_IGNORE; - - case FillBufferCallback::ON_ERROR_RETRY: - return ON_ERROR_RETRY; - } - assert(false); - return ON_ERROR_IGNORE; -} - - -DirCallback::HandleError DirCallback::reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) -{ - switch (cfg.acb_.reportError(msg, retryNumber)) - { - case FillBufferCallback::ON_ERROR_IGNORE: - cfg.failedItemReads_.insert(relNameParentPf_ + shortName); - return ON_ERROR_IGNORE; - - case FillBufferCallback::ON_ERROR_RETRY: - return ON_ERROR_RETRY; - } - assert(false); - return ON_ERROR_IGNORE; -} - - -#ifdef ZEN_WIN -class DstHackCallbackImpl : public DstHackCallback -{ -public: - DstHackCallbackImpl(AsyncCallback& acb, long threadID) : - acb_(acb), - threadID_(threadID), - textApplyingDstHack(replaceCpy(_("Encoding extended time information: %x"), L"%x", L"\n%x")) {} - -private: - virtual void requestUiRefresh(const Zstring& filename) //applying DST hack imposes significant one-time performance drawback => callback to inform user - { - acb_.reportCurrentStatus(replaceCpy(textApplyingDstHack, L"%x", fmtFileName(filename)), threadID_); - } - - AsyncCallback& acb_; - long threadID_; - const std::wstring textApplyingDstHack; -}; -#endif - -//------------------------------------------------------------------------------------------ - -class WorkerThread -{ -public: - WorkerThread(long threadID, - const std::shared_ptr& acb, - const DirectoryKey& dirKey, - DirectoryValue& dirOutput) : - threadID_(threadID), - acb_(acb), - dirKey_(dirKey), - dirOutput_(dirOutput) {} - - void operator()() //thread entry - { - acb_->incActiveWorker(); - ZEN_ON_SCOPE_EXIT(acb_->decActiveWorker();); - - acb_->reportCurrentFile(dirKey_.dirnameFull_, threadID_); //just in case first directory access is blocking - - TraverserShared travCfg(threadID_, - dirKey_.handleSymlinks_, //shared by all(!) instances of DirCallback while traversing a folder hierarchy - dirKey_.filter_, - dirOutput_.failedDirReads, - dirOutput_.failedItemReads, - *acb_); - - DirCallback traverser(travCfg, - Zstring(), - dirOutput_.dirCont); - - DstHackCallback* dstCallbackPtr = nullptr; -#ifdef ZEN_WIN - DstHackCallbackImpl dstCallback(*acb_, threadID_); - dstCallbackPtr = &dstCallback; -#endif - - //get all files and folders from directoryPostfixed (and subdirectories) - traverseFolder(dirKey_.dirnameFull_, traverser, dstCallbackPtr); //exceptions may be thrown! - } - -private: - long threadID_; - std::shared_ptr acb_; - const DirectoryKey dirKey_; - DirectoryValue& dirOutput_; -}; -} - - -void zen::fillBuffer(const std::set& keysToRead, //in - std::map& buf, //out - FillBufferCallback& callback, - size_t updateInterval) -{ - buf.clear(); - - FixedList worker; //note: we cannot use std::vector: compiler error on GCC 4.7, probably a boost screw-up - - zen::ScopeGuard guardWorker = zen::makeGuard([&] - { - for (boost::thread& wt : worker) - wt.interrupt(); //interrupt all at once first, then join - for (boost::thread& wt : worker) - if (wt.joinable()) //= precondition of thread::join(), which throws an exception if violated! - wt.join(); //in this context it is possible a thread is *not* joinable anymore due to the thread::timed_join() below! - }); - - auto acb = std::make_shared(); - - //init worker threads - for (const DirectoryKey& key : keysToRead) - { - assert(buf.find(key) == buf.end()); - DirectoryValue& dirOutput = buf[key]; - - const long threadId = static_cast(worker.size()); - worker.emplace_back(WorkerThread(threadId, acb, key, dirOutput)); - } - - //wait until done - for (boost::thread& wt : worker) - { - do - { - //update status - callback.reportStatus(acb->getCurrentStatus(), acb->getItemsScanned()); //throw! - - //process errors - acb->processErrors(callback); - } - while (!wt.timed_join(boost::posix_time::milliseconds(updateInterval))); - - acb->incrementNotifyingThreadId(); //process info messages of one thread at a time only - } - - guardWorker.dismiss(); -} diff --git a/lib/parallel_scan.h b/lib/parallel_scan.h deleted file mode 100644 index 408882bf..00000000 --- a/lib/parallel_scan.h +++ /dev/null @@ -1,76 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef PARALLEL_SCAN_H_INCLUDED -#define PARALLEL_SCAN_H_INCLUDED - -#include -#include -#include "hard_filter.h" -#include "../structures.h" -#include "../file_hierarchy.h" - -namespace zen -{ -struct DirectoryKey -{ - DirectoryKey(const Zstring& dirnameFull, - const HardFilter::FilterRef& filter, - SymLinkHandling handleSymlinks) : - dirnameFull_(dirnameFull), - filter_(filter), - handleSymlinks_(handleSymlinks) {} - - Zstring dirnameFull_; - HardFilter::FilterRef filter_; //filter interface: always bound by design! - SymLinkHandling handleSymlinks_; -}; - -inline -bool operator<(const DirectoryKey& lhs, const DirectoryKey& rhs) -{ - if (lhs.handleSymlinks_ != rhs.handleSymlinks_) - return lhs.handleSymlinks_ < rhs.handleSymlinks_; - - const int cmpName = cmpFileName(lhs.dirnameFull_, rhs.dirnameFull_); - if (cmpName != 0) - return cmpName < 0; - - return *lhs.filter_ < *rhs.filter_; -} - - -struct DirectoryValue -{ - DirContainer dirCont; - std::set failedDirReads; //relative postfixed names (or empty string for root) for directories that could not be read (completely), e.g. access denied, or temporal network drop - std::set failedItemReads; //relative postfixed names (never empty) for failure to read single file/dir/symlink -}; - - -class FillBufferCallback -{ -public: - virtual ~FillBufferCallback() {} - - enum HandleError - { - ON_ERROR_RETRY, - ON_ERROR_IGNORE - }; - virtual HandleError reportError (const std::wstring& msg, size_t retryNumber) = 0; //may throw! - virtual void reportStatus(const std::wstring& msg, int itemsTotal ) = 0; // -}; - -//attention: ensure directory filtering is applied later to exclude filtered directories which have been kept as parent folders - -void fillBuffer(const std::set& keysToRead, //in - std::map& buf, //out - FillBufferCallback& callback, - size_t updateInterval); //unit: [ms] -} - -#endif // PARALLEL_SCAN_H_INCLUDED diff --git a/lib/parse_lng.h b/lib/parse_lng.h deleted file mode 100644 index 19a8e751..00000000 --- a/lib/parse_lng.h +++ /dev/null @@ -1,706 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef PARSE_LNG_HEADER_INCLUDED -#define PARSE_LNG_HEADER_INCLUDED - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "parse_plural.h" -//#include - -namespace lngfile -{ -//singular forms -typedef std::map TranslationMap; //orig |-> translation - -//plural forms -typedef std::pair SingularPluralPair; //1 house| n houses -typedef std::vector PluralForms; //1 dom | 2 domy | 5 domw -typedef std::map TranslationPluralMap; //(sing/plu) |-> pluralforms - -struct TransHeader -{ - TransHeader() : pluralCount(0) {} - std::string languageName; //display name: "English (UK)" - std::string translatorName; //"Zenju" - std::string localeName; //ISO 639 language code + ISO 3166 country code, e.g. "en_GB", or "en_US" - std::string flagFile; //"england.png" - int pluralCount; //2 - std::string pluralDefinition; //"n == 1 ? 0 : 1" -}; - - -struct ParsingError -{ - ParsingError(const std::wstring& msg, size_t row, size_t col) : msg_(msg), row_(row), col_(col) {} - std::wstring msg_; //parser error message - size_t row_; //starting with 0 - size_t col_; // -}; -void parseLng(const std::string& fileStream, TransHeader& header, TranslationMap& out, TranslationPluralMap& pluralOut); //throw ParsingError -void parseHeader(const std::string& fileStream, TransHeader& header); //throw ParsingError - -class TranslationUnorderedList; //unordered list of unique translation items -std::string generateLng(const TranslationUnorderedList& in, const TransHeader& header); - - - - - - - - - - - - - - - - - - - -//--------------------------- implementation --------------------------- -enum class TranslationNewItemPos -{ - REL, - TOP -}; - -class TranslationUnorderedList //unordered list of unique translation items -{ -public: - TranslationUnorderedList(TranslationNewItemPos newItemPos, TranslationMap&& transOld, TranslationPluralMap&& transPluralOld) : - newItemPos_(newItemPos), transOld_(std::move(transOld)), transPluralOld_(std::move(transPluralOld)) {} - - void addItem(const std::string& orig) - { - if (!transUnique.insert(orig).second) return; - auto it = transOld_.find(orig); - if (it != transOld_.end() && !it->second.empty()) //preserve old translation from .lng file if existing - sequence.push_back(std::make_shared(std::make_pair(orig, it->second))); - else - switch (newItemPos_) - { - case TranslationNewItemPos::REL: - sequence.push_back(std::make_shared(std::make_pair(orig, std::string()))); - break; - case TranslationNewItemPos::TOP: - sequence.push_front(std::make_shared(std::make_pair(orig, std::string()))); //put untranslated items to the front of the .lng filebreak; - break; - } - } - - void addItem(const SingularPluralPair& orig) - { - if (!pluralUnique.insert(orig).second) return; - auto it = transPluralOld_.find(orig); - if (it != transPluralOld_.end() && !it->second.empty()) //preserve old translation from .lng file if existing - sequence.push_back(std::make_shared(std::make_pair(orig, it->second))); - else - switch (newItemPos_) - { - case TranslationNewItemPos::REL: - sequence.push_back(std::make_shared(std::make_pair(orig, PluralForms()))); - break; - case TranslationNewItemPos::TOP: - sequence.push_front(std::make_shared(std::make_pair(orig, PluralForms()))); //put untranslated items to the front of the .lng file - break; - } - } - - bool untranslatedTextExists() const { return std::any_of(sequence.begin(), sequence.end(), [](const std::shared_ptr& item) { return !item->hasTranslation(); }); } - - template - void visitItems(Function onTrans, Function2 onPluralTrans) const //onTrans takes (const TranslationMap::value_type&), onPluralTrans takes (const TranslationPluralMap::value_type&) - { - for (const auto& item : sequence) - if (auto regular = dynamic_cast(item.get())) - onTrans(regular->value); - else if (auto plural = dynamic_cast(item.get())) - onPluralTrans(plural->value); - else assert(false); - } - -private: - struct Item { virtual ~Item() {} virtual bool hasTranslation() const = 0; }; - struct RegularItem : public Item { RegularItem(const TranslationMap ::value_type& val) : value(val) {} virtual bool hasTranslation() const { return !value.second.empty(); } TranslationMap ::value_type value; }; - struct PluralItem : public Item { PluralItem (const TranslationPluralMap::value_type& val) : value(val) {} virtual bool hasTranslation() const { return !value.second.empty(); } TranslationPluralMap::value_type value; }; - - const TranslationNewItemPos newItemPos_; - std::list> sequence; //ordered list of translation elements - - std::set transUnique; //check uniqueness - std::set pluralUnique; // - - const TranslationMap transOld_; //reuse existing translation - const TranslationPluralMap transPluralOld_; // -}; - - -struct Token -{ - enum Type - { - //header information - TK_HEADER_BEGIN, - TK_HEADER_END, - TK_LANG_NAME_BEGIN, - TK_LANG_NAME_END, - TK_TRANS_NAME_BEGIN, - TK_TRANS_NAME_END, - TK_LOCALE_NAME_BEGIN, - TK_LOCALE_NAME_END, - TK_FLAG_FILE_BEGIN, - TK_FLAG_FILE_END, - TK_PLURAL_COUNT_BEGIN, - TK_PLURAL_COUNT_END, - TK_PLURAL_DEF_BEGIN, - TK_PLURAL_DEF_END, - - //item level - TK_SRC_BEGIN, - TK_SRC_END, - TK_TRG_BEGIN, - TK_TRG_END, - TK_TEXT, - TK_PLURAL_BEGIN, - TK_PLURAL_END, - TK_END - }; - - Token(Type t) : type(t) {} - Type type; - - std::string text; -}; - - -class KnownTokens -{ -public: - typedef std::map TokenMap; - - static const TokenMap& getList() - { - static KnownTokens inst; - return inst.tokens; - } - - static std::string text(Token::Type t) - { - auto it = getList().find(t); - return it != getList().end() ? it->second : std::string(); - } - -private: - KnownTokens() - { - //header information - tokens.insert(std::make_pair(Token::TK_HEADER_BEGIN, "
    ")); - tokens.insert(std::make_pair(Token::TK_HEADER_END, "
    ")); - tokens.insert(std::make_pair(Token::TK_LANG_NAME_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_LANG_NAME_END, "")); - tokens.insert(std::make_pair(Token::TK_TRANS_NAME_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_TRANS_NAME_END, "")); - tokens.insert(std::make_pair(Token::TK_LOCALE_NAME_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_LOCALE_NAME_END, "")); - tokens.insert(std::make_pair(Token::TK_FLAG_FILE_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_FLAG_FILE_END, "")); - tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_END, "")); - tokens.insert(std::make_pair(Token::TK_PLURAL_DEF_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_PLURAL_DEF_END, "")); - - //item level - tokens.insert(std::make_pair(Token::TK_SRC_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_SRC_END, "")); - tokens.insert(std::make_pair(Token::TK_TRG_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_TRG_END, "")); - tokens.insert(std::make_pair(Token::TK_PLURAL_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_PLURAL_END, "")); - } - TokenMap tokens; -}; - - -class Scanner -{ -public: - Scanner(const std::string& fileStream) : stream(fileStream), pos(stream.begin()) - { - if (zen::startsWith(stream, zen::BYTE_ORDER_MARK_UTF8)) - pos += zen::strLength(zen::BYTE_ORDER_MARK_UTF8); - } - - Token nextToken() - { - //skip whitespace - pos = std::find_if(pos, stream.end(), [](char c) { return !zen::isWhiteSpace(c); }); - - if (pos == stream.end()) - return Token(Token::TK_END); - - for (const auto& token : KnownTokens::getList()) - if (startsWith(token.second)) - { - pos += token.second.size(); - return Token(token.first); - } - - //rest must be "text" - auto itBegin = pos; - while (pos != stream.end() && !startsWithKnownTag()) - pos = std::find(pos + 1, stream.end(), '<'); - - std::string text(itBegin, pos); - - normalize(text); //remove whitespace from end ect. - - if (text.empty() && pos == stream.end()) - return Token(Token::TK_END); - - Token out(Token::TK_TEXT); - out.text = text; - return out; - } - - size_t posRow() const //current row beginning with 0 - { - //count line endings - const size_t crSum = std::count(stream.begin(), pos, '\r'); //carriage returns - const size_t nlSum = std::count(stream.begin(), pos, '\n'); //new lines - assert(crSum == 0 || nlSum == 0 || crSum == nlSum); - return std::max(crSum, nlSum); //be compatible with Linux/Mac/Win - } - - size_t posCol() const //current col beginning with 0 - { - //seek beginning of line - for (auto it = pos; it != stream.begin(); ) - { - --it; - if (*it == '\r' || *it == '\n') - return pos - it - 1; - } - return pos - stream.begin(); - } - -private: - bool startsWithKnownTag() const - { - return std::any_of(KnownTokens::getList().begin(), KnownTokens::getList().end(), - [&](const KnownTokens::TokenMap::value_type& p) { return startsWith(p.second); }); - } - - bool startsWith(const std::string& prefix) const - { - if (stream.end() - pos < static_cast(prefix.size())) - return false; - return std::equal(prefix.begin(), prefix.end(), pos); - } - - static void normalize(std::string& text) - { - zen::trim(text); //remove whitespace from both ends - - //Delimiter: - //---------- - //Linux: 0xA \n - //Mac: 0xD \r - //Win: 0xD 0xA \r\n <- language files are in Windows format - zen::replace(text, "\r\n", '\n'); // - zen::replace(text, "\r", '\n'); //ensure c-style line breaks - } - - const std::string stream; - std::string::const_iterator pos; -}; - - -class LngParser -{ -public: - LngParser(const std::string& fileStream) : scn(fileStream), tk(scn.nextToken()) {} - - void parse(TranslationMap& out, TranslationPluralMap& pluralOut, TransHeader& header) - { - parseHeader(header); - - try - { - parse_plural::PluralFormInfo pi(header.pluralDefinition, header.pluralCount); - - //items - while (token().type != Token::TK_END) - parseRegular(out, pluralOut, pi); - } - catch (const parse_plural::InvalidPluralForm&) - { - throw ParsingError(L"Invalid plural form definition", scn.posRow(), scn.posCol()); - } - } - - void parseHeader(TransHeader& header) - { - consumeToken(Token::TK_HEADER_BEGIN); - - consumeToken(Token::TK_LANG_NAME_BEGIN); - header.languageName = tk.text; - consumeToken(Token::TK_TEXT); - consumeToken(Token::TK_LANG_NAME_END); - - consumeToken(Token::TK_TRANS_NAME_BEGIN); - header.translatorName = tk.text; - consumeToken(Token::TK_TEXT); - consumeToken(Token::TK_TRANS_NAME_END); - - consumeToken(Token::TK_LOCALE_NAME_BEGIN); - header.localeName = tk.text; - consumeToken(Token::TK_TEXT); - consumeToken(Token::TK_LOCALE_NAME_END); - - consumeToken(Token::TK_FLAG_FILE_BEGIN); - header.flagFile = tk.text; - consumeToken(Token::TK_TEXT); - consumeToken(Token::TK_FLAG_FILE_END); - - consumeToken(Token::TK_PLURAL_COUNT_BEGIN); - header.pluralCount = zen::stringTo(tk.text); - consumeToken(Token::TK_TEXT); - consumeToken(Token::TK_PLURAL_COUNT_END); - - consumeToken(Token::TK_PLURAL_DEF_BEGIN); - header.pluralDefinition = tk.text; - consumeToken(Token::TK_TEXT); - consumeToken(Token::TK_PLURAL_DEF_END); - - consumeToken(Token::TK_HEADER_END); - } - -private: - void parseRegular(TranslationMap& out, TranslationPluralMap& pluralOut, const parse_plural::PluralFormInfo& pluralInfo) - { - consumeToken(Token::TK_SRC_BEGIN); - - if (token().type == Token::TK_PLURAL_BEGIN) - return parsePlural(pluralOut, pluralInfo); - - if (token().type != Token::TK_TEXT) - throw ParsingError(L"Source text empty", scn.posRow(), scn.posCol()); - std::string original = tk.text; - nextToken(); - - consumeToken(Token::TK_SRC_END); - - consumeToken(Token::TK_TRG_BEGIN); - std::string translation; - if (token().type == Token::TK_TEXT) - { - translation = token().text; - nextToken(); - } - consumeToken(Token::TK_TRG_END); - - validateTranslation(original, translation); //throw throw ParsingError - out.insert(std::make_pair(original, translation)); - } - - void parsePlural(TranslationPluralMap& pluralOut, const parse_plural::PluralFormInfo& pluralInfo) - { - //Token::TK_SRC_BEGIN already consumed - - consumeToken(Token::TK_PLURAL_BEGIN); - std::string engSingular = tk.text; - consumeToken(Token::TK_TEXT); - consumeToken(Token::TK_PLURAL_END); - - consumeToken(Token::TK_PLURAL_BEGIN); - std::string engPlural = tk.text; - consumeToken(Token::TK_TEXT); - consumeToken(Token::TK_PLURAL_END); - - consumeToken(Token::TK_SRC_END); - - consumeToken(Token::TK_TRG_BEGIN); - - PluralForms pluralList; - while (token().type == Token::TK_PLURAL_BEGIN) - { - consumeToken(Token::TK_PLURAL_BEGIN); - std::string pluralForm = tk.text; - consumeToken(Token::TK_TEXT); - consumeToken(Token::TK_PLURAL_END); - pluralList.push_back(pluralForm); - } - - consumeToken(Token::TK_TRG_END); - - const SingularPluralPair original(engSingular, engPlural); - validateTranslation(original, pluralList, pluralInfo); - pluralOut.insert(std::make_pair(original, pluralList)); - } - - void validateTranslation(const std::string& original, const std::string& translation) //throw ParsingError - { - if (original.empty()) - throw ParsingError(L"Source translation is empty", scn.posRow(), scn.posCol()); - - if (!translation.empty()) - { - //if original contains placeholder, so should translation! - auto checkPlaceholder = [&](const std::string& placeholder) - { - if (zen::contains(original, placeholder) && - !zen::contains(translation, placeholder)) - throw ParsingError(zen::replaceCpy(L"Placeholder %x missing in translation", L"%x", zen::utfCvrtTo(placeholder)), scn.posRow(), scn.posCol()); - }; - checkPlaceholder("%x"); - checkPlaceholder("%y"); - checkPlaceholder("%z"); - - //if source contains ampersand to mark menu accellerator key, so must translation - if (hasSingleAmpersand(original) && !hasSingleAmpersand(translation)) - throw ParsingError(L"Translation is missing the & character to mark an access key for the menu item", scn.posRow(), scn.posCol()); - } - } - - void validateTranslation(const SingularPluralPair& original, const PluralForms& translation, const parse_plural::PluralFormInfo& pluralInfo) //throw ParsingError - { - using namespace zen; - //check the primary placeholder is existing at least for the second english text - if (!contains(original.second, "%x")) - throw ParsingError(L"Plural form source does not contain %x placeholder", scn.posRow(), scn.posCol()); - - if (!translation.empty()) - { - //check for invalid number of plural forms - if (pluralInfo.getCount() != static_cast(translation.size())) - throw ParsingError(replaceCpy(replaceCpy(L"Invalid number of plural forms; actual: %x, expected: %y", L"%x", numberTo(translation.size())), L"%y", numberTo(pluralInfo.getCount())), scn.posRow(), scn.posCol()); - - //check for duplicate plural form translations (catch copy & paste errors for single-number form translations) - for (auto it = translation.begin(); it != translation.end(); ++it) - if (!contains(*it, "%x")) - { - auto it2 = std::find(it + 1, translation.end(), *it); - if (it2 != translation.end()) - throw ParsingError(replaceCpy(L"Duplicate plural form translation at index position %x", L"%x", numberTo(it2 - translation.begin())), scn.posRow(), scn.posCol()); - } - - for (int pos = 0; pos < static_cast(translation.size()); ++pos) - if (pluralInfo.isSingleNumberForm(pos)) - { - //translation needs to use decimal number if english source does so (e.g. frequently changing text like statistics) - if (contains(original.first, "%x") || - contains(original.first, "1")) - { - const int firstNumber = pluralInfo.getFirstNumber(pos); - if (!(contains(translation[pos], "%x") || - contains(translation[pos], numberTo(firstNumber)))) - throw ParsingError(replaceCpy(replaceCpy(L"Plural form translation at index position %y needs to use the decimal number %z or the %x placeholder", - L"%y", numberTo(pos)), L"%z", numberTo(firstNumber)), scn.posRow(), scn.posCol()); - } - } - else - { - //ensure the placeholder is used when needed - if (!contains(translation[pos], "%x")) - throw ParsingError(replaceCpy(L"Plural form at index position %y is missing the %x placeholder", L"%y", numberTo(pos)), scn.posRow(), scn.posCol()); - } - - 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::utfCvrtTo(placeholder)), scn.posRow(), scn.posCol()); - - //secondary placeholder is required for all plural forms - if (!std::all_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::utfCvrtTo(placeholder)), scn.posRow(), scn.posCol()); - } - }; - - checkSecondaryPlaceholder("%y"); - checkSecondaryPlaceholder("%z"); - } - } - - static bool hasSingleAmpersand(const std::string& str) - { - size_t pos = 0; - for (;;) - { - pos = str.find('&', pos); - if (pos == std::string::npos) - return false; - - bool freeBefore = pos == 0 || str[pos - 1] != '&'; - bool freeAfter = pos >= str.size() - 1 || str[pos + 1] != '&'; //str.size() > 0 here! - - if (freeBefore && freeAfter) //make sure to not catch && which windows resolves as just one & for display! - return true; - ++pos; - } - } - - void nextToken() { tk = scn.nextToken(); } - const Token& token() const { return tk; } - - void consumeToken(Token::Type t) //throw ParsingError - { - expectToken(t); //throw ParsingError - nextToken(); - } - - void expectToken(Token::Type t) //throw ParsingError - { - if (token().type != t) - throw ParsingError(L"Unexpected token", scn.posRow(), scn.posCol()); - } - - Scanner scn; - Token tk; -}; - - -inline -void parseLng(const std::string& fileStream, TransHeader& header, TranslationMap& out, TranslationPluralMap& pluralOut) //throw ParsingError -{ - out.clear(); - pluralOut.clear(); - - LngParser(fileStream).parse(out, pluralOut, header); -} - - -inline -void parseHeader(const std::string& fileStream, TransHeader& header) //throw ParsingError -{ - LngParser(fileStream).parseHeader(header); -} - - -inline -void formatMultiLineText(std::string& text) -{ - assert(!zen::contains(text, "\r\n")); - - if (text.find('\n') != std::string::npos) //multiple lines - { - if (*text.begin() != '\n') - text = '\n' + text; - if (*text.rbegin() != '\n') - text += '\n'; - } -} - - -std::string generateLng(const TranslationUnorderedList& in, const TransHeader& header) -{ - std::string out; - //header - out += KnownTokens::text(Token::TK_HEADER_BEGIN) + '\n'; - - out += '\t' + KnownTokens::text(Token::TK_LANG_NAME_BEGIN); - out += header.languageName; - out += KnownTokens::text(Token::TK_LANG_NAME_END) + '\n'; - - out += '\t' + KnownTokens::text(Token::TK_TRANS_NAME_BEGIN); - out += header.translatorName; - out += KnownTokens::text(Token::TK_TRANS_NAME_END) + '\n'; - - out += '\t' + KnownTokens::text(Token::TK_LOCALE_NAME_BEGIN); - out += header.localeName; - out += KnownTokens::text(Token::TK_LOCALE_NAME_END) + '\n'; - - out += '\t' + KnownTokens::text(Token::TK_FLAG_FILE_BEGIN); - out += header.flagFile; - out += KnownTokens::text(Token::TK_FLAG_FILE_END) + '\n'; - - out += '\t' + KnownTokens::text(Token::TK_PLURAL_COUNT_BEGIN); - out += zen::numberTo(header.pluralCount); - out += KnownTokens::text(Token::TK_PLURAL_COUNT_END) + '\n'; - - out += '\t' + KnownTokens::text(Token::TK_PLURAL_DEF_BEGIN); - out += header.pluralDefinition; - out += KnownTokens::text(Token::TK_PLURAL_DEF_END) + '\n'; - - out += KnownTokens::text(Token::TK_HEADER_END) + '\n'; - - out += '\n'; - - - in.visitItems([&](const TranslationMap::value_type& trans) - { - std::string original = trans.first; - std::string translation = trans.second; - - formatMultiLineText(original); - formatMultiLineText(translation); - - out += KnownTokens::text(Token::TK_SRC_BEGIN); - out += original; - out += KnownTokens::text(Token::TK_SRC_END) + '\n'; - - out += KnownTokens::text(Token::TK_TRG_BEGIN); - out += translation; - out += KnownTokens::text(Token::TK_TRG_END) + '\n' + '\n'; - }, - [&](const TranslationPluralMap::value_type& transPlural) - { - std::string engSingular = transPlural.first.first; - std::string engPlural = transPlural.first.second; - const PluralForms& forms = transPlural.second; - - formatMultiLineText(engSingular); - formatMultiLineText(engPlural); - - out += KnownTokens::text(Token::TK_SRC_BEGIN) + '\n'; - out += KnownTokens::text(Token::TK_PLURAL_BEGIN); - out += engSingular; - out += KnownTokens::text(Token::TK_PLURAL_END) + '\n'; - out += KnownTokens::text(Token::TK_PLURAL_BEGIN); - out += engPlural; - out += KnownTokens::text(Token::TK_PLURAL_END) + '\n'; - out += KnownTokens::text(Token::TK_SRC_END) + '\n'; - - out += KnownTokens::text(Token::TK_TRG_BEGIN); - out += '\n'; - - for (std::string plForm : forms) - { - formatMultiLineText(plForm); - - out += KnownTokens::text(Token::TK_PLURAL_BEGIN); - out += plForm; - out += KnownTokens::text(Token::TK_PLURAL_END) + '\n'; - } - out += KnownTokens::text(Token::TK_TRG_END) + '\n' + '\n'; - }); - - assert(!zen::contains(out, "\r\n") && !zen::contains(out, "\r")); - return zen::replaceCpy(out, '\n', "\r\n"); //back to win line endings -} -} - -#endif //PARSE_LNG_HEADER_INCLUDED diff --git a/lib/parse_plural.h b/lib/parse_plural.h deleted file mode 100644 index bac933c9..00000000 --- a/lib/parse_plural.h +++ /dev/null @@ -1,478 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef PARSE_PLURAL_H_INCLUDED -#define PARSE_PLURAL_H_INCLUDED - -#include -#include -#include -#include - -namespace parse_plural -{ -//expression interface -struct Expression { virtual ~Expression() {} }; - -template -struct Expr : public Expression -{ - typedef T ValueType; - virtual ValueType eval() const = 0; -}; - - -class ParsingError {}; - -class PluralForm -{ -public: - PluralForm(const std::string& stream); //throw ParsingError - int getForm(std::int64_t n) const { n_ = std::abs(n) ; return static_cast(expr->eval()); } - -private: - std::shared_ptr> expr; - mutable std::int64_t n_; -}; - - -//validate plural form -class InvalidPluralForm {}; - -class PluralFormInfo -{ -public: - PluralFormInfo(const std::string& definition, int pluralCount); //throw InvalidPluralForm - - int getCount() const { return static_cast(forms.size()); } - bool isSingleNumberForm(int index) const { return 0 <= index && index < static_cast(forms.size()) ? forms[index].count == 1 : false; } - int getFirstNumber (int index) const { return 0 <= index && index < static_cast(forms.size()) ? forms[index].firstNumber : -1; } - -private: - struct FormInfo - { - FormInfo() : count(0), firstNumber(0) {} - int count; - int firstNumber; //which maps to the plural form index position - }; - std::vector forms; -}; - - - - - -//--------------------------- implementation --------------------------- - -//http://www.gnu.org/software/hello/manual/gettext/Plural-forms.html -//http://translate.sourceforge.net/wiki/l10n/pluralforms -/* -Grammar for Plural forms parser -------------------------------- -expression: - conditional-expression - -conditional-expression: - logical-or-expression - logical-or-expression ? expression : expression - -logical-or-expression: - logical-and-expression - logical-or-expression || logical-and-expression - -logical-and-expression: - equality-expression - logical-and-expression && equality-expression - -equality-expression: - relational-expression - relational-expression == relational-expression - relational-expression != relational-expression - -relational-expression: - multiplicative-expression - multiplicative-expression > multiplicative-expression - multiplicative-expression < multiplicative-expression - multiplicative-expression >= multiplicative-expression - multiplicative-expression <= multiplicative-expression - -multiplicative-expression: - pm-expression - multiplicative-expression % pm-expression - -pm-expression: - variable-number-n-expression - constant-number-expression - ( expression ) - - -.po format,e.g.: (n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2) -*/ - -namespace implementation -{ -//specific binary expression based on STL function objects -template -struct BinaryExp : public Expr -{ - typedef std::shared_ptr> ExpLhs; - typedef std::shared_ptr> ExpRhs; - - BinaryExp(const ExpLhs& lhs, const ExpRhs& rhs, StlOp biop) : lhs_(lhs), rhs_(rhs), biop_(biop) { assert(lhs && rhs); } - virtual typename StlOp::result_type eval() const { return biop_(lhs_->eval(), rhs_->eval()); } -private: - ExpLhs lhs_; - ExpRhs rhs_; - StlOp biop_; -}; - -template inline -std::shared_ptr> makeBiExp(const std::shared_ptr& lhs, const std::shared_ptr& rhs, StlOp biop) //throw ParsingError -{ - auto exLeft = std::dynamic_pointer_cast>(lhs); - auto exRight = std::dynamic_pointer_cast>(rhs); - if (!exLeft || !exRight) - throw ParsingError(); - return std::make_shared>(exLeft, exRight, biop); -} - -template -struct ConditionalExp : public Expr -{ - ConditionalExp(const std::shared_ptr>& ifExp, - const std::shared_ptr>& thenExp, - const std::shared_ptr>& elseExp) : ifExp_(ifExp), thenExp_(thenExp), elseExp_(elseExp) { assert(ifExp && thenExp && elseExp); } - - virtual typename Expr::ValueType eval() const { return ifExp_->eval() ? thenExp_->eval() : elseExp_->eval(); } -private: - std::shared_ptr> ifExp_; - std::shared_ptr> thenExp_; - std::shared_ptr> elseExp_; -}; - -struct ConstNumberExp : public Expr -{ - ConstNumberExp(std::int64_t n) : n_(n) {} - virtual std::int64_t eval() const { return n_; } -private: - std::int64_t n_; -}; - -struct VariableNumberNExp : public Expr -{ - VariableNumberNExp(std::int64_t& n) : n_(n) {} - virtual std::int64_t eval() const { return n_; } -private: - std::int64_t& n_; -}; - -//------------------------------------------------------------------------------- - -struct Token -{ - enum Type - { - TK_TERNARY_QUEST, - TK_TERNARY_COLON, - TK_OR, - TK_AND, - TK_EQUAL, - TK_NOT_EQUAL, - TK_LESS, - TK_LESS_EQUAL, - TK_GREATER, - TK_GREATER_EQUAL, - TK_MODULUS, - TK_VARIABLE_N, - TK_CONST_NUMBER, - TK_BRACKET_LEFT, - TK_BRACKET_RIGHT, - TK_END - }; - - Token(Type t) : type(t), number(0) {} - Token(std::int64_t num) : type(TK_CONST_NUMBER), number(num) {} - - Type type; - std::int64_t number; //if type == TK_CONST_NUMBER -}; - -class Scanner -{ -public: - Scanner(const std::string& stream) : stream_(stream), pos(stream_.begin()) - { - tokens.push_back(std::make_pair("?" , Token::TK_TERNARY_QUEST)); - tokens.push_back(std::make_pair(":" , Token::TK_TERNARY_COLON)); - tokens.push_back(std::make_pair("||", Token::TK_OR )); - tokens.push_back(std::make_pair("&&", Token::TK_AND )); - tokens.push_back(std::make_pair("==", Token::TK_EQUAL )); - tokens.push_back(std::make_pair("!=", Token::TK_NOT_EQUAL )); - tokens.push_back(std::make_pair("<=", Token::TK_LESS_EQUAL )); - tokens.push_back(std::make_pair("<" , Token::TK_LESS )); - tokens.push_back(std::make_pair(">=", Token::TK_GREATER_EQUAL)); - tokens.push_back(std::make_pair(">" , Token::TK_GREATER )); - tokens.push_back(std::make_pair("%" , Token::TK_MODULUS )); - tokens.push_back(std::make_pair("n" , Token::TK_VARIABLE_N )); - tokens.push_back(std::make_pair("N" , Token::TK_VARIABLE_N )); - tokens.push_back(std::make_pair("(" , Token::TK_BRACKET_LEFT )); - tokens.push_back(std::make_pair(")" , Token::TK_BRACKET_RIGHT)); - } - - Token nextToken() - { - //skip whitespace - pos = std::find_if(pos, stream_.end(), [](char c) { return !zen::isWhiteSpace(c); }); - - if (pos == stream_.end()) - return Token::TK_END; - - for (auto iter = tokens.begin(); iter != tokens.end(); ++iter) - if (startsWith(iter->first)) - { - pos += iter->first.size(); - return Token(iter->second); - } - - auto digitEnd = std::find_if(pos, stream_.end(), [](char c) { return !zen::isDigit(c); }); - - if (digitEnd != pos) - { - auto number = zen::stringTo(std::string(pos, digitEnd)); - pos = digitEnd; - return number; - } - - throw ParsingError(); //unknown token - } - -private: - bool startsWith(const std::string& prefix) const - { - if (stream_.end() - pos < static_cast(prefix.size())) - return false; - return std::equal(prefix.begin(), prefix.end(), pos); - } - - typedef std::vector> TokenList; - TokenList tokens; - - const std::string stream_; - std::string::const_iterator pos; -}; - -//------------------------------------------------------------------------------- - -class Parser -{ -public: - Parser(const std::string& stream, std::int64_t& n) : - scn(stream), - tk(scn.nextToken()), - n_(n) {} - - std::shared_ptr> parse() //throw ParsingError; return value always bound! - { - auto e = std::dynamic_pointer_cast>(parseExpression()); //throw ParsingError - if (!e) - throw ParsingError(); - expectToken(Token::TK_END); - return e; - } - -private: - std::shared_ptr parseExpression() { return parseConditional(); }//throw ParsingError - - std::shared_ptr parseConditional() //throw ParsingError - { - std::shared_ptr e = parseLogicalOr(); - - if (token().type == Token::TK_TERNARY_QUEST) - { - nextToken(); - - auto ifExp = std::dynamic_pointer_cast>(e); - auto thenExp = std::dynamic_pointer_cast>(parseExpression()); //associativity: <- - - expectToken(Token::TK_TERNARY_COLON); - nextToken(); - - auto elseExp = std::dynamic_pointer_cast>(parseExpression()); // - if (!ifExp || !thenExp || !elseExp) - throw ParsingError(); - return std::make_shared>(ifExp, thenExp, elseExp); - } - return e; - } - - std::shared_ptr parseLogicalOr() - { - std::shared_ptr e = parseLogicalAnd(); - while (token().type == Token::TK_OR) //associativity: -> - { - nextToken(); - - std::shared_ptr rhs = parseLogicalAnd(); - e = makeBiExp(e, rhs, std::logical_or()); //throw ParsingError - } - return e; - } - - std::shared_ptr parseLogicalAnd() - { - std::shared_ptr e = parseEquality(); - while (token().type == Token::TK_AND) //associativity: -> - { - nextToken(); - std::shared_ptr rhs = parseEquality(); - - e = makeBiExp(e, rhs, std::logical_and()); //throw ParsingError - } - return e; - } - - std::shared_ptr parseEquality() - { - std::shared_ptr e = parseRelational(); - - Token::Type t = token().type; - if (t == Token::TK_EQUAL || //associativity: n/a - t == Token::TK_NOT_EQUAL) - { - nextToken(); - std::shared_ptr rhs = parseRelational(); - - if (t == Token::TK_EQUAL) return makeBiExp(e, rhs, std::equal_to ()); //throw ParsingError - if (t == Token::TK_NOT_EQUAL) return makeBiExp(e, rhs, std::not_equal_to()); // - } - return e; - } - - std::shared_ptr parseRelational() - { - std::shared_ptr e = parseMultiplicative(); - - Token::Type t = token().type; - if (t == Token::TK_LESS || //associativity: n/a - t == Token::TK_LESS_EQUAL || - t == Token::TK_GREATER || - t == Token::TK_GREATER_EQUAL) - { - nextToken(); - std::shared_ptr rhs = parseMultiplicative(); - - if (t == Token::TK_LESS) return makeBiExp(e, rhs, std::less ()); // - if (t == Token::TK_LESS_EQUAL) return makeBiExp(e, rhs, std::less_equal ()); //throw ParsingError - if (t == Token::TK_GREATER) return makeBiExp(e, rhs, std::greater ()); // - if (t == Token::TK_GREATER_EQUAL) return makeBiExp(e, rhs, std::greater_equal()); // - } - return e; - } - - std::shared_ptr parseMultiplicative() - { - std::shared_ptr e = parsePrimary(); - - while (token().type == Token::TK_MODULUS) //associativity: -> - { - nextToken(); - std::shared_ptr rhs = parsePrimary(); - - //"compile-time" check: n % 0 - if (auto literal = std::dynamic_pointer_cast(rhs)) - if (literal->eval() == 0) - throw ParsingError(); - - e = makeBiExp(e, rhs, std::modulus()); //throw ParsingError - } - return e; - } - - std::shared_ptr parsePrimary() - { - if (token().type == Token::TK_VARIABLE_N) - { - nextToken(); - return std::make_shared(n_); - } - else if (token().type == Token::TK_CONST_NUMBER) - { - const std::int64_t number = token().number; - nextToken(); - return std::make_shared(number); - } - else if (token().type == Token::TK_BRACKET_LEFT) - { - nextToken(); - std::shared_ptr e = parseExpression(); - - expectToken(Token::TK_BRACKET_RIGHT); - nextToken(); - return e; - } - else - throw ParsingError(); - } - - void nextToken() { tk = scn.nextToken(); } - const Token& token() const { return tk; } - - void expectToken(Token::Type t) //throw ParsingError - { - if (token().type != t) - throw ParsingError(); - } - - Scanner scn; - Token tk; - std::int64_t& n_; -}; -} - - -inline -PluralFormInfo::PluralFormInfo(const std::string& definition, int pluralCount) //throw InvalidPluralForm -{ - if (pluralCount < 1) - throw InvalidPluralForm(); - - forms.resize(pluralCount); - try - { - parse_plural::PluralForm pf(definition); //throw parse_plural::ParsingError - //PERF_START - - //perf: 80ns per iteration max (for arabic) - //=> 1000 iterations should be fast enough and still detect all "single number forms" - for (int j = 0; j < 1000; ++j) - { - int form = pf.getForm(j); - if (0 <= form && form < static_cast(forms.size())) - { - if (forms[form].count == 0) - forms[form].firstNumber = j; - ++forms[form].count; - } - else - throw InvalidPluralForm(); - } - } - catch (const parse_plural::ParsingError&) - { - throw InvalidPluralForm(); - } - - //ensure each form is used at least once: - if (!std::all_of(forms.begin(), forms.end(), [](const FormInfo& fi) { return fi.count >= 1; })) - throw InvalidPluralForm(); -} - - -inline -PluralForm::PluralForm(const std::string& stream) : expr(implementation::Parser(stream, n_).parse()) {} //throw ParsingError -} - -#endif // PARSE_PLURAL_H_INCLUDED diff --git a/lib/perf_check.cpp b/lib/perf_check.cpp deleted file mode 100644 index bf232add..00000000 --- a/lib/perf_check.cpp +++ /dev/null @@ -1,262 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "perf_check.h" - -#include -//#include -#include -#include -#include - -using namespace zen; - - -PerfCheck::PerfCheck(unsigned int windowSizeRemainingTime, - unsigned int windowSizeSpeed) : - windowSizeRemTime(windowSizeRemainingTime), - windowSizeSpeed_(windowSizeSpeed), - windowMax(std::max(windowSizeRemainingTime, windowSizeSpeed)) {} - - -PerfCheck::~PerfCheck() -{ - /* - //write samples to a file - wxFFile outputFile(wxT("statistics.dat"), wxT("w")); - - outputFile.Write(wxT("Time(ms);Objects;Data\n")); - - for (auto it = samples.begin(); it != samples.end(); ++it) - { - outputFile.Write(numberTo(it->first)); - outputFile.Write(wxT(";")); - outputFile.Write(numberTo(it->second.objCount_)); - outputFile.Write(wxT(";")); - outputFile.Write(numberTo(it->second.data_)); - outputFile.Write(wxT("\n")); - } - */ -} - - -void PerfCheck::addSample(int itemsCurrent, double dataCurrent, long timeMs) -{ - samples.insert(samples.end(), std::make_pair(timeMs, Record(itemsCurrent, dataCurrent))); //use fact that time is monotonously ascending - - //remove all records earlier than "now - windowMax" - const long newBegin = timeMs - windowMax; - auto it = samples.upper_bound(newBegin); - if (it != samples.begin()) - samples.erase(samples.begin(), --it); //keep one point before newBegin in order to handle "measurement holes" -} - - -inline -std::pair::value_type*, const std::multimap::value_type*> PerfCheck::getBlockFromEnd(long windowSize) const -{ - if (!samples.empty()) - { - auto itBack = samples.rbegin(); - //find start of records "window" - auto itFront = samples.upper_bound(itBack->first - windowSize); - if (itFront != samples.begin()) - --itFront; //one point before window begin in order to handle "measurement holes" - return std::make_pair(&*itFront, &*itBack); - } - return std::make_pair(nullptr, nullptr); -} - - -zen::Opt PerfCheck::getRemainingTime(double dataRemaining) const -{ - auto blk = getBlockFromEnd(windowSizeRemTime); - if (blk.first && blk.second) - { - const auto& itemFront = *blk.first; - const auto& itemBack = *blk.second; - //----------------------------------------------------------------------------------------------- - const long timeDelta = itemBack.first - itemFront.first; - const double dataDelta = itemBack.second.data_ - itemFront.second.data_; - - //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(dataDelta)) //sign(dataRemaining) != sign(dataDelta) usually an error, so show it! - return remainingTimeToString(dataRemaining * timeDelta / (1000.0 * dataDelta)); - } - return NoValue(); -} - - -zen::Opt PerfCheck::getBytesPerSecond() const -{ - auto blk = getBlockFromEnd(windowSizeSpeed_); - if (blk.first && blk.second) - { - const auto& itemFront = *blk.first; - const auto& itemBack = *blk.second; - //----------------------------------------------------------------------------------------------- - const long timeDelta = itemBack.first - itemFront.first; - const double dataDelta = itemBack.second.data_ - itemFront.second.data_; - - if (timeDelta != 0/* && dataDelta > 0*/) - return filesizeToShortString(zen::Int64(dataDelta * 1000.0 / timeDelta)) + _("/sec"); - } - return NoValue(); -} - - -zen::Opt PerfCheck::getItemsPerSecond() const -{ - auto blk = getBlockFromEnd(windowSizeSpeed_); - if (blk.first && blk.second) - { - const auto& itemFront = *blk.first; - const auto& itemBack = *blk.second; - //----------------------------------------------------------------------------------------------- - const long timeDelta = itemBack.first - itemFront.first; - const int itemsDelta = itemBack.second.itemCount_ - itemFront.second.itemCount_; - - if (timeDelta != 0) - return replaceCpy(_("%x items/sec"), L"%x", formatThreeDigitPrecision(itemsDelta * 1000.0 / timeDelta)); - } - return NoValue(); -} - - -/* -class for calculation of remaining time: ----------------------------------------- -"filesize |-> time" is an affine linear function f(x) = z_1 + z_2 x - -For given n measurements, sizes x_0, ..., x_n and times f_0, ..., f_n, the function f (as a polynom of degree 1) can be lineary approximated by - -z_1 = (r - s * q / p) / ((n + 1) - s * s / p) -z_2 = (q - s * z_1) / p = (r - (n + 1) z_1) / s - -with -p := x_0^2 + ... + x_n^2 -q := f_0 x_0 + ... + f_n x_n -r := f_0 + ... + f_n -s := x_0 + ... + x_n - -=> the time to process N files with amount of data D is: N * z_1 + D * z_2 - -Problem: --------- -Times f_0, ..., f_n can be very small so that precision of the PC clock is poor. -=> Times have to be accumulated to enhance precision: -Copying of m files with sizes x_i and times f_i (i = 1, ..., m) takes sum_i f(x_i) := m * z_1 + z_2 * sum x_i = sum f_i -With X defined as the accumulated sizes and F the accumulated times this gives: (in theory...) -m * z_1 + z_2 * X = F <=> -z_1 + z_2 * X / m = F / m - -=> we obtain a new (artificial) measurement with size X / m and time F / m to be used in the linear approximation above - - -Statistics::Statistics(int totalObjectCount, double totalDataAmount, unsigned recordCount) : - objectsTotal(totalObjectCount), - dataTotal(totalDataAmount), - recordsMax(recordCount), - objectsLast(0), - dataLast(0), - timeLast(wxGetLocalTimeMillis()), - z1_current(0), - z2_current(0), - dummyRecordPresent(false) {} - - -wxString Statistics::getRemainingTime(int objectsCurrent, double dataCurrent) -{ - //add new measurement point - const int m = objectsCurrent - objectsLast; - if (m != 0) - { - objectsLast = objectsCurrent; - - const double X = dataCurrent - dataLast; - dataLast = dataCurrent; - - const zen::Int64 timeCurrent = wxGetLocalTimeMillis(); - const double F = (timeCurrent - timeLast).ToDouble(); - timeLast = timeCurrent; - - record newEntry; - newEntry.x_i = X / m; - newEntry.f_i = F / m; - - //remove dummy record - if (dummyRecordPresent) - { - measurements.pop_back(); - dummyRecordPresent = false; - } - - //insert new record - measurements.push_back(newEntry); - if (measurements.size() > recordsMax) - measurements.pop_front(); - } - else //dataCurrent increased without processing new objects: - { //modify last measurement until m != 0 - const double X = dataCurrent - dataLast; //do not set dataLast, timeLast variables here, but write dummy record instead - if (!isNull(X)) - { - const zen::Int64 timeCurrent = wxGetLocalTimeMillis(); - const double F = (timeCurrent - timeLast).ToDouble(); - - record modifyEntry; - modifyEntry.x_i = X; - modifyEntry.f_i = F; - - //insert dummy record - if (!dummyRecordPresent) - { - measurements.push_back(modifyEntry); - if (measurements.size() > recordsMax) - measurements.pop_front(); - dummyRecordPresent = true; - } - else //modify dummy record - measurements.back() = modifyEntry; - } - } - - //calculate remaining time based on stored measurement points - double p = 0; - double q = 0; - double r = 0; - double s = 0; - for (const record& rec : measurements) - { - const double x_i = rec.x_i; - const double f_i = rec.f_i; - p += x_i * x_i; - q += f_i * x_i; - r += f_i; - s += x_i; - } - - if (!isNull(p)) - { - const double n = measurements.size(); - const double tmp = (n - s * s / p); - - if (!isNull(tmp) && !isNull(s)) - { - const double z1 = (r - s * q / p) / tmp; - const double z2 = (r - n * z1) / s; //not (n + 1) here, since n already is the number of measurements - - //refresh current values for z1, z2 - z1_current = z1; - z2_current = z2; - } - } - - return formatRemainingTime((objectsTotal - objectsCurrent) * z1_current + (dataTotal - dataCurrent) * z2_current); -} -*/ diff --git a/lib/perf_check.h b/lib/perf_check.h deleted file mode 100644 index 9d82be57..00000000 --- a/lib/perf_check.h +++ /dev/null @@ -1,45 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef STATISTICS_H_INCLUDED -#define STATISTICS_H_INCLUDED - -#include -#include -#include - -class PerfCheck -{ -public: - PerfCheck(unsigned int windowSizeRemainingTime, //unit: [ms] - unsigned int windowSizeSpeed); // - ~PerfCheck(); - - void addSample(int itemsCurrent, double dataCurrent, long timeMs); //timeMs must be ascending! - - zen::Opt getRemainingTime(double dataRemaining) const; - zen::Opt getBytesPerSecond() const; //for window - zen::Opt getItemsPerSecond() const; //for window - -private: - struct Record - { - Record(int itemCount, double data) : itemCount_(itemCount), data_(data) {} - int itemCount_; - double data_; //unit: [bytes] - }; - - std::pair::value_type*, - const std::multimap::value_type*> getBlockFromEnd(long windowSize) const; - - const long windowSizeRemTime; //unit: [ms] - const long windowSizeSpeed_; // - const long windowMax; - - std::map samples; //time, unit: [ms] -}; - -#endif // STATISTICS_H_INCLUDED diff --git a/lib/process_xml.cpp b/lib/process_xml.cpp deleted file mode 100644 index 3e3e72f1..00000000 --- a/lib/process_xml.cpp +++ /dev/null @@ -1,1577 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "process_xml.h" -#include -#include -#include "ffs_paths.h" -#include -#include -//#include -#include "xml_base.h" - -using namespace zen; -using namespace xmlAccess; //functionally needed for correct overload resolution!!! -using namespace std::rel_ops; - -namespace -{ -//------------------------------------------------------------------------------------------------------------------------------- -const int XML_FORMAT_VER_GLOBAL = 1; -const int XML_FORMAT_VER_FFS_GUI = 3; //for FFS 5.22 -const int XML_FORMAT_VER_FFS_BATCH = 3; // -//------------------------------------------------------------------------------------------------------------------------------- -} - -XmlType getXmlType(const zen::XmlDoc& doc) //throw() -{ - if (doc.root().getNameAs() == "FreeFileSync") - { - std::string type; - if (doc.root().getAttribute("XmlType", type)) - { - if (type == "GUI") - return XML_TYPE_GUI; - else if (type == "BATCH") - return XML_TYPE_BATCH; - else if (type == "GLOBAL") - return XML_TYPE_GLOBAL; - } - } - return XML_TYPE_OTHER; -} - - -XmlType xmlAccess::getXmlType(const Zstring& filename) //throw() -{ - try - { - //do NOT use zen::loadStream as it will needlessly load even huge files! - XmlDoc doc = loadXmlDocument(filename); //throw FfsXmlError, quick exit if file is not an FFS XML - return ::getXmlType(doc); - } - catch (const FfsXmlError&) - { - return XML_TYPE_OTHER; - } -} - - -void setXmlType(XmlDoc& doc, XmlType type) //throw() -{ - switch (type) - { - case XML_TYPE_GUI: - doc.root().setAttribute("XmlType", "GUI"); - break; - case XML_TYPE_BATCH: - doc.root().setAttribute("XmlType", "BATCH"); - break; - case XML_TYPE_GLOBAL: - doc.root().setAttribute("XmlType", "GLOBAL"); - break; - case XML_TYPE_OTHER: - assert(false); - break; - } -} - -//################################################################################################################ - -Zstring xmlAccess::getGlobalConfigFile() -{ - return zen::getConfigDir() + Zstr("GlobalSettings.xml"); -} - - -void xmlAccess::OptionalDialogs::resetDialogs() -{ - warningDependentFolders = true; - warningFolderPairRaceCondition = true; - warningSignificantDifference = true; - warningNotEnoughDiskSpace = true; - warningUnresolvedConflicts = true; - warningDatabaseError = true; - warningRecyclerMissing = true; - warningInputFieldEmpty = true; - warningDirectoryLockFailed = true; - popupOnConfigChange = true; - confirmSyncStart = true; - confirmExternalCommandMassInvoke = true; -} - - -xmlAccess::XmlGuiConfig xmlAccess::convertBatchToGui(const xmlAccess::XmlBatchConfig& batchCfg) //noexcept -{ - XmlGuiConfig output; - output.mainCfg = batchCfg.mainCfg; - - switch (batchCfg.handleError) - { - case ON_ERROR_POPUP: - case ON_ERROR_STOP: - output.handleError = ON_GUIERROR_POPUP; - break; - case ON_ERROR_IGNORE: - output.handleError = ON_GUIERROR_IGNORE; - break; - } - return output; -} - - -xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatch(const xmlAccess::XmlGuiConfig& guiCfg) //noexcept -{ - XmlBatchConfig output; //use default batch-settings - output.mainCfg = guiCfg.mainCfg; - - switch (guiCfg.handleError) - { - case ON_GUIERROR_POPUP: - output.handleError = ON_ERROR_POPUP; - break; - case ON_GUIERROR_IGNORE: - output.handleError = ON_ERROR_IGNORE; - break; - } - return output; -} - - -xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatchPreservingExistingBatch(const xmlAccess::XmlGuiConfig& guiCfg, const Zstring& referenceBatchFile) //noexcept -{ - //try to take over batch-specific settings from reference file if possible - if (!referenceBatchFile.empty()) - try - { - XmlBatchConfig batchCfg; - readConfig(referenceBatchFile, batchCfg); //throw FfsXmlError - - batchCfg.mainCfg = guiCfg.mainCfg; - return batchCfg; - } - catch (xmlAccess::FfsXmlError&) {} - - return convertGuiToBatch(guiCfg); -} - - -namespace -{ -std::vector splitFilterByLines(const Zstring& filterPhrase) -{ - if (filterPhrase.empty()) - return std::vector(); - return split(filterPhrase, Zstr('\n')); -} - -Zstring mergeFilterLines(const std::vector& filterLines) -{ - if (filterLines.empty()) - return Zstring(); - Zstring out = filterLines[0]; - std::for_each(filterLines.begin() + 1, filterLines.end(), [&](const Zstring& line) { out += Zstr('\n'); out += line; }); - return out; -} -} - - -namespace zen -{ -template <> inline -void writeText(const CompareVariant& value, std::string& output) -{ - switch (value) - { - case zen::CMP_BY_TIME_SIZE: - output = "TimeAndSize"; - break; - case zen::CMP_BY_CONTENT: - output = "Content"; - break; - } -} - -template <> inline -bool readText(const std::string& input, CompareVariant& value) -{ - std::string tmp = input; - zen::trim(tmp); - warn_static("remove after migration. 2013.08.20") - if (tmp == "ByTimeAndSize") //obsolete - value = zen::CMP_BY_TIME_SIZE; - else if (tmp == "ByContent") //obsolete - value = zen::CMP_BY_CONTENT; - else - - if (tmp == "TimeAndSize") - value = zen::CMP_BY_TIME_SIZE; - else if (tmp == "Content") - value = zen::CMP_BY_CONTENT; - else - return false; - return true; -} - - -template <> inline -void writeText(const SyncDirection& value, std::string& output) -{ - switch (value) - { - case SyncDirection::LEFT: - output = "left"; - break; - case SyncDirection::RIGHT: - output = "right"; - break; - case SyncDirection::NONE: - output = "none"; - break; - } -} - -template <> inline -bool readText(const std::string& input, SyncDirection& value) -{ - std::string tmp = input; - zen::trim(tmp); - if (tmp == "left") - value = SyncDirection::LEFT; - else if (tmp == "right") - value = SyncDirection::RIGHT; - else if (tmp == "none") - value = SyncDirection::NONE; - else - return false; - return true; -} - - -template <> inline -void writeText(const OnError& value, std::string& output) -{ - switch (value) - { - case ON_ERROR_IGNORE: - output = "Ignore"; - break; - case ON_ERROR_POPUP: - output = "Popup"; - break; - case ON_ERROR_STOP: - output = "Stop"; - break; - } -} - -template <> inline -bool readText(const std::string& input, OnError& value) -{ - std::string tmp = input; - zen::trim(tmp); - warn_static("remove after migration. 2013.08.20") - if (tmp == "Exit") //obsolete - value = ON_ERROR_STOP; - else if (tmp == "Abort") //obsolete: 2013.09.04 - value = ON_ERROR_STOP; - else - - if (tmp == "Ignore") - value = ON_ERROR_IGNORE; - else if (tmp == "Popup") - value = ON_ERROR_POPUP; - else if (tmp == "Stop") - value = ON_ERROR_STOP; - else - return false; - return true; -} - - -template <> inline -void writeText(const OnGuiError& value, std::string& output) -{ - switch (value) - { - case ON_GUIERROR_IGNORE: - output = "Ignore"; - break; - case ON_GUIERROR_POPUP: - output = "Popup"; - break; - } -} - -template <> inline -bool readText(const std::string& input, OnGuiError& value) -{ - std::string tmp = input; - zen::trim(tmp); - if (tmp == "Ignore") - value = ON_GUIERROR_IGNORE; - else if (tmp == "Popup") - value = ON_GUIERROR_POPUP; - else - return false; - return true; -} - - -template <> inline -void writeText(const FileIconSize& value, std::string& output) -{ - switch (value) - { - case ICON_SIZE_SMALL: - output = "Small"; - break; - case ICON_SIZE_MEDIUM: - output = "Medium"; - break; - case ICON_SIZE_LARGE: - output = "Large"; - break; - } -} - - -template <> inline -bool readText(const std::string& input, FileIconSize& value) -{ - std::string tmp = input; - zen::trim(tmp); - if (tmp == "Small") - value = ICON_SIZE_SMALL; - else if (tmp == "Medium") - value = ICON_SIZE_MEDIUM; - else if (tmp == "Large") - value = ICON_SIZE_LARGE; - else - return false; - return true; -} - - -template <> inline -void writeText(const DeletionPolicy& value, std::string& output) -{ - switch (value) - { - case DELETE_PERMANENTLY: - output = "Permanent"; - break; - case DELETE_TO_RECYCLER: - output = "RecycleBin"; - break; - case DELETE_TO_VERSIONING: - output = "Versioning"; - break; - } -} - -template <> inline -bool readText(const std::string& input, DeletionPolicy& value) -{ - std::string tmp = input; - zen::trim(tmp); - //------------------ - warn_static("remove after migration?") - if (tmp == "DeletePermanently")//obsolete name - value = DELETE_PERMANENTLY; - else if (tmp == "MoveToRecycleBin")//obsolete name - value = DELETE_TO_RECYCLER; - else if (tmp == "MoveToCustomDirectory")//obsolete name - value = DELETE_TO_VERSIONING; - else - - if (tmp == "Permanent") - value = DELETE_PERMANENTLY; - else if (tmp == "RecycleBin") - value = DELETE_TO_RECYCLER; - else if (tmp == "Versioning") - value = DELETE_TO_VERSIONING; - else - return false; - return true; -} - - -template <> inline -void writeText(const SymLinkHandling& value, std::string& output) -{ - switch (value) - { - case SYMLINK_EXCLUDE: - output = "Exclude"; - break; - case SYMLINK_USE_DIRECTLY: - output = "Direct"; - break; - case SYMLINK_FOLLOW_LINK: - output = "Follow"; - break; - } -} - -template <> inline -bool readText(const std::string& input, SymLinkHandling& value) -{ - std::string tmp = input; - zen::trim(tmp); - warn_static("remove after migration. 2013.08.20") - if (tmp == "UseDirectly") //obsolete! - value = SYMLINK_USE_DIRECTLY; - else if (tmp == "FollowLink") //obsolete! - value = SYMLINK_FOLLOW_LINK; - else if (tmp == "Ignore") //obsolete! - value = SYMLINK_EXCLUDE; - else - - if (tmp == "Exclude") - value = SYMLINK_EXCLUDE; - else if (tmp == "Direct") - value = SYMLINK_USE_DIRECTLY; - else if (tmp == "Follow") - value = SYMLINK_FOLLOW_LINK; - else - return false; - return true; -} - - -template <> inline -void writeText(const ColumnTypeRim& value, std::string& output) -{ - switch (value) - { - case COL_TYPE_DIRECTORY: - output = "Base"; - break; - case COL_TYPE_FULL_PATH: - output = "Full"; - break; - case COL_TYPE_REL_PATH: - output = "Rel"; - break; - case COL_TYPE_FILENAME: - output = "Name"; - break; - case COL_TYPE_SIZE: - output = "Size"; - break; - case COL_TYPE_DATE: - output = "Date"; - break; - case COL_TYPE_EXTENSION: - output = "Ext"; - break; - } -} - -template <> inline -bool readText(const std::string& input, ColumnTypeRim& value) -{ - std::string tmp = input; - zen::trim(tmp); - if (tmp == "Base") - value = COL_TYPE_DIRECTORY; - else if (tmp == "Full") - value = COL_TYPE_FULL_PATH; - else if (tmp == "Rel") - value = COL_TYPE_REL_PATH; - else if (tmp == "Name") - value = COL_TYPE_FILENAME; - else if (tmp == "Size") - value = COL_TYPE_SIZE; - else if (tmp == "Date") - value = COL_TYPE_DATE; - else if (tmp == "Ext") - value = COL_TYPE_EXTENSION; - else - return false; - return true; -} - - -template <> inline -void writeText(const ColumnTypeNavi& value, std::string& output) -{ - switch (value) - { - case COL_TYPE_NAVI_BYTES: - output = "Bytes"; - break; - case COL_TYPE_NAVI_DIRECTORY: - output = "Tree"; - break; - case COL_TYPE_NAVI_ITEM_COUNT: - output = "Count"; - break; - } -} - -template <> inline -bool readText(const std::string& input, ColumnTypeNavi& value) -{ - std::string tmp = input; - zen::trim(tmp); - if (tmp == "Bytes") - value = COL_TYPE_NAVI_BYTES; - else if (tmp == "Tree") - value = COL_TYPE_NAVI_DIRECTORY; - else if (tmp == "Count") - value = COL_TYPE_NAVI_ITEM_COUNT; - else - return false; - return true; -} - - -template <> inline -void writeText(const UnitSize& value, std::string& output) -{ - switch (value) - { - case USIZE_NONE: - output = "None"; - break; - case USIZE_BYTE: - output = "Byte"; - break; - case USIZE_KB: - output = "KB"; - break; - case USIZE_MB: - output = "MB"; - break; - } -} - -template <> inline -bool readText(const std::string& input, UnitSize& value) -{ - std::string tmp = input; - zen::trim(tmp); - warn_static("remove after migration. 2013.08.20") - if (tmp == "Inactive") //obsolete! - value = USIZE_NONE; - else - - if (tmp == "None") - value = USIZE_NONE; - else if (tmp == "Byte") - value = USIZE_BYTE; - else if (tmp == "KB") - value = USIZE_KB; - else if (tmp == "MB") - value = USIZE_MB; - else - return false; - return true; -} - -template <> inline -void writeText(const UnitTime& value, std::string& output) -{ - switch (value) - { - case UTIME_NONE: - output = "None"; - break; - case UTIME_TODAY: - output = "Today"; - break; - case UTIME_THIS_MONTH: - output = "Month"; - break; - case UTIME_THIS_YEAR: - output = "Year"; - break; - case UTIME_LAST_X_DAYS: - output = "x-days"; - break; - } -} - -template <> inline -bool readText(const std::string& input, UnitTime& value) -{ - std::string tmp = input; - zen::trim(tmp); - warn_static("remove after migration. 2013.08.20") - if (tmp == "Inactive") //obsolete! - value = UTIME_NONE; - else - - if (tmp == "None") - value = UTIME_NONE; - else if (tmp == "Today") - value = UTIME_TODAY; - else if (tmp == "Month") - value = UTIME_THIS_MONTH; - else if (tmp == "Year") - value = UTIME_THIS_YEAR; - else if (tmp == "x-days") - value = UTIME_LAST_X_DAYS; - else - return false; - return true; -} - -template <> inline -void writeText(const VersioningStyle& value, std::string& output) -{ - switch (value) - { - case VER_STYLE_REPLACE: - output = "Replace"; - break; - case VER_STYLE_ADD_TIMESTAMP: - output = "TimeStamp"; - break; - } -} - -template <> inline -bool readText(const std::string& input, VersioningStyle& value) -{ - std::string tmp = input; - zen::trim(tmp); - warn_static("remove after migration. 2013.08.20") - if (tmp == "AddTimeStamp") //obsolete - value = VER_STYLE_ADD_TIMESTAMP; - else - - if (tmp == "Replace") - value = VER_STYLE_REPLACE; - else if (tmp == "TimeStamp") - value = VER_STYLE_ADD_TIMESTAMP; - else - return false; - return true; -} - - -template <> inline -void writeText(const DirectionConfig::Variant& value, std::string& output) -{ - switch (value) - { - case DirectionConfig::TWOWAY: - output = "TwoWay"; - break; - case DirectionConfig::MIRROR: - output = "Mirror"; - break; - case DirectionConfig::UPDATE: - output = "Update"; - break; - case DirectionConfig::CUSTOM: - output = "Custom"; - break; - } -} - -template <> inline -bool readText(const std::string& input, DirectionConfig::Variant& value) -{ - std::string tmp = input; - zen::trim(tmp); - warn_static("remove after migration. 2013.08.20") - if (tmp == "Automatic") //obsolete! - value = DirectionConfig::TWOWAY; - else - - if (tmp == "TwoWay") - value = DirectionConfig::TWOWAY; - else if (tmp == "Mirror") - value = DirectionConfig::MIRROR; - else if (tmp == "Update") - value = DirectionConfig::UPDATE; - else if (tmp == "Custom") - value = DirectionConfig::CUSTOM; - else - return false; - return true; -} - - -template <> inline -bool readStruc(const XmlElement& input, ColumnAttributeRim& value) -{ - XmlIn in(input); - bool rv1 = in.attribute("Type", value.type_); - bool rv2 = in.attribute("Visible", value.visible_); - bool rv3 = in.attribute("Width", value.offset_); //offset == width if stretch is 0 - bool rv4 = in.attribute("Stretch", value.stretch_); - return rv1 && rv2 && rv3 && rv4; -} - -template <> inline -void writeStruc(const ColumnAttributeRim& value, XmlElement& output) -{ - XmlOut out(output); - out.attribute("Type", value.type_); - out.attribute("Visible", value.visible_); - out.attribute("Width", value.offset_); - out.attribute("Stretch", value.stretch_); -} - - -template <> inline -bool readStruc(const XmlElement& input, ColumnAttributeNavi& value) -{ - XmlIn in(input); - bool rv1 = in.attribute("Type", value.type_); - bool rv2 = in.attribute("Visible", value.visible_); - bool rv3 = in.attribute("Width", value.offset_); //offset == width if stretch is 0 - bool rv4 = in.attribute("Stretch", value.stretch_); - return rv1 && rv2 && rv3 && rv4; -} - -template <> inline -void writeStruc(const ColumnAttributeNavi& value, XmlElement& output) -{ - XmlOut out(output); - out.attribute("Type", value.type_); - out.attribute("Visible", value.visible_); - out.attribute("Width", value.offset_); - out.attribute("Stretch", value.stretch_); -} - - -template <> inline -bool readStruc(const XmlElement& input, ViewFilterDefault& value) -{ - XmlIn in(input); - - bool success = true; - auto readAttr = [&](XmlIn& elemIn, const char name[], bool& v) - { - if (!elemIn.attribute(name, v)) - success = false; - }; - - XmlIn catView = in["CategoryView"]; - readAttr(catView, "LeftOnly" , value.leftOnly); - readAttr(catView, "RightOnly" , value.rightOnly); - readAttr(catView, "LeftNewer" , value.leftNewer); - readAttr(catView, "RightNewer", value.rightNewer); - readAttr(catView, "Different" , value.different); - readAttr(catView, "Equal" , value.equal); - readAttr(catView, "Conflict" , value.conflict); - - XmlIn actView = in["ActionView"]; - readAttr(actView, "CreateLeft" , value.createLeft); - readAttr(actView, "CreateRight", value.createRight); - readAttr(actView, "UpdateLeft" , value.updateLeft); - readAttr(actView, "UpdateRight", value.updateRight); - readAttr(actView, "DeleteLeft" , value.deleteLeft); - readAttr(actView, "DeleteRight", value.deleteRight); - readAttr(actView, "DoNothing" , value.doNothing); - - return success; //[!] avoid short-circuit evaluation above -} - -template <> inline -void writeStruc(const ViewFilterDefault& value, XmlElement& output) -{ - XmlOut out(output); - - XmlOut catView = out["CategoryView"]; - catView.attribute("LeftOnly" , value.leftOnly); - catView.attribute("RightOnly" , value.rightOnly); - catView.attribute("LeftNewer" , value.leftNewer); - catView.attribute("RightNewer", value.rightNewer); - catView.attribute("Different" , value.different); - catView.attribute("Equal" , value.equal); - catView.attribute("Conflict" , value.conflict); - - XmlOut actView = out["ActionView"]; - actView.attribute("CreateLeft" , value.createLeft); - actView.attribute("CreateRight", value.createRight); - actView.attribute("UpdateLeft" , value.updateLeft); - actView.attribute("UpdateRight", value.updateRight); - actView.attribute("DeleteLeft" , value.deleteLeft); - actView.attribute("DeleteRight", value.deleteRight); - actView.attribute("DoNothing" , value.doNothing); -} - - -template <> inline -bool readStruc(const XmlElement& input, ConfigHistoryItem& value) -{ - XmlIn in(input); - bool rv1 = in(value.configFile); - //bool rv2 = in.attribute("LastUsed", value.lastUseTime); - return rv1 /*&& rv2*/; -} - - -template <> inline -void writeStruc(const ConfigHistoryItem& value, XmlElement& output) -{ - XmlOut out(output); - out(value.configFile); - //out.attribute("LastUsed", value.lastUseTime); -} -} - - -namespace -{ -void readConfig(const XmlIn& in, CompConfig& cmpConfig) -{ - in["Variant" ](cmpConfig.compareVar); - - warn_static("remove check after migration. 2013.09.7") - if (in["HandleSymlinks"])//obsolete name - in["HandleSymlinks"](cmpConfig.handleSymlinks); - else - in["Symlinks"](cmpConfig.handleSymlinks); -} - - -void readConfig(const XmlIn& in, DirectionConfig& directCfg) -{ - in["Variant"](directCfg.var); - - XmlIn inCustDir = in["CustomDirections"]; - inCustDir["LeftOnly" ](directCfg.custom.exLeftSideOnly); - inCustDir["RightOnly" ](directCfg.custom.exRightSideOnly); - inCustDir["LeftNewer" ](directCfg.custom.leftNewer); - inCustDir["RightNewer"](directCfg.custom.rightNewer); - inCustDir["Different" ](directCfg.custom.different); - inCustDir["Conflict" ](directCfg.custom.conflict); - - warn_static("remove check after migration. 2013.08.17") - if (in["DetectMovedFiles"]) //new value: remove check - in["DetectMovedFiles"](directCfg.detectMovedFiles); -} - - -void readConfig(const XmlIn& in, SyncConfig& syncCfg) -{ - readConfig(in, syncCfg.directionCfg); - - in["DeletionPolicy"](syncCfg.handleDeletion); - - warn_static("remove after migration?") - if (in["CustomDeletionFolder"]) - in["CustomDeletionFolder"](syncCfg.versioningDirectory);//obsolete name - else - in["VersioningFolder"](syncCfg.versioningDirectory); - - warn_static("remove after migration?") - if (in["VersioningFolder"] && - in["VersioningFolder"].get()->getAttribute("Style", syncCfg.versioningStyle)) //new parameter, do not complain when missing - ; - else if (in["VersioningStyle"]) //obsolete name - in["VersioningStyle"](syncCfg.versioningStyle); - else - syncCfg.versioningStyle = VER_STYLE_ADD_TIMESTAMP; //obsolete fallback -} - - -void readConfig(const XmlIn& in, FilterConfig& filter) -{ - warn_static("remove after migration?") - auto haveFilterAsSingleString = [&]() -> bool - { - if (in["Include"]) - if (auto elem = in["Include"].get()) - { - std::string tmp; - if (elem->getValue(tmp)) - return !tmp.empty(); - } - return false; - }; - if (haveFilterAsSingleString()) //obsolete style - { - in["Include"](filter.includeFilter); - in["Exclude"](filter.excludeFilter); - } - else - { - std::vector tmp = splitFilterByLines(filter.includeFilter); //default value - in["Include"](tmp); - filter.includeFilter = mergeFilterLines(tmp); - - std::vector tmp2 = splitFilterByLines(filter.excludeFilter); //default value - in["Exclude"](tmp2); - filter.excludeFilter = mergeFilterLines(tmp2); - } - - in["TimeSpan"](filter.timeSpan); - warn_static("remove after migration?") - if (in["UnitTimeSpan"]) in["UnitTimeSpan"](filter.unitTimeSpan);//obsolete name - else - in["TimeSpan"].attribute("Type", filter.unitTimeSpan); - - in["SizeMin"](filter.sizeMin); - if (in["UnitSizeMin"]) in["UnitSizeMin"](filter.unitSizeMin);//obsolete name - else - in["SizeMin"].attribute("Unit", filter.unitSizeMin); - - in["SizeMax"](filter.sizeMax); - if (in["UnitSizeMax"]) in["UnitSizeMax"](filter.unitSizeMax);//obsolete name - else - in["SizeMax"].attribute("Unit", filter.unitSizeMax); -} - - -void readConfig(const XmlIn& in, FolderPairEnh& enhPair) -{ - //read folder pairs - in["Left" ](enhPair.leftDirectory); - in["Right"](enhPair.rightDirectory); - - //########################################################### - //alternate comp configuration (optional) - if (XmlIn inAltCmp = in["CompareConfig"]) - { - CompConfig altCmpCfg; - readConfig(inAltCmp, altCmpCfg); - - enhPair.altCmpConfig = std::make_shared(altCmpCfg); - } - //########################################################### - //alternate sync configuration (optional) - if (XmlIn inAltSync = in["SyncConfig"]) - { - SyncConfig altSyncCfg; - readConfig(inAltSync, altSyncCfg); - - enhPair.altSyncConfig = std::make_shared(altSyncCfg); - } - - //########################################################### - //alternate filter configuration - if (XmlIn inLocFilter = in["LocalFilter"]) - readConfig(inLocFilter, enhPair.localFilter); -} - - -void readConfig(const XmlIn& in, MainConfiguration& mainCfg) -{ - //read compare settings - XmlIn inMain = in["MainConfig"]; - - readConfig(inMain["Comparison"], mainCfg.cmpConfig); - //########################################################### - - //read sync configuration - readConfig(inMain["SyncConfig"], mainCfg.syncCfg); - //########################################################### - - //read filter settings - readConfig(inMain["GlobalFilter"], mainCfg.globalFilter); - - //########################################################### - //read all folder pairs - mainCfg.additionalPairs.clear(); - - bool firstItem = true; - for (XmlIn inPair = inMain["FolderPairs"]["Pair"]; inPair; inPair.next()) - { - FolderPairEnh newPair; - readConfig(inPair, newPair); - - if (firstItem) - { - firstItem = false; - mainCfg.firstPair = newPair; //set first folder pair - } - else - mainCfg.additionalPairs.push_back(newPair); //set additional folder pairs - } - - warn_static("remove after migration?") - if (inMain["ExecuteWhenFinished"]) inMain["ExecuteWhenFinished"](mainCfg.onCompletion); //obsolete name - else - inMain["OnCompletion"](mainCfg.onCompletion); -} - - -void readConfig(const XmlIn& in, xmlAccess::XmlGuiConfig& config) -{ - readConfig(in, config.mainCfg); //read main config - - //read GUI specific config data - XmlIn inGuiCfg = in["GuiConfig"]; - - warn_static("remove after migration?") - if (inGuiCfg["HideFiltered" ]) //obsolete name - inGuiCfg["HideFiltered" ](config.hideExcludedItems); - else if (inGuiCfg["ShowFiltered" ]) //obsolete name - { - inGuiCfg["ShowFiltered"](config.hideExcludedItems); - config.hideExcludedItems = !config.hideExcludedItems; - } - else - inGuiCfg["HideExcluded"](config.hideExcludedItems); - - inGuiCfg["HandleError" ](config.handleError); - - warn_static("remove after migration?") - if (inGuiCfg["SyncPreviewActive"]) //obsolete name - inGuiCfg["SyncPreviewActive"](config.highlightSyncAction); - else - { - std::string val; - if (inGuiCfg["MiddleGridView"](val)) //refactor into enum!? - config.highlightSyncAction = val == "Action"; - } -} - - -void readConfig(const XmlIn& in, xmlAccess::XmlBatchConfig& config) -{ - readConfig(in, config.mainCfg); //read main config - - //read GUI specific config data - XmlIn inBatchCfg = in["BatchConfig"]; - - inBatchCfg["HandleError" ](config.handleError); - inBatchCfg["ShowProgress" ](config.showProgress); - - warn_static("remove after migration?") - if (inBatchCfg["LogfileDirectory"]) inBatchCfg["LogfileDirectory"](config.logFileDirectory); //obsolete name - else - inBatchCfg["LogfileFolder"](config.logFileDirectory); - - if (inBatchCfg["LogfileCountMax" ]) inBatchCfg["LogfileCountMax"](config.logfilesCountLimit); //obsolete name - else - inBatchCfg["LogfileFolder"].attribute("Limit", config.logfilesCountLimit); -} - - -void readConfig(const XmlIn& in, XmlGlobalSettings& config) -{ - XmlIn inShared = in["Shared"]; - - inShared["Language"].attribute("Id", config.programLanguage); - - inShared["FailSafeFileCopy" ].attribute("Enabled", config.failsafeFileCopy); - inShared["CopyLockedFiles" ].attribute("Enabled", config.copyLockedFiles); - inShared["CopyFilePermissions" ].attribute("Enabled", config.copyFilePermissions); - inShared["AutomaticRetry" ].attribute("Count" , config.automaticRetryCount); - inShared["AutomaticRetry" ].attribute("Delay" , config.automaticRetryDelay); - inShared["FileTimeTolerance" ].attribute("Seconds", config.fileTimeTolerance); - inShared["RunWithBackgroundPriority"].attribute("Enabled", config.runWithBackgroundPriority); - inShared["LockDirectoriesDuringSync"].attribute("Enabled", config.createLockFile); - inShared["VerifyCopiedFiles" ].attribute("Enabled", config.verifyFileCopy); - inShared["LastSyncsLogSizeMax" ].attribute("Bytes" , config.lastSyncsLogFileSizeMax); - - XmlIn inOpt = inShared["OptionalDialogs"]; - inOpt["WarnUnresolvedConflicts" ].attribute("Enabled", config.optDialogs.warningUnresolvedConflicts); - inOpt["WarnNotEnoughDiskSpace" ].attribute("Enabled", config.optDialogs.warningNotEnoughDiskSpace); - inOpt["WarnSignificantDifference" ].attribute("Enabled", config.optDialogs.warningSignificantDifference); - inOpt["WarnRecycleBinNotAvailable" ].attribute("Enabled", config.optDialogs.warningRecyclerMissing); - inOpt["WarnInputFieldEmpty" ].attribute("Enabled", config.optDialogs.warningInputFieldEmpty); - inOpt["WarnDatabaseError" ].attribute("Enabled", config.optDialogs.warningDatabaseError); - inOpt["WarnDependentFolders" ].attribute("Enabled", config.optDialogs.warningDependentFolders); - inOpt["WarnFolderPairRaceCondition"].attribute("Enabled", config.optDialogs.warningFolderPairRaceCondition); - inOpt["WarnDirectoryLockFailed" ].attribute("Enabled", config.optDialogs.warningDirectoryLockFailed); - inOpt["ConfirmSaveConfig" ].attribute("Enabled", config.optDialogs.popupOnConfigChange); - inOpt["ConfirmStartSync" ].attribute("Enabled", config.optDialogs.confirmSyncStart); - inOpt["ConfirmExternalCommandMassInvoke"].attribute("Enabled", config.optDialogs.confirmExternalCommandMassInvoke); - - //gui specific global settings (optional) - XmlIn inGui = in["Gui"]; - XmlIn inWnd = inGui["MainDialog"]; - - //read application window size and position - inWnd.attribute("Width", config.gui.dlgSize.x); - inWnd.attribute("Height", config.gui.dlgSize.y); - inWnd.attribute("PosX", config.gui.dlgPos.x); - inWnd.attribute("PosY", config.gui.dlgPos.y); - inWnd.attribute("Maximized", config.gui.isMaximized); - - XmlIn inManualDel = inWnd["ManualDeletion"]; - //inManualDel.attribute("DeleteOnBothSides", config.gui.deleteOnBothSides); - inManualDel.attribute("UseRecycler" , config.gui.useRecyclerForManualDeletion); - - inWnd["CaseSensitiveSearch"].attribute("Enabled", config.gui.textSearchRespectCase); - inWnd["FolderPairsVisible" ].attribute("Max", config.gui.maxFolderPairsVisible); - - //########################################################### - - XmlIn inOverview = inWnd["OverviewPanel"]; - inOverview.attribute("ShowPercentage", config.gui.showPercentBar); - inOverview.attribute("SortByColumn", config.gui.naviLastSortColumn); - inOverview.attribute("SortAscending", config.gui.naviLastSortAscending); - - //read column attributes - XmlIn inColNavi = inOverview["Columns"]; - inColNavi(config.gui.columnAttribNavi); - - XmlIn inMainGrid = inWnd["MainGrid"]; - inMainGrid.attribute("ShowIcons", config.gui.showIcons); - inMainGrid.attribute("IconSize", config.gui.iconSize); - inMainGrid.attribute("SashOffset", config.gui.sashOffset); - - XmlIn inColLeft = inMainGrid["ColumnsLeft"]; - inColLeft(config.gui.columnAttribLeft); - - XmlIn inColRight = inMainGrid["ColumnsRight"]; - inColRight(config.gui.columnAttribRight); - //########################################################### - - inWnd["ViewFilterDefault"](config.gui.viewFilterDefault); - inWnd["Perspective2" ](config.gui.guiPerspectiveLast); - - std::vector tmp = splitFilterByLines(config.gui.defaultExclusionFilter); //default value - inGui["DefaultExclusionFilter"](tmp); - config.gui.defaultExclusionFilter = mergeFilterLines(tmp); - - //load config file history - inGui["LastUsedConfig"](config.gui.lastUsedConfigFiles); - - inGui["ConfigHistory"](config.gui.cfgFileHistory); - inGui["ConfigHistory"].attribute("MaxSize", config.gui.cfgFileHistMax); - - inGui["FolderHistoryLeft" ](config.gui.folderHistoryLeft); - inGui["FolderHistoryRight"](config.gui.folderHistoryRight); - inGui["FolderHistoryLeft"].attribute("MaxSize", config.gui.folderHistMax); - - inGui["OnCompletionHistory"](config.gui.onCompletionHistory); - inGui["OnCompletionHistory"].attribute("MaxSize", config.gui.onCompletionHistoryMax); - - //external applications - inGui["ExternalApplications"](config.gui.externelApplications); - - //last update check - inGui["LastVersionCheck"](config.gui.lastUpdateCheck); - - //batch specific global settings - //XmlIn inBatch = in["Batch"]; -} - - -bool needsMigration(const XmlDoc& doc, int currentXmlFormatVer) -{ - //(try to) migrate old configuration if needed - int xmlFormatVer = 0; - /*bool success = */doc.root().getAttribute("XmlFormat", xmlFormatVer); - return xmlFormatVer < currentXmlFormatVer; -} - - -template -void readConfig(const Zstring& filename, XmlType type, ConfigType& cfg, int currentXmlFormatVer, bool& needMigration) //throw FfsXmlError -{ - XmlDoc doc = loadXmlDocument(filename); //throw FfsXmlError - - if (getXmlType(doc) != type) //throw() - throw FfsXmlError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename))); - - XmlIn in(doc); - ::readConfig(in, cfg); - - if (in.errorsOccured()) - throw FfsXmlError(replaceCpy(_("Configuration file %x loaded partially only."), L"%x", fmtFileName(filename)) + L"\n\n" + - getErrorMessageFormatted(in.getErrorsAs()), FfsXmlError::WARNING); - - //(try to) migrate old configuration if needed - needMigration = needsMigration(doc, currentXmlFormatVer); -} -} - - -void xmlAccess::readConfig(const Zstring& filename, xmlAccess::XmlGuiConfig& cfg) -{ - bool needMigration = false; - ::readConfig(filename, XML_TYPE_GUI, cfg, XML_FORMAT_VER_FFS_GUI, needMigration); //throw FfsXmlError - - if (needMigration) //(try to) migrate old configuration - try { xmlAccess::writeConfig(cfg, filename); /*throw FfsXmlError*/ } - catch (FfsXmlError&) { assert(false); } //don't bother user! -} - - -void xmlAccess::readConfig(const Zstring& filename, xmlAccess::XmlBatchConfig& cfg) -{ - bool needMigration = false; - ::readConfig(filename, XML_TYPE_BATCH, cfg, XML_FORMAT_VER_FFS_BATCH, needMigration); //throw FfsXmlError - - if (needMigration) //(try to) migrate old configuration - try { xmlAccess::writeConfig(cfg, filename); /*throw FfsXmlError*/ } - catch (FfsXmlError&) { assert(false); } //don't bother user! -} - - -void xmlAccess::readConfig(xmlAccess::XmlGlobalSettings& cfg) -{ - bool needMigration = false; - ::readConfig(getGlobalConfigFile(), XML_TYPE_GLOBAL, cfg, XML_FORMAT_VER_GLOBAL, needMigration); //throw FfsXmlError -} - - -namespace -{ -template -XmlCfg parseConfig(const XmlDoc& doc, const Zstring& filename, int currentXmlFormatVer, std::unique_ptr& warning) //nothrow -{ - XmlCfg cfg; - XmlIn in(doc); - ::readConfig(in, cfg); - - if (in.errorsOccured()) - { - if (!warning) - warning = make_unique(replaceCpy(_("Configuration file %x loaded partially only."), L"%x", fmtFileName(filename)) + L"\n\n" + - getErrorMessageFormatted(in.getErrorsAs()), FfsXmlError::WARNING); - } - else - { - //(try to) migrate old configuration if needed - if (needsMigration(doc, currentXmlFormatVer)) - try { xmlAccess::writeConfig(cfg, filename); /*throw FfsXmlError*/ } - catch (FfsXmlError&) { assert(false); } //don't bother user! - } - return cfg; -} -} - - -void xmlAccess::readAnyConfig(const std::vector& filenames, XmlGuiConfig& config) //throw FfsXmlError -{ - assert(!filenames.empty()); - - std::vector mainCfgs; - std::unique_ptr warning; - - for (auto it = filenames.begin(); it != filenames.end(); ++it) - { - const Zstring& filename = *it; - const bool firstItem = it == filenames.begin(); //init all non-"mainCfg" settings with first config file - - XmlDoc doc = loadXmlDocument(filename); //throw FfsXmlError - //do NOT use zen::loadStream as it will superfluously load even huge files! - - switch (::getXmlType(doc)) - { - case XML_TYPE_GUI: - { - XmlGuiConfig guiCfg = parseConfig(doc, filename, XML_FORMAT_VER_FFS_GUI, warning); //nothrow - if (firstItem) - config = guiCfg; - mainCfgs.push_back(guiCfg.mainCfg); - } - break; - - case XML_TYPE_BATCH: - { - XmlBatchConfig batchCfg = parseConfig(doc, filename, XML_FORMAT_VER_FFS_BATCH, warning); //nothrow - if (firstItem) - config = convertBatchToGui(batchCfg); - mainCfgs.push_back(batchCfg.mainCfg); - } - break; - - case XML_TYPE_GLOBAL: - case XML_TYPE_OTHER: - throw FfsXmlError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename))); - } - } - - config.mainCfg = merge(mainCfgs); - - if (warning) - throw* warning; -} - -//################################################################################################ - -namespace -{ -void writeConfig(const CompConfig& cmpConfig, XmlOut& out) -{ - out["Variant" ](cmpConfig.compareVar); - out["Symlinks"](cmpConfig.handleSymlinks); -} - - -void writeConfig(const DirectionConfig& directCfg, XmlOut& out) -{ - out["Variant"](directCfg.var); - - XmlOut outCustDir = out["CustomDirections"]; - outCustDir["LeftOnly" ](directCfg.custom.exLeftSideOnly); - outCustDir["RightOnly" ](directCfg.custom.exRightSideOnly); - outCustDir["LeftNewer" ](directCfg.custom.leftNewer); - outCustDir["RightNewer"](directCfg.custom.rightNewer); - outCustDir["Different" ](directCfg.custom.different); - outCustDir["Conflict" ](directCfg.custom.conflict); - - out["DetectMovedFiles"](directCfg.detectMovedFiles); -} - - -void writeConfig(const SyncConfig& syncCfg, XmlOut& out) -{ - writeConfig(syncCfg.directionCfg, out); - - out["DeletionPolicy" ](syncCfg.handleDeletion); - out["VersioningFolder"](syncCfg.versioningDirectory); - //out["VersioningFolder"].attribute("Limit", syncCfg.versionCountLimit); - out["VersioningFolder"].attribute("Style", syncCfg.versioningStyle); -} - - -void writeConfig(const FilterConfig& filter, XmlOut& out) -{ - out["Include"](splitFilterByLines(filter.includeFilter)); - out["Exclude"](splitFilterByLines(filter.excludeFilter)); - - out["TimeSpan"](filter.timeSpan); - out["TimeSpan"].attribute("Type", filter.unitTimeSpan); - - out["SizeMin"](filter.sizeMin); - out["SizeMin"].attribute("Unit", filter.unitSizeMin); - - out["SizeMax"](filter.sizeMax); - out["SizeMax"].attribute("Unit", filter.unitSizeMax); -} - - -void writeConfigFolderPair(const FolderPairEnh& enhPair, XmlOut& out) -{ - XmlOut outPair = out.ref().addChild("Pair"); - - //read folder pairs - outPair["Left" ](enhPair.leftDirectory); - outPair["Right"](enhPair.rightDirectory); - - //########################################################### - //alternate comp configuration (optional) - if (enhPair.altCmpConfig.get()) - { - XmlOut outAlt = outPair["CompareConfig"]; - writeConfig(*enhPair.altCmpConfig, outAlt); - } - //########################################################### - //alternate sync configuration (optional) - if (enhPair.altSyncConfig.get()) - { - XmlOut outAltSync = outPair["SyncConfig"]; - writeConfig(*enhPair.altSyncConfig, outAltSync); - } - - //########################################################### - //alternate filter configuration - if (enhPair.localFilter != FilterConfig()) //don't spam .ffs_gui file with default filter entries - { - XmlOut outFilter = outPair["LocalFilter"]; - writeConfig(enhPair.localFilter, outFilter); - } -} - - -void writeConfig(const MainConfiguration& mainCfg, XmlOut& out) -{ - XmlOut outMain = out["MainConfig"]; - - XmlOut outCmp = outMain["Comparison"]; - - writeConfig(mainCfg.cmpConfig, outCmp); - //########################################################### - - XmlOut outSync = outMain["SyncConfig"]; - - writeConfig(mainCfg.syncCfg, outSync); - //########################################################### - - XmlOut outFilter = outMain["GlobalFilter"]; - //write filter settings - writeConfig(mainCfg.globalFilter, outFilter); - - //########################################################### - //write all folder pairs - - XmlOut outFp = outMain["FolderPairs"]; - - //write first folder pair - writeConfigFolderPair(mainCfg.firstPair, outFp); - - //write additional folder pairs - std::for_each(mainCfg.additionalPairs.begin(), mainCfg.additionalPairs.end(), - [&](const FolderPairEnh& fp) { writeConfigFolderPair(fp, outFp); }); - - outMain["OnCompletion"](mainCfg.onCompletion); -} - - -void writeConfig(const XmlGuiConfig& config, XmlOut& out) -{ - writeConfig(config.mainCfg, out); //write main config - - //write GUI specific config data - XmlOut outGuiCfg = out["GuiConfig"]; - - outGuiCfg["HideExcluded" ](config.hideExcludedItems); - outGuiCfg["HandleError" ](config.handleError); - outGuiCfg["MiddleGridView"](config.highlightSyncAction ? "Action" : "Category"); //refactor into enum!? -} - -void writeConfig(const XmlBatchConfig& config, XmlOut& out) -{ - - writeConfig(config.mainCfg, out); //write main config - - //write GUI specific config data - XmlOut outBatchCfg = out["BatchConfig"]; - - outBatchCfg["HandleError" ](config.handleError); - outBatchCfg["ShowProgress" ](config.showProgress); - outBatchCfg["LogfileFolder" ](config.logFileDirectory); - outBatchCfg["LogfileFolder"].attribute("Limit", config.logfilesCountLimit); -} - - -void writeConfig(const XmlGlobalSettings& config, XmlOut& out) -{ - XmlOut outShared = out["Shared"]; - - outShared["Language"].attribute("Id", config.programLanguage); - - outShared["FailSafeFileCopy" ].attribute("Enabled", config.failsafeFileCopy); - outShared["CopyLockedFiles" ].attribute("Enabled", config.copyLockedFiles); - outShared["CopyFilePermissions" ].attribute("Enabled", config.copyFilePermissions); - outShared["AutomaticRetry" ].attribute("Count" , config.automaticRetryCount); - outShared["AutomaticRetry" ].attribute("Delay" , config.automaticRetryDelay); - outShared["FileTimeTolerance" ].attribute("Seconds", config.fileTimeTolerance); - outShared["RunWithBackgroundPriority"].attribute("Enabled", config.runWithBackgroundPriority); - outShared["LockDirectoriesDuringSync"].attribute("Enabled", config.createLockFile); - outShared["VerifyCopiedFiles" ].attribute("Enabled", config.verifyFileCopy); - outShared["LastSyncsLogSizeMax" ].attribute("Bytes" , config.lastSyncsLogFileSizeMax); - - XmlOut outOpt = outShared["OptionalDialogs"]; - outOpt["WarnUnresolvedConflicts" ].attribute("Enabled", config.optDialogs.warningUnresolvedConflicts); - outOpt["WarnNotEnoughDiskSpace" ].attribute("Enabled", config.optDialogs.warningNotEnoughDiskSpace); - outOpt["WarnSignificantDifference" ].attribute("Enabled", config.optDialogs.warningSignificantDifference); - outOpt["WarnRecycleBinNotAvailable" ].attribute("Enabled", config.optDialogs.warningRecyclerMissing); - outOpt["WarnInputFieldEmpty" ].attribute("Enabled", config.optDialogs.warningInputFieldEmpty); - outOpt["WarnDatabaseError" ].attribute("Enabled", config.optDialogs.warningDatabaseError); - outOpt["WarnDependentFolders" ].attribute("Enabled", config.optDialogs.warningDependentFolders); - outOpt["WarnFolderPairRaceCondition"].attribute("Enabled", config.optDialogs.warningFolderPairRaceCondition); - outOpt["WarnDirectoryLockFailed" ].attribute("Enabled", config.optDialogs.warningDirectoryLockFailed); - outOpt["ConfirmSaveConfig" ].attribute("Enabled", config.optDialogs.popupOnConfigChange); - outOpt["ConfirmStartSync" ].attribute("Enabled", config.optDialogs.confirmSyncStart); - outOpt["ConfirmExternalCommandMassInvoke"].attribute("Enabled", config.optDialogs.confirmExternalCommandMassInvoke); - - //gui specific global settings (optional) - XmlOut outGui = out["Gui"]; - XmlOut outWnd = outGui["MainDialog"]; - - //write application window size and position - outWnd.attribute("Width", config.gui.dlgSize.x); - outWnd.attribute("Height", config.gui.dlgSize.y); - outWnd.attribute("PosX", config.gui.dlgPos.x); - outWnd.attribute("PosY", config.gui.dlgPos.y); - outWnd.attribute("Maximized", config.gui.isMaximized); - - XmlOut outManualDel = outWnd["ManualDeletion"]; - //outManualDel.attribute("DeleteOnBothSides", config.gui.deleteOnBothSides); - outManualDel.attribute("UseRecycler" , config.gui.useRecyclerForManualDeletion); - - outWnd["CaseSensitiveSearch"].attribute("Enabled", config.gui.textSearchRespectCase); - outWnd["FolderPairsVisible" ].attribute("Max", config.gui.maxFolderPairsVisible); - - //########################################################### - - XmlOut outOverview = outWnd["OverviewPanel"]; - outOverview.attribute("ShowPercentage", config.gui.showPercentBar); - outOverview.attribute("SortByColumn", config.gui.naviLastSortColumn); - outOverview.attribute("SortAscending", config.gui.naviLastSortAscending); - - //write column attributes - XmlOut outColNavi = outOverview["Columns"]; - outColNavi(config.gui.columnAttribNavi); - - XmlOut outMainGrid = outWnd["MainGrid"]; - outMainGrid.attribute("ShowIcons", config.gui.showIcons); - outMainGrid.attribute("IconSize", config.gui.iconSize); - outMainGrid.attribute("SashOffset", config.gui.sashOffset); - - XmlOut outColLeft = outMainGrid["ColumnsLeft"]; - outColLeft(config.gui.columnAttribLeft); - - XmlOut outColRight = outMainGrid["ColumnsRight"]; - outColRight(config.gui.columnAttribRight); - //########################################################### - - outWnd["ViewFilterDefault"](config.gui.viewFilterDefault); - outWnd["Perspective2" ](config.gui.guiPerspectiveLast); - - outGui["DefaultExclusionFilter"](splitFilterByLines(config.gui.defaultExclusionFilter)); - - //load config file history - outGui["LastUsedConfig"](config.gui.lastUsedConfigFiles); - - outGui["ConfigHistory" ](config.gui.cfgFileHistory); - outGui["ConfigHistory"].attribute("MaxSize", config.gui.cfgFileHistMax); - - outGui["FolderHistoryLeft" ](config.gui.folderHistoryLeft); - outGui["FolderHistoryRight"](config.gui.folderHistoryRight); - outGui["FolderHistoryLeft" ].attribute("MaxSize", config.gui.folderHistMax); - - outGui["OnCompletionHistory"](config.gui.onCompletionHistory); - outGui["OnCompletionHistory"].attribute("MaxSize", config.gui.onCompletionHistoryMax); - - //external applications - outGui["ExternalApplications"](config.gui.externelApplications); - - //last update check - outGui["LastVersionCheck"](config.gui.lastUpdateCheck); - - //batch specific global settings - //XmlOut outBatch = out["Batch"]; -} - - -template -void writeConfig(const ConfigType& config, XmlType type, int xmlFormatVer, const Zstring& filename) -{ - XmlDoc doc("FreeFileSync"); - setXmlType(doc, type); //throw() - - doc.root().setAttribute("XmlFormat", xmlFormatVer); - - XmlOut out(doc); - writeConfig(config, out); - - saveXmlDocument(doc, filename); //throw FfsXmlError -} -} - -void xmlAccess::writeConfig(const XmlGuiConfig& cfg, const Zstring& filename) -{ - ::writeConfig(cfg, XML_TYPE_GUI, XML_FORMAT_VER_FFS_GUI, filename); //throw FfsXmlError -} - - -void xmlAccess::writeConfig(const XmlBatchConfig& cfg, const Zstring& filename) -{ - ::writeConfig(cfg, XML_TYPE_BATCH, XML_FORMAT_VER_FFS_BATCH, filename); //throw FfsXmlError -} - - -void xmlAccess::writeConfig(const XmlGlobalSettings& cfg) -{ - ::writeConfig(cfg, XML_TYPE_GLOBAL, XML_FORMAT_VER_GLOBAL, getGlobalConfigFile()); //throw FfsXmlError -} - - -std::wstring xmlAccess::extractJobName(const Zstring& configFilename) -{ - const Zstring shortName = afterLast(configFilename, FILE_NAME_SEPARATOR); //returns the whole string if separator not found - const Zstring jobName = beforeLast(shortName, Zstr('.')); //returns empty string if seperator not found - return utfCvrtTo(jobName.empty() ? shortName : jobName); -} diff --git a/lib/process_xml.h b/lib/process_xml.h deleted file mode 100644 index 4092c072..00000000 --- a/lib/process_xml.h +++ /dev/null @@ -1,298 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef PROCESSXML_H_INCLUDED -#define PROCESSXML_H_INCLUDED - -#include -#include "../structures.h" -#include "xml_base.h" -#include "localization.h" -#include "../ui/column_attr.h" -#include "../ui/folder_history_types.h" -//#include "ffs_paths.h" - -namespace xmlAccess -{ -enum XmlType -{ - XML_TYPE_GUI, - XML_TYPE_BATCH, - XML_TYPE_GLOBAL, - XML_TYPE_OTHER -}; - -XmlType getXmlType(const Zstring& filename); //throw() - - -enum OnError -{ - ON_ERROR_IGNORE, - ON_ERROR_POPUP, - ON_ERROR_STOP -}; - -enum OnGuiError -{ - ON_GUIERROR_POPUP, - ON_GUIERROR_IGNORE -}; - -typedef std::wstring Description; -typedef std::wstring Commandline; -typedef std::vector> ExternalApps; - -//--------------------------------------------------------------------- -struct XmlGuiConfig -{ - XmlGuiConfig() : - hideExcludedItems(false), - handleError(ON_GUIERROR_POPUP), - highlightSyncAction(true) {} //initialize values - - zen::MainConfiguration mainCfg; - - bool hideExcludedItems; - OnGuiError handleError; //reaction on error situation during synchronization - bool highlightSyncAction; -}; - - -inline -bool operator==(const XmlGuiConfig& lhs, const XmlGuiConfig& rhs) -{ - return lhs.mainCfg == rhs.mainCfg && - lhs.hideExcludedItems == rhs.hideExcludedItems && - lhs.handleError == rhs.handleError && - lhs.highlightSyncAction == rhs.highlightSyncAction; -} - - -struct XmlBatchConfig -{ - XmlBatchConfig() : - showProgress(true), - logfilesCountLimit(-1), - handleError(ON_ERROR_POPUP) {} - - zen::MainConfiguration mainCfg; - - bool showProgress; - Zstring logFileDirectory; - int logfilesCountLimit; //max logfiles; 0 := don't save logfiles; < 0 := no limit - OnError handleError; //reaction on error situation during synchronization -}; - - -struct OptionalDialogs -{ - OptionalDialogs() { resetDialogs();} - - void resetDialogs(); - - bool warningDependentFolders; - bool warningFolderPairRaceCondition; - bool warningSignificantDifference; - bool warningNotEnoughDiskSpace; - bool warningUnresolvedConflicts; - bool warningDatabaseError; - bool warningRecyclerMissing; - bool warningInputFieldEmpty; - bool warningDirectoryLockFailed; - bool popupOnConfigChange; - bool confirmSyncStart; - bool confirmExternalCommandMassInvoke; -}; - - -enum FileIconSize -{ - ICON_SIZE_SMALL, - ICON_SIZE_MEDIUM, - ICON_SIZE_LARGE -}; - - -struct ViewFilterDefault -{ - ViewFilterDefault() : equal(false) - { - leftOnly = rightOnly = leftNewer = rightNewer = different = conflict = true; - createLeft = createRight = updateLeft = updateRight = deleteLeft = deleteRight = doNothing = true; - } - bool equal; - bool leftOnly, rightOnly, leftNewer, rightNewer, different, conflict; //category view - bool createLeft, createRight, updateLeft, updateRight, deleteLeft, deleteRight, doNothing; //action view -}; - -Zstring getGlobalConfigFile(); - -struct XmlGlobalSettings -{ - //--------------------------------------------------------------------- - //Shared (GUI/BATCH) settings - XmlGlobalSettings() : - programLanguage(zen::retrieveSystemLanguage()), - failsafeFileCopy(true), - copyLockedFiles(true), - copyFilePermissions(false), - automaticRetryCount(0), - automaticRetryDelay(5), - fileTimeTolerance(2), //default 2s: FAT vs NTFS - runWithBackgroundPriority(false), - createLockFile(true), - verifyFileCopy(false), - lastSyncsLogFileSizeMax(100000) //maximum size for LastSyncs.log: use a human-readable number - {} - - int programLanguage; - bool failsafeFileCopy; - bool copyLockedFiles; - bool copyFilePermissions; - size_t automaticRetryCount; - size_t automaticRetryDelay; //unit: [sec] - - size_t fileTimeTolerance; //max. allowed file time deviation - bool runWithBackgroundPriority; - bool createLockFile; - bool verifyFileCopy; //verify copied files - size_t lastSyncsLogFileSizeMax; - - OptionalDialogs optDialogs; - - //--------------------------------------------------------------------- - struct Gui - { - Gui() : - dlgPos(wxDefaultCoord, wxDefaultCoord), - dlgSize(wxDefaultCoord, wxDefaultCoord), - isMaximized(false), - sashOffset(0), - maxFolderPairsVisible(6), - columnAttribNavi (zen::getDefaultColumnAttributesNavi()), - columnAttribLeft (zen::getDefaultColumnAttributesLeft()), - columnAttribRight(zen::getDefaultColumnAttributesRight()), - naviLastSortColumn(zen::defaultValueLastSortColumn), - naviLastSortAscending(zen::defaultValueLastSortAscending), - showPercentBar(zen::defaultValueShowPercentage), - cfgFileHistMax(30), - folderHistMax(15), - onCompletionHistoryMax(8), -#ifdef ZEN_WIN - defaultExclusionFilter(Zstr("\\System Volume Information\\") Zstr("\n") - Zstr("\\$Recycle.Bin\\") Zstr("\n") - Zstr("\\RECYCLER\\") Zstr("\n") - Zstr("\\RECYCLED\\") Zstr("\n") - Zstr("*\\desktop.ini") Zstr("\n") - Zstr("*\\thumbs.db")), -#elif defined ZEN_LINUX - defaultExclusionFilter(Zstr("/.Trash-*/") Zstr("\n") - Zstr("/.recycle/")), -#elif defined ZEN_MAC - defaultExclusionFilter(Zstr("/.fseventsd/") Zstr("\n") - Zstr("/.Spotlight-V100/") Zstr("\n") - Zstr("/.Trashes/") Zstr("\n") - Zstr("/._.Trashes") Zstr("\n") - Zstr("*/.DS_Store")), -#endif - //deleteOnBothSides(false), - useRecyclerForManualDeletion(true), //enable if OS supports it; else user will have to activate first and then get an error message -#if defined ZEN_WIN || defined ZEN_MAC - textSearchRespectCase(false), -#elif defined ZEN_LINUX - textSearchRespectCase(true), -#endif - showIcons(true), - iconSize(ICON_SIZE_SMALL), - lastUpdateCheck(0) - { - //default external apps will be translated "on the fly"!!! First entry will be used for [Enter] or mouse double-click! -#ifdef ZEN_WIN - externelApplications.push_back(std::make_pair(L"Show in Explorer", L"explorer /select, \"%item_path%\"")); - externelApplications.push_back(std::make_pair(L"Open with default application", L"\"%item_path%\"")); - //mark for extraction: _("Show in Explorer") - //mark for extraction: _("Open with default application") -#elif defined ZEN_LINUX - externelApplications.push_back(std::make_pair(L"Browse directory", L"xdg-open \"%item_folder%\"")); - externelApplications.push_back(std::make_pair(L"Open with default application", L"xdg-open \"%item_path%\"")); - //mark for extraction: _("Browse directory") Linux doesn't use the term "folder" -#elif defined ZEN_MAC - externelApplications.push_back(std::make_pair(L"Browse directory", L"open -R \"%item_path%\"")); - externelApplications.push_back(std::make_pair(L"Open with default application", L"open \"%item_path%\"")); -#endif - } - - wxPoint dlgPos; - wxSize dlgSize; - bool isMaximized; - int sashOffset; - - int maxFolderPairsVisible; - - std::vector columnAttribNavi; //compressed view/navigation - std::vector columnAttribLeft; - std::vector columnAttribRight; - - zen::ColumnTypeNavi naviLastSortColumn; //remember sort on navigation panel - bool naviLastSortAscending; // - - bool showPercentBar; //in navigation panel - - ExternalApps externelApplications; - - std::vector cfgFileHistory; - size_t cfgFileHistMax; - - std::vector lastUsedConfigFiles; - - std::vector folderHistoryLeft; - std::vector folderHistoryRight; - size_t folderHistMax; - - std::vector onCompletionHistory; - size_t onCompletionHistoryMax; - - Zstring defaultExclusionFilter; - - //bool deleteOnBothSides; - bool useRecyclerForManualDeletion; - bool textSearchRespectCase; - - bool showIcons; - FileIconSize iconSize; - - long lastUpdateCheck; //time of last update check - - ViewFilterDefault viewFilterDefault; - wxString guiPerspectiveLast; //used by wxAuiManager - } gui; - - //--------------------------------------------------------------------- - //struct Batch -}; - -//read/write specific config types -void readConfig(const Zstring& filename, XmlGuiConfig& config); // -void readConfig(const Zstring& filename, XmlBatchConfig& config); //throw FfsXmlError -void readConfig( XmlGlobalSettings& config); // - -void writeConfig(const XmlGuiConfig& config, const Zstring& filename); // -void writeConfig(const XmlBatchConfig& config, const Zstring& filename); //throw FfsXmlError -void writeConfig(const XmlGlobalSettings& config); // - -//convert (multiple) *.ffs_gui, *.ffs_batch files or combinations of both into target config structure: -void readAnyConfig(const std::vector& filenames, XmlGuiConfig& config); //throw FfsXmlError - -//config conversion utilities -XmlGuiConfig convertBatchToGui(const XmlBatchConfig& batchCfg); //noexcept -XmlBatchConfig convertGuiToBatch(const XmlGuiConfig& guiCfg ); // -XmlBatchConfig convertGuiToBatchPreservingExistingBatch(const xmlAccess::XmlGuiConfig& guiCfg, const Zstring& referenceBatchFile); //noexcept - -std::wstring extractJobName(const Zstring& configFilename); -} - - -#endif // PROCESSXML_H_INCLUDED diff --git a/lib/resolve_path.cpp b/lib/resolve_path.cpp deleted file mode 100644 index 768876c9..00000000 --- a/lib/resolve_path.cpp +++ /dev/null @@ -1,708 +0,0 @@ -#include "resolve_path.h" -#include //not necessarily included by ! -#include -#include -#include -#include -#include -#include //wxGetEnv - -#ifdef ZEN_WIN -#include -#include -#include //includes "windows.h" -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "Mpr.lib") -#endif - -#elif defined ZEN_LINUX || defined ZEN_MAC -#include //getenv() -#include //getcwd -#endif - -using namespace zen; - - -namespace -{ -#ifdef ZEN_WIN -Zstring resolveRelativePath(const Zstring& relativeName) //note: ::GetFullPathName() is documented not threadsafe! -{ - //don't use long path prefix! does not work with relative paths "." and ".." - const DWORD bufferSize = ::GetFullPathName(relativeName.c_str(), 0, nullptr, nullptr); - if (bufferSize > 0) - { - std::vector buffer(bufferSize); - const DWORD charsWritten = ::GetFullPathName(relativeName.c_str(), //__in LPCTSTR lpFileName, - bufferSize, //__in DWORD nBufferLength, - &buffer[0], //__out LPTSTR lpBuffer, - nullptr); //__out LPTSTR *lpFilePart - if (0 < charsWritten && charsWritten < bufferSize) //theoretically, charsWritten can never be == "bufferSize" - return Zstring(&buffer[0], charsWritten); - } - return relativeName; //ERROR! Don't do anything -} - -#elif defined ZEN_LINUX || defined ZEN_MAC -Zstring resolveRelativePath(const Zstring& relativeName) -{ - //http://linux.die.net/man/2/path_resolution - if (!startsWith(relativeName, FILE_NAME_SEPARATOR)) //absolute names are exactly those starting with a '/' - { - /* - basic support for '~': strictly speaking this is a shell-layer feature, so "realpath()" won't handle it - http://www.gnu.org/software/bash/manual/html_node/Tilde-Expansion.html - - http://linux.die.net/man/3/getpwuid: An application that wants to determine its user's home directory - should inspect the value of HOME (rather than the value getpwuid(getuid())->pw_dir) since this allows - the user to modify their notion of "the home directory" during a login session. - */ - if (startsWith(relativeName, "~/") || relativeName == "~") - { - const char* homeDir = ::getenv("HOME"); - if (!homeDir) - return relativeName; //error! no further processing! - - if (startsWith(relativeName, "~/")) - return appendSeparator(homeDir) + afterFirst(relativeName, '/'); - else if (relativeName == "~") - return homeDir; - } - - //we cannot use ::realpath() since it resolves *existing* relative paths only! - if (char* dirpath = ::getcwd(nullptr, 0)) - { - ZEN_ON_SCOPE_EXIT(::free(dirpath)); - return appendSeparator(dirpath) + relativeName; - } - } - return relativeName; -} -#endif - - -#ifdef ZEN_WIN -class CsidlConstants -{ -public: - typedef std::map CsidlToDirMap; //case-insensitive comparison - - static const CsidlToDirMap& get() - { - static CsidlConstants inst; //potential MT solved by intializing at startup, see below - return inst.csidlToDir; - } - -private: - CsidlConstants() - { - auto addCsidl = [&](int csidl, const Zstring& paramName) - { - wchar_t buffer[MAX_PATH] = {}; - if (SUCCEEDED(::SHGetFolderPath(nullptr, //__in HWND hwndOwner, - csidl | CSIDL_FLAG_DONT_VERIFY, //__in int nFolder, - nullptr, //__in HANDLE hToken, - 0 /* == SHGFP_TYPE_CURRENT*/, //__in DWORD dwFlags, - buffer))) //__out LPTSTR pszPath - { - Zstring dirname = buffer; - if (!dirname.empty()) - csidlToDir.insert(std::make_pair(paramName, dirname)); - } - }; - - //================================================================================================ - //SHGetKnownFolderPath: API available only with Windows Vista and later: -#ifdef __MINGW32__ -#define KF_FLAG_DONT_VERIFY 0x00004000 -#endif - typedef HRESULT (STDAPICALLTYPE* SHGetKnownFolderPathFunc)(REFKNOWNFOLDERID rfid, DWORD dwFlags, HANDLE hToken, PWSTR* ppszPath); - const SysDllFun shGetKnownFolderPath(L"Shell32.dll", "SHGetKnownFolderPath"); - - auto addFolderId = [&](REFKNOWNFOLDERID rfid, const Zstring& paramName) - { - if (shGetKnownFolderPath != nullptr) //[!] avoid bogus MSVC "performance warning" - { - PWSTR path = nullptr; - if (SUCCEEDED(shGetKnownFolderPath(rfid, //_In_ REFKNOWNFOLDERID rfid, - KF_FLAG_DONT_VERIFY, //_In_ DWORD dwFlags, - nullptr, //_In_opt_ HANDLE hToken, - &path))) //_Out_ PWSTR *ppszPath - { - ZEN_ON_SCOPE_EXIT(::CoTaskMemFree(path)); - - Zstring dirname = path; - if (!dirname.empty()) - csidlToDir.insert(std::make_pair(paramName, dirname)); - } - } - }; - - addCsidl(CSIDL_DESKTOPDIRECTORY, L"csidl_Desktop"); // C:\Users\\Desktop - addCsidl(CSIDL_COMMON_DESKTOPDIRECTORY, L"csidl_PublicDesktop"); // C:\Users\All Users\Desktop - - addCsidl(CSIDL_FAVORITES, L"csidl_Favorites"); // C:\Users\\Favorites - addCsidl(CSIDL_COMMON_FAVORITES, L"csidl_PublicFavorites"); // C:\Users\\Favorites; unused? -> http://blogs.msdn.com/b/oldnewthing/archive/2012/09/04/10346022.aspx - - addCsidl(CSIDL_PERSONAL, L"csidl_MyDocuments"); // C:\Users\\Documents - addCsidl(CSIDL_COMMON_DOCUMENTS, L"csidl_PublicDocuments"); // C:\Users\Public\Documents - - addCsidl(CSIDL_MYMUSIC, L"csidl_MyMusic"); // C:\Users\\Music - addCsidl(CSIDL_COMMON_MUSIC, L"csidl_PublicMusic"); // C:\Users\Public\Music - - addCsidl(CSIDL_MYPICTURES, L"csidl_MyPictures"); // C:\Users\\Pictures - addCsidl(CSIDL_COMMON_PICTURES, L"csidl_PublicPictures"); // C:\Users\Public\Pictures - - addCsidl(CSIDL_MYVIDEO, L"csidl_MyVideos"); // C:\Users\\Videos - addCsidl(CSIDL_COMMON_VIDEO, L"csidl_PublicVideos"); // C:\Users\Public\Videos - - addCsidl(CSIDL_NETHOOD, L"csidl_Nethood"); // C:\Users\\AppData\Roaming\Microsoft\Windows\Network Shortcuts - - addCsidl(CSIDL_PROGRAMS, L"csidl_Programs"); // C:\Users\\AppData\Roaming\Microsoft\Windows\Start Menu\Programs - addCsidl(CSIDL_COMMON_PROGRAMS, L"csidl_PublicPrograms"); // C:\ProgramData\Microsoft\Windows\Start Menu\Programs - - addCsidl(CSIDL_RESOURCES, L"csidl_Resources"); // C:\Windows\Resources - - addCsidl(CSIDL_STARTMENU, L"csidl_StartMenu"); // C:\Users\\AppData\Roaming\Microsoft\Windows\Start Menu - addCsidl(CSIDL_COMMON_STARTMENU, L"csidl_PublicStartMenu"); // C:\ProgramData\Microsoft\Windows\Start Menu - - addCsidl(CSIDL_STARTUP, L"csidl_Startup"); // C:\Users\\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\StartUp - addCsidl(CSIDL_COMMON_STARTUP, L"csidl_PublicStartup"); // C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp - - addCsidl(CSIDL_TEMPLATES, L"csidl_Templates"); // C:\Users\\AppData\Roaming\Microsoft\Windows\Templates - addCsidl(CSIDL_COMMON_TEMPLATES, L"csidl_PublicTemplates"); // C:\ProgramData\Microsoft\Windows\Templates - - //Vista and later: - addFolderId(FOLDERID_Downloads, L"csidl_Downloads"); // C:\Users\\Downloads - addFolderId(FOLDERID_PublicDownloads, L"csidl_PublicDownloads"); // C:\Users\Public\Downloads - - addFolderId(FOLDERID_QuickLaunch, L"csidl_QuickLaunch"); // C:\Users\\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch - - /* - CSIDL_APPDATA covered by %AppData% - CSIDL_LOCAL_APPDATA covered by %LocalAppData% -> not on XP! - CSIDL_COMMON_APPDATA covered by %ProgramData% -> not on XP! - CSIDL_PROFILE covered by %UserProfile% - CSIDL_WINDOWS covered by %WinDir% - CSIDL_SYSTEM covered by %WinDir% - CSIDL_SYSTEMX86 covered by %WinDir% - CSIDL_PROGRAM_FILES covered by %ProgramFiles% - CSIDL_PROGRAM_FILES_COMMON covered by %CommonProgramFiles% - CSIDL_PROGRAM_FILESX86 covered by %ProgramFiles(x86)% -> not on XP! - CSIDL_PROGRAM_FILES_COMMONX86 covered by %CommonProgramFiles(x86)% -> not on XP! - CSIDL_ADMINTOOLS not relevant? - CSIDL_COMMON_ADMINTOOLS not relevant? - - FOLDERID_Public covered by %Public% - */ - } - - CsidlConstants(const CsidlConstants&); - CsidlConstants& operator=(const CsidlConstants&); - - CsidlToDirMap csidlToDir; -}; - -//caveat: function scope static initialization is not thread-safe in VS 2010! => make sure to call at app start! -struct Dummy { Dummy() { CsidlConstants::get(); }} blah; -#endif - - -std::unique_ptr getEnvironmentVar(const Zstring& envName) //return nullptr if not found -{ - wxString value; - if (!wxGetEnv(utfCvrtTo(envName), &value)) - return nullptr; - - //some postprocessing: - trim(value); //remove leading, trailing blanks - - //remove leading, trailing double-quotes - if (startsWith(value, L"\"") && - endsWith (value, L"\"") && - value.length() >= 2) - value = wxString(value.c_str() + 1, value.length() - 2); - - return make_unique(utfCvrtTo(value)); -} - - -std::unique_ptr resolveMacro(const Zstring& macro, //macro without %-characters - const std::vector>& ext) //return nullptr if not resolved -{ - auto equalNoCase = [](const Zstring& lhs, const Zstring& rhs) { return utfCvrtTo(lhs).CmpNoCase(utfCvrtTo(rhs)) == 0; }; - - //there exist environment variables named %TIME%, %DATE% so check for our internal macros first! - if (equalNoCase(macro, Zstr("time"))) - return make_unique(formatTime(Zstr("%H%M%S"))); - - if (equalNoCase(macro, Zstr("date"))) - return make_unique(formatTime(FORMAT_ISO_DATE)); - - if (equalNoCase(macro, Zstr("timestamp"))) - return make_unique(formatTime(Zstr("%Y-%m-%d %H%M%S"))); //e.g. "2012-05-15 131513" - - std::unique_ptr cand; - auto processPhrase = [&](const Zchar* phrase, const Zchar* format) -> bool - { - if (!equalNoCase(macro, phrase)) - return false; - - cand = make_unique(formatTime(format)); - return true; - }; - - if (processPhrase(Zstr("weekday"), Zstr("%A"))) return cand; - if (processPhrase(Zstr("day" ), Zstr("%d"))) return cand; - if (processPhrase(Zstr("month" ), Zstr("%m"))) return cand; - if (processPhrase(Zstr("week" ), Zstr("%U"))) return cand; - if (processPhrase(Zstr("year" ), Zstr("%Y"))) return cand; - if (processPhrase(Zstr("hour" ), Zstr("%H"))) return cand; - if (processPhrase(Zstr("min" ), Zstr("%M"))) return cand; - if (processPhrase(Zstr("sec" ), Zstr("%S"))) return cand; - - //check domain-specific extensions - { - auto it = std::find_if(ext.begin(), ext.end(), [&](const std::pair& p) { return equalNoCase(macro, p.first); }); - if (it != ext.end()) - return make_unique(it->second); - } - - //try to resolve as environment variable - if (std::unique_ptr value = getEnvironmentVar(macro)) - return value; - -#ifdef ZEN_WIN - //try to resolve as CSIDL value - { - const auto& csidlMap = CsidlConstants::get(); - auto it = csidlMap.find(macro); - if (it != csidlMap.end()) - return make_unique(it->second); - } -#endif - - return nullptr; -} - -const Zchar MACRO_SEP = Zstr('%'); - -//returns expanded or original string -Zstring expandMacros(const Zstring& text, const std::vector>& ext) -{ - if (contains(text, MACRO_SEP)) - { - Zstring prefix = beforeFirst(text, MACRO_SEP); - Zstring rest = afterFirst (text, MACRO_SEP); - if (contains(rest, MACRO_SEP)) - { - Zstring potentialMacro = beforeFirst(rest, MACRO_SEP); - Zstring postfix = afterFirst (rest, MACRO_SEP); //text == prefix + MACRO_SEP + potentialMacro + MACRO_SEP + postfix - - if (std::unique_ptr value = resolveMacro(potentialMacro, ext)) - return prefix + *value + expandMacros(postfix, ext); - else - return prefix + MACRO_SEP + potentialMacro + expandMacros(MACRO_SEP + postfix, ext); - } - } - return text; -} -} - - -Zstring zen::expandMacros(const Zstring& text) { return ::expandMacros(text, std::vector>()); } - - -namespace -{ -#ifdef ZEN_WIN -//networks and cdrom excluded - may still block for slow USB sticks! -Zstring getPathByVolumenName(const Zstring& volumeName) //return empty string on error -{ - //FindFirstVolume(): traverses volumes on local hard disks only! - //GetLogicalDriveStrings(): traverses all *logical* volumes, including CD-ROM, FreeOTFE virtual volumes - - const DWORD bufferSize = ::GetLogicalDriveStrings(0, nullptr); - std::vector buffer(bufferSize); - - const DWORD rv = ::GetLogicalDriveStrings(bufferSize, //__in DWORD nBufferLength, - &buffer[0]); //__out LPTSTR lpBuffer - if (0 < rv && rv < bufferSize) - { - //search for matching path in parallel until first hit - RunUntilFirstHit findFirstMatch; - - for (const wchar_t* it = &buffer[0]; *it != 0; it += strLength(it) + 1) //list terminated by empty c-string - { - const Zstring path = it; - - findFirstMatch.addJob([path, volumeName]() -> std::unique_ptr - { - UINT type = ::GetDriveType(appendSeparator(path).c_str()); //non-blocking call! - if (type == DRIVE_REMOTE || type == DRIVE_CDROM) - return nullptr; - - //next call seriously blocks for non-existing network drives! - std::vector volName(MAX_PATH + 1); //docu says so - - if (::GetVolumeInformation(appendSeparator(path).c_str(), //__in_opt LPCTSTR lpRootPathName, - &volName[0], //__out LPTSTR lpVolumeNameBuffer, - static_cast(volName.size()), //__in DWORD nVolumeNameSize, - nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, - nullptr, //__out_opt LPDWORD lpMaximumComponentLength, - nullptr, //__out_opt LPDWORD lpFileSystemFlags, - nullptr, //__out LPTSTR lpFileSystemNameBuffer, - 0)) //__in DWORD nFileSystemNameSize - if (EqualFilename()(volumeName, Zstring(&volName[0]))) - return zen::make_unique(path); - return nullptr; - }); - } - if (auto result = findFirstMatch.get()) //blocks until ready - return *result; - } - - return Zstring(); -} - - -//networks and cdrom excluded - this should not block -Zstring getVolumeName(const Zstring& volumePath) //return empty string on error -{ - UINT rv = ::GetDriveType(appendSeparator(volumePath).c_str()); //non-blocking call! - if (rv != DRIVE_REMOTE && - rv != DRIVE_CDROM) - { - const DWORD bufferSize = MAX_PATH + 1; - std::vector buffer(bufferSize); - if (::GetVolumeInformation(appendSeparator(volumePath).c_str(), //__in_opt LPCTSTR lpRootPathName, - &buffer[0], //__out LPTSTR lpVolumeNameBuffer, - bufferSize, //__in DWORD nVolumeNameSize, - nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, - nullptr, //__out_opt LPDWORD lpMaximumComponentLength, - nullptr, //__out_opt LPDWORD lpFileSystemFlags, - nullptr, //__out LPTSTR lpFileSystemNameBuffer, - 0)) //__in DWORD nFileSystemNameSize - return &buffer[0]; - } - return Zstring(); -} -#endif - - -//expand volume name if possible, return original input otherwise -Zstring expandVolumeName(const Zstring& text) // [volname]:\folder [volname]\folder [volname]folder -> C:\folder -{ - //this would be a nice job for a C++11 regex... - - //we only expect the [.*] pattern at the beginning => do not touch dir names like "C:\somedir\[stuff]" - Zstring textTmp = text; - trim(textTmp, true, false); - - if (startsWith(textTmp, Zstr("["))) - { - size_t posEnd = textTmp.find(Zstr("]")); - if (posEnd != Zstring::npos) - { - Zstring volname = Zstring(textTmp.c_str() + 1, posEnd - 1); - Zstring rest = Zstring(textTmp.c_str() + posEnd + 1); - - if (startsWith(rest, Zstr(':'))) - rest = afterFirst(rest, Zstr(':')); - if (startsWith(rest, FILE_NAME_SEPARATOR)) - rest = afterFirst(rest, FILE_NAME_SEPARATOR); -#ifdef ZEN_WIN - //[.*] pattern was found... - if (!volname.empty()) - { - Zstring volPath = getPathByVolumenName(volname); //may block for slow USB sticks! - if (!volPath.empty()) - return appendSeparator(volPath) + rest; //successfully replaced pattern - } - //error: did not find corresponding volume name: - - /*make sure directory creation will fail later if attempted, instead of inconveniently interpreting this string as a relative name! - [FFS USB]\FreeFileSync will be resolved as - ?:\[FFS USB]\FreeFileSync\ - Windows - /.../[FFS USB]/FreeFileSync/ - Linux - instead of: - C:\Program Files\FreeFileSync\[FFS USB]\FreeFileSync\ */ - return L"?:\\[" + volname + L"]\\" + rest; - -#elif defined ZEN_LINUX || defined ZEN_MAC //neither supported nor needed - return "/.../[" + volname + "]/" + rest; -#endif - } - } - return text; -} -} - - -void getDirectoryAliasesRecursive(const Zstring& dirname, std::set& output) -{ -#ifdef ZEN_WIN - //1. replace volume path by volume name: c:\dirname -> [SYSTEM]\dirname - if (dirname.size() >= 3 && - std::iswalpha(dirname[0]) && - dirname[1] == L':' && - dirname[2] == L'\\') - { - Zstring volname = getVolumeName(Zstring(dirname.c_str(), 3)); //should not block - if (!volname.empty()) - output.insert(L"[" + volname + L"]" + Zstring(dirname.c_str() + 2)); - } - - //2. replace volume name by volume path: [SYSTEM]\dirname -> c:\dirname - { - Zstring testVolname = expandVolumeName(dirname); //should not block - if (testVolname != dirname) - if (output.insert(testVolname).second) - getDirectoryAliasesRecursive(testVolname, output); //recurse! - } -#endif - - //3. environment variables: C:\Users\ -> %USERPROFILE% - { - std::map envToDir; - - //get list of useful variables - auto addEnvVar = [&](const Zstring& envName) - { - if (std::unique_ptr value = getEnvironmentVar(envName)) - envToDir.insert(std::make_pair(envName, *value)); - }; -#ifdef ZEN_WIN - addEnvVar(L"AllUsersProfile"); // C:\ProgramData - addEnvVar(L"AppData"); // C:\Users\\AppData\Roaming - addEnvVar(L"LocalAppData"); // C:\Users\\AppData\Local - addEnvVar(L"ProgramData"); // C:\ProgramData - addEnvVar(L"ProgramFiles"); // C:\Program Files - addEnvVar(L"ProgramFiles(x86)");// C:\Program Files (x86) - addEnvVar(L"CommonProgramFiles"); // C:\Program Files\Common Files - addEnvVar(L"CommonProgramFiles(x86)"); // C:\Program Files (x86)\Common Files - addEnvVar(L"Public"); // C:\Users\Public - addEnvVar(L"UserProfile"); // C:\Users\ - addEnvVar(L"WinDir"); // C:\Windows - addEnvVar(L"Temp"); // C:\Windows\Temp - - //add CSIDL values: http://msdn.microsoft.com/en-us/library/bb762494(v=vs.85).aspx - const auto& csidlMap = CsidlConstants::get(); - envToDir.insert(csidlMap.begin(), csidlMap.end()); - -#elif defined ZEN_LINUX || defined ZEN_MAC - addEnvVar("HOME"); //Linux: /home/ Mac: /Users/ -#endif - //substitute paths by symbolic names - auto pathStartsWith = [](const Zstring& path, const Zstring& prefix) -> bool - { -#if defined ZEN_WIN || defined ZEN_MAC - Zstring tmp = path; - Zstring tmp2 = prefix; - ::makeUpper(tmp); - ::makeUpper(tmp2); - return startsWith(tmp, tmp2); -#elif defined ZEN_LINUX - return startsWith(path, prefix); -#endif - }; - for (const auto& entry : envToDir) - if (pathStartsWith(dirname, entry.second)) - output.insert(MACRO_SEP + entry.first + MACRO_SEP + (dirname.c_str() + entry.second.size())); - } - - //4. replace (all) macros: %USERPROFILE% -> C:\Users\ - { - Zstring testMacros = expandMacros(dirname); - if (testMacros != dirname) - if (output.insert(testMacros).second) - getDirectoryAliasesRecursive(testMacros, output); //recurse! - } -} - - -std::vector zen::getDirectoryAliases(const Zstring& dirString) -{ - Zstring dirname = dirString; - trim(dirname, true, false); - if (dirname.empty()) - return std::vector(); - - std::set tmp; - getDirectoryAliasesRecursive(dirname, tmp); - - tmp.erase(dirname); - tmp.erase(Zstring()); - - return std::vector(tmp.begin(), tmp.end()); -} - - -Zstring zen::getFormattedDirectoryName(const Zstring& dirString) // throw() -{ - //formatting is needed since functions expect the directory to end with '\' to be able to split the relative names. - - Zstring dirname = dirString; - - //remove leading/trailing whitespace before allowing misinterpretation in applyLongPathPrefix() - trim(dirname, true, false); - while (endsWith(dirname, Zstr(' '))) //don't remove all whitespace from right, e.g. 0xa0 may be used as part of dir name - dirname.resize(dirname.size() - 1); - - if (dirname.empty()) //an empty string would later be resolved as "\"; this is not desired - return Zstring(); - - dirname = expandMacros(dirname); - dirname = expandVolumeName(dirname); //may block for slow USB sticks! - - /* - need to resolve relative paths: - WINDOWS: - - \\?\-prefix which needs absolute names - - Volume Shadow Copy: volume name needs to be part of each filename - - file icon buffer (at least for extensions that are actually read from disk, like "exe") - - ::SHFileOperation(): Using relative path names is not thread safe - WINDOWS/LINUX: - - detection of dependent directories, e.g. "\" and "C:\test" - */ - dirname = resolveRelativePath(dirname); - - return appendSeparator(dirname); -} - - -#ifdef ZEN_WIN -void zen::loginNetworkShare(const Zstring& dirnameOrig, bool allowUserInteraction) //throw() - user interaction: show OS password prompt -{ - /* - ATTENTION: it is not safe to retrieve UNC path via ::WNetGetConnection() for every type of network share: - - network type |::WNetGetConnection rv | lpRemoteName | existing UNC path - -----------------------------|-------------------------|---------------------------------|---------------- - inactive local network share | ERROR_CONNECTION_UNAVAIL| \\192.168.1.27\new2 | YES - WebDrive | NO_ERROR | \\Webdrive-ZenJu\GNU | NO - Box.net (WebDav) | NO_ERROR | \\www.box.net\DavWWWRoot\dav | YES - NetDrive | ERROR_NOT_CONNECTED | | NO - ____________________________________________________________________________________________________________ - - Windows Login Prompt Naming Conventions: - user account: \ e.g. WIN-XP\ZenJu - network share: \\\ e.g. \\WIN-XP\test - - Windows Command Line: - - list *all* active network connections, including deviceless ones which are hidden in Explorer: - net use - - delete active connection: - net use /delete \\server\share - ____________________________________________________________________________________________________________ - - Scenario: XP-shared folder is accessed by Win 7 over LAN with access limited to a certain user - - Problems: - I. WNetAddConnection2() allows (at least certain) invalid credentials (e.g. username: a/password: a) and establishes an *unusable* connection - II. WNetAddConnection2() refuses to overwrite an existing (unusable) connection created in I), but shows prompt repeatedly - III. WNetAddConnection2() won't bring up the prompt if *wrong* credentials had been entered just recently, even with CONNECT_INTERACTIVE specified! => 2-step proccess - */ - - auto connect = [&](NETRESOURCE& trgRes) //blocks heavily if network is not reachable!!! - { - //1. first try to connect without user interaction - blocks! - DWORD rv = ::WNetAddConnection2(&trgRes, // __in LPNETRESOURCE lpNetResource, - nullptr, // __in LPCTSTR lpPassword, - nullptr, // __in LPCTSTR lpUsername, - 0); //__in DWORD dwFlags - //53L ERROR_BAD_NETPATH The network path was not found. - //86L ERROR_INVALID_PASSWORD - //1219L ERROR_SESSION_CREDENTIAL_CONFLICT Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again. - //1326L ERROR_LOGON_FAILURE Logon failure: unknown user name or bad password. - //1236L ERROR_CONNECTION_ABORTED - if (somethingExists(trgRes.lpRemoteName)) //blocks! - return; //success: connection usable! -> don't care about "rv" - - if (rv == ERROR_BAD_NETPATH || //Windows 7 - rv == ERROR_BAD_NET_NAME|| //XP - rv == ERROR_CONNECTION_ABORTED) //failed to connect to a network that existed not too long ago; will later return ERROR_BAD_NETPATH - return; //no need to show a prompt for an unreachable network device - - //2. if first attempt failed, we need to *force* prompt by using CONNECT_PROMPT - if (allowUserInteraction) - { - //avoid problem II.) - DWORD rv2= ::WNetCancelConnection2(trgRes.lpRemoteName, //_In_ LPCTSTR lpName, - 0, //_In_ DWORD dwFlags, - true); //_In_ BOOL fForce - //2250L ERROR_NOT_CONNECTED - - //enforce login prompt - DWORD rv3 = ::WNetAddConnection2(&trgRes, // __in LPNETRESOURCE lpNetResource, - nullptr, // __in LPCTSTR lpPassword, - nullptr, // __in LPCTSTR lpUsername, - CONNECT_INTERACTIVE | CONNECT_PROMPT); //__in DWORD dwFlags - (void)rv2; - (void)rv3; - } - }; - - - Zstring dirname = removeLongPathPrefix(dirnameOrig); - trim(dirname, true, false); - - //1. locally mapped network share - if (dirname.size() >= 2 && iswalpha(dirname[0]) && dirname[1] == L':') - { - Zstring driveLetter(dirname.c_str(), 2); //e.g.: "Q:" - { - DWORD bufferSize = 10000; - std::vector remoteNameBuffer(bufferSize); - - //map local -> remote - - //note: following function call does NOT block! - DWORD rv = ::WNetGetConnection(driveLetter.c_str(), //__in LPCTSTR lpLocalName in the form ":" - &remoteNameBuffer[0], //__out LPTSTR lpRemoteName, - &bufferSize); //__inout LPDWORD lpnLength - if (rv == ERROR_CONNECTION_UNAVAIL) //remoteNameBuffer will be filled nevertheless! - { - //ERROR_CONNECTION_UNAVAIL: network mapping is existing, but not connected - - Zstring networkShare = &remoteNameBuffer[0]; - if (!networkShare.empty()) - { - NETRESOURCE trgRes = {}; - trgRes.dwType = RESOURCETYPE_DISK; - trgRes.lpLocalName = const_cast(driveLetter.c_str()); //lpNetResource is marked "__in", seems WNetAddConnection2 is not const correct! - trgRes.lpRemoteName = const_cast(networkShare.c_str()); // - - connect(trgRes); //blocks! - } - } - } - } - //2. deviceless network connection - else if (startsWith(dirname, L"\\\\")) //UNC path - { - const Zstring networkShare = [&]() -> Zstring //extract prefix "\\server\share" - { - size_t pos = dirname.find('\\', 2); - if (pos == Zstring::npos) - return Zstring(); - pos = dirname.find('\\', pos + 1); - return pos == Zstring::npos ? dirname : Zstring(dirname.c_str(), pos); - }(); - - if (!networkShare.empty()) - { - //::WNetGetResourceInformation seems to fail with ERROR_BAD_NET_NAME even for existing unconnected network shares! - // => unconditionally try to connect to network share, seems we cannot reliably detect connection status otherwise - - NETRESOURCE trgRes = {}; - trgRes.dwType = RESOURCETYPE_DISK; - trgRes.lpRemoteName = const_cast(networkShare.c_str()); //trgRes is "__in" - - connect(trgRes); //blocks! - } - } -} -#endif diff --git a/lib/resolve_path.h b/lib/resolve_path.h deleted file mode 100644 index b9c7196f..00000000 --- a/lib/resolve_path.h +++ /dev/null @@ -1,36 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef RESOLVE_PATH_H_INCLUDED -#define RESOLVE_PATH_H_INCLUDED - -#include -#include - -namespace zen -{ -/* -FULL directory format: - - expand macros - - expand volume path by name - - convert relative paths into absolute - - trim whitespace and append file name separator -*/ -Zstring getFormattedDirectoryName(const Zstring& dirString); //throw() - may still block for slow USB sticks! not thread-safe!!!(see ::GetFullPathName()) - -//macro substitution only -Zstring expandMacros(const Zstring& text); - -std::vector getDirectoryAliases(const Zstring& dirString); //may block for slow USB sticks when resolving [] - -#ifdef ZEN_WIN -//*blocks* if network is not reachable or when showing login prompt dialog! -void loginNetworkShare(const Zstring& dirname, bool allowUserInteraction); //throw() - user interaction: show OS password prompt -#endif -} - - -#endif // RESOLVE_PATH_H_INCLUDED diff --git a/lib/return_codes.h b/lib/return_codes.h deleted file mode 100644 index a37e11f2..00000000 --- a/lib/return_codes.h +++ /dev/null @@ -1,30 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef RETURN_CODES_H_INCLUDED -#define RETURN_CODES_H_INCLUDED - -namespace zen -{ -enum FfsReturnCode -{ - FFS_RC_SUCCESS = 0, - FFS_RC_FINISHED_WITH_WARNINGS, - FFS_RC_FINISHED_WITH_ERRORS, - FFS_RC_ABORTED, - FFS_RC_EXCEPTION, -}; - - -inline -void raiseReturnCode(FfsReturnCode& rc, FfsReturnCode rcProposed) -{ - if (rc < rcProposed) - rc = rcProposed; -} -} - -#endif // RETURN_CODES_H_INCLUDED diff --git a/lib/shadow.cpp b/lib/shadow.cpp deleted file mode 100644 index d1027f69..00000000 --- a/lib/shadow.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "shadow.h" -#include -#include //includes "windows.h" -#include -#include -#include -#include -#include -#include "ShadowCopy/shadow.h" -#include - -using namespace zen; -using namespace shadow; - - -namespace -{ -bool runningWOW64() //test if process is running under WOW64 (reference http://msdn.microsoft.com/en-us/library/ms684139(VS.85).aspx) -{ - typedef BOOL (WINAPI* IsWow64ProcessFun)(HANDLE hProcess, PBOOL Wow64Process); - - const SysDllFun isWow64Process(L"kernel32.dll", "IsWow64Process"); - if (isWow64Process) - { - BOOL isWow64 = FALSE; - if (isWow64Process(::GetCurrentProcess(), &isWow64)) - return isWow64 != FALSE; - } - return false; -} - -const bool wereVistaOrLater = vistaOrLater(); //thread-safety: init at startup -} - -//############################################################################################################# - -class ShadowCopy::ShadowVolume -{ -public: - ShadowVolume(const Zstring& volumeNamePf) : //throw FileError - createShadowCopy (getDllName(), funName_createShadowCopy), - releaseShadowCopy(getDllName(), funName_releaseShadowCopy), - getShadowVolume (getDllName(), funName_getShadowVolume), - getLastError (getDllName(), funName_getLastError), - backupHandle(nullptr) - { - //VSS does not support running under WOW64 except for Windows XP and Windows Server 2003 - //reference: http://msdn.microsoft.com/en-us/library/aa384627(VS.85).aspx - if (runningWOW64()) - throw FileError(_("Cannot access the Volume Shadow Copy Service."), - _("Please use FreeFileSync 64-bit version to create shadow copies on this system.")); - - //check if shadow copy dll was loaded correctly - if (!createShadowCopy || !releaseShadowCopy || !getShadowVolume || !getLastError) - throw FileError(_("Cannot access the Volume Shadow Copy Service."), - replaceCpy(_("Cannot load file %x."), L"%x", fmtFileName(getDllName()))); - - //--------------------------------------------------------------------------------------------------------- - //start volume shadow copy service: - backupHandle = createShadowCopy(volumeNamePf.c_str()); - if (!backupHandle) - throw FileError(_("Cannot access the Volume Shadow Copy Service."), - getLastError() + std::wstring(L" Volume: ") + fmtFileName(volumeNamePf)); - - shadowVolPf = appendSeparator(getShadowVolume(backupHandle)); //shadowVolName NEVER has a trailing backslash - } - - ~ShadowVolume() { releaseShadowCopy(backupHandle); } //fast! no performance optimization necessary - - Zstring geNamePf() const { return shadowVolPf; } //with trailing path separator - -private: - ShadowVolume(const ShadowVolume&); - ShadowVolume& operator=(const ShadowVolume&); - - const DllFun createShadowCopy; - const DllFun releaseShadowCopy; - const DllFun getShadowVolume; - const DllFun getLastError; - - Zstring shadowVolPf; - ShadowHandle backupHandle; -}; - -//############################################################################################################# - -Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile, const std::function& onBeforeMakeVolumeCopy) -{ - Zstring filenameFinal = inputFile; - - //try to resolve symlinks and junctions: - //1. symlinks: we need to retrieve the target path, else we would just return a symlink on a VSS volume while the target outside were still locked! - //2. junctions: C:\Users\ is a junction that may link to e.g. D:\Users\, so GetVolumePathName() returns "D:\" => "Volume name %x not part of file name %y." - if (wereVistaOrLater) - filenameFinal = getResolvedFilePath(inputFile); //throw FileError; requires Vista or later! - //-> returns paths with \\?\ prefix! => make sure to avoid duplicate shadow copies for volume paths with/without prefix - - DWORD bufferSize = 10000; - std::vector volBuffer(bufferSize); - if (!::GetVolumePathName(filenameFinal.c_str(), //__in LPCTSTR lpszFileName, - &volBuffer[0], //__out LPTSTR lpszVolumePathName, - bufferSize)) //__in DWORD cchBufferLength - throw FileError(replaceCpy(_("Cannot determine volume name for %x."), L"%x", fmtFileName(filenameFinal)), formatSystemError(L"GetVolumePathName", ::GetLastError())); - - const Zstring volumeNamePf = appendSeparator(&volBuffer[0]); //msdn: if buffer is 1 char too short, GetVolumePathName() may skip last separator without error! - - //input file is always absolute! directory formatting takes care of this! Therefore volume name can always be found. - const size_t pos = filenameFinal.find(volumeNamePf); //filenameFinal needs NOT to begin with volumeNamePf: consider \\?\ prefix! - if (pos == Zstring::npos) - throw FileError(replaceCpy(replaceCpy(_("Volume name %x is not part of file path %y."), - L"%x", fmtFileName(volumeNamePf)), - L"%y", fmtFileName(filenameFinal))); - - //get or create instance of shadow volume - auto it = shadowVol.find(volumeNamePf); - if (it == shadowVol.end()) - { - onBeforeMakeVolumeCopy(volumeNamePf); //notify client before (potentially) blocking some time - auto newEntry = std::make_shared(volumeNamePf); //throw FileError - it = shadowVol.insert(std::make_pair(volumeNamePf, newEntry)).first; - } - - //return filename alias on shadow copy volume - return it->second->geNamePf() + Zstring(filenameFinal.c_str() + pos + volumeNamePf.length()); -} diff --git a/lib/shadow.h b/lib/shadow.h deleted file mode 100644 index 36c72c25..00000000 --- a/lib/shadow.h +++ /dev/null @@ -1,36 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef SHADOW_H_INCLUDED -#define SHADOW_H_INCLUDED - -#include -#include -#include -#include -#include - -namespace shadow -{ -class ShadowCopy //take and buffer Windows Volume Shadow Copy snapshots as needed -{ -public: - ShadowCopy() {} - - //return filename on shadow copy volume - follows symlinks! - Zstring makeShadowCopy(const Zstring& inputFile, const std::function& onBeforeMakeVolumeCopy); //throw FileError - -private: - ShadowCopy(const ShadowCopy&); - ShadowCopy& operator=(const ShadowCopy&); - - class ShadowVolume; - typedef std::map, LessFilename> VolNameShadowMap; - VolNameShadowMap shadowVol; -}; -} - -#endif //SHADOW_H_INCLUDED diff --git a/lib/soft_filter.h b/lib/soft_filter.h deleted file mode 100644 index 010a8913..00000000 --- a/lib/soft_filter.h +++ /dev/null @@ -1,112 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef SOFT_FILTER_H_INCLUDED -#define SOFT_FILTER_H_INCLUDED - -#include -#include -#include "../structures.h" -//#include - -namespace zen -{ -/* -Semantics of SoftFilter: -1. It potentially may match only one side => it MUST NOT be applied while traversing a single folder to avoid mismatches -2. => it is applied after traversing and just marks rows, (NO deletions after comparison are allowed) -3. => equivalent to a user temporarily (de-)selecting rows => not relevant for -mode! -*/ - -class SoftFilter -{ -public: - SoftFilter(size_t timeSpan, UnitTime unitTimeSpan, - size_t sizeMin, UnitSize unitSizeMin, - size_t sizeMax, UnitSize unitSizeMax); - - bool matchTime(Int64 writeTime) const { return timeFrom_ <= writeTime; } - bool matchSize(UInt64 fileSize) const { return sizeMin_ <= fileSize && fileSize <= sizeMax_; } - bool matchFolder() const { return matchesFolder_; } - bool isNull() const; //filter is equivalent to NullFilter, but may be technically slower - - //small helper method: merge two soft filters - friend SoftFilter combineFilters(const SoftFilter& first, const SoftFilter& second); - -private: - SoftFilter(const Int64& timeFrom, - const UInt64& sizeMin, - const UInt64& sizeMax, - bool matchesFolder); - - Int64 timeFrom_; //unit: UTC, seconds - UInt64 sizeMin_; //unit: bytes - UInt64 sizeMax_; //unit: bytes - bool matchesFolder_; -}; -} - - - - - - - - - - - - - -// ----------------------- implementation ----------------------- -namespace zen -{ -inline -SoftFilter::SoftFilter(size_t timeSpan, UnitTime unitTimeSpan, - size_t sizeMin, UnitSize unitSizeMin, - size_t sizeMax, UnitSize unitSizeMax) : - matchesFolder_(unitTimeSpan == UTIME_NONE&& - unitSizeMin == USIZE_NONE&& - unitSizeMax == USIZE_NONE) //exclude folders if size or date filter is active: avoids creating empty folders if not needed! -{ - resolveUnits(timeSpan, unitTimeSpan, - sizeMin, unitSizeMin, - sizeMax, unitSizeMax, - timeFrom_, - sizeMin_, - sizeMax_); -} - -inline -SoftFilter::SoftFilter(const Int64& timeFrom, - const UInt64& sizeMin, - const UInt64& sizeMax, - bool matchesFolder) : - timeFrom_(timeFrom), - sizeMin_ (sizeMin), - sizeMax_ (sizeMax), - matchesFolder_(matchesFolder) {} - -inline -SoftFilter combineFilters(const SoftFilter& lhs, const SoftFilter& rhs) -{ - return SoftFilter(std::max(lhs.timeFrom_, rhs.timeFrom_), - std::max(lhs.sizeMin_, rhs.sizeMin_), - std::min(lhs.sizeMax_, rhs.sizeMax_), - lhs.matchesFolder_ && rhs.matchesFolder_); -} - -inline -bool SoftFilter::isNull() const //filter is equivalent to NullFilter, but may be technically slower -{ - return timeFrom_ == std::numeric_limits::min() && - sizeMin_ == 0U && - sizeMax_ == std::numeric_limits::max() && - matchesFolder_ == true;; -} -} - -#endif // SOFT_FILTER_H_INCLUDED diff --git a/lib/status_handler.cpp b/lib/status_handler.cpp deleted file mode 100644 index 5fb80161..00000000 --- a/lib/status_handler.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "status_handler.h" -//#include -#include - -using namespace zen; - - -namespace -{ -const std::int64_t TICKS_UPDATE_INTERVAL = UI_UPDATE_INTERVAL* ticksPerSec() / 1000; -TickVal lastExec = getTicks(); -}; - -bool zen::updateUiIsAllowed() -{ - const TickVal now = getTicks(); //0 on error - if (dist(lastExec, now) >= TICKS_UPDATE_INTERVAL) //perform ui updates not more often than necessary - { - lastExec = now; - return true; - } - return false; -} diff --git a/lib/status_handler.h b/lib/status_handler.h deleted file mode 100644 index 9a288b6f..00000000 --- a/lib/status_handler.h +++ /dev/null @@ -1,147 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef STATUSHANDLER_H_INCLUDED -#define STATUSHANDLER_H_INCLUDED - -#include "../process_callback.h" -#include -#include -#include -#include - -namespace zen -{ -bool updateUiIsAllowed(); //test if a specific amount of time is over - -/* -Updating GUI is fast! - time per single call to ProcessCallback::forceUiRefresh() - - Comparison 0.025 ms - - Synchronization 0.74 ms (despite complex graph control!) -*/ - -//gui may want to abort process -struct AbortCallback -{ - virtual ~AbortCallback() {} - virtual void requestAbortion() = 0; -}; - - -//common statistics "everybody" needs -struct Statistics -{ - virtual ~Statistics() {} - - virtual ProcessCallback::Phase currentPhase() const = 0; - - virtual int getObjectsCurrent(ProcessCallback::Phase phaseId) const = 0; - virtual int getObjectsTotal (ProcessCallback::Phase phaseId) const = 0; - - virtual Int64 getDataCurrent(ProcessCallback::Phase phaseId) const = 0; - virtual Int64 getDataTotal (ProcessCallback::Phase phaseId) const = 0; - - virtual const std::wstring& currentStatusText() const = 0; -}; - - -//partial callback implementation with common functionality for "batch", "GUI/Compare" and "GUI/Sync" -class StatusHandler : public ProcessCallback, public AbortCallback, public Statistics -{ -public: - StatusHandler() : currentPhase_(PHASE_NONE), - numbersCurrent_(4), //init with phase count - numbersTotal_ (4), // - abortRequested(false) {} - -protected: - //implement parts of ProcessCallback - virtual void initNewPhase(int objectsTotal, Int64 dataTotal, Phase phaseId) - { - currentPhase_ = phaseId; - refNumbers(numbersTotal_, currentPhase_) = std::make_pair(objectsTotal, dataTotal); - } - - virtual void updateProcessedData(int objectsDelta, Int64 dataDelta) { updateData(numbersCurrent_, objectsDelta, dataDelta); } //note: these methods MUST NOT throw in order - virtual void updateTotalData (int objectsDelta, Int64 dataDelta) { updateData(numbersTotal_ , objectsDelta, dataDelta); } //to properly allow undoing setting of statistics! - - virtual void requestUiRefresh() - { - if (abortRequested) //triggered by requestAbortion() - { - forceUiRefresh(); - abortThisProcess(); - } - else if (updateUiIsAllowed()) //test if specific time span between ui updates is over - forceUiRefresh(); - } - - virtual void reportStatus(const std::wstring& text) { statusText_ = text; requestUiRefresh(); /*throw AbortThisProcess */ } - virtual void reportInfo (const std::wstring& text) { statusText_ = text; requestUiRefresh(); /*throw AbortThisProcess */ } //log text in derived class - - //implement AbortCallback - virtual void requestAbortion() - { - abortRequested = true; - statusText_ =_("Stop requested: Waiting for current operation to finish..."); - } //this does NOT call abortThisProcess immediately, but when we're out of the C GUI call stack - - //implement Statistics - virtual Phase currentPhase() const { return currentPhase_; } - - virtual int getObjectsCurrent(Phase phaseId) const { return refNumbers(numbersCurrent_, phaseId).first; } - virtual int getObjectsTotal (Phase phaseId) const { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersTotal_ , phaseId).first; } - - virtual Int64 getDataCurrent(Phase phaseId) const { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersCurrent_, phaseId).second; } - virtual Int64 getDataTotal (Phase phaseId) const { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersTotal_ , phaseId).second; } - - virtual const std::wstring& currentStatusText() const { return statusText_; } - - //enrich this interface - virtual void abortThisProcess() = 0; - - bool abortIsRequested() const { return abortRequested; } - -private: - typedef std::vector> StatNumbers; - - void updateData(StatNumbers& num, int objectsDelta, Int64 dataDelta) - { - auto& st = refNumbers(num, currentPhase_); - st.first += objectsDelta; - st.second += dataDelta; - } - - static const std::pair& refNumbers(const StatNumbers& num, Phase phaseId) - { - switch (phaseId) - { - case PHASE_SCANNING: - return num[0]; - case PHASE_COMPARING_CONTENT: - return num[1]; - case PHASE_SYNCHRONIZING: - return num[2]; - case PHASE_NONE: - break; - } - assert(false); - return num[3]; //dummy entry! - } - - static std::pair& refNumbers(StatNumbers& num, Phase phaseId) { return const_cast&>(refNumbers(static_cast(num), phaseId)); } - - Phase currentPhase_; - StatNumbers numbersCurrent_; - StatNumbers numbersTotal_; - std::wstring statusText_; - - bool abortRequested; -}; -} - -#endif // STATUSHANDLER_H_INCLUDED diff --git a/lib/status_handler_impl.h b/lib/status_handler_impl.h deleted file mode 100644 index 210e6ecd..00000000 --- a/lib/status_handler_impl.h +++ /dev/null @@ -1,37 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef STATUSHANDLER_IMPL_H_INCLUDED -#define STATUSHANDLER_IMPL_H_INCLUDED - -#include -#include -#include "process_callback.h" - - -template inline -zen::Opt tryReportingError(Function cmd, ProcessCallback& handler) //return ignored error message if available -{ - for (size_t retryNumber = 0;; ++retryNumber) - try - { - cmd(); //throw FileError - return zen::NoValue(); - } - catch (zen::FileError& error) - { - switch (handler.reportError(error.toString(), retryNumber)) //may throw! - { - case ProcessCallback::IGNORE_ERROR: - return error.toString(); - case ProcessCallback::RETRY: - break; //continue with loop - } - } -} - - -#endif //STATUSHANDLER_IMPL_H_INCLUDED diff --git a/lib/versioning.cpp b/lib/versioning.cpp deleted file mode 100644 index 82696e3a..00000000 --- a/lib/versioning.cpp +++ /dev/null @@ -1,439 +0,0 @@ -#include "versioning.h" -#include -#include -#include -#include - -using namespace zen; - - -namespace -{ -Zstring getExtension(const Zstring& relativeName) //including "." if extension is existing, returns empty string otherwise -{ - auto iterSep = find_last(relativeName.begin(), relativeName.end(), FILE_NAME_SEPARATOR); - auto iterName = iterSep != relativeName.end() ? iterSep + 1 : relativeName.begin(); //find beginning of short name - auto iterDot = find_last(iterName, relativeName.end(), Zstr('.')); //equal to relativeName.end() if file has no extension!! - return Zstring(&*iterDot, relativeName.end() - iterDot); -}; -} - -bool impl::isMatchingVersion(const Zstring& shortname, const Zstring& shortnameVersion) //e.g. ("Sample.txt", "Sample.txt 2012-05-15 131513.txt") -{ - auto it = shortnameVersion.begin(); - auto last = shortnameVersion.end(); - - auto nextDigit = [&]() -> bool - { - if (it == last || !isDigit(*it)) - return false; - ++it; - return true; - }; - auto nextDigits = [&](size_t count) -> bool - { - while (count-- > 0) - if (!nextDigit()) - return false; - return true; - }; - auto nextChar = [&](Zchar c) -> bool - { - if (it == last || *it != c) - return false; - ++it; - return true; - }; - auto nextStringI = [&](const Zstring& str) -> bool //windows: ignore case! - { - if (last - it < static_cast(str.size()) || !EqualFilename()(str, Zstring(&*it, str.size()))) - return false; - it += str.size(); - return true; - }; - - return nextStringI(shortname) && //versioned file starts with original name - nextChar(Zstr(' ')) && //validate timestamp: e.g. "2012-05-15 131513"; Regex: \d{4}-\d{2}-\d{2} \d{6} - nextDigits(4) && //YYYY - nextChar(Zstr('-')) && // - nextDigits(2) && //MM - nextChar(Zstr('-')) && // - nextDigits(2) && //DD - nextChar(Zstr(' ')) && // - nextDigits(6) && //HHMMSS - nextStringI(getExtension(shortname)) && - it == last; -} - - -namespace -{ -/* -- handle not existing source -- create target super directories if missing -*/ -template -void moveItemToVersioning(const Zstring& fullName, //throw FileError - const Zstring& relativeName, - const Zstring& versioningDirectory, - const Zstring& timestamp, - VersioningStyle versioningStyle, - Function moveObj) //move source -> target; may throw FileError -{ - assert(!startsWith(relativeName, FILE_NAME_SEPARATOR)); - assert(!endsWith (relativeName, FILE_NAME_SEPARATOR)); - - Zstring targetName; - switch (versioningStyle) - { - case VER_STYLE_REPLACE: - targetName = appendSeparator(versioningDirectory) + relativeName; - break; - - case VER_STYLE_ADD_TIMESTAMP: - //assemble time-stamped version name - targetName = appendSeparator(versioningDirectory) + relativeName + Zstr(' ') + timestamp + getExtension(relativeName); - assert(impl::isMatchingVersion(afterLast(relativeName, FILE_NAME_SEPARATOR), afterLast(targetName, FILE_NAME_SEPARATOR))); //paranoid? no! - break; - } - - try - { - moveObj(fullName, targetName); //throw FileError - } - catch (FileError&) //expected to fail if target directory is not yet existing! - { - if (!somethingExists(fullName)) //no source at all is not an error (however a directory as source when a file is expected, *is* an error!) - return; //object *not* processed - - //create intermediate directories if missing - const Zstring targetDir = beforeLast(targetName, FILE_NAME_SEPARATOR); - if (!dirExists(targetDir)) //->(minor) file system race condition! - { - makeDirectory(targetDir); //throw FileError - moveObj(fullName, targetName); //throw FileError -> this should work now! - } - else - throw; - } -} - - -//move source to target across volumes -//no need to check if: - super-directories of target exist - source exists: done by moveItemToVersioning() -//if target already exists, it is overwritten, even if it is a different type, e.g. a directory! -template -void moveObject(const Zstring& sourceFile, //throw FileError - const Zstring& targetFile, - Function copyDelete) //throw FileError; fallback if move failed -{ - assert(fileExists(sourceFile) || symlinkExists(sourceFile) || !somethingExists(sourceFile)); //we process files and symlinks only - - auto removeTarget = [&] - { - //remove target object - if (dirExists(targetFile)) //directory or dir-symlink - removeDirectory(targetFile); //throw FileError; we do not expect targetFile to be a directory in general => no callback required - else //file or (broken) file-symlink - removeFile(targetFile); //throw FileError - }; - - //first try to move directly without copying - try - { - renameFile(sourceFile, targetFile); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting - return; //great, we get away cheaply! - } - //if moving failed treat as error (except when it tried to move to a different volume: in this case we will copy the file) - catch (const ErrorDifferentVolume&) - { - removeTarget(); //throw FileError - copyDelete(); // - } - catch (const ErrorTargetExisting&) - { - removeTarget(); //throw FileError - try - { - renameFile(sourceFile, targetFile); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting - } - catch (const ErrorDifferentVolume&) - { - copyDelete(); //throw FileError - } - } -} - - -void moveFile(const Zstring& sourceFile, const Zstring& targetFile, CallbackCopyFile& callback) //throw FileError -{ - moveObject(sourceFile, //throw FileError - targetFile, - [&] - { - //create target - if (symlinkExists(sourceFile)) - copySymlink(sourceFile, targetFile, false); //throw FileError; don't copy filesystem permissions - else - copyFile(sourceFile, targetFile, false, true, &callback); //throw FileError - permissions "false", transactional copy "true" - - //delete source - removeFile(sourceFile); //throw FileError; newly copied file is NOT deleted if exception is thrown here! - }); -} - - -void moveDirSymlink(const Zstring& sourceLink, const Zstring& targetLink) //throw FileError -{ - moveObject(sourceLink, //throw FileError - targetLink, - [&] - { - //create target - copySymlink(sourceLink, targetLink, false); //throw FileError; don't copy filesystem permissions - - //delete source - removeDirectory(sourceLink); //throw FileError; newly copied link is NOT deleted if exception is thrown here! - }); -} - - -class TraverseFilesOneLevel : public TraverseCallback -{ -public: - TraverseFilesOneLevel(std::vector& files, std::vector& dirs) : files_(files), dirs_(dirs) {} - -private: - virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) - { - files_.push_back(shortName); - } - - virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) - { - if (dirExists(fullName)) //dir symlink - dirs_.push_back(shortName); - else //file symlink, broken symlink - files_.push_back(shortName); - return LINK_SKIP; - } - - virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& fullName) - { - dirs_.push_back(shortName); - return nullptr; //DON'T traverse into subdirs; moveDirectory works recursively! - } - - virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) { throw FileError(msg); } - virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) { throw FileError(msg); } - - std::vector& files_; - std::vector& dirs_; -}; -} - - -bool FileVersioner::revisionFile(const Zstring& fullName, const Zstring& relativeName, CallbackMoveFile& callback) //throw FileError -{ - struct CallbackMoveFileImpl : public CallbackMoveDir - { - CallbackMoveFileImpl(CallbackMoveFile& callback) : callback_(callback) {} - private: - virtual void onBeforeFileMove(const Zstring& fileFrom, const Zstring& fileTo) {} - virtual void onBeforeDirMove (const Zstring& dirFrom, const Zstring& dirTo ) {} - virtual void updateStatus(Int64 bytesDelta) { callback_.updateStatus(bytesDelta); } - CallbackMoveFile& callback_; - } cb(callback); - - return revisionFileImpl(fullName, relativeName, cb); //throw FileError -} - - -bool FileVersioner::revisionFileImpl(const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError -{ - bool moveSuccessful = false; - - moveItemToVersioning(fullName, //throw FileError - relativeName, - versioningDirectory_, - timeStamp_, - versioningStyle_, - [&](const Zstring& source, const Zstring& target) - { - callback.onBeforeFileMove(source, target); //if we're called by revisionDirImpl() we know that "source" exists! - //when called by revisionFile(), "source" might not exist, however onBeforeFileMove() is not propagated in this case! - - struct CopyCallbackImpl : public CallbackCopyFile - { - CopyCallbackImpl(CallbackMoveDir& callback) : callback_(callback) {} - private: - virtual void deleteTargetFile(const Zstring& targetFile) { assert(!somethingExists(targetFile)); } - virtual void updateCopyStatus(Int64 bytesDelta) { callback_.updateStatus(bytesDelta); } - CallbackMoveDir& callback_; - } copyCallback(callback); - - moveFile(source, target, copyCallback); //throw FileError - moveSuccessful = true; - }); - return moveSuccessful; -} - - -void FileVersioner::revisionDir(const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError -{ - //no error situation if directory is not existing! manual deletion relies on it! - if (!somethingExists(fullName)) - return; //neither directory nor any other object (e.g. broken symlink) with that name existing - revisionDirImpl(fullName, relativeName, callback); //throw FileError -} - - -void FileVersioner::revisionDirImpl(const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError -{ - assert(somethingExists(fullName)); //[!] - - //create target - if (symlinkExists(fullName)) //on Linux there is just one type of symlink, and since we do revision file symlinks, we should revision dir symlinks as well! - { - moveItemToVersioning(fullName, //throw FileError - relativeName, - versioningDirectory_, - timeStamp_, - versioningStyle_, - [&](const Zstring& source, const Zstring& target) - { - callback.onBeforeDirMove(source, target); - moveDirSymlink(source, target); //throw FileError - }); - } - else - { - assert(!startsWith(relativeName, FILE_NAME_SEPARATOR)); - assert(endsWith(fullName, relativeName)); //usually, yes, but we might relax this in the future - const Zstring targetDir = appendSeparator(versioningDirectory_) + relativeName; - - //makeDirectory(targetDir); //FileError -> create only when needed in moveFileToVersioning(); avoids empty directories - - //traverse source directory one level - std::vector fileList; //list of *short* names - std::vector dirList; // - { - TraverseFilesOneLevel tol(fileList, dirList); //throw FileError - traverseFolder(fullName, tol); // - } - - const Zstring fullNamePf = appendSeparator(fullName); - const Zstring relnamePf = appendSeparator(relativeName); - - //move files - std::for_each(fileList.begin(), fileList.end(), - [&](const Zstring& shortname) - { - revisionFileImpl(fullNamePf + shortname, //throw FileError - relnamePf + shortname, - callback); - }); - - //move items in subdirectories - std::for_each(dirList.begin(), dirList.end(), - [&](const Zstring& shortname) - { - revisionDirImpl(fullNamePf + shortname, //throw FileError - relnamePf + shortname, - callback); - }); - - //delete source - callback.onBeforeDirMove(fullName, targetDir); - removeDirectory(fullName); //throw FileError - } -} - - -/* -namespace -{ -class TraverseVersionsOneLevel : public TraverseCallback -{ -public: - TraverseVersionsOneLevel(std::vector& files, std::function updateUI) : files_(files), updateUI_(updateUI) {} - -private: - virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) { files_.push_back(shortName); updateUI_(); } - virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) { files_.push_back(shortName); updateUI_(); return LINK_SKIP; } - virtual std::shared_ptr onDir(const Zchar* shortName, const Zstring& fullName) { updateUI_(); return nullptr; } //DON'T traverse into subdirs - virtual HandleError reportDirError (const std::wstring& msg) { throw FileError(msg); } - virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) { throw FileError(msg); } - - std::vector& files_; - std::function updateUI_; -}; -} - -void FileVersioner::limitVersions(std::function updateUI) //throw FileError -{ - if (versionCountLimit_ < 0) //no limit! - return; - - //buffer map "directory |-> list of immediate child file and symlink short names" - std::map, LessFilename> dirBuffer; - - auto getVersionsBuffered = [&](const Zstring& dirname) -> const std::vector& - { - auto it = dirBuffer.find(dirname); - if (it != dirBuffer.end()) - return it->second; - - std::vector fileShortNames; - TraverseVersionsOneLevel tol(fileShortNames, updateUI); //throw FileError - traverseFolder(dirname, tol); - - auto& newEntry = dirBuffer[dirname]; //transactional behavior!!! - newEntry.swap(fileShortNames); //-> until C++11 emplace is available - - return newEntry; - }; - - std::for_each(fileRelNames.begin(), fileRelNames.end(), - [&](const Zstring& relativeName) //e.g. "subdir\Sample.txt" - { - const Zstring fullname = appendSeparator(versioningDirectory_) + relativeName; //e.g. "D:\Revisions\subdir\Sample.txt" - const Zstring parentDir = beforeLast(fullname, FILE_NAME_SEPARATOR); //e.g. "D:\Revisions\subdir" - const Zstring shortname = afterLast(relativeName, FILE_NAME_SEPARATOR); //e.g. "Sample.txt"; returns the whole string if seperator not found - - const std::vector& allVersions = getVersionsBuffered(parentDir); - - //filter out only those versions that match the given relative name - std::vector matches; //e.g. "Sample.txt 2012-05-15 131513.txt" - - std::copy_if(allVersions.begin(), allVersions.end(), std::back_inserter(matches), - [&](const Zstring& shortnameVer) { return impl::isMatchingVersion(shortname, shortnameVer); }); - - //take advantage of version naming convention to find oldest versions - if (matches.size() <= static_cast(versionCountLimit_)) - return; - std::nth_element(matches.begin(), matches.end() - versionCountLimit_, matches.end(), LessFilename()); //windows: ignore case! - - //delete obsolete versions - std::for_each(matches.begin(), matches.end() - versionCountLimit_, - [&](const Zstring& shortnameVer) - { - updateUI(); - const Zstring fullnameVer = parentDir + FILE_NAME_SEPARATOR + shortnameVer; - try - { - removeFile(fullnameVer); //throw FileError - } - catch (FileError&) - { -#ifdef ZEN_WIN //if it's a directory symlink: - if (symlinkExists(fullnameVer) && dirExists(fullnameVer)) - removeDirectory(fullnameVer); //throw FileError - else -#endif - throw; - } - }); - }); -} -*/ diff --git a/lib/versioning.h b/lib/versioning.h deleted file mode 100644 index 06065656..00000000 --- a/lib/versioning.h +++ /dev/null @@ -1,89 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef VERSIONING_HEADER_8760247652438056 -#define VERSIONING_HEADER_8760247652438056 - -#include -#include -#include -#include -#include -#include -#include "../structures.h" - -namespace zen -{ -struct CallbackMoveDir; -struct CallbackMoveFile; - -//e.g. move C:\Source\subdir\Sample.txt -> D:\Revisions\subdir\Sample.txt 2012-05-15 131513.txt -//scheme: \\. YYYY-MM-DD HHMMSS. -/* - - ignores missing source files/dirs - - creates missing intermediate directories - - does not create empty directories - - handles symlinks - - replaces already existing target files/dirs (supports retry) - => (unlikely) risk of data loss for naming convention "versioning": - race-condition if two FFS instances start at the very same second OR multiple folder pairs process the same filename!! -*/ - -class FileVersioner -{ -public: - FileVersioner(const Zstring& versioningDirectory, //throw FileError - VersioningStyle versioningStyle, - const TimeComp& timeStamp) : //max versions per file; < 0 := no limit - versioningStyle_(versioningStyle), - versioningDirectory_(versioningDirectory), - timeStamp_(formatTime(Zstr("%Y-%m-%d %H%M%S"), timeStamp)) //e.g. "2012-05-15 131513" - { - if (timeStamp_.size() != 17) //formatTime() returns empty string on error; unexpected length: e.g. problem in year 10000! - throw FileError(_("Unable to create timestamp for versioning:") + L" \"" + timeStamp_ + L"\""); - } - - bool revisionFile(const Zstring& fullName, const Zstring& relativeName, CallbackMoveFile& callback); //throw FileError; return "false" if file is not existing - void revisionDir (const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback); //throw FileError - - //void limitVersions(std::function updateUI); //throw FileError; call when done revisioning! - -private: - bool revisionFileImpl(const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback); //throw FileError - void revisionDirImpl (const Zstring& fullName, const Zstring& relativeName, CallbackMoveDir& callback); //throw FileError - - const VersioningStyle versioningStyle_; - const Zstring versioningDirectory_; - const Zstring timeStamp_; - - //std::vector fileRelNames; //store list of revisioned file and symlink relative names for limitVersions() -}; - - -struct CallbackMoveFile //see CallbackCopyFile for limitations when throwing exceptions! -{ - virtual ~CallbackMoveFile() {} - virtual void updateStatus(Int64 bytesDelta) = 0; //called frequently if move has to revert to copy + delete: -}; - -struct CallbackMoveDir //see CallbackCopyFile for limitations when throwing exceptions! -{ - virtual ~CallbackMoveDir() {} - - virtual void onBeforeFileMove(const Zstring& fileFrom, const Zstring& fileTo) = 0; //one call for each *existing* object! - virtual void onBeforeDirMove (const Zstring& dirFrom, const Zstring& dirTo ) = 0; // - virtual void updateStatus(Int64 bytesDelta) = 0; //called frequently if move has to revert to copy + delete: -}; - - - -namespace impl //declare for unit tests: -{ -bool isMatchingVersion(const Zstring& shortname, const Zstring& shortnameVersion); -} -} - -#endif //VERSIONING_HEADER_8760247652438056 diff --git a/lib/xml_base.cpp b/lib/xml_base.cpp deleted file mode 100644 index f504d19a..00000000 --- a/lib/xml_base.cpp +++ /dev/null @@ -1,108 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "xml_base.h" -#include -#include - -using namespace zen; - - -//loadXmlDocument vs loadStream: -//1. better error reporting -//2. quick exit if (potentially large) input file is not an XML -XmlDoc xmlAccess::loadXmlDocument(const Zstring& filename) //throw FfsXmlError -{ - std::string stream; - try - { - FileInput inputFile(filename); //throw FileError - { - //quick test whether input is an XML: avoid loading large binary files up front! - const std::string xmlBegin = "(e.row + 1)), - L"%z", numberTo(e.col + 1))); - } -} - - -const std::wstring xmlAccess::getErrorMessageFormatted(const std::vector& failedElements) -{ - std::wstring msg; - - if (!failedElements.empty()) - { - msg = _("Cannot read the following XML elements:") + L"\n"; - std::for_each(failedElements.begin(), failedElements.end(), [&](const std::wstring& elem) { msg += L"\n" + elem; }); - } - - return msg; -} - - -void xmlAccess::saveXmlDocument(const zen::XmlDoc& doc, const Zstring& filename) //throw FfsXmlError -{ - std::string stream = serialize(doc); //throw () - - bool saveNecessary = true; - try - { - if (zen::getFilesize(filename) == stream.size()) //throw FileError - try - { - if (zen::loadStream(filename) == stream) //throw XmlFileError - saveNecessary = false; - } - catch (const zen::XmlFileError&) {} - } - catch (FileError&) {} - - if (saveNecessary) - try - { - FileOutput outputFile(filename, FileOutput::ACC_OVERWRITE); //throw FileError - outputFile.write(stream.c_str(), stream.length()); // - } - catch (const FileError& error) - { - throw FfsXmlError(error.toString()); - } -} diff --git a/lib/xml_base.h b/lib/xml_base.h deleted file mode 100644 index 85d4dfa1..00000000 --- a/lib/xml_base.h +++ /dev/null @@ -1,42 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef XMLBASE_H_INCLUDED -#define XMLBASE_H_INCLUDED - -#include -#include -#include - -//bind zen::Xml and zen file handling together - -namespace xmlAccess -{ -class FfsXmlError //Exception class -{ -public: - enum Severity - { - WARNING = 77, - FATAL - }; - - explicit FfsXmlError(const std::wstring& message, Severity sev = FATAL) : errorMessage(message), m_severity(sev) {} - - const std::wstring& toString() const { return errorMessage; } - Severity getSeverity() const { return m_severity; } -private: - const std::wstring errorMessage; - const Severity m_severity; -}; - -void saveXmlDocument(const zen::XmlDoc& doc, const Zstring& filename); //throw FfsXmlError -zen::XmlDoc loadXmlDocument(const Zstring& filename); //throw FfsXmlError - -const std::wstring getErrorMessageFormatted(const std::vector& failedElements); -} - -#endif // XMLBASE_H_INCLUDED diff --git a/process_callback.h b/process_callback.h deleted file mode 100644 index cbd9437f..00000000 --- a/process_callback.h +++ /dev/null @@ -1,74 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef PROC_HEADER_48257827842345454545 -#define PROC_HEADER_48257827842345454545 - -#include -#include - -//interface for comparison and synchronization process status updates (used by GUI or Batch mode) -const int UI_UPDATE_INTERVAL = 100; //unit: [ms]; perform ui updates not more often than necessary, -//100 seems to be a good value with only a minimal performance loss; also used by Win 7 copy progress bar -//this one is required by async directory existence check! - -//report status during comparison and synchronization -struct ProcessCallback -{ - virtual ~ProcessCallback() {} - - //these methods have to be implemented in the derived classes to handle error and status information - - //notify synchronization phases - enum Phase - { - PHASE_NONE, //initial status - PHASE_SCANNING, - PHASE_COMPARING_CONTENT, - PHASE_SYNCHRONIZING - }; - virtual void initNewPhase(int objectsTotal, zen::Int64 dataTotal, Phase phaseId) = 0; //informs about the estimated amount of data that will be processed in this phase - - //note: this one must NOT throw in order to properly allow undoing setting of statistics! - //it is in general paired with a call to requestUiRefresh() to compensate! - virtual void updateProcessedData(int objectsDelta, zen::Int64 dataDelta) = 0; //noexcept!! - virtual void updateTotalData (int objectsDelta, zen::Int64 dataDelta) = 0; // - /*the estimated and actual total workload may change *during* sync: - 1. detected file can be moved -> fallback to copy + delete - 2. file copy, actual size changed after comparison - 3. file contains significant ADS data, is sparse or compressed - 4. auto-resolution for failed create operations due to missing source - 5. directory deletion: may contain more items than scanned by FFS (excluded by filter) or less (contains followed symlinks) - 6. delete directory to recycler: no matter how many child-elements exist, this is only 1 item to process! - 7. file/directory already deleted externally: nothing to do, 0 logical operations and data - 8. user-defined deletion directory on different volume: full file copy required (instead of move) - 9. Binary file comparison: if files differ at the first few bytes, the result is already known - 10. Error during file copy, retry: bytes were copied => increases total workload! - */ - - //opportunity to abort must be implemented in a frequently executed method like requestUiRefresh() - virtual void requestUiRefresh() = 0; //throw ? - virtual void forceUiRefresh () = 0; //throw ? - called before starting long running tasks which don't update regularly - - //called periodically after data was processed: expected(!) to request GUI update - virtual void reportStatus(const std::wstring& text) = 0; //UI info only, should not be logged! - - //called periodically after data was processed: expected(!) to request GUI update - virtual void reportInfo(const std::wstring& text) = 0; - - virtual void reportWarning(const std::wstring& warningMessage, bool& warningActive) = 0; - - //error handling: - enum Response - { - IGNORE_ERROR = 10, - RETRY - }; - virtual Response reportError (const std::wstring& errorMessage, size_t retryNumber) = 0; //recoverable error situation - virtual void reportFatalError(const std::wstring& errorMessage) = 0; //non-recoverable error situation -}; - -#endif //PROC_HEADER_48257827842345454545 diff --git a/structures.cpp b/structures.cpp deleted file mode 100644 index e5dc08fd..00000000 --- a/structures.cpp +++ /dev/null @@ -1,516 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "structures.h" -#include -#include -#include -#include -#include - -using namespace zen; - - -std::wstring zen::getVariantName(CompareVariant var) -{ - switch (var) - { - case CMP_BY_CONTENT: - return _("File content"); - case CMP_BY_TIME_SIZE: - return _("File time and size"); - } - assert(false); - return _("Error"); -} - - -std::wstring zen::getVariantName(DirectionConfig::Variant var) -{ - switch (var) - { - case DirectionConfig::TWOWAY: - return L"<- " + _("Two way") + L" ->"; - case DirectionConfig::MIRROR: - return _("Mirror") + L" ->>"; - case DirectionConfig::UPDATE: - return _("Update") + L" ->"; - case DirectionConfig::CUSTOM: - return _("Custom"); - } - assert(false); - return _("Error"); -} - - -DirectionSet zen::extractDirections(const DirectionConfig& cfg) -{ - DirectionSet output; - switch (cfg.var) - { - case DirectionConfig::TWOWAY: - throw std::logic_error("there are no predefined directions for automatic mode!"); - - case DirectionConfig::MIRROR: - output.exLeftSideOnly = SyncDirection::RIGHT; - output.exRightSideOnly = SyncDirection::RIGHT; - output.leftNewer = SyncDirection::RIGHT; - output.rightNewer = SyncDirection::RIGHT; - output.different = SyncDirection::RIGHT; - output.conflict = SyncDirection::RIGHT; - break; - - case DirectionConfig::UPDATE: - output.exLeftSideOnly = SyncDirection::RIGHT; - output.exRightSideOnly = SyncDirection::NONE; - output.leftNewer = SyncDirection::RIGHT; - output.rightNewer = SyncDirection::NONE; - output.different = SyncDirection::RIGHT; - output.conflict = SyncDirection::NONE; - break; - - case DirectionConfig::CUSTOM: - output = cfg.custom; - break; - } - return output; -} - - -bool zen::detectMovedFilesSelectable(const DirectionConfig& cfg) -{ - if (cfg.var == DirectionConfig::TWOWAY) - return false; //moved files are always detected since we have the database file anyway - - const DirectionSet tmp = zen::extractDirections(cfg); - return (tmp.exLeftSideOnly == SyncDirection::RIGHT && - tmp.exRightSideOnly == SyncDirection::RIGHT) || - (tmp.exLeftSideOnly == SyncDirection::LEFT&& - tmp.exRightSideOnly == SyncDirection::LEFT); -} - - -bool zen::detectMovedFilesEnabled(const DirectionConfig& cfg) -{ - return detectMovedFilesSelectable(cfg) ? cfg.detectMovedFiles : cfg.var == DirectionConfig::TWOWAY; -} - - -DirectionSet zen::getTwoWayUpdateSet() -{ - DirectionSet output; - output.exLeftSideOnly = SyncDirection::RIGHT; - output.exRightSideOnly = SyncDirection::LEFT; - output.leftNewer = SyncDirection::RIGHT; - output.rightNewer = SyncDirection::LEFT; - output.different = SyncDirection::NONE; - output.conflict = SyncDirection::NONE; - return output; -} - - -std::wstring MainConfiguration::getCompVariantName() const -{ - const CompareVariant firstVariant = firstPair.altCmpConfig.get() ? - firstPair.altCmpConfig->compareVar : - cmpConfig.compareVar; //fallback to main sync cfg - - //test if there's a deviating variant within the additional folder pairs - for (const FolderPairEnh& fp : additionalPairs) - { - const CompareVariant thisVariant = fp.altCmpConfig.get() ? - fp.altCmpConfig->compareVar : - cmpConfig.compareVar; //fallback to main sync cfg - if (thisVariant != firstVariant) - return _("Multiple..."); - } - - //seems to be all in sync... - return getVariantName(firstVariant); -} - - -std::wstring MainConfiguration::getSyncVariantName() const -{ - const DirectionConfig::Variant firstVariant = firstPair.altSyncConfig.get() ? - firstPair.altSyncConfig->directionCfg.var : - syncCfg.directionCfg.var; //fallback to main sync cfg - - //test if there's a deviating variant within the additional folder pairs - for (const FolderPairEnh& fp : additionalPairs) - { - const DirectionConfig::Variant thisVariant = fp.altSyncConfig.get() ? - fp.altSyncConfig->directionCfg.var : - syncCfg.directionCfg.var; - if (thisVariant != firstVariant) - return _("Multiple..."); - } - - //seems to be all in sync... - return getVariantName(firstVariant); -} - - -std::wstring zen::getSymbol(CompareFilesResult cmpRes) -{ - switch (cmpRes) - { - case FILE_LEFT_SIDE_ONLY: - return L"only <-"; - case FILE_RIGHT_SIDE_ONLY: - return L"only ->"; - case FILE_LEFT_NEWER: - return L"newer <-"; - case FILE_RIGHT_NEWER: - return L"newer ->"; - case FILE_DIFFERENT: - return L"!="; - case FILE_EQUAL: - case FILE_DIFFERENT_METADATA: //= sub-category of equal! - return L"'=="; //added quotation mark to avoid error in Excel cell when exporting to *.cvs - case FILE_CONFLICT: - return L"conflict"; - } - assert(false); - return std::wstring(); -} - - -std::wstring zen::getSymbol(SyncOperation op) -{ - switch (op) - { - case SO_CREATE_NEW_LEFT: - return L"create <-"; - case SO_CREATE_NEW_RIGHT: - return L"create ->"; - case SO_DELETE_LEFT: - return L"delete <-"; - case SO_DELETE_RIGHT: - return L"delete ->"; - case SO_MOVE_LEFT_SOURCE: - return L"move from <-"; - case SO_MOVE_LEFT_TARGET: - return L"move to <-"; - case SO_MOVE_RIGHT_SOURCE: - return L"move from ->"; - case SO_MOVE_RIGHT_TARGET: - return L"move to ->"; - case SO_OVERWRITE_LEFT: - case SO_COPY_METADATA_TO_LEFT: - return L"update <-"; - case SO_OVERWRITE_RIGHT: - case SO_COPY_METADATA_TO_RIGHT: - return L"update ->"; - case SO_DO_NOTHING: - return L" -"; - case SO_EQUAL: - return L"'=="; //added quotation mark to avoid error in Excel cell when exporting to *.cvs - case SO_UNRESOLVED_CONFLICT: - return L"conflict"; - }; - assert(false); - return std::wstring(); -} - - -namespace -{ -assert_static(std::numeric_limits::is_specialized); -assert_static(std::numeric_limits::is_specialized); - -/* -int daysSinceBeginOfWeek(int dayOfWeek) //0-6, 0=Monday, 6=Sunday -{ - assert(0 <= dayOfWeek && dayOfWeek <= 6); -#ifdef ZEN_WIN - DWORD firstDayOfWeek = 0; - if (::GetLocaleInfo(LOCALE_USER_DEFAULT, //__in LCID Locale, - LOCALE_IFIRSTDAYOFWEEK | // first day of week specifier, 0-6, 0=Monday, 6=Sunday - LOCALE_RETURN_NUMBER, //__in LCTYPE LCType, - reinterpret_cast(&firstDayOfWeek), //__out LPTSTR lpLCData, - sizeof(firstDayOfWeek) / sizeof(TCHAR)) > 0) //__in int cchData - { - assert(firstDayOfWeek <= 6); - return (dayOfWeek + (7 - firstDayOfWeek)) % 7; - } - else //default -#endif - return dayOfWeek; //let all weeks begin with monday -} -*/ - - -Int64 resolve(size_t value, UnitTime unit, Int64 defaultVal) -{ - TimeComp locTimeStruc = zen::localTime(); - - switch (unit) - { - case UTIME_NONE: - return defaultVal; - - case UTIME_TODAY: - locTimeStruc.second = 0; //0-61 - locTimeStruc.minute = 0; //0-59 - locTimeStruc.hour = 0; //0-23 - return localToTimeT(locTimeStruc); //convert local time back to UTC - - //case UTIME_THIS_WEEK: - //{ - // localTimeFmt->tm_sec = 0; //0-61 - // localTimeFmt->tm_min = 0; //0-59 - // localTimeFmt->tm_hour = 0; //0-23 - // const time_t timeFrom = ::mktime(localTimeFmt); - - // int dayOfWeek = (localTimeFmt->tm_wday + 6) % 7; //tm_wday := days since Sunday 0-6 - // // +6 == -1 in Z_7 - - // return Int64(timeFrom) - daysSinceBeginOfWeek(dayOfWeek) * 24 * 3600; - //} - - case UTIME_THIS_MONTH: - locTimeStruc.second = 0; //0-61 - locTimeStruc.minute = 0; //0-59 - locTimeStruc.hour = 0; //0-23 - locTimeStruc.day = 1; //1-31 - return localToTimeT(locTimeStruc); - - case UTIME_THIS_YEAR: - locTimeStruc.second = 0; //0-61 - locTimeStruc.minute = 0; //0-59 - locTimeStruc.hour = 0; //0-23 - locTimeStruc.day = 1; //1-31 - locTimeStruc.month = 1; //1-12 - return localToTimeT(locTimeStruc); - - case UTIME_LAST_X_DAYS: - locTimeStruc.second = 0; //0-61 - locTimeStruc.minute = 0; //0-59 - locTimeStruc.hour = 0; //0-23 - return localToTimeT(locTimeStruc) - Int64(value) * 24 * 3600; - } - - assert(false); - return localToTimeT(locTimeStruc); -} - - -UInt64 resolve(size_t value, UnitSize unit, UInt64 defaultVal) -{ - const UInt64 maxVal =std::numeric_limits::max(); - - switch (unit) - { - case USIZE_NONE: - return defaultVal; - case USIZE_BYTE: - return value; - case USIZE_KB: - return value > maxVal / 1024U ? maxVal : //prevent overflow!!! - 1024U * value; - case USIZE_MB: - return value > maxVal / (1024 * 1024U) ? maxVal : //prevent overflow!!! - 1024 * 1024U * value; - } - assert(false); - return defaultVal; -} -} - -void zen::resolveUnits(size_t timeSpan, UnitTime unitTimeSpan, - size_t sizeMin, UnitSize unitSizeMin, - size_t sizeMax, UnitSize unitSizeMax, - zen::Int64& timeFrom, //unit: UTC time, seconds - zen::UInt64& sizeMinBy, //unit: bytes - zen::UInt64& sizeMaxBy) //unit: bytes -{ - timeFrom = resolve(timeSpan, unitTimeSpan, std::numeric_limits::min()); - sizeMinBy = resolve(sizeMin, unitSizeMin, 0U); - sizeMaxBy = resolve(sizeMax, unitSizeMax, std::numeric_limits::max()); -} - - -namespace -{ -FilterConfig mergeFilterConfig(const FilterConfig& global, const FilterConfig& local) -{ - FilterConfig out = local; - - //hard filter - if (out.includeFilter == FilterConfig().includeFilter) - out.includeFilter = global.includeFilter; - //else: if both global and local include filter contain data, only local filter is preserved - - trim(out.excludeFilter, true, false); - out.excludeFilter = global.excludeFilter + Zstr("\n") + out.excludeFilter; - trim(out.excludeFilter, true, false); - - //soft filter - Int64 loctimeFrom; - UInt64 locSizeMinBy; - UInt64 locSizeMaxBy; - resolveUnits(out.timeSpan, out.unitTimeSpan, - out.sizeMin, out.unitSizeMin, - out.sizeMax, out.unitSizeMax, - loctimeFrom, //unit: UTC time, seconds - locSizeMinBy, //unit: bytes - locSizeMaxBy); //unit: bytes - - //soft filter - Int64 glotimeFrom; - UInt64 gloSizeMinBy; - UInt64 gloSizeMaxBy; - resolveUnits(global.timeSpan, global.unitTimeSpan, - global.sizeMin, global.unitSizeMin, - global.sizeMax, global.unitSizeMax, - glotimeFrom, - gloSizeMinBy, - gloSizeMaxBy); - - if (glotimeFrom > loctimeFrom) - { - out.timeSpan = global.timeSpan; - out.unitTimeSpan = global.unitTimeSpan; - } - if (gloSizeMinBy > locSizeMinBy) - { - out.sizeMin = global.sizeMin; - out.unitSizeMin = global.unitSizeMin; - } - if (gloSizeMaxBy < locSizeMaxBy) - { - out.sizeMax = global.sizeMax; - out.unitSizeMax = global.unitSizeMax; - } - return out; -} - - -inline -bool effectivelyEmpty(const FolderPairEnh& fp) -{ - auto isEmpty = [](Zstring dirname) - { - trim(dirname); - return dirname.empty(); - }; - return isEmpty(fp.leftDirectory) && isEmpty(fp.rightDirectory); -} -} - - -MainConfiguration zen::merge(const std::vector& mainCfgs) -{ - assert(!mainCfgs.empty()); - if (mainCfgs.empty()) - return MainConfiguration(); - - if (mainCfgs.size() == 1) //mergeConfigFilesImpl relies on this! - return mainCfgs[0]; // - - //merge folder pair config - std::vector fpMerged; - for (const MainConfiguration& mainCfg : mainCfgs) - { - std::vector fpTmp; - - //skip empty folder pairs - if (!effectivelyEmpty(mainCfg.firstPair)) - fpTmp.push_back(mainCfg.firstPair); - for (const FolderPairEnh& fp : mainCfg.additionalPairs) - if (!effectivelyEmpty(fp)) - fpTmp.push_back(fp); - - //move all configuration down to item level - for (FolderPairEnh& fp : fpTmp) - { - if (!fp.altCmpConfig.get()) - fp.altCmpConfig = std::make_shared(mainCfg.cmpConfig); - - if (!fp.altSyncConfig.get()) - fp.altSyncConfig = std::make_shared(mainCfg.syncCfg); - - fp.localFilter = mergeFilterConfig(mainCfg.globalFilter, fp.localFilter); - } - fpMerged.insert(fpMerged.end(), fpTmp.begin(), fpTmp.end()); - } - - if (fpMerged.empty()) - return MainConfiguration(); - - //optimization: remove redundant configuration - - //######################################################################################################################## - //find out which comparison and synchronization setting are used most often and use them as new "header" - std::vector> cmpCfgStat; - std::vector> syncCfgStat; - for (const FolderPairEnh& fp : fpMerged) - { - //rather inefficient algorithm, but it does not require a less-than operator: - { - const CompConfig& cmpCfg = *fp.altCmpConfig; - - auto it = std::find_if(cmpCfgStat.begin(), cmpCfgStat.end(), - [&](const std::pair& entry) { return effectivelyEqual(entry.first, cmpCfg); }); - if (it == cmpCfgStat.end()) - cmpCfgStat.push_back(std::make_pair(cmpCfg, 1)); - else - ++(it->second); - } - { - const SyncConfig& syncCfg = *fp.altSyncConfig; - - auto it = std::find_if(syncCfgStat.begin(), syncCfgStat.end(), - [&](const std::pair& entry) { return effectivelyEqual(entry.first, syncCfg); }); - if (it == syncCfgStat.end()) - syncCfgStat.push_back(std::make_pair(syncCfg, 1)); - else - ++(it->second); - } - } - - //set most-used comparison and synchronization settings as new header options - const CompConfig cmpCfgHead = cmpCfgStat.empty() ? CompConfig() : - std::max_element(cmpCfgStat.begin(), cmpCfgStat.end(), - [](const std::pair& lhs, const std::pair& rhs) { return lhs.second < rhs.second; })->first; - - const SyncConfig syncCfgHead = syncCfgStat.empty() ? SyncConfig() : - std::max_element(syncCfgStat.begin(), syncCfgStat.end(), - [](const std::pair& lhs, const std::pair& rhs) { return lhs.second < rhs.second; })->first; - //######################################################################################################################## - - FilterConfig globalFilter; - const bool allFiltersEqual = std::all_of(fpMerged.begin(), fpMerged.end(), [&](const FolderPairEnh& fp) { return fp.localFilter == fpMerged[0].localFilter; }); - if (allFiltersEqual) - globalFilter = fpMerged[0].localFilter; - - //strip redundancy... - for (FolderPairEnh& fp : fpMerged) - { - //if local config matches output global config we don't need local one - if (fp.altCmpConfig && - effectivelyEqual(*fp.altCmpConfig, cmpCfgHead)) - fp.altCmpConfig.reset(); - - if (fp.altSyncConfig && - effectivelyEqual(*fp.altSyncConfig, syncCfgHead)) - fp.altSyncConfig.reset(); - - if (allFiltersEqual) //use global filter in this case - fp.localFilter = FilterConfig(); - } - - //final assembly - zen::MainConfiguration cfgOut; - cfgOut.cmpConfig = cmpCfgHead; - cfgOut.syncCfg = syncCfgHead; - cfgOut.globalFilter = globalFilter; - cfgOut.firstPair = fpMerged[0]; - cfgOut.additionalPairs.assign(fpMerged.begin() + 1, fpMerged.end()); - cfgOut.onCompletion = mainCfgs[0].onCompletion; - return cfgOut; -} diff --git a/structures.h b/structures.h deleted file mode 100644 index fdf36e09..00000000 --- a/structures.h +++ /dev/null @@ -1,406 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef FREEFILESYNC_H_INCLUDED -#define FREEFILESYNC_H_INCLUDED - -#include -#include -#include -#include -#include - -namespace zen -{ -enum CompareVariant -{ - CMP_BY_TIME_SIZE, - CMP_BY_CONTENT -}; - -std::wstring getVariantName(CompareVariant var); - -enum SymLinkHandling -{ - SYMLINK_EXCLUDE, - SYMLINK_USE_DIRECTLY, - SYMLINK_FOLLOW_LINK -}; - - -enum class SyncDirection : unsigned char //we need to save space for use in FileSystemObject! -{ - LEFT, - RIGHT, - NONE -}; - - -enum CompareFilesResult -{ - FILE_EQUAL, - FILE_LEFT_SIDE_ONLY, - FILE_RIGHT_SIDE_ONLY, - FILE_LEFT_NEWER, //CMP_BY_TIME_SIZE only! - FILE_RIGHT_NEWER, // - FILE_DIFFERENT, //CMP_BY_CONTENT only! - FILE_DIFFERENT_METADATA, //both sides equal, but different metadata only: short name case, modification time - FILE_CONFLICT -}; -//attention make sure these /|\ \|/ three enums match!!! -enum CompareDirResult -{ - DIR_EQUAL = FILE_EQUAL, - DIR_LEFT_SIDE_ONLY = FILE_LEFT_SIDE_ONLY, - DIR_RIGHT_SIDE_ONLY = FILE_RIGHT_SIDE_ONLY, - DIR_DIFFERENT_METADATA = FILE_DIFFERENT_METADATA //both sides equal, but different metadata only: short name case -}; - -enum CompareSymlinkResult -{ - SYMLINK_EQUAL = FILE_EQUAL, - SYMLINK_LEFT_SIDE_ONLY = FILE_LEFT_SIDE_ONLY, - SYMLINK_RIGHT_SIDE_ONLY = FILE_RIGHT_SIDE_ONLY, - SYMLINK_LEFT_NEWER = FILE_LEFT_NEWER, - SYMLINK_RIGHT_NEWER = FILE_RIGHT_NEWER, - SYMLINK_DIFFERENT = FILE_DIFFERENT, - SYMLINK_DIFFERENT_METADATA = FILE_DIFFERENT_METADATA, //both sides equal, but different metadata only: short name case - SYMLINK_CONFLICT = FILE_CONFLICT -}; - - -std::wstring getSymbol(CompareFilesResult cmpRes); - - -enum SyncOperation -{ - SO_CREATE_NEW_LEFT, - SO_CREATE_NEW_RIGHT, - SO_DELETE_LEFT, - SO_DELETE_RIGHT, - - SO_MOVE_LEFT_SOURCE, //SO_DELETE_LEFT - optimization! - SO_MOVE_LEFT_TARGET, //SO_CREATE_NEW_LEFT - - SO_MOVE_RIGHT_SOURCE, //SO_DELETE_RIGHT - optimization! - SO_MOVE_RIGHT_TARGET, //SO_CREATE_NEW_RIGHT - - SO_OVERWRITE_LEFT, - SO_OVERWRITE_RIGHT, - SO_COPY_METADATA_TO_LEFT, //objects are already equal: transfer metadata only - optimization - SO_COPY_METADATA_TO_RIGHT, // - - SO_DO_NOTHING, //= both sides differ, but nothing will be synced - SO_EQUAL, //= both sides are equal, so nothing will be synced - SO_UNRESOLVED_CONFLICT -}; - -std::wstring getSymbol(SyncOperation op); //method used for exporting .csv file only! - - -struct DirectionSet -{ - DirectionSet() : - exLeftSideOnly (SyncDirection::RIGHT), - exRightSideOnly(SyncDirection::LEFT), - leftNewer (SyncDirection::RIGHT), - rightNewer (SyncDirection::LEFT), - different (SyncDirection::NONE), - conflict (SyncDirection::NONE) {} - - SyncDirection exLeftSideOnly; - SyncDirection exRightSideOnly; - SyncDirection leftNewer; //CMP_BY_TIME_SIZE only! - SyncDirection rightNewer; // - SyncDirection different; //CMP_BY_CONTENT only! - SyncDirection conflict; -}; - -DirectionSet getTwoWayUpdateSet(); - -inline -bool operator==(const DirectionSet& lhs, const DirectionSet& rhs) -{ - return lhs.exLeftSideOnly == rhs.exLeftSideOnly && - lhs.exRightSideOnly == rhs.exRightSideOnly && - lhs.leftNewer == rhs.leftNewer && - lhs.rightNewer == rhs.rightNewer && - lhs.different == rhs.different && - lhs.conflict == rhs.conflict; -} - -struct DirectionConfig //technical representation of sync-config -{ - enum Variant - { - TWOWAY, //use sync-database to determine directions - MIRROR, //predefined - UPDATE, // - CUSTOM //use custom directions - }; - - DirectionConfig() : var(TWOWAY), detectMovedFiles(false) {} - - Variant var; - DirectionSet custom; //custom sync directions - bool detectMovedFiles; //dependent from Variant: e.g. always active for DirectionConfig::TWOWAY! => use functions below for evaluation! -}; - -inline -bool operator==(const DirectionConfig& lhs, const DirectionConfig& rhs) -{ - return lhs.var == rhs.var && - lhs.custom == rhs.custom && - lhs.detectMovedFiles == rhs.detectMovedFiles; - //adapt effectivelyEqual() on changes, too! -} - -bool detectMovedFilesSelectable(const DirectionConfig& cfg); -bool detectMovedFilesEnabled (const DirectionConfig& cfg); - -DirectionSet extractDirections(const DirectionConfig& cfg); //get sync directions: DON'T call for DirectionConfig::TWOWAY! - -std::wstring getVariantName(DirectionConfig::Variant var); - -inline -bool effectivelyEqual(const DirectionConfig& lhs, const DirectionConfig& rhs) -{ - return (lhs.var == DirectionConfig::TWOWAY) == (rhs.var == DirectionConfig::TWOWAY) && //either both two-way or none - (lhs.var == DirectionConfig::TWOWAY || extractDirections(lhs) == extractDirections(rhs)) && - detectMovedFilesEnabled(lhs) == detectMovedFilesEnabled(rhs); -} - - -struct CompConfig -{ - CompConfig() : - compareVar(CMP_BY_TIME_SIZE), - handleSymlinks(SYMLINK_EXCLUDE) {} - - CompareVariant compareVar; - SymLinkHandling handleSymlinks; -}; - -inline -bool operator==(const CompConfig& lhs, const CompConfig& rhs) -{ - return lhs.compareVar == rhs.compareVar && - lhs.handleSymlinks == rhs.handleSymlinks; -} - -inline -bool effectivelyEqual(const CompConfig& lhs, const CompConfig& rhs) { return lhs == rhs; } //no change in behavior - - -enum DeletionPolicy -{ - DELETE_PERMANENTLY, - DELETE_TO_RECYCLER, - DELETE_TO_VERSIONING -}; - -enum VersioningStyle -{ - VER_STYLE_REPLACE, - VER_STYLE_ADD_TIMESTAMP, -}; - -struct SyncConfig -{ - SyncConfig() : - handleDeletion(DELETE_TO_RECYCLER), - versioningStyle(VER_STYLE_REPLACE) {} - - //sync direction settings - DirectionConfig directionCfg; - - DeletionPolicy handleDeletion; //use Recycle, delete permanently or move to user-defined location - //versioning options - VersioningStyle versioningStyle; - Zstring versioningDirectory; - //int versionCountLimit; //max versions per file (DELETE_TO_VERSIONING); < 0 := no limit -}; - - -inline -bool operator==(const SyncConfig& lhs, const SyncConfig& rhs) -{ - return lhs.directionCfg == rhs.directionCfg && - lhs.handleDeletion == rhs.handleDeletion && - lhs.versioningStyle == rhs.versioningStyle && - lhs.versioningDirectory == rhs.versioningDirectory; - //adapt effectivelyEqual() on changes, too! -} - - -inline -bool effectivelyEqual(const SyncConfig& lhs, const SyncConfig& rhs) -{ - return effectivelyEqual(lhs.directionCfg, rhs.directionCfg) && - lhs.handleDeletion == rhs.handleDeletion && - (lhs.handleDeletion != DELETE_TO_VERSIONING || //only compare deletion directory if required! - (lhs.versioningStyle == rhs.versioningStyle && - lhs.versioningDirectory == rhs.versioningDirectory)); -} - - -enum UnitSize -{ - USIZE_NONE, - USIZE_BYTE, - USIZE_KB, - USIZE_MB -}; - -enum UnitTime -{ - UTIME_NONE, - UTIME_TODAY, - // UTIME_THIS_WEEK, - UTIME_THIS_MONTH, - UTIME_THIS_YEAR, - UTIME_LAST_X_DAYS -}; - -struct FilterConfig -{ - FilterConfig(const Zstring& include = Zstr("*"), - const Zstring& exclude = Zstring(), - size_t timeSpanIn = 0, - UnitTime unitTimeSpanIn = UTIME_NONE, - size_t sizeMinIn = 0, - UnitSize unitSizeMinIn = USIZE_NONE, - size_t sizeMaxIn = 0, - UnitSize unitSizeMaxIn = USIZE_NONE) : - includeFilter(include), - excludeFilter(exclude), - timeSpan (timeSpanIn), - unitTimeSpan (unitTimeSpanIn), - sizeMin (sizeMinIn), - unitSizeMin (unitSizeMinIn), - sizeMax (sizeMaxIn), - unitSizeMax (unitSizeMaxIn) {} - - /* - Semantics of HardFilter: - 1. using it creates a NEW folder hierarchy! -> must be considered by -mode! (fortunately it turns out, doing nothing already has perfect semantics :) - 2. it applies equally to both sides => it always matches either both sides or none! => can be used while traversing a single folder! - */ - Zstring includeFilter; - Zstring excludeFilter; - - /* - Semantics of SoftFilter: - 1. It potentially may match only one side => it MUST NOT be applied while traversing a single folder to avoid mismatches - 2. => it is applied after traversing and just marks rows, (NO deletions after comparison are allowed) - 3. => equivalent to a user temporarily (de-)selecting rows -> not relevant for -mode! ;) - */ - size_t timeSpan; - UnitTime unitTimeSpan; - - size_t sizeMin; - UnitSize unitSizeMin; - - size_t sizeMax; - UnitSize unitSizeMax; -}; - -inline -bool operator==(const FilterConfig& lhs, const FilterConfig& rhs) -{ - return lhs.includeFilter == rhs.includeFilter && - lhs.excludeFilter == rhs.excludeFilter && - lhs.timeSpan == rhs.timeSpan && - lhs.unitTimeSpan == rhs.unitTimeSpan && - lhs.sizeMin == rhs.sizeMin && - lhs.unitSizeMin == rhs.unitSizeMin && - lhs.sizeMax == rhs.sizeMax && - lhs.unitSizeMax == rhs.unitSizeMax; -} - -void resolveUnits(size_t timeSpan, UnitTime unitTimeSpan, - size_t sizeMin, UnitSize unitSizeMin, - size_t sizeMax, UnitSize unitSizeMax, - zen::Int64& timeFrom, //unit: UTC time, seconds - zen::UInt64& sizeMinBy, //unit: bytes - zen::UInt64& sizeMaxBy); //unit: bytes - - -struct FolderPairEnh //enhanced folder pairs with (optional) alternate configuration -{ - FolderPairEnh() {} - - FolderPairEnh(const Zstring& leftDir, - const Zstring& rightDir, - const std::shared_ptr& cmpConfig, - const std::shared_ptr& syncConfig, - const FilterConfig& filter) : - leftDirectory(leftDir), - rightDirectory(rightDir) , - altCmpConfig(cmpConfig), - altSyncConfig(syncConfig), - localFilter(filter) {} - - Zstring leftDirectory; - Zstring rightDirectory; - - std::shared_ptr altCmpConfig; //optional - std::shared_ptr altSyncConfig; // - FilterConfig localFilter; -}; - - -inline -bool operator==(const FolderPairEnh& lhs, const FolderPairEnh& rhs) -{ - return lhs.leftDirectory == rhs.leftDirectory && - lhs.rightDirectory == rhs.rightDirectory && - - (lhs.altCmpConfig.get() && rhs.altCmpConfig.get() ? - *lhs.altCmpConfig == *rhs.altCmpConfig : - lhs.altCmpConfig.get() == rhs.altCmpConfig.get()) && - - (lhs.altSyncConfig.get() && rhs.altSyncConfig.get() ? - *lhs.altSyncConfig == *rhs.altSyncConfig : - lhs.altSyncConfig.get() == rhs.altSyncConfig.get()) && - - lhs.localFilter == rhs.localFilter; -} - - -struct MainConfiguration -{ - CompConfig cmpConfig; //global compare settings: may be overwritten by folder pair settings - SyncConfig syncCfg; //global synchronisation settings: may be overwritten by folder pair settings - FilterConfig globalFilter; //global filter settings: combined with folder pair settings - - FolderPairEnh firstPair; //there needs to be at least one pair! - std::vector additionalPairs; - - std::wstring onCompletion; //user-defined command line - - std::wstring getCompVariantName() const; - std::wstring getSyncVariantName() const; -}; - - -inline -bool operator==(const MainConfiguration& lhs, const MainConfiguration& rhs) -{ - return lhs.cmpConfig == rhs.cmpConfig && - lhs.syncCfg == rhs.syncCfg && - lhs.globalFilter == rhs.globalFilter && - lhs.firstPair == rhs.firstPair && - lhs.additionalPairs == rhs.additionalPairs && - lhs.onCompletion == rhs.onCompletion; -} - - -//facilitate drag & drop config merge: -MainConfiguration merge(const std::vector& mainCfgs); -} - -#endif // FREEFILESYNC_H_INCLUDED diff --git a/synchronization.cpp b/synchronization.cpp deleted file mode 100644 index f5785b1a..00000000 --- a/synchronization.cpp +++ /dev/null @@ -1,2536 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "synchronization.h" -#include -#include -#include -#include //get rid!? -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "lib/resolve_path.h" -#include "lib/db_file.h" -#include "lib/dir_exist_async.h" -#include "lib/cmp_filetime.h" -#include "lib/status_handler_impl.h" -#include "lib/versioning.h" - -#ifdef ZEN_WIN -#include -#include -#include "lib/shadow.h" -#endif - -using namespace zen; - - -namespace -{ -inline -int getCUD(const SyncStatistics& stat) -{ - return stat.getCreate() + - stat.getUpdate() + - stat.getDelete(); -} -} - -void SyncStatistics::init() -{ - createLeft = 0; - createRight = 0; - updateLeft = 0; - updateRight = 0; - deleteLeft = 0; - deleteRight = 0; - rowsTotal = 0; -} - - -SyncStatistics::SyncStatistics(const FolderComparison& folderCmp) -{ - init(); - std::for_each(begin(folderCmp), end(folderCmp), [&](const BaseDirPair& baseDirObj) { recurse(baseDirObj); }); -} - - -SyncStatistics::SyncStatistics(const HierarchyObject& hierObj) -{ - init(); - recurse(hierObj); -} - - -SyncStatistics::SyncStatistics(const FilePair& fileObj) -{ - init(); - processFile(fileObj); - rowsTotal += 1; -} - - -inline -void SyncStatistics::recurse(const HierarchyObject& hierObj) -{ - for (const FilePair& fileObj : hierObj.refSubFiles()) - processFile(fileObj); - for (const SymlinkPair& linkObj : hierObj.refSubLinks()) - processLink(linkObj); - for (const DirPair& dirObj : hierObj.refSubDirs()) - processDir(dirObj); - - rowsTotal += hierObj.refSubDirs(). size(); - rowsTotal += hierObj.refSubFiles().size(); - rowsTotal += hierObj.refSubLinks().size(); -} - - -inline -void SyncStatistics::processFile(const FilePair& fileObj) -{ - switch (fileObj.getSyncOperation()) //evaluate comparison result and sync direction - { - case SO_CREATE_NEW_LEFT: - ++createLeft; - dataToProcess += to(fileObj.getFileSize()); - break; - - case SO_CREATE_NEW_RIGHT: - ++createRight; - dataToProcess += to(fileObj.getFileSize()); - break; - - case SO_DELETE_LEFT: - ++deleteLeft; - break; - - case SO_DELETE_RIGHT: - ++deleteRight; - break; - - case SO_MOVE_LEFT_TARGET: - ++updateLeft; - break; - - case SO_MOVE_RIGHT_TARGET: - ++updateRight; - break; - - case SO_MOVE_LEFT_SOURCE: //ignore; already counted - case SO_MOVE_RIGHT_SOURCE: // - break; - - case SO_OVERWRITE_LEFT: - ++updateLeft; - dataToProcess += to(fileObj.getFileSize()); - break; - - case SO_OVERWRITE_RIGHT: - ++updateRight; - dataToProcess += to(fileObj.getFileSize()); - break; - - case SO_UNRESOLVED_CONFLICT: - conflictMsgs.push_back(std::make_pair(fileObj.getObjRelativeName(), fileObj.getSyncOpConflict())); - break; - - case SO_COPY_METADATA_TO_LEFT: - ++updateLeft; - break; - - case SO_COPY_METADATA_TO_RIGHT: - ++updateRight; - break; - - case SO_DO_NOTHING: - case SO_EQUAL: - break; - } -} - - -inline -void SyncStatistics::processLink(const SymlinkPair& linkObj) -{ - switch (linkObj.getSyncOperation()) //evaluate comparison result and sync direction - { - case SO_CREATE_NEW_LEFT: - ++createLeft; - break; - - case SO_CREATE_NEW_RIGHT: - ++createRight; - break; - - case SO_DELETE_LEFT: - ++deleteLeft; - break; - - case SO_DELETE_RIGHT: - ++deleteRight; - break; - - case SO_OVERWRITE_LEFT: - case SO_COPY_METADATA_TO_LEFT: - ++updateLeft; - break; - - case SO_OVERWRITE_RIGHT: - case SO_COPY_METADATA_TO_RIGHT: - ++updateRight; - break; - - case SO_UNRESOLVED_CONFLICT: - conflictMsgs.push_back(std::make_pair(linkObj.getObjRelativeName(), linkObj.getSyncOpConflict())); - break; - - case SO_MOVE_LEFT_SOURCE: - case SO_MOVE_RIGHT_SOURCE: - case SO_MOVE_LEFT_TARGET: - case SO_MOVE_RIGHT_TARGET: - assert(false); - case SO_DO_NOTHING: - case SO_EQUAL: - break; - } -} - - -inline -void SyncStatistics::processDir(const DirPair& dirObj) -{ - switch (dirObj.getSyncOperation()) //evaluate comparison result and sync direction - { - case SO_CREATE_NEW_LEFT: - ++createLeft; - break; - - case SO_CREATE_NEW_RIGHT: - ++createRight; - break; - - case SO_DELETE_LEFT: //if deletion variant == user-defined directory existing on other volume, this results in a full copy + delete operation! - ++deleteLeft; //however we cannot (reliably) anticipate this situation, fortunately statistics can be adapted during sync! - break; - - case SO_DELETE_RIGHT: - ++deleteRight; - break; - - case SO_UNRESOLVED_CONFLICT: - conflictMsgs.push_back(std::make_pair(dirObj.getObjRelativeName(), dirObj.getSyncOpConflict())); - break; - - case SO_COPY_METADATA_TO_LEFT: - ++updateLeft; - break; - - case SO_COPY_METADATA_TO_RIGHT: - ++updateRight; - break; - - case SO_OVERWRITE_LEFT: - case SO_OVERWRITE_RIGHT: - case SO_MOVE_LEFT_SOURCE: - case SO_MOVE_RIGHT_SOURCE: - case SO_MOVE_LEFT_TARGET: - case SO_MOVE_RIGHT_TARGET: - assert(false); - case SO_DO_NOTHING: - case SO_EQUAL: - break; - } - - recurse(dirObj); //since we model logical stats, we recurse, even if deletion variant is "recycler" or "versioning + same volume", which is a single physical operation! -} - -//----------------------------------------------------------------------------------------------------------- - -std::vector zen::extractSyncCfg(const MainConfiguration& mainCfg) -{ - //merge first and additional pairs - std::vector allPairs; - allPairs.push_back(mainCfg.firstPair); - allPairs.insert(allPairs.end(), - mainCfg.additionalPairs.begin(), //add additional pairs - mainCfg.additionalPairs.end()); - - std::vector output; - - //process all pairs - for (const FolderPairEnh& fp : allPairs) - { - SyncConfig syncCfg = fp.altSyncConfig.get() ? *fp.altSyncConfig : mainCfg.syncCfg; - - output.push_back( - FolderPairSyncCfg(syncCfg.directionCfg.var == DirectionConfig::TWOWAY || detectMovedFilesEnabled(syncCfg.directionCfg), - syncCfg.handleDeletion, - syncCfg.versioningStyle, - getFormattedDirectoryName(syncCfg.versioningDirectory))); - } - return output; -} - -//------------------------------------------------------------------------------------------------------------ - -//test if user accidentally selected the wrong folders to sync -bool significantDifferenceDetected(const SyncStatistics& folderPairStat) -{ - //initial file copying shall not be detected as major difference - if ((folderPairStat.getCreate() == 0 || - folderPairStat.getCreate() == 0) && - folderPairStat.getUpdate () == 0 && - folderPairStat.getDelete () == 0 && - folderPairStat.getConflict() == 0) - return false; - - const int nonMatchingRows = folderPairStat.getCreate() + - folderPairStat.getDelete(); - //folderPairStat.getUpdate() + -> not relevant when testing for "wrong folder selected" - //folderPairStat.getConflict(); - - return nonMatchingRows >= 10 && nonMatchingRows > 0.5 * folderPairStat.getRowCount(); -} - -//################################################################################################################# - -class DeletionHandling //abstract deletion variants: permanently, recycle bin, user-defined directory -{ -public: - DeletionHandling(DeletionPolicy handleDel, //nothrow! - const Zstring& versioningDir, - VersioningStyle versioningStyle, - const TimeComp& timeStamp, - const Zstring& baseDirPf, //with separator postfix - ProcessCallback& procCallback); - ~DeletionHandling() - { - //always (try to) clean up, even if synchronization is aborted! - try - { - tryCleanup(false); //throw FileError, (throw X) - } - catch (FileError&) {} - catch (...) { assert(false); } //what is this? - /* - may block heavily, but still do not allow user callback: - -> avoid throwing user cancel exception again, leading to incomplete clean-up! - */ - } - - //clean-up temporary directory (recycle bin optimization) - void tryCleanup(bool allowUserCallback = true); //throw FileError; throw X -> call this in non-exceptional coding, i.e. somewhere after sync! - - template void removeFileUpdating(const Zstring& fullName, const Zstring& relativeName, const Int64& bytesExpected, Function notifyItemDeletion); //throw FileError - template void removeDirUpdating (const Zstring& fullName, const Zstring& relativeName, const Int64& bytesExpected, Function notifyItemDeletion); //reports ONLY data delta via updateProcessedData()! - template void removeLinkUpdating(const Zstring& fullName, const Zstring& relativeName, const Int64& bytesExpected, Function notifyItemDeletion); // - - const std::wstring& getTxtRemovingFile () const { return txtRemovingFile; } // - const std::wstring& getTxtRemovingSymLink() const { return txtRemovingSymlink; } //buffered status texts - const std::wstring& getTxtRemovingDir () const { return txtRemovingDirectory; } // - -private: - DeletionHandling(const DeletionHandling&); - DeletionHandling& operator=(const DeletionHandling&); - - FileVersioner& getOrCreateVersioner() //throw FileError! => dont create in DeletionHandling()!!! - { - if (!versioner.get()) - versioner = make_unique(versioningDir_, versioningStyle_, timeStamp_); //throw FileError - return *versioner; - }; - - ProcessCallback& procCallback_; - const Zstring baseDirPf_; //ends with path separator - const Zstring versioningDir_; - const VersioningStyle versioningStyle_; - const TimeComp timeStamp_; - -#ifdef ZEN_WIN - Zstring getOrCreateRecyclerTempDirPf(); //throw FileError - Zstring recyclerTmpDir; //temporary folder holding files/folders for *deferred* recycling - std::vector toBeRecycled; //full path of files located in temporary folder, waiting for batch-recycling -#endif - - //magage three states: allow dynamic fallback from recycler to permanent deletion - const DeletionPolicy deletionPolicy_; - std::unique_ptr versioner; //used for DELETE_TO_VERSIONING; throw FileError in constructor => create on demand! - - //buffer status texts: - std::wstring txtRemovingFile; - std::wstring txtRemovingSymlink; - std::wstring txtRemovingDirectory; -}; - - -DeletionHandling::DeletionHandling(DeletionPolicy handleDel, //nothrow! - const Zstring& versioningDir, - VersioningStyle versioningStyle, - const TimeComp& timeStamp, - const Zstring& baseDirPf, //with separator postfix - ProcessCallback& procCallback) : - procCallback_(procCallback), - baseDirPf_(baseDirPf), - versioningDir_(versioningDir), - versioningStyle_(versioningStyle), - timeStamp_(timeStamp), - deletionPolicy_(handleDel) -{ - switch (deletionPolicy_) - { - case DELETE_PERMANENTLY: - txtRemovingFile = _("Deleting file %x" ); - txtRemovingDirectory = _("Deleting folder %x" ); - txtRemovingSymlink = _("Deleting symbolic link %x"); - break; - - case DELETE_TO_RECYCLER: - txtRemovingFile = _("Moving file %x to the recycle bin" ); - txtRemovingDirectory = _("Moving folder %x to the recycle bin" ); - txtRemovingSymlink = _("Moving symbolic link %x to the recycle bin"); - break; - - case DELETE_TO_VERSIONING: - txtRemovingFile = replaceCpy(_("Moving file %x to %y" ), L"%y", fmtFileName(versioningDir_)); - txtRemovingDirectory = replaceCpy(_("Moving folder %x to %y" ), L"%y", fmtFileName(versioningDir_)); - txtRemovingSymlink = replaceCpy(_("Moving symbolic link %x to %y"), L"%y", fmtFileName(versioningDir_)); - break; - } -} - - -#ifdef ZEN_WIN -namespace -{ -class CallbackMassRecycling : public CallbackRecycling -{ -public: - CallbackMassRecycling(ProcessCallback& statusHandler) : - statusHandler_(statusHandler), - txtRecyclingFile(_("Moving file %x to the recycle bin")) {} - - //may throw: first exception is swallowed, updateStatus() is then called again where it should throw again and the exception will propagate as expected - virtual void updateStatus(const Zstring& currentItem) - { - if (!currentItem.empty()) - statusHandler_.reportStatus(replaceCpy(txtRecyclingFile, L"%x", fmtFileName(currentItem))); //throw ? - else - statusHandler_.requestUiRefresh(); //throw ? - } - -private: - ProcessCallback& statusHandler_; - const std::wstring txtRecyclingFile; -}; -} - -//create + returns temporary directory postfixed with file name separator -//to support later cleanup if automatic deletion fails for whatever reason -Zstring DeletionHandling::getOrCreateRecyclerTempDirPf() //throw FileError -{ - assert(!baseDirPf_.empty()); - if (baseDirPf_.empty()) - return Zstring(); - - if (recyclerTmpDir.empty()) - { - recyclerTmpDir = [&] - { - assert(endsWith(baseDirPf_, FILE_NAME_SEPARATOR)); - /* - -> this naming convention is too cute and confusing for end users: - - //1. generate random directory name - static std::mt19937 rng(std::time(nullptr)); //don't use std::default_random_engine and leave the choice to the STL implementer! - //- the alternative std::random_device may not always be available and can even throw an exception! - //- seed with second precision is sufficient: collisions are handled below - - const Zstring chars(Zstr("abcdefghijklmnopqrstuvwxyz") - Zstr("1234567890")); - std::uniform_int_distribution distrib(0, chars.size() - 1); //takes closed range - - auto generatePath = [&]() -> Zstring //e.g. C:\Source\3vkf74fq.ffs_tmp - { - Zstring path = baseDirPf; - for (int i = 0; i < 8; ++i) - path += chars[distrib(rng)]; - return path + TEMP_FILE_ENDING; - }; - */ - - //ensure unique ownership: - Zstring dirname = baseDirPf_ + Zstr("RecycleBin") + TEMP_FILE_ENDING; - for (int i = 1;; ++i) - try - { - makeDirectory(dirname, /*bool failIfExists*/ true); //throw FileError, ErrorTargetExisting - return dirname; - } - catch (const ErrorTargetExisting&) - { - dirname = baseDirPf_ + Zstr("RecycleBin") + Zchar('_') + numberTo(i) + TEMP_FILE_ENDING; - } - }(); - } - //assemble temporary recycle bin directory with random name and .ffs_tmp ending - return appendSeparator(recyclerTmpDir); -} -#endif - - -void DeletionHandling::tryCleanup(bool allowUserCallback) //throw FileError; throw X -{ - switch (deletionPolicy_) - { - case DELETE_PERMANENTLY: - break; - - case DELETE_TO_RECYCLER: -#ifdef ZEN_WIN - if (!toBeRecycled.empty()) - { - //move content of temporary directory to recycle bin in a single call - CallbackMassRecycling cbmr(procCallback_); - recycleOrDelete(toBeRecycled, allowUserCallback ? &cbmr : nullptr); //throw FileError - toBeRecycled.clear(); - } - - //clean up temp directory itself (should contain remnant empty directories only) - if (!recyclerTmpDir.empty()) - { - removeDirectory(recyclerTmpDir); //throw FileError - recyclerTmpDir.clear(); - } -#endif - break; - - case DELETE_TO_VERSIONING: - //if (versioner.get()) - //{ - // if (allowUserCallback) - // { - // procCallback_.reportStatus(_("Removing old versions...")); //throw ? - // versioner->limitVersions([&] { procCallback_.requestUiRefresh(); /*throw ? */ }); //throw FileError - // } - // else - // versioner->limitVersions([] {}); //throw FileError - //} - break; - } -} - - -namespace -{ -template -struct CallbackRemoveDirImpl : public CallbackRemoveDir -{ - CallbackRemoveDirImpl(ProcessCallback& statusHandler, - const DeletionHandling& delHandling, - Function notifyItemDeletion) : - statusHandler_(statusHandler), - notifyItemDeletion_(notifyItemDeletion), - txtDeletingFile (delHandling.getTxtRemovingFile()), - txtDeletingFolder(delHandling.getTxtRemovingDir ()) {} - -private: - virtual void onBeforeFileDeletion(const Zstring& filename) { notifyDeletion(txtDeletingFile, filename); } - virtual void onBeforeDirDeletion (const Zstring& dirname ) { notifyDeletion(txtDeletingFolder, dirname ); } - - void notifyDeletion(const std::wstring& statusText, const Zstring& objName) - { - notifyItemDeletion_(); //it would be more correct to report *after* work was done! - statusHandler_.reportStatus(replaceCpy(statusText, L"%x", fmtFileName(objName))); - } - - ProcessCallback& statusHandler_; - Function notifyItemDeletion_; - const std::wstring txtDeletingFile; - const std::wstring txtDeletingFolder; -}; - - -template -class CallbackMoveDirImpl : public CallbackMoveDir -{ -public: - CallbackMoveDirImpl(ProcessCallback& callback, - Int64& bytesReported, - Function notifyItemDeletion) : - callback_ (callback), - bytesReported_(bytesReported), - notifyItemDeletion_(notifyItemDeletion), - txtMovingFile (_("Moving file %x to %y")), - txtMovingFolder (_("Moving folder %x to %y")) {} - -private: - virtual void onBeforeFileMove(const Zstring& fileFrom, const Zstring& fileTo) { notifyMove(txtMovingFile, fileFrom, fileTo); } - virtual void onBeforeDirMove (const Zstring& dirFrom, const Zstring& dirTo ) { notifyMove(txtMovingFolder, dirFrom, dirTo); } - - void notifyMove(const std::wstring& statusText, const Zstring& fileFrom, const Zstring& fileTo) const - { - notifyItemDeletion_(); //it would be more correct to report *after* work was done! - callback_.reportStatus(replaceCpy(replaceCpy(statusText, L"%x", L"\n" + fmtFileName(fileFrom)), L"%y", L"\n" + fmtFileName(fileTo))); - }; - - virtual void updateStatus(Int64 bytesDelta) - { - callback_.updateProcessedData(0, bytesDelta); //throw()! -> ensure client and service provider are in sync! - bytesReported_ += bytesDelta; // - - callback_.requestUiRefresh(); //may throw - } - - ProcessCallback& callback_; - Int64& bytesReported_; - Function notifyItemDeletion_; - const std::wstring txtMovingFile; - const std::wstring txtMovingFolder; -}; -} - - -template -void DeletionHandling::removeDirUpdating(const Zstring& fullName, - const Zstring& relativeName, - const Int64& bytesExpected, Function notifyItemDeletion) //throw FileError -{ - Int64 bytesReported; - ScopeGuard guardStatistics = makeGuard([&] { procCallback_.updateTotalData(0, bytesReported); }); //error = unexpected increase of total workload - - switch (deletionPolicy_) - { - case DELETE_PERMANENTLY: - { - CallbackRemoveDirImpl remDirCallback(procCallback_, *this, notifyItemDeletion); - removeDirectory(fullName, &remDirCallback); - } - break; - - case DELETE_TO_RECYCLER: - { -#ifdef ZEN_WIN - const Zstring targetDir = getOrCreateRecyclerTempDirPf() + relativeName; //throw FileError - bool deleted = false; - - auto moveToTempDir = [&] - { - try - { - //performance optimization: Instead of moving each object into recycle bin separately, - //we rename them one by one into a temporary directory and batch-recycle this directory after sync - renameFile(fullName, targetDir); //throw FileError, ErrorDifferentVolume - this->toBeRecycled.push_back(targetDir); - deleted = true; - } - catch (ErrorDifferentVolume&) //MoveFileEx() returns ERROR_PATH_NOT_FOUND *before* considering ERROR_NOT_SAME_DEVICE! => we have to create targetDir in any case! - { - deleted = recycleOrDelete(fullName); //throw FileError - } - }; - - try - { - moveToTempDir(); //throw FileError, ErrorDifferentVolume - } - catch (FileError&) - { - if (somethingExists(fullName)) - { - const Zstring targetSuperDir = beforeLast(targetDir, FILE_NAME_SEPARATOR); //what if C:\ ? - if (!dirExists(targetSuperDir)) - { - makeDirectory(targetSuperDir); //throw FileError -> may legitimately fail on Linux if permissions are missing - moveToTempDir(); //throw FileError -> this should work now! - } - else - throw; - } - } -#elif defined ZEN_LINUX || defined ZEN_MAC - const bool deleted = recycleOrDelete(fullName); //throw FileError -#endif - if (deleted) - notifyItemDeletion(); //moving to recycler is ONE logical operation, irrespective of the number of child elements! - } - break; - - case DELETE_TO_VERSIONING: - { - CallbackMoveDirImpl callback(procCallback_, bytesReported, notifyItemDeletion); - getOrCreateVersioner().revisionDir(fullName, relativeName, callback); //throw FileError - } - break; - } - - //update statistics to consider the real amount of data - guardStatistics.dismiss(); - if (bytesReported != bytesExpected) - procCallback_.updateTotalData(0, bytesReported - bytesExpected); //noexcept! -} - - -template -void DeletionHandling::removeFileUpdating(const Zstring& fullName, - const Zstring& relativeName, - const Int64& bytesExpected, Function notifyItemDeletion) //throw FileError -{ - Int64 bytesReported; - auto guardStatistics = makeGuard([&] { procCallback_.updateTotalData(0, bytesReported); }); //error = unexpected increase of total workload - bool deleted = false; - - if (endsWith(relativeName, TEMP_FILE_ENDING)) //special rule for .ffs_tmp files: always delete permanently! - deleted = zen::removeFile(fullName); - else - switch (deletionPolicy_) - { - case DELETE_PERMANENTLY: - deleted = zen::removeFile(fullName); //[!] scope specifier resolves nameclash! - break; - - case DELETE_TO_RECYCLER: -#ifdef ZEN_WIN - { - const Zstring targetFile = getOrCreateRecyclerTempDirPf() + relativeName; //throw FileError - - auto moveToTempDir = [&] - { - try - { - //performance optimization: Instead of moving each object into recycle bin separately, - //we rename them one by one into a temporary directory and batch-recycle this directory after sync - renameFile(fullName, targetFile); //throw FileError, ErrorDifferentVolume - this->toBeRecycled.push_back(targetFile); - deleted = true; - } - catch (ErrorDifferentVolume&) //MoveFileEx() returns ERROR_PATH_NOT_FOUND *before* considering ERROR_NOT_SAME_DEVICE! => we have to create targetDir in any case! - { - deleted = recycleOrDelete(fullName); //throw FileError - } - }; - - try - { - moveToTempDir(); //throw FileError, ErrorDifferentVolume - } - catch (FileError&) - { - if (somethingExists(fullName)) - { - const Zstring targetDir = beforeLast(targetFile, FILE_NAME_SEPARATOR); - if (!dirExists(targetDir)) - { - makeDirectory(targetDir); //throw FileError -> may legitimately fail on Linux if permissions are missing - moveToTempDir(); //throw FileError -> this should work now! - } - else - throw; - } - } - } -#elif defined ZEN_LINUX || defined ZEN_MAC - deleted = recycleOrDelete(fullName); //throw FileError -#endif - break; - - case DELETE_TO_VERSIONING: - { - struct CallbackMoveFileImpl : public CallbackMoveFile - { - CallbackMoveFileImpl(ProcessCallback& callback, Int64& bytes) : callback_(callback), bytesReported_(bytes) {} - - private: - virtual void updateStatus(Int64 bytesDelta) - { - callback_.updateProcessedData(0, bytesDelta); //throw()! -> ensure client and service provider are in sync! - bytesReported_ += bytesDelta; // - - callback_.requestUiRefresh(); //may throw - } - ProcessCallback& callback_; - Int64& bytesReported_; - } cb(procCallback_, bytesReported); - - deleted = getOrCreateVersioner().revisionFile(fullName, relativeName, cb); //throw FileError - } - break; - } - if (deleted) - notifyItemDeletion(); - - //update statistics to consider the real amount of data - guardStatistics.dismiss(); - if (bytesReported != bytesExpected) - procCallback_.updateTotalData(0, bytesReported - bytesExpected); //noexcept! -} - - -template inline -void DeletionHandling::removeLinkUpdating(const Zstring& fullName, const Zstring& relativeName, const Int64& bytesExpected, Function notifyItemDeletion) //throw FileError -{ - if (dirExists(fullName)) //dir symlink - return removeDirUpdating(fullName, relativeName, bytesExpected, notifyItemDeletion); //throw FileError - else //file symlink, broken symlink - return removeFileUpdating(fullName, relativeName, bytesExpected, notifyItemDeletion); //throw FileError -} - -//------------------------------------------------------------------------------------------------------------ - -namespace -{ -/* - DELETE_PERMANENTLY: deletion frees space - DELETE_TO_RECYCLER: won't free space until recycler is full, but then frees space - DELETE_TO_VERSIONING: depends on whether versioning folder is on a different volume --> if deleted item is a followed symlink, no space is freed --> created/updated/deleted item may be on a different volume than base directory: consider symlinks, junctions! - -=> generally assume deletion frees space; may avoid false positive disk space warnings for recycler and versioning -*/ -class MinimumDiskSpaceNeeded -{ -public: - static std::pair calculate(const BaseDirPair& baseObj) - { - MinimumDiskSpaceNeeded inst; - inst.recurse(baseObj); - return std::make_pair(inst.spaceNeededLeft, inst.spaceNeededRight); - } - -private: - MinimumDiskSpaceNeeded() {} - - void recurse(const HierarchyObject& hierObj) - { - //don't process directories - - //process files - for (auto it = hierObj.refSubFiles().begin(); it != hierObj.refSubFiles().end(); ++it) - switch (it->getSyncOperation()) //evaluate comparison result and sync direction - { - case SO_CREATE_NEW_LEFT: - spaceNeededLeft += to(it->getFileSize()); - break; - - case SO_CREATE_NEW_RIGHT: - spaceNeededRight += to(it->getFileSize()); - break; - - case SO_DELETE_LEFT: - //if (freeSpaceDelLeft_) - spaceNeededLeft -= to(it->getFileSize()); - break; - - case SO_DELETE_RIGHT: - //if (freeSpaceDelRight_) - spaceNeededRight -= to(it->getFileSize()); - break; - - case SO_OVERWRITE_LEFT: - //if (freeSpaceDelLeft_) - spaceNeededLeft -= to(it->getFileSize()); - spaceNeededLeft += to(it->getFileSize()); - break; - - case SO_OVERWRITE_RIGHT: - //if (freeSpaceDelRight_) - spaceNeededRight -= to(it->getFileSize()); - spaceNeededRight += to(it->getFileSize()); - break; - - case SO_DO_NOTHING: - case SO_EQUAL: - case SO_UNRESOLVED_CONFLICT: - case SO_COPY_METADATA_TO_LEFT: - case SO_COPY_METADATA_TO_RIGHT: - case SO_MOVE_LEFT_SOURCE: - case SO_MOVE_RIGHT_SOURCE: - case SO_MOVE_LEFT_TARGET: - case SO_MOVE_RIGHT_TARGET: - break; - } - - //symbolic links - //[...] - - //recurse into sub-dirs - for (auto& subDir : hierObj.refSubDirs()) - recurse(subDir); - } - - Int64 spaceNeededLeft; - Int64 spaceNeededRight; -}; - -//---------------------------------------------------------------------------------------- - -class SynchronizeFolderPair -{ -public: - SynchronizeFolderPair(ProcessCallback& procCallback, - bool verifyCopiedFiles, - bool copyFilePermissions, - bool transactionalFileCopy, -#ifdef ZEN_WIN - shadow::ShadowCopy* shadowCopyHandler, -#endif - DeletionHandling& delHandlingLeft, - DeletionHandling& delHandlingRight) : - procCallback_(procCallback), -#ifdef ZEN_WIN - shadowCopyHandler_(shadowCopyHandler), -#endif - delHandlingLeft_(delHandlingLeft), - delHandlingRight_(delHandlingRight), - verifyCopiedFiles_(verifyCopiedFiles), - copyFilePermissions_(copyFilePermissions), - transactionalFileCopy_(transactionalFileCopy), - txtCreatingFile (_("Creating file %x" )), - txtCreatingLink (_("Creating symbolic link %x" )), - txtCreatingFolder (_("Creating folder %x" )), - txtOverwritingFile (_("Overwriting file %x" )), - txtOverwritingLink (_("Overwriting symbolic link %x")), - txtVerifying (_("Verifying file %x" )), - txtWritingAttributes(_("Updating attributes of %x" )), - txtMovingFile (_("Moving file %x to %y")) - {} - - void startSync(BaseDirPair& baseDirObj) - { - runZeroPass(baseDirObj); //first process file moves - runPass(baseDirObj); //delete files (or overwrite big ones with smaller ones) - runPass(baseDirObj); //copy rest - } - -private: - enum PassId - { - PASS_ONE, //delete files - PASS_TWO, //create, modify - PASS_NEVER //skip - }; - - static PassId getPass(const FilePair& fileObj); - static PassId getPass(const SymlinkPair& linkObj); - static PassId getPass(const DirPair& dirObj); - - template - void prepare2StepMove(FilePair& sourceObj, FilePair& targetObj); //throw FileError - bool createParentDir(FileSystemObject& fsObj); //throw FileError - template - void manageFileMove(FilePair& sourceObj, FilePair& targetObj); //throw FileError - - void runZeroPass(HierarchyObject& hierObj); - template - void runPass(HierarchyObject& hierObj); - - void synchronizeFile(FilePair& fileObj); - template void synchronizeFileInt(FilePair& fileObj, SyncOperation syncOp); - - void synchronizeLink(SymlinkPair& linkObj); - template void synchronizeLinkInt(SymlinkPair& linkObj, SyncOperation syncOp); - - void synchronizeFolder(DirPair& dirObj); - template void synchronizeFolderInt(DirPair& dirObj, SyncOperation syncOp); - - void reportStatus(const std::wstring& rawText, const Zstring& objname) const { procCallback_.reportStatus(replaceCpy(rawText, L"%x", fmtFileName(objname))); }; - void reportInfo (const std::wstring& rawText, const Zstring& objname) const { procCallback_.reportInfo (replaceCpy(rawText, L"%x", fmtFileName(objname))); }; - void reportInfo (const std::wstring& rawText, - const Zstring& objname1, - const Zstring& objname2) const - { - procCallback_.reportInfo(replaceCpy(replaceCpy(rawText, L"%x", L"\n" + fmtFileName(objname1)), L"%y", L"\n" + fmtFileName(objname2))); - }; - - template - FileAttrib copyFileUpdating(const Zstring& sourceFile, const Zstring& targetFile, const Int64& bytesExpected, Function delTargetCommand) const; //throw FileError; reports data delta via updateProcessedData() - void verifyFileCopy(const Zstring& source, const Zstring& target) const; - - template - DeletionHandling& getDelHandling(); - - ProcessCallback& procCallback_; -#ifdef ZEN_WIN - shadow::ShadowCopy* shadowCopyHandler_; //optional! -#endif - DeletionHandling& delHandlingLeft_; - DeletionHandling& delHandlingRight_; - - const bool verifyCopiedFiles_; - const bool copyFilePermissions_; - const bool transactionalFileCopy_; - - //preload status texts - const std::wstring txtCreatingFile; - const std::wstring txtCreatingLink; - const std::wstring txtCreatingFolder; - const std::wstring txtOverwritingFile; - const std::wstring txtOverwritingLink; - const std::wstring txtVerifying; - const std::wstring txtWritingAttributes; - const std::wstring txtMovingFile; -}; - -//--------------------------------------------------------------------------------------------------------------- - -template <> inline -DeletionHandling& SynchronizeFolderPair::getDelHandling() { return delHandlingLeft_; } - -template <> inline -DeletionHandling& SynchronizeFolderPair::getDelHandling() { return delHandlingRight_; } -} - -/* -__________________________ -|Move algorithm, 0th pass| --------------------------- -1. loop over hierarchy and find "move source" - -2. check whether parent directory of "move source" is going to be deleted or location of "move source" may lead to name clash with other dir/symlink - -> no: delay move until 2nd pass - -3. create move target's parent directory recursively + execute move - do we have name clash? - -> prepare a 2-step move operation: 1. move source to root and update "move target" accordingly 2. delay move until 2nd pass - -4. If any of the operations above did not succeed (even after retry), update statistics and revert to "copy + delete" - Note: first pass may delete "move source"!!! - -__________________ -|killer-scenarios| ------------------- -propagate the following move sequences: -I) a -> a/a caveat sync'ing parent directory first leads to circular dependency! - -II) a/a -> a caveat: fixing name clash will remove source! - -III) c -> d caveat: move-sequence needs to be processed in correct order! - b -> c/b - a -> b/a -*/ - -namespace -{ -template inline -bool haveNameClash(const Zstring& shortname, List& m) -{ - return std::any_of(m.begin(), m.end(), - [&](const typename List::value_type& obj) { return EqualFilename()(obj.getObjShortName(), shortname); }); -} - - -Zstring findUnusedTempName(const Zstring& filename) -{ - Zstring output = filename + zen::TEMP_FILE_ENDING; - - //ensure uniqueness (+ minor file system race condition!) - for (int i = 1; somethingExists(output); ++i) - output = filename + Zchar('_') + numberTo(i) + zen::TEMP_FILE_ENDING; - - return output; -} -} - - -template -void SynchronizeFolderPair::prepare2StepMove(FilePair& sourceObj, - FilePair& targetObj) //throw FileError -{ - const Zstring& source = sourceObj.getFullName(); - const Zstring& tmpTarget = findUnusedTempName(sourceObj.getBaseDirPf() + sourceObj.getShortName()); - //this could still lead to a name-clash in obscure cases, if some file exists on the other side with - //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 the move - - reportInfo(txtMovingFile, source, tmpTarget); - - warn_static("was wenn diff volume: symlink aliasing!") //throw FileError, ErrorDifferentVolume, ErrorTargetExisting - - renameFile(source, tmpTarget); //throw FileError - - //update file hierarchy - const FileDescriptor descrSource(sourceObj.getLastWriteTime (), - sourceObj.getFileSize (), - sourceObj.getFileId (), - sourceObj.isFollowedSymlink()); - - FilePair& tempFile = sourceObj.root().addSubFile(afterLast(tmpTarget, FILE_NAME_SEPARATOR), descrSource); - static_assert(IsSameType, HierarchyObject::SubFileVec>::value, - "ATTENTION: we're adding to the file list WHILE looping over it! This is only working because FixedList iterators are not invalidated by insertion!"); - sourceObj.removeObject(); //remove only *after* evaluating "sourceObj, side"! - - //prepare move in second pass - tempFile.setSyncDir(side == LEFT_SIDE ? SyncDirection::LEFT : SyncDirection::RIGHT); - - targetObj.setMoveRef(tempFile .getId()); - tempFile .setMoveRef(targetObj.getId()); - - //NO statistics update! - procCallback_.requestUiRefresh(); //may throw -} - - -bool SynchronizeFolderPair::createParentDir(FileSystemObject& fsObj) //throw FileError, "false" on name clash -{ - if (DirPair* parentDir = dynamic_cast(&fsObj.parent())) - { - if (!createParentDir(*parentDir)) - return false; - - //detect (and try to resolve) file type conflicts: 1. symlinks 2. files - const Zstring& shortname = parentDir->getObjShortName(); - if (haveNameClash(shortname, parentDir->parent().refSubLinks()) || - haveNameClash(shortname, parentDir->parent().refSubFiles())) - return false; - - //in this context "parentDir" cannot be scheduled for deletion since it contains a "move target"! - //note: if parentDir were deleted, we'd end up destroying "fsObj"! - assert(parentDir->getSyncOperation() != SO_DELETE_LEFT && - parentDir->getSyncOperation() != SO_DELETE_RIGHT); - - synchronizeFolder(*parentDir); //throw FileError - } - return true; -} - - -template -void SynchronizeFolderPair::manageFileMove(FilePair& sourceObj, - FilePair& targetObj) //throw FileError -{ - assert((sourceObj.getSyncOperation() == SO_MOVE_LEFT_SOURCE && targetObj.getSyncOperation() == SO_MOVE_LEFT_TARGET && side == LEFT_SIDE) || - (sourceObj.getSyncOperation() == SO_MOVE_RIGHT_SOURCE && targetObj.getSyncOperation() == SO_MOVE_RIGHT_TARGET && side == RIGHT_SIDE)); - - const bool sourceWillBeDeleted = [&]() -> bool - { - if (DirPair* parentDir = dynamic_cast(&sourceObj.parent())) - { - switch (parentDir->getSyncOperation()) //evaluate comparison result and sync direction - { - case SO_DELETE_LEFT: - case SO_DELETE_RIGHT: - return true; //we need to do something about it - case SO_MOVE_LEFT_SOURCE: - case SO_MOVE_RIGHT_SOURCE: - case SO_MOVE_LEFT_TARGET: - case SO_MOVE_RIGHT_TARGET: - case SO_OVERWRITE_LEFT: - case SO_OVERWRITE_RIGHT: - case SO_CREATE_NEW_LEFT: - case SO_CREATE_NEW_RIGHT: - case SO_DO_NOTHING: - case SO_EQUAL: - case SO_UNRESOLVED_CONFLICT: - case SO_COPY_METADATA_TO_LEFT: - case SO_COPY_METADATA_TO_RIGHT: - break; - } - } - return false; - }(); - - auto haveNameClash = [](const FilePair& fileObj) - { - return ::haveNameClash(fileObj.getObjShortName(), fileObj.parent().refSubLinks()) || - ::haveNameClash(fileObj.getObjShortName(), fileObj.parent().refSubDirs()); - }; - - if (sourceWillBeDeleted || haveNameClash(sourceObj)) - { - //prepare for move now: - revert to 2-step move on name clashes - if (haveNameClash(targetObj) || - !createParentDir(targetObj)) //throw FileError - return prepare2StepMove(sourceObj, targetObj); //throw FileError - - //finally start move! this should work now: - synchronizeFile(targetObj); //throw FileError - //SynchronizeFolderPair::synchronizeFileInt() is *not* expecting SO_MOVE_LEFT_SOURCE/SO_MOVE_RIGHT_SOURCE => start move from targetObj, not sourceObj! - } - //else: sourceObj will not be deleted, and is not standing in the way => delay to second pass - //note: this case may include new "move sources" from two-step sub-routine!!! -} - - -//search for file move-operations -void SynchronizeFolderPair::runZeroPass(HierarchyObject& hierObj) -{ - for (auto it = hierObj.refSubFiles().begin(); it != hierObj.refSubFiles().end(); ++it) //VS 2010 crashes if we use for_each + lambda here... - { - FilePair& fileObj = *it; - - const SyncOperation syncOp = fileObj.getSyncOperation(); - switch (syncOp) //evaluate comparison result and sync direction - { - case SO_MOVE_LEFT_SOURCE: - case SO_MOVE_RIGHT_SOURCE: - { - FilePair* sourceObj = &fileObj; - if (FilePair* targetObj = dynamic_cast(FileSystemObject::retrieve(fileObj.getMoveRef()))) - { - zen::Opt errMsg = tryReportingError([&] - { - if (syncOp == SO_MOVE_LEFT_SOURCE) - this->manageFileMove(*sourceObj, *targetObj); //throw FileError - else - this->manageFileMove(*sourceObj, *targetObj); // - }, procCallback_); - - if (errMsg) - { - //move operation has failed! We cannot allow to continue and have move source's parent directory deleted, messing up statistics! - // => revert to ordinary "copy + delete" - - auto getStat = [&]() -> std::pair - { - SyncStatistics statSrc(*sourceObj); - SyncStatistics statTrg(*targetObj); - - return std::make_pair(getCUD(statSrc) + getCUD(statTrg), - statSrc.getDataToProcess() + statTrg.getDataToProcess()); - }; - - const auto statBefore = getStat(); - sourceObj->setMoveRef(nullptr); - targetObj->setMoveRef(nullptr); - const auto statAfter = getStat(); - //fix statistics to total to match "copy + delete" - procCallback_.updateTotalData(statAfter.first - statBefore.first, statAfter.second - statBefore.second); - } - } - } - break; - case SO_MOVE_LEFT_TARGET: //it's enough to try each move-pair *once* - case SO_MOVE_RIGHT_TARGET: // - case SO_DELETE_LEFT: - case SO_DELETE_RIGHT: - case SO_OVERWRITE_LEFT: - case SO_OVERWRITE_RIGHT: - case SO_CREATE_NEW_LEFT: - case SO_CREATE_NEW_RIGHT: - case SO_DO_NOTHING: - case SO_EQUAL: - case SO_UNRESOLVED_CONFLICT: - case SO_COPY_METADATA_TO_LEFT: - case SO_COPY_METADATA_TO_RIGHT: - break; - } - } - - std::for_each(hierObj.refSubDirs().begin(), hierObj.refSubDirs().end(), - [&](DirPair& dirObj) { this->runZeroPass(dirObj); /*recurse */ }); -} - -//--------------------------------------------------------------------------------------------------------------- - -//1st, 2nd pass requirements: -// - avoid disk space shortage: 1. delete files, 2. overwrite big with small files first -// - support change in type: overwrite file by directory, symlink by file, ect. - -inline -SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const FilePair& fileObj) -{ - switch (fileObj.getSyncOperation()) //evaluate comparison result and sync direction - { - case SO_DELETE_LEFT: - case SO_DELETE_RIGHT: - return PASS_ONE; - - case SO_OVERWRITE_LEFT: - return fileObj.getFileSize() > fileObj.getFileSize() ? PASS_ONE : PASS_TWO; - - case SO_OVERWRITE_RIGHT: - return fileObj.getFileSize() < fileObj.getFileSize() ? PASS_ONE : PASS_TWO; - - case SO_MOVE_LEFT_SOURCE: // - case SO_MOVE_RIGHT_SOURCE: // [!] - return PASS_NEVER; - case SO_MOVE_LEFT_TARGET: // - case SO_MOVE_RIGHT_TARGET: //make sure 2-step move is processed in second pass, after move *target* parent directory was created! - return PASS_TWO; - - case SO_CREATE_NEW_LEFT: - case SO_CREATE_NEW_RIGHT: - case SO_COPY_METADATA_TO_LEFT: - case SO_COPY_METADATA_TO_RIGHT: - return PASS_TWO; - - case SO_DO_NOTHING: - case SO_EQUAL: - case SO_UNRESOLVED_CONFLICT: - return PASS_NEVER; - } - assert(false); - return PASS_TWO; //dummy -} - - -inline -SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const SymlinkPair& linkObj) -{ - switch (linkObj.getSyncOperation()) //evaluate comparison result and sync direction - { - case SO_DELETE_LEFT: - case SO_DELETE_RIGHT: - return PASS_ONE; //make sure to delete symlinks in first pass, and equally named file or dir in second pass: usecase "overwrite symlink with regular file"! - - case SO_OVERWRITE_LEFT: - case SO_OVERWRITE_RIGHT: - case SO_CREATE_NEW_LEFT: - case SO_CREATE_NEW_RIGHT: - case SO_COPY_METADATA_TO_LEFT: - case SO_COPY_METADATA_TO_RIGHT: - return PASS_TWO; - - case SO_MOVE_LEFT_SOURCE: - case SO_MOVE_RIGHT_SOURCE: - case SO_MOVE_LEFT_TARGET: - case SO_MOVE_RIGHT_TARGET: - assert(false); - case SO_DO_NOTHING: - case SO_EQUAL: - case SO_UNRESOLVED_CONFLICT: - return PASS_NEVER; - } - assert(false); - return PASS_TWO; //dummy -} - - -inline -SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const DirPair& dirObj) -{ - switch (dirObj.getSyncOperation()) //evaluate comparison result and sync direction - { - case SO_DELETE_LEFT: - case SO_DELETE_RIGHT: - return PASS_ONE; - - case SO_CREATE_NEW_LEFT: - case SO_CREATE_NEW_RIGHT: - case SO_COPY_METADATA_TO_LEFT: - case SO_COPY_METADATA_TO_RIGHT: - return PASS_TWO; - - case SO_OVERWRITE_LEFT: - case SO_OVERWRITE_RIGHT: - case SO_MOVE_LEFT_SOURCE: - case SO_MOVE_RIGHT_SOURCE: - case SO_MOVE_LEFT_TARGET: - case SO_MOVE_RIGHT_TARGET: - assert(false); - case SO_DO_NOTHING: - case SO_EQUAL: - case SO_UNRESOLVED_CONFLICT: - return PASS_NEVER; - } - assert(false); - return PASS_TWO; //dummy -} - - -template -void SynchronizeFolderPair::runPass(HierarchyObject& hierObj) -{ - //synchronize files: - for (FilePair& fileObj : hierObj.refSubFiles()) - if (pass == this->getPass(fileObj)) //"this->" required by two-pass lookup as enforced by GCC 4.7 - tryReportingError([&] { synchronizeFile(fileObj); }, procCallback_); - - //synchronize symbolic links: - for (SymlinkPair& linkObj : hierObj.refSubLinks()) - if (pass == this->getPass(linkObj)) - tryReportingError([&] { synchronizeLink(linkObj); }, procCallback_); - - //synchronize folders: - for (DirPair& dirObj : hierObj.refSubDirs()) - { - if (pass == this->getPass(dirObj)) - tryReportingError([&] { synchronizeFolder(dirObj); }, procCallback_); - - this->runPass(dirObj); //recurse - } -} - -//--------------------------------------------------------------------------------------------------------------- - -namespace -{ -inline -Opt getTargetDirection(SyncOperation syncOp) -{ - switch (syncOp) - { - case SO_CREATE_NEW_LEFT: - case SO_DELETE_LEFT: - case SO_OVERWRITE_LEFT: - case SO_COPY_METADATA_TO_LEFT: - case SO_MOVE_LEFT_SOURCE: - case SO_MOVE_LEFT_TARGET: - return LEFT_SIDE; - - case SO_CREATE_NEW_RIGHT: - case SO_DELETE_RIGHT: - case SO_OVERWRITE_RIGHT: - case SO_COPY_METADATA_TO_RIGHT: - case SO_MOVE_RIGHT_SOURCE: - case SO_MOVE_RIGHT_TARGET: - return RIGHT_SIDE; - - case SO_DO_NOTHING: - case SO_EQUAL: - case SO_UNRESOLVED_CONFLICT: - break; //nothing to do - } - return NoValue(); -} -} - - -inline -void SynchronizeFolderPair::synchronizeFile(FilePair& fileObj) -{ - const SyncOperation syncOp = fileObj.getSyncOperation(); - - if (Opt sideTrg = getTargetDirection(syncOp)) - { - if (*sideTrg == LEFT_SIDE) - synchronizeFileInt(fileObj, syncOp); - else - synchronizeFileInt(fileObj, syncOp); - } -} - - -template -void SynchronizeFolderPair::synchronizeFileInt(FilePair& fileObj, SyncOperation syncOp) -{ - static const SelectedSide sideSrc = OtherSide::result; - - switch (syncOp) - { - case SO_CREATE_NEW_LEFT: - case SO_CREATE_NEW_RIGHT: - { - if (const DirPair* parentDir = dynamic_cast(&fileObj.parent())) - if (parentDir->isEmpty()) //BaseDirPair OTOH is always non-empty and existing in this context => else: fatal error in zen::synchronize() - return; //if parent directory creation failed, there's no reason to show more errors! - - const Zstring& target = fileObj.getBaseDirPf() + fileObj.getRelativeName(); //can't use "getFullName" as target is not yet existing - reportInfo(txtCreatingFile, target); - - try - { - const FileAttrib newAttr = copyFileUpdating(fileObj.getFullName(), - target, - to(fileObj.getFileSize()), [] {} /*no target to delete*/); //throw FileError - //update FilePair - fileObj.setSyncedTo(fileObj.getShortName(), newAttr.fileSize, - newAttr.modificationTime, //target time set from source - newAttr.modificationTime, - newAttr.targetFileId, - newAttr.sourceFileId, - false, fileObj.isFollowedSymlink()); - - procCallback_.updateProcessedData(1, 0); //processed bytes are reported in copyFileUpdating()! - } - catch (FileError&) - { - if (somethingExists(fileObj.getFullName())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should error out! - throw; - //source deleted meanwhile...nothing was done (logical point of view!) - procCallback_.updateTotalData(-1, -to(fileObj.getFileSize())); - fileObj.removeObject(); //remove only *after* evaluating "fileObj, sideSrc"! - } - } - break; - - case SO_DELETE_LEFT: - case SO_DELETE_RIGHT: - reportInfo(getDelHandling().getTxtRemovingFile(), fileObj.getFullName()); - { - int objectsReported = 0; - auto guardStatistics = makeGuard([&] { procCallback_.updateTotalData(objectsReported, 0); }); //error = unexpected increase of total workload - const int objectsExpected = 1; - const Int64 bytesExpected = 0; - - getDelHandling().removeFileUpdating(fileObj.getFullName(), fileObj.getObjRelativeName(), bytesExpected, [&] //throw FileError - { - procCallback_.updateProcessedData(1, 0); //noexcept - ++objectsReported; - }); - - guardStatistics.dismiss(); //update statistics to consider the real amount of data - if (objectsReported != objectsExpected) - procCallback_.updateTotalData(objectsReported - objectsExpected, 0); //noexcept! - } - fileObj.removeObject(); //update FilePair - break; - - case SO_MOVE_LEFT_TARGET: - case SO_MOVE_RIGHT_TARGET: - if (FilePair* moveSource = dynamic_cast(FileSystemObject::retrieve(fileObj.getMoveRef()))) - { - FilePair* moveTarget = &fileObj; - - assert((moveSource->getSyncOperation() == SO_MOVE_LEFT_SOURCE && moveTarget->getSyncOperation() == SO_MOVE_LEFT_TARGET && sideTrg == LEFT_SIDE) || - (moveSource->getSyncOperation() == SO_MOVE_RIGHT_SOURCE && moveTarget->getSyncOperation() == SO_MOVE_RIGHT_TARGET && sideTrg == RIGHT_SIDE)); - - const Zstring& oldName = moveSource->getFullName(); - const Zstring& newName = moveSource->getBaseDirPf() + moveTarget->getRelativeName(); - - reportInfo(txtMovingFile, oldName, newName); - warn_static("was wenn diff volume: symlink aliasing!") //throw FileError, ErrorDifferentVolume, ErrorTargetExisting - renameFile(oldName, newName); //throw FileError - - //update FilePair - assert(moveSource->getFileSize() == moveTarget->getFileSize()); - moveTarget->setSyncedTo(moveTarget->getShortName(), moveTarget->getFileSize(), - moveSource->getLastWriteTime(), //awkward naming! moveSource is renamed on "sideTrg" side! - moveTarget->getLastWriteTime(), - moveSource->getFileId(), - moveTarget->getFileId(), - moveSource->isFollowedSymlink(), - moveTarget->isFollowedSymlink()); - moveSource->removeObject(); //remove only *after* evaluating "moveSource, sideTrg"! - - procCallback_.updateProcessedData(1, 0); - } - else (assert(false)); - break; - - case SO_OVERWRITE_LEFT: - case SO_OVERWRITE_RIGHT: - { - const Zstring targetFile = fileObj.isFollowedSymlink() ? //follow link when updating file rather than delete it and replace with regular file!!! - zen::getResolvedFilePath(fileObj.getFullName()) : //throw FileError - fileObj.getBaseDirPf() + fileObj.getRelativeName(); //respect differences in case of source object - - reportInfo(txtOverwritingFile, targetFile); - - if (fileObj.isFollowedSymlink()) //since we follow the link, we need to handle case sensitivity of the link manually! - if (fileObj.getShortName() != fileObj.getShortName()) //adapt difference in case (windows only) - renameFile(fileObj.getFullName(), - beforeLast(fileObj.getFullName(), FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + fileObj.getShortName()); //throw FileError - - const FileAttrib newAttr = copyFileUpdating(fileObj.getFullName(), - targetFile, - to(fileObj.getFileSize()), - [&] //delete target at appropriate time - { - reportStatus(this->getDelHandling().getTxtRemovingFile(), targetFile); - - this->getDelHandling().removeFileUpdating(targetFile, fileObj.getObjRelativeName(), 0, [] {}); //throw FileError; - //no (logical) item count update desired - but total byte count may change, e.g. move(copy) deleted file to versioning dir - - //fileObj.removeObject(); -> doesn't make sense for isFollowedSymlink(); "fileObj, sideTrg" evaluated below! - - //if fail-safe file copy is active, then the next operation will be a simple "rename" - //=> don't risk reportStatus() throwing GuiAbortProcess() leaving the target deleted rather than updated! - if (!transactionalFileCopy_) - reportStatus(txtOverwritingFile, targetFile); //restore status text copy file - }); //throw FileError - - //update FilePair - fileObj.setSyncedTo(fileObj.getShortName(), newAttr.fileSize, - newAttr.modificationTime, //target time set from source - newAttr.modificationTime, - newAttr.targetFileId, - newAttr.sourceFileId, - fileObj.isFollowedSymlink(), - fileObj.isFollowedSymlink()); - - procCallback_.updateProcessedData(1, 0); //we model "delete + copy" as ONE logical operation - } - break; - - case SO_COPY_METADATA_TO_LEFT: - case SO_COPY_METADATA_TO_RIGHT: - { - //harmonize with file_hierarchy.cpp::getSyncOpDescription!! - - reportInfo(txtWritingAttributes, fileObj.getFullName()); - - if (fileObj.getShortName() != fileObj.getShortName()) //adapt difference in case (windows only) - renameFile(fileObj.getFullName(), - beforeLast(fileObj.getFullName(), FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + fileObj.getShortName()); //throw FileError - - if (!sameFileTime(fileObj.getLastWriteTime(), fileObj.getLastWriteTime(), 2)) //respect 2 second FAT/FAT32 precision - setFileTime(fileObj.getFullName(), fileObj.getLastWriteTime(), SYMLINK_FOLLOW); //throw FileError - //do NOT read *current* source file time, but use buffered value which corresponds to time of comparison! - - //-> both sides *should* be completely equal now... - assert(fileObj.getFileSize() == fileObj.getFileSize()); - fileObj.setSyncedTo(fileObj.getShortName(), fileObj.getFileSize(), - fileObj.getLastWriteTime(), //target time set from source - fileObj.getLastWriteTime(), - fileObj.getFileId (), - fileObj.getFileId (), - fileObj.isFollowedSymlink(), - fileObj.isFollowedSymlink()); - - procCallback_.updateProcessedData(1, 0); - } - break; - - case SO_MOVE_LEFT_SOURCE: - case SO_MOVE_RIGHT_SOURCE: - case SO_DO_NOTHING: - case SO_EQUAL: - case SO_UNRESOLVED_CONFLICT: - assert(false); //should have been filtered out by SynchronizeFolderPair::getPass() - return; //no update on processed data! - } - - procCallback_.requestUiRefresh(); //may throw -} - - -inline -void SynchronizeFolderPair::synchronizeLink(SymlinkPair& linkObj) -{ - const SyncOperation syncOp = linkObj.getSyncOperation(); - - if (Opt sideTrg = getTargetDirection(syncOp)) - { - if (*sideTrg == LEFT_SIDE) - synchronizeLinkInt(linkObj, syncOp); - else - synchronizeLinkInt(linkObj, syncOp); - } -} - - -template -void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& linkObj, SyncOperation syncOp) -{ - static const SelectedSide sideSrc = OtherSide::result; - - switch (syncOp) - { - case SO_CREATE_NEW_LEFT: - case SO_CREATE_NEW_RIGHT: - { - if (const DirPair* parentDir = dynamic_cast(&linkObj.parent())) - if (parentDir->isEmpty()) //BaseDirPair OTOH is always non-empty and existing in this context => else: fatal error in zen::synchronize() - return; //if parent directory creation failed, there's no reason to show more errors! - - const Zstring& target = linkObj.getBaseDirPf() + linkObj.getRelativeName(); - - reportInfo(txtCreatingLink, target); - - try - { - zen::copySymlink(linkObj.getFullName(), target, copyFilePermissions_); //throw FileError - //update SymlinkPair - linkObj.setSyncedTo(linkObj.getShortName(), - linkObj.getLastWriteTime(), //target time set from source - linkObj.getLastWriteTime()); - - procCallback_.updateProcessedData(1, 0); - } - catch (FileError&) - { - if (somethingExists(linkObj.getFullName())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should not be quiet about it! - throw; - //source deleted meanwhile...nothing was done (logical point of view!) - procCallback_.updateTotalData(-1, 0); - linkObj.removeObject(); - } - } - break; - - case SO_DELETE_LEFT: - case SO_DELETE_RIGHT: - reportInfo(getDelHandling().getTxtRemovingSymLink(), linkObj.getFullName()); - { - int objectsReported = 0; - auto guardStatistics = makeGuard([&] { procCallback_.updateTotalData(objectsReported, 0); }); //error = unexpected increase of total workload - const int objectsExpected = 1; - const Int64 bytesExpected = 0; - - getDelHandling().removeLinkUpdating(linkObj.getFullName(), linkObj.getObjRelativeName(), bytesExpected, [&] //throw FileError - { - procCallback_.updateProcessedData(1, 0); //noexcept - ++objectsReported; - }); - - guardStatistics.dismiss(); //update statistics to consider the real amount of data - if (objectsReported != objectsExpected) - procCallback_.updateTotalData(objectsReported - objectsExpected, 0); //noexcept! - } - linkObj.removeObject(); //update SymlinkPair - break; - - case SO_OVERWRITE_LEFT: - case SO_OVERWRITE_RIGHT: - reportInfo(txtOverwritingLink, linkObj.getFullName()); - - //reportStatus(getDelHandling().getTxtRemovingSymLink(), linkObj.getFullName()); - getDelHandling().removeLinkUpdating(linkObj.getFullName(), linkObj.getObjRelativeName(), 0, [] {}); //throw FileError - - //linkObj.removeObject(); -> "linkObj, sideTrg" evaluated below! - - //=> don't risk reportStatus() throwing GuiAbortProcess() leaving the target deleted rather than updated: - - //reportStatus(txtOverwritingLink, linkObj.getFullName()); //restore status text - zen::copySymlink(linkObj.getFullName(), - linkObj.getBaseDirPf() + linkObj.getRelativeName(), //respect differences in case of source object - copyFilePermissions_); //throw FileError - - //update SymlinkPair - linkObj.setSyncedTo(linkObj.getShortName(), - linkObj.getLastWriteTime(), //target time set from source - linkObj.getLastWriteTime()); - - procCallback_.updateProcessedData(1, 0); //we model "delete + copy" as ONE logical operation - break; - - case SO_COPY_METADATA_TO_LEFT: - case SO_COPY_METADATA_TO_RIGHT: - reportInfo(txtWritingAttributes, linkObj.getFullName()); - - if (linkObj.getShortName() != linkObj.getShortName()) //adapt difference in case (windows only) - renameFile(linkObj.getFullName(), - beforeLast(linkObj.getFullName(), FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + linkObj.getShortName()); //throw FileError - - if (!sameFileTime(linkObj.getLastWriteTime(), linkObj.getLastWriteTime(), 2)) //respect 2 second FAT/FAT32 precision - setFileTime(linkObj.getFullName(), linkObj.getLastWriteTime(), SYMLINK_DIRECT); //throw FileError - - //-> both sides *should* be completely equal now... - linkObj.setSyncedTo(linkObj.getShortName(), - linkObj.getLastWriteTime(), //target time set from source - linkObj.getLastWriteTime()); - - procCallback_.updateProcessedData(1, 0); - break; - - case SO_MOVE_LEFT_SOURCE: - case SO_MOVE_RIGHT_SOURCE: - case SO_MOVE_LEFT_TARGET: - case SO_MOVE_RIGHT_TARGET: - case SO_DO_NOTHING: - case SO_EQUAL: - case SO_UNRESOLVED_CONFLICT: - assert(false); //should have been filtered out by SynchronizeFolderPair::getPass() - return; //no update on processed data! - } - - procCallback_.requestUiRefresh(); //may throw -} - - -inline -void SynchronizeFolderPair::synchronizeFolder(DirPair& dirObj) -{ - const SyncOperation syncOp = dirObj.getSyncOperation(); - - if (Opt sideTrg = getTargetDirection(syncOp)) - { - if (*sideTrg == LEFT_SIDE) - synchronizeFolderInt(dirObj, syncOp); - else - synchronizeFolderInt(dirObj, syncOp); - } -} - - -template -void SynchronizeFolderPair::synchronizeFolderInt(DirPair& dirObj, SyncOperation syncOp) -{ - static const SelectedSide sideSrc = OtherSide::result; - - switch (syncOp) - { - case SO_CREATE_NEW_LEFT: - case SO_CREATE_NEW_RIGHT: - if (const DirPair* parentDir = dynamic_cast(&dirObj.parent())) - if (parentDir->isEmpty()) //BaseDirPair OTOH is always non-empty and existing in this context => else: fatal error in zen::synchronize() - return; //if parent directory creation failed, there's no reason to show more errors! - - if (somethingExists(dirObj.getFullName())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should error out! - { - const Zstring& target = dirObj.getBaseDirPf() + dirObj.getRelativeName(); - - reportInfo(txtCreatingFolder, target); - try - { - makeDirectoryPlain(target, dirObj.getFullName(), copyFilePermissions_); //throw FileError, ErrorTargetExisting, (ErrorTargetPathMissing) - } - catch (const ErrorTargetExisting&) { if (!dirExists(target)) throw; } //detect clash with file (dir-symlink OTOH is okay) - - //update DirPair - dirObj.setSyncedTo(dirObj.getShortName()); - - procCallback_.updateProcessedData(1, 0); - } - else //source deleted meanwhile...nothing was done (logical point of view!) -> uh....what about a temporary network drop??? - { - const SyncStatistics subStats(dirObj); - procCallback_.updateTotalData(-getCUD(subStats) - 1, -subStats.getDataToProcess()); - - //remove only *after* evaluating dirObj!! - dirObj.refSubFiles().clear(); // - dirObj.refSubLinks().clear(); //update DirPair - dirObj.refSubDirs ().clear(); // - dirObj.removeObject(); // - } - break; - - case SO_DELETE_LEFT: - case SO_DELETE_RIGHT: - reportInfo(getDelHandling().getTxtRemovingDir(), dirObj.getFullName()); - { - int objectsReported = 0; - auto guardStatistics = makeGuard([&] { procCallback_.updateTotalData(objectsReported, 0); }); //error = unexpected increase of total workload - const SyncStatistics subStats(dirObj); //counts sub-objects only! - const int objectsExpected = 1 + getCUD(subStats); - const Int64 bytesExpected = subStats.getDataToProcess(); - assert(bytesExpected == 0); - - getDelHandling().removeDirUpdating(dirObj.getFullName(), dirObj.getObjRelativeName(), bytesExpected, [&] //throw FileError - { - procCallback_.updateProcessedData(1, 0); //noexcept - ++objectsReported; - }); - - guardStatistics.dismiss(); //update statistics to consider the real amount of data - if (objectsReported != objectsExpected) - procCallback_.updateTotalData(objectsReported - objectsExpected, 0); //noexcept! - } - dirObj.refSubFiles().clear(); // - dirObj.refSubLinks().clear(); //update DirPair - dirObj.refSubDirs ().clear(); // - dirObj.removeObject(); // - break; - - case SO_COPY_METADATA_TO_LEFT: - case SO_COPY_METADATA_TO_RIGHT: - reportInfo(txtWritingAttributes, dirObj.getFullName()); - - if (dirObj.getShortName() != dirObj.getShortName()) //adapt difference in case (windows only) - renameFile(dirObj.getFullName(), - beforeLast(dirObj.getFullName(), FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + dirObj.getShortName()); //throw FileError - //copyFileTimes -> useless: modification time changes with each child-object creation/deletion - - //-> both sides *should* be completely equal now... - dirObj.setSyncedTo(dirObj.getShortName()); - - procCallback_.updateProcessedData(1, 0); - break; - - case SO_OVERWRITE_LEFT: - case SO_OVERWRITE_RIGHT: - case SO_MOVE_LEFT_SOURCE: - case SO_MOVE_RIGHT_SOURCE: - case SO_MOVE_LEFT_TARGET: - case SO_MOVE_RIGHT_TARGET: - case SO_DO_NOTHING: - case SO_EQUAL: - case SO_UNRESOLVED_CONFLICT: - assert(false); //should have been filtered out by SynchronizeFolderPair::getPass() - return; //no update on processed data! - } - - procCallback_.requestUiRefresh(); //may throw -} - - -namespace -{ -#ifdef ZEN_WIN -//recycleBinStatus() blocks seriously if recycle bin is really full and drive is slow -StatusRecycler recycleBinStatusUpdating(const Zstring& dirname, ProcessCallback& procCallback) -{ - procCallback.reportStatus(replaceCpy(_("Checking recycle bin availability for folder %x..."), L"%x", fmtFileName(dirname), false)); - - auto ft = async([=] { return recycleBinStatus(dirname); }); - - while (!ft.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL / 2))) - procCallback.requestUiRefresh(); //may throw! - return ft.get(); -} -#endif - - -/* -struct LessDependentDirectory : public std::binary_function -{ --> a *very* bad idea: this is NOT a strict weak ordering! No transitivity of equivalence! - - bool operator()(const Zstring& lhs, const Zstring& rhs) const - { - return LessFilename()(Zstring(lhs.c_str(), std::min(lhs.length(), rhs.length())), - Zstring(rhs.c_str(), std::min(lhs.length(), rhs.length()))); - } -}; -*/ - -template //create base directories first (if not yet existing) -> no symlink or attribute copying! -bool createBaseDirectory(BaseDirPair& baseDirObj, ProcessCallback& callback) //nothrow; return false if fatal error occurred -{ - const Zstring dirname = beforeLast(baseDirObj.getBaseDirPf(), FILE_NAME_SEPARATOR); //what about C:\ ??? - if (dirname.empty()) - return true; - - if (baseDirObj.isExisting()) //atomicity: do NOT check directory existence again! - { - //just convenience: exit sync right here instead of showing tons of error messages during file copy - zen::Opt errMsg = tryReportingError([&] - { - if (!dirExistsUpdating(dirname, false, callback)) - throw FileError(replaceCpy(_("Cannot find %x."), L"%x", fmtFileName(dirname))); //should be logged as a "fatal error" if ignored by the user... - }, callback); //may throw in error-callback! - - return !errMsg; - } - else //create target directory: user presumably ignored error "dir existing" in order to have it created automatically - { - bool temporaryNetworkDrop = false; - zen::Opt errMsg = tryReportingError([&] - { - try - { - //a nice race-free check and set operation: - makeDirectory(dirname, /*bool failIfExists*/ true); //throw FileError, ErrorTargetExisting - baseDirObj.setExisting(true); //update our model! - } - catch (const ErrorTargetExisting&) - { - //TEMPORARY network drop: base directory not found during comparison, but reappears during synchronization - //=> sync-directions are based on false assumptions! Abort. - callback.reportFatalError(replaceCpy(_("Target folder %x already existing."), L"%x", fmtFileName(dirname))); - temporaryNetworkDrop = true; - - //Is it possible we're catching a "false-positive" here, could FFS have created the directory indirectly after comparison? - // 1. deletion handling: recycler -> no, temp directory created only at first deletion - // 2. deletion handling: versioning -> " - // 3. log file creates containing folder -> no, log only created in batch mode, and only *before* comparison - } - }, callback); //may throw in error-callback! - return !errMsg && !temporaryNetworkDrop; - } -} -} - - -void zen::synchronize(const TimeComp& timeStamp, - xmlAccess::OptionalDialogs& warnings, - bool verifyCopiedFiles, - bool copyLockedFiles, - bool copyFilePermissions, - bool transactionalFileCopy, - bool runWithBackgroundPriority, - const std::vector& syncConfig, - FolderComparison& folderCmp, - ProcessCallback& callback) -{ - //specify process and resource handling priorities - std::unique_ptr backgroundPrio; - if (runWithBackgroundPriority) - try - { - backgroundPrio = make_unique(); //throw FileError - } - catch (const FileError& e) - { - //not an error in this context - callback.reportInfo(e.toString()); //may throw! - } - - //prevent operating system going into sleep state - std::unique_ptr noStandby; - try - { - noStandby = make_unique(); //throw FileError - } - catch (const FileError& e) - { - //not an error in this context - callback.reportInfo(e.toString()); //may throw! - } - - //PERF_START; - - if (syncConfig.size() != folderCmp.size()) - throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo(__LINE__)); - - //inform about the total amount of data that will be processed from now on - const SyncStatistics statisticsTotal(folderCmp); - - //keep at beginning so that all gui elements are initialized properly - callback.initNewPhase(getCUD(statisticsTotal), - statisticsTotal.getDataToProcess(), - ProcessCallback::PHASE_SYNCHRONIZING); - - - std::deque skipFolderPair(folderCmp.size()); //folder pairs may be skipped after fatal errors were found - - //-------------------execute basic checks all at once before starting sync-------------------------------------- - - auto dependentDir = [](const Zstring& lhs, const Zstring& rhs) //note: this is NOT an equivalence relation! - { - return EqualFilename()(Zstring(lhs.c_str(), std::min(lhs.length(), rhs.length())), - Zstring(rhs.c_str(), std::min(lhs.length(), rhs.length()))); - }; - - //aggregate information - std::map> dirReadWriteCount; //count read/write accesses - auto incReadCount = [&](const Zstring& baseDir) - { - dirReadWriteCount[baseDir]; //create entry - for (auto it = dirReadWriteCount.begin(); it != dirReadWriteCount.end(); ++it) - { - auto& countRef = it->second; - if (dependentDir(baseDir, it->first)) - ++countRef.first; - } - }; - auto incWriteCount = [&](const Zstring& baseDir) - { - dirReadWriteCount[baseDir]; //create entry - for (auto it = dirReadWriteCount.begin(); it != dirReadWriteCount.end(); ++it) - { - auto& countRef = it->second; - if (dependentDir(baseDir, it->first)) - ++countRef.second; - } - }; - - typedef std::vector> DirPairList; - DirPairList significantDiff; - - typedef std::vector>> DirSpaceRequAvailList; //dirname / space required / space available - DirSpaceRequAvailList diskSpaceMissing; - -#ifdef ZEN_WIN - //status of base directories which are set to DELETE_TO_RECYCLER (and contain actual items to be deleted) - std::map baseDirHasRecycler; //might be expensive to determine => buffer + check recycle bin existence only once per base directory! -#endif - - //start checking folder pairs - for (auto j = begin(folderCmp); j != end(folderCmp); ++j) - { - const size_t folderIndex = j - begin(folderCmp); - - //exclude some pathological case (leftdir, rightdir are empty) - if (EqualFilename()(j->getBaseDirPf(), j->getBaseDirPf())) - continue; - - const FolderPairSyncCfg& folderPairCfg = syncConfig[folderIndex]; - - const SyncStatistics folderPairStat(*j); - - //aggregate basic information - const bool writeLeft = folderPairStat.getCreate() + - folderPairStat.getUpdate() + - folderPairStat.getDelete() > 0; - - const bool writeRight = folderPairStat.getCreate() + - folderPairStat.getUpdate() + - folderPairStat.getDelete() > 0; - - //skip folder pair if there is nothing to do (except for two-way mode and move-detection, where DB files need to be written) - if (!writeLeft && !writeRight && - !folderPairCfg.saveSyncDB_) - { - skipFolderPair[folderIndex] = true; //skip creating (not yet existing) base directories in particular if there's no need - continue; - } - - //check empty input fields: this only makes sense if empty field is source (and no DB files need to be created) - if ((j->getBaseDirPf().empty() && (writeLeft || folderPairCfg.saveSyncDB_)) || - (j->getBaseDirPf().empty() && (writeRight || folderPairCfg.saveSyncDB_))) - { - callback.reportFatalError(_("Target folder input field must not be empty.")); - skipFolderPair[folderIndex] = true; - continue; - } - - //aggregate information of folders used by multiple pairs in read/write access - if (!dependentDir(j->getBaseDirPf(), j->getBaseDirPf())) //true in general - { - if (writeLeft && writeRight) - { - incWriteCount(j->getBaseDirPf()); - incWriteCount(j->getBaseDirPf()); - } - else if (writeLeft) - { - incWriteCount(j->getBaseDirPf()); - incReadCount (j->getBaseDirPf()); - } - else if (writeRight) - { - incReadCount (j->getBaseDirPf()); - incWriteCount(j->getBaseDirPf()); - } - } - else //if folder pair contains two dependent folders, a warning was already issued after comparison; in this context treat as one write access at most - { - if (writeLeft || writeRight) - incWriteCount(j->getBaseDirPf()); - } - - - if (folderPairStat.getUpdate() + folderPairStat.getDelete() > 0 && - folderPairCfg.handleDeletion == zen::DELETE_TO_VERSIONING) - { - //check if user-defined directory for deletion was specified - if (folderPairCfg.versioningFolder.empty()) //already trimmed by getFormattedDirectoryName() - { - //should never arrive here: already checked in SyncCfgDialog - callback.reportFatalError(_("Please enter a target folder for versioning.")); - skipFolderPair[folderIndex] = true; - continue; - } - } - - //the following scenario is covered by base directory creation below in case source directory exists (accessible or not), but latter doesn't cover source created after comparison, but before sync!!! - auto checkSourceMissing = [&](const Zstring& baseDirPf, bool wasExisting) -> bool //avoid race-condition: we need to evaluate existence status from time of comparison! - { - if (!baseDirPf.empty()) - { - //PERMANENT network drop: avoid data loss when source directory is not found AND user chose to ignore errors (else we wouldn't arrive here) - if (folderPairStat.getCreate() + - folderPairStat.getUpdate() == 0 && - folderPairStat.getDelete() > 0) //deletions only... (respect filtered items!) - //folderPairStat.getConflict() == 0 && -> there COULD be conflicts for if directory existence check fails, but loading sync.ffs_db succeeds - //https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3531351&group_id=234430 -> fixed, but still better not consider conflicts - { - if (!wasExisting) //avoid race-condition: we need to evaluate existence status from time of comparison! - { - callback.reportFatalError(replaceCpy(_("Source folder %x not found."), L"%x", fmtFileName(baseDirPf))); - skipFolderPair[folderIndex] = true; - return false; - } - } - } - return true; - }; - if (!checkSourceMissing(j->getBaseDirPf(), j->isExisting()) || - !checkSourceMissing(j->getBaseDirPf(), j->isExisting())) - continue; - - //check if more than 50% of total number of files/dirs are to be created/overwritten/deleted - if (significantDifferenceDetected(folderPairStat)) - significantDiff.push_back(std::make_pair(j->getBaseDirPf(), j->getBaseDirPf())); - - //check for sufficient free diskspace - auto checkSpace = [&](const Zstring& baseDirPf, const Int64& minSpaceNeeded) - { - try - { - const Int64 freeSpace = to(getFreeDiskSpace(baseDirPf)); //throw FileError - - if (0 < freeSpace && //zero disk space probably means "request not supported" (e.g. see WebDav) - freeSpace < minSpaceNeeded) - diskSpaceMissing.push_back(std::make_pair(baseDirPf, std::make_pair(minSpaceNeeded, freeSpace))); - } - catch (FileError&) {} - }; - const std::pair spaceNeeded = MinimumDiskSpaceNeeded::calculate(*j); - checkSpace(j->getBaseDirPf(), spaceNeeded.first); - checkSpace(j->getBaseDirPf(), spaceNeeded.second); - -#ifdef ZEN_WIN - //windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong - auto checkRecycler = [&](const Zstring& baseDirPf) - { - if (!baseDirPf.empty()) //should be - if (baseDirHasRecycler.find(baseDirPf) == baseDirHasRecycler.end()) //perf: avoid duplicate checks! - baseDirHasRecycler[baseDirPf] = recycleBinStatusUpdating(baseDirPf, callback) == STATUS_REC_EXISTS; - }; - - if (folderPairCfg.handleDeletion == DELETE_TO_RECYCLER) - { - if (folderPairStat.getUpdate() + - folderPairStat.getDelete() > 0) - checkRecycler(j->getBaseDirPf()); - - if (folderPairStat.getUpdate() + - folderPairStat.getDelete() > 0) - checkRecycler(j->getBaseDirPf()); - } -#endif - } - - //check if unresolved conflicts exist - if (statisticsTotal.getConflict() > 0) - { - //show the first few conflicts in warning message also: - std::wstring msg = _("The following items have unresolved conflicts and will not be synchronized:"); - - const auto& conflictMsgs = statisticsTotal.getConflictMessages(); //get *all* sync conflicts - for (auto it = conflictMsgs.begin(); it != conflictMsgs.end(); ++it) - msg += L"\n\n" + fmtFileName(it->first) + L": " + it->second; - - callback.reportWarning(msg, warnings.warningUnresolvedConflicts); - } - - - //check if user accidentally selected wrong directories for sync - if (!significantDiff.empty()) - { - std::wstring msg = _("The following folders are significantly different. Make sure you are matching the correct folders for synchronization."); - - for (auto it = significantDiff.begin(); it != significantDiff.end(); ++it) - msg += std::wstring(L"\n\n") + - it->first + L" <-> " + L"\n" + - it->second; - - callback.reportWarning(msg, warnings.warningSignificantDifference); - } - - - //check for sufficient free diskspace - if (!diskSpaceMissing.empty()) - { - std::wstring msg = _("Not enough free disk space available in:"); - - for (auto it = diskSpaceMissing.begin(); it != diskSpaceMissing.end(); ++it) - msg += std::wstring(L"\n\n") + - it->first + L"\n" + - _("Required:") + L" " + filesizeToShortString(it->second.first) + L"\n" + - _("Available:") + L" " + filesizeToShortString(it->second.second); - - callback.reportWarning(msg, warnings.warningNotEnoughDiskSpace); - } - -#ifdef ZEN_WIN - //windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong - { - std::wstring dirListMissingRecycler; - for (auto it = baseDirHasRecycler.begin(); it != baseDirHasRecycler.end(); ++it) - if (!it->second) - dirListMissingRecycler += std::wstring(L"\n") + it->first; - - if (!dirListMissingRecycler.empty()) - callback.reportWarning(_("The recycle bin is not available for the following folders. Files will be deleted permanently instead:") + L"\n" + dirListMissingRecycler, warnings.warningRecyclerMissing); - } -#endif - - //check if folders are used by multiple pairs in read/write access - std::vector conflictDirs; - for (auto it = dirReadWriteCount.cbegin(); it != dirReadWriteCount.cend(); ++it) - { - const std::pair& countRef = it->second; //# read/write accesses - - if (countRef.first + countRef.second >= 2 && countRef.second >= 1) //race condition := multiple accesses of which at least one is a write - conflictDirs.push_back(it->first); - } - - if (!conflictDirs.empty()) - { - std::wstring msg = _("A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.") + L"\n"; - std::for_each(conflictDirs.begin(), conflictDirs.end(), - [&](const Zstring& dirname) { msg += std::wstring(L"\n") + dirname; }); - - callback.reportWarning(msg, warnings.warningFolderPairRaceCondition); - } - - //-------------------end of basic checks------------------------------------------ - -#ifdef ZEN_WIN - //shadow copy buffer: per sync-instance, not folder pair - std::unique_ptr shadowCopyHandler; - if (copyLockedFiles) - shadowCopyHandler = make_unique(); -#endif - - try - { - //loop through all directory pairs - for (auto j = begin(folderCmp); j != end(folderCmp); ++j) - { - //exclude pathological cases (e.g. leftdir, rightdir are empty) - if (EqualFilename()(j->getBaseDirPf(), j->getBaseDirPf())) - continue; - - //------------------------------------------------------------------------------------------ - callback.reportInfo(_("Synchronizing folder pair:") + L"\n" + - L" " + j->getBaseDirPf() + L"\n" + - L" " + j->getBaseDirPf()); - //------------------------------------------------------------------------------------------ - - const size_t folderIndex = j - begin(folderCmp); - const FolderPairSyncCfg& folderPairCfg = syncConfig[folderIndex]; - - if (skipFolderPair[folderIndex]) //folder pairs may be skipped after fatal errors were found - continue; - - //create base directories first (if not yet existing) -> no symlink or attribute copying! - if (!createBaseDirectory(*j, callback) || - !createBaseDirectory(*j, callback)) - continue; //skip this folder pair - - //------------------------------------------------------------------------------------------ - //execute synchronization recursively - - //update synchronization database (automatic sync only) - ScopeGuard guardUpdateDb = makeGuard([&] - { - if (folderPairCfg.saveSyncDB_) - try { zen::saveLastSynchronousState(*j); } //throw FileError - catch (FileError&) {} - }); - - //guarantee removal of invalid entries (where element on both sides is empty) - ZEN_ON_SCOPE_EXIT(BaseDirPair::removeEmpty(*j);); - - bool copyPermissionsFp = false; - tryReportingError([&] - { - copyPermissionsFp = copyFilePermissions && //copy permissions only if asked for and supported by *both* sides! - !j->getBaseDirPf().empty() && //scenario: directory selected on one side only - !j->getBaseDirPf().empty() && // - supportsPermissions(beforeLast(j->getBaseDirPf(), FILE_NAME_SEPARATOR)) && //throw FileError - supportsPermissions(beforeLast(j->getBaseDirPf(), FILE_NAME_SEPARATOR)); - }, callback); //show error dialog if necessary - - - auto getEffectiveDeletionPolicy = [&](const Zstring& baseDirPf) -> DeletionPolicy - { -#ifdef ZEN_WIN - if (folderPairCfg.handleDeletion == DELETE_TO_RECYCLER) - { - auto it = baseDirHasRecycler.find(baseDirPf); - if (it != baseDirHasRecycler.end()) - if (!it->second) - return DELETE_PERMANENTLY; //Windows' ::SHFileOperation() will do this anyway, but we have a better and faster deletion routine (e.g. on networks) - } -#endif - return folderPairCfg.handleDeletion; - }; - - - DeletionHandling delHandlerL(getEffectiveDeletionPolicy(j->getBaseDirPf()), - folderPairCfg.versioningFolder, - folderPairCfg.versioningStyle_, - timeStamp, - j->getBaseDirPf(), - callback); - - DeletionHandling delHandlerR(getEffectiveDeletionPolicy(j->getBaseDirPf()), - folderPairCfg.versioningFolder, - folderPairCfg.versioningStyle_, - timeStamp, - j->getBaseDirPf(), - callback); - - - SynchronizeFolderPair syncFP(callback, verifyCopiedFiles, copyPermissionsFp, transactionalFileCopy, -#ifdef ZEN_WIN - shadowCopyHandler.get(), -#endif - delHandlerL, delHandlerR); - syncFP.startSync(*j); - - //(try to gracefully) cleanup temporary Recycle bin folders and versioning -> will be done in ~DeletionHandling anyway... - tryReportingError([&] { delHandlerL.tryCleanup(); }, callback); //show error dialog if necessary - tryReportingError([&] { delHandlerR.tryCleanup(); }, callback); // - - //(try to gracefully) write database file - if (folderPairCfg.saveSyncDB_) - { - callback.reportStatus(_("Generating database...")); - callback.forceUiRefresh(); - - tryReportingError([&] { zen::saveLastSynchronousState(*j); }, callback); //throw FileError - guardUpdateDb.dismiss(); - } - } - } - catch (const std::exception& e) - { - callback.reportFatalError(utfCvrtTo(e.what())); - } -} - -//########################################################################################### - -template -class WhileCopying : public zen::CallbackCopyFile -{ -public: - WhileCopying(Int64& bytesReported, - ProcessCallback& statusHandler, - Function delTargetCmd) : - bytesReported_(bytesReported), - statusHandler_(statusHandler), - delTargetCmd_(std::move(delTargetCmd)) {} - - virtual void deleteTargetFile(const Zstring& targetFile) { delTargetCmd_(); } - - virtual void updateCopyStatus(Int64 bytesDelta) - { - statusHandler_.updateProcessedData(0, bytesDelta); //throw()! -> ensure client and service provider are in sync! - bytesReported_ += bytesDelta; // - - statusHandler_.requestUiRefresh(); //may throw - } - -private: - Int64& bytesReported_; - ProcessCallback& statusHandler_; - Function delTargetCmd_; -}; - - -//throw FileError; reports data delta via updateProcessedData() -template -FileAttrib SynchronizeFolderPair::copyFileUpdating(const Zstring& sourceFile, - const Zstring& targetFile, const Int64& bytesExpected, Function delTargetCommand) const //returns current attributes of source file -{ - Zstring source = sourceFile; - FileAttrib newAttr; - Int64 bytesReported; - - auto copyOperation = [&] - { - auto guardStatistics = makeGuard([&] - { - procCallback_.updateTotalData(0, bytesReported); //error = unexpected increase of total workload - bytesReported = 0; - }); - - WhileCopying callback(bytesReported, procCallback_, delTargetCommand); - - copyFile(source, //type File implicitly means symlinks need to be dereferenced! - targetFile, - copyFilePermissions_, - transactionalFileCopy_, - &callback, - &newAttr); //throw FileError, ErrorFileLocked - - //#################### Verification ############################# - if (verifyCopiedFiles_) - { - auto guardTarget = makeGuard([&] { removeFile(targetFile); }); //delete target if verification fails - verifyFileCopy(source, targetFile); //throw FileError - guardTarget.dismiss(); - } - //#################### /Verification ############################# - - //update statistics to consider the real amount of data, e.g. more than the "file size" for ADS streams, - //less for sparse and compressed files, or file changed in the meantime! - if (bytesReported != bytesExpected) - procCallback_.updateTotalData(0, bytesReported - bytesExpected); //noexcept! - - guardStatistics.dismiss(); - }; - -#ifdef ZEN_WIN - try - { - copyOperation(); - } - catch (ErrorFileLocked& e1) - { - //if file is locked (try to) use Windows Volume Shadow Copy Service - if (!shadowCopyHandler_) - throw; - try - { - //contains prefix: E.g. "\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Program Files\FFS\sample.dat" - source = shadowCopyHandler_->makeShadowCopy(source, //throw FileError - [&](const Zstring& volumeName) - { - procCallback_.reportStatus(replaceCpy(_("Creating a Volume Shadow Copy for %x..."), L"%x", fmtFileName(volumeName))); - procCallback_.forceUiRefresh(); - }); - } - catch (const FileError& e2) - { - throw FileError(e1.toString(), e2.toString()); - } - - //now try again - copyOperation(); - } -#else - copyOperation(); -#endif - - return newAttr; -} - - -//--------------------- data verification ------------------------- -struct VerifyCallback -{ - virtual ~VerifyCallback() {} - virtual void updateStatus() = 0; -}; - -void verifyFiles(const Zstring& source, const Zstring& target, VerifyCallback& callback) //throw FileError -{ - static std::vector memory1(1024 * 1024); //1024 kb seems to be a reasonable buffer size - static std::vector memory2(1024 * 1024); - -#ifdef ZEN_WIN - wxFile file1(applyLongPathPrefix(source).c_str(), wxFile::read); //don't use buffered file input for verification! -#elif defined ZEN_LINUX || defined ZEN_MAC - wxFile file1(::open(source.c_str(), O_RDONLY)); //utilize UTF-8 filename -#endif - if (!file1.IsOpened()) - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(source)) + L" (open)"); - -#ifdef ZEN_WIN - wxFile file2(applyLongPathPrefix(target).c_str(), wxFile::read); //don't use buffered file input for verification! -#elif defined ZEN_LINUX || defined ZEN_MAC - wxFile file2(::open(target.c_str(), O_RDONLY)); //utilize UTF-8 filename -#endif - if (!file2.IsOpened()) //NO cleanup necessary for (wxFile) file1 - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(target)) + L" (open)"); - - do - { - const size_t length1 = file1.Read(&memory1[0], memory1.size()); - if (file1.Error()) - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(source))); - callback.updateStatus(); - - const size_t length2 = file2.Read(&memory2[0], memory2.size()); - if (file2.Error()) - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(target))); - callback.updateStatus(); - - if (length1 != length2 || ::memcmp(&memory1[0], &memory2[0], length1) != 0) - throw FileError(replaceCpy(replaceCpy(_("Data verification error: %x and %y have different content."), L"%x", L"\n" + fmtFileName(source)), L"%y", L"\n" + fmtFileName(target))); - } - while (!file1.Eof()); - - if (!file2.Eof()) - throw FileError(replaceCpy(replaceCpy(_("Data verification error: %x and %y have different content."), L"%x", L"\n" + fmtFileName(source)), L"%y", L"\n" + fmtFileName(target))); -} - - -class VerifyStatusUpdater : public VerifyCallback -{ -public: - VerifyStatusUpdater(ProcessCallback& statusHandler) : statusHandler_(statusHandler) {} - - virtual void updateStatus() { statusHandler_.requestUiRefresh(); } //trigger display refresh - -private: - ProcessCallback& statusHandler_; -}; - - -void SynchronizeFolderPair::verifyFileCopy(const Zstring& source, const Zstring& target) const -{ - procCallback_.reportInfo(replaceCpy(txtVerifying, L"%x", fmtFileName(target))); - - VerifyStatusUpdater callback(procCallback_); - tryReportingError([&] { ::verifyFiles(source, target, callback); }, procCallback_); -} diff --git a/synchronization.h b/synchronization.h deleted file mode 100644 index bc6b79db..00000000 --- a/synchronization.h +++ /dev/null @@ -1,159 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef SYNCHRONIZATION_H_INCLUDED -#define SYNCHRONIZATION_H_INCLUDED - -#include -#include "file_hierarchy.h" -#include "lib/process_xml.h" -#include "process_callback.h" - - -namespace zen -{ -class SyncStatistics //this class counts *logical* operations, (create, update, delete + bytes), *not* disk accesses! -{ - //-> note the fundamental difference to counting disk accesses! -public: - SyncStatistics(const HierarchyObject& hierObj); - SyncStatistics(const FolderComparison& folderCmp); - SyncStatistics(const FilePair& fileObj); - - int getCreate() const; - template int getCreate() const; - - int getUpdate() const; - template int getUpdate() const; - - int getDelete() const; - template int getDelete() const; - - int getConflict() const { return static_cast(conflictMsgs.size()); } - - typedef std::vector> ConflictTexts; // Pair(filename/conflict text) - const ConflictTexts& getConflictMessages() const { return conflictMsgs; } - - Int64 getDataToProcess() const { return dataToProcess; } - size_t getRowCount() const { return rowsTotal; } - -private: - void init(); - - void recurse(const HierarchyObject& hierObj); - - void processFile(const FilePair& fileObj); - void processLink(const SymlinkPair& linkObj); - void processDir(const DirPair& dirObj); - - int createLeft, createRight; - int updateLeft, updateRight; - int deleteLeft, deleteRight; - ConflictTexts conflictMsgs; //conflict texts to display as a warning message - Int64 dataToProcess; - size_t rowsTotal; -}; - - -struct FolderPairSyncCfg -{ - FolderPairSyncCfg(bool saveSyncDB, - const DeletionPolicy handleDel, - VersioningStyle versioningStyle, - const Zstring& versioningDirFmt) : - saveSyncDB_(saveSyncDB), - handleDeletion(handleDel), - versioningStyle_(versioningStyle), - versioningFolder(versioningDirFmt) {} - - bool saveSyncDB_; //save database if in automatic mode or dection of moved files is active - DeletionPolicy handleDeletion; - VersioningStyle versioningStyle_; - Zstring versioningFolder; //formatted directory name -}; -std::vector extractSyncCfg(const MainConfiguration& mainCfg); - - -//FFS core routine: -void synchronize(const TimeComp& timeStamp, - xmlAccess::OptionalDialogs& warnings, - bool verifyCopiedFiles, - bool copyLockedFiles, - bool copyFilePermissions, - bool transactionalFileCopy, - bool runWithBackgroundPriority, - - const std::vector& syncConfig, //CONTRACT: syncConfig and folderCmp correspond row-wise! - FolderComparison& folderCmp, // - ProcessCallback& callback); - - - - - - - - - - -// ----------- implementation ---------------- -template <> inline -int SyncStatistics::getCreate() const -{ - return createLeft; -} - -template <> inline -int SyncStatistics::getCreate() const -{ - return createRight; -} - -inline -int SyncStatistics::getCreate() const -{ - return getCreate() + getCreate(); -} - -template <> inline -int SyncStatistics::getUpdate() const -{ - return updateLeft; -} - -template <> inline -int SyncStatistics::getUpdate() const -{ - return updateRight; -} - -inline -int SyncStatistics::getUpdate() const -{ - return getUpdate() + getUpdate(); -} - - -template <> inline -int SyncStatistics::getDelete() const -{ - return deleteLeft; -} - -template <> inline -int SyncStatistics::getDelete() const -{ - return deleteRight; -} - -inline -int SyncStatistics::getDelete() const -{ - return getDelete() + getDelete(); -} -} - -#endif // SYNCHRONIZATION_H_INCLUDED diff --git a/ui/IFileDialog_Vista/IFileDialog_Vista.vcxproj b/ui/IFileDialog_Vista/IFileDialog_Vista.vcxproj deleted file mode 100644 index 7f84b4fb..00000000 --- a/ui/IFileDialog_Vista/IFileDialog_Vista.vcxproj +++ /dev/null @@ -1,244 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - - - - - - - - {70394AEF-5897-4911-AFA1-82EAF0581EFA} - ShadowDll - Win32Proj - - - - DynamicLibrary - Unicode - true - v120_xp - - - DynamicLibrary - Unicode - v120_xp - - - DynamicLibrary - Unicode - true - v120_xp - - - DynamicLibrary - Unicode - v120_xp - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - false - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - false - IFileDialog_Vista_$(Platform) - IFileDialog_Vista_$(Platform) - IFileDialog_Vista_$(Platform) - IFileDialog_Vista_$(Platform) - - - - $(IntDir)Build.html - - - Disabled - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;IFILE_DIALOG_VISTA_DLL_EXPORTS;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebugDLL - - - Level4 - true - EditAndContinue - 4100 - ../.. - true - true - NoExtensions - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - $(IntDir)$(TargetName).pdb - Windows - - - $(IntDir)$(TargetName).lib - MachineX86 - %(AdditionalLibraryDirectories) - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - X64 - - - Disabled - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;IFILE_DIALOG_VISTA_DLL_EXPORTS;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebugDLL - - - Level4 - true - ProgramDatabase - 4100 - ../.. - true - true - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - $(IntDir)$(TargetName).pdb - Windows - - - $(IntDir)$(TargetName).lib - MachineX64 - - - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - MaxSpeed - true - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;IFILE_DIALOG_VISTA_DLL_EXPORTS;%(PreprocessorDefinitions) - MultiThreaded - true - - - Level4 - true - ProgramDatabase - 4100 - Speed - ../.. - false - true - NoExtensions - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - Windows - true - true - UseLinkTimeCodeGeneration - - - $(IntDir)$(TargetName).lib - MachineX86 - %(AdditionalLibraryDirectories) - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - X64 - - - MaxSpeed - true - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;IFILE_DIALOG_VISTA_DLL_EXPORTS;%(PreprocessorDefinitions) - MultiThreaded - true - - - Level4 - true - ProgramDatabase - 4100 - Speed - ../.. - false - true - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - Windows - true - true - UseLinkTimeCodeGeneration - - - $(IntDir)$(TargetName).lib - MachineX64 - - - %(AdditionalDependencies) - - - - - - \ No newline at end of file diff --git a/ui/IFileDialog_Vista/ifile_dialog.cpp b/ui/IFileDialog_Vista/ifile_dialog.cpp deleted file mode 100644 index f56df9e7..00000000 --- a/ui/IFileDialog_Vista/ifile_dialog.cpp +++ /dev/null @@ -1,108 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "ifile_dialog.h" -#define WIN32_LEAN_AND_MEAN -#include -#include -#include -#include -#include - -using namespace zen; - - -namespace -{ -bool showFolderPickerImpl(HWND ownerWindow, //throw SysError; return "false" if cancelled by user - const wchar_t* defaultFolder, //optional! - const GUID* persistenceGuid, // - std::wstring& selectedFolder) -{ - ComPtr fileDlg; - ZEN_COM_CHECK(::CoCreateInstance(CLSID_FileOpenDialog, //throw SysError - nullptr, - CLSCTX_ALL, - IID_PPV_ARGS(fileDlg.init()))); - - if (persistenceGuid) - ZEN_COM_CHECK(fileDlg->SetClientGuid(*persistenceGuid)); - - FILEOPENDIALOGOPTIONS dlgOptions = 0; - ZEN_COM_CHECK(fileDlg->GetOptions(&dlgOptions)); //throw SysError - ZEN_COM_CHECK(fileDlg->SetOptions(dlgOptions | FOS_PICKFOLDERS | FOS_NOVALIDATE | FOS_FORCEFILESYSTEM)); - - if (defaultFolder) //show last selection instead of top level if no default available - { - ComPtr folderItem; - ZEN_COM_CHECK(::SHCreateItemFromParsingName(defaultFolder, - nullptr, - IID_PPV_ARGS(folderItem.init()))); - ZEN_COM_CHECK(fileDlg->SetFolder(folderItem.get())); - } - - try - { - ZEN_COM_CHECK(fileDlg->Show(ownerWindow)); //may fail with: HRESULT_FROM_WIN32(ERROR_CANCELLED) - } - catch (const SysError&) { return false; } - - ComPtr folderItem; - ZEN_COM_CHECK(fileDlg->GetResult(folderItem.init())); - - LPWSTR folderPath = nullptr; - ZEN_COM_CHECK(folderItem->GetDisplayName(SIGDN_FILESYSPATH, &folderPath)); - ZEN_ON_SCOPE_EXIT(::CoTaskMemFree(folderPath)); - - selectedFolder = folderPath; - return true; -} - -wchar_t* allocString(const std::wstring& msg) //ownership passed -{ - auto tmp = new wchar_t [msg.size() + 1]; //std::bad_alloc ? - ::wmemcpy(tmp, msg.c_str(), msg.size() + 1); //include 0-termination - return tmp; -} -} - -//################################################################################################## - -void ifile::showFolderPicker(void* ownerWindow, - const wchar_t* defaultFolder, - const GuidProxy* guid, - wchar_t*& selectedFolder, - bool& cancelled, - wchar_t*& errorMsg) -{ - selectedFolder = nullptr; - cancelled = false; - errorMsg = nullptr; - - try - { - static_assert(sizeof(GuidProxy) == sizeof(GUID), ""); - GUID winGuid = {}; - if (guid) - ::memcpy(&winGuid, guid, sizeof(GUID)); - - std::wstring folderPath; - if (showFolderPickerImpl(static_cast(ownerWindow), defaultFolder, guid ? &winGuid : nullptr, folderPath)) //throw SysError - selectedFolder = allocString(folderPath); - else - cancelled = true; - } - catch (const SysError& e) - { - errorMsg = allocString(e.toString()); //std::bad_alloc ? - } -} - - -void ifile::freeString(const wchar_t* str) -{ - delete [] str; -} diff --git a/ui/IFileDialog_Vista/ifile_dialog.h b/ui/IFileDialog_Vista/ifile_dialog.h deleted file mode 100644 index 5b4dc532..00000000 --- a/ui/IFileDialog_Vista/ifile_dialog.h +++ /dev/null @@ -1,67 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef IFILE_DIALOG_HEADER_916743921746324 -#define IFILE_DIALOG_HEADER_916743921746324 - -#ifdef IFILE_DIALOG_VISTA_DLL_EXPORTS -#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllexport) -#else -#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllimport) -#endif - -#include - - -namespace ifile -{ -/*-------------- - |declarations| - --------------*/ - -//COM needs to be initialized before calling any of these functions! CoInitializeEx/CoUninitialize -//Requires Windows Vista and later - -typedef char GuidProxy[16]; //= Windows 128-bit GUID; we don't want to include "Guiddef.h" here! - -DLL_FUNCTION_DECLARATION -void showFolderPicker(void* ownerWindow, //in; ==HWND - const wchar_t* defaultFolder, //in, optional! - const GuidProxy* guid, //set nullptr by default: Windows stores dialog state (position, x, y coordinates, ect.) associated with the process executable name => use other GUID when needed - wchar_t*& selectedFolder, //out: call freeString() after use! - bool& cancelled, //out - wchar_t*& errorMsg); //out, optional: call freeString() after use! - -DLL_FUNCTION_DECLARATION -void freeString(const wchar_t* str); - -/*---------- - |typedefs| - ----------*/ -typedef bool (*FunType_showFolderPicker)(void* ownerWindow, - const wchar_t* defaultFolder, - const GuidProxy* guid, - wchar_t*& selectedFolder, - bool& cancelled, - wchar_t*& errorMsg); -typedef void (*FunType_freeString)(const wchar_t* str); - -/*-------------- - |symbol names| - --------------*/ -//(use const pointers to ensure internal linkage) -const char funName_showFolderPicker[] = "showFolderPicker"; -const char funName_freeString [] = "freeString"; - -/*--------------- - |library names| - ---------------*/ -inline const wchar_t* getDllName() { return zen::is64BitBuild ? L"IFileDialog_Vista_x64.dll" : L"IFileDialog_Vista_Win32.dll"; } -} - -#undef DLL_FUNCTION_DECLARATION - -#endif //IFILE_DIALOG_HEADER_916743921746324 diff --git a/ui/Taskbar_Seven/Taskbar_Seven.vcxproj b/ui/Taskbar_Seven/Taskbar_Seven.vcxproj deleted file mode 100644 index 2617569a..00000000 --- a/ui/Taskbar_Seven/Taskbar_Seven.vcxproj +++ /dev/null @@ -1,236 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {70394AEF-5897-4911-AFA1-82EAF0581EFA} - ShadowDll - Win32Proj - - - - DynamicLibrary - Unicode - true - v120_xp - - - DynamicLibrary - Unicode - v120_xp - - - DynamicLibrary - Unicode - true - v120_xp - - - DynamicLibrary - Unicode - v120_xp - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - false - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - false - Taskbar7_$(Platform) - Taskbar7_$(Platform) - Taskbar7_$(Platform) - Taskbar7_$(Platform) - - - - $(IntDir)Build.html - - - Disabled - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;TASKBAR_SEVEN_DLL_EXPORTS;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebugDLL - - - Level4 - true - EditAndContinue - 4100;4996 - ../.. - true - true - NoExtensions - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - $(IntDir)$(TargetName).pdb - Windows - - - $(IntDir)$(TargetName).lib - MachineX86 - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - X64 - - - Disabled - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;TASKBAR_SEVEN_DLL_EXPORTS;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebugDLL - - - Level4 - true - ProgramDatabase - 4100;4996 - ../.. - true - true - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - $(IntDir)$(TargetName).pdb - Windows - - - $(IntDir)$(TargetName).lib - MachineX64 - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - MaxSpeed - true - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;TASKBAR_SEVEN_DLL_EXPORTS;%(PreprocessorDefinitions) - MultiThreaded - true - - - Level4 - true - ProgramDatabase - 4100;4996 - Speed - ../.. - true - NoExtensions - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - Windows - true - true - UseLinkTimeCodeGeneration - - - $(IntDir)$(TargetName).lib - MachineX86 - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - X64 - - - MaxSpeed - true - ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;TASKBAR_SEVEN_DLL_EXPORTS;%(PreprocessorDefinitions) - MultiThreaded - true - - - Level4 - true - ProgramDatabase - 4100;4996 - Speed - ../.. - true - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - Windows - true - true - UseLinkTimeCodeGeneration - - - $(IntDir)$(TargetName).lib - MachineX64 - %(AdditionalDependencies) - - - - - - - - - - - - - \ No newline at end of file diff --git a/ui/Taskbar_Seven/taskbar.cpp b/ui/Taskbar_Seven/taskbar.cpp deleted file mode 100644 index 72be7016..00000000 --- a/ui/Taskbar_Seven/taskbar.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "taskbar.h" -#include -#include - -#define WIN32_LEAN_AND_MEAN -#include -#include - -#include - -using namespace zen; - - -namespace -{ -std::wstring lastErrorMessage; - - -ComPtr getInstance() -{ - ComPtr taskbarlist; - - HRESULT hr = ::CoCreateInstance(CLSID_TaskbarList, - nullptr, - CLSCTX_ALL, - IID_PPV_ARGS(taskbarlist.init())); - if (FAILED(hr)) - { - lastErrorMessage = formatComError(L"Error calling \"CoCreateInstance\".", hr); - return ComPtr(); - } - - return taskbarlist; -} -} -//################################################################################################## - - -bool tbseven::setStatus(void* hwnd, //HWND: window assciated to the taskbar icon - TaskBarStatus status) -{ - TBPFLAG flag = TBPF_NORMAL; - switch (status) - { - case STATUS_NOPROGRESS: - flag = TBPF_NOPROGRESS; - break; - case STATUS_INDETERMINATE: - flag = TBPF_INDETERMINATE; - break; - case STATUS_NORMAL: - flag = TBPF_NORMAL; - break; - case STATUS_ERROR: - flag = TBPF_ERROR; - break; - case STATUS_PAUSED: - flag = TBPF_PAUSED; - break; - } - - ComPtr taskbarlist = getInstance(); - if (!taskbarlist) //error msg already set - return false; - - HRESULT hr = taskbarlist->SetProgressState(static_cast(hwnd), //[in] HWND hwnd, - flag); //[in] TBPFLAG tbpFlags - if (FAILED(hr)) - { - lastErrorMessage = formatComError(L"Error calling \"SetProgressState\".", hr); - return false; - } - - return true; -} - - -bool tbseven::setProgress(void* hwnd, //HWND: window assciated to the taskbar icon - size_t current, - size_t total) -{ - ComPtr taskbarlist = getInstance(); - if (!taskbarlist) //error msg already set - return false; - - HRESULT hr = taskbarlist->SetProgressValue( - static_cast(hwnd), //[in] HWND hwnd, - current, //[in] ULONGLONG ullCompleted, - total); //[in] ULONGLONG ullTotal - if (FAILED(hr)) - { - lastErrorMessage = formatComError(L"Error calling \"SetProgressValue\".", hr); - return false; - } - - return true; -} - - -void tbseven::getLastError(wchar_t* buffer, size_t bufferSize) -{ - if (bufferSize > 0) - { - size_t endPos = lastErrorMessage.copy(buffer, bufferSize - 1); - buffer[endPos] = 0; - } -} diff --git a/ui/Taskbar_Seven/taskbar.h b/ui/Taskbar_Seven/taskbar.h deleted file mode 100644 index 4a158d1f..00000000 --- a/ui/Taskbar_Seven/taskbar.h +++ /dev/null @@ -1,74 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef TASKBAR_SEVEN_DLL_H -#define TASKBAR_SEVEN_DLL_H - -#ifdef TASKBAR_SEVEN_DLL_EXPORTS -#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllexport) -#else -#define DLL_FUNCTION_DECLARATION extern "C" __declspec(dllimport) -#endif - -#include - - -namespace tbseven -{ -/*-------------- - |declarations| - --------------*/ - -enum TaskBarStatus -{ - STATUS_NOPROGRESS, - STATUS_INDETERMINATE, - STATUS_NORMAL, - STATUS_ERROR, - STATUS_PAUSED -}; - - -//COM needs to be initialized before calling any of these functions! CoInitializeEx/CoUninitialize - -DLL_FUNCTION_DECLARATION -bool setStatus(void* hwnd, //HWND: window assciated to the taskbar icon - TaskBarStatus status); - - -DLL_FUNCTION_DECLARATION -bool setProgress(void* hwnd, //HWND: window assciated to the taskbar icon - size_t current, - size_t total); - -//if any of the functions above returns 'false', this message returns last error -DLL_FUNCTION_DECLARATION -void getLastError(wchar_t* buffer, size_t bufferSize); - -/*---------- - |typedefs| - ----------*/ -typedef bool (*FunType_setStatus )(void* hwnd, TaskBarStatus status); -typedef bool (*FunType_setProgress )(void* hwnd, size_t current, size_t total); -typedef void (*FunType_getLastError)(wchar_t* buffer, size_t bufferSize); - -/*-------------- - |symbol names| - --------------*/ -//(use const pointers to ensure internal linkage) -const char funName_setStatus [] = "setStatus"; -const char funName_setProgress [] = "setProgress"; -const char funName_getLastError[] = "getLastError"; - -/*--------------- - |library names| - ---------------*/ -inline const wchar_t* getDllName() { return zen::is64BitBuild ? L"Taskbar7_x64.dll" : L"Taskbar7_Win32.dll"; } -} - -#undef DLL_FUNCTION_DECLARATION - -#endif //TASKBAR_SEVEN_DLL_H diff --git a/ui/app_icon.h b/ui/app_icon.h deleted file mode 100644 index a1240fee..00000000 --- a/ui/app_icon.h +++ /dev/null @@ -1,40 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef APP_ICON_H6748179634932174683214 -#define APP_ICON_H6748179634932174683214 - -#include -#include - -namespace zen -{ -inline -wxIcon getFfsIcon() -{ - //wxWidgets' bitmap to icon conversion on OS X can only deal with very specific sizes => check on all platforms! - assert(getResourceImage(L"FreeFileSync").GetWidth () == getResourceImage(L"FreeFileSync").GetHeight() && - getResourceImage(L"FreeFileSync").GetWidth() % 128 == 0); -#ifdef ZEN_WIN - //for compatibility it seems we need to stick with a "real" icon - return wxIcon(L"A_FFS_ICON"); - -#elif defined ZEN_LINUX - //attention: make sure to not implicitly call "instance()" again => deadlock on Linux - wxIcon icon; - icon.CopyFromBitmap(getResourceImage(L"FreeFileSync")); //use big logo bitmap for better quality - return icon; - -#elif defined ZEN_MAC - wxIcon icon; - icon.CopyFromBitmap(getResourceImage(L"FreeFileSync").ConvertToImage().Scale(128, 128, wxIMAGE_QUALITY_HIGH)); //"von hinten durch die Brust ins Auge" - return icon; -#endif -} -} - - -#endif //APP_ICON_H6748179634932174683214 diff --git a/ui/batch_config.cpp b/ui/batch_config.cpp deleted file mode 100644 index 4807e4a1..00000000 --- a/ui/batch_config.cpp +++ /dev/null @@ -1,177 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "batch_config.h" -#include -#include -#include -#include -#include -#include "gui_generated.h" -#include "dir_name.h" -#include "../ui/exec_finished_box.h" -#include "../lib/help_provider.h" - -using namespace zen; -using namespace xmlAccess; - - -namespace -{ -enum ButtonPressed -{ - BUTTON_CANCEL, - BUTTON_SAVE_AS -}; - - -class BatchDialog : public BatchDlgGenerated -{ -public: - BatchDialog(wxWindow* parent, - XmlBatchConfig& batchCfg, //in/out - std::vector& onCompletionHistory, - size_t onCompletionHistoryMax); - -private: - virtual void OnClose (wxCloseEvent& event) { EndModal(BUTTON_CANCEL); } - virtual void OnCancel (wxCommandEvent& event) { EndModal(BUTTON_CANCEL); } - virtual void OnSaveBatchJob(wxCommandEvent& event); - virtual void OnErrorPopup (wxCommandEvent& event) { localBatchCfg.handleError = ON_ERROR_POPUP; updateGui(); } - virtual void OnErrorIgnore(wxCommandEvent& event) { localBatchCfg.handleError = ON_ERROR_IGNORE; updateGui(); } - virtual void OnErrorStop (wxCommandEvent& event) { localBatchCfg.handleError = ON_ERROR_STOP; updateGui(); } - virtual void OnHelpScheduleBatch(wxHyperlinkEvent& event) { displayHelpEntry(L"html/Schedule a Batch Job.html", this); } - - virtual void OnToggleGenerateLogfile(wxCommandEvent& event) { updateGui(); } - virtual void OnToggleLogfilesLimit (wxCommandEvent& event) { updateGui(); } - - void updateGui(); //re-evaluate gui after config changes - - void setConfig(const XmlBatchConfig& batchCfg); - XmlBatchConfig getConfig() const; - - XmlBatchConfig& batchCfgOutRef; //output only! - XmlBatchConfig localBatchCfg; //a mixture of settings some of which have OWNERSHIP WITHIN GUI CONTROLS! use getConfig() to resolve - - std::unique_ptr> logfileDir; //always bound, solve circular compile-time dependency -}; - -//################################################################################################################################### - -BatchDialog::BatchDialog(wxWindow* parent, - XmlBatchConfig& batchCfg, - std::vector& onCompletionHistory, - size_t onCompletionHistoryMax) : - BatchDlgGenerated(parent), - batchCfgOutRef(batchCfg) -{ -#ifdef ZEN_WIN - new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" - wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! -#endif - setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonSaveAs).setCancel(m_buttonCancel)); - - m_staticTextDescr->SetLabel(replaceCpy(m_staticTextDescr->GetLabel(), L"%x", L"FreeFileSync.exe <" + _("job name") + L">.ffs_batch")); - - m_comboBoxExecFinished->initHistory(onCompletionHistory, onCompletionHistoryMax); - - m_bitmapBatchJob->SetBitmap(getResourceImage(L"batch")); - - logfileDir = make_unique>(*m_panelLogfile, *m_buttonSelectLogfileDir, *m_logfileDir); - - setConfig(batchCfg); - - GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() - //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! - - // Layout(); - - m_buttonSaveAs->SetFocus(); -} - - -void BatchDialog::updateGui() //re-evaluate gui after config changes -{ - XmlBatchConfig cfg = getConfig(); //resolve parameter ownership: some on GUI controls, others member variables - - m_panelLogfile ->Enable(m_checkBoxGenerateLogfile->GetValue()); //enabled status is *not* directly dependent from resolved config! (but transitively) - m_spinCtrlLogfileLimit->Enable(m_checkBoxGenerateLogfile->GetValue() && m_checkBoxLogfilesLimit->GetValue()); - - m_toggleBtnErrorIgnore->SetValue(false); - m_toggleBtnErrorPopup ->SetValue(false); - m_toggleBtnErrorStop ->SetValue(false); - switch (cfg.handleError) //*not* owned by GUI controls - { - case ON_ERROR_IGNORE: - m_toggleBtnErrorIgnore->SetValue(true); - break; - case ON_ERROR_POPUP: - m_toggleBtnErrorPopup->SetValue(true); - break; - case ON_ERROR_STOP: - m_toggleBtnErrorStop->SetValue(true); - break; - } -} - - -void BatchDialog::setConfig(const XmlBatchConfig& batchCfg) -{ -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! -#endif - - localBatchCfg = batchCfg; //contains some parameters not owned by GUI controls - - //transfer parameter ownership to GUI - m_checkBoxShowProgress->SetValue(batchCfg.showProgress); - logfileDir->setName(utfCvrtTo(batchCfg.logFileDirectory)); - m_comboBoxExecFinished->setValue(batchCfg.mainCfg.onCompletion); - - //map single parameter "logfiles limit" to all three checkboxs and spin ctrl: - m_checkBoxGenerateLogfile->SetValue(batchCfg.logfilesCountLimit != 0); - m_checkBoxLogfilesLimit ->SetValue(batchCfg.logfilesCountLimit >= 0); - m_spinCtrlLogfileLimit ->SetValue(batchCfg.logfilesCountLimit >= 0 ? batchCfg.logfilesCountLimit : 100 /*XmlBatchConfig().logfilesCountLimit*/); - //attention: emits a "change value" event!! => updateGui() called implicitly! - - updateGui(); //re-evaluate gui after config changes -} - - -XmlBatchConfig BatchDialog::getConfig() const -{ - XmlBatchConfig batchCfg = localBatchCfg; - - //load parameters with ownership within GIU controls... - - //load structure with batch settings "batchCfg" - batchCfg.showProgress = m_checkBoxShowProgress->GetValue(); - batchCfg.logFileDirectory = utfCvrtTo(logfileDir->getName()); - batchCfg.mainCfg.onCompletion = m_comboBoxExecFinished->getValue(); - //get single parameter "logfiles limit" from all three checkboxes and spin ctrl: - batchCfg.logfilesCountLimit = m_checkBoxGenerateLogfile->GetValue() ? (m_checkBoxLogfilesLimit->GetValue() ? m_spinCtrlLogfileLimit->GetValue() : -1) : 0; - - return batchCfg; -} - - -void BatchDialog::OnSaveBatchJob(wxCommandEvent& event) -{ - batchCfgOutRef = getConfig(); - m_comboBoxExecFinished->addItemHistory(); //a good place to commit current "on completion" history item - EndModal(BUTTON_SAVE_AS); -} -} - - -bool zen::customizeBatchConfig(wxWindow* parent, - xmlAccess::XmlBatchConfig& batchCfg, //in/out - std::vector& execFinishedhistory, - size_t execFinishedhistoryMax) -{ - BatchDialog batchDlg(parent, batchCfg, execFinishedhistory, execFinishedhistoryMax); - return static_cast(batchDlg.ShowModal()) == BUTTON_SAVE_AS; -} diff --git a/ui/batch_config.h b/ui/batch_config.h deleted file mode 100644 index 5bff09e1..00000000 --- a/ui/batch_config.h +++ /dev/null @@ -1,22 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef BATCHCONFIG_H_INCLUDED -#define BATCHCONFIG_H_INCLUDED - -#include -#include "../lib/process_xml.h" - -namespace zen -{ -//show and let user customize batch settings (without saving) -bool customizeBatchConfig(wxWindow* parent, //return "false" if aborted, "true" on "do save" - xmlAccess::XmlBatchConfig& batchCfg, //in/out - std::vector& execFinishedhistory, - size_t execFinishedhistoryMax); -} - -#endif // BATCHCONFIG_H_INCLUDED diff --git a/ui/batch_status_handler.cpp b/ui/batch_status_handler.cpp deleted file mode 100644 index bca6b484..00000000 --- a/ui/batch_status_handler.cpp +++ /dev/null @@ -1,477 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "batch_status_handler.h" -#include -#include -//#include -#include -#include -#include -#include "exec_finished_box.h" -#include "../lib/ffs_paths.h" -#include "../lib/resolve_path.h" -#include "../lib/status_handler_impl.h" -#include "../lib/generate_logfile.h" - -using namespace zen; - - -namespace -{ -class FindLogfiles : public TraverseCallback -{ -public: - FindLogfiles(const Zstring& prefix, std::vector& logfiles) : prefix_(prefix), logfiles_(logfiles) {} - -private: - virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) - { - const Zstring fileName(shortName); - if (startsWith(fileName, prefix_) && endsWith(fileName, Zstr(".log"))) - logfiles_.push_back(fullName); - } - - virtual TraverseCallback* onDir (const Zchar* shortName, const Zstring& fullName) { return nullptr; } //DON'T traverse into subdirs - virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) { return LINK_SKIP; } - virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) { assert(false); return ON_ERROR_IGNORE; } //errors are not really critical in this context - virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) { assert(false); return ON_ERROR_IGNORE; } // - - const Zstring prefix_; - std::vector& logfiles_; -}; - - -void limitLogfileCount(const Zstring& logdir, const std::wstring& jobname, size_t maxCount) //throw() -{ - std::vector logFiles; - FindLogfiles traverseCallback(utfCvrtTo(jobname), logFiles); //throw()! - - traverseFolder(logdir, - traverseCallback); - - if (logFiles.size() <= maxCount) - return; - - //delete oldest logfiles: take advantage of logfile naming convention to find them - std::nth_element(logFiles.begin(), logFiles.end() - maxCount, logFiles.end(), LessFilename()); - - std::for_each(logFiles.begin(), logFiles.end() - maxCount, - [](const Zstring& filename) { try { removeFile(filename); } catch (FileError&) {} }); -} - - -std::unique_ptr prepareNewLogfile(const Zstring& logfileDirectory, //throw FileError - const std::wstring& jobName, - const TimeComp& timeStamp) //return value always bound! -{ - //create logfile directory if required - Zstring logfileDir = logfileDirectory.empty() ? - getConfigDir() + Zstr("Logs") : - getFormattedDirectoryName(logfileDirectory); - - makeDirectory(logfileDir); //throw FileError - - //assemble logfile name - const Zstring body = appendSeparator(logfileDir) + utfCvrtTo(jobName) + Zstr(" ") + formatTime(Zstr("%Y-%m-%d %H%M%S"), timeStamp); - - //ensure uniqueness - for (int i = 0;; ++i) - try - { - const Zstring& filename = i == 0 ? - body + Zstr(".log") : - body + Zstr('_') + numberTo(i) + Zstr(".log"); - - return make_unique(filename, FileOutput::ACC_CREATE_NEW); //throw FileError, ErrorTargetExisting - //*no* file system race-condition! - } - catch (const ErrorTargetExisting&) {} -} -} - -//############################################################################################################################## - -BatchStatusHandler::BatchStatusHandler(bool showProgress, - const std::wstring& jobName, - const TimeComp& timeStamp, - const Zstring& logfileDirectory, //may be empty - int logfilesCountLimit, - size_t lastSyncsLogFileSizeMax, - const xmlAccess::OnError handleError, - size_t automaticRetryCount, - size_t automaticRetryDelay, - const SwitchToGui& switchBatchToGui, //functionality to change from batch mode to GUI mode - FfsReturnCode& returnCode, - const std::wstring& execWhenFinished, - std::vector& execFinishedHistory) : - switchBatchToGui_(switchBatchToGui), - showFinalResults(showProgress), //=> exit immediately or wait when finished - switchToGuiRequested(false), - logfilesCountLimit_(logfilesCountLimit), - lastSyncsLogFileSizeMax_(lastSyncsLogFileSizeMax), - handleError_(handleError), - returnCode_(returnCode), - automaticRetryCount_(automaticRetryCount), - automaticRetryDelay_(automaticRetryDelay), - progressDlg(createProgressDialog(*this, [this] { this->onProgressDialogTerminate(); }, *this, nullptr, showProgress, jobName, execWhenFinished, execFinishedHistory)), - jobName_(jobName) -{ - if (logfilesCountLimit != 0) - { - zen::Opt errMsg = tryReportingError([&] { logFile = prepareNewLogfile(logfileDirectory, jobName, timeStamp); }, //throw FileError; return value always bound! - *this); - if (errMsg) - { - raiseReturnCode(returnCode_, FFS_RC_ABORTED); - throw BatchAbortProcess(); - } - } - - totalTime.Start(); //measure total time - - //if (logFile) - // ::wxSetEnv(L"logfile", utfCvrtTo(logFile->getFilename())); -} - - -BatchStatusHandler::~BatchStatusHandler() -{ - //------------ "on completion" command conceptually is part of the sync, not cleanup -------------------------------------- - - //decide whether to stay on status screen or exit immediately... - if (switchToGuiRequested) //-> avoid recursive yield() calls, thous switch not before ending batch mode - { - try - { - switchBatchToGui_.execute(); //open FreeFileSync GUI - } - catch (...) {} - showFinalResults = false; - } - else if (progressDlg) - { - if (progressDlg->getWindowIfVisible()) - showFinalResults = true; - - //execute "on completion" command (even in case of ignored errors) - if (!abortIsRequested()) //if aborted (manually), we don't execute the command - { - const std::wstring finalCommand = progressDlg->getExecWhenFinishedCommand(); //final value (after possible user modification) - if (!finalCommand.empty()) - { - if (isCloseProgressDlgCommand(finalCommand)) - showFinalResults = false; //take precedence over current visibility status - else - try - { - tryReportingError([&] { shellExecute2(expandMacros(utfCvrtTo(finalCommand)), EXEC_TYPE_SYNC); }, //throw FileError, throw X? - *this); - } - catch (...) {} - } - } - } - //------------ end of sync: begin of cleanup -------------------------------------- - - const int totalErrors = errorLog.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR); //evaluate before finalizing log - const int totalWarnings = errorLog.getItemCount(TYPE_WARNING); - - //finalize error log - std::wstring finalStatus; - if (abortIsRequested()) - { - raiseReturnCode(returnCode_, FFS_RC_ABORTED); - finalStatus = _("Synchronization stopped"); - errorLog.logMsg(finalStatus, TYPE_ERROR); - } - else if (totalErrors > 0) - { - raiseReturnCode(returnCode_, FFS_RC_FINISHED_WITH_ERRORS); - finalStatus = _("Synchronization completed with errors"); - errorLog.logMsg(finalStatus, TYPE_ERROR); - } - else if (totalWarnings > 0) - { - raiseReturnCode(returnCode_, FFS_RC_FINISHED_WITH_WARNINGS); - finalStatus = _("Synchronization completed with warnings"); - errorLog.logMsg(finalStatus, TYPE_WARNING); - } - else - { - if (getObjectsTotal(PHASE_SYNCHRONIZING) == 0 && //we're past "initNewPhase(PHASE_SYNCHRONIZING)" at this point! - getDataTotal (PHASE_SYNCHRONIZING) == 0) - finalStatus = _("Nothing to synchronize"); //even if "ignored conflicts" occurred! - else - finalStatus = _("Synchronization completed successfully"); - errorLog.logMsg(finalStatus, TYPE_INFO); - } - - const SummaryInfo summary = - { - jobName_, - finalStatus, - getObjectsCurrent(PHASE_SYNCHRONIZING), getDataCurrent(PHASE_SYNCHRONIZING), - getObjectsTotal (PHASE_SYNCHRONIZING), getDataTotal (PHASE_SYNCHRONIZING), - totalTime.Time() / 1000 - }; - - //print the results list: logfile - if (logFile.get()) - { - try - { - //saving log file below may take a *long* time, so report (without logging) - reportStatus(replaceCpy(_("Saving log file %x..."), L"%x", fmtFileName(logFile->getFilename()))); //throw? - forceUiRefresh(); // - } - catch (...) {} - - if (logfilesCountLimit_ > 0) - limitLogfileCount(beforeLast(logFile->getFilename(), FILE_NAME_SEPARATOR), jobName_, logfilesCountLimit_); //throw() - - try - { - saveLogToFile(summary, errorLog, *logFile); //throw FileError - } - catch (FileError&) {} - } - try - { - saveToLastSyncsLog(summary, errorLog, lastSyncsLogFileSizeMax_); //throw FileError - } - catch (FileError&) {} - - if (progressDlg) - { - if (showFinalResults) //warning: wxWindow::Show() is called within processHasFinished()! - { - //notify about (logical) application main window => program won't quit, but stay on this dialog - //setMainWindow(progressDlg->getAsWindow()); -> not required anymore since we block waiting until dialog is closed below - - //notify to progressDlg that current process has ended - if (abortIsRequested()) - progressDlg->processHasFinished(SyncProgressDialog::RESULT_ABORTED, errorLog); //enable okay and close events - else if (totalErrors > 0) - progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_ERROR, errorLog); - else if (totalWarnings > 0) - progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS, errorLog); - else - progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS, errorLog); - } - else - progressDlg->closeWindowDirectly(); //progressDlg is main window => program will quit directly - - //wait until progress dialog notified shutdown via onProgressDialogTerminate() - //-> required since it has our "this" pointer captured in lambda "notifyWindowTerminate"! - //-> nicely manages dialog lifetime - while (progressDlg) - { - wxTheApp->Yield(); //*first* refresh GUI (removing flicker) before sleeping! - boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL)); - } - } -} - - -void BatchStatusHandler::initNewPhase(int objectsTotal, Int64 dataTotal, ProcessCallback::Phase phaseID) -{ - StatusHandler::initNewPhase(objectsTotal, dataTotal, phaseID); - if (progressDlg) - progressDlg->initNewPhase(); //call after "StatusHandler::initNewPhase" -} - - -void BatchStatusHandler::updateProcessedData(int objectsDelta, Int64 dataDelta) -{ - StatusHandler::updateProcessedData(objectsDelta, dataDelta); - - if (progressDlg) - progressDlg->notifyProgressChange(); //noexcept - //note: this method should NOT throw in order to properly allow undoing setting of statistics! -} - - -void BatchStatusHandler::reportInfo(const std::wstring& text) -{ - StatusHandler::reportInfo(text); - errorLog.logMsg(text, TYPE_INFO); -} - - -void BatchStatusHandler::reportWarning(const std::wstring& warningMessage, bool& warningActive) -{ - errorLog.logMsg(warningMessage, TYPE_WARNING); - - if (!warningActive) - return; - - switch (handleError_) - { - case xmlAccess::ON_ERROR_POPUP: - { - if (!progressDlg) abortThisProcess(); - PauseTimers dummy(*progressDlg); - forceUiRefresh(); - - bool dontWarnAgain = false; - switch (showConfirmationDialog3(progressDlg->getWindowIfVisible(), DialogInfoType::WARNING, PopupDialogCfg3(). - setDetailInstructions(warningMessage + L"\n\n" + _("You can switch to FreeFileSync's main window to resolve this issue.")). - setCheckBox(dontWarnAgain, _("&Don't show this warning again"), ConfirmationButton3::DONT_DO_IT), - _("&Ignore"), _("&Switch"))) - { - case ConfirmationButton3::DO_IT: //ignore - warningActive = !dontWarnAgain; - break; - - case ConfirmationButton3::DONT_DO_IT: //switch - errorLog.logMsg(_("Switching to FreeFileSync's main window"), TYPE_INFO); - switchToGuiRequested = true; - abortThisProcess(); - break; - - case ConfirmationButton3::CANCEL: - abortThisProcess(); - break; - } - } - break; //keep it! last switch might not find match - - case xmlAccess::ON_ERROR_STOP: - abortThisProcess(); - break; - - case xmlAccess::ON_ERROR_IGNORE: - break; - } -} - - -ProcessCallback::Response BatchStatusHandler::reportError(const std::wstring& errorMessage, size_t retryNumber) -{ - //auto-retry - if (retryNumber < automaticRetryCount_) - { - errorLog.logMsg(errorMessage + L"\n=> " + - _P("Automatic retry in 1 second...", "Automatic retry in %x seconds...", automaticRetryDelay_), TYPE_INFO); - //delay - const int iterations = static_cast(1000 * automaticRetryDelay_ / UI_UPDATE_INTERVAL); //always round down: don't allow for negative remaining time below - for (int i = 0; i < iterations; ++i) - { - reportStatus(_("Error") + L": " + _P("Automatic retry in 1 second...", "Automatic retry in %x seconds...", - (1000 * automaticRetryDelay_ - i * UI_UPDATE_INTERVAL + 999) / 1000)); //integer round up - boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL)); - } - return ProcessCallback::RETRY; - } - - - //always, except for "retry": - zen::ScopeGuard guardWriteLog = zen::makeGuard([&] { errorLog.logMsg(errorMessage, TYPE_ERROR); }); - - switch (handleError_) - { - case xmlAccess::ON_ERROR_POPUP: - { - if (!progressDlg) abortThisProcess(); - PauseTimers dummy(*progressDlg); - forceUiRefresh(); - - bool ignoreNextErrors = false; - switch (showConfirmationDialog3(progressDlg->getWindowIfVisible(), DialogInfoType::ERROR2, PopupDialogCfg3(). - setDetailInstructions(errorMessage). - setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors"), ConfirmationButton3::DONT_DO_IT), - _("&Ignore"), _("&Retry"))) - { - case ConfirmationButton3::DO_IT: //ignore - if (ignoreNextErrors) //falsify only - handleError_ = xmlAccess::ON_ERROR_IGNORE; - return ProcessCallback::IGNORE_ERROR; - - case ConfirmationButton3::DONT_DO_IT: //retry - guardWriteLog.dismiss(); - errorLog.logMsg(errorMessage + L"\n=> " + _("Retrying operation..."), TYPE_INFO); - return ProcessCallback::RETRY; - - case ConfirmationButton3::CANCEL: - abortThisProcess(); - break; - } - } - break; //used if last switch didn't find a match - - case xmlAccess::ON_ERROR_STOP: - abortThisProcess(); - break; - - case xmlAccess::ON_ERROR_IGNORE: - return ProcessCallback::IGNORE_ERROR; - } - - assert(false); - return ProcessCallback::IGNORE_ERROR; //dummy value -} - - -void BatchStatusHandler::reportFatalError(const std::wstring& errorMessage) -{ - errorLog.logMsg(errorMessage, TYPE_FATAL_ERROR); - - switch (handleError_) - { - case xmlAccess::ON_ERROR_POPUP: - { - if (!progressDlg) abortThisProcess(); - PauseTimers dummy(*progressDlg); - forceUiRefresh(); - - bool ignoreNextErrors = false; - switch (showConfirmationDialog(progressDlg->getWindowIfVisible(), DialogInfoType::ERROR2, - PopupDialogCfg().setTitle(_("Serious Error")). - setDetailInstructions(errorMessage). - setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors")), - _("&Ignore"))) - { - case ConfirmationButton::DO_IT: - if (ignoreNextErrors) //falsify only - handleError_ = xmlAccess::ON_ERROR_IGNORE; - break; - case ConfirmationButton::CANCEL: - abortThisProcess(); - break; - } - } - break; - - case xmlAccess::ON_ERROR_STOP: - abortThisProcess(); - break; - - case xmlAccess::ON_ERROR_IGNORE: - break; - } -} - - -void BatchStatusHandler::forceUiRefresh() -{ - if (progressDlg) - progressDlg->updateGui(); -} - - -void BatchStatusHandler::abortThisProcess() -{ - requestAbortion(); //just make sure... - throw BatchAbortProcess(); //abort can be triggered by progressDlg -} - - -void BatchStatusHandler::onProgressDialogTerminate() -{ - //it's responsibility of "progressDlg" to call requestAbortion() when closing dialog - progressDlg = nullptr; -} diff --git a/ui/batch_status_handler.h b/ui/batch_status_handler.h deleted file mode 100644 index de6e71b3..00000000 --- a/ui/batch_status_handler.h +++ /dev/null @@ -1,76 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef BATCHSTATUSHANDLER_H_INCLUDED -#define BATCHSTATUSHANDLER_H_INCLUDED - -#include -#include -#include -#include "../lib/status_handler.h" -#include "../lib/process_xml.h" -#include "progress_indicator.h" -#include "switch_to_gui.h" -#include "lib/return_codes.h" - - -//Exception class used to abort the "compare" and "sync" process -class BatchAbortProcess {}; - - -//BatchStatusHandler(SyncProgressDialog) will internally process Window messages! disable GUI controls to avoid unexpected callbacks! -class BatchStatusHandler : public zen::StatusHandler //throw BatchAbortProcess -{ -public: - BatchStatusHandler(bool showProgress, //defines: -start minimized and -quit immediately when finished - const std::wstring& jobName, //should not be empty for a batch job! - const zen::TimeComp& timeStamp, - const Zstring& logfileDirectory, - int logfilesCountLimit, //0: logging inactive; < 0: no limit - size_t lastSyncsLogFileSizeMax, - const xmlAccess::OnError handleError, - size_t automaticRetryCount, - size_t automaticRetryDelay, - const zen::SwitchToGui& switchBatchToGui, //functionality to change from batch mode to GUI mode - zen::FfsReturnCode& returnCode, - const std::wstring& execWhenFinished, - std::vector& execFinishedHistory); - ~BatchStatusHandler(); - - virtual void initNewPhase (int objectsTotal, zen::Int64 dataTotal, Phase phaseID); - virtual void updateProcessedData(int objectsDelta, zen::Int64 dataDelta); - virtual void reportInfo(const std::wstring& text); - virtual void forceUiRefresh(); - - virtual void reportWarning(const std::wstring& warningMessage, bool& warningActive); - virtual Response reportError(const std::wstring& errorMessage, size_t retryNumber); - virtual void reportFatalError(const std::wstring& errorMessage); - -private: - virtual void abortThisProcess(); //throw BatchAbortProcess - void onProgressDialogTerminate(); - - const zen::SwitchToGui& switchBatchToGui_; //functionality to change from batch mode to GUI mode - bool showFinalResults; - bool switchToGuiRequested; - const int logfilesCountLimit_; - const size_t lastSyncsLogFileSizeMax_; - xmlAccess::OnError handleError_; - zen::ErrorLog errorLog; //list of non-resolved errors and warnings - zen::FfsReturnCode& returnCode_; - - const size_t automaticRetryCount_; - const size_t automaticRetryDelay_; - - SyncProgressDialog* progressDlg; //managed to have shorter lifetime than this handler! - std::unique_ptr logFile; //optional! - - const std::wstring jobName_; - wxStopWatch totalTime; -}; - - -#endif // BATCHSTATUSHANDLER_H_INCLUDED diff --git a/ui/check_version.cpp b/ui/check_version.cpp deleted file mode 100644 index 79fd2507..00000000 --- a/ui/check_version.cpp +++ /dev/null @@ -1,318 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "check_version.h" -//#include -#include -#include -#include -#include -#include -#include -#include "../version/version.h" -////#include "../lib/ffs_paths.h" -#include - -#ifdef ZEN_WIN -#include //tame wininet include -#include - -#elif defined ZEN_LINUX || defined ZEN_MAC -#include -#include -#endif - -using namespace zen; - - -namespace -{ -#ifdef ZEN_WIN -class InternetConnectionError {}; - -class WinInetAccess //using IE proxy settings! :) -{ -public: - WinInetAccess(const wchar_t* url) //throw InternetConnectionError (if url cannot be reached; no need to also call readBytes()) - { - //::InternetAttemptConnect(0) -> not working as expected: succeeds even when there is no internet connection! - - hInternet = ::InternetOpen(L"FreeFileSync", //_In_ LPCTSTR lpszAgent, - INTERNET_OPEN_TYPE_PRECONFIG, //_In_ DWORD dwAccessType, - nullptr, //_In_ LPCTSTR lpszProxyName, - nullptr, //_In_ LPCTSTR lpszProxyBypass, - 0); //_In_ DWORD dwFlags - if (!hInternet) - throw InternetConnectionError(); - zen::ScopeGuard guardInternet = zen::makeGuard([&] { ::InternetCloseHandle(hInternet); }); - - hRequest = ::InternetOpenUrl(hInternet, //_In_ HINTERNET hInternet, - url, //_In_ LPCTSTR lpszUrl, - nullptr, //_In_ LPCTSTR lpszHeaders, - 0, //_In_ DWORD dwHeadersLength, - INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_UI, //_In_ DWORD dwFlags, - 0); //_In_ DWORD_PTR dwContext - if (!hRequest) //won't fail due to unreachable url here! There is no substitute for HTTP_QUERY_STATUS_CODE!!! - throw InternetConnectionError(); - zen::ScopeGuard guardRequest = zen::makeGuard([&] { ::InternetCloseHandle(hRequest); }); - - DWORD statusCode = 0; - DWORD bufferLength = sizeof(statusCode); - if (!::HttpQueryInfo(hRequest, //_In_ HINTERNET hRequest, - HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, //_In_ DWORD dwInfoLevel, - &statusCode, //_Inout_ LPVOID lpvBuffer, - &bufferLength, //_Inout_ LPDWORD lpdwBufferLength, - nullptr)) //_Inout_ LPDWORD lpdwIndex - throw InternetConnectionError(); - - if (statusCode != HTTP_STATUS_OK) - throw InternetConnectionError(); //e.g. 404 - HTTP_STATUS_NOT_FOUND - - guardRequest .dismiss(); - guardInternet.dismiss(); - } - - ~WinInetAccess() - { - ::InternetCloseHandle(hRequest); - ::InternetCloseHandle(hInternet); - } - - template - OutputIterator readBytes(OutputIterator result) //throw InternetConnectionError - { - //internet says "HttpQueryInfo() + HTTP_QUERY_CONTENT_LENGTH" not supported by all http servers... - const DWORD bufferSize = 64 * 1024; - std::vector buffer(bufferSize); - for (;;) - { - DWORD bytesRead = 0; - if (!::InternetReadFile(hRequest, //_In_ HINTERNET hFile, - &buffer[0], //_Out_ LPVOID lpBuffer, - bufferSize, //_In_ DWORD dwNumberOfBytesToRead, - &bytesRead)) //_Out_ LPDWORD lpdwNumberOfBytesRead - throw InternetConnectionError(); - if (bytesRead == 0) - return result; - - result = std::copy(buffer.begin(), buffer.begin() + bytesRead, result); - } - } - -private: - HINTERNET hInternet; - HINTERNET hRequest; -}; - - -inline -bool canAccessUrl(const wchar_t* url) //throw () -{ - try - { - (void)WinInetAccess(url); //throw InternetConnectionError - return true; - } - catch (const InternetConnectionError&) { return false; } -} - - -template inline -OutputIterator readBytesUrl(const wchar_t* url, OutputIterator result) //throw InternetConnectionError -{ - return WinInetAccess(url).readBytes(result); //throw InternetConnectionError -} -#endif - - -enum GetVerResult -{ - GET_VER_SUCCESS, - GET_VER_NO_CONNECTION, //no internet connection or just Sourceforge down? - GET_VER_PAGE_NOT_FOUND //version file seems to have moved! => trigger an update! -}; - -GetVerResult getOnlineVersion(wxString& version) //empty string on error; -{ -#ifdef ZEN_WIN - //internet access supporting proxy connections - std::vector output; - try - { - readBytesUrl(L"http://freefilesync.sourceforge.net/latest_version.txt", std::back_inserter(output)); //throw InternetConnectionError - } - catch (const InternetConnectionError&) - { - return canAccessUrl(L"http://sourceforge.net/") ? GET_VER_PAGE_NOT_FOUND : GET_VER_NO_CONNECTION; - } - - output.push_back('\0'); - version = utfCvrtTo(&output[0]); - return GET_VER_SUCCESS; - -#elif defined ZEN_LINUX || defined ZEN_MAC - wxWindowDisabler dummy; - - auto getStringFromUrl = [](const wxString& server, const wxString& page, int timeout, wxString* output) -> bool //true on successful connection - { - wxHTTP webAccess; - webAccess.SetHeader(L"content-type", L"text/html; charset=utf-8"); - webAccess.SetTimeout(timeout); //default: 10 minutes(WTF are these wxWidgets people thinking???)... - - if (webAccess.Connect(server)) //will *not* fail for non-reachable url here! - { - //wxApp::IsMainLoopRunning(); // should return true - - std::unique_ptr httpStream(webAccess.GetInputStream(page)); - //must be deleted BEFORE webAccess is closed - - if (httpStream && webAccess.GetError() == wxPROTO_NOERR) - { - if (output) - { - output->clear(); - wxStringOutputStream outStream(output); - httpStream->Read(outStream); - } - return true; - } - } - return false; - }; - - if (getStringFromUrl(L"freefilesync.sourceforge.net", L"/latest_version.txt", 5, &version)) - return GET_VER_SUCCESS; - - const bool canConnectToSf = getStringFromUrl(L"sourceforge.net", L"/", 1, nullptr); - return canConnectToSf ? GET_VER_PAGE_NOT_FOUND : GET_VER_NO_CONNECTION; -#endif -} - - -const wchar_t VERSION_SEP = L'.'; - -std::vector parseVersion(const wxString& version) -{ - std::vector digits = split(version, VERSION_SEP); - - std::vector output; - std::transform(digits.begin(), digits.end(), std::back_inserter(output), [&](const wxString& d) { return stringTo(d); }); - return output; -} - - -bool haveNewerVersion(const wxString& onlineVersion) -{ - std::vector current = parseVersion(zen::currentVersion); - std::vector online = parseVersion(onlineVersion); - - if (online.empty() || online[0] == 0) //online version may be "This website has been moved..." In this case better check for an update - return true; - - return std::lexicographical_compare(current.begin(), current.end(), - online .begin(), online .end()); -} -} - - -void zen::checkForUpdateNow(wxWindow* parent) -{ - wxString onlineVersion; - switch (getOnlineVersion(onlineVersion)) - { - case GET_VER_SUCCESS: - if (haveNewerVersion(onlineVersion)) - { - switch (showConfirmationDialog(parent, DialogInfoType::INFO, PopupDialogCfg(). - setTitle(_("Check for Program Updates")). - setMainInstructions(_("A new version of FreeFileSync is available:") + L" " + onlineVersion + L"\n\n" + _("Download now?")), - _("&Download"))) - { - case ConfirmationButton::DO_IT: - wxLaunchDefaultBrowser(L"http://freefilesync.sourceforge.net/get_latest.php"); - break; - case ConfirmationButton::CANCEL: - break; - } - } - else - showNotificationDialog(parent, DialogInfoType::INFO, PopupDialogCfg(). - setTitle(_("Check for Program Updates")). - setMainInstructions(_("FreeFileSync is up to date."))); - break; - - case GET_VER_NO_CONNECTION: - showNotificationDialog(parent, DialogInfoType::ERROR2, PopupDialogCfg(). - setTitle(("Check for Program Updates")). - setMainInstructions(_("Unable to connect to sourceforge.net."))); - break; - - case GET_VER_PAGE_NOT_FOUND: - switch (showConfirmationDialog(parent, DialogInfoType::ERROR2, PopupDialogCfg(). - setTitle(_("Check for Program Updates")). - setMainInstructions(_("Cannot find current FreeFileSync version number online. Do you want to check manually?")), - _("&Check"))) - { - case ConfirmationButton::DO_IT: - wxLaunchDefaultBrowser(L"http://freefilesync.sourceforge.net/"); - break; - case ConfirmationButton::CANCEL: - break; - } - break; - } -} - - -void zen::checkForUpdatePeriodically(wxWindow* parent, long& lastUpdateCheck, const std::function& onBeforeInternetAccess) -{ - if (lastUpdateCheck != -1) - { - if (wxGetLocalTime() >= lastUpdateCheck + 7 * 24 * 3600) //check weekly - { - onBeforeInternetAccess(); //notify client before (potentially) blocking some time - wxString onlineVersion; - switch (getOnlineVersion(onlineVersion)) - { - case GET_VER_SUCCESS: - lastUpdateCheck = wxGetLocalTime(); - - if (haveNewerVersion(onlineVersion)) - { - switch (showConfirmationDialog(parent, DialogInfoType::INFO, PopupDialogCfg(). - setTitle(_("Check for Program Updates")). - setMainInstructions(_("A new version of FreeFileSync is available:") + L" " + onlineVersion + L"\n\n" + _("Download now?")), - _("&Download"))) - { - case ConfirmationButton::DO_IT: - wxLaunchDefaultBrowser(L"http://freefilesync.sourceforge.net/get_latest.php"); - break; - case ConfirmationButton::CANCEL: - break; - } - } - break; - - case GET_VER_NO_CONNECTION: - break; //ignore this error - - case GET_VER_PAGE_NOT_FOUND: - switch (showConfirmationDialog(parent, DialogInfoType::ERROR2, PopupDialogCfg(). - setTitle(_("Check for Program Updates")). - setMainInstructions(_("Cannot find current FreeFileSync version number online. Do you want to check manually?")), - _("&Check"))) - { - case ConfirmationButton::DO_IT: - wxLaunchDefaultBrowser(L"http://freefilesync.sourceforge.net/"); - break; - case ConfirmationButton::CANCEL: - break; - } - break; - } - } - } -} diff --git a/ui/check_version.h b/ui/check_version.h deleted file mode 100644 index d2e7220f..00000000 --- a/ui/check_version.h +++ /dev/null @@ -1,20 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef UPDATEVERSION_H_INCLUDED -#define UPDATEVERSION_H_INCLUDED - -#include -#include - - -namespace zen -{ -void checkForUpdateNow(wxWindow* parent); -void checkForUpdatePeriodically(wxWindow* parent, long& lastUpdateCheck, const std::function& onBeforeInternetAccess); //-1: check never -} - -#endif // UPDATEVERSION_H_INCLUDED diff --git a/ui/column_attr.h b/ui/column_attr.h deleted file mode 100644 index 070e6dd8..00000000 --- a/ui/column_attr.h +++ /dev/null @@ -1,111 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef COL_ATTR_HEADER_189467891346732143214 -#define COL_ATTR_HEADER_189467891346732143214 - -#include - -namespace zen -{ -enum ColumnTypeRim -{ - COL_TYPE_DIRECTORY, - COL_TYPE_FULL_PATH, - COL_TYPE_REL_PATH, - COL_TYPE_FILENAME, - COL_TYPE_SIZE, - COL_TYPE_DATE, - COL_TYPE_EXTENSION -}; - -struct ColumnAttributeRim -{ - ColumnAttributeRim() : type_(COL_TYPE_DIRECTORY), offset_(0), stretch_(0), visible_(false) {} - ColumnAttributeRim(ColumnTypeRim type, int offset, int stretch, bool visible) : type_(type), offset_(offset), stretch_(stretch), visible_(visible) {} - - ColumnTypeRim type_; - int offset_; - int stretch_; - bool visible_; -}; - -warn_static("two stretched oclumsn: hide vergrert range!") -inline -std::vector getDefaultColumnAttributesLeft() -{ - std::vector attr; - attr.push_back(ColumnAttributeRim(COL_TYPE_FULL_PATH, 250, 0, false)); - attr.push_back(ColumnAttributeRim(COL_TYPE_DIRECTORY, 200, 0, false)); - attr.push_back(ColumnAttributeRim(COL_TYPE_REL_PATH, 200, 0, true)); - attr.push_back(ColumnAttributeRim(COL_TYPE_FILENAME, -280, 1, true)); //stretch to full width and substract sum of fixed size widths! - attr.push_back(ColumnAttributeRim(COL_TYPE_DATE, 112, 0, false)); - attr.push_back(ColumnAttributeRim(COL_TYPE_SIZE, 80, 0, true)); - attr.push_back(ColumnAttributeRim(COL_TYPE_EXTENSION, 60, 0, false)); - return attr; -} - -inline -std::vector getDefaultColumnAttributesRight() -{ - std::vector attr; - attr.push_back(ColumnAttributeRim(COL_TYPE_FULL_PATH, 250, 0, false)); - attr.push_back(ColumnAttributeRim(COL_TYPE_DIRECTORY, 200, 0, false)); - attr.push_back(ColumnAttributeRim(COL_TYPE_REL_PATH, 200, 0, false)); //already shown on left side - attr.push_back(ColumnAttributeRim(COL_TYPE_FILENAME, -80, 1, true)); //stretch to full width and substract sum of fixed size widths! - attr.push_back(ColumnAttributeRim(COL_TYPE_DATE, 112, 0, false)); - attr.push_back(ColumnAttributeRim(COL_TYPE_SIZE, 80, 0, true)); - attr.push_back(ColumnAttributeRim(COL_TYPE_EXTENSION, 60, 0, false)); - return attr; -} - -//------------------------------------------------------------------ - -enum ColumnTypeMiddle -{ - COL_TYPE_CHECKBOX, - COL_TYPE_CMP_CATEGORY, - COL_TYPE_SYNC_ACTION, -}; - -//------------------------------------------------------------------ - -enum ColumnTypeNavi -{ - COL_TYPE_NAVI_BYTES, - COL_TYPE_NAVI_DIRECTORY, - COL_TYPE_NAVI_ITEM_COUNT -}; - - -struct ColumnAttributeNavi -{ - ColumnAttributeNavi() : type_(COL_TYPE_NAVI_DIRECTORY), offset_(0), stretch_(0), visible_(false) {} - ColumnAttributeNavi(ColumnTypeNavi type, int offset, int stretch, bool visible) : type_(type), offset_(offset), stretch_(stretch), visible_(visible) {} - - ColumnTypeNavi type_; - int offset_; - int stretch_; - bool visible_; -}; - - -const bool defaultValueShowPercentage = true; -const ColumnTypeNavi defaultValueLastSortColumn = COL_TYPE_NAVI_BYTES; //remember sort on navigation panel -const bool defaultValueLastSortAscending = false; // - -inline -std::vector getDefaultColumnAttributesNavi() -{ - std::vector attr; - attr.push_back(ColumnAttributeNavi(COL_TYPE_NAVI_DIRECTORY, -120, 1, true)); //stretch to full width and substract sum of fixed size widths - attr.push_back(ColumnAttributeNavi(COL_TYPE_NAVI_ITEM_COUNT, 60, 0, true)); - attr.push_back(ColumnAttributeNavi(COL_TYPE_NAVI_BYTES, 60, 0, true)); //GTK needs a few pixels width more - return attr; -} -} - -#endif // COL_ATTR_HEADER_189467891346732143214 diff --git a/ui/custom_grid.cpp b/ui/custom_grid.cpp deleted file mode 100644 index 4f498a20..00000000 --- a/ui/custom_grid.cpp +++ /dev/null @@ -1,1800 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "custom_grid.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../file_hierarchy.h" - -using namespace zen; -using namespace gridview; - - -const wxEventType zen::EVENT_GRID_CHECK_ROWS = wxNewEventType(); -const wxEventType zen::EVENT_GRID_SYNC_DIRECTION = wxNewEventType(); - -namespace -{ -const wxColour COLOR_ORANGE (238, 201, 0); -const wxColour COLOR_GREY (212, 208, 200); -const wxColour COLOR_YELLOW (247, 252, 62); -const wxColour COLOR_YELLOW_LIGHT(253, 252, 169); -const wxColour COLOR_CMP_RED (255, 185, 187); -const wxColour COLOR_SYNC_BLUE (185, 188, 255); -const wxColour COLOR_SYNC_GREEN (196, 255, 185); -const wxColour COLOR_NOT_ACTIVE (228, 228, 228); //light grey - - -const Zstring ICON_FILE_FOLDER = Zstr("folder"); -const size_t ROW_COUNT_NO_DATA = 10; - -/* -class hierarchy: - GridDataBase - /|\ - ________________|________________ - | | - GridDataRim | - /|\ | - __________|__________ | - | | | - GridDataLeft GridDataRight GridDataMiddle -*/ - - - -void refreshCell(Grid& grid, size_t row, ColumnType colType) -{ - wxRect cellArea = grid.getCellArea(row, colType); //returns empty rect if column not found; absolute coordinates! - if (cellArea.height > 0) - { - cellArea.SetTopLeft(grid.CalcScrolledPosition(cellArea.GetTopLeft())); - grid.getMainWin().RefreshRect(cellArea, false); - } -} - - -std::pair getVisibleRows(const Grid& grid) //returns range [from, to) -{ - const wxSize clientSize = grid.getMainWin().GetClientSize(); - if (clientSize.GetHeight() > 0) - { - wxPoint topLeft = grid.CalcUnscrolledPosition(wxPoint(0, 0)); - wxPoint bottom = grid.CalcUnscrolledPosition(wxPoint(0, clientSize.GetHeight() - 1)); - - const ptrdiff_t rowCount = grid.getRowCount(); - const ptrdiff_t rowFrom = grid.getRowAtPos(topLeft.y); //return -1 for invalid position, rowCount if out of range - if (rowFrom >= 0) - { - const ptrdiff_t rowTo = grid.getRowAtPos(bottom.y); - if (0 <= rowTo && rowTo < rowCount) - return std::make_pair(rowFrom, rowTo + 1); - else - return std::make_pair(rowFrom, rowCount); - } - } - return std::make_pair(0, 0); -} - - -void fillBackgroundDefaultColorAlternating(wxDC& dc, const wxRect& rect, bool evenRowNumber) -{ - //alternate background color to improve readability (while lacking cell borders) - if (!evenRowNumber) - { - //accessibility, support high-contrast schemes => work with user-defined background color! - const auto backCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); - - auto incChannel = [](unsigned char c, int diff) { return static_cast(std::max(0, std::min(255, c + diff))); }; - - auto getAdjustedColor = [&](int diff) - { - return wxColor(incChannel(backCol.Red (), diff), - incChannel(backCol.Green(), diff), - incChannel(backCol.Blue (), diff)); - }; - - auto colorDist = [](const wxColor& lhs, const wxColor& rhs) //just some metric - { - return numeric::power<2>(static_cast(lhs.Red ()) - static_cast(rhs.Red ())) + - numeric::power<2>(static_cast(lhs.Green()) - static_cast(rhs.Green())) + - numeric::power<2>(static_cast(lhs.Blue ()) - static_cast(rhs.Blue ())); - }; - - const int signLevel = colorDist(backCol, *wxBLACK) < colorDist(backCol, *wxWHITE) ? 1 : -1; //brighten or darken - - const wxColor colOutter = getAdjustedColor(signLevel * 14); //just some very faint gradient to avoid visual distraction - const wxColor colInner = getAdjustedColor(signLevel * 11); // - - //clearArea(dc, rect, backColAlt); - - //add some nice background gradient - wxRect rectUpper = rect; - rectUpper.height /= 2; - wxRect rectLower = rect; - rectLower.y += rectUpper.height; - rectLower.height -= rectUpper.height; - dc.GradientFillLinear(rectUpper, colOutter, colInner, wxSOUTH); - dc.GradientFillLinear(rectLower, colOutter, colInner, wxNORTH); - } - else - clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); -} - - -Zstring getExtension(const Zstring& shortName) -{ - return contains(shortName, Zchar('.')) ? afterLast(shortName, Zchar('.')) : Zstring(); -}; - - -class IconUpdater; -class GridEventManager; -class GridDataLeft; -class GridDataRight; - -struct IconManager -{ - IconManager(GridDataLeft& provLeft, GridDataRight& provRight, IconBuffer::IconSize sz) : iconBuffer(sz), - iconUpdater(make_unique(provLeft, provRight, iconBuffer)) {} - - void startIconUpdater(); - IconBuffer& refIconBuffer() { return iconBuffer; } -private: - IconBuffer iconBuffer; - std::unique_ptr iconUpdater; //bind ownership to GridDataRim<>! -}; - -//######################################################################################################## - -class GridDataBase : public GridData -{ -public: - GridDataBase(Grid& grid, const std::shared_ptr& gridDataView) : grid_(grid), gridDataView_(gridDataView) {} - - void holdOwnership(const std::shared_ptr& evtMgr) { evtMgr_ = evtMgr; } - GridEventManager* getEventManager() { return evtMgr_.get(); } - -protected: - Grid& refGrid() { return grid_; } - const Grid& refGrid() const { return grid_; } - - const GridView* getGridDataView() const { return gridDataView_.get(); } - - const FileSystemObject* getRawData(size_t row) const - { - if (auto view = getGridDataView()) - return view->getObject(row); - return nullptr; - } - -private: - virtual size_t getRowCount() const - { - if (gridDataView_) - { - if (gridDataView_->rowsTotal() == 0) - return ROW_COUNT_NO_DATA; - return gridDataView_->rowsOnView(); - } - else - return ROW_COUNT_NO_DATA; - - //return std::max(MIN_ROW_COUNT, gridDataView_ ? gridDataView_->rowsOnView() : 0); - } - - std::shared_ptr evtMgr_; - Grid& grid_; - std::shared_ptr gridDataView_; -}; - -//######################################################################################################## - -template -class GridDataRim : public GridDataBase -{ -public: - GridDataRim(const std::shared_ptr& gridDataView, Grid& grid) : GridDataBase(grid, gridDataView) {} - - void setIconManager(const std::shared_ptr& iconMgr) { iconMgr_ = iconMgr; } - - void updateNewAndGetMissingIcons(std::list& newLoad) //loads all (not yet) drawn icons - { - //don't check too often! give worker thread some time to fetch data - if (iconMgr_) - { - const auto& rowsOnScreen = getVisibleRows(refGrid()); - - //loop over all visible rows - const ptrdiff_t firstRow = rowsOnScreen.first; - const ptrdiff_t rowCount = rowsOnScreen.second - firstRow; - - for (ptrdiff_t i = 0; i < rowCount; ++i) - { - //alternate when adding rows: first, last, first + 1, last - 1 ... -> Icon buffer will then load reversely, i.e. from inside out - const ptrdiff_t currentRow = firstRow + (i % 2 == 0 ? - i / 2 : - rowCount - 1 - (i - 1) / 2); - - if (isFailedLoad(currentRow)) //find failed attempts to load icon - { - const Zstring filename = getIconFile(currentRow); - if (!filename.empty() && filename != ICON_FILE_FOLDER) - { - //test if they are already loaded in buffer: - if (iconMgr_->refIconBuffer().readyForRetrieval(filename)) - { - //do a *full* refresh for *every* failed load to update partial DC updates while scrolling - refreshCell(refGrid(), currentRow, static_cast(COL_TYPE_FILENAME)); - setFailedLoad(currentRow, false); - } - else //not yet in buffer: mark for async. loading - newLoad.push_back(filename); - } - } - } - } - } - -private: - bool isFailedLoad(size_t row) const { return row < failedLoads.size() ? failedLoads[row] != 0 : false; } - - void setFailedLoad(size_t row, bool failed) - { - if (failed) //let's only pay for iconupdater when needed - if (iconMgr_) - iconMgr_->startIconUpdater(); - - if (failedLoads.size() != refGrid().getRowCount()) - failedLoads.resize(refGrid().getRowCount()); - - if (row < failedLoads.size()) - failedLoads[row] = failed; - } - -protected: - virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) override - { - if (enabled) - { - if (selected) - dc.GradientFillLinear(rect, getColorSelectionGradientFrom(), getColorSelectionGradientTo(), wxEAST); - //ignore focus - else - { - //alternate background color to improve readability (while lacking cell borders) - if (getRowDisplayType(row) == DISP_TYPE_NORMAL) - fillBackgroundDefaultColorAlternating(dc, rect, row % 2 == 0); - else - clearArea(dc, rect, getBackGroundColor(row)); - - //draw horizontal border if required - DisplayType dispTp = getRowDisplayType(row); - if (dispTp != DISP_TYPE_NORMAL && - dispTp == getRowDisplayType(row + 1)) - { - const wxColor colorGridLine = wxColour(192, 192, 192); //light grey - wxDCPenChanger dummy2(dc, wxPen(colorGridLine, 1, wxSOLID)); - dc.DrawLine(rect.GetBottomLeft(), rect.GetBottomRight() + wxPoint(1, 0)); - } - } - } - else - clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); - } - - wxColor getBackGroundColor(size_t row) const - { - //accessibility: always set both foreground AND background colors! - // => harmonize with renderCell()! - - switch (getRowDisplayType(row)) - { - case DISP_TYPE_NORMAL: - break; - case DISP_TYPE_FOLDER: - return COLOR_GREY; - case DISP_TYPE_SYMLINK: - return COLOR_ORANGE; - case DISP_TYPE_INACTIVE: - return COLOR_NOT_ACTIVE; - } - return wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); - } - -private: - enum DisplayType - { - DISP_TYPE_NORMAL, - DISP_TYPE_FOLDER, - DISP_TYPE_SYMLINK, - DISP_TYPE_INACTIVE, - }; - - DisplayType getRowDisplayType(size_t row) const - { - const FileSystemObject* fsObj = getRawData(row); - if (!fsObj ) - return DISP_TYPE_NORMAL; - - //mark filtered rows - if (!fsObj->isActive()) - return DISP_TYPE_INACTIVE; - - if (fsObj->isEmpty()) //always show not existing files/dirs/symlinks as empty - return DISP_TYPE_NORMAL; - - DisplayType output = DISP_TYPE_NORMAL; - //mark directories and symlinks - struct GetRowType : public FSObjectVisitor - { - GetRowType(DisplayType& result) : result_(result) {} - - virtual void visit(const FilePair& fileObj) {} - virtual void visit(const SymlinkPair& linkObj) - { - result_ = DISP_TYPE_SYMLINK; - } - virtual void visit(const DirPair& dirObj) - { - result_ = DISP_TYPE_FOLDER; - } - private: - DisplayType& result_; - } getType(output); - fsObj->accept(getType); - return output; - } - - virtual wxString getValue(size_t row, ColumnType colType) const - { - if (const FileSystemObject* fsObj = getRawData(row)) - { - struct GetTextValue : public FSObjectVisitor - { - GetTextValue(ColumnTypeRim colType, const FileSystemObject& fso) : colType_(colType), fsObj_(fso) {} - - virtual void visit(const FilePair& fileObj) - { - switch (colType_) - { - case COL_TYPE_FULL_PATH: - value = toWx(appendSeparator(beforeLast(fileObj.getBaseDirPf() + fileObj.getObjRelativeName(), FILE_NAME_SEPARATOR))); - break; - case COL_TYPE_FILENAME: //filename - value = toWx(fileObj.getShortName()); - break; - case COL_TYPE_REL_PATH: //relative path - value = toWx(beforeLast(fileObj.getObjRelativeName(), FILE_NAME_SEPARATOR)); //returns empty string if ch not found - break; - case COL_TYPE_DIRECTORY: - value = toWx(fileObj.getBaseDirPf()); - break; - case COL_TYPE_SIZE: //file size - if (!fsObj_.isEmpty()) - value = zen::toGuiString(fileObj.getFileSize()); - - // -> test file id - //if (!fsObj_.isEmpty()) - // value = toGuiString(fileObj.getFileId().second) + L" " + toGuiString(fileObj.getFileId().first); - break; - case COL_TYPE_DATE: //date - if (!fsObj_.isEmpty()) - value = zen::utcToLocalTimeString(fileObj.getLastWriteTime()); - break; - case COL_TYPE_EXTENSION: //file extension - value = toWx(getExtension(fileObj.getShortName())); - break; - } - } - - virtual void visit(const SymlinkPair& linkObj) - { - switch (colType_) - { - case COL_TYPE_FULL_PATH: - value = toWx(appendSeparator(beforeLast(linkObj.getBaseDirPf() + linkObj.getObjRelativeName(), FILE_NAME_SEPARATOR))); - break; - case COL_TYPE_FILENAME: //filename - value = toWx(linkObj.getShortName()); - break; - case COL_TYPE_REL_PATH: //relative path - value = toWx(beforeLast(linkObj.getObjRelativeName(), FILE_NAME_SEPARATOR)); //returns empty string if ch not found - break; - case COL_TYPE_DIRECTORY: - value = toWx(linkObj.getBaseDirPf()); - break; - case COL_TYPE_SIZE: //file size - if (!fsObj_.isEmpty()) - value = L"<" + _("Symlink") + L">"; - break; - case COL_TYPE_DATE: //date - if (!fsObj_.isEmpty()) - value = zen::utcToLocalTimeString(linkObj.getLastWriteTime()); - break; - case COL_TYPE_EXTENSION: //file extension - value = toWx(getExtension(linkObj.getShortName())); - break; - } - } - - virtual void visit(const DirPair& dirObj) - { - switch (colType_) - { - case COL_TYPE_FULL_PATH: - value = toWx(appendSeparator(beforeLast(dirObj.getBaseDirPf() + dirObj.getObjRelativeName(), FILE_NAME_SEPARATOR))); - break; - case COL_TYPE_FILENAME: - value = toWx(dirObj.getShortName()); - break; - case COL_TYPE_REL_PATH: - value = toWx(beforeLast(dirObj.getObjRelativeName(), FILE_NAME_SEPARATOR)); //returns empty string if ch not found - break; - case COL_TYPE_DIRECTORY: - value = toWx(dirObj.getBaseDirPf()); - break; - case COL_TYPE_SIZE: //file size - if (!fsObj_.isEmpty()) - value = L"<" + _("Folder") + L">"; - break; - case COL_TYPE_DATE: //date - if (!fsObj_.isEmpty()) - value = wxEmptyString; - break; - case COL_TYPE_EXTENSION: //file extension - value = wxEmptyString; - break; - } - } - ColumnTypeRim colType_; - wxString value; - - const FileSystemObject& fsObj_; - } getVal(static_cast(colType), *fsObj); - fsObj->accept(getVal); - return getVal.value; - } - //if data is not found: - return wxEmptyString; - } - - static const int GAP_SIZE = 2; - - virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool selected) override - { - wxRect rectTmp = rect; - - const bool isActive = [&]() -> bool - { - if (const FileSystemObject* fsObj = this->getRawData(row)) - return fsObj->isActive(); - return true; - }(); - - //draw file icon - if (static_cast(colType) == COL_TYPE_FILENAME && - iconMgr_) - { - rectTmp.x += GAP_SIZE; - rectTmp.width -= GAP_SIZE; - - const int iconSize = iconMgr_->refIconBuffer().getSize(); - if (rectTmp.GetWidth() >= iconSize) - { - // Partitioning: - // __________________________ - // | gap | icon | gap | text | - // -------------------------- - - const Zstring filename = getIconFile(row); - if (!filename.empty()) - { - wxBitmap fileIcon; - - //first check if it is a directory icon: - if (filename == ICON_FILE_FOLDER) - fileIcon = iconMgr_->refIconBuffer().genericDirIcon(); - else //retrieve file icon - { - if (Opt tmpIco = iconMgr_->refIconBuffer().retrieveFileIcon(filename)) - fileIcon = *tmpIco; - else - { - fileIcon = iconMgr_->refIconBuffer().genericFileIcon(); //better than nothing - setFailedLoad(row, true); //save status of failed icon load -> used for async. icon loading - //falsify only! we want to avoid writing incorrect success values when only partially updating the DC, e.g. when scrolling, - //see repaint behavior of ::ScrollWindow() function! - } - } - - if (fileIcon.IsOk()) - { - wxRect rectIcon = rectTmp; - rectIcon.width = iconSize; //support small thumbnail centering - if (isActive) - drawBitmapRtlNoMirror(dc, fileIcon, rectIcon, wxALIGN_CENTER, buffer); - else - drawBitmapRtlNoMirror(dc, wxBitmap(fileIcon.ConvertToImage().ConvertToGreyscale(1.0 / 3, 1.0 / 3, 1.0 / 3)), //treat all channels equally! - rectIcon, wxALIGN_CENTER, buffer); - } - } - } - rectTmp.x += iconSize; - rectTmp.width -= iconSize; - } - - std::unique_ptr dummy3; - if (getRowDisplayType(row) != DISP_TYPE_NORMAL) - dummy3 = make_unique(dc, *wxBLACK); //accessibility: always set both foreground AND background colors! - - //draw text - if (static_cast(colType) == COL_TYPE_SIZE && refGrid().GetLayoutDirection() != wxLayout_RightToLeft) - { - //have file size right-justified (but don't change for RTL languages) - rectTmp.width -= GAP_SIZE; - drawCellText(dc, rectTmp, getValue(row, colType), isActive, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL); - } - else - { - rectTmp.x += GAP_SIZE; - rectTmp.width -= GAP_SIZE; - drawCellText(dc, rectTmp, getValue(row, colType), isActive, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); - } - } - - virtual int getBestSize(wxDC& dc, size_t row, ColumnType colType) override - { - // Partitioning: - // ________________________________ - // | gap | icon | gap | text | gap | - // -------------------------------- - - int bestSize = 0; - if (static_cast(colType) == COL_TYPE_FILENAME && iconMgr_) - bestSize += GAP_SIZE + iconMgr_->refIconBuffer().getSize(); - - bestSize += GAP_SIZE + dc.GetTextExtent(getValue(row, colType)).GetWidth() + GAP_SIZE; - - return bestSize; // + 1 pix for cell border line -> not used anymore! - } - - virtual wxString getColumnLabel(ColumnType colType) const - { - switch (static_cast(colType)) - { - case COL_TYPE_FULL_PATH: - return _("Full path"); - case COL_TYPE_FILENAME: - return _("Name"); //= short name - case COL_TYPE_REL_PATH: - return _("Relative path"); - case COL_TYPE_DIRECTORY: - return _("Base folder"); - case COL_TYPE_SIZE: - return _("Size"); - case COL_TYPE_DATE: - return _("Date"); - case COL_TYPE_EXTENSION: - return _("Extension"); - } - return wxEmptyString; - } - - virtual void renderColumnLabel(Grid& tree, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted) override - { - wxRect rectInside = drawColumnLabelBorder(dc, rect); - drawColumnLabelBackground(dc, rectInside, highlighted); - - rectInside.x += COLUMN_GAP_LEFT; - rectInside.width -= COLUMN_GAP_LEFT; - drawColumnLabelText(dc, rectInside, getColumnLabel(colType)); - - //draw sort marker - if (getGridDataView()) - { - auto sortInfo = getGridDataView()->getSortInfo(); - if (sortInfo) - { - if (colType == static_cast(sortInfo->type_) && (side == LEFT_SIDE) == sortInfo->onLeft_) - { - const wxBitmap& marker = getResourceImage(sortInfo->ascending_ ? L"sortAscending" : L"sortDescending"); - wxPoint markerBegin = rectInside.GetTopLeft() + wxPoint((rectInside.width - marker.GetWidth()) / 2, 0); - dc.DrawBitmap(marker, markerBegin, true); //respect 2-pixel gap - } - } - } - } - - Zstring getIconFile(size_t row) const //return ICON_FILE_FOLDER if row points to a folder - { - const FileSystemObject* fsObj = getRawData(row); - if (fsObj && !fsObj->isEmpty()) - { - struct GetIcon : public FSObjectVisitor - { - virtual void visit(const FilePair& fileObj) - { - iconName = fileObj.getFullName(); - } - virtual void visit(const SymlinkPair& linkObj) - { - iconName = linkObj.getFullName(); - } - virtual void visit(const DirPair& dirObj) - { - iconName = ICON_FILE_FOLDER; - } - - Zstring iconName; - } getIcon; - fsObj->accept(getIcon); - return getIcon.iconName; - } - return Zstring(); - } - - virtual wxString getToolTip(size_t row, ColumnType colType) const override - { - wxString toolTip; - - const FileSystemObject* fsObj = getRawData(row); - if (fsObj && !fsObj->isEmpty()) - { - toolTip = toWx(getGridDataView() && getGridDataView()->getFolderPairCount() > 1 ? - fsObj->getFullName() : - fsObj->getRelativeName()); - - struct AssembleTooltip : public FSObjectVisitor - { - AssembleTooltip(wxString& tipMsg) : tipMsg_(tipMsg) {} - - virtual void visit(const FilePair& fileObj) - { - tipMsg_ += L"\n" + - _("Size:") + L" " + zen::filesizeToShortString(to(fileObj.getFileSize())) + L"\n" + - _("Date:") + L" " + zen::utcToLocalTimeString(fileObj.getLastWriteTime()); - } - - virtual void visit(const SymlinkPair& linkObj) - { - tipMsg_ += L"\n" + - _("Date:") + L" " + zen::utcToLocalTimeString(linkObj.getLastWriteTime()); - } - - virtual void visit(const DirPair& dirObj) {} - - wxString& tipMsg_; - } assembler(toolTip); - fsObj->accept(assembler); - } - return toolTip; - } - - std::shared_ptr iconMgr_; //optional - std::vector failedLoads; //effectively a vector of size "number of rows" - std::unique_ptr buffer; //avoid costs of recreating this temporal variable -}; - - -class GridDataLeft : public GridDataRim -{ -public: - GridDataLeft(const std::shared_ptr& gridDataView, Grid& grid) : GridDataRim(gridDataView, grid) {} - - void setNavigationMarker(hash_set&& markedFilesAndLinks, - hash_set&& markedContainer) - { - markedFilesAndLinks_.swap(markedFilesAndLinks); - markedContainer_ .swap(markedContainer); - } - -private: - virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) override - { - GridDataRim::renderRowBackgound(dc, rect, row, enabled, selected); - - //mark rows selected on navigation grid: - if (enabled && !selected) - { - const bool markRow = [&]() -> bool - { - if (const FileSystemObject* fsObj = getRawData(row)) - { - if (markedFilesAndLinks_.find(fsObj) != markedFilesAndLinks_.end()) //mark files/links directly - return true; - - if (auto dirObj = dynamic_cast(fsObj)) - { - if (markedContainer_.find(dirObj) != markedContainer_.end()) //mark directories which *are* the given HierarchyObject* - return true; - } - - //mark all objects which have the HierarchyObject as *any* matching ancestor - const HierarchyObject* parent = &(fsObj->parent()); - for (;;) - { - if (markedContainer_.find(parent) != markedContainer_.end()) - return true; - - if (auto dirObj = dynamic_cast(parent)) - parent = &(dirObj->parent()); - else - break; - } - } - return false; - }(); - - if (markRow) - { - //const wxColor COLOR_TREE_SELECTION_GRADIENT = wxColor(101, 148, 255); //H:158 S:255 V:178 - const wxColor COLOR_TREE_SELECTION_GRADIENT = getColorSelectionGradientFrom(); - - wxRect rectTmp = rect; - rectTmp.width /= 20; - dc.GradientFillLinear(rectTmp, COLOR_TREE_SELECTION_GRADIENT, GridDataRim::getBackGroundColor(row), wxEAST); - } - } - } - - hash_set markedFilesAndLinks_; //mark files/symlinks directly within a container - hash_set markedContainer_; //mark full container including all child-objects - //DO NOT DEREFERENCE!!!! NOT GUARANTEED TO BE VALID!!! -}; - - -class GridDataRight : public GridDataRim -{ -public: - GridDataRight(const std::shared_ptr& gridDataView, Grid& grid) : GridDataRim(gridDataView, grid) {} -}; - -//######################################################################################################## - -class GridDataMiddle : public GridDataBase -{ -public: - GridDataMiddle(const std::shared_ptr& gridDataView, Grid& grid) : - GridDataBase(grid, gridDataView), - highlightSyncAction_(false), - toolTip(grid), //tool tip must not live longer than grid! - notch(getResourceImage(L"notch").ConvertToImage()) {} - - void onSelectBegin(const wxPoint& clientPos, size_t row, ColumnType colType) - { - if (row < refGrid().getRowCount()) - { - refGrid().clearSelection(false); //don't emit event, prevent recursion! - dragSelection = make_unique>(row, mousePosToBlock(clientPos, row, static_cast(colType))); - toolTip.hide(); //handle custom tooltip - } - } - - void onSelectEnd(size_t rowFirst, size_t rowLast) //we cannot reuse row from "onSelectBegin": if user is holding shift, this may now be in the middle of the range! - { - refGrid().clearSelection(false); //don't emit event, prevent recursion! - - //issue custom event - if (dragSelection) - { - if (rowFirst < rowLast && //may be empty? probably not in this context - rowLast <= refGrid().getRowCount()) - { - if (wxEvtHandler* evtHandler = refGrid().GetEventHandler()) - switch (dragSelection->second) - { - case BLOCKPOS_CHECK_BOX: - { - const FileSystemObject* fsObj = getRawData(dragSelection->first); - const bool setIncluded = fsObj ? !fsObj->isActive() : true; - - CheckRowsEvent evt(rowFirst, rowLast, setIncluded); - evtHandler->ProcessEvent(evt); - } - break; - case BLOCKPOS_LEFT: - { - SyncDirectionEvent evt(rowFirst, rowLast, SyncDirection::LEFT); - evtHandler->ProcessEvent(evt); - } - break; - case BLOCKPOS_MIDDLE: - { - SyncDirectionEvent evt(rowFirst, rowLast, SyncDirection::NONE); - evtHandler->ProcessEvent(evt); - } - break; - case BLOCKPOS_RIGHT: - { - SyncDirectionEvent evt(rowFirst, rowLast, SyncDirection::RIGHT); - evtHandler->ProcessEvent(evt); - } - break; - } - } - dragSelection.reset(); - } - - //update highlight and tooltip: on OS X no mouse movement event is generated after a mouse button click (unlike on Windows) - wxPoint clientPos = refGrid().getMainWin().ScreenToClient(wxGetMousePosition()); - onMouseMovement(clientPos); - } - - void onMouseMovement(const wxPoint& clientPos) - { - //manage block highlighting and custom tooltip - if (!dragSelection) - { - auto refreshHighlight = [&](size_t row) - { - refreshCell(refGrid(), row, static_cast(COL_TYPE_CHECKBOX)); - refreshCell(refGrid(), row, static_cast(COL_TYPE_SYNC_ACTION)); - }; - - const wxPoint& topLeftAbs = refGrid().CalcUnscrolledPosition(clientPos); - const size_t row = refGrid().getRowAtPos(topLeftAbs.y); //return -1 for invalid position, rowCount if one past the end - auto colInfo = refGrid().getColumnAtPos(topLeftAbs.x); //(column type, component position) - - if (row < refGrid().getRowCount() && colInfo) - { - if (highlight) refreshHighlight(highlight->row_); //refresh old highlight - - highlight = make_unique(row, mousePosToBlock(clientPos, row, static_cast(colInfo->first))); - - refreshHighlight(highlight->row_); - - //show custom tooltip - if (refGrid().getMainWin().GetClientRect().Contains(clientPos)) //cursor might have moved outside visible client area - showToolTip(row, static_cast(colInfo->first), refGrid().getMainWin().ClientToScreen(clientPos)); - } - else - onMouseLeave(); - } - } - - void onMouseLeave() //wxEVT_LEAVE_WINDOW does not respect mouse capture! - { - if (!dragSelection) - { - if (highlight) - { - refreshCell(refGrid(), highlight->row_, static_cast(COL_TYPE_CHECKBOX)); - refreshCell(refGrid(), highlight->row_, static_cast(COL_TYPE_SYNC_ACTION)); - highlight.reset(); - } - toolTip.hide(); //handle custom tooltip - } - } - - void highlightSyncAction(bool value) { highlightSyncAction_ = value; } - -private: - virtual wxString getValue(size_t row, ColumnType colType) const override - { - if (const FileSystemObject* fsObj = getRawData(row)) - switch (static_cast(colType)) - { - case COL_TYPE_CHECKBOX: - break; - case COL_TYPE_CMP_CATEGORY: - return getSymbol(fsObj->getCategory()); - case COL_TYPE_SYNC_ACTION: - return getSymbol(fsObj->getSyncOperation()); - } - return wxString(); - } - - virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) override - { - const FileSystemObject* fsObj = getRawData(row); - GridData::drawCellBackground(dc, rect, enabled, selected, highlightSyncAction_ ? - getBackGroundColorSyncAction(fsObj) : - getBackGroundColorCmpCategory(fsObj)); - } - - virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool selected) override - { - auto drawInactiveColumBackground = [&](const FileSystemObject& fsObj) - { - if (fsObj.isActive()) - fillBackgroundDefaultColorAlternating(dc, rect, row % 2 == 0); - else - clearArea(dc, rect, COLOR_NOT_ACTIVE); - }; - - switch (static_cast(colType)) - { - case COL_TYPE_CHECKBOX: - if (const FileSystemObject* fsObj = getRawData(row)) - { - wxRect rectInside = rect; - - //if sync action is shown draw notch on left side, right side otherwise - if (notch.GetHeight() != rectInside.GetHeight()) - notch.Rescale(notch.GetWidth(), rectInside.GetHeight()); - if (highlightSyncAction_) - { - drawBitmapRtlMirror(dc, notch, rectInside, wxALIGN_LEFT, buffer); - rectInside.x += notch.GetWidth(); - rectInside.width -= notch.GetWidth(); - } - else - { - //wxWidgets screws up again and has wxALIGN_RIGHT off by one pixel! -> use wxALIGN_LEFT instead - wxRect rectNotch(rectInside.x + rectInside.width - notch.GetWidth(), rectInside.y, - notch.GetWidth(), rectInside.height); - drawBitmapRtlMirror(dc, notch, rectNotch, wxALIGN_LEFT, buffer); - rectInside.width -= notch.GetWidth(); - } - - const bool rowHighlighted = dragSelection ? row == dragSelection->first : highlight ? row == highlight->row_ : false; - const BlockPosition highlightBlock = dragSelection ? dragSelection->second : highlight ? highlight->blockPos_ : BLOCKPOS_CHECK_BOX; - - if (rowHighlighted && highlightBlock == BLOCKPOS_CHECK_BOX) - drawBitmapRtlMirror(dc, getResourceImage(fsObj->isActive() ? L"checkboxTrueFocus" : L"checkboxFalseFocus"), rectInside, wxALIGN_CENTER, buffer); - else //default - drawBitmapRtlMirror(dc, getResourceImage(fsObj->isActive() ? L"checkboxTrue" : L"checkboxFalse" ), rectInside, wxALIGN_CENTER, buffer); - } - break; - - case COL_TYPE_CMP_CATEGORY: - if (const FileSystemObject* fsObj = getRawData(row)) - { - if (highlightSyncAction_) - drawInactiveColumBackground(*fsObj); - - if (!highlightSyncAction_) - drawBitmapRtlMirror(dc, getCmpResultImage(fsObj->getCategory()), rect, wxALIGN_CENTER, buffer); - else if (fsObj->getCategory() != FILE_EQUAL) //don't show = in both middle columns - drawBitmapRtlMirror(dc, greyScale(getCmpResultImage(fsObj->getCategory())), rect, wxALIGN_CENTER, buffer); - } - break; - - case COL_TYPE_SYNC_ACTION: - if (const FileSystemObject* fsObj = getRawData(row)) - { - if (!highlightSyncAction_) - drawInactiveColumBackground(*fsObj); - - const bool rowHighlighted = dragSelection ? row == dragSelection->first : highlight ? row == highlight->row_ : false; - const BlockPosition highlightBlock = dragSelection ? dragSelection->second : highlight ? highlight->blockPos_ : BLOCKPOS_CHECK_BOX; - - //synchronization preview - if (rowHighlighted && highlightBlock != BLOCKPOS_CHECK_BOX) - switch (highlightBlock) - { - case BLOCKPOS_CHECK_BOX: - break; - case BLOCKPOS_LEFT: - drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::LEFT)), rect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, buffer); - break; - case BLOCKPOS_MIDDLE: - drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::NONE)), rect, wxALIGN_CENTER, buffer); - break; - case BLOCKPOS_RIGHT: - drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::RIGHT)), rect, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, buffer); - break; - } - else //default - { - if (highlightSyncAction_) - drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->getSyncOperation()), rect, wxALIGN_CENTER, buffer); - else if (fsObj->getSyncOperation() != SO_EQUAL) //don't show = in both middle columns - drawBitmapRtlMirror(dc, greyScale(getSyncOpImage(fsObj->getSyncOperation())), rect, wxALIGN_CENTER, buffer); - } - } - break; - } - } - - virtual wxString getColumnLabel(ColumnType colType) const override - { - switch (static_cast(colType)) - { - case COL_TYPE_CHECKBOX: - break; - case COL_TYPE_CMP_CATEGORY: - return _("Category") + L" (F9)"; - case COL_TYPE_SYNC_ACTION: - return _("Action") + L" (F9)"; - } - return wxEmptyString; - } - - virtual wxString getToolTip(ColumnType colType) const override { return getColumnLabel(colType); } - - virtual void renderColumnLabel(Grid& tree, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted) override - { - switch (static_cast(colType)) - { - case COL_TYPE_CHECKBOX: - drawColumnLabelBackground(dc, rect, false); - break; - - case COL_TYPE_CMP_CATEGORY: - { - wxRect rectInside = drawColumnLabelBorder(dc, rect); - drawColumnLabelBackground(dc, rectInside, highlighted); - - const wxBitmap& cmpIcon = getResourceImage(L"compare_small"); - drawBitmapRtlNoMirror(dc, highlightSyncAction_ ? greyScale(cmpIcon) : cmpIcon, rectInside, wxALIGN_CENTER, buffer); - } - break; - - case COL_TYPE_SYNC_ACTION: - { - wxRect rectInside = drawColumnLabelBorder(dc, rect); - drawColumnLabelBackground(dc, rectInside, highlighted); - - const wxBitmap& syncIcon = getResourceImage(L"sync_small"); - drawBitmapRtlNoMirror(dc, highlightSyncAction_ ? syncIcon : greyScale(syncIcon), rectInside, wxALIGN_CENTER, buffer); - } - break; - } - } - - static wxColor getBackGroundColorSyncAction(const FileSystemObject* fsObj) - { - if (fsObj) - { - if (!fsObj->isActive()) - return COLOR_NOT_ACTIVE; - - switch (fsObj->getSyncOperation()) //evaluate comparison result and sync direction - { - case SO_DO_NOTHING: - return COLOR_NOT_ACTIVE; - case SO_EQUAL: - break; //usually white - - case SO_CREATE_NEW_LEFT: - case SO_OVERWRITE_LEFT: - case SO_DELETE_LEFT: - case SO_MOVE_LEFT_SOURCE: - case SO_MOVE_LEFT_TARGET: - case SO_COPY_METADATA_TO_LEFT: - return COLOR_SYNC_BLUE; - - case SO_CREATE_NEW_RIGHT: - case SO_OVERWRITE_RIGHT: - case SO_DELETE_RIGHT: - case SO_MOVE_RIGHT_SOURCE: - case SO_MOVE_RIGHT_TARGET: - case SO_COPY_METADATA_TO_RIGHT: - return COLOR_SYNC_GREEN; - - case SO_UNRESOLVED_CONFLICT: - return COLOR_YELLOW; - } - } - return wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); - } - - static wxColor getBackGroundColorCmpCategory(const FileSystemObject* fsObj) - { - if (fsObj) - { - if (!fsObj->isActive()) - return COLOR_NOT_ACTIVE; - - switch (fsObj->getCategory()) - { - case FILE_LEFT_SIDE_ONLY: - case FILE_LEFT_NEWER: - return COLOR_SYNC_BLUE; //COLOR_CMP_BLUE; - - case FILE_RIGHT_SIDE_ONLY: - case FILE_RIGHT_NEWER: - return COLOR_SYNC_GREEN; //COLOR_CMP_GREEN; - - case FILE_DIFFERENT: - return COLOR_CMP_RED; - case FILE_EQUAL: - break; //usually white - case FILE_CONFLICT: - case FILE_DIFFERENT_METADATA: //= sub-category of equal, but hint via background that sync direction follows conflict-setting - return COLOR_YELLOW; - //return COLOR_YELLOW_LIGHT; - } - } - return wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); - } - - enum BlockPosition //each cell can be divided into four blocks concerning mouse selections - { - BLOCKPOS_CHECK_BOX, - BLOCKPOS_LEFT, - BLOCKPOS_MIDDLE, - BLOCKPOS_RIGHT - }; - - //determine block position within cell - BlockPosition mousePosToBlock(const wxPoint& clientPos, size_t row, ColumnTypeMiddle colType) const - { - switch (static_cast(colType)) - { - case COL_TYPE_CHECKBOX: - case COL_TYPE_CMP_CATEGORY: - break; - - case COL_TYPE_SYNC_ACTION: - { - const int absX = refGrid().CalcUnscrolledPosition(clientPos).x; - - const wxRect rect = refGrid().getCellArea(row, static_cast(COL_TYPE_SYNC_ACTION)); //returns empty rect if column not found; absolute coordinates! - if (rect.width > 0 && rect.height > 0) - if (const FileSystemObject* const fsObj = getRawData(row)) - if (fsObj->getSyncOperation() != SO_EQUAL) //in sync-preview equal files shall be treated like a checkbox - // cell: - // ----------------------- - // | left | middle | right| - // ----------------------- - if (rect.GetX() <= absX) - { - if (absX < rect.GetX() + rect.GetWidth() / 3) - return BLOCKPOS_LEFT; - else if (absX < rect.GetX() + 2 * rect.GetWidth() / 3) - return BLOCKPOS_MIDDLE; - else if (absX < rect.GetX() + rect.GetWidth()) - return BLOCKPOS_RIGHT; - } - } - break; - } - return BLOCKPOS_CHECK_BOX; - } - - void showToolTip(size_t row, ColumnTypeMiddle colType, wxPoint posScreen) - { - if (const FileSystemObject* fsObj = getRawData(row)) - { - bool showTooltipSyncAction = true; - switch (colType) - { - case COL_TYPE_CHECKBOX: - showTooltipSyncAction = highlightSyncAction_; - break; - case COL_TYPE_CMP_CATEGORY: - showTooltipSyncAction = false; - break; - case COL_TYPE_SYNC_ACTION: - break; - } - - if (showTooltipSyncAction) //synchronization preview - { - const wchar_t* imageName = [&]() -> const wchar_t* - { - const SyncOperation syncOp = fsObj->getSyncOperation(); - switch (syncOp) - { - case SO_CREATE_NEW_LEFT: - return L"so_create_left"; - case SO_CREATE_NEW_RIGHT: - return L"so_create_right"; - case SO_DELETE_LEFT: - return L"so_delete_left"; - case SO_DELETE_RIGHT: - return L"so_delete_right"; - case SO_MOVE_LEFT_SOURCE: - return L"so_move_left_source"; - case SO_MOVE_LEFT_TARGET: - return L"so_move_left_target"; - case SO_MOVE_RIGHT_SOURCE: - return L"so_move_right_source"; - case SO_MOVE_RIGHT_TARGET: - return L"so_move_right_target"; - case SO_OVERWRITE_LEFT: - return L"so_update_left"; - case SO_OVERWRITE_RIGHT: - return L"so_update_right"; - case SO_COPY_METADATA_TO_LEFT: - return L"so_move_left"; - case SO_COPY_METADATA_TO_RIGHT: - return L"so_move_right"; - case SO_DO_NOTHING: - return L"so_none"; - case SO_EQUAL: - return L"cat_equal"; - case SO_UNRESOLVED_CONFLICT: - return L"cat_conflict"; - }; - assert(false); - return L""; - }(); - const auto& img = mirrorIfRtl(getResourceImage(imageName)); - toolTip.show(getSyncOpDescription(*fsObj), posScreen, &img); - } - else - { - const wchar_t* imageName = [&]() -> const wchar_t* - { - const CompareFilesResult cmpRes = fsObj->getCategory(); - switch (cmpRes) - { - case FILE_LEFT_SIDE_ONLY: - return L"cat_left_only"; - case FILE_RIGHT_SIDE_ONLY: - return L"cat_right_only"; - case FILE_LEFT_NEWER: - return L"cat_left_newer"; - case FILE_RIGHT_NEWER: - return L"cat_right_newer"; - case FILE_DIFFERENT: - return L"cat_different"; - case FILE_EQUAL: - case FILE_DIFFERENT_METADATA: //= sub-category of equal - return L"cat_equal"; - case FILE_CONFLICT: - return L"cat_conflict"; - } - assert(false); - return L""; - }(); - const auto& img = mirrorIfRtl(getResourceImage(imageName)); - toolTip.show(getCategoryDescription(*fsObj), posScreen, &img); - } - } - else - toolTip.hide(); //if invalid row... - } - - bool highlightSyncAction_; - - struct MouseHighlight - { - MouseHighlight(size_t row, BlockPosition blockPos) : row_(row), blockPos_(blockPos) {} - const size_t row_; - const BlockPosition blockPos_; - }; - std::unique_ptr highlight; //current mouse highlight - std::unique_ptr> dragSelection; //(row, block); area clicked when beginning selection - std::unique_ptr buffer; //avoid costs of recreating this temporal variable - Tooltip toolTip; - wxImage notch; -}; - -//######################################################################################################## - -const wxEventType EVENT_ALIGN_SCROLLBARS = wxNewEventType(); - -class GridEventManager : private wxEvtHandler -{ -public: - GridEventManager(Grid& gridL, - Grid& gridC, - Grid& gridR, - GridDataMiddle& provMiddle) : - gridL_(gridL), gridC_(gridC), gridR_(gridR), scrollMaster(nullptr), - provMiddle_(provMiddle), - scrollbarUpdatePending(false) - { - gridL_.Connect(EVENT_GRID_COL_RESIZE, GridColumnResizeEventHandler(GridEventManager::onResizeColumnL), nullptr, this); - gridR_.Connect(EVENT_GRID_COL_RESIZE, GridColumnResizeEventHandler(GridEventManager::onResizeColumnR), nullptr, this); - - gridL_.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler (GridEventManager::onKeyDownL), nullptr, this); - gridC_.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler (GridEventManager::onKeyDownC), nullptr, this); - gridR_.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler (GridEventManager::onKeyDownR), nullptr, this); - - gridC_.getMainWin().Connect(wxEVT_MOTION, wxMouseEventHandler(GridEventManager::onCenterMouseMovement), nullptr, this); - gridC_.getMainWin().Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(GridEventManager::onCenterMouseLeave ), nullptr, this); - - gridC_.Connect(EVENT_GRID_MOUSE_LEFT_DOWN, GridClickEventHandler (GridEventManager::onCenterSelectBegin), nullptr, this); - gridC_.Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(GridEventManager::onCenterSelectEnd ), nullptr, this); - - //clear selection of other grid when selecting on - gridL_.Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(GridEventManager::onGridSelectionL), nullptr, this); - gridR_.Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(GridEventManager::onGridSelectionR), nullptr, this); - - //parallel grid scrolling: do NOT use DoPrepareDC() to align grids! GDI resource leak! Use regular paint event instead: - gridL_.getMainWin().Connect(wxEVT_PAINT, wxEventHandler(GridEventManager::onPaintGridL), nullptr, this); - gridC_.getMainWin().Connect(wxEVT_PAINT, wxEventHandler(GridEventManager::onPaintGridC), nullptr, this); - gridR_.getMainWin().Connect(wxEVT_PAINT, wxEventHandler(GridEventManager::onPaintGridR), nullptr, this); - - auto connectGridAccess = [&](Grid& grid, wxObjectEventFunction func) - { - grid.Connect(wxEVT_SCROLLWIN_TOP, func, nullptr, this); - grid.Connect(wxEVT_SCROLLWIN_BOTTOM, func, nullptr, this); - grid.Connect(wxEVT_SCROLLWIN_LINEUP, func, nullptr, this); - grid.Connect(wxEVT_SCROLLWIN_LINEDOWN, func, nullptr, this); - grid.Connect(wxEVT_SCROLLWIN_PAGEUP, func, nullptr, this); - grid.Connect(wxEVT_SCROLLWIN_PAGEDOWN, func, nullptr, this); - grid.Connect(wxEVT_SCROLLWIN_THUMBTRACK, func, nullptr, this); - //wxEVT_KILL_FOCUS -> there's no need to reset "scrollMaster" - //wxEVT_SET_FOCUS -> not good enough: - //e.g.: left grid has input, right grid is "scrollMaster" due to dragging scroll thumb via mouse. - //=> Next keyboard input on left does *not* emit focus change event, but still "scrollMaster" needs to change - //=> hook keyboard input instead of focus event: - grid.getMainWin().Connect(wxEVT_CHAR, func, nullptr, this); - grid.getMainWin().Connect(wxEVT_KEY_UP, func, nullptr, this); - grid.getMainWin().Connect(wxEVT_KEY_DOWN, func, nullptr, this); - }; - connectGridAccess(gridL_, wxEventHandler(GridEventManager::onGridAccessL)); // - connectGridAccess(gridC_, wxEventHandler(GridEventManager::onGridAccessC)); //connect *after* onKeyDown() in order to receive callback *before*!!! - connectGridAccess(gridR_, wxEventHandler(GridEventManager::onGridAccessR)); // - - Connect(EVENT_ALIGN_SCROLLBARS, wxEventHandler(GridEventManager::onAlignScrollBars), NULL, this); - } - - ~GridEventManager() { assert(!scrollbarUpdatePending); } - - void setScrollMaster(const Grid& grid) { scrollMaster = &grid; } - -private: - void onCenterSelectBegin(GridClickEvent& event) - { - - provMiddle_.onSelectBegin(event.GetPosition(), event.row_, event.colType_); - event.Skip(); - } - - void onCenterSelectEnd(GridRangeSelectEvent& event) - { - provMiddle_.onSelectEnd(event.rowFirst_, event.rowLast_); - event.Skip(); - } - - void onCenterMouseMovement(wxMouseEvent& event) - { - provMiddle_.onMouseMovement(event.GetPosition()); - event.Skip(); - } - - void onCenterMouseLeave(wxMouseEvent& event) - { - provMiddle_.onMouseLeave(); - event.Skip(); - } - - void onGridSelectionL(GridRangeSelectEvent& event) { onGridSelection(gridL_, gridR_); event.Skip(); } - void onGridSelectionR(GridRangeSelectEvent& event) { onGridSelection(gridR_, gridL_); event.Skip(); } - - void onGridSelection(const Grid& grid, Grid& other) - { - if (!wxGetKeyState(WXK_CONTROL)) //clear other grid unless user is holding CTRL - other.clearSelection(false); //don't emit event, prevent recursion! - } - - void onKeyDownL(wxKeyEvent& event) { onKeyDown(event, gridL_); } - void onKeyDownC(wxKeyEvent& event) { onKeyDown(event, gridC_); } - void onKeyDownR(wxKeyEvent& event) { onKeyDown(event, gridR_); } - - void onKeyDown(wxKeyEvent& event, const Grid& grid) - { - int keyCode = event.GetKeyCode(); - if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft) - { - if (keyCode == WXK_LEFT) - keyCode = WXK_RIGHT; - else if (keyCode == WXK_RIGHT) - keyCode = WXK_LEFT; - else if (keyCode == WXK_NUMPAD_LEFT) - keyCode = WXK_NUMPAD_RIGHT; - else if (keyCode == WXK_NUMPAD_RIGHT) - keyCode = WXK_NUMPAD_LEFT; - } - - //skip middle component when navigating via keyboard - - const auto row = grid.getGridCursor().first; - - if (event.ShiftDown()) - ; - else if (event.ControlDown()) - ; - else - switch (keyCode) - { - case WXK_LEFT: - case WXK_NUMPAD_LEFT: - gridL_.setGridCursor(row); - gridL_.SetFocus(); - //since key event is likely originating from right grid, we need to set scrollMaster manually! - scrollMaster = &gridL_; //onKeyDown is called *after* onGridAccessL()! - return; //swallow event - - case WXK_RIGHT: - case WXK_NUMPAD_RIGHT: - gridR_.setGridCursor(row); - gridR_.SetFocus(); - scrollMaster = &gridR_; - return; //swallow event - } - - event.Skip(); - } - - void onResizeColumnL(GridColumnResizeEvent& event) { resizeOtherSide(gridL_, gridR_, event.colType_, event.offset_); } - void onResizeColumnR(GridColumnResizeEvent& event) { resizeOtherSide(gridR_, gridL_, event.colType_, event.offset_); } - - void resizeOtherSide(const Grid& src, Grid& trg, ColumnType type, int offset) - { - //find stretch factor of resized column: type is unique due to makeConsistent()! - std::vector cfgSrc = src.getColumnConfig(); - auto it = std::find_if(cfgSrc.begin(), cfgSrc.end(), [&](Grid::ColumnAttribute& ca) { return ca.type_ == type; }); - if (it == cfgSrc.end()) - return; - const int stretchSrc = it->stretch_; - - //we do not propagate resizings on stretched columns to the other side: awkward user experience - if (stretchSrc > 0) - return; - - //apply resized offset to other side, but only if stretch factors match! - std::vector cfgTrg = trg.getColumnConfig(); - std::for_each(cfgTrg.begin(), cfgTrg.end(), [&](Grid::ColumnAttribute& ca) - { - if (ca.type_ == type && ca.stretch_ == stretchSrc) - ca.offset_ = offset; - }); - trg.setColumnConfig(cfgTrg); - } - - void onGridAccessL(wxEvent& event) { scrollMaster = &gridL_; event.Skip(); } - void onGridAccessC(wxEvent& event) { scrollMaster = &gridC_; event.Skip(); } - void onGridAccessR(wxEvent& event) { scrollMaster = &gridR_; event.Skip(); } - - void onPaintGridL(wxEvent& event) { onPaintGrid(gridL_); event.Skip(); } - void onPaintGridC(wxEvent& event) { onPaintGrid(gridC_); event.Skip(); } - void onPaintGridR(wxEvent& event) { onPaintGrid(gridR_); event.Skip(); } - - void onPaintGrid(const Grid& grid) - { - //align scroll positions of all three grids *synchronously* during paint event! (wxGTK has visible delay when this is done asynchronously, no delay on Windows) - - //determine lead grid - const Grid* lead = nullptr; - Grid* follow1 = nullptr; - Grid* follow2 = nullptr; - auto setGrids = [&](const Grid& l, Grid& f1, Grid& f2) { lead = &l; follow1 = &f1; follow2 = &f2; }; - - if (&gridC_ == scrollMaster) - setGrids(gridC_, gridL_, gridR_); - else if (&gridR_ == scrollMaster) - setGrids(gridR_, gridL_, gridC_); - else //default: left panel - setGrids(gridL_, gridC_, gridR_); - - //align other grids only while repainting the lead grid to avoid scrolling and updating a grid at the same time! - if (lead != &grid) return; - - auto scroll = [](Grid& target, int y) //support polling - { - //scroll vertically only - scrolling horizontally becomes annoying if left and right sides have different widths; - //e.g. h-scroll on left would be undone when scrolling vertically on right which doesn't have a h-scrollbar - int yOld = 0; - target.GetViewStart(nullptr, &yOld); - if (yOld != y) - target.Scroll(-1, y); //empirical test Windows/Ubuntu: this call does NOT trigger a wxEVT_SCROLLWIN event, which would incorrectly set "scrollMaster" to "&target"! - }; - int y = 0; - lead->GetViewStart(nullptr, &y); - scroll(*follow1, y); - scroll(*follow2, y); - - //harmonize placement of horizontal scrollbar to avoid grids getting out of sync! - //since this affects the grid that is currently repainted as well, we do work asynchronously! - //avoids at least this problem: remaining graphics artifact when changing from Grid::SB_SHOW_ALWAYS to Grid::SB_SHOW_NEVER at location of old scrollbar (Windows only) - - //perf note: send one async event at most, else they may accumulate and create perf issues, see grid.cpp - if (!scrollbarUpdatePending) - { - scrollbarUpdatePending = true; - wxCommandEvent alignEvent(EVENT_ALIGN_SCROLLBARS); - AddPendingEvent(alignEvent); //waits until next idle event - may take up to a second if the app is busy on wxGTK! - } - } - - void onAlignScrollBars(wxEvent& event) - { - ZEN_ON_SCOPE_EXIT(scrollbarUpdatePending = false); - assert(scrollbarUpdatePending); - - auto needsHorizontalScrollbars = [](const Grid& grid) -> bool - { - const wxWindow& mainWin = grid.getMainWin(); - return mainWin.GetVirtualSize().GetWidth() > mainWin.GetClientSize().GetWidth(); - //assuming Grid::updateWindowSizes() does its job well, this should suffice! - //CAVEAT: if horizontal and vertical scrollbar are circular dependent from each other - //(h-scrollbar is shown due to v-scrollbar consuming horizontal width, ect...) - //while in fact both are NOT needed, this special case results in a bogus need for scrollbars! - //see https://sourceforge.net/tracker/?func=detail&aid=3514183&group_id=234430&atid=1093083 - // => since we're outside the Grid abstraction, we should not duplicate code to handle this special case as it seems to be insignificant - }; - - Grid::ScrollBarStatus sbStatusX = needsHorizontalScrollbars(gridL_) || - needsHorizontalScrollbars(gridR_) ? - Grid::SB_SHOW_ALWAYS : Grid::SB_SHOW_NEVER; - gridL_.showScrollBars(sbStatusX, Grid::SB_SHOW_NEVER); - gridC_.showScrollBars(sbStatusX, Grid::SB_SHOW_NEVER); - gridR_.showScrollBars(sbStatusX, Grid::SB_SHOW_AUTOMATIC); - } - - Grid& gridL_; - Grid& gridC_; - Grid& gridR_; - - const Grid* scrollMaster; //for address check only; this needn't be the grid having focus! - //e.g. mouse wheel events should set window under cursor as scrollMaster, but *not* change focus - - GridDataMiddle& provMiddle_; - bool scrollbarUpdatePending; -}; -} - -//######################################################################################################## - -void gridview::init(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, const std::shared_ptr& gridDataView) -{ - auto provLeft_ = std::make_shared(gridDataView, gridLeft); - auto provMiddle_ = std::make_shared(gridDataView, gridCenter); - auto provRight_ = std::make_shared(gridDataView, gridRight); - - gridLeft .setDataProvider(provLeft_); //data providers reference grid => - gridCenter.setDataProvider(provMiddle_); //ownership must belong *exclusively* to grid! - gridRight .setDataProvider(provRight_); - - auto evtMgr = std::make_shared(gridLeft, gridCenter, gridRight, *provMiddle_); - provLeft_ ->holdOwnership(evtMgr); - provMiddle_->holdOwnership(evtMgr); - provRight_ ->holdOwnership(evtMgr); - - gridCenter.enableColumnMove (false); - gridCenter.enableColumnResize(false); - - gridCenter.showRowLabel(false); - gridRight .showRowLabel(false); - - //gridLeft .showScrollBars(Grid::SB_SHOW_AUTOMATIC, Grid::SB_SHOW_NEVER); -> redundant: configuration happens in GridEventManager::onAlignScrollBars() - //gridCenter.showScrollBars(Grid::SB_SHOW_NEVER, Grid::SB_SHOW_NEVER); - - const int widthCategory = 30; - const int widthCheckbox = getResourceImage(L"checkboxTrue").GetWidth() + 4 + getResourceImage(L"notch").GetWidth(); - const int widthAction = 45; - gridCenter.SetSize(widthCategory + widthCheckbox + widthAction, -1); - - std::vector attribMiddle; - attribMiddle.push_back(Grid::ColumnAttribute(static_cast(COL_TYPE_CMP_CATEGORY), widthCategory, 0, true)); - attribMiddle.push_back(Grid::ColumnAttribute(static_cast(COL_TYPE_CHECKBOX ), widthCheckbox, 0, true)); - attribMiddle.push_back(Grid::ColumnAttribute(static_cast(COL_TYPE_SYNC_ACTION ), widthAction, 0, true)); - gridCenter.setColumnConfig(attribMiddle); -} - - -namespace -{ -std::vector makeConsistent(const std::vector& attribs) -{ - std::set usedTypes; - - std::vector output; - //remove duplicates: required by GridEventManager::resizeOtherSide() to find corresponding column on other side - std::copy_if(attribs.begin(), attribs.end(), std::back_inserter(output), - [&](const ColumnAttributeRim& a) { return usedTypes.insert(a.type_).second; }); - - //make sure each type is existing! -> should *only* be a problem if user manually messes with globalsettings.xml - const auto& defAttr = getDefaultColumnAttributesLeft(); - std::copy_if(defAttr.begin(), defAttr.end(), std::back_inserter(output), - [&](const ColumnAttributeRim& a) { return usedTypes.insert(a.type_).second; }); - - return output; -} -} - -std::vector gridview::convertConfig(const std::vector& attribs) -{ - const auto& attribClean = makeConsistent(attribs); - - std::vector output; - std::transform(attribClean.begin(), attribClean.end(), std::back_inserter(output), - [&](const ColumnAttributeRim& ca) { return Grid::ColumnAttribute(static_cast(ca.type_), ca.offset_, ca.stretch_, ca.visible_); }); - - return output; -} - - -std::vector gridview::convertConfig(const std::vector& attribs) -{ - std::vector output; - - std::transform(attribs.begin(), attribs.end(), std::back_inserter(output), - [&](const Grid::ColumnAttribute& ca) { return ColumnAttributeRim(static_cast(ca.type_), ca.offset_, ca.stretch_, ca.visible_); }); - - return makeConsistent(output); -} - - -namespace -{ -class IconUpdater : private wxEvtHandler //update file icons periodically: use SINGLE instance to coordinate left and right grid in parallel -{ -public: - IconUpdater(GridDataLeft& provLeft, GridDataRight& provRight, IconBuffer& iconBuffer) : provLeft_(provLeft), provRight_(provRight), iconBuffer_(iconBuffer) - { - timer.Connect(wxEVT_TIMER, wxEventHandler(IconUpdater::loadIconsAsynchronously), nullptr, this); - } - - void start() { if (!timer.IsRunning()) timer.Start(50); } //timer interval in [ms] - -private: - void stop() { if ( timer.IsRunning()) timer.Stop(); } - - void loadIconsAsynchronously(wxEvent& event) //loads all (not yet) drawn icons - { - std::list newLoad; - provLeft_ .updateNewAndGetMissingIcons(newLoad); - provRight_.updateNewAndGetMissingIcons(newLoad); - iconBuffer_.setWorkload(newLoad); - - if (newLoad.empty()) //let's only pay for iconupdater when needed - stop(); - } - - GridDataLeft& provLeft_; - GridDataRight& provRight_; - IconBuffer& iconBuffer_; - wxTimer timer; -}; - - -//resolve circular linker dependencies -inline -void IconManager::startIconUpdater() { if (iconUpdater) iconUpdater->start(); } -} - - -void gridview::setupIcons(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, bool show, IconBuffer::IconSize sz) -{ - auto* provLeft = dynamic_cast(gridLeft .getDataProvider()); - auto* provRight = dynamic_cast(gridRight.getDataProvider()); - - if (provLeft && provRight) - { - int iconHeight = 0; - if (show) - { - auto iconMgr = std::make_shared(*provLeft, *provRight, sz); - provLeft ->setIconManager(iconMgr); - provRight->setIconManager(iconMgr); - iconHeight = iconMgr->refIconBuffer().getSize(); - } - else - { - provLeft ->setIconManager(nullptr); - provRight->setIconManager(nullptr); - iconHeight = IconBuffer::getSize(IconBuffer::SIZE_SMALL); - } - - const int newRowHeight = std::max(iconHeight, gridLeft.getMainWin().GetCharHeight()) + 1; //add some space - - gridLeft .setRowHeight(newRowHeight); - gridCenter.setRowHeight(newRowHeight); - gridRight .setRowHeight(newRowHeight); - } - else - assert(false); -} - - -void gridview::clearSelection(Grid& gridLeft, Grid& gridCenter, Grid& gridRight) -{ - gridLeft .clearSelection(); - gridCenter.clearSelection(); - gridRight .clearSelection(); -} - -void gridview::refresh(Grid& gridLeft, Grid& gridCenter, Grid& gridRight) -{ - gridLeft .Refresh(); - gridCenter.Refresh(); - gridRight .Refresh(); -} - - -void gridview::setScrollMaster(Grid& grid) -{ - if (auto prov = dynamic_cast(grid.getDataProvider())) - if (auto evtMgr = prov->getEventManager()) - { - evtMgr->setScrollMaster(grid); - return; - } - assert(false); -} - - -void gridview::setNavigationMarker(Grid& gridLeft, - hash_set&& markedFilesAndLinks, - hash_set&& markedContainer) -{ - if (auto provLeft = dynamic_cast(gridLeft.getDataProvider())) - provLeft->setNavigationMarker(std::move(markedFilesAndLinks), std::move(markedContainer)); - else - assert(false); - gridLeft.Refresh(); -} - - -void gridview::highlightSyncAction(Grid& gridCenter, bool value) -{ - if (auto provMiddle = dynamic_cast(gridCenter.getDataProvider())) - provMiddle->highlightSyncAction(value); - else - assert(false); -} - -wxBitmap zen::getSyncOpImage(SyncOperation syncOp) -{ - switch (syncOp) //evaluate comparison result and sync direction - { - case SO_CREATE_NEW_LEFT: - return getResourceImage(L"so_create_left_small"); - case SO_CREATE_NEW_RIGHT: - return getResourceImage(L"so_create_right_small"); - case SO_DELETE_LEFT: - return getResourceImage(L"so_delete_left_small"); - case SO_DELETE_RIGHT: - return getResourceImage(L"so_delete_right_small"); - case SO_MOVE_LEFT_SOURCE: - return getResourceImage(L"so_move_left_source_small"); - case SO_MOVE_LEFT_TARGET: - return getResourceImage(L"so_move_left_target_small"); - case SO_MOVE_RIGHT_SOURCE: - return getResourceImage(L"so_move_right_source_small"); - case SO_MOVE_RIGHT_TARGET: - return getResourceImage(L"so_move_right_target_small"); - case SO_OVERWRITE_LEFT: - return getResourceImage(L"so_update_left_small"); - case SO_OVERWRITE_RIGHT: - return getResourceImage(L"so_update_right_small"); - case SO_COPY_METADATA_TO_LEFT: - return getResourceImage(L"so_move_left_small"); - case SO_COPY_METADATA_TO_RIGHT: - return getResourceImage(L"so_move_right_small"); - case SO_DO_NOTHING: - return getResourceImage(L"so_none_small"); - case SO_EQUAL: - return getResourceImage(L"cat_equal_small"); - case SO_UNRESOLVED_CONFLICT: - return getResourceImage(L"cat_conflict_small"); - } - return wxNullBitmap; -} - - -wxBitmap zen::getCmpResultImage(CompareFilesResult cmpResult) -{ - switch (cmpResult) - { - case FILE_LEFT_SIDE_ONLY: - return getResourceImage(L"cat_left_only_small"); - case FILE_RIGHT_SIDE_ONLY: - return getResourceImage(L"cat_right_only_small"); - case FILE_LEFT_NEWER: - return getResourceImage(L"cat_left_newer_small"); - case FILE_RIGHT_NEWER: - return getResourceImage(L"cat_right_newer_small"); - case FILE_DIFFERENT: - return getResourceImage(L"cat_different_small"); - case FILE_EQUAL: - case FILE_DIFFERENT_METADATA: //= sub-category of equal - return getResourceImage(L"cat_equal_small"); - case FILE_CONFLICT: - return getResourceImage(L"cat_conflict_small"); - } - return wxNullBitmap; -} diff --git a/ui/custom_grid.h b/ui/custom_grid.h deleted file mode 100644 index 32beee01..00000000 --- a/ui/custom_grid.h +++ /dev/null @@ -1,82 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef CUSTOMGRID_H_INCLUDED -#define CUSTOMGRID_H_INCLUDED - -#include -#include "grid_view.h" -#include "column_attr.h" -#include "../lib/icon_buffer.h" - -namespace zen -{ -//setup grid to show grid view within three components: -namespace gridview -{ -void init(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, const std::shared_ptr& gridDataView); - -std::vector convertConfig(const std::vector& attribs); //+ make consistent -std::vector convertConfig(const std::vector& attribs); // - -void highlightSyncAction(Grid& gridCenter, bool value); - -void setupIcons(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, bool show, IconBuffer::IconSize sz); - -void clearSelection(Grid& gridLeft, Grid& gridCenter, Grid& gridRight); //clear all components -void refresh(Grid& gridLeft, Grid& gridCenter, Grid& gridRight); - -void setScrollMaster(Grid& grid); - -//mark rows selected in navigation/compressed tree and navigate to leading object -void setNavigationMarker(Grid& gridLeft, - hash_set&& markedFilesAndLinks,//mark files/symlinks directly within a container - hash_set&& markedContainer); //mark full container including child-objects -} - -wxBitmap getSyncOpImage(SyncOperation syncOp); -wxBitmap getCmpResultImage(CompareFilesResult cmpResult); - - -//---------- custom events for middle grid ---------- - -//(UN-)CHECKING ROWS FROM SYNCHRONIZATION -extern const wxEventType EVENT_GRID_CHECK_ROWS; -//SELECTING SYNC DIRECTION -extern const wxEventType EVENT_GRID_SYNC_DIRECTION; - -struct CheckRowsEvent : public wxCommandEvent -{ - CheckRowsEvent(size_t rowFirst, size_t rowLast, bool setIncluded) : wxCommandEvent(EVENT_GRID_CHECK_ROWS), rowFirst_(rowFirst), rowLast_(rowLast), setIncluded_(setIncluded) { assert(rowFirst <= rowLast); } - virtual wxEvent* Clone() const { return new CheckRowsEvent(*this); } - - const size_t rowFirst_; //selected range: [rowFirst_, rowLast_) - const size_t rowLast_; //range is empty when clearing selection - const bool setIncluded_; -}; - - -struct SyncDirectionEvent : public wxCommandEvent -{ - SyncDirectionEvent(size_t rowFirst, size_t rowLast, SyncDirection direction) : wxCommandEvent(EVENT_GRID_SYNC_DIRECTION), rowFirst_(rowFirst), rowLast_(rowLast), direction_(direction) { assert(rowFirst <= rowLast); } - virtual wxEvent* Clone() const { return new SyncDirectionEvent(*this); } - - const size_t rowFirst_; //see CheckRowsEvent - const size_t rowLast_; // - const SyncDirection direction_; -}; - -typedef void (wxEvtHandler::*CheckRowsEventFunction)(CheckRowsEvent&); -typedef void (wxEvtHandler::*SyncDirectionEventFunction)(SyncDirectionEvent&); - -#define CheckRowsEventHandler(func) \ - (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(CheckRowsEventFunction, &func) - -#define SyncDirectionEventHandler(func) \ - (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(SyncDirectionEventFunction, &func) -} - -#endif // CUSTOMGRID_H_INCLUDED diff --git a/ui/dir_name.cpp b/ui/dir_name.cpp deleted file mode 100644 index 022773e4..00000000 --- a/ui/dir_name.cpp +++ /dev/null @@ -1,274 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "dir_name.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../lib/resolve_path.h" -#include "folder_history_box.h" - -#ifdef ZEN_WIN -#include -#include -#include "IFileDialog_Vista\ifile_dialog.h" -#endif - -using namespace zen; - - -const wxEventType zen::EVENT_ON_DIR_SELECTED = wxNewEventType(); -const wxEventType zen::EVENT_ON_DIR_MANUAL_CORRECTION = wxNewEventType(); - -namespace -{ -void setDirectoryNameImpl(const wxString& dirname, wxWindow& tooltipWnd, wxStaticText* staticText) -{ - const wxString dirFormatted = utfCvrtTo(getFormattedDirectoryName(toZ(dirname))); //may block when resolving [] - - tooltipWnd.SetToolTip(nullptr); //workaround wxComboBox bug http://trac.wxwidgets.org/ticket/10512 / http://trac.wxwidgets.org/ticket/12659 - tooltipWnd.SetToolTip(dirFormatted); //who knows when the real bugfix reaches mere mortals via an official release... - - if (staticText) - { - //change static box label only if there is a real difference to what is shown in wxTextCtrl anyway - wxString dirNormalized = dirname; - trim(dirNormalized); - if (!dirNormalized.empty()) - if (!endsWith(dirNormalized, FILE_NAME_SEPARATOR)) - dirNormalized += FILE_NAME_SEPARATOR; - - staticText->SetLabel(dirNormalized == dirFormatted ? wxString(_("Drag && drop")) : dirFormatted); - } -} - - -void setDirectoryName(const wxString& dirname, - wxTextCtrl* txtCtrl, - wxWindow& tooltipWnd, - wxStaticText* staticText) //pointers are optional -{ - if (txtCtrl) - txtCtrl->ChangeValue(dirname); - setDirectoryNameImpl(dirname, tooltipWnd, staticText); -} - - -void setDirectoryName(const wxString& dirname, - FolderHistoryBox* comboBox, - wxWindow& tooltipWnd, - wxStaticText* staticText) //pointers are optional -{ - if (comboBox) - comboBox->setValue(dirname); - setDirectoryNameImpl(dirname, tooltipWnd, staticText); -} -} -//############################################################################################################## - -template -DirectoryName::DirectoryName(wxWindow& dropWindow, - wxButton& selectButton, - NameControl& dirName, - wxStaticText* staticText, - wxWindow* dropWindow2) : - dropWindow_(dropWindow), - dropWindow2_(dropWindow2), - selectButton_(selectButton), - dirName_(dirName), - staticText_(staticText) -{ - //prepare drag & drop - setupFileDrop(dropWindow_); - dropWindow_.Connect(EVENT_DROP_FILE, FileDropEventHandler(DirectoryName::onFilesDropped), nullptr, this); - - if (dropWindow2_) - { - setupFileDrop(*dropWindow2_); - dropWindow2_->Connect(EVENT_DROP_FILE, FileDropEventHandler(DirectoryName::onFilesDropped), nullptr, this); - } - - //keep dirPicker and dirName synchronous - dirName_ .Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler (DirectoryName::onMouseWheel), nullptr, this); - dirName_ .Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(DirectoryName::onWriteDirManually), nullptr, this); - selectButton_.Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DirectoryName::onSelectDir ), nullptr, this); -} - - -template -DirectoryName::~DirectoryName() -{ - dropWindow_.Disconnect(EVENT_DROP_FILE, FileDropEventHandler(DirectoryName::onFilesDropped), nullptr, this); - if (dropWindow2_) - dropWindow2_->Disconnect(EVENT_DROP_FILE, FileDropEventHandler(DirectoryName::onFilesDropped), nullptr, this); - - dirName_ .Disconnect(wxEVT_MOUSEWHEEL, wxMouseEventHandler (DirectoryName::onMouseWheel), nullptr, this); - dirName_ .Disconnect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(DirectoryName::onWriteDirManually), nullptr, this); - selectButton_.Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DirectoryName::onSelectDir ), nullptr, this); -} - - -template -void DirectoryName::onMouseWheel(wxMouseEvent& event) -{ - //for combobox: although switching through available items is wxWidgets default, this is NOT windows default, e.g. explorer - //additionally this will delete manual entries, although all the users wanted is scroll the parent window! - - //redirect to parent scrolled window! - wxWindow* wnd = &dirName_; - while ((wnd = wnd->GetParent()) != nullptr) //silence MSVC warning - if (dynamic_cast(wnd) != nullptr) - if (wxEvtHandler* evtHandler = wnd->GetEventHandler()) - { - evtHandler->AddPendingEvent(event); - break; - } - - // event.Skip(); -} - - -template -void DirectoryName::onFilesDropped(FileDropEvent& event) -{ - const auto& files = event.getFiles(); - if (files.empty()) - return; - - if (acceptDrop(files, event.getDropPosition(), event.getDropWindow())) - { - const wxString fileName = event.getFiles()[0]; - if (dirExists(toZ(fileName))) - setDirectoryName(fileName, &dirName_, dirName_, staticText_); - else - { - wxString parentName = beforeLast(fileName, utfCvrtTo(FILE_NAME_SEPARATOR)); //returns empty string if ch not found -#ifdef ZEN_WIN - if (endsWith(parentName, L":")) //volume name - parentName += FILE_NAME_SEPARATOR; -#endif - if (dirExists(toZ(parentName))) - setDirectoryName(parentName, &dirName_, dirName_, staticText_); - else //set original name unconditionally: usecase: inactive mapped network shares - setDirectoryName(fileName, &dirName_, dirName_, staticText_); - } - - //notify action invoked by user - wxCommandEvent dummy(EVENT_ON_DIR_SELECTED); - ProcessEvent(dummy); - } - else - event.Skip(); //let other handlers try!!! -} - - -template -void DirectoryName::onWriteDirManually(wxCommandEvent& event) -{ - setDirectoryName(event.GetString(), static_cast(nullptr), dirName_, staticText_); - - wxCommandEvent dummy(EVENT_ON_DIR_MANUAL_CORRECTION); - ProcessEvent(dummy); - event.Skip(); -} - - -template -void DirectoryName::onSelectDir(wxCommandEvent& event) -{ - wxString defaultDirname; //default selection for dir picker - { - const Zstring dirFmt = getFormattedDirectoryName(toZ(getName())); - if (!dirFmt.empty()) - { - //convert to Zstring first: we don't want to pass wxString by value and risk MT issues! - auto ft = async([=] { return zen::dirExists(dirFmt); }); - - if (ft.timed_wait(boost::posix_time::milliseconds(200)) && ft.get()) //potentially slow network access: wait 200ms at most - defaultDirname = utfCvrtTo(dirFmt); - } - } - - //wxDirDialog internally uses lame-looking SHBrowseForFolder(); we better use IFileDialog() instead! (remembers size and position!) - std::unique_ptr newFolder; -#ifdef ZEN_WIN - if (vistaOrLater()) - { - using namespace ifile; - const DllFun showFolderPicker(getDllName(), funName_showFolderPicker); - const DllFun freeString (getDllName(), funName_freeString); - if (showFolderPicker && freeString) - { - wchar_t* selectedFolder = nullptr; - wchar_t* errorMsg = nullptr; - bool cancelled = false; - ZEN_ON_SCOPE_EXIT(freeString(selectedFolder)); - ZEN_ON_SCOPE_EXIT(freeString(errorMsg)); - - const GuidProxy guid = { '\x0', '\x4a', '\xf9', '\x31', '\xb4', '\x92', '\x40', '\xa0', - '\x8d', '\xc2', '\xc', '\xa5', '\xef', '\x59', '\x6e', '\x3b' - }; //some random GUID => have Windows save IFileDialog state separately from other file/dir pickers! - - showFolderPicker(static_cast(selectButton_.GetHWND()), //in; ==HWND - defaultDirname.empty() ? static_cast(nullptr) : defaultDirname.c_str(), //in, optional! - &guid, - selectedFolder, //out: call freeString() after use! - cancelled, //out - errorMsg); //out, optional: call freeString() after use! - if (errorMsg) - { - showNotificationDialog(&dropWindow_, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(errorMsg)); - return; - } - if (cancelled || !selectedFolder) - return; - newFolder = make_unique(selectedFolder); - } - } -#endif - if (!newFolder.get()) - { - wxDirDialog dirPicker(&selectButton_, _("Select a folder"), defaultDirname); //put modal wxWidgets dialogs on stack: creating on freestore leads to memleak! - if (dirPicker.ShowModal() != wxID_OK) - return; - newFolder = make_unique(dirPicker.GetPath()); - } - - setDirectoryName(*newFolder, &dirName_, dirName_, staticText_); - - //notify action invoked by user - wxCommandEvent dummy(EVENT_ON_DIR_SELECTED); - ProcessEvent(dummy); -} - - -template -wxString DirectoryName::getName() const -{ - return dirName_.GetValue(); -} - - -template -void DirectoryName::setName(const wxString& dirname) -{ - setDirectoryName(dirname, &dirName_, dirName_, staticText_); -} - - -//explicit template instantiations -namespace zen -{ -template class DirectoryName; -template class DirectoryName; -} diff --git a/ui/dir_name.h b/ui/dir_name.h deleted file mode 100644 index 21f2b574..00000000 --- a/ui/dir_name.h +++ /dev/null @@ -1,64 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef DRAGANDDROP_H_INCLUDED -#define DRAGANDDROP_H_INCLUDED - -#include -#include -#include -#include -#include -#include - -namespace zen -{ -//handle drag and drop, tooltip, label and manual input, coordinating a wxWindow, wxButton, and wxComboBox/wxTextCtrl -/* -Reasons NOT to use wxDirPickerCtrl, but wxButton instead: - - Crash on GTK 2: http://favapps.wordpress.com/2012/06/11/freefilesync-crash-in-linux-when-syncing-solved/ - - still uses outdated ::SHBrowseForFolder() (even on Windows 7) - - selection dialog remembers size, but NOT position => if user enlarges window, the next time he opens the dialog it may leap out of visible screen - - hard-codes "Browse" button label -*/ - -extern const wxEventType EVENT_ON_DIR_SELECTED; //directory is changed by the user (except manual type-in) -extern const wxEventType EVENT_ON_DIR_MANUAL_CORRECTION; //manual type-in -//example: wnd.Connect(EVENT_ON_DIR_SELECTED, wxCommandEventHandler(MyDlg::OnDirSelected), nullptr, this); - -template //NameControl may be wxTextCtrl, FolderHistoryBox -class DirectoryName: public wxEvtHandler -{ -public: - DirectoryName(wxWindow& dropWindow, - wxButton& selectButton, - NameControl& dirName, - wxStaticText* staticText = nullptr, //optional - wxWindow* dropWindow2 = nullptr); // - - ~DirectoryName(); - - wxString getName() const; - void setName(const wxString& dirname); - -private: - virtual bool acceptDrop(const std::vector& droppedFiles, const wxPoint& clientPos, const wxWindow& wnd) { return true; }; //return true if drop should be processed - - void onMouseWheel (wxMouseEvent& event); - void onFilesDropped (FileDropEvent& event); - void onWriteDirManually(wxCommandEvent& event); - void onSelectDir (wxCommandEvent& event); - - wxWindow& dropWindow_; - wxWindow* dropWindow2_; - wxButton& selectButton_; - NameControl& dirName_; - wxStaticText* staticText_; //optional -}; -} - - -#endif // DRAGANDDROP_H_INCLUDED diff --git a/ui/exec_finished_box.cpp b/ui/exec_finished_box.cpp deleted file mode 100644 index cbbfa17d..00000000 --- a/ui/exec_finished_box.cpp +++ /dev/null @@ -1,279 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "exec_finished_box.h" -#include -#include -#include -#include -#ifdef ZEN_WIN -#include -#endif - -using namespace zen; - - -namespace -{ -const std::wstring cmdTxtCloseProgressDlg = L"Close progress dialog"; //special command //mark for extraction: _("Close progress dialog") - -const std::wstring separationLine(L"---------------------------------------------------------------------------------------------------------------"); - -std::vector> getDefaultCommands() //(gui name/command) pairs -{ - std::vector> output; - - auto addEntry = [&](const std::wstring& name, const std::wstring& value) { output.push_back(std::make_pair(name, value)); }; - -#ifdef ZEN_WIN - if (zen::vistaOrLater()) - { - addEntry(_("Standby" ), L"rundll32.exe powrprof.dll,SetSuspendState Sleep"); //suspend/Suspend to RAM/sleep - addEntry(_("Log off" ), L"shutdown /l"); - addEntry(_("Shut down"), L"shutdown /s /t 60"); - //addEntry(_("Hibernate"), L"shutdown /h"); //Suspend to disk -> Standby is better anyway - } - else //XP - { - addEntry(_("Standby"), L"rundll32.exe powrprof.dll,SetSuspendState"); //this triggers standby OR hibernate, depending on whether hibernate setting is active! - addEntry(_("Log off" ), L"shutdown -l"); - addEntry(_("Shut down"), L"shutdown -s -t 60"); - //no suspend on XP? - } - -#elif defined ZEN_LINUX - addEntry(_("Standby" ), L"sudo pm-suspend"); - addEntry(_("Log off" ), L"gnome-session-quit"); //alternative requiring admin: sudo killall Xorg - addEntry(_("Shut down"), L"dbus-send --print-reply --dest=org.gnome.SessionManager /org/gnome/SessionManager org.gnome.SessionManager.RequestShutdown"); - //alternative requiring admin: sudo shutdown -h 1 - //addEntry(_("Hibernate"), L"sudo pm-hibernate"); - //alternative: "pmi action suspend" and "pmi action hibernate", require "sudo apt-get install powermanagement-interaface" - -#elif defined ZEN_MAC - addEntry(_("Standby" ), L"osascript -e \'tell application \"System Events\" to sleep\'"); - addEntry(_("Log off" ), L"osascript -e \'tell application \"System Events\" to log out\'"); - addEntry(_("Shut down"), L"osascript -e \'tell application \"System Events\" to shut down\'"); -#endif - return output; -} - -const wxEventType wxEVT_VALIDATE_USER_SELECTION = wxNewEventType(); -} - - -bool isCloseProgressDlgCommand(const std::wstring& value) -{ - std::wstring tmp = value; - trim(tmp); - return tmp == cmdTxtCloseProgressDlg; -} - - -ExecFinishedBox::ExecFinishedBox(wxWindow* parent, - wxWindowID id, - const wxString& value, - const wxPoint& pos, - const wxSize& size, - int n, - const wxString choices[], - long style, - const wxValidator& validator, - const wxString& name) : - wxComboBox(parent, id, value, pos, size, n, choices, style, validator, name), - history_(nullptr), - historyMax_(0), - defaultCommands(getDefaultCommands()) -{ - //##################################### - /*##*/ SetMinSize(wxSize(150, -1)); //## workaround yet another wxWidgets bug: default minimum size is much too large for a wxComboBox - //##################################### - - Connect(wxEVT_KEY_DOWN, wxKeyEventHandler (ExecFinishedBox::OnKeyEvent ), nullptr, this); - Connect(wxEVT_LEFT_DOWN, wxEventHandler (ExecFinishedBox::OnUpdateList), nullptr, this); - Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(ExecFinishedBox::OnSelection ), nullptr, this); - Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler (ExecFinishedBox::OnMouseWheel), nullptr, this); - - Connect(wxEVT_VALIDATE_USER_SELECTION, wxCommandEventHandler(ExecFinishedBox::OnValidateSelection), nullptr, this); -} - - -void ExecFinishedBox::addItemHistory() -{ - if (history_) - { - std::wstring command = getValue(); - trim(command); - - bool skipCmd = command == separationLine || //do not add sep. line - command == cmdTxtCloseProgressDlg || //do not add special command - command.empty(); - - //do not add built-in commands to history - if (!skipCmd) - { - for (auto it = defaultCommands.begin(); it != defaultCommands.end(); ++it) - if (command == it->first || - command == it->second) - { - skipCmd = true; - break; - } - } - - if (!skipCmd) - history_->insert(history_->begin(), command); - - if (history_->size() > historyMax_) - history_->resize(historyMax_); - } -} - - -std::wstring ExecFinishedBox::getValue() const -{ - const std::wstring value = zen::copyStringTo(GetValue()); - - { - std::wstring tmp = value; - trim(tmp); - if (tmp == implementation::translate(cmdTxtCloseProgressDlg)) //have this symbolic constant translated properly - return cmdTxtCloseProgressDlg; - } - - return value; -} - - -void ExecFinishedBox::setValue(const std::wstring& value) -{ - std::wstring tmp = value; - trim(tmp); - - if (tmp == cmdTxtCloseProgressDlg) - setValueAndUpdateList(implementation::translate(cmdTxtCloseProgressDlg)); //have this symbolic constant translated properly - else - setValueAndUpdateList(value); -} - -//set value and update list are technically entangled: see potential bug description below -void ExecFinishedBox::setValueAndUpdateList(const std::wstring& value) -{ - //it may be a little lame to update the list on each mouse-button click, but it should be working and we dont't have to manipulate wxComboBox internals - - std::deque items; - - //1. special command - items.push_back(implementation::translate(cmdTxtCloseProgressDlg)); - - //2. built in commands - for (auto it = defaultCommands.begin(); it != defaultCommands.end(); ++it) - items.push_back(it->first); - - //3. history elements - if (history_ && !history_->empty()) - { - items.push_back(separationLine); - items.insert(items.end(), history_->begin(), history_->end()); - std::sort(items.end() - history_->size(), items.end()); - } - - //attention: if the target value is not part of the dropdown list, SetValue() will look for a string that *starts with* this value: - //e.g. if the dropdown list contains "222" SetValue("22") will erroneously set and select "222" instead, while "111" would be set correctly! - // -> by design on Windows! - if (std::find(items.begin(), items.end(), value) == items.end()) - { - if (!value.empty()) - items.push_front(separationLine); - items.push_front(value); - } - - Clear(); - std::for_each(items.begin(), items.end(), [&](const std::wstring& item) { this->Append(item); }); - //this->SetSelection(wxNOT_FOUND); //don't select anything - SetValue(value); //preserve main text! -} - - -void ExecFinishedBox::OnSelection(wxCommandEvent& event) -{ - wxCommandEvent dummy2(wxEVT_VALIDATE_USER_SELECTION); //we cannot replace built-in commands at this position in call stack, so defer to a later time! - if (auto handler = GetEventHandler()) - handler->AddPendingEvent(dummy2); - - event.Skip(); -} - - -void ExecFinishedBox::OnValidateSelection(wxCommandEvent& event) -{ - const auto& value = getValue(); - - if (value == separationLine) - setValueAndUpdateList(std::wstring()); - else - for (auto it = defaultCommands.begin(); it != defaultCommands.end(); ++it) - if (it->first == value) - return setValueAndUpdateList(it->second); //replace GUI name by actual command string -} - - -void ExecFinishedBox::OnUpdateList(wxEvent& event) -{ - setValue(getValue()); - event.Skip(); -} - - -void ExecFinishedBox::OnKeyEvent(wxKeyEvent& event) -{ - switch (event.GetKeyCode()) - { - case WXK_DELETE: - case WXK_NUMPAD_DELETE: - { - //try to delete the currently selected config history item - int pos = this->GetCurrentSelection(); - if (0 <= pos && pos < static_cast(this->GetCount()) && - //what a mess...: - (GetValue() != GetString(pos) || //avoid problems when a character shall be deleted instead of list item - GetValue() == wxEmptyString)) //exception: always allow removing empty entry - { - const std::wstring selValue = copyStringTo(GetString(pos)); - - if (history_ && std::find(history_->begin(), history_->end(), selValue) != history_->end()) //only history elements may be deleted - { - //save old (selected) value: deletion seems to have influence on this - const wxString currentVal = this->GetValue(); - //this->SetSelection(wxNOT_FOUND); - - //delete selected row - vector_remove_if(*history_, [&](const std::wstring& item) { return item == selValue; }); - - SetString(pos, wxString()); //in contrast to Delete(), this one does not kill the drop-down list and gives a nice visual feedback! - //Delete(pos); - - //(re-)set value - SetValue(currentVal); - } - - //eat up key event - return; - } - } - break; - - case WXK_UP: - case WXK_NUMPAD_UP: - case WXK_DOWN: - case WXK_NUMPAD_DOWN: - case WXK_PAGEUP: - case WXK_NUMPAD_PAGEUP: - case WXK_PAGEDOWN: - case WXK_NUMPAD_PAGEDOWN: - return; //swallow -> using these keys gives a weird effect due to this weird control - } - event.Skip(); -} diff --git a/ui/exec_finished_box.h b/ui/exec_finished_box.h deleted file mode 100644 index 2a69faef..00000000 --- a/ui/exec_finished_box.h +++ /dev/null @@ -1,60 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef EXEC_FINISHED_BOX_18947773210473214 -#define EXEC_FINISHED_BOX_18947773210473214 - -#include -#include -#include -#include -#include - -//combobox with history function + functionality to delete items (DEL) - -//special command -bool isCloseProgressDlgCommand(const std::wstring& value); - - -class ExecFinishedBox : public wxComboBox -{ -public: - ExecFinishedBox(wxWindow* parent, - wxWindowID id, - const wxString& value = wxEmptyString, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - int n = 0, - const wxString choices[] = nullptr, - long style = 0, - const wxValidator& validator = wxDefaultValidator, - const wxString& name = wxComboBoxNameStr); - - void initHistory(std::vector& history, size_t historyMax) { history_ = &history; historyMax_ = historyMax; } - void addItemHistory(); //adds current item to history - - // use these two accessors instead of GetValue()/SetValue(): - std::wstring getValue() const; - void setValue(const std::wstring& value); - //required for setting value correctly + Linux to ensure the dropdown is shown as being populated - -private: - void OnKeyEvent(wxKeyEvent& event); - void OnMouseWheel(wxMouseEvent& event) {} //swallow! this gives confusing UI feedback anyway - void OnSelection(wxCommandEvent& event); - void OnValidateSelection(wxCommandEvent& event); - void OnUpdateList(wxEvent& event); - - void setValueAndUpdateList(const std::wstring& value); - - std::vector* history_; - size_t historyMax_; - - const std::vector> defaultCommands; -}; - - -#endif //EXEC_FINISHED_BOX_18947773210473214 diff --git a/ui/folder_history_box.cpp b/ui/folder_history_box.cpp deleted file mode 100644 index 4676aa43..00000000 --- a/ui/folder_history_box.cpp +++ /dev/null @@ -1,137 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "folder_history_box.h" -#include -#include -#include -#include "../lib/resolve_path.h" -#ifdef ZEN_LINUX -#include -#endif - -using namespace zen; - - -FolderHistoryBox::FolderHistoryBox(wxWindow* parent, - wxWindowID id, - const wxString& value, - const wxPoint& pos, - const wxSize& size, - int n, - const wxString choices[], - long style, - const wxValidator& validator, - const wxString& name) : - wxComboBox(parent, id, value, pos, size, n, choices, style, validator, name) -{ - //##################################### - /*##*/ SetMinSize(wxSize(150, -1)); //## workaround yet another wxWidgets bug: default minimum size is much too large for a wxComboBox - //##################################### - - Connect(wxEVT_KEY_DOWN, wxKeyEventHandler (FolderHistoryBox::OnKeyEvent ), nullptr, this); - - warn_static("mac") - warn_static("linux") - -#if defined ZEN_WIN - //on Win, this mouse click event only fires, when clicking on the small down arrow, NOT when clicking on the text field - //thanks to wxWidgets' non-portability it's exactly the converse on Linux! - Connect(wxEVT_LEFT_DOWN, wxEventHandler(FolderHistoryBox::OnRequireHistoryUpdate), nullptr, this); -#elif defined ZEN_LINUX || defined ZEN_MAC - /* - we can't attach to wxEVT_COMMAND_TEXT_UPDATED, since setValueAndUpdateList() will implicitly emit wxEVT_COMMAND_TEXT_UPDATED again when calling Clear()! - => Crash on Suse/X11/wxWidgets 2.9.4 on startup (setting a flag to guard against recursion does not work, still crash) - - On OS attaching to wxEVT_LEFT_DOWN leads to occasional crashes, especially when double-clicking - */ -#endif - -#ifdef ZEN_LINUX - //file drag and drop directly into the text control unhelpfully inserts in format "file://.." - //1. this format's implementation is a mess: http://www.lephpfacile.com/manuel-php-gtk/tutorials.filednd.urilist.php - //2. even if we handle "drag-data-received" for "text/uri-list" this doesn't consider logic in dirname.cpp - //=> disable all drop events on the text control (disables text drop, too, but not a big loss) - //=> all drops are nicely propagated as regular file drop events like they should have been in the first place! - if (GtkWidget* widget = GetConnectWidget()) - ::gtk_drag_dest_unset(widget); -#endif -} - - -void FolderHistoryBox::OnRequireHistoryUpdate(wxEvent& event) -{ - setValueAndUpdateList(GetValue()); - event.Skip(); -} - -//set value and update list are technically entangled: see potential bug description below -void FolderHistoryBox::setValueAndUpdateList(const wxString& dirname) -{ - //populate selection list.... - std::vector dirList; - { - //add some aliases to allow user changing to volume name and back, if possible - std::vector aliases = getDirectoryAliases(toZ(dirname)); //may block when resolving [] - std::transform(aliases.begin(), aliases.end(), std::back_inserter(dirList), [](const Zstring& str) { return utfCvrtTo(str); }); - } - if (sharedHistory_.get()) - { - std::vector tmp = sharedHistory_->getList(); - std::sort(tmp.begin(), tmp.end(), LessFilename()); - - if (!dirList.empty() && !tmp.empty()) - dirList.push_back(FolderHistory::separationLine()); - - std::transform(tmp.begin(), tmp.end(), std::back_inserter(dirList), [](const Zstring& str) { return utfCvrtTo(str); }); - } - - //########################################################################################### - - //attention: if the target value is not part of the dropdown list, SetValue() will look for a string that *starts with* this value: - //e.g. if the dropdown list contains "222" SetValue("22") will erroneously set and select "222" instead, while "111" would be set correctly! - // -> by design on Windows! - if (std::find(dirList.begin(), dirList.end(), dirname) == dirList.end()) - dirList.insert(dirList.begin(), dirname); - - Clear(); //emits yet another wxEVT_COMMAND_TEXT_UPDATED on Suse/X11/wxWidgets 2.9.4!!! - std::for_each(dirList.begin(), dirList.end(), [&](const wxString& dir) { this->Append(dir); }); - //this->SetSelection(wxNOT_FOUND); //don't select anything - this->SetValue(dirname); //preserve main text! -} - - -void FolderHistoryBox::OnKeyEvent(wxKeyEvent& event) -{ - const int keyCode = event.GetKeyCode(); - if (keyCode == WXK_DELETE || keyCode == WXK_NUMPAD_DELETE) - { - //try to delete the currently selected config history item - int pos = this->GetCurrentSelection(); - if (0 <= pos && pos < static_cast(this->GetCount()) && - //what a mess...: - (GetValue() != GetString(pos) || //avoid problems when a character shall be deleted instead of list item - GetValue() == wxEmptyString)) //exception: always allow removing empty entry - { - //save old (selected) value: deletion seems to have influence on this - const wxString currentVal = this->GetValue(); - //this->SetSelection(wxNOT_FOUND); - - //delete selected row - if (sharedHistory_.get()) - sharedHistory_->delItem(toZ(GetString(pos))); - SetString(pos, wxString()); //in contrast to Delete(), this one does not kill the drop-down list and gives a nice visual feedback! - //Delete(pos); - - //(re-)set value - this->SetValue(currentVal); - - //eat up key event - return; - } - } - event.Skip(); -} diff --git a/ui/folder_history_box.h b/ui/folder_history_box.h deleted file mode 100644 index 9ffa2d74..00000000 --- a/ui/folder_history_box.h +++ /dev/null @@ -1,97 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef CUSTOMCOMBOBOX_H_INCLUDED -#define CUSTOMCOMBOBOX_H_INCLUDED - -#include -#include -#include -#include -#include - -//combobox with history function + functionality to delete items (DEL) - - -class FolderHistory -{ -public: - FolderHistory() : maxSize_(0) {} - - FolderHistory(const std::vector& dirnames, size_t maxSize) : - maxSize_(maxSize), - dirnames_(dirnames) - { - if (dirnames_.size() > maxSize_) //keep maximal size of history list - dirnames_.resize(maxSize_); - } - - const std::vector& getList() const { return dirnames_; } - - static const wxString separationLine() { return L"---------------------------------------------------------------------------------------------------------------"; } - - void addItem(const Zstring& dirname) - { - if (dirname.empty() || dirname == zen::utfCvrtTo(separationLine())) - return; - - Zstring nameTmp = dirname; - zen::trim(nameTmp); - - //insert new folder or put it to the front if already existing - auto it = std::find_if(dirnames_.begin(), dirnames_.end(), - [&](const Zstring& entry) { return ::EqualFilename()(entry, nameTmp); }); - - if (it != dirnames_.end()) - dirnames_.erase(it); - dirnames_.insert(dirnames_.begin(), nameTmp); - - if (dirnames_.size() > maxSize_) //keep maximal size of history list - dirnames_.resize(maxSize_); - } - - void delItem(const Zstring& dirname) { zen::vector_remove_if(dirnames_, [&](const Zstring& entry) { return ::EqualFilename()(entry, dirname); }); } - -private: - - size_t maxSize_; - std::vector dirnames_; -}; - - -class FolderHistoryBox : public wxComboBox -{ -public: - FolderHistoryBox(wxWindow* parent, - wxWindowID id, - const wxString& value = wxEmptyString, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - int n = 0, - const wxString choices[] = nullptr, - long style = 0, - const wxValidator& validator = wxDefaultValidator, - const wxString& name = wxComboBoxNameStr); - - void init(const std::shared_ptr& sharedHistory) { sharedHistory_ = sharedHistory; } - - void setValue(const wxString& dirname) - { - setValueAndUpdateList(dirname); //required for setting value correctly; Linux: ensure the dropdown is shown as being populated - } - - // GetValue - -private: - void OnKeyEvent(wxKeyEvent& event); - void OnRequireHistoryUpdate(wxEvent& event); - void setValueAndUpdateList(const wxString& dirname); - - std::shared_ptr sharedHistory_; -}; - - -#endif // CUSTOMCOMBOBOX_H_INCLUDED diff --git a/ui/folder_history_types.h b/ui/folder_history_types.h deleted file mode 100644 index 23766413..00000000 --- a/ui/folder_history_types.h +++ /dev/null @@ -1,25 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef FOLDER_HIST_TYPES_32481457137432143214 -#define FOLDER_HIST_TYPES_32481457137432143214 - -//#include -#include - - -namespace zen -{ -struct ConfigHistoryItem -{ - explicit ConfigHistoryItem(const Zstring& name) : configFile(name) {} - ConfigHistoryItem() {} - Zstring configFile; - //time_t lastSyncTime; -}; -} - -#endif //FOLDER_HIST_TYPES_32481457137432143214 diff --git a/ui/folder_pair.h b/ui/folder_pair.h deleted file mode 100644 index be01e79d..00000000 --- a/ui/folder_pair.h +++ /dev/null @@ -1,221 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef FOLDERPAIR_H_89341750847252345 -#define FOLDERPAIR_H_89341750847252345 - -#include -#include -#include -#include -#include -#include -#include "dir_name.h" -#include "small_dlgs.h" -#include "sync_cfg.h" -#include "../lib/norm_filter.h" -#include "../structures.h" - -namespace zen -{ -//basic functionality for handling alternate folder pair configuration: change sync-cfg/filter cfg, right-click context menu, button icons... - -template -class FolderPairPanelBasic : private wxEvtHandler -{ -public: - typedef std::shared_ptr AltCompCfgPtr; - typedef std::shared_ptr AltSyncCfgPtr; - - void setConfig(AltCompCfgPtr compConfig, AltSyncCfgPtr syncCfg, const FilterConfig& filter) - { - altCompConfig = compConfig; - altSyncConfig = syncCfg; - localFilter = filter; - refreshButtons(); - } - - AltCompCfgPtr getAltCompConfig() const { return altCompConfig; } - AltSyncCfgPtr getAltSyncConfig() const { return altSyncConfig; } - FilterConfig getAltFilterConfig() const { return localFilter; } - - - FolderPairPanelBasic(GuiPanel& basicPanel) : //takes reference on basic panel to be enhanced - basicPanel_(basicPanel) - { - //register events for removal of alternate configuration - basicPanel_.m_bpButtonAltCompCfg ->Connect(wxEVT_RIGHT_DOWN, wxCommandEventHandler(FolderPairPanelBasic::OnAltCompCfgContext ), nullptr, this); - basicPanel_.m_bpButtonAltSyncCfg ->Connect(wxEVT_RIGHT_DOWN, wxCommandEventHandler(FolderPairPanelBasic::OnAltSyncCfgContext ), nullptr, this); - basicPanel_.m_bpButtonLocalFilter->Connect(wxEVT_RIGHT_DOWN, wxCommandEventHandler(FolderPairPanelBasic::OnLocalFilterCfgContext), nullptr, this); - - basicPanel_.m_bpButtonAltCompCfg-> Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FolderPairPanelBasic::OnAltCompCfg ), nullptr, this); - basicPanel_.m_bpButtonAltSyncCfg-> Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FolderPairPanelBasic::OnAltSyncCfg ), nullptr, this); - basicPanel_.m_bpButtonLocalFilter->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FolderPairPanelBasic::OnLocalFilterCfg), nullptr, this); - - basicPanel_.m_bpButtonRemovePair->SetBitmapLabel(getResourceImage(L"item_remove")); - } - -private: - void refreshButtons() - { - if (altCompConfig.get()) - { - setImage(*basicPanel_.m_bpButtonAltCompCfg, getResourceImage(L"cfg_compare_small")); - basicPanel_.m_bpButtonAltCompCfg->SetToolTip(_("Alternate comparison settings") + L" (" + getVariantName(altCompConfig->compareVar) + L")"); - } - else - { - setImage(*basicPanel_.m_bpButtonAltCompCfg, greyScale(getResourceImage(L"cfg_compare_small"))); - basicPanel_.m_bpButtonAltCompCfg->SetToolTip(_("Alternate comparison settings")); - } - - if (altSyncConfig.get()) - { - setImage(*basicPanel_.m_bpButtonAltSyncCfg, getResourceImage(L"cfg_sync_small")); - basicPanel_.m_bpButtonAltSyncCfg->SetToolTip(_("Alternate synchronization settings") + L" (" + getVariantName(altSyncConfig->directionCfg.var) + L")"); - } - else - { - setImage(*basicPanel_.m_bpButtonAltSyncCfg, greyScale(getResourceImage(L"cfg_sync_small"))); - basicPanel_.m_bpButtonAltSyncCfg->SetToolTip(_("Alternate synchronization settings")); - } - - if (!isNullFilter(localFilter)) - { - setImage(*basicPanel_.m_bpButtonLocalFilter, getResourceImage(L"filter_small")); - basicPanel_.m_bpButtonLocalFilter->SetToolTip(_("Local filter") + L" (" + _("Active") + L")"); - } - else - { - setImage(*basicPanel_.m_bpButtonLocalFilter, greyScale(getResourceImage(L"filter_small"))); - basicPanel_.m_bpButtonLocalFilter->SetToolTip(_("Local filter") + L" (" + _("None") + L")"); - } - } - - void OnAltCompCfgContext(wxCommandEvent& event) - { - auto removeAltCompCfg = [&] - { - this->altCompConfig.reset(); //"this->" galore: workaround GCC compiler bugs - this->refreshButtons(); - this->onAltCompCfgChange(); - }; - - ContextMenu menu; - menu.addItem(_("Remove alternate settings"), removeAltCompCfg, nullptr, altCompConfig.get() != nullptr); - menu.popup(basicPanel_); - } - - void OnAltSyncCfgContext(wxCommandEvent& event) - { - auto removeAltSyncCfg = [&] - { - this->altSyncConfig.reset(); - this->refreshButtons(); - this->onAltSyncCfgChange(); - }; - - ContextMenu menu; - menu.addItem(_("Remove alternate settings"), removeAltSyncCfg, nullptr, altSyncConfig.get() != nullptr); - menu.popup(basicPanel_); - } - - void OnLocalFilterCfgContext(wxCommandEvent& event) - { - auto removeLocalFilterCfg = [&] - { - this->localFilter = FilterConfig(); - this->refreshButtons(); - this->onLocalFilterCfgChange(); - }; - - std::unique_ptr& filterCfgOnClipboard = getFilterCfgOnClipboardRef(); - - auto copyFilter = [&] { filterCfgOnClipboard = make_unique(this->localFilter); }; - auto pasteFilter = [&] - { - if (filterCfgOnClipboard) - { - this->localFilter = *filterCfgOnClipboard; - this->refreshButtons(); - this->onLocalFilterCfgChange(); - } - }; - - ContextMenu menu; - menu.addItem(_("Clear filter settings"), removeLocalFilterCfg, nullptr, !isNullFilter(localFilter)); - menu.addSeparator(); - menu.addItem( _("Copy"), copyFilter, nullptr, !isNullFilter(localFilter)); - menu.addItem( _("Paste"), pasteFilter, nullptr, filterCfgOnClipboard.get() != nullptr); - menu.popup(basicPanel_); - } - - - virtual MainConfiguration getMainConfig() const = 0; - virtual wxWindow* getParentWindow() = 0; - virtual std::unique_ptr& getFilterCfgOnClipboardRef() = 0; - - virtual void onAltCompCfgChange() = 0; - virtual void onAltSyncCfgChange() = 0; - virtual void onLocalFilterCfgChange() = 0; - - void OnAltCompCfg(wxCommandEvent& event) - { - const MainConfiguration mainCfg = getMainConfig(); - - CompConfig cmpCfg = altCompConfig.get() ? *altCompConfig : mainCfg.cmpConfig; - - if (showCompareCfgDialog(getParentWindow(), cmpCfg, _("Alternate Comparison Settings")) == ReturnSmallDlg::BUTTON_OKAY) - { - altCompConfig = std::make_shared(cmpCfg); - refreshButtons(); - onAltCompCfgChange(); - } - } - - void OnAltSyncCfg(wxCommandEvent& event) - { - const MainConfiguration mainCfg = getMainConfig(); - - CompConfig cmpCfg = altCompConfig.get() ? *altCompConfig : mainCfg.cmpConfig; - SyncConfig syncCfg = altSyncConfig.get() ? *altSyncConfig : mainCfg.syncCfg; - - if (showSyncConfigDlg(getParentWindow(), - cmpCfg.compareVar, - syncCfg, - _("Alternate Synchronization Settings"), - nullptr, - nullptr) == ReturnSyncConfig::BUTTON_OKAY) //optional input parameter - { - altSyncConfig = std::make_shared(syncCfg); - refreshButtons(); - onAltSyncCfgChange(); - } - } - - void OnLocalFilterCfg(wxCommandEvent& event) - { - FilterConfig localFiltTmp = localFilter; - - if (showFilterDialog(getParentWindow(), localFiltTmp, _("Local Filter")) == ReturnSmallDlg::BUTTON_OKAY) - { - localFilter = localFiltTmp; - refreshButtons(); - onLocalFilterCfgChange(); - } - } - - GuiPanel& basicPanel_; //panel to be enhanced by this template - - //alternate configuration attached to it - AltCompCfgPtr altCompConfig; //optional - AltSyncCfgPtr altSyncConfig; // - FilterConfig localFilter; -}; -} - - -#endif //FOLDERPAIR_H_89341750847252345 diff --git a/ui/grid_view.cpp b/ui/grid_view.cpp deleted file mode 100644 index e1e16e47..00000000 --- a/ui/grid_view.cpp +++ /dev/null @@ -1,548 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "grid_view.h" -#include "sorting.h" -#include "../synchronization.h" -#include -//#include - -using namespace zen; - - -template -void getNumbers(const FileSystemObject& fsObj, StatusResult& result) -{ - struct GetValues : public FSObjectVisitor - { - GetValues(StatusResult& res) : result_(res) {} - - virtual void visit(const FilePair& fileObj) - { - if (!fileObj.isEmpty()) - { - result_.filesizeLeftView += fileObj.getFileSize(); - ++result_.filesOnLeftView; - } - if (!fileObj.isEmpty()) - { - result_.filesizeRightView += fileObj.getFileSize(); - ++result_.filesOnRightView; - } - } - - virtual void visit(const SymlinkPair& linkObj) - { - if (!linkObj.isEmpty()) - ++result_.filesOnLeftView; - - if (!linkObj.isEmpty()) - ++result_.filesOnRightView; - } - - virtual void visit(const DirPair& dirObj) - { - if (!dirObj.isEmpty()) - ++result_.foldersOnLeftView; - - if (!dirObj.isEmpty()) - ++result_.foldersOnRightView; - } - StatusResult& result_; - } getVal(result); - fsObj.accept(getVal); -} - - -template -void GridView::updateView(Predicate pred) -{ - viewRef.clear(); - rowPositions.clear(); - rowPositionsFirstChild.clear(); - - std::for_each(sortedRef.begin(), sortedRef.end(), - [&](const RefIndex& ref) - { - if (const FileSystemObject* fsObj = FileSystemObject::retrieve(ref.objId)) - if (pred(*fsObj)) - { - //save row position for direct random access to FilePair or DirPair - this->rowPositions.insert(std::make_pair(ref.objId, viewRef.size())); //costs: 0.28 s per call - MSVC based on std::set - //"this->" required by two-pass lookup as enforced by GCC 4.7 - - //save row position to identify first child *on sorted subview* of DirPair or BaseDirPair in case latter are filtered out - const HierarchyObject* parent = &fsObj->parent(); - for (;;) //map all yet unassociated parents to this row - { - const auto rv = this->rowPositionsFirstChild.insert(std::make_pair(parent, viewRef.size())); - if (!rv.second) - break; - - if (auto dirObj = dynamic_cast(parent)) - parent = &(dirObj->parent()); - else - break; - } - - //build subview - this->viewRef.push_back(ref.objId); - } - }); -} - - -ptrdiff_t GridView::findRowDirect(FileSystemObject::ObjectIdConst objId) const -{ - auto it = rowPositions.find(objId); - return it != rowPositions.end() ? it->second : -1; -} - -ptrdiff_t GridView::findRowFirstChild(const HierarchyObject* hierObj) const -{ - auto it = rowPositionsFirstChild.find(hierObj); - return it != rowPositionsFirstChild.end() ? it->second : -1; -} - - -GridView::StatusCmpResult::StatusCmpResult() : - existsLeftOnly (false), - existsRightOnly (false), - existsLeftNewer (false), - existsRightNewer(false), - existsDifferent (false), - existsEqual (false), - existsConflict (false), - filesOnLeftView (0), - foldersOnLeftView (0), - filesOnRightView (0), - foldersOnRightView(0) {} - - -GridView::StatusCmpResult GridView::updateCmpResult(bool hideFiltered, //maps sortedRef to viewRef - bool leftOnlyFilesActive, - bool rightOnlyFilesActive, - bool leftNewerFilesActive, - bool rightNewerFilesActive, - bool differentFilesActive, - bool equalFilesActive, - bool conflictFilesActive) -{ - StatusCmpResult output; - - updateView([&](const FileSystemObject& fsObj) -> bool - { - if (hideFiltered && !fsObj.isActive()) - return false; - - switch (fsObj.getCategory()) - { - case FILE_LEFT_SIDE_ONLY: - output.existsLeftOnly = true; - if (!leftOnlyFilesActive) return false; - break; - case FILE_RIGHT_SIDE_ONLY: - output.existsRightOnly = true; - if (!rightOnlyFilesActive) return false; - break; - case FILE_LEFT_NEWER: - output.existsLeftNewer = true; - if (!leftNewerFilesActive) return false; - break; - case FILE_RIGHT_NEWER: - output.existsRightNewer = true; - if (!rightNewerFilesActive) return false; - break; - case FILE_DIFFERENT: - output.existsDifferent = true; - if (!differentFilesActive) return false; - break; - case FILE_EQUAL: - case FILE_DIFFERENT_METADATA: //= sub-category of equal - output.existsEqual = true; - if (!equalFilesActive) return false; - break; - case FILE_CONFLICT: - output.existsConflict = true; - if (!conflictFilesActive) return false; - break; - } - //calculate total number of bytes for each side - getNumbers(fsObj, output); - return true; - }); - - return output; -} - - -GridView::StatusSyncPreview::StatusSyncPreview() : - existsSyncCreateLeft (false), - existsSyncCreateRight(false), - existsSyncDeleteLeft (false), - existsSyncDeleteRight(false), - existsSyncDirLeft (false), - existsSyncDirRight (false), - existsSyncDirNone (false), - existsSyncEqual (false), - existsConflict (false), - filesOnLeftView (0), - foldersOnLeftView (0), - filesOnRightView (0), - foldersOnRightView(0) {} - - -GridView::StatusSyncPreview GridView::updateSyncPreview(bool hideFiltered, //maps sortedRef to viewRef - bool syncCreateLeftActive, - bool syncCreateRightActive, - bool syncDeleteLeftActive, - bool syncDeleteRightActive, - bool syncDirOverwLeftActive, - bool syncDirOverwRightActive, - bool syncDirNoneActive, - bool syncEqualActive, - bool conflictFilesActive) -{ - StatusSyncPreview output; - - updateView([&](const FileSystemObject& fsObj) -> bool - { - if (hideFiltered && !fsObj.isActive()) - return false; - - switch (fsObj.getSyncOperation()) //evaluate comparison result and sync direction - { - case SO_CREATE_NEW_LEFT: - output.existsSyncCreateLeft = true; - if (!syncCreateLeftActive) return false; - break; - case SO_CREATE_NEW_RIGHT: - output.existsSyncCreateRight = true; - if (!syncCreateRightActive) return false; - break; - case SO_DELETE_LEFT: - output.existsSyncDeleteLeft = true; - if (!syncDeleteLeftActive) return false; - break; - case SO_DELETE_RIGHT: - output.existsSyncDeleteRight = true; - if (!syncDeleteRightActive) return false; - break; - case SO_OVERWRITE_RIGHT: - case SO_COPY_METADATA_TO_RIGHT: //no extra button on screen - case SO_MOVE_RIGHT_SOURCE: - case SO_MOVE_RIGHT_TARGET: - output.existsSyncDirRight = true; - if (!syncDirOverwRightActive) return false; - break; - case SO_OVERWRITE_LEFT: - case SO_COPY_METADATA_TO_LEFT: //no extra button on screen - case SO_MOVE_LEFT_TARGET: - case SO_MOVE_LEFT_SOURCE: - output.existsSyncDirLeft = true; - if (!syncDirOverwLeftActive) return false; - break; - case SO_DO_NOTHING: - output.existsSyncDirNone = true; - if (!syncDirNoneActive) return false; - break; - case SO_EQUAL: - output.existsSyncEqual = true; - if (!syncEqualActive) return false; - break; - case SO_UNRESOLVED_CONFLICT: - output.existsConflict = true; - if (!conflictFilesActive) return false; - break; - } - - //calculate total number of bytes for each side - getNumbers(fsObj, output); - return true; - }); - - return output; -} - - -std::vector GridView::getAllFileRef(const std::set& rows) -{ - std::vector output; - - auto iterLast = rows.lower_bound(rowsOnView()); //loop over valid rows only! - std::for_each(rows.begin(), iterLast, - [&](size_t pos) - { - if (FileSystemObject* fsObj = FileSystemObject::retrieve(viewRef[pos])) - output.push_back(fsObj); - }); - return output; -} - - -void GridView::removeInvalidRows() -{ - viewRef.clear(); - rowPositions.clear(); - rowPositionsFirstChild.clear(); - - //remove rows that have been deleted meanwhile - vector_remove_if(sortedRef, [&](const RefIndex& refIdx) { return FileSystemObject::retrieve(refIdx.objId) == nullptr; }); -} - - -class GridView::SerializeHierarchy -{ -public: - static void execute(HierarchyObject& hierObj, std::vector& sortedRef, size_t index) { SerializeHierarchy(sortedRef, index).recurse(hierObj); } - -private: - SerializeHierarchy(std::vector& sortedRef, size_t index) : - index_(index), - sortedRef_(sortedRef) {} - - void recurse(HierarchyObject& hierObj) - { - for (FilePair& fileObj : hierObj.refSubFiles()) - sortedRef_.push_back(RefIndex(index_, fileObj.getId())); - for (SymlinkPair& linkObj : hierObj.refSubLinks()) - sortedRef_.push_back(RefIndex(index_, linkObj.getId())); - for (DirPair& dirObj : hierObj.refSubDirs()) - { - sortedRef_.push_back(RefIndex(index_, dirObj.getId())); - recurse(dirObj); //add recursion here to list sub-objects directly below parent! - } - } - - size_t index_; - std::vector& sortedRef_; -}; - - -void GridView::setData(FolderComparison& folderCmp) -{ - //clear everything - std::vector().swap(viewRef); //free mem - std::vector().swap(sortedRef); // - currentSort.reset(); - - folderPairCount = std::count_if(begin(folderCmp), end(folderCmp), - [](const BaseDirPair& baseObj) //count non-empty pairs to distinguish single/multiple folder pair cases - { - return !baseObj.getBaseDirPf().empty() || - !baseObj.getBaseDirPf().empty(); - }); - - for (auto it = begin(folderCmp); it != end(folderCmp); ++it) - SerializeHierarchy::execute(*it, sortedRef, it - begin(folderCmp)); -} - - -//------------------------------------ SORTING TEMPLATES ------------------------------------------------ -template -class GridView::LessRelativeName : public std::binary_function -{ -public: - bool operator()(const RefIndex a, const RefIndex b) const - { - //presort by folder pair - if (a.folderIndex != b.folderIndex) - return ascending ? - a.folderIndex < b.folderIndex : - a.folderIndex > b.folderIndex; - - const FileSystemObject* fsObjA = FileSystemObject::retrieve(a.objId); - const FileSystemObject* fsObjB = FileSystemObject::retrieve(b.objId); - if (!fsObjA) //invalid rows shall appear at the end - return false; - else if (!fsObjB) - return true; - - return lessRelativeName(*fsObjA, *fsObjB); - } -}; - - -template -class GridView::LessShortFileName : public std::binary_function -{ -public: - bool operator()(const RefIndex a, const RefIndex b) const - { - const FileSystemObject* fsObjA = FileSystemObject::retrieve(a.objId); - const FileSystemObject* fsObjB = FileSystemObject::retrieve(b.objId); - if (!fsObjA) //invalid rows shall appear at the end - return false; - else if (!fsObjB) - return true; - - return lessShortFileName(*fsObjA, *fsObjB); - } -}; - - -template -class GridView::LessFilesize : public std::binary_function -{ -public: - bool operator()(const RefIndex a, const RefIndex b) const - { - const FileSystemObject* fsObjA = FileSystemObject::retrieve(a.objId); - const FileSystemObject* fsObjB = FileSystemObject::retrieve(b.objId); - if (!fsObjA) //invalid rows shall appear at the end - return false; - else if (!fsObjB) - return true; - - return lessFilesize(*fsObjA, *fsObjB); - } -}; - - -template -class GridView::LessFiletime : public std::binary_function -{ -public: - bool operator()(const RefIndex a, const RefIndex b) const - { - const FileSystemObject* fsObjA = FileSystemObject::retrieve(a.objId); - const FileSystemObject* fsObjB = FileSystemObject::retrieve(b.objId); - if (!fsObjA) //invalid rows shall appear at the end - return false; - else if (!fsObjB) - return true; - - return lessFiletime(*fsObjA, *fsObjB); - } -}; - - -template -class GridView::LessExtension : public std::binary_function -{ -public: - bool operator()(const RefIndex a, const RefIndex b) const - { - const FileSystemObject* fsObjA = FileSystemObject::retrieve(a.objId); - const FileSystemObject* fsObjB = FileSystemObject::retrieve(b.objId); - if (!fsObjA) //invalid rows shall appear at the end - return false; - else if (!fsObjB) - return true; - - return lessExtension(*fsObjA, *fsObjB); - } -}; - - -template -class GridView::LessCmpResult : public std::binary_function -{ -public: - bool operator()(const RefIndex a, const RefIndex b) const - { - const FileSystemObject* fsObjA = FileSystemObject::retrieve(a.objId); - const FileSystemObject* fsObjB = FileSystemObject::retrieve(b.objId); - if (!fsObjA) //invalid rows shall appear at the end - return false; - else if (!fsObjB) - return true; - - return lessCmpResult(*fsObjA, *fsObjB); - } -}; - - -template -class GridView::LessSyncDirection : public std::binary_function -{ -public: - bool operator()(const RefIndex a, const RefIndex b) const - { - const FileSystemObject* fsObjA = FileSystemObject::retrieve(a.objId); - const FileSystemObject* fsObjB = FileSystemObject::retrieve(b.objId); - if (!fsObjA) //invalid rows shall appear at the end - return false; - else if (!fsObjB) - return true; - - return lessSyncDirection(*fsObjA, *fsObjB); - } -}; - -//------------------------------------------------------------------------------------------------------- -bool GridView::getDefaultSortDirection(ColumnTypeRim type) //true: ascending; false: descending -{ - switch (type) - { - case COL_TYPE_SIZE: - case COL_TYPE_DATE: - return false; - - case COL_TYPE_DIRECTORY: - case COL_TYPE_FULL_PATH: - case COL_TYPE_REL_PATH: - case COL_TYPE_FILENAME: - case COL_TYPE_EXTENSION: - return true; - } - assert(false); - return true; -} - - -void GridView::sortView(ColumnTypeRim type, bool onLeft, bool ascending) -{ - viewRef.clear(); - rowPositions.clear(); - rowPositionsFirstChild.clear(); - currentSort = make_unique(type, onLeft, ascending); - - switch (type) - { - case COL_TYPE_FULL_PATH: - case COL_TYPE_REL_PATH: - if ( ascending) std::sort(sortedRef.begin(), sortedRef.end(), LessRelativeName()); - else if (!ascending) std::sort(sortedRef.begin(), sortedRef.end(), LessRelativeName()); - break; - case COL_TYPE_FILENAME: - if ( ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessShortFileName()); - else if ( ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessShortFileName()); - else if (!ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessShortFileName()); - else if (!ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessShortFileName()); - break; - case COL_TYPE_SIZE: - if ( ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFilesize()); - else if ( ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFilesize()); - else if (!ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFilesize()); - else if (!ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFilesize()); - break; - case COL_TYPE_DATE: - if ( ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFiletime()); - else if ( ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFiletime()); - else if (!ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFiletime()); - else if (!ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), LessFiletime()); - break; - case COL_TYPE_EXTENSION: - if ( ascending && onLeft) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessExtension()); - else if ( ascending && !onLeft) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessExtension()); - else if (!ascending && onLeft) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessExtension()); - else if (!ascending && !onLeft) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessExtension()); - break; - //case SORT_BY_CMP_RESULT: - // if ( ascending) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessCmpResult()); - // else if (!ascending) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessCmpResult()); - // break; - //case SORT_BY_SYNC_DIRECTION: - // if ( ascending) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessSyncDirection()); - // else if (!ascending) std::stable_sort(sortedRef.begin(), sortedRef.end(), LessSyncDirection()); - // break; - case COL_TYPE_DIRECTORY: - if ( ascending) std::stable_sort(sortedRef.begin(), sortedRef.end(), [](const RefIndex a, const RefIndex b) { return a.folderIndex < b.folderIndex; }); - else if (!ascending) std::stable_sort(sortedRef.begin(), sortedRef.end(), [](const RefIndex a, const RefIndex b) { return a.folderIndex > b.folderIndex; }); - break; - } -} diff --git a/ui/grid_view.h b/ui/grid_view.h deleted file mode 100644 index 0cef5dea..00000000 --- a/ui/grid_view.h +++ /dev/null @@ -1,202 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef GRIDVIEW_H_INCLUDED -#define GRIDVIEW_H_INCLUDED - -#include -#include "column_attr.h" -#include "../file_hierarchy.h" - - -namespace zen -{ -//grid view of FolderComparison -class GridView -{ -public: - GridView() : folderPairCount(0) {} - - //direct data access via row number - const FileSystemObject* getObject(size_t row) const; //returns nullptr if object is not found; complexity: constant! - /**/ - FileSystemObject* getObject(size_t row); // - size_t rowsOnView() const { return viewRef .size(); } //only visible elements - size_t rowsTotal () const { return sortedRef.size(); } //total rows available - - //get references to FileSystemObject: no nullptr-check needed! Everything's bound. - std::vector getAllFileRef(const std::set& rows); - - struct StatusCmpResult - { - StatusCmpResult(); - - bool existsLeftOnly; - bool existsRightOnly; - bool existsLeftNewer; - bool existsRightNewer; - bool existsDifferent; - bool existsEqual; - bool existsConflict; - - unsigned int filesOnLeftView; - unsigned int foldersOnLeftView; - unsigned int filesOnRightView; - unsigned int foldersOnRightView; - - zen::UInt64 filesizeLeftView; - zen::UInt64 filesizeRightView; - }; - - //comparison results view - StatusCmpResult updateCmpResult(bool hideFiltered, - bool leftOnlyFilesActive, - bool rightOnlyFilesActive, - bool leftNewerFilesActive, - bool rightNewerFilesActive, - bool differentFilesActive, - bool equalFilesActive, - bool conflictFilesActive); - - struct StatusSyncPreview - { - StatusSyncPreview(); - - bool existsSyncCreateLeft; - bool existsSyncCreateRight; - bool existsSyncDeleteLeft; - bool existsSyncDeleteRight; - bool existsSyncDirLeft; - bool existsSyncDirRight; - bool existsSyncDirNone; - bool existsSyncEqual; - bool existsConflict; - - unsigned int filesOnLeftView; - unsigned int foldersOnLeftView; - unsigned int filesOnRightView; - unsigned int foldersOnRightView; - - zen::UInt64 filesizeLeftView; - zen::UInt64 filesizeRightView; - }; - - //synchronization preview - StatusSyncPreview updateSyncPreview(bool hideFiltered, - bool syncCreateLeftActive, - bool syncCreateRightActive, - bool syncDeleteLeftActive, - bool syncDeleteRightActive, - bool syncDirOverwLeftActive, - bool syncDirOverwRightActive, - bool syncDirNoneActive, - bool syncEqualActive, - bool conflictFilesActive); - - void setData(FolderComparison& newData); - void removeInvalidRows(); //remove rows that have been deleted meanwhile: call after manual deletion and synchronization! - - //sorting... - bool static getDefaultSortDirection(zen::ColumnTypeRim type); //true: ascending; false: descending - - void sortView(zen::ColumnTypeRim type, bool onLeft, bool ascending); //always call this method for sorting, never sort externally! - - struct SortInfo - { - SortInfo(zen::ColumnTypeRim type, bool onLeft, bool ascending) : type_(type), onLeft_(onLeft), ascending_(ascending) {} - zen::ColumnTypeRim type_; - bool onLeft_; - bool ascending_; - }; - const SortInfo* getSortInfo() const { return currentSort.get(); } //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 HierarchyObject* hierObj) const; // find first child of DirPair or BaseDirPair *on sorted sub view* - //"hierObj" may be invalid, it is NOT dereferenced, return < 0 if not found - - size_t getFolderPairCount() const { return folderPairCount; } //count non-empty pairs to distinguish single/multiple folder pair cases - -private: - struct RefIndex - { - RefIndex(size_t folderInd, FileSystemObject::ObjectId id) : - folderIndex(folderInd), - objId(id) {} - size_t folderIndex; //because of alignment there's no benefit in using "unsigned int" in 64-bit code here! - FileSystemObject::ObjectId objId; - }; - - template void updateView(Predicate pred); - - - zen::hash_map rowPositions; //find row positions on sortedRef directly - zen::hash_map rowPositionsFirstChild; //find first child on sortedRef of a hierarchy object - //void* instead of HierarchyObject*: these are weak pointers and should *never be dereferenced*! - - std::vector viewRef; //partial view on sortedRef - /* /|\ - | (update...) - | */ - std::vector sortedRef; //flat view of weak pointers on folderCmp; may be sorted - /* /|\ - | (setData...) - | */ - //std::shared_ptr folderCmp; //actual comparison data: owned by GridView! - size_t folderPairCount; //number of non-empty folder pairs - - - class SerializeHierarchy; - - //sorting classes - template - class LessRelativeName; - - template - class LessShortFileName; - - template - class LessFilesize; - - template - class LessFiletime; - - template - class LessExtension; - - template - class LessCmpResult; - - template - class LessSyncDirection; - - std::unique_ptr currentSort; -}; - - - - - - - -//##################### implementation ######################################### - -inline -const FileSystemObject* GridView::getObject(size_t row) const -{ - return row < viewRef.size() ? - FileSystemObject::retrieve(viewRef[row]) : nullptr; -} - -inline -FileSystemObject* GridView::getObject(size_t row) -{ - //code re-use of const method: see Meyers Effective C++ - return const_cast(static_cast(*this).getObject(row)); -} -} - - -#endif // GRIDVIEW_H_INCLUDED diff --git a/ui/gui_generated.cpp b/ui/gui_generated.cpp deleted file mode 100644 index 717a8535..00000000 --- a/ui/gui_generated.cpp +++ /dev/null @@ -1,3505 +0,0 @@ -/////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Oct 8 2012) -// http://www.wxformbuilder.org/ -// -// PLEASE DO "NOT" EDIT THIS FILE! -/////////////////////////////////////////////////////////////////////////// - -#include "../wx+/bitmap_button.h" -#include "../wx+/graph.h" -#include "../wx+/grid.h" -#include "../wx+/toggle_button.h" -#include "exec_finished_box.h" -#include "folder_history_box.h" -#include "triple_splitter.h" - -#include "gui_generated.h" - -/////////////////////////////////////////////////////////////////////////// - -MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style ) -{ - this->SetSizeHints( wxSize( 640,400 ), wxDefaultSize ); - - m_menubar1 = new wxMenuBar( 0 ); - m_menuFile = new wxMenu(); - m_menuItemNew = new wxMenuItem( m_menuFile, wxID_NEW, wxString( _("&New") ) + wxT('\t') + wxT("Ctrl+N"), wxEmptyString, wxITEM_NORMAL ); - m_menuFile->Append( m_menuItemNew ); - - m_menuItemLoad = new wxMenuItem( m_menuFile, wxID_OPEN, wxString( _("&Open...") ) + wxT('\t') + wxT("Ctrl+O"), wxEmptyString, wxITEM_NORMAL ); - m_menuFile->Append( m_menuItemLoad ); - - m_menuItemSave = new wxMenuItem( m_menuFile, wxID_SAVE, wxString( _("&Save") ) + wxT('\t') + wxT("Ctrl+S"), wxEmptyString, wxITEM_NORMAL ); - m_menuFile->Append( m_menuItemSave ); - - m_menuItemSaveAs = new wxMenuItem( m_menuFile, wxID_SAVEAS, wxString( _("Save &as...") ) , wxEmptyString, wxITEM_NORMAL ); - m_menuFile->Append( m_menuItemSaveAs ); - - m_menuItem7 = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("Save as &batch job...") ) , wxEmptyString, wxITEM_NORMAL ); - m_menuFile->Append( m_menuItem7 ); - - m_menuFile->AppendSeparator(); - - m_menuItem10 = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("1. &Compare") ) + wxT('\t') + wxT("F5"), wxEmptyString, wxITEM_NORMAL ); - m_menuFile->Append( m_menuItem10 ); - - m_menuItem11 = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("2. &Synchronize") ) + wxT('\t') + wxT("F8"), wxEmptyString, wxITEM_NORMAL ); - m_menuFile->Append( m_menuItem11 ); - - m_menuFile->AppendSeparator(); - - wxMenuItem* m_menuItem4; - m_menuItem4 = new wxMenuItem( m_menuFile, wxID_EXIT, wxString( _("&Quit") ) , wxEmptyString, wxITEM_NORMAL ); - m_menuFile->Append( m_menuItem4 ); - - m_menubar1->Append( m_menuFile, _("&Program") ); - - m_menuTools = new wxMenu(); - m_menuItemGlobSett = new wxMenuItem( m_menuTools, wxID_PREFERENCES, wxString( _("&Global settings") ) , wxEmptyString, wxITEM_NORMAL ); - m_menuTools->Append( m_menuItemGlobSett ); - - m_menuTools->AppendSeparator(); - - m_menuLanguages = new wxMenu(); - m_menuTools->Append( -1, _("&Language"), m_menuLanguages ); - - wxMenuItem* m_menuItem15; - m_menuItem15 = new wxMenuItem( m_menuTools, wxID_FIND, wxString( _("&Find...") ) + wxT('\t') + wxT("Ctrl+F"), wxEmptyString, wxITEM_NORMAL ); - m_menuTools->Append( m_menuItem15 ); - - wxMenuItem* m_menuItem5; - m_menuItem5 = new wxMenuItem( m_menuTools, wxID_ANY, wxString( _("&Export file list...") ) , wxEmptyString, wxITEM_NORMAL ); - m_menuTools->Append( m_menuItem5 ); - - m_menubar1->Append( m_menuTools, _("&Tools") ); - - m_menuHelp = new wxMenu(); - m_menuItemManual = new wxMenuItem( m_menuHelp, wxID_HELP, wxString( _("&View help") ) + wxT('\t') + wxT("F1"), wxEmptyString, wxITEM_NORMAL ); - m_menuHelp->Append( m_menuItemManual ); - - m_menuCheckVersion = new wxMenu(); - m_menuItemCheckVersionNow = new wxMenuItem( m_menuCheckVersion, wxID_ANY, wxString( _("&Check now") ) , wxEmptyString, wxITEM_NORMAL ); - m_menuCheckVersion->Append( m_menuItemCheckVersionNow ); - - m_menuItemCheckVersionAuto = new wxMenuItem( m_menuCheckVersion, wxID_ANY, wxString( _("Check &automatically once a week") ) , wxEmptyString, wxITEM_CHECK ); - m_menuCheckVersion->Append( m_menuItemCheckVersionAuto ); - m_menuItemCheckVersionAuto->Check( true ); - - m_menuHelp->Append( -1, _("&Check for new version"), m_menuCheckVersion ); - - m_menuHelp->AppendSeparator(); - - m_menuItemAbout = new wxMenuItem( m_menuHelp, wxID_ABOUT, wxString( _("&About") ) + wxT('\t') + wxT("Shift+F1"), wxEmptyString, wxITEM_NORMAL ); - m_menuHelp->Append( m_menuItemAbout ); - - m_menubar1->Append( m_menuHelp, _("&Help") ); - - this->SetMenuBar( m_menubar1 ); - - bSizerPanelHolder = new wxBoxSizer( wxVERTICAL ); - - m_panelTopButtons = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxRAISED_BORDER|wxTAB_TRAVERSAL ); - bSizerTopButtons = new wxBoxSizer( wxHORIZONTAL ); - - - bSizerTopButtons->Add( 15, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - wxBoxSizer* bSizer1721; - bSizer1721 = new wxBoxSizer( wxHORIZONTAL ); - - m_buttonCompare = new zen::BitmapTextButton( m_panelTopButtons, wxID_ANY, _("Compare"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_buttonCompare->SetDefault(); - m_buttonCompare->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - m_buttonCompare->SetToolTip( _("dummy") ); - - bSizer1721->Add( m_buttonCompare, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - m_buttonCancel = new zen::BitmapTextButton( m_panelTopButtons, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( 180,-1 ), 0 ); - m_buttonCancel->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - m_buttonCancel->Enable( false ); - m_buttonCancel->Hide(); - - bSizer1721->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - m_bpButtonCmpConfig = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); - m_bpButtonCmpConfig->SetToolTip( _("dummy") ); - - bSizer1721->Add( m_bpButtonCmpConfig, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 3 ); - - - bSizerTopButtons->Add( bSizer1721, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 4 ); - - - bSizerTopButtons->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - wxBoxSizer* bSizer1731; - bSizer1731 = new wxBoxSizer( wxHORIZONTAL ); - - m_bpButtonSyncConfig = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); - m_bpButtonSyncConfig->SetToolTip( _("dummy") ); - - bSizer1731->Add( m_bpButtonSyncConfig, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 3 ); - - m_buttonSync = new zen::BitmapTextButton( m_panelTopButtons, wxID_ANY, _("Synchronize"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_buttonSync->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - m_buttonSync->SetToolTip( _("dummy") ); - - bSizer1731->Add( m_buttonSync, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizerTopButtons->Add( bSizer1731, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 4 ); - - - bSizerTopButtons->Add( 15, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - m_panelTopButtons->SetSizer( bSizerTopButtons ); - m_panelTopButtons->Layout(); - bSizerTopButtons->Fit( m_panelTopButtons ); - bSizerPanelHolder->Add( m_panelTopButtons, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - m_panelDirectoryPairs = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSTATIC_BORDER|wxTAB_TRAVERSAL ); - wxBoxSizer* bSizer1601; - bSizer1601 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer91; - bSizer91 = new wxBoxSizer( wxHORIZONTAL ); - - m_panelTopLeft = new wxPanel( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelTopLeft->SetMinSize( wxSize( 1,-1 ) ); - - wxFlexGridSizer* fgSizer8; - fgSizer8 = new wxFlexGridSizer( 0, 2, 0, 0 ); - fgSizer8->AddGrowableCol( 1 ); - fgSizer8->SetFlexibleDirection( wxBOTH ); - fgSizer8->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_ALL ); - - - fgSizer8->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextResolvedPathL = new wxStaticText( m_panelTopLeft, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextResolvedPathL->Wrap( -1 ); - fgSizer8->Add( m_staticTextResolvedPathL, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); - - wxBoxSizer* bSizer159; - bSizer159 = new wxBoxSizer( wxHORIZONTAL ); - - m_bpButtonAddPair = new wxBitmapButton( m_panelTopLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - m_bpButtonAddPair->SetToolTip( _("Add folder pair") ); - - bSizer159->Add( m_bpButtonAddPair, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_bpButtonRemovePair = new wxBitmapButton( m_panelTopLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - m_bpButtonRemovePair->SetToolTip( _("Remove folder pair") ); - - bSizer159->Add( m_bpButtonRemovePair, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - fgSizer8->Add( bSizer159, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - wxBoxSizer* bSizer182; - bSizer182 = new wxBoxSizer( wxHORIZONTAL ); - - m_directoryLeft = new FolderHistoryBox( m_panelTopLeft, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizer182->Add( m_directoryLeft, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - m_buttonSelectDirLeft = new wxButton( m_panelTopLeft, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); - m_buttonSelectDirLeft->SetToolTip( _("Select a folder") ); - - bSizer182->Add( m_buttonSelectDirLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - fgSizer8->Add( bSizer182, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - m_panelTopLeft->SetSizer( fgSizer8 ); - m_panelTopLeft->Layout(); - fgSizer8->Fit( m_panelTopLeft ); - bSizer91->Add( m_panelTopLeft, 1, wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); - - m_panelTopMiddle = new wxPanel( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - wxBoxSizer* bSizer1771; - bSizer1771 = new wxBoxSizer( wxVERTICAL ); - - - bSizer1771->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_bpButtonSwapSides = new wxBitmapButton( m_panelTopMiddle, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW ); - m_bpButtonSwapSides->SetToolTip( _("Swap sides") ); - - bSizer1771->Add( m_bpButtonSwapSides, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - wxBoxSizer* bSizer160; - bSizer160 = new wxBoxSizer( wxHORIZONTAL ); - - m_bpButtonAltCompCfg = new wxBitmapButton( m_panelTopMiddle, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - bSizer160->Add( m_bpButtonAltCompCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_bpButtonLocalFilter = new wxBitmapButton( m_panelTopMiddle, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - bSizer160->Add( m_bpButtonLocalFilter, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 2 ); - - m_bpButtonAltSyncCfg = new wxBitmapButton( m_panelTopMiddle, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - bSizer160->Add( m_bpButtonAltSyncCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer1771->Add( bSizer160, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizer1771->Add( 0, 0, 1, wxEXPAND, 5 ); - - - m_panelTopMiddle->SetSizer( bSizer1771 ); - m_panelTopMiddle->Layout(); - bSizer1771->Fit( m_panelTopMiddle ); - bSizer91->Add( m_panelTopMiddle, 0, wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - m_panelTopRight = new wxPanel( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelTopRight->SetMinSize( wxSize( 1,-1 ) ); - - wxBoxSizer* bSizer183; - bSizer183 = new wxBoxSizer( wxVERTICAL ); - - m_staticTextResolvedPathR = new wxStaticText( m_panelTopRight, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextResolvedPathR->Wrap( -1 ); - bSizer183->Add( m_staticTextResolvedPathR, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); - - wxBoxSizer* bSizer179; - bSizer179 = new wxBoxSizer( wxHORIZONTAL ); - - m_directoryRight = new FolderHistoryBox( m_panelTopRight, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizer179->Add( m_directoryRight, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - m_buttonSelectDirRight = new wxButton( m_panelTopRight, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); - m_buttonSelectDirRight->SetToolTip( _("Select a folder") ); - - bSizer179->Add( m_buttonSelectDirRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer183->Add( bSizer179, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - m_panelTopRight->SetSizer( bSizer183 ); - m_panelTopRight->Layout(); - bSizer183->Fit( m_panelTopRight ); - bSizer91->Add( m_panelTopRight, 1, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - - bSizer1601->Add( bSizer91, 0, wxEXPAND, 5 ); - - m_scrolledWindowFolderPairs = new wxScrolledWindow( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), wxHSCROLL|wxVSCROLL ); - m_scrolledWindowFolderPairs->SetScrollRate( 10, 10 ); - m_scrolledWindowFolderPairs->SetMinSize( wxSize( -1,0 ) ); - - bSizerAddFolderPairs = new wxBoxSizer( wxVERTICAL ); - - - m_scrolledWindowFolderPairs->SetSizer( bSizerAddFolderPairs ); - m_scrolledWindowFolderPairs->Layout(); - bSizerAddFolderPairs->Fit( m_scrolledWindowFolderPairs ); - bSizer1601->Add( m_scrolledWindowFolderPairs, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - m_panelDirectoryPairs->SetSizer( bSizer1601 ); - m_panelDirectoryPairs->Layout(); - bSizer1601->Fit( m_panelDirectoryPairs ); - bSizerPanelHolder->Add( m_panelDirectoryPairs, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - m_gridNavi = new zen::Grid( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); - m_gridNavi->SetScrollRate( 5, 5 ); - bSizerPanelHolder->Add( m_gridNavi, 1, wxEXPAND, 5 ); - - m_panelCenter = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - wxBoxSizer* bSizer1711; - bSizer1711 = new wxBoxSizer( wxVERTICAL ); - - m_splitterMain = new zen::TripleSplitter( m_panelCenter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - wxBoxSizer* bSizer1781; - bSizer1781 = new wxBoxSizer( wxHORIZONTAL ); - - m_gridMainL = new zen::Grid( m_splitterMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); - m_gridMainL->SetScrollRate( 5, 5 ); - bSizer1781->Add( m_gridMainL, 1, wxEXPAND, 5 ); - - m_gridMainC = new zen::Grid( m_splitterMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); - m_gridMainC->SetScrollRate( 5, 5 ); - bSizer1781->Add( m_gridMainC, 0, wxEXPAND, 5 ); - - m_gridMainR = new zen::Grid( m_splitterMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); - m_gridMainR->SetScrollRate( 5, 5 ); - bSizer1781->Add( m_gridMainR, 1, wxEXPAND, 5 ); - - - m_splitterMain->SetSizer( bSizer1781 ); - m_splitterMain->Layout(); - bSizer1781->Fit( m_splitterMain ); - bSizer1711->Add( m_splitterMain, 1, wxEXPAND, 5 ); - - m_panelStatusBar = new wxPanel( m_panelCenter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSTATIC_BORDER|wxTAB_TRAVERSAL ); - wxBoxSizer* bSizer451; - bSizer451 = new wxBoxSizer( wxHORIZONTAL ); - - bSizer451->SetMinSize( wxSize( -1,22 ) ); - bSizerFileStatus = new wxBoxSizer( wxHORIZONTAL ); - - bSizerStatusLeft = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer53; - bSizer53 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer53->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - bSizerStatusLeftDirectories = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapSmallDirectoryLeft = new wxStaticBitmap( m_panelStatusBar, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizerStatusLeftDirectories->Add( m_bitmapSmallDirectoryLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatusLeftDirectories->Add( 2, 0, 0, 0, 5 ); - - m_staticTextStatusLeftDirs = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextStatusLeftDirs->Wrap( -1 ); - bSizerStatusLeftDirectories->Add( m_staticTextStatusLeftDirs, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizer53->Add( bSizerStatusLeftDirectories, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - bSizerStatusLeftFiles = new wxBoxSizer( wxHORIZONTAL ); - - - bSizerStatusLeftFiles->Add( 10, 0, 0, 0, 5 ); - - m_bitmapSmallFileLeft = new wxStaticBitmap( m_panelStatusBar, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizerStatusLeftFiles->Add( m_bitmapSmallFileLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatusLeftFiles->Add( 2, 0, 0, 0, 5 ); - - m_staticTextStatusLeftFiles = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextStatusLeftFiles->Wrap( -1 ); - bSizerStatusLeftFiles->Add( m_staticTextStatusLeftFiles, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatusLeftFiles->Add( 4, 0, 0, 0, 5 ); - - m_staticTextStatusLeftBytes = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextStatusLeftBytes->Wrap( -1 ); - bSizerStatusLeftFiles->Add( m_staticTextStatusLeftBytes, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer53->Add( bSizerStatusLeftFiles, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer53->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatusLeft->Add( bSizer53, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticline9 = new wxStaticLine( m_panelStatusBar, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizerStatusLeft->Add( m_staticline9, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxTOP, 2 ); - - - bSizerFileStatus->Add( bSizerStatusLeft, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerFileStatus->Add( 26, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextStatusMiddle = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextStatusMiddle->Wrap( -1 ); - bSizerFileStatus->Add( m_staticTextStatusMiddle, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerFileStatus->Add( 26, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - bSizerStatusRight = new wxBoxSizer( wxHORIZONTAL ); - - m_staticline10 = new wxStaticLine( m_panelStatusBar, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizerStatusRight->Add( m_staticline10, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP, 2 ); - - wxBoxSizer* bSizer52; - bSizer52 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer52->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - bSizerStatusRightDirectories = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapSmallDirectoryRight = new wxStaticBitmap( m_panelStatusBar, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizerStatusRightDirectories->Add( m_bitmapSmallDirectoryRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatusRightDirectories->Add( 2, 0, 0, 0, 5 ); - - m_staticTextStatusRightDirs = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextStatusRightDirs->Wrap( -1 ); - bSizerStatusRightDirectories->Add( m_staticTextStatusRightDirs, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer52->Add( bSizerStatusRightDirectories, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - bSizerStatusRightFiles = new wxBoxSizer( wxHORIZONTAL ); - - - bSizerStatusRightFiles->Add( 10, 0, 0, 0, 5 ); - - m_bitmapSmallFileRight = new wxStaticBitmap( m_panelStatusBar, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizerStatusRightFiles->Add( m_bitmapSmallFileRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatusRightFiles->Add( 2, 0, 0, 0, 5 ); - - m_staticTextStatusRightFiles = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextStatusRightFiles->Wrap( -1 ); - bSizerStatusRightFiles->Add( m_staticTextStatusRightFiles, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatusRightFiles->Add( 4, 0, 0, 0, 5 ); - - m_staticTextStatusRightBytes = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextStatusRightBytes->Wrap( -1 ); - bSizerStatusRightFiles->Add( m_staticTextStatusRightBytes, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer52->Add( bSizerStatusRightFiles, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer52->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatusRight->Add( bSizer52, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerFileStatus->Add( bSizerStatusRight, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer451->Add( bSizerFileStatus, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - m_staticTextFullStatus = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextFullStatus->Wrap( -1 ); - m_staticTextFullStatus->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer451->Add( m_staticTextFullStatus, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - - m_panelStatusBar->SetSizer( bSizer451 ); - m_panelStatusBar->Layout(); - bSizer451->Fit( m_panelStatusBar ); - bSizer1711->Add( m_panelStatusBar, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - - m_panelCenter->SetSizer( bSizer1711 ); - m_panelCenter->Layout(); - bSizer1711->Fit( m_panelCenter ); - bSizerPanelHolder->Add( m_panelCenter, 1, wxEXPAND, 5 ); - - m_panelSearch = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - wxBoxSizer* bSizer1713; - bSizer1713 = new wxBoxSizer( wxHORIZONTAL ); - - m_bpButtonHideSearch = new wxBitmapButton( m_panelSearch, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - m_bpButtonHideSearch->SetToolTip( _("Close search bar") ); - - bSizer1713->Add( m_bpButtonHideSearch, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_staticText101 = new wxStaticText( m_panelSearch, wxID_ANY, _("Find:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText101->Wrap( -1 ); - bSizer1713->Add( m_staticText101, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_textCtrlSearchTxt = new wxTextCtrl( m_panelSearch, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 220,-1 ), 0|wxWANTS_CHARS ); - m_textCtrlSearchTxt->SetMaxLength( 0 ); - bSizer1713->Add( m_textCtrlSearchTxt, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); - - m_checkBoxMatchCase = new wxCheckBox( m_panelSearch, wxID_ANY, _("Match case"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer1713->Add( m_checkBoxMatchCase, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - - m_panelSearch->SetSizer( bSizer1713 ); - m_panelSearch->Layout(); - bSizer1713->Fit( m_panelSearch ); - bSizerPanelHolder->Add( m_panelSearch, 0, 0, 5 ); - - m_panelConfig = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - bSizerConfig = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer151; - bSizer151 = new wxBoxSizer( wxHORIZONTAL ); - - m_bpButtonOpen = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - m_bpButtonOpen->SetToolTip( _("dummy") ); - - bSizer151->Add( m_bpButtonOpen, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_bpButtonSave = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - m_bpButtonSave->SetToolTip( _("dummy") ); - - bSizer151->Add( m_bpButtonSave, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_bpButtonBatchJob = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - m_bpButtonBatchJob->SetToolTip( _("Save as batch job") ); - - bSizer151->Add( m_bpButtonBatchJob, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerConfig->Add( bSizer151, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_listBoxHistory = new wxListBox( m_panelConfig, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_EXTENDED|wxLB_NEEDED_SB ); - m_listBoxHistory->SetMinSize( wxSize( -1,40 ) ); - - bSizerConfig->Add( m_listBoxHistory, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - - m_panelConfig->SetSizer( bSizerConfig ); - m_panelConfig->Layout(); - bSizerConfig->Fit( m_panelConfig ); - bSizerPanelHolder->Add( m_panelConfig, 0, 0, 5 ); - - m_panelFilter = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - wxBoxSizer* bSizer171; - bSizer171 = new wxBoxSizer( wxHORIZONTAL ); - - m_bpButtonFilter = new wxBitmapButton( m_panelFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW|wxFULL_REPAINT_ON_RESIZE ); - bSizer171->Add( m_bpButtonFilter, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_checkBoxHideExcluded = new wxCheckBox( m_panelFilter, wxID_ANY, _("Hide excluded items"), wxDefaultPosition, wxDefaultSize, 0 ); - m_checkBoxHideExcluded->SetToolTip( _("Show filtered or temporarily excluded files") ); - - bSizer171->Add( m_checkBoxHideExcluded, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - - m_panelFilter->SetSizer( bSizer171 ); - m_panelFilter->Layout(); - bSizer171->Fit( m_panelFilter ); - bSizerPanelHolder->Add( m_panelFilter, 0, 0, 5 ); - - m_panelStatistics = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - bSizer1801 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer1801->Add( 0, 0, 1, wxEXPAND, 5 ); - - bSizerStatistics = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer1712; - bSizer1712 = new wxBoxSizer( wxVERTICAL ); - - m_bitmapCreateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapCreateLeft->SetToolTip( _("Number of files and folders that will be created") ); - - bSizer1712->Add( m_bitmapCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizer1712->Add( 5, 2, 0, 0, 5 ); - - - bSizer1712->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextCreateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextCreateLeft->Wrap( -1 ); - m_staticTextCreateLeft->SetToolTip( _("Number of files and folders that will be created") ); - - bSizer1712->Add( m_staticTextCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizerStatistics->Add( bSizer1712, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizerStatistics->Add( 5, 5, 0, 0, 5 ); - - wxBoxSizer* bSizer172; - bSizer172 = new wxBoxSizer( wxVERTICAL ); - - m_bitmapUpdateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapUpdateLeft->SetToolTip( _("Number of files that will be overwritten") ); - - bSizer172->Add( m_bitmapUpdateLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer172->Add( 5, 2, 0, 0, 5 ); - - - bSizer172->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextUpdateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextUpdateLeft->Wrap( -1 ); - m_staticTextUpdateLeft->SetToolTip( _("Number of files that will be overwritten") ); - - bSizer172->Add( m_staticTextUpdateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizerStatistics->Add( bSizer172, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizerStatistics->Add( 5, 5, 0, 0, 5 ); - - wxBoxSizer* bSizer173; - bSizer173 = new wxBoxSizer( wxVERTICAL ); - - m_bitmapDeleteLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") ); - - bSizer173->Add( m_bitmapDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer173->Add( 5, 2, 0, 0, 5 ); - - - bSizer173->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextDeleteLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextDeleteLeft->Wrap( -1 ); - m_staticTextDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") ); - - bSizer173->Add( m_staticTextDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatistics->Add( bSizer173, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizerStatistics->Add( 5, 5, 0, 0, 5 ); - - bSizerData = new wxBoxSizer( wxVERTICAL ); - - m_bitmapData = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapData->SetToolTip( _("Total bytes to copy") ); - - bSizerData->Add( m_bitmapData, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizerData->Add( 5, 2, 0, 0, 5 ); - - - bSizerData->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextData = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextData->Wrap( -1 ); - m_staticTextData->SetToolTip( _("Total bytes to copy") ); - - bSizerData->Add( m_staticTextData, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatistics->Add( bSizerData, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizerStatistics->Add( 5, 5, 0, 0, 5 ); - - wxBoxSizer* bSizer176; - bSizer176 = new wxBoxSizer( wxVERTICAL ); - - m_bitmapDeleteRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") ); - - bSizer176->Add( m_bitmapDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer176->Add( 5, 2, 0, 0, 5 ); - - - bSizer176->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextDeleteRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextDeleteRight->Wrap( -1 ); - m_staticTextDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") ); - - bSizer176->Add( m_staticTextDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatistics->Add( bSizer176, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizerStatistics->Add( 5, 5, 0, 0, 5 ); - - wxBoxSizer* bSizer177; - bSizer177 = new wxBoxSizer( wxVERTICAL ); - - m_bitmapUpdateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapUpdateRight->SetToolTip( _("Number of files that will be overwritten") ); - - bSizer177->Add( m_bitmapUpdateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizer177->Add( 5, 2, 0, 0, 5 ); - - - bSizer177->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextUpdateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextUpdateRight->Wrap( -1 ); - m_staticTextUpdateRight->SetToolTip( _("Number of files that will be overwritten") ); - - bSizer177->Add( m_staticTextUpdateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatistics->Add( bSizer177, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizerStatistics->Add( 5, 5, 0, 0, 5 ); - - wxBoxSizer* bSizer178; - bSizer178 = new wxBoxSizer( wxVERTICAL ); - - m_bitmapCreateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapCreateRight->SetToolTip( _("Number of files and folders that will be created") ); - - bSizer178->Add( m_bitmapCreateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizer178->Add( 5, 2, 0, 0, 5 ); - - - bSizer178->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextCreateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextCreateRight->Wrap( -1 ); - m_staticTextCreateRight->SetToolTip( _("Number of files and folders that will be created") ); - - bSizer178->Add( m_staticTextCreateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStatistics->Add( bSizer178, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer1801->Add( bSizerStatistics, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer1801->Add( 0, 0, 1, wxEXPAND, 5 ); - - - m_panelStatistics->SetSizer( bSizer1801 ); - m_panelStatistics->Layout(); - bSizer1801->Fit( m_panelStatistics ); - bSizerPanelHolder->Add( m_panelStatistics, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_panelViewFilter = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - bSizerViewFilter = new wxBoxSizer( wxHORIZONTAL ); - - m_bpButtonViewTypeSyncAction = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonViewTypeSyncAction, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizerViewFilter->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_bpButtonShowCreateLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowUpdateLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowUpdateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowDeleteLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowDeleteLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowLeftOnly = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowLeftOnly, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowLeftNewer = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowLeftNewer, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowEqual = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowEqual, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowDifferent = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowDifferent, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowDoNothing = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowDoNothing, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowRightNewer = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowRightNewer, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowRightOnly = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowRightOnly, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowDeleteRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowDeleteRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowUpdateRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowUpdateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowCreateRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowCreateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonShowConflict = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonShowConflict, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizerViewFilter->Add( 0, 0, 1, wxEXPAND, 5 ); - - - m_panelViewFilter->SetSizer( bSizerViewFilter ); - m_panelViewFilter->Layout(); - bSizerViewFilter->Fit( m_panelViewFilter ); - bSizerPanelHolder->Add( m_panelViewFilter, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - this->SetSizer( bSizerPanelHolder ); - this->Layout(); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( MainDialogGenerated::OnClose ) ); - this->Connect( m_menuItemNew->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigNew ) ); - this->Connect( m_menuItemLoad->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigLoad ) ); - this->Connect( m_menuItemSave->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigSave ) ); - this->Connect( m_menuItemSaveAs->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigSaveAs ) ); - this->Connect( m_menuItem7->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnSaveAsBatchJob ) ); - this->Connect( m_menuItem10->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnCompare ) ); - this->Connect( m_menuItem11->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnStartSync ) ); - this->Connect( m_menuItem4->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuQuit ) ); - this->Connect( m_menuItemGlobSett->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuGlobalSettings ) ); - this->Connect( m_menuItem15->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuFindItem ) ); - this->Connect( m_menuItem5->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuExportFileList ) ); - this->Connect( m_menuItemManual->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnShowHelp ) ); - this->Connect( m_menuItemCheckVersionNow->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuCheckVersion ) ); - this->Connect( m_menuItemCheckVersionAuto->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuCheckVersionAutomatically ) ); - this->Connect( m_menuItemAbout->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuAbout ) ); - m_buttonCompare->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnCompare ), NULL, this ); - m_bpButtonCmpConfig->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnCmpSettings ), NULL, this ); - m_bpButtonCmpConfig->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnCompSettingsContext ), NULL, this ); - m_bpButtonSyncConfig->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncSettings ), NULL, this ); - m_bpButtonSyncConfig->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnSyncSettingsContext ), NULL, this ); - m_buttonSync->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnStartSync ), NULL, this ); - m_bpButtonAddPair->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnAddFolderPair ), NULL, this ); - m_bpButtonRemovePair->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnRemoveTopFolderPair ), NULL, this ); - m_bpButtonSwapSides->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSwapSides ), NULL, this ); - m_bpButtonHideSearch->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnHideSearchPanel ), NULL, this ); - m_textCtrlSearchTxt->Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( MainDialogGenerated::OnSearchGridEnter ), NULL, this ); - m_bpButtonOpen->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigLoad ), NULL, this ); - m_bpButtonSave->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigSave ), NULL, this ); - m_bpButtonBatchJob->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSaveAsBatchJob ), NULL, this ); - m_listBoxHistory->Connect( wxEVT_CHAR, wxKeyEventHandler( MainDialogGenerated::OnCfgHistoryKeyEvent ), NULL, this ); - m_listBoxHistory->Connect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnLoadFromHistory ), NULL, this ); - m_listBoxHistory->Connect( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, wxCommandEventHandler( MainDialogGenerated::OnLoadFromHistoryDoubleClick ), NULL, this ); - m_listBoxHistory->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnCfgHistoryRightClick ), NULL, this ); - m_bpButtonFilter->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigureFilter ), NULL, this ); - m_bpButtonFilter->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnGlobalFilterContext ), NULL, this ); - m_checkBoxHideExcluded->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnShowExcluded ), NULL, this ); - m_bpButtonViewTypeSyncAction->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewType ), NULL, this ); - m_bpButtonShowCreateLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowCreateLeft->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowUpdateLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowUpdateLeft->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowDeleteLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowDeleteLeft->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowLeftOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowLeftOnly->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowLeftNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowLeftNewer->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowEqual->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowEqual->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowDifferent->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowDifferent->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowDoNothing->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowDoNothing->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowRightNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowRightNewer->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowRightOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowRightOnly->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowDeleteRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowDeleteRight->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowUpdateRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowUpdateRight->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowCreateRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowCreateRight->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); - m_bpButtonShowConflict->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); - m_bpButtonShowConflict->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); -} - -MainDialogGenerated::~MainDialogGenerated() -{ -} - -CmpCfgDlgGenerated::CmpCfgDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) -{ - this->SetSizeHints( wxDefaultSize, wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer136; - bSizer136 = new wxBoxSizer( wxVERTICAL ); - - m_panel36 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panel36->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer159; - bSizer159 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer182; - bSizer182 = new wxBoxSizer( wxVERTICAL ); - - m_staticText91 = new wxStaticText( m_panel36, wxID_ANY, _("Select a variant:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText91->Wrap( -1 ); - bSizer182->Add( m_staticText91, 0, wxALL, 5 ); - - wxFlexGridSizer* fgSizer16; - fgSizer16 = new wxFlexGridSizer( 2, 2, 5, 5 ); - fgSizer16->SetFlexibleDirection( wxBOTH ); - fgSizer16->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - - m_bitmapByTime = new wxStaticBitmap( m_panel36, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapByTime->SetToolTip( _("Identify equal files by comparing modification time and size.") ); - - fgSizer16->Add( m_bitmapByTime, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_toggleBtnTimeSize = new wxToggleButton( m_panel36, wxID_ANY, _("File time and size"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_toggleBtnTimeSize->SetValue( true ); - m_toggleBtnTimeSize->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - m_toggleBtnTimeSize->SetToolTip( _("Identify equal files by comparing modification time and size.") ); - - fgSizer16->Add( m_toggleBtnTimeSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - m_bitmapByContent = new wxStaticBitmap( m_panel36, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapByContent->SetToolTip( _("Identify equal files by comparing the file content.") ); - - fgSizer16->Add( m_bitmapByContent, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_toggleBtnContent = new wxToggleButton( m_panel36, wxID_ANY, _("File content"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_toggleBtnContent->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - m_toggleBtnContent->SetToolTip( _("Identify equal files by comparing the file content.") ); - - fgSizer16->Add( m_toggleBtnContent, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer182->Add( fgSizer16, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizer159->Add( bSizer182, 0, wxALL, 5 ); - - m_staticline33 = new wxStaticLine( m_panel36, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer159->Add( m_staticline33, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer172; - bSizer172 = new wxBoxSizer( wxVERTICAL ); - - m_staticText92 = new wxStaticText( m_panel36, wxID_ANY, _("Symbolic links:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText92->Wrap( -1 ); - bSizer172->Add( m_staticText92, 0, wxBOTTOM, 5 ); - - wxArrayString m_choiceHandleSymlinksChoices; - m_choiceHandleSymlinks = new wxChoice( m_panel36, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceHandleSymlinksChoices, 0 ); - m_choiceHandleSymlinks->SetSelection( -1 ); - bSizer172->Add( m_choiceHandleSymlinks, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - m_hyperlink24 = new wxHyperlinkCtrl( m_panel36, wxID_ANY, _("More information"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - bSizer172->Add( m_hyperlink24, 0, wxTOP, 5 ); - - - bSizer159->Add( bSizer172, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 10 ); - - - m_panel36->SetSizer( bSizer159 ); - m_panel36->Layout(); - bSizer159->Fit( m_panel36 ); - bSizer136->Add( m_panel36, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - m_staticline14 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer136->Add( m_staticline14, 0, wxEXPAND, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - m_buttonOkay->SetDefault(); - m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizerStdButtons->Add( m_buttonOkay, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer136->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); - - - this->SetSizer( bSizer136 ); - this->Layout(); - bSizer136->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( CmpCfgDlgGenerated::OnClose ) ); - m_toggleBtnTimeSize->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( CmpCfgDlgGenerated::OnTimeSizeDouble ), NULL, this ); - m_toggleBtnTimeSize->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( CmpCfgDlgGenerated::OnTimeSize ), NULL, this ); - m_toggleBtnContent->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( CmpCfgDlgGenerated::OnContentDouble ), NULL, this ); - m_toggleBtnContent->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( CmpCfgDlgGenerated::OnContent ), NULL, this ); - m_choiceHandleSymlinks->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( CmpCfgDlgGenerated::OnChangeErrorHandling ), NULL, this ); - m_hyperlink24->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( CmpCfgDlgGenerated::OnHelpComparisonSettings ), NULL, this ); - m_buttonOkay->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( CmpCfgDlgGenerated::OnOkay ), NULL, this ); - m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( CmpCfgDlgGenerated::OnCancel ), NULL, this ); -} - -CmpCfgDlgGenerated::~CmpCfgDlgGenerated() -{ -} - -SyncCfgDlgGenerated::SyncCfgDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) -{ - this->SetSizeHints( wxDefaultSize, wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer7; - bSizer7 = new wxBoxSizer( wxVERTICAL ); - - m_panel37 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panel37->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer181; - bSizer181 = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer29; - bSizer29 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer183; - bSizer183 = new wxBoxSizer( wxVERTICAL ); - - m_staticText86 = new wxStaticText( m_panel37, wxID_ANY, _("Select a variant:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText86->Wrap( -1 ); - bSizer183->Add( m_staticText86, 0, wxALL, 5 ); - - wxFlexGridSizer* fgSizer1; - fgSizer1 = new wxFlexGridSizer( 4, 2, 5, 5 ); - fgSizer1->SetFlexibleDirection( wxBOTH ); - fgSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - - wxBoxSizer* bSizer171; - bSizer171 = new wxBoxSizer( wxVERTICAL ); - - - bSizer171->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_toggleBtnTwoWay = new wxToggleButton( m_panel37, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - m_toggleBtnTwoWay->SetValue( true ); - m_toggleBtnTwoWay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer171->Add( m_toggleBtnTwoWay, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer171->Add( 0, 0, 1, wxEXPAND, 5 ); - - - fgSizer1->Add( bSizer171, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - m_staticTextAutomatic = new wxStaticText( m_panel37, wxID_ANY, _("Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database."), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextAutomatic->Wrap( 480 ); - fgSizer1->Add( m_staticTextAutomatic, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - wxBoxSizer* bSizer172; - bSizer172 = new wxBoxSizer( wxVERTICAL ); - - - bSizer172->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_toggleBtnMirror = new wxToggleButton( m_panel37, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - m_toggleBtnMirror->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer172->Add( m_toggleBtnMirror, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer172->Add( 0, 0, 1, wxEXPAND, 5 ); - - - fgSizer1->Add( bSizer172, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextMirror = new wxStaticText( m_panel37, wxID_ANY, _("Create a mirror backup of the left folder which exactly matches the right folder after synchronization."), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextMirror->Wrap( 480 ); - fgSizer1->Add( m_staticTextMirror, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - wxBoxSizer* bSizer173; - bSizer173 = new wxBoxSizer( wxVERTICAL ); - - - bSizer173->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_toggleBtnUpdate = new wxToggleButton( m_panel37, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - m_toggleBtnUpdate->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer173->Add( m_toggleBtnUpdate, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer173->Add( 0, 0, 1, wxEXPAND, 5 ); - - - fgSizer1->Add( bSizer173, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - m_staticTextUpdate = new wxStaticText( m_panel37, wxID_ANY, _("Copy new and updated files to the right folder."), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextUpdate->Wrap( 480 ); - fgSizer1->Add( m_staticTextUpdate, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - wxBoxSizer* bSizer1741; - bSizer1741 = new wxBoxSizer( wxVERTICAL ); - - - bSizer1741->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_toggleBtnCustom = new wxToggleButton( m_panel37, wxID_ANY, _("Custom"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - m_toggleBtnCustom->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer1741->Add( m_toggleBtnCustom, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer1741->Add( 0, 0, 1, wxEXPAND, 5 ); - - - fgSizer1->Add( bSizer1741, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextCustom = new wxStaticText( m_panel37, wxID_ANY, _("Configure your own synchronization rules."), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextCustom->Wrap( 480 ); - fgSizer1->Add( m_staticTextCustom, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer183->Add( fgSizer1, 0, wxRIGHT|wxLEFT, 5 ); - - wxBoxSizer* bSizer1751; - bSizer1751 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer1751->Add( 8, 0, 0, 0, 5 ); - - m_checkBoxDetectMove = new wxCheckBox( m_panel37, wxID_ANY, _("Detect moved files"), wxDefaultPosition, wxDefaultSize, 0 ); - m_checkBoxDetectMove->SetValue(true); - m_checkBoxDetectMove->SetToolTip( _("Requires database files. Not supported by all file systems.") ); - - bSizer1751->Add( m_checkBoxDetectMove, 1, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 ); - - - bSizer183->Add( bSizer1751, 0, wxEXPAND, 5 ); - - - bSizer29->Add( bSizer183, 0, wxALL, 5 ); - - m_staticline32 = new wxStaticLine( m_panel37, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer29->Add( m_staticline32, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer184; - bSizer184 = new wxBoxSizer( wxVERTICAL ); - - m_staticText87 = new wxStaticText( m_panel37, wxID_ANY, _("Delete files:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText87->Wrap( -1 ); - bSizer184->Add( m_staticText87, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - wxBoxSizer* bSizer180; - bSizer180 = new wxBoxSizer( wxHORIZONTAL ); - - m_toggleBtnPermanent = new wxToggleButton( m_panel37, wxID_ANY, _("Permanent"), wxDefaultPosition, wxDefaultSize, 0 ); - m_toggleBtnPermanent->SetToolTip( _("Delete or overwrite files permanently") ); - - bSizer180->Add( m_toggleBtnPermanent, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_toggleBtnRecycler = new wxToggleButton( m_panel37, wxID_ANY, _("Recycle bin"), wxDefaultPosition, wxDefaultSize, 0 ); - m_toggleBtnRecycler->SetToolTip( _("Back up deleted and overwritten files in the recycle bin") ); - - bSizer180->Add( m_toggleBtnRecycler, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 ); - - m_toggleBtnVersioning = new wxToggleButton( m_panel37, wxID_ANY, _("Versioning"), wxDefaultPosition, wxDefaultSize, 0 ); - m_toggleBtnVersioning->SetToolTip( _("Move files to a user-defined folder") ); - - bSizer180->Add( m_toggleBtnVersioning, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer184->Add( bSizer180, 0, wxTOP, 5 ); - - m_panelVersioning = new wxPanel( m_panel37, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelVersioning->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer191; - bSizer191 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer156; - bSizer156 = new wxBoxSizer( wxHORIZONTAL ); - - m_versioningFolder = new FolderHistoryBox( m_panelVersioning, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizer156->Add( m_versioningFolder, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - m_buttonSelectDirVersioning = new wxButton( m_panelVersioning, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); - m_buttonSelectDirVersioning->SetToolTip( _("Select a folder") ); - - bSizer156->Add( m_buttonSelectDirVersioning, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer191->Add( bSizer156, 1, wxEXPAND|wxBOTTOM, 5 ); - - bSizer192 = new wxBoxSizer( wxHORIZONTAL ); - - m_staticText93 = new wxStaticText( m_panelVersioning, wxID_ANY, _("Naming convention:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText93->Wrap( -1 ); - bSizer192->Add( m_staticText93, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - wxArrayString m_choiceVersioningStyleChoices; - m_choiceVersioningStyle = new wxChoice( m_panelVersioning, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceVersioningStyleChoices, 0 ); - m_choiceVersioningStyle->SetSelection( 0 ); - bSizer192->Add( m_choiceVersioningStyle, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_staticTextNamingCvtPart1 = new wxStaticText( m_panelVersioning, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextNamingCvtPart1->Wrap( -1 ); - m_staticTextNamingCvtPart1->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); - - bSizer192->Add( m_staticTextNamingCvtPart1, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextNamingCvtPart2Bold = new wxStaticText( m_panelVersioning, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextNamingCvtPart2Bold->Wrap( -1 ); - m_staticTextNamingCvtPart2Bold->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - m_staticTextNamingCvtPart2Bold->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); - - bSizer192->Add( m_staticTextNamingCvtPart2Bold, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextNamingCvtPart3 = new wxStaticText( m_panelVersioning, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextNamingCvtPart3->Wrap( -1 ); - m_staticTextNamingCvtPart3->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); - - bSizer192->Add( m_staticTextNamingCvtPart3, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer192->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_hyperlink17 = new wxHyperlinkCtrl( m_panelVersioning, wxID_ANY, _("Show examples"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - bSizer192->Add( m_hyperlink17, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); - - - bSizer191->Add( bSizer192, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - m_panelVersioning->SetSizer( bSizer191 ); - m_panelVersioning->Layout(); - bSizer191->Fit( m_panelVersioning ); - bSizer184->Add( m_panelVersioning, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxTOP, 5 ); - - - bSizer29->Add( bSizer184, 0, wxALL|wxEXPAND, 10 ); - - bSizerExtraConfig = new wxBoxSizer( wxVERTICAL ); - - m_staticline321 = new wxStaticLine( m_panel37, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizerExtraConfig->Add( m_staticline321, 0, wxEXPAND, 5 ); - - bSizer179 = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer174; - bSizer174 = new wxBoxSizer( wxVERTICAL ); - - m_staticText88 = new wxStaticText( m_panel37, wxID_ANY, _("Handle errors:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText88->Wrap( -1 ); - bSizer174->Add( m_staticText88, 0, wxBOTTOM, 5 ); - - wxBoxSizer* bSizer175; - bSizer175 = new wxBoxSizer( wxHORIZONTAL ); - - m_toggleBtnErrorIgnore = new wxToggleButton( m_panel37, wxID_ANY, _("Ignore"), wxDefaultPosition, wxDefaultSize, 0 ); - m_toggleBtnErrorIgnore->SetToolTip( _("Hide all error and warning messages") ); - - bSizer175->Add( m_toggleBtnErrorIgnore, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 ); - - m_toggleBtnErrorPopup = new wxToggleButton( m_panel37, wxID_ANY, _("Pop-up"), wxDefaultPosition, wxDefaultSize, 0 ); - m_toggleBtnErrorPopup->SetToolTip( _("Show pop-up on errors or warnings") ); - - bSizer175->Add( m_toggleBtnErrorPopup, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer174->Add( bSizer175, 0, 0, 5 ); - - - bSizer179->Add( bSizer174, 0, wxALL, 10 ); - - m_staticline36 = new wxStaticLine( m_panel37, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer179->Add( m_staticline36, 0, wxEXPAND, 5 ); - - bSizerOnCompletion = new wxBoxSizer( wxVERTICAL ); - - m_staticText89 = new wxStaticText( m_panel37, wxID_ANY, _("On completion:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText89->Wrap( -1 ); - bSizerOnCompletion->Add( m_staticText89, 0, wxBOTTOM, 5 ); - - m_comboBoxExecFinished = new ExecFinishedBox( m_panel37, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizerOnCompletion->Add( m_comboBoxExecFinished, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer179->Add( bSizerOnCompletion, 1, wxALL, 10 ); - - - bSizerExtraConfig->Add( bSizer179, 0, wxEXPAND, 5 ); - - - bSizer29->Add( bSizerExtraConfig, 0, wxEXPAND, 5 ); - - - bSizer181->Add( bSizer29, 0, wxEXPAND, 5 ); - - m_staticline31 = new wxStaticLine( m_panel37, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer181->Add( m_staticline31, 0, wxEXPAND, 5 ); - - bSizerConfig = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer18011; - bSizer18011 = new wxBoxSizer( wxHORIZONTAL ); - - m_staticTextHeaderCategory1 = new wxStaticText( m_panel37, wxID_ANY, _("Category"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT ); - m_staticTextHeaderCategory1->Wrap( -1 ); - bSizer18011->Add( m_staticTextHeaderCategory1, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer18011->Add( 5, 0, 0, 0, 5 ); - - m_staticTextHeaderAction1 = new wxStaticText( m_panel37, wxID_ANY, _("Action"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT ); - m_staticTextHeaderAction1->Wrap( -1 ); - bSizer18011->Add( m_staticTextHeaderAction1, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizerConfig->Add( bSizer18011, 0, wxEXPAND, 5 ); - - - bSizerConfig->Add( 0, 5, 0, 0, 5 ); - - m_bitmapDatabase = new wxStaticBitmap( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizerConfig->Add( m_bitmapDatabase, 0, wxALIGN_CENTER_HORIZONTAL|wxTOP, 10 ); - - wxBoxSizer* sbSizerKeepWidthStableIfSyncDirsNotShown; - sbSizerKeepWidthStableIfSyncDirsNotShown = new wxBoxSizer( wxHORIZONTAL ); - - - sbSizerKeepWidthStableIfSyncDirsNotShown->Add( 45, 0, 0, 0, 5 ); - - - sbSizerKeepWidthStableIfSyncDirsNotShown->Add( 5, 0, 0, 0, 5 ); - - - sbSizerKeepWidthStableIfSyncDirsNotShown->Add( 46, 0, 0, 0, 5 ); - - - bSizerConfig->Add( sbSizerKeepWidthStableIfSyncDirsNotShown, 0, 0, 5 ); - - sbSizerSyncDirections = new wxBoxSizer( wxVERTICAL ); - - bSizerLeftOnly = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapLeftOnly = new wxStaticBitmap( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); - m_bitmapLeftOnly->SetToolTip( _("Item exists on left side only") ); - - bSizerLeftOnly->Add( m_bitmapLeftOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerLeftOnly->Add( 5, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_bpButtonLeftOnly = new wxBitmapButton( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); - bSizerLeftOnly->Add( m_bpButtonLeftOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - sbSizerSyncDirections->Add( bSizerLeftOnly, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - - bSizerRightOnly = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapRightOnly = new wxStaticBitmap( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); - m_bitmapRightOnly->SetToolTip( _("Item exists on right side only") ); - - bSizerRightOnly->Add( m_bitmapRightOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerRightOnly->Add( 5, 0, 0, 0, 5 ); - - m_bpButtonRightOnly = new wxBitmapButton( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); - bSizerRightOnly->Add( m_bpButtonRightOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - sbSizerSyncDirections->Add( bSizerRightOnly, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - - bSizerLeftNewer = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapLeftNewer = new wxStaticBitmap( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); - m_bitmapLeftNewer->SetToolTip( _("Left side is newer") ); - - bSizerLeftNewer->Add( m_bitmapLeftNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerLeftNewer->Add( 5, 0, 0, 0, 5 ); - - m_bpButtonLeftNewer = new wxBitmapButton( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); - bSizerLeftNewer->Add( m_bpButtonLeftNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - sbSizerSyncDirections->Add( bSizerLeftNewer, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - - bSizerRightNewer = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapRightNewer = new wxStaticBitmap( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); - m_bitmapRightNewer->SetToolTip( _("Right side is newer") ); - - bSizerRightNewer->Add( m_bitmapRightNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerRightNewer->Add( 5, 0, 0, 0, 5 ); - - m_bpButtonRightNewer = new wxBitmapButton( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); - bSizerRightNewer->Add( m_bpButtonRightNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - sbSizerSyncDirections->Add( bSizerRightNewer, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - - bSizerDifferent = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapDifferent = new wxStaticBitmap( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); - m_bitmapDifferent->SetToolTip( _("Items have different content") ); - - bSizerDifferent->Add( m_bitmapDifferent, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerDifferent->Add( 5, 0, 0, 0, 5 ); - - m_bpButtonDifferent = new wxBitmapButton( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); - bSizerDifferent->Add( m_bpButtonDifferent, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - sbSizerSyncDirections->Add( bSizerDifferent, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - - bSizerConflict = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapConflict = new wxStaticBitmap( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); - m_bitmapConflict->SetToolTip( _("Conflict/item cannot be categorized") ); - - bSizerConflict->Add( m_bitmapConflict, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerConflict->Add( 5, 0, 0, 0, 5 ); - - m_bpButtonConflict = new wxBitmapButton( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); - bSizerConflict->Add( m_bpButtonConflict, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - sbSizerSyncDirections->Add( bSizerConflict, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizerConfig->Add( sbSizerSyncDirections, 0, wxEXPAND, 5 ); - - - bSizer181->Add( bSizerConfig, 0, wxALL|wxEXPAND, 10 ); - - - m_panel37->SetSizer( bSizer181 ); - m_panel37->Layout(); - bSizer181->Fit( m_panel37 ); - bSizer7->Add( m_panel37, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - m_staticline15 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer7->Add( m_staticline15, 0, wxEXPAND, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - m_buttonOK = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - m_buttonOK->SetDefault(); - m_buttonOK->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizerStdButtons->Add( m_buttonOK, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer7->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); - - - this->SetSizer( bSizer7 ); - this->Layout(); - bSizer7->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SyncCfgDlgGenerated::OnClose ) ); - m_toggleBtnTwoWay->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( SyncCfgDlgGenerated::OnSyncTwoWayDouble ), NULL, this ); - m_toggleBtnTwoWay->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnSyncTwoWay ), NULL, this ); - m_toggleBtnMirror->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( SyncCfgDlgGenerated::OnSyncMirrorDouble ), NULL, this ); - m_toggleBtnMirror->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnSyncMirror ), NULL, this ); - m_toggleBtnUpdate->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( SyncCfgDlgGenerated::OnSyncUpdateDouble ), NULL, this ); - m_toggleBtnUpdate->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnSyncUpdate ), NULL, this ); - m_toggleBtnCustom->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( SyncCfgDlgGenerated::OnSyncCustomDouble ), NULL, this ); - m_toggleBtnCustom->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnSyncCustom ), NULL, this ); - m_checkBoxDetectMove->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnToggleDetectMovedFiles ), NULL, this ); - m_toggleBtnPermanent->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnDeletionPermanent ), NULL, this ); - m_toggleBtnRecycler->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnDeletionRecycler ), NULL, this ); - m_toggleBtnVersioning->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnDeletionVersioning ), NULL, this ); - m_choiceVersioningStyle->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( SyncCfgDlgGenerated::OnParameterChange ), NULL, this ); - m_hyperlink17->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( SyncCfgDlgGenerated::OnHelpVersioning ), NULL, this ); - m_toggleBtnErrorIgnore->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnErrorIgnore ), NULL, this ); - m_toggleBtnErrorPopup->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnErrorPopup ), NULL, this ); - m_bpButtonLeftOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnExLeftSideOnly ), NULL, this ); - m_bpButtonRightOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnExRightSideOnly ), NULL, this ); - m_bpButtonLeftNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnLeftNewer ), NULL, this ); - m_bpButtonRightNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnRightNewer ), NULL, this ); - m_bpButtonDifferent->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnDifferent ), NULL, this ); - m_bpButtonConflict->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnConflict ), NULL, this ); - m_buttonOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnOkay ), NULL, this ); - m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnCancel ), NULL, this ); -} - -SyncCfgDlgGenerated::~SyncCfgDlgGenerated() -{ -} - -SyncConfirmationDlgGenerated::SyncConfirmationDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) -{ - this->SetSizeHints( wxDefaultSize, wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer134; - bSizer134 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer72; - bSizer72 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapSync = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizer72->Add( m_bitmapSync, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); - - m_staticTextHeader = new wxStaticText( this, wxID_ANY, _("Start synchronization now?"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextHeader->Wrap( -1 ); - bSizer72->Add( m_staticTextHeader, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 10 ); - - - bSizer134->Add( bSizer72, 0, 0, 5 ); - - m_staticline371 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer134->Add( m_staticline371, 0, wxEXPAND, 5 ); - - m_panelStatistics = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelStatistics->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer185; - bSizer185 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer185->Add( 0, 0, 1, 0, 5 ); - - m_staticline38 = new wxStaticLine( m_panelStatistics, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer185->Add( m_staticline38, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer162; - bSizer162 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer182; - bSizer182 = new wxBoxSizer( wxHORIZONTAL ); - - m_staticText84 = new wxStaticText( m_panelStatistics, wxID_ANY, _("Variant:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText84->Wrap( -1 ); - bSizer182->Add( m_staticText84, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); - - - bSizer182->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_staticTextVariant = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextVariant->Wrap( -1 ); - m_staticTextVariant->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer182->Add( m_staticTextVariant, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - - bSizer182->Add( 0, 0, 1, wxEXPAND, 5 ); - - - bSizer162->Add( bSizer182, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxALL, 5 ); - - m_staticline14 = new wxStaticLine( m_panelStatistics, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer162->Add( m_staticline14, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer181; - bSizer181 = new wxBoxSizer( wxVERTICAL ); - - m_staticText83 = new wxStaticText( m_panelStatistics, wxID_ANY, _("Statistics"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText83->Wrap( -1 ); - bSizer181->Add( m_staticText83, 0, wxALL, 5 ); - - wxFlexGridSizer* fgSizer11; - fgSizer11 = new wxFlexGridSizer( 2, 7, 2, 5 ); - fgSizer11->SetFlexibleDirection( wxBOTH ); - fgSizer11->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - - m_bitmapCreateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapCreateLeft->SetToolTip( _("Number of files and folders that will be created") ); - - fgSizer11->Add( m_bitmapCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bitmapUpdateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapUpdateLeft->SetToolTip( _("Number of files that will be overwritten") ); - - fgSizer11->Add( m_bitmapUpdateLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_bitmapDeleteLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") ); - - fgSizer11->Add( m_bitmapDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_bitmapData = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapData->SetToolTip( _("Total bytes to copy") ); - - fgSizer11->Add( m_bitmapData, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bitmapDeleteRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") ); - - fgSizer11->Add( m_bitmapDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_bitmapUpdateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapUpdateRight->SetToolTip( _("Number of files that will be overwritten") ); - - fgSizer11->Add( m_bitmapUpdateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bitmapCreateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - m_bitmapCreateRight->SetToolTip( _("Number of files and folders that will be created") ); - - fgSizer11->Add( m_bitmapCreateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_staticTextCreateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextCreateLeft->Wrap( -1 ); - m_staticTextCreateLeft->SetToolTip( _("Number of files and folders that will be created") ); - - fgSizer11->Add( m_staticTextCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_staticTextUpdateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextUpdateLeft->Wrap( -1 ); - m_staticTextUpdateLeft->SetToolTip( _("Number of files that will be overwritten") ); - - fgSizer11->Add( m_staticTextUpdateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_staticTextDeleteLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextDeleteLeft->Wrap( -1 ); - m_staticTextDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") ); - - fgSizer11->Add( m_staticTextDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextData = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextData->Wrap( -1 ); - m_staticTextData->SetToolTip( _("Total bytes to copy") ); - - fgSizer11->Add( m_staticTextData, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextDeleteRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextDeleteRight->Wrap( -1 ); - m_staticTextDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") ); - - fgSizer11->Add( m_staticTextDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextUpdateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextUpdateRight->Wrap( -1 ); - m_staticTextUpdateRight->SetToolTip( _("Number of files that will be overwritten") ); - - fgSizer11->Add( m_staticTextUpdateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextCreateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextCreateRight->Wrap( -1 ); - m_staticTextCreateRight->SetToolTip( _("Number of files and folders that will be created") ); - - fgSizer11->Add( m_staticTextCreateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer181->Add( fgSizer11, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer162->Add( bSizer181, 0, wxEXPAND|wxALL, 5 ); - - - bSizer185->Add( bSizer162, 0, 0, 5 ); - - - m_panelStatistics->SetSizer( bSizer185 ); - m_panelStatistics->Layout(); - bSizer185->Fit( m_panelStatistics ); - bSizer134->Add( m_panelStatistics, 0, wxEXPAND, 5 ); - - m_staticline12 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer134->Add( m_staticline12, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer164; - bSizer164 = new wxBoxSizer( wxVERTICAL ); - - m_checkBoxDontShowAgain = new wxCheckBox( this, wxID_ANY, _("&Don't show this dialog again"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer164->Add( m_checkBoxDontShowAgain, 0, wxALIGN_CENTER_HORIZONTAL|wxTOP|wxRIGHT|wxLEFT, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - m_buttonStartSync = new wxButton( this, wxID_OK, _("&Start"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - m_buttonStartSync->SetDefault(); - m_buttonStartSync->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizerStdButtons->Add( m_buttonStartSync, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer164->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); - - - bSizer134->Add( bSizer164, 1, wxEXPAND, 5 ); - - - this->SetSizer( bSizer134 ); - this->Layout(); - bSizer134->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SyncConfirmationDlgGenerated::OnClose ) ); - m_buttonStartSync->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncConfirmationDlgGenerated::OnStartSync ), NULL, this ); - m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncConfirmationDlgGenerated::OnCancel ), NULL, this ); -} - -SyncConfirmationDlgGenerated::~SyncConfirmationDlgGenerated() -{ -} - -FolderPairPanelGenerated::FolderPairPanelGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) -{ - wxBoxSizer* bSizer74; - bSizer74 = new wxBoxSizer( wxHORIZONTAL ); - - m_panelLeft = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelLeft->SetMinSize( wxSize( 1,-1 ) ); - - wxBoxSizer* bSizer134; - bSizer134 = new wxBoxSizer( wxHORIZONTAL ); - - m_bpButtonRemovePair = new wxBitmapButton( m_panelLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - m_bpButtonRemovePair->SetToolTip( _("Remove folder pair") ); - - bSizer134->Add( m_bpButtonRemovePair, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_directoryLeft = new FolderHistoryBox( m_panelLeft, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizer134->Add( m_directoryLeft, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - m_buttonSelectDirLeft = new wxButton( m_panelLeft, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); - m_buttonSelectDirLeft->SetToolTip( _("Select a folder") ); - - bSizer134->Add( m_buttonSelectDirLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - m_panelLeft->SetSizer( bSizer134 ); - m_panelLeft->Layout(); - bSizer134->Fit( m_panelLeft ); - bSizer74->Add( m_panelLeft, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxEXPAND, 5 ); - - m_panel20 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - wxBoxSizer* bSizer95; - bSizer95 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer95->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_bpButtonAltCompCfg = new wxBitmapButton( m_panel20, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - bSizer95->Add( m_bpButtonAltCompCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_bpButtonLocalFilter = new wxBitmapButton( m_panel20, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - bSizer95->Add( m_bpButtonLocalFilter, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 2 ); - - m_bpButtonAltSyncCfg = new wxBitmapButton( m_panel20, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - bSizer95->Add( m_bpButtonAltSyncCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer95->Add( 0, 0, 1, wxEXPAND, 5 ); - - - m_panel20->SetSizer( bSizer95 ); - m_panel20->Layout(); - bSizer95->Fit( m_panel20 ); - bSizer74->Add( m_panel20, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT|wxEXPAND, 5 ); - - m_panelRight = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelRight->SetMinSize( wxSize( 1,-1 ) ); - - wxBoxSizer* bSizer135; - bSizer135 = new wxBoxSizer( wxHORIZONTAL ); - - m_directoryRight = new FolderHistoryBox( m_panelRight, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizer135->Add( m_directoryRight, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - m_buttonSelectDirRight = new wxButton( m_panelRight, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); - m_buttonSelectDirRight->SetToolTip( _("Select a folder") ); - - bSizer135->Add( m_buttonSelectDirRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - m_panelRight->SetSizer( bSizer135 ); - m_panelRight->Layout(); - bSizer135->Fit( m_panelRight ); - bSizer74->Add( m_panelRight, 1, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxEXPAND, 5 ); - - - this->SetSizer( bSizer74 ); - this->Layout(); - bSizer74->Fit( this ); -} - -FolderPairPanelGenerated::~FolderPairPanelGenerated() -{ -} - -CompareProgressDlgGenerated::CompareProgressDlgGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) -{ - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer40; - bSizer40 = new wxBoxSizer( wxVERTICAL ); - - - bSizer40->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_textCtrlStatus = new wxTextCtrl( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, wxTE_READONLY ); - m_textCtrlStatus->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - bSizer40->Add( m_textCtrlStatus, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); - - m_gauge2 = new wxGauge( this, wxID_ANY, 100, wxDefaultPosition, wxSize( -1,14 ), wxGA_HORIZONTAL|wxGA_SMOOTH ); - bSizer40->Add( m_gauge2, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 ); - - bSizer42 = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer162StretchSpeedAndRemTimeIndependently; - bSizer162StretchSpeedAndRemTimeIndependently = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer157; - bSizer157 = new wxBoxSizer( wxVERTICAL ); - - bSizerFilesFound = new wxBoxSizer( wxHORIZONTAL ); - - m_staticText321 = new wxStaticText( this, wxID_ANY, _("Items found:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText321->Wrap( -1 ); - bSizerFilesFound->Add( m_staticText321, 0, wxALIGN_BOTTOM, 5 ); - - m_staticTextScanned = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextScanned->Wrap( -1 ); - m_staticTextScanned->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizerFilesFound->Add( m_staticTextScanned, 0, wxALIGN_BOTTOM|wxLEFT, 5 ); - - - bSizer157->Add( bSizerFilesFound, 0, 0, 5 ); - - bSizerFilesRemaining = new wxBoxSizer( wxHORIZONTAL ); - - m_staticText46 = new wxStaticText( this, wxID_ANY, _("Items remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText46->Wrap( -1 ); - bSizerFilesRemaining->Add( m_staticText46, 0, wxALIGN_BOTTOM, 5 ); - - wxBoxSizer* bSizer154; - bSizer154 = new wxBoxSizer( wxHORIZONTAL ); - - m_staticTextFilesRemaining = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextFilesRemaining->Wrap( -1 ); - m_staticTextFilesRemaining->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer154->Add( m_staticTextFilesRemaining, 0, wxALIGN_BOTTOM, 5 ); - - m_staticTextDataRemaining = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextDataRemaining->Wrap( -1 ); - bSizer154->Add( m_staticTextDataRemaining, 0, wxALIGN_BOTTOM|wxLEFT, 5 ); - - - bSizerFilesRemaining->Add( bSizer154, 0, wxALIGN_BOTTOM|wxLEFT, 5 ); - - - bSizer157->Add( bSizerFilesRemaining, 0, 0, 5 ); - - - bSizer162StretchSpeedAndRemTimeIndependently->Add( bSizer157, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer162StretchSpeedAndRemTimeIndependently->Add( 0, 0, 1, wxEXPAND, 5 ); - - sSizerSpeed = new wxBoxSizer( wxHORIZONTAL ); - - m_staticText104 = new wxStaticText( this, wxID_ANY, _("Speed:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText104->Wrap( -1 ); - sSizerSpeed->Add( m_staticText104, 0, wxALIGN_BOTTOM, 5 ); - - m_staticTextSpeed = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextSpeed->Wrap( -1 ); - m_staticTextSpeed->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - sSizerSpeed->Add( m_staticTextSpeed, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); - - - bSizer162StretchSpeedAndRemTimeIndependently->Add( sSizerSpeed, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer42->Add( bSizer162StretchSpeedAndRemTimeIndependently, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer42->Add( 10, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - wxBoxSizer* bSizer163; - bSizer163 = new wxBoxSizer( wxHORIZONTAL ); - - sSizerTimeRemaining = new wxBoxSizer( wxHORIZONTAL ); - - m_staticTextTimeRemFixed = new wxStaticText( this, wxID_ANY, _("Time remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextTimeRemFixed->Wrap( -1 ); - sSizerTimeRemaining->Add( m_staticTextTimeRemFixed, 0, wxALIGN_BOTTOM, 5 ); - - m_staticTextRemTime = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextRemTime->Wrap( -1 ); - m_staticTextRemTime->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - sSizerTimeRemaining->Add( m_staticTextRemTime, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); - - - bSizer163->Add( sSizerTimeRemaining, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer163->Add( 0, 0, 1, wxEXPAND, 5 ); - - sSizerTimeElapsed = new wxBoxSizer( wxHORIZONTAL ); - - wxStaticText* m_staticText37; - m_staticText37 = new wxStaticText( this, wxID_ANY, _("Time elapsed:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText37->Wrap( -1 ); - sSizerTimeElapsed->Add( m_staticText37, 0, wxALIGN_BOTTOM, 5 ); - - m_staticTextTimeElapsed = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextTimeElapsed->Wrap( -1 ); - m_staticTextTimeElapsed->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - sSizerTimeElapsed->Add( m_staticTextTimeElapsed, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); - - - bSizer163->Add( sSizerTimeElapsed, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer42->Add( bSizer163, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer40->Add( bSizer42, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 ); - - - bSizer40->Add( 0, 0, 1, wxEXPAND, 5 ); - - - this->SetSizer( bSizer40 ); - this->Layout(); - bSizer40->Fit( this ); -} - -CompareProgressDlgGenerated::~CompareProgressDlgGenerated() -{ -} - -SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) -{ - bSizerRoot = new wxBoxSizer( wxVERTICAL ); - - bSizer42 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer42->Add( 32, 0, 0, 0, 5 ); - - - bSizer42->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_bitmapStatus = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32,32 ), 0 ); - bSizer42->Add( m_bitmapStatus, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); - - m_staticTextPhase = new wxStaticText( this, wxID_ANY, _("Synchronizing..."), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextPhase->Wrap( -1 ); - m_staticTextPhase->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer42->Add( m_staticTextPhase, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 ); - - m_animCtrlSyncing = new wxAnimationCtrl( this, wxID_ANY, wxNullAnimation, wxDefaultPosition, wxSize( 32,32 ), wxAC_DEFAULT_STYLE ); - bSizer42->Add( m_animCtrlSyncing, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); - - - bSizer42->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_bpButtonMinimizeToTray = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32,32 ), wxBU_AUTODRAW ); - m_bpButtonMinimizeToTray->SetToolTip( _("Minimize to notification area") ); - - bSizer42->Add( m_bpButtonMinimizeToTray, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerRoot->Add( bSizer42, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT|wxEXPAND, 5 ); - - bSizerStatusText = new wxBoxSizer( wxVERTICAL ); - - m_staticTextStatus = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextStatus->Wrap( -1 ); - bSizerStatusText->Add( m_staticTextStatus, 0, wxEXPAND|wxLEFT, 10 ); - - - bSizerStatusText->Add( 0, 5, 0, 0, 5 ); - - - bSizerRoot->Add( bSizerStatusText, 0, wxEXPAND, 5 ); - - wxStaticLine* m_staticlineHeader; - m_staticlineHeader = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizerRoot->Add( m_staticlineHeader, 0, wxEXPAND, 5 ); - - m_panelProgress = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelProgress->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer173; - bSizer173 = new wxBoxSizer( wxVERTICAL ); - - bSizer171 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer171->Add( 10, 0, 0, 0, 5 ); - - wxBoxSizer* bSizer164; - bSizer164 = new wxBoxSizer( wxVERTICAL ); - - m_panelItemsProcessed = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); - m_panelItemsProcessed->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer165; - bSizer165 = new wxBoxSizer( wxVERTICAL ); - - - bSizer165->Add( 0, 5, 0, 0, 5 ); - - wxStaticText* m_staticText96; - m_staticText96 = new wxStaticText( m_panelItemsProcessed, wxID_ANY, _("Items processed:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText96->Wrap( -1 ); - bSizer165->Add( m_staticText96, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); - - wxBoxSizer* bSizer169; - bSizer169 = new wxBoxSizer( wxHORIZONTAL ); - - m_staticTextProcessedObj = new wxStaticText( m_panelItemsProcessed, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_staticTextProcessedObj->Wrap( -1 ); - m_staticTextProcessedObj->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer169->Add( m_staticTextProcessedObj, 0, wxALIGN_BOTTOM, 5 ); - - m_staticTextDataProcessed = new wxStaticText( m_panelItemsProcessed, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextDataProcessed->Wrap( -1 ); - bSizer169->Add( m_staticTextDataProcessed, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); - - - bSizer165->Add( bSizer169, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer165->Add( 0, 5, 0, 0, 5 ); - - - m_panelItemsProcessed->SetSizer( bSizer165 ); - m_panelItemsProcessed->Layout(); - bSizer165->Fit( m_panelItemsProcessed ); - bSizer164->Add( m_panelItemsProcessed, 0, wxEXPAND|wxTOP, 7 ); - - m_panelItemsRemaining = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); - m_panelItemsRemaining->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer166; - bSizer166 = new wxBoxSizer( wxVERTICAL ); - - - bSizer166->Add( 0, 5, 0, 0, 5 ); - - wxStaticText* m_staticText97; - m_staticText97 = new wxStaticText( m_panelItemsRemaining, wxID_ANY, _("Items remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText97->Wrap( -1 ); - bSizer166->Add( m_staticText97, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); - - wxBoxSizer* bSizer170; - bSizer170 = new wxBoxSizer( wxHORIZONTAL ); - - m_staticTextRemainingObj = new wxStaticText( m_panelItemsRemaining, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_staticTextRemainingObj->Wrap( -1 ); - m_staticTextRemainingObj->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer170->Add( m_staticTextRemainingObj, 0, wxALIGN_BOTTOM, 5 ); - - m_staticTextDataRemaining = new wxStaticText( m_panelItemsRemaining, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextDataRemaining->Wrap( -1 ); - bSizer170->Add( m_staticTextDataRemaining, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); - - - bSizer166->Add( bSizer170, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer166->Add( 0, 5, 0, 0, 5 ); - - - m_panelItemsRemaining->SetSizer( bSizer166 ); - m_panelItemsRemaining->Layout(); - bSizer166->Fit( m_panelItemsRemaining ); - bSizer164->Add( m_panelItemsRemaining, 0, wxTOP|wxEXPAND, 7 ); - - m_panelTimeRemaining = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); - m_panelTimeRemaining->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer167; - bSizer167 = new wxBoxSizer( wxVERTICAL ); - - - bSizer167->Add( 0, 5, 0, 0, 5 ); - - wxStaticText* m_staticText98; - m_staticText98 = new wxStaticText( m_panelTimeRemaining, wxID_ANY, _("Time remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText98->Wrap( -1 ); - bSizer167->Add( m_staticText98, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); - - m_staticTextRemTime = new wxStaticText( m_panelTimeRemaining, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextRemTime->Wrap( -1 ); - m_staticTextRemTime->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer167->Add( m_staticTextRemTime, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer167->Add( 0, 5, 0, 0, 5 ); - - - m_panelTimeRemaining->SetSizer( bSizer167 ); - m_panelTimeRemaining->Layout(); - bSizer167->Fit( m_panelTimeRemaining ); - bSizer164->Add( m_panelTimeRemaining, 0, wxTOP|wxEXPAND, 7 ); - - wxPanel* m_panelTimeElapsed; - m_panelTimeElapsed = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); - m_panelTimeElapsed->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer168; - bSizer168 = new wxBoxSizer( wxVERTICAL ); - - - bSizer168->Add( 0, 5, 0, 0, 5 ); - - wxStaticText* m_staticText961; - m_staticText961 = new wxStaticText( m_panelTimeElapsed, wxID_ANY, _("Time elapsed:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText961->Wrap( -1 ); - bSizer168->Add( m_staticText961, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); - - m_staticTextTimeElapsed = new wxStaticText( m_panelTimeElapsed, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextTimeElapsed->Wrap( -1 ); - m_staticTextTimeElapsed->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer168->Add( m_staticTextTimeElapsed, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer168->Add( 0, 5, 0, 0, 5 ); - - - m_panelTimeElapsed->SetSizer( bSizer168 ); - m_panelTimeElapsed->Layout(); - bSizer168->Fit( m_panelTimeElapsed ); - bSizer164->Add( m_panelTimeElapsed, 0, wxTOP|wxEXPAND, 7 ); - - - bSizer171->Add( bSizer164, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer171->Add( 10, 0, 0, 0, 5 ); - - wxBoxSizer* bSizer161; - bSizer161 = new wxBoxSizer( wxVERTICAL ); - - - bSizer161->Add( 0, 15, 0, 0, 5 ); - - m_panelGraphBytes = new zen::Graph2D( m_panelProgress, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_panelGraphBytes->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - bSizer161->Add( m_panelGraphBytes, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - m_panelGraphItems = new zen::Graph2D( m_panelProgress, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_panelGraphItems->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - bSizer161->Add( m_panelGraphItems, 1, wxEXPAND, 5 ); - - - bSizer161->Add( 430, 0, 0, 0, 5 ); - - - bSizer171->Add( bSizer161, 1, wxEXPAND, 5 ); - - - bSizer171->Add( 0, 230, 0, 0, 5 ); - - - bSizer173->Add( bSizer171, 1, wxEXPAND, 5 ); - - - m_panelProgress->SetSizer( bSizer173 ); - m_panelProgress->Layout(); - bSizer173->Fit( m_panelProgress ); - bSizerRoot->Add( m_panelProgress, 1, wxEXPAND, 5 ); - - m_notebookResult = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_FIXEDWIDTH ); - - bSizerRoot->Add( m_notebookResult, 1, wxEXPAND, 5 ); - - m_staticlineFooter = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizerRoot->Add( m_staticlineFooter, 0, wxEXPAND, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer160; - bSizer160 = new wxBoxSizer( wxHORIZONTAL ); - - bSizerExecFinished = new wxBoxSizer( wxHORIZONTAL ); - - m_staticText87 = new wxStaticText( this, wxID_ANY, _("On completion:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText87->Wrap( -1 ); - bSizerExecFinished->Add( m_staticText87, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_comboBoxExecFinished = new ExecFinishedBox( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizerExecFinished->Add( m_comboBoxExecFinished, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer160->Add( bSizerExecFinished, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer160->Add( 0, 0, 0, 0, 5 ); - - - bSizerStdButtons->Add( bSizer160, 1, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); - - m_buttonClose = new wxButton( this, wxID_OK, _("Close"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - m_buttonClose->SetDefault(); - m_buttonClose->Enable( false ); - - bSizerStdButtons->Add( m_buttonClose, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_buttonPause = new wxButton( this, wxID_ANY, _("&Pause"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - bSizerStdButtons->Add( m_buttonPause, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - m_buttonStop = new wxButton( this, wxID_CANCEL, _("Stop"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - bSizerStdButtons->Add( m_buttonStop, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizerRoot->Add( bSizerStdButtons, 0, wxALIGN_RIGHT|wxEXPAND, 5 ); - - - this->SetSizer( bSizerRoot ); - this->Layout(); - bSizerRoot->Fit( this ); -} - -SyncProgressPanelGenerated::~SyncProgressPanelGenerated() -{ -} - -LogPanelGenerated::LogPanelGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) -{ - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer179; - bSizer179 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer153; - bSizer153 = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer154; - bSizer154 = new wxBoxSizer( wxVERTICAL ); - - m_bpButtonErrors = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49,49 ), wxBU_AUTODRAW ); - bSizer154->Add( m_bpButtonErrors, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonWarnings = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49,49 ), wxBU_AUTODRAW ); - bSizer154->Add( m_bpButtonWarnings, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_bpButtonInfo = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49,49 ), wxBU_AUTODRAW ); - bSizer154->Add( m_bpButtonInfo, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizer153->Add( bSizer154, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); - - m_staticline13 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer153->Add( m_staticline13, 0, wxEXPAND, 5 ); - - m_gridMessages = new zen::Grid( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); - m_gridMessages->SetScrollRate( 5, 5 ); - bSizer153->Add( m_gridMessages, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer179->Add( bSizer153, 1, wxEXPAND, 5 ); - - - this->SetSizer( bSizer179 ); - this->Layout(); - bSizer179->Fit( this ); - - // Connect Events - m_bpButtonErrors->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( LogPanelGenerated::OnErrors ), NULL, this ); - m_bpButtonWarnings->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( LogPanelGenerated::OnWarnings ), NULL, this ); - m_bpButtonInfo->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( LogPanelGenerated::OnInfo ), NULL, this ); -} - -LogPanelGenerated::~LogPanelGenerated() -{ -} - -BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) -{ - this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer54; - bSizer54 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer72; - bSizer72 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapBatchJob = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizer72->Add( m_bitmapBatchJob, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); - - m_staticTextDescr = new wxStaticText( this, wxID_ANY, _("Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextDescr->Wrap( 520 ); - bSizer72->Add( m_staticTextDescr, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 10 ); - - - bSizer54->Add( bSizer72, 0, 0, 5 ); - - m_staticline18 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer54->Add( m_staticline18, 0, wxEXPAND, 5 ); - - m_panel35 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panel35->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer172; - bSizer172 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer180; - bSizer180 = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer171; - bSizer171 = new wxBoxSizer( wxVERTICAL ); - - m_staticText82 = new wxStaticText( m_panel35, wxID_ANY, _("Handle errors:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText82->Wrap( -1 ); - bSizer171->Add( m_staticText82, 0, wxALL, 5 ); - - wxBoxSizer* bSizer169; - bSizer169 = new wxBoxSizer( wxHORIZONTAL ); - - m_toggleBtnErrorIgnore = new wxToggleButton( m_panel35, wxID_ANY, _("Ignore"), wxDefaultPosition, wxDefaultSize, 0 ); - m_toggleBtnErrorIgnore->SetToolTip( _("Hide all error and warning messages") ); - - bSizer169->Add( m_toggleBtnErrorIgnore, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 ); - - m_toggleBtnErrorPopup = new wxToggleButton( m_panel35, wxID_ANY, _("Pop-up"), wxDefaultPosition, wxDefaultSize, 0 ); - m_toggleBtnErrorPopup->SetToolTip( _("Show pop-up on errors or warnings") ); - - bSizer169->Add( m_toggleBtnErrorPopup, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_toggleBtnErrorStop = new wxToggleButton( m_panel35, wxID_ANY, _("Stop"), wxDefaultPosition, wxDefaultSize, 0 ); - m_toggleBtnErrorStop->SetToolTip( _("Stop synchronization at first error") ); - - bSizer169->Add( m_toggleBtnErrorStop, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer171->Add( bSizer169, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer180->Add( bSizer171, 0, wxALL, 5 ); - - m_staticline26 = new wxStaticLine( m_panel35, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer180->Add( m_staticline26, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer170; - bSizer170 = new wxBoxSizer( wxVERTICAL ); - - m_checkBoxShowProgress = new wxCheckBox( m_panel35, wxID_ANY, _("Show progress dialog"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer170->Add( m_checkBoxShowProgress, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxALL, 5 ); - - wxBoxSizer* bSizer179; - bSizer179 = new wxBoxSizer( wxHORIZONTAL ); - - m_staticText81 = new wxStaticText( m_panel35, wxID_ANY, _("On completion:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText81->Wrap( -1 ); - bSizer179->Add( m_staticText81, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_comboBoxExecFinished = new ExecFinishedBox( m_panel35, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizer179->Add( m_comboBoxExecFinished, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer170->Add( bSizer179, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer180->Add( bSizer170, 1, wxALL, 5 ); - - - bSizer172->Add( bSizer180, 0, wxEXPAND, 5 ); - - m_staticline25 = new wxStaticLine( m_panel35, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer172->Add( m_staticline25, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer191; - bSizer191 = new wxBoxSizer( wxVERTICAL ); - - m_checkBoxGenerateLogfile = new wxCheckBox( m_panel35, wxID_ANY, _("Save log:"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer191->Add( m_checkBoxGenerateLogfile, 0, wxEXPAND|wxBOTTOM, 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_logfileDir = new FolderHistoryBox( m_panelLogfile, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - bSizer1721->Add( m_logfileDir, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - m_buttonSelectLogfileDir = new wxButton( m_panelLogfile, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); - m_buttonSelectLogfileDir->SetToolTip( _("Select a folder") ); - - bSizer1721->Add( m_buttonSelectLogfileDir, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_checkBoxLogfilesLimit = new wxCheckBox( m_panelLogfile, wxID_ANY, _("Limit:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_checkBoxLogfilesLimit->SetToolTip( _("Limit maximum number of log files") ); - - bSizer1721->Add( m_checkBoxLogfilesLimit, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); - - m_spinCtrlLogfileLimit = new wxSpinCtrl( m_panelLogfile, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 60,-1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 ); - m_spinCtrlLogfileLimit->SetToolTip( _("Limit maximum number of log files") ); - - bSizer1721->Add( m_spinCtrlLogfileLimit, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - m_panelLogfile->SetSizer( bSizer1721 ); - m_panelLogfile->Layout(); - bSizer1721->Fit( m_panelLogfile ); - bSizer191->Add( m_panelLogfile, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer172->Add( bSizer191, 0, wxEXPAND|wxALL, 10 ); - - 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, wxALIGN_CENTER_VERTICAL|wxALL, 10 ); - - - m_panel35->SetSizer( bSizer172 ); - m_panel35->Layout(); - bSizer172->Fit( m_panel35 ); - bSizer54->Add( m_panel35, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - m_staticline13 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer54->Add( m_staticline13, 0, wxEXPAND, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - - bSizerStdButtons->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_buttonSaveAs = new wxButton( this, wxID_SAVE, _("Save &as..."), wxDefaultPosition, wxSize( -1,30 ), 0 ); - m_buttonSaveAs->SetDefault(); - m_buttonSaveAs->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizerStdButtons->Add( m_buttonSaveAs, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer54->Add( bSizerStdButtons, 0, wxALIGN_RIGHT|wxEXPAND, 5 ); - - - this->SetSizer( bSizer54 ); - this->Layout(); - bSizer54->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( BatchDlgGenerated::OnClose ) ); - m_toggleBtnErrorIgnore->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnErrorIgnore ), NULL, this ); - m_toggleBtnErrorPopup->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnErrorPopup ), NULL, this ); - m_toggleBtnErrorStop->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnErrorStop ), NULL, this ); - m_checkBoxGenerateLogfile->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnToggleGenerateLogfile ), NULL, this ); - m_checkBoxLogfilesLimit->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnToggleLogfilesLimit ), NULL, this ); - 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 ); -} - -BatchDlgGenerated::~BatchDlgGenerated() -{ -} - -DeleteDlgGenerated::DeleteDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) -{ - this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer24; - bSizer24 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer72; - bSizer72 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapDeleteType = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizer72->Add( m_bitmapDeleteType, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); - - m_staticTextHeader = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0|wxNO_BORDER ); - m_staticTextHeader->Wrap( -1 ); - bSizer72->Add( m_staticTextHeader, 0, wxALIGN_CENTER_VERTICAL|wxALL, 10 ); - - - bSizer24->Add( bSizer72, 0, 0, 5 ); - - m_staticline91 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer24->Add( m_staticline91, 0, wxEXPAND, 5 ); - - m_panel31 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panel31->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer185; - bSizer185 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer185->Add( 60, 0, 0, 0, 5 ); - - m_staticline42 = new wxStaticLine( m_panel31, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer185->Add( m_staticline42, 0, wxEXPAND, 5 ); - - m_textCtrlFileList = new wxTextCtrl( m_panel31, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 480,200 ), wxTE_DONTWRAP|wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER ); - bSizer185->Add( m_textCtrlFileList, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - - m_panel31->SetSizer( bSizer185 ); - m_panel31->Layout(); - bSizer185->Fit( m_panel31 ); - bSizer24->Add( m_panel31, 1, wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_staticline9 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer24->Add( m_staticline9, 0, wxEXPAND, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer99; - bSizer99 = new wxBoxSizer( wxVERTICAL ); - - m_checkBoxUseRecycler = new wxCheckBox( this, wxID_ANY, _("&Recycle bin"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer99->Add( m_checkBoxUseRecycler, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxTOP|wxBOTTOM|wxLEFT, 5 ); - - m_checkBoxDeleteBothSides = new wxCheckBox( this, wxID_ANY, _("Delete on both sides"), wxDefaultPosition, wxDefaultSize, 0 ); - m_checkBoxDeleteBothSides->Hide(); - m_checkBoxDeleteBothSides->SetToolTip( _("Delete on both sides even if the file is selected on one side only") ); - - bSizer99->Add( m_checkBoxDeleteBothSides, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxBOTTOM|wxLEFT, 5 ); - - - bSizerStdButtons->Add( bSizer99, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_buttonOK = new wxButton( this, wxID_OK, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - m_buttonOK->SetDefault(); - m_buttonOK->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizerStdButtons->Add( m_buttonOK, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer24->Add( bSizerStdButtons, 0, wxEXPAND, 5 ); - - - this->SetSizer( bSizer24 ); - this->Layout(); - bSizer24->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DeleteDlgGenerated::OnClose ) ); - m_checkBoxUseRecycler->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DeleteDlgGenerated::OnUseRecycler ), NULL, this ); - m_checkBoxDeleteBothSides->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DeleteDlgGenerated::OnDelOnBothSides ), NULL, this ); - m_buttonOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DeleteDlgGenerated::OnOK ), NULL, this ); - m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DeleteDlgGenerated::OnCancel ), NULL, this ); -} - -DeleteDlgGenerated::~DeleteDlgGenerated() -{ -} - -FilterDlgGenerated::FilterDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) -{ - this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer21; - bSizer21 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer72; - bSizer72 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapFilter = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizer72->Add( m_bitmapFilter, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); - - m_staticText44 = new wxStaticText( this, wxID_ANY, _("Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair."), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_staticText44->Wrap( 480 ); - bSizer72->Add( m_staticText44, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 10 ); - - - bSizer21->Add( bSizer72, 0, 0, 5 ); - - m_staticline17 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer21->Add( m_staticline17, 0, wxEXPAND, 5 ); - - m_panel38 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panel38->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer159; - bSizer159 = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer166; - bSizer166 = new wxBoxSizer( wxVERTICAL ); - - - bSizer166->Add( 0, 10, 0, 0, 5 ); - - wxBoxSizer* bSizer1661; - bSizer1661 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapInclude = new wxStaticBitmap( m_panel38, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 30,30 ), 0 ); - bSizer1661->Add( m_bitmapInclude, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); - - wxBoxSizer* bSizer173; - bSizer173 = new wxBoxSizer( wxVERTICAL ); - - m_staticText78 = new wxStaticText( m_panel38, wxID_ANY, _("Include:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText78->Wrap( -1 ); - bSizer173->Add( m_staticText78, 0, 0, 5 ); - - m_textCtrlInclude = new wxTextCtrl( m_panel38, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE ); - m_textCtrlInclude->SetMinSize( wxSize( 280,-1 ) ); - - bSizer173->Add( m_textCtrlInclude, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxTOP, 5 ); - - - bSizer1661->Add( bSizer173, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer166->Add( bSizer1661, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxLEFT, 5 ); - - m_staticline22 = new wxStaticLine( m_panel38, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer166->Add( m_staticline22, 0, wxEXPAND, 5 ); - - - bSizer166->Add( 0, 10, 0, 0, 5 ); - - wxBoxSizer* bSizer1651; - bSizer1651 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapExclude = new wxStaticBitmap( m_panel38, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 30,30 ), 0 ); - bSizer1651->Add( m_bitmapExclude, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - wxBoxSizer* bSizer174; - bSizer174 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer189; - bSizer189 = new wxBoxSizer( wxHORIZONTAL ); - - m_staticText77 = new wxStaticText( m_panel38, wxID_ANY, _("Exclude:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText77->Wrap( -1 ); - bSizer189->Add( m_staticText77, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer189->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_hyperlink17 = new wxHyperlinkCtrl( m_panel38, wxID_ANY, _("Show examples"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - bSizer189->Add( m_hyperlink17, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); - - - bSizer174->Add( bSizer189, 0, wxEXPAND, 5 ); - - m_textCtrlExclude = new wxTextCtrl( m_panel38, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE ); - bSizer174->Add( m_textCtrlExclude, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxTOP, 5 ); - - - bSizer1651->Add( bSizer174, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer166->Add( bSizer1651, 2, wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxLEFT, 5 ); - - - bSizer159->Add( bSizer166, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - m_staticline24 = new wxStaticLine( m_panel38, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer159->Add( m_staticline24, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - wxBoxSizer* bSizer160; - bSizer160 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer167; - bSizer167 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapFilterDate = new wxStaticBitmap( m_panel38, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 34,34 ), 0 ); - bSizer167->Add( m_bitmapFilterDate, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - wxBoxSizer* bSizer165; - bSizer165 = new wxBoxSizer( wxVERTICAL ); - - m_staticText79 = new wxStaticText( m_panel38, wxID_ANY, _("Time span:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText79->Wrap( -1 ); - bSizer165->Add( m_staticText79, 0, wxBOTTOM, 5 ); - - m_spinCtrlTimespan = new wxSpinCtrl( m_panel38, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 2000000000, 0 ); - bSizer165->Add( m_spinCtrlTimespan, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - wxArrayString m_choiceUnitTimespanChoices; - m_choiceUnitTimespan = new wxChoice( m_panel38, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceUnitTimespanChoices, 0 ); - m_choiceUnitTimespan->SetSelection( 0 ); - bSizer165->Add( m_choiceUnitTimespan, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer167->Add( bSizer165, 1, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer160->Add( bSizer167, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxALL, 5 ); - - m_staticline23 = new wxStaticLine( m_panel38, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer160->Add( m_staticline23, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer168; - bSizer168 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapFilterSize = new wxStaticBitmap( m_panel38, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32,32 ), 0 ); - bSizer168->Add( m_bitmapFilterSize, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); - - wxBoxSizer* bSizer158; - bSizer158 = new wxBoxSizer( wxVERTICAL ); - - m_staticText80 = new wxStaticText( m_panel38, wxID_ANY, _("File size:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText80->Wrap( -1 ); - bSizer158->Add( m_staticText80, 0, wxBOTTOM, 5 ); - - wxBoxSizer* bSizer162; - bSizer162 = new wxBoxSizer( wxVERTICAL ); - - m_staticText101 = new wxStaticText( m_panel38, wxID_ANY, _("Minimum:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText101->Wrap( -1 ); - bSizer162->Add( m_staticText101, 0, wxBOTTOM, 2 ); - - m_spinCtrlMinSize = new wxSpinCtrl( m_panel38, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 2000000000, 0 ); - bSizer162->Add( m_spinCtrlMinSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - wxArrayString m_choiceUnitMinSizeChoices; - m_choiceUnitMinSize = new wxChoice( m_panel38, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceUnitMinSizeChoices, 0 ); - m_choiceUnitMinSize->SetSelection( 0 ); - bSizer162->Add( m_choiceUnitMinSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer158->Add( bSizer162, 0, wxBOTTOM|wxEXPAND, 5 ); - - wxBoxSizer* bSizer163; - bSizer163 = new wxBoxSizer( wxVERTICAL ); - - m_staticText102 = new wxStaticText( m_panel38, wxID_ANY, _("Maximum:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText102->Wrap( -1 ); - bSizer163->Add( m_staticText102, 0, wxBOTTOM, 2 ); - - m_spinCtrlMaxSize = new wxSpinCtrl( m_panel38, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 2000000000, 0 ); - bSizer163->Add( m_spinCtrlMaxSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - wxArrayString m_choiceUnitMaxSizeChoices; - m_choiceUnitMaxSize = new wxChoice( m_panel38, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceUnitMaxSizeChoices, 0 ); - m_choiceUnitMaxSize->SetSelection( 0 ); - bSizer163->Add( m_choiceUnitMaxSize, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer158->Add( bSizer163, 0, wxEXPAND, 5 ); - - - bSizer168->Add( bSizer158, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer160->Add( bSizer168, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxALL, 5 ); - - - bSizer159->Add( bSizer160, 0, wxEXPAND, 5 ); - - - m_panel38->SetSizer( bSizer159 ); - m_panel38->Layout(); - bSizer159->Fit( m_panel38 ); - bSizer21->Add( m_panel38, 1, wxEXPAND, 5 ); - - m_staticline16 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer21->Add( m_staticline16, 0, wxEXPAND, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - m_buttonClear = new wxButton( this, wxID_DEFAULT, _("&Clear"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - bSizerStdButtons->Add( m_buttonClear, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); - - - bSizerStdButtons->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); - - m_buttonOk = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - m_buttonOk->SetDefault(); - m_buttonOk->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizerStdButtons->Add( m_buttonOk, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer21->Add( bSizerStdButtons, 0, wxEXPAND, 5 ); - - - this->SetSizer( bSizer21 ); - this->Layout(); - bSizer21->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( FilterDlgGenerated::OnClose ) ); - m_textCtrlInclude->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( FilterDlgGenerated::OnUpdateNameFilter ), NULL, this ); - m_hyperlink17->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( FilterDlgGenerated::OnHelpShowExamples ), NULL, this ); - m_textCtrlExclude->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( FilterDlgGenerated::OnUpdateNameFilter ), NULL, this ); - m_choiceUnitTimespan->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( FilterDlgGenerated::OnUpdateChoice ), NULL, this ); - m_choiceUnitMinSize->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( FilterDlgGenerated::OnUpdateChoice ), NULL, this ); - m_choiceUnitMaxSize->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( FilterDlgGenerated::OnUpdateChoice ), NULL, this ); - m_buttonClear->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnClear ), NULL, this ); - m_buttonOk->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnOkay ), NULL, this ); - m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnCancel ), NULL, this ); -} - -FilterDlgGenerated::~FilterDlgGenerated() -{ -} - -GlobalSettingsDlgGenerated::GlobalSettingsDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) -{ - this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer95; - bSizer95 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer72; - bSizer72 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapSettings = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizer72->Add( m_bitmapSettings, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); - - m_staticText44 = new wxStaticText( this, wxID_ANY, _("The following settings are used for all synchronization jobs."), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_staticText44->Wrap( 500 ); - bSizer72->Add( m_staticText44, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 10 ); - - - bSizer95->Add( bSizer72, 0, 0, 5 ); - - m_staticline20 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer95->Add( m_staticline20, 0, wxEXPAND, 5 ); - - m_panel39 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panel39->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer166; - bSizer166 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer186; - bSizer186 = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer160; - bSizer160 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer176; - bSizer176 = new wxBoxSizer( wxHORIZONTAL ); - - m_checkBoxFailSafe = new wxCheckBox( m_panel39, wxID_ANY, _("Fail-safe file copy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_checkBoxFailSafe->SetValue(true); - m_checkBoxFailSafe->SetToolTip( _("Copy to a temporary file (*.ffs_tmp) before overwriting target.\nThis guarantees a consistent state even in case of a serious error.") ); - - bSizer176->Add( m_checkBoxFailSafe, 1, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticText91 = new wxStaticText( m_panel39, wxID_ANY, _("(recommended)"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText91->Wrap( -1 ); - m_staticText91->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); - - bSizer176->Add( m_staticText91, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer160->Add( bSizer176, 0, wxEXPAND, 5 ); - - bSizerLockedFiles = new wxBoxSizer( wxHORIZONTAL ); - - m_checkBoxCopyLocked = new wxCheckBox( m_panel39, wxID_ANY, _("Copy locked files"), wxDefaultPosition, wxDefaultSize, 0 ); - m_checkBoxCopyLocked->SetValue(true); - m_checkBoxCopyLocked->SetToolTip( _("Copy shared or locked files using the Volume Shadow Copy Service.") ); - - bSizerLockedFiles->Add( m_checkBoxCopyLocked, 1, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticText92 = new wxStaticText( m_panel39, wxID_ANY, _("(requires administrator rights)"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText92->Wrap( -1 ); - m_staticText92->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); - - bSizerLockedFiles->Add( m_staticText92, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer160->Add( bSizerLockedFiles, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer178; - bSizer178 = new wxBoxSizer( wxHORIZONTAL ); - - m_checkBoxCopyPermissions = new wxCheckBox( m_panel39, wxID_ANY, _("Copy file access permissions"), wxDefaultPosition, wxDefaultSize, 0 ); - m_checkBoxCopyPermissions->SetValue(true); - m_checkBoxCopyPermissions->SetToolTip( _("Transfer file and folder permissions.") ); - - bSizer178->Add( m_checkBoxCopyPermissions, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_staticText93 = new wxStaticText( m_panel39, wxID_ANY, _("(requires administrator rights)"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText93->Wrap( -1 ); - m_staticText93->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); - - bSizer178->Add( m_staticText93, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer160->Add( bSizer178, 0, wxEXPAND, 5 ); - - - bSizer186->Add( bSizer160, 0, wxEXPAND|wxALL, 5 ); - - m_staticline39 = new wxStaticLine( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer186->Add( m_staticline39, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer188; - bSizer188 = new wxBoxSizer( wxVERTICAL ); - - m_staticText95 = new wxStaticText( m_panel39, wxID_ANY, _("Automatic retry on error:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText95->Wrap( -1 ); - bSizer188->Add( m_staticText95, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM, 5 ); - - wxFlexGridSizer* fgSizer6; - fgSizer6 = new wxFlexGridSizer( 0, 2, 5, 5 ); - fgSizer6->SetFlexibleDirection( wxBOTH ); - fgSizer6->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - - m_staticText96 = new wxStaticText( m_panel39, wxID_ANY, _("Retry count:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText96->Wrap( -1 ); - fgSizer6->Add( m_staticText96, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_spinCtrlAutoRetryCount = new wxSpinCtrl( m_panel39, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 60,-1 ), wxSP_ARROW_KEYS, 0, 2000000000, 4 ); - fgSizer6->Add( m_spinCtrlAutoRetryCount, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextAutoRetryDelay = new wxStaticText( m_panel39, wxID_ANY, _("Delay (in seconds):"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextAutoRetryDelay->Wrap( -1 ); - fgSizer6->Add( m_staticTextAutoRetryDelay, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_spinCtrlAutoRetryDelay = new wxSpinCtrl( m_panel39, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 60,-1 ), wxSP_ARROW_KEYS, 0, 2000000000, 0 ); - fgSizer6->Add( m_spinCtrlAutoRetryDelay, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer188->Add( fgSizer6, 0, wxLEFT, 10 ); - - - bSizer186->Add( bSizer188, 0, wxALL, 10 ); - - - bSizer166->Add( bSizer186, 0, wxEXPAND, 5 ); - - m_staticline191 = new wxStaticLine( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer166->Add( m_staticline191, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer181; - bSizer181 = new wxBoxSizer( wxVERTICAL ); - - m_staticText85 = new wxStaticText( m_panel39, wxID_ANY, _("Customize context menu:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText85->Wrap( -1 ); - bSizer181->Add( m_staticText85, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM, 5 ); - - m_gridCustomCommand = new wxGrid( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); - - // Grid - m_gridCustomCommand->CreateGrid( 5, 2 ); - m_gridCustomCommand->EnableEditing( true ); - m_gridCustomCommand->EnableGridLines( true ); - m_gridCustomCommand->EnableDragGridSize( false ); - m_gridCustomCommand->SetMargins( 0, 0 ); - - // Columns - m_gridCustomCommand->SetColSize( 0, 165 ); - m_gridCustomCommand->SetColSize( 1, 196 ); - m_gridCustomCommand->EnableDragColMove( false ); - m_gridCustomCommand->EnableDragColSize( true ); - m_gridCustomCommand->SetColLabelSize( 20 ); - m_gridCustomCommand->SetColLabelValue( 0, _("Description") ); - m_gridCustomCommand->SetColLabelValue( 1, _("Command line") ); - m_gridCustomCommand->SetColLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE ); - - // Rows - m_gridCustomCommand->EnableDragRowSize( false ); - m_gridCustomCommand->SetRowLabelSize( 1 ); - m_gridCustomCommand->SetRowLabelAlignment( wxALIGN_LEFT, wxALIGN_CENTRE ); - - // Label Appearance - - // Cell Defaults - m_gridCustomCommand->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_TOP ); - bSizer181->Add( m_gridCustomCommand, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - wxBoxSizer* bSizer193; - bSizer193 = new wxBoxSizer( wxHORIZONTAL ); - - m_bpButtonAddRow = new wxBitmapButton( m_panel39, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - bSizer193->Add( m_bpButtonAddRow, 0, 0, 5 ); - - m_bpButtonRemoveRow = new wxBitmapButton( m_panel39, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); - bSizer193->Add( m_bpButtonRemoveRow, 0, 0, 5 ); - - - bSizer193->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_hyperlink17 = new wxHyperlinkCtrl( m_panel39, wxID_ANY, _("Show examples"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - bSizer193->Add( m_hyperlink17, 0, wxLEFT, 5 ); - - - bSizer181->Add( bSizer193, 0, wxTOP|wxEXPAND, 5 ); - - - bSizer166->Add( bSizer181, 1, wxEXPAND|wxALL, 10 ); - - m_staticline192 = new wxStaticLine( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer166->Add( m_staticline192, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer1881; - bSizer1881 = new wxBoxSizer( wxHORIZONTAL ); - - m_buttonResetDialogs = new zen::BitmapTextButton( m_panel39, wxID_ANY, _("Restore hidden windows"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizer1881->Add( m_buttonResetDialogs, 0, wxALL|wxALIGN_CENTER_VERTICAL, 10 ); - - m_staticline40 = new wxStaticLine( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer1881->Add( m_staticline40, 0, wxEXPAND, 5 ); - - - bSizer166->Add( bSizer1881, 0, 0, 5 ); - - - m_panel39->SetSizer( bSizer166 ); - m_panel39->Layout(); - bSizer166->Fit( m_panel39 ); - bSizer95->Add( m_panel39, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - m_staticline36 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer95->Add( m_staticline36, 0, wxEXPAND, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - m_buttonDefault = new wxButton( this, wxID_DEFAULT, _("&Default"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - bSizerStdButtons->Add( m_buttonDefault, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerStdButtons->Add( 0, 0, 1, 0, 5 ); - - m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - m_buttonOkay->SetDefault(); - m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizerStdButtons->Add( m_buttonOkay, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer95->Add( bSizerStdButtons, 0, wxEXPAND, 5 ); - - - this->SetSizer( bSizer95 ); - this->Layout(); - bSizer95->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( GlobalSettingsDlgGenerated::OnClose ) ); - m_spinCtrlAutoRetryCount->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnToggleAutoRetryCount ), NULL, this ); - m_bpButtonAddRow->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnAddRow ), NULL, this ); - m_bpButtonRemoveRow->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnRemoveRow ), NULL, this ); - m_hyperlink17->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( GlobalSettingsDlgGenerated::OnHelpShowExamples ), NULL, this ); - m_buttonResetDialogs->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnResetDialogs ), NULL, this ); - m_buttonDefault->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnDefault ), NULL, this ); - m_buttonOkay->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnOkay ), NULL, this ); - m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnCancel ), NULL, this ); -} - -GlobalSettingsDlgGenerated::~GlobalSettingsDlgGenerated() -{ -} - -TooltipDialogGenerated::TooltipDialogGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) -{ - this->SetSizeHints( wxDefaultSize, wxDefaultSize ); - - wxBoxSizer* bSizer158; - bSizer158 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapLeft = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizer158->Add( m_bitmapLeft, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticTextMain = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextMain->Wrap( 600 ); - bSizer158->Add( m_staticTextMain, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - this->SetSizer( bSizer158 ); - this->Layout(); - bSizer158->Fit( this ); -} - -TooltipDialogGenerated::~TooltipDialogGenerated() -{ -} - -SelectTimespanDlgGenerated::SelectTimespanDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) -{ - this->SetSizeHints( wxDefaultSize, wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer96; - bSizer96 = new wxBoxSizer( wxVERTICAL ); - - m_panel35 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panel35->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer98; - bSizer98 = new wxBoxSizer( wxHORIZONTAL ); - - m_calendarFrom = new wxCalendarCtrl( m_panel35, wxID_ANY, wxDefaultDateTime, wxDefaultPosition, wxDefaultSize, wxCAL_SHOW_HOLIDAYS|wxNO_BORDER ); - bSizer98->Add( m_calendarFrom, 0, wxTOP|wxBOTTOM|wxLEFT, 10 ); - - m_calendarTo = new wxCalendarCtrl( m_panel35, wxID_ANY, wxDefaultDateTime, wxDefaultPosition, wxDefaultSize, wxCAL_SHOW_HOLIDAYS|wxNO_BORDER ); - bSizer98->Add( m_calendarTo, 0, wxALL, 10 ); - - - m_panel35->SetSizer( bSizer98 ); - m_panel35->Layout(); - bSizer98->Fit( m_panel35 ); - bSizer96->Add( m_panel35, 0, wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_staticline21 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer96->Add( m_staticline21, 0, wxEXPAND, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - m_buttonOkay->SetDefault(); - m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizerStdButtons->Add( m_buttonOkay, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer96->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); - - - this->SetSizer( bSizer96 ); - this->Layout(); - bSizer96->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SelectTimespanDlgGenerated::OnClose ) ); - m_calendarFrom->Connect( wxEVT_CALENDAR_SEL_CHANGED, wxCalendarEventHandler( SelectTimespanDlgGenerated::OnChangeSelectionFrom ), NULL, this ); - m_calendarTo->Connect( wxEVT_CALENDAR_SEL_CHANGED, wxCalendarEventHandler( SelectTimespanDlgGenerated::OnChangeSelectionTo ), NULL, this ); - m_buttonOkay->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SelectTimespanDlgGenerated::OnOkay ), NULL, this ); - m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SelectTimespanDlgGenerated::OnCancel ), NULL, this ); -} - -SelectTimespanDlgGenerated::~SelectTimespanDlgGenerated() -{ -} - -AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) -{ - this->SetSizeHints( wxDefaultSize, wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer31; - bSizer31 = new wxBoxSizer( wxVERTICAL ); - - m_panel41 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panel41->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer162; - bSizer162 = new wxBoxSizer( wxVERTICAL ); - - m_bitmapLogo = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizer162->Add( m_bitmapLogo, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - m_staticline341 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer162->Add( m_staticline341, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer174; - bSizer174 = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizer181; - bSizer181 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer187; - bSizer187 = new wxBoxSizer( wxVERTICAL ); - - m_staticText96 = new wxStaticText( m_panel41, wxID_ANY, _("Source code written in C++ using:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText96->Wrap( -1 ); - bSizer187->Add( m_staticText96, 0, wxALL, 5 ); - - wxBoxSizer* bSizer171; - bSizer171 = new wxBoxSizer( wxHORIZONTAL ); - - m_hyperlink11 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("MS Visual C++"), wxT("http://msdn.microsoft.com/library/60k1461a.aspx"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink11->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink11->SetToolTip( _("http://msdn.microsoft.com/library/60k1461a.aspx") ); - - bSizer171->Add( m_hyperlink11, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_hyperlink9 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("MinGW"), wxT("http://www.mingw.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink9->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink9->SetToolTip( _("http://www.mingw.org") ); - - bSizer171->Add( m_hyperlink9, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_hyperlink10 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Code::Blocks"), wxT("http://www.codeblocks.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink10->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink10->SetToolTip( _("http://www.codeblocks.org") ); - - bSizer171->Add( m_hyperlink10, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_hyperlink7 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("wxWidgets"), wxT("http://www.wxwidgets.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink7->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink7->SetToolTip( _("http://www.wxwidgets.org") ); - - bSizer171->Add( m_hyperlink7, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_hyperlink14 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("wxFormBuilder"), wxT("http://wxformbuilder.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink14->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink14->SetToolTip( _("http://wxformbuilder.org") ); - - bSizer171->Add( m_hyperlink14, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer187->Add( bSizer171, 0, wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - wxBoxSizer* bSizer172; - bSizer172 = new wxBoxSizer( wxHORIZONTAL ); - - m_hyperlink15 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("zen::Xml"), wxT("http://zenxml.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink15->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink15->SetToolTip( _("http://zenxml.sourceforge.net") ); - - bSizer172->Add( m_hyperlink15, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_hyperlink13 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Boost"), wxT("http://www.boost.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink13->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink13->SetToolTip( _("http://www.boost.org") ); - - bSizer172->Add( m_hyperlink13, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_hyperlink16 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Artistic Style"), wxT("http://astyle.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink16->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink16->SetToolTip( _("http://astyle.sourceforge.net") ); - - bSizer172->Add( m_hyperlink16, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_hyperlink12 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Google Test"), wxT("http://code.google.com/p/googletest"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink12->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink12->SetToolTip( _("http://code.google.com/p/googletest") ); - - bSizer172->Add( m_hyperlink12, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_hyperlink18 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Unicode NSIS"), wxT("http://www.scratchpaper.com"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink18->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink18->SetToolTip( _("http://www.scratchpaper.com") ); - - bSizer172->Add( m_hyperlink18, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer187->Add( bSizer172, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizer181->Add( bSizer187, 0, wxALL|wxEXPAND, 5 ); - - m_panelDonate = new wxPanel( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelDonate->SetBackgroundColour( wxColour( 153, 170, 187 ) ); - - wxBoxSizer* bSizer183; - bSizer183 = new wxBoxSizer( wxVERTICAL ); - - m_panel39 = new wxPanel( m_panelDonate, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panel39->SetBackgroundColour( wxColour( 221, 221, 255 ) ); - - wxBoxSizer* bSizer184; - bSizer184 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer184->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_animCtrlWink = new wxAnimationCtrl( m_panel39, wxID_ANY, wxNullAnimation, wxDefaultPosition, wxSize( 48,48 ), wxAC_DEFAULT_STYLE ); - bSizer184->Add( m_animCtrlWink, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - wxBoxSizer* bSizer178; - bSizer178 = new wxBoxSizer( wxVERTICAL ); - - m_staticText83 = new wxStaticText( m_panel39, wxID_ANY, _("If you like FreeFileSync"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText83->Wrap( -1 ); - m_staticText83->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - m_staticText83->SetForegroundColour( wxColour( 0, 0, 0 ) ); - - bSizer178->Add( m_staticText83, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 ); - - m_buttonDonate = new wxButton( m_panel39, wxID_ANY, _("Donate with PayPal"), wxDefaultPosition, wxDefaultSize, 0 ); - m_buttonDonate->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString ) ); - m_buttonDonate->SetToolTip( _("http://freefilesync.sourceforge.net/donate.php") ); - - bSizer178->Add( m_buttonDonate, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 ); - - - bSizer184->Add( bSizer178, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer184->Add( 0, 0, 1, wxEXPAND, 5 ); - - - m_panel39->SetSizer( bSizer184 ); - m_panel39->Layout(); - bSizer184->Fit( m_panel39 ); - bSizer183->Add( m_panel39, 0, wxEXPAND|wxALL, 5 ); - - - m_panelDonate->SetSizer( bSizer183 ); - m_panelDonate->Layout(); - bSizer183->Fit( m_panelDonate ); - bSizer181->Add( m_panelDonate, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxRIGHT|wxLEFT, 5 ); - - wxBoxSizer* bSizer186; - bSizer186 = new wxBoxSizer( wxVERTICAL ); - - m_staticText94 = new wxStaticText( m_panel41, wxID_ANY, _("Feedback and suggestions are welcome"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText94->Wrap( -1 ); - bSizer186->Add( m_staticText94, 0, wxALL, 5 ); - - wxBoxSizer* bSizer166; - bSizer166 = new wxBoxSizer( wxHORIZONTAL ); - - - bSizer166->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_bitmap9 = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_bitmap9->SetToolTip( _("Homepage") ); - - bSizer166->Add( m_bitmap9, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); - - m_hyperlink1 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("freefilesync.sf.net"), wxT("http://freefilesync.sf.net/"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink1->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, true, wxEmptyString ) ); - m_hyperlink1->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - bSizer166->Add( m_hyperlink1, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer166->Add( 0, 0, 1, wxEXPAND, 5 ); - - m_bitmap10 = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_bitmap10->SetToolTip( _("Email") ); - - bSizer166->Add( m_bitmap10, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); - - m_hyperlink2 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("zenju@gmx.de"), wxT("mailto:zenju@gmx.de"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink2->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, true, wxEmptyString ) ); - m_hyperlink2->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - bSizer166->Add( m_hyperlink2, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer166->Add( 0, 0, 1, wxEXPAND, 5 ); - - - bSizer186->Add( bSizer166, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer181->Add( bSizer186, 0, wxALL|wxEXPAND, 5 ); - - m_staticline34 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer181->Add( m_staticline34, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer185; - bSizer185 = new wxBoxSizer( wxVERTICAL ); - - m_staticText93 = new wxStaticText( m_panel41, wxID_ANY, _("Published under the GNU General Public License"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText93->Wrap( -1 ); - bSizer185->Add( m_staticText93, 0, wxALL, 5 ); - - wxBoxSizer* bSizer1671; - bSizer1671 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmap13 = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizer1671->Add( m_bitmap13, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_hyperlink5 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("http://www.gnu.org/licenses/gpl.html"), wxT("http://www.gnu.org/licenses/gpl.html"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink5->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - bSizer1671->Add( m_hyperlink5, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer185->Add( bSizer1671, 0, wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer181->Add( bSizer185, 0, wxALL|wxEXPAND, 5 ); - - - bSizer174->Add( bSizer181, 0, 0, 5 ); - - m_staticline37 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer174->Add( m_staticline37, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer177; - bSizer177 = new wxBoxSizer( wxVERTICAL ); - - m_staticText54 = new wxStaticText( m_panel41, wxID_ANY, _("Many thanks for localization:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText54->Wrap( 200 ); - m_staticText54->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); - - bSizer177->Add( m_staticText54, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); - - - bSizer177->Add( 0, 5, 0, 0, 5 ); - - m_scrolledWindowTranslators = new wxScrolledWindow( m_panel41, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), wxVSCROLL ); - m_scrolledWindowTranslators->SetScrollRate( 10, 10 ); - m_scrolledWindowTranslators->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_scrolledWindowTranslators->SetMinSize( wxSize( 220,-1 ) ); - - fgSizerTranslators = new wxFlexGridSizer( 0, 2, 2, 10 ); - fgSizerTranslators->SetFlexibleDirection( wxBOTH ); - fgSizerTranslators->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - - - m_scrolledWindowTranslators->SetSizer( fgSizerTranslators ); - m_scrolledWindowTranslators->Layout(); - fgSizerTranslators->Fit( m_scrolledWindowTranslators ); - bSizer177->Add( m_scrolledWindowTranslators, 1, wxALIGN_CENTER_HORIZONTAL|wxLEFT|wxEXPAND, 5 ); - - - bSizer174->Add( bSizer177, 0, wxEXPAND|wxTOP|wxLEFT, 5 ); - - - bSizer162->Add( bSizer174, 0, 0, 5 ); - - - m_panel41->SetSizer( bSizer162 ); - m_panel41->Layout(); - bSizer162->Fit( m_panel41 ); - bSizer31->Add( m_panel41, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - m_staticline36 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer31->Add( m_staticline36, 0, wxEXPAND, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - m_buttonClose = new wxButton( this, wxID_OK, _("Close"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - m_buttonClose->SetDefault(); - bSizerStdButtons->Add( m_buttonClose, 0, wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizer31->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); - - - this->SetSizer( bSizer31 ); - this->Layout(); - bSizer31->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( AboutDlgGenerated::OnClose ) ); - m_buttonDonate->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AboutDlgGenerated::OnDonate ), NULL, this ); - m_buttonClose->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AboutDlgGenerated::OnOK ), NULL, this ); -} - -AboutDlgGenerated::~AboutDlgGenerated() -{ -} diff --git a/ui/gui_generated.h b/ui/gui_generated.h deleted file mode 100644 index 04aed9a9..00000000 --- a/ui/gui_generated.h +++ /dev/null @@ -1,880 +0,0 @@ -/////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Oct 8 2012) -// http://www.wxformbuilder.org/ -// -// PLEASE DO "NOT" EDIT THIS FILE! -/////////////////////////////////////////////////////////////////////////// - -#ifndef __GUI_GENERATED_H__ -#define __GUI_GENERATED_H__ - -#include -#include -#include -class ExecFinishedBox; -class FolderHistoryBox; -class ToggleButton; -namespace zen { class BitmapTextButton; } -namespace zen { class Graph2D; } -namespace zen { class Grid; } -namespace zen { class TripleSplitter; } - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "zen/i18n.h" - -/////////////////////////////////////////////////////////////////////////// - - -/////////////////////////////////////////////////////////////////////////////// -/// Class MainDialogGenerated -/////////////////////////////////////////////////////////////////////////////// -class MainDialogGenerated : public wxFrame -{ -private: - -protected: - wxMenuBar* m_menubar1; - wxMenu* m_menuFile; - wxMenuItem* m_menuItemNew; - wxMenuItem* m_menuItemLoad; - wxMenuItem* m_menuItemSave; - wxMenuItem* m_menuItemSaveAs; - wxMenuItem* m_menuItem7; - wxMenuItem* m_menuItem10; - wxMenuItem* m_menuItem11; - wxMenu* m_menuTools; - wxMenuItem* m_menuItemGlobSett; - wxMenu* m_menuLanguages; - wxMenu* m_menuHelp; - wxMenuItem* m_menuItemManual; - wxMenu* m_menuCheckVersion; - wxMenuItem* m_menuItemCheckVersionNow; - wxMenuItem* m_menuItemCheckVersionAuto; - wxMenuItem* m_menuItemAbout; - wxBoxSizer* bSizerPanelHolder; - wxPanel* m_panelTopButtons; - wxBoxSizer* bSizerTopButtons; - zen::BitmapTextButton* m_buttonCompare; - zen::BitmapTextButton* m_buttonCancel; - wxBitmapButton* m_bpButtonCmpConfig; - wxBitmapButton* m_bpButtonSyncConfig; - zen::BitmapTextButton* m_buttonSync; - wxPanel* m_panelDirectoryPairs; - wxStaticText* m_staticTextResolvedPathL; - wxBitmapButton* m_bpButtonAddPair; - wxButton* m_buttonSelectDirLeft; - wxPanel* m_panelTopMiddle; - wxBitmapButton* m_bpButtonSwapSides; - wxStaticText* m_staticTextResolvedPathR; - wxButton* m_buttonSelectDirRight; - wxScrolledWindow* m_scrolledWindowFolderPairs; - wxBoxSizer* bSizerAddFolderPairs; - zen::Grid* m_gridNavi; - wxPanel* m_panelCenter; - zen::TripleSplitter* m_splitterMain; - zen::Grid* m_gridMainL; - zen::Grid* m_gridMainC; - zen::Grid* m_gridMainR; - wxPanel* m_panelStatusBar; - wxBoxSizer* bSizerFileStatus; - wxBoxSizer* bSizerStatusLeft; - wxBoxSizer* bSizerStatusLeftDirectories; - wxStaticBitmap* m_bitmapSmallDirectoryLeft; - wxStaticText* m_staticTextStatusLeftDirs; - wxBoxSizer* bSizerStatusLeftFiles; - wxStaticBitmap* m_bitmapSmallFileLeft; - wxStaticText* m_staticTextStatusLeftFiles; - wxStaticText* m_staticTextStatusLeftBytes; - wxStaticLine* m_staticline9; - wxStaticText* m_staticTextStatusMiddle; - wxBoxSizer* bSizerStatusRight; - wxStaticLine* m_staticline10; - wxBoxSizer* bSizerStatusRightDirectories; - wxStaticBitmap* m_bitmapSmallDirectoryRight; - wxStaticText* m_staticTextStatusRightDirs; - wxBoxSizer* bSizerStatusRightFiles; - wxStaticBitmap* m_bitmapSmallFileRight; - wxStaticText* m_staticTextStatusRightFiles; - wxStaticText* m_staticTextStatusRightBytes; - wxStaticText* m_staticTextFullStatus; - wxPanel* m_panelSearch; - wxBitmapButton* m_bpButtonHideSearch; - wxStaticText* m_staticText101; - wxTextCtrl* m_textCtrlSearchTxt; - wxCheckBox* m_checkBoxMatchCase; - wxPanel* m_panelConfig; - wxBoxSizer* bSizerConfig; - wxBitmapButton* m_bpButtonOpen; - wxBitmapButton* m_bpButtonSave; - wxBitmapButton* m_bpButtonBatchJob; - wxListBox* m_listBoxHistory; - wxPanel* m_panelFilter; - wxBitmapButton* m_bpButtonFilter; - wxCheckBox* m_checkBoxHideExcluded; - wxPanel* m_panelStatistics; - wxBoxSizer* bSizer1801; - wxStaticBitmap* m_bitmapCreateLeft; - wxStaticText* m_staticTextCreateLeft; - wxStaticBitmap* m_bitmapUpdateLeft; - wxStaticText* m_staticTextUpdateLeft; - wxStaticBitmap* m_bitmapDeleteLeft; - wxStaticText* m_staticTextDeleteLeft; - wxStaticBitmap* m_bitmapData; - wxStaticText* m_staticTextData; - wxStaticBitmap* m_bitmapDeleteRight; - wxStaticText* m_staticTextDeleteRight; - wxStaticBitmap* m_bitmapUpdateRight; - wxStaticText* m_staticTextUpdateRight; - wxStaticBitmap* m_bitmapCreateRight; - wxStaticText* m_staticTextCreateRight; - wxPanel* m_panelViewFilter; - wxBoxSizer* bSizerViewFilter; - ToggleButton* m_bpButtonViewTypeSyncAction; - ToggleButton* m_bpButtonShowCreateLeft; - ToggleButton* m_bpButtonShowUpdateLeft; - ToggleButton* m_bpButtonShowDeleteLeft; - ToggleButton* m_bpButtonShowLeftOnly; - ToggleButton* m_bpButtonShowLeftNewer; - ToggleButton* m_bpButtonShowEqual; - ToggleButton* m_bpButtonShowDifferent; - ToggleButton* m_bpButtonShowDoNothing; - ToggleButton* m_bpButtonShowRightNewer; - ToggleButton* m_bpButtonShowRightOnly; - ToggleButton* m_bpButtonShowDeleteRight; - ToggleButton* m_bpButtonShowUpdateRight; - ToggleButton* m_bpButtonShowCreateRight; - ToggleButton* m_bpButtonShowConflict; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnConfigNew( wxCommandEvent& event ) { event.Skip(); } - virtual void OnConfigLoad( wxCommandEvent& event ) { event.Skip(); } - virtual void OnConfigSave( wxCommandEvent& event ) { event.Skip(); } - virtual void OnConfigSaveAs( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSaveAsBatchJob( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCompare( wxCommandEvent& event ) { event.Skip(); } - virtual void OnStartSync( wxCommandEvent& event ) { event.Skip(); } - virtual void OnMenuQuit( wxCommandEvent& event ) { event.Skip(); } - virtual void OnMenuGlobalSettings( wxCommandEvent& event ) { event.Skip(); } - virtual void OnMenuFindItem( wxCommandEvent& event ) { event.Skip(); } - virtual void OnMenuExportFileList( wxCommandEvent& event ) { event.Skip(); } - virtual void OnShowHelp( wxCommandEvent& event ) { event.Skip(); } - virtual void OnMenuCheckVersion( wxCommandEvent& event ) { event.Skip(); } - virtual void OnMenuCheckVersionAutomatically( wxCommandEvent& event ) { event.Skip(); } - virtual void OnMenuAbout( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCmpSettings( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCompSettingsContext( wxMouseEvent& event ) { event.Skip(); } - virtual void OnSyncSettings( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSyncSettingsContext( wxMouseEvent& event ) { event.Skip(); } - virtual void OnAddFolderPair( wxCommandEvent& event ) { event.Skip(); } - virtual void OnRemoveTopFolderPair( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSwapSides( wxCommandEvent& event ) { event.Skip(); } - virtual void OnHideSearchPanel( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSearchGridEnter( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCfgHistoryKeyEvent( wxKeyEvent& event ) { event.Skip(); } - virtual void OnLoadFromHistory( wxCommandEvent& event ) { event.Skip(); } - virtual void OnLoadFromHistoryDoubleClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCfgHistoryRightClick( wxMouseEvent& event ) { event.Skip(); } - virtual void OnConfigureFilter( wxCommandEvent& event ) { event.Skip(); } - virtual void OnGlobalFilterContext( wxMouseEvent& event ) { event.Skip(); } - virtual void OnShowExcluded( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToggleViewType( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToggleViewButton( wxCommandEvent& event ) { event.Skip(); } - virtual void OnViewButtonRightClick( wxMouseEvent& event ) { event.Skip(); } - - -public: - wxPanel* m_panelTopLeft; - wxBitmapButton* m_bpButtonRemovePair; - FolderHistoryBox* m_directoryLeft; - wxBitmapButton* m_bpButtonAltCompCfg; - wxBitmapButton* m_bpButtonLocalFilter; - wxBitmapButton* m_bpButtonAltSyncCfg; - wxPanel* m_panelTopRight; - FolderHistoryBox* m_directoryRight; - wxBoxSizer* bSizerStatistics; - wxBoxSizer* bSizerData; - - MainDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 900,600 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL ); - - ~MainDialogGenerated(); - -}; - -/////////////////////////////////////////////////////////////////////////////// -/// Class CmpCfgDlgGenerated -/////////////////////////////////////////////////////////////////////////////// -class CmpCfgDlgGenerated : public wxDialog -{ -private: - -protected: - wxPanel* m_panel36; - wxStaticText* m_staticText91; - wxStaticBitmap* m_bitmapByTime; - wxToggleButton* m_toggleBtnTimeSize; - wxStaticBitmap* m_bitmapByContent; - wxToggleButton* m_toggleBtnContent; - wxStaticLine* m_staticline33; - wxStaticText* m_staticText92; - wxChoice* m_choiceHandleSymlinks; - wxHyperlinkCtrl* m_hyperlink24; - wxStaticLine* m_staticline14; - wxBoxSizer* bSizerStdButtons; - wxButton* m_buttonOkay; - wxButton* m_buttonCancel; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnTimeSizeDouble( wxMouseEvent& event ) { event.Skip(); } - virtual void OnTimeSize( wxCommandEvent& event ) { event.Skip(); } - virtual void OnContentDouble( wxMouseEvent& event ) { event.Skip(); } - virtual void OnContent( wxCommandEvent& event ) { event.Skip(); } - virtual void OnChangeErrorHandling( wxCommandEvent& event ) { event.Skip(); } - virtual void OnHelpComparisonSettings( wxHyperlinkEvent& event ) { event.Skip(); } - virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } - - -public: - - CmpCfgDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE ); - ~CmpCfgDlgGenerated(); - -}; - -/////////////////////////////////////////////////////////////////////////////// -/// Class SyncCfgDlgGenerated -/////////////////////////////////////////////////////////////////////////////// -class SyncCfgDlgGenerated : public wxDialog -{ -private: - -protected: - wxPanel* m_panel37; - wxStaticText* m_staticText86; - wxToggleButton* m_toggleBtnTwoWay; - wxStaticText* m_staticTextAutomatic; - wxToggleButton* m_toggleBtnMirror; - wxStaticText* m_staticTextMirror; - wxToggleButton* m_toggleBtnUpdate; - wxStaticText* m_staticTextUpdate; - wxToggleButton* m_toggleBtnCustom; - wxStaticText* m_staticTextCustom; - wxCheckBox* m_checkBoxDetectMove; - wxStaticLine* m_staticline32; - wxStaticText* m_staticText87; - wxToggleButton* m_toggleBtnPermanent; - wxToggleButton* m_toggleBtnRecycler; - wxToggleButton* m_toggleBtnVersioning; - wxPanel* m_panelVersioning; - FolderHistoryBox* m_versioningFolder; - wxButton* m_buttonSelectDirVersioning; - wxBoxSizer* bSizer192; - wxStaticText* m_staticText93; - wxChoice* m_choiceVersioningStyle; - wxStaticText* m_staticTextNamingCvtPart1; - wxStaticText* m_staticTextNamingCvtPart2Bold; - wxStaticText* m_staticTextNamingCvtPart3; - wxHyperlinkCtrl* m_hyperlink17; - wxBoxSizer* bSizerExtraConfig; - wxStaticLine* m_staticline321; - wxBoxSizer* bSizer179; - wxStaticText* m_staticText88; - wxToggleButton* m_toggleBtnErrorIgnore; - wxToggleButton* m_toggleBtnErrorPopup; - wxStaticLine* m_staticline36; - wxBoxSizer* bSizerOnCompletion; - wxStaticText* m_staticText89; - ExecFinishedBox* m_comboBoxExecFinished; - wxStaticLine* m_staticline31; - wxBoxSizer* bSizerConfig; - wxStaticText* m_staticTextHeaderCategory1; - wxStaticText* m_staticTextHeaderAction1; - wxStaticBitmap* m_bitmapDatabase; - wxBoxSizer* sbSizerSyncDirections; - wxBoxSizer* bSizerLeftOnly; - wxStaticBitmap* m_bitmapLeftOnly; - wxBitmapButton* m_bpButtonLeftOnly; - wxBoxSizer* bSizerRightOnly; - wxStaticBitmap* m_bitmapRightOnly; - wxBitmapButton* m_bpButtonRightOnly; - wxBoxSizer* bSizerLeftNewer; - wxStaticBitmap* m_bitmapLeftNewer; - wxBitmapButton* m_bpButtonLeftNewer; - wxBoxSizer* bSizerRightNewer; - wxStaticBitmap* m_bitmapRightNewer; - wxBitmapButton* m_bpButtonRightNewer; - wxBoxSizer* bSizerDifferent; - wxStaticBitmap* m_bitmapDifferent; - wxBitmapButton* m_bpButtonDifferent; - wxBoxSizer* bSizerConflict; - wxStaticBitmap* m_bitmapConflict; - wxBitmapButton* m_bpButtonConflict; - wxStaticLine* m_staticline15; - wxBoxSizer* bSizerStdButtons; - wxButton* m_buttonOK; - wxButton* m_buttonCancel; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnSyncTwoWayDouble( wxMouseEvent& event ) { event.Skip(); } - virtual void OnSyncTwoWay( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSyncMirrorDouble( wxMouseEvent& event ) { event.Skip(); } - virtual void OnSyncMirror( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSyncUpdateDouble( wxMouseEvent& event ) { event.Skip(); } - virtual void OnSyncUpdate( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSyncCustomDouble( wxMouseEvent& event ) { event.Skip(); } - virtual void OnSyncCustom( wxCommandEvent& event ) { event.Skip(); } - virtual void OnToggleDetectMovedFiles( wxCommandEvent& event ) { event.Skip(); } - virtual void OnDeletionPermanent( wxCommandEvent& event ) { event.Skip(); } - virtual void OnDeletionRecycler( wxCommandEvent& event ) { event.Skip(); } - virtual void OnDeletionVersioning( wxCommandEvent& event ) { event.Skip(); } - virtual void OnParameterChange( wxCommandEvent& event ) { event.Skip(); } - virtual void OnHelpVersioning( wxHyperlinkEvent& event ) { event.Skip(); } - virtual void OnErrorIgnore( wxCommandEvent& event ) { event.Skip(); } - virtual void OnErrorPopup( wxCommandEvent& event ) { event.Skip(); } - virtual void OnExLeftSideOnly( wxCommandEvent& event ) { event.Skip(); } - virtual void OnExRightSideOnly( wxCommandEvent& event ) { event.Skip(); } - virtual void OnLeftNewer( wxCommandEvent& event ) { event.Skip(); } - virtual void OnRightNewer( wxCommandEvent& event ) { event.Skip(); } - virtual void OnDifferent( wxCommandEvent& event ) { event.Skip(); } - virtual void OnConflict( wxCommandEvent& event ) { event.Skip(); } - virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } - - -public: - - SyncCfgDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); - ~SyncCfgDlgGenerated(); - -}; - -/////////////////////////////////////////////////////////////////////////////// -/// Class SyncConfirmationDlgGenerated -/////////////////////////////////////////////////////////////////////////////// -class SyncConfirmationDlgGenerated : public wxDialog -{ -private: - -protected: - wxStaticBitmap* m_bitmapSync; - wxStaticText* m_staticTextHeader; - wxStaticLine* m_staticline371; - wxPanel* m_panelStatistics; - wxStaticLine* m_staticline38; - wxStaticText* m_staticText84; - wxStaticText* m_staticTextVariant; - wxStaticLine* m_staticline14; - wxStaticText* m_staticText83; - wxStaticBitmap* m_bitmapCreateLeft; - wxStaticBitmap* m_bitmapUpdateLeft; - wxStaticBitmap* m_bitmapDeleteLeft; - wxStaticBitmap* m_bitmapData; - wxStaticBitmap* m_bitmapDeleteRight; - wxStaticBitmap* m_bitmapUpdateRight; - wxStaticBitmap* m_bitmapCreateRight; - wxStaticText* m_staticTextCreateLeft; - wxStaticText* m_staticTextUpdateLeft; - wxStaticText* m_staticTextDeleteLeft; - wxStaticText* m_staticTextData; - wxStaticText* m_staticTextDeleteRight; - wxStaticText* m_staticTextUpdateRight; - wxStaticText* m_staticTextCreateRight; - wxStaticLine* m_staticline12; - wxCheckBox* m_checkBoxDontShowAgain; - wxBoxSizer* bSizerStdButtons; - wxButton* m_buttonStartSync; - wxButton* m_buttonCancel; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnStartSync( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } - - -public: - - SyncConfirmationDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("FreeFileSync"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); - ~SyncConfirmationDlgGenerated(); - -}; - -/////////////////////////////////////////////////////////////////////////////// -/// Class FolderPairPanelGenerated -/////////////////////////////////////////////////////////////////////////////// -class FolderPairPanelGenerated : public wxPanel -{ -private: - -protected: - wxButton* m_buttonSelectDirLeft; - wxButton* m_buttonSelectDirRight; - -public: - wxPanel* m_panelLeft; - wxBitmapButton* m_bpButtonRemovePair; - FolderHistoryBox* m_directoryLeft; - wxPanel* m_panel20; - wxBitmapButton* m_bpButtonAltCompCfg; - wxBitmapButton* m_bpButtonLocalFilter; - wxBitmapButton* m_bpButtonAltSyncCfg; - wxPanel* m_panelRight; - FolderHistoryBox* m_directoryRight; - - FolderPairPanelGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0 ); - ~FolderPairPanelGenerated(); - -}; - -/////////////////////////////////////////////////////////////////////////////// -/// Class CompareProgressDlgGenerated -/////////////////////////////////////////////////////////////////////////////// -class CompareProgressDlgGenerated : public wxPanel -{ -private: - -protected: - wxTextCtrl* m_textCtrlStatus; - wxGauge* m_gauge2; - wxBoxSizer* bSizer42; - wxBoxSizer* bSizerFilesFound; - wxStaticText* m_staticText321; - wxStaticText* m_staticTextScanned; - wxBoxSizer* bSizerFilesRemaining; - wxStaticText* m_staticText46; - wxStaticText* m_staticTextFilesRemaining; - wxStaticText* m_staticTextDataRemaining; - wxBoxSizer* sSizerSpeed; - wxStaticText* m_staticText104; - wxStaticText* m_staticTextSpeed; - wxBoxSizer* sSizerTimeRemaining; - wxStaticText* m_staticTextTimeRemFixed; - wxStaticText* m_staticTextRemTime; - wxBoxSizer* sSizerTimeElapsed; - wxStaticText* m_staticTextTimeElapsed; - -public: - - CompareProgressDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxRAISED_BORDER ); - ~CompareProgressDlgGenerated(); - -}; - -/////////////////////////////////////////////////////////////////////////////// -/// Class SyncProgressPanelGenerated -/////////////////////////////////////////////////////////////////////////////// -class SyncProgressPanelGenerated : public wxPanel -{ -private: - -protected: - wxBoxSizer* bSizer42; - wxBoxSizer* bSizer171; - wxStaticText* m_staticText87; - -public: - wxBoxSizer* bSizerRoot; - wxStaticBitmap* m_bitmapStatus; - wxStaticText* m_staticTextPhase; - wxAnimationCtrl* m_animCtrlSyncing; - wxBitmapButton* m_bpButtonMinimizeToTray; - wxBoxSizer* bSizerStatusText; - wxStaticText* m_staticTextStatus; - wxPanel* m_panelProgress; - wxPanel* m_panelItemsProcessed; - wxStaticText* m_staticTextProcessedObj; - wxStaticText* m_staticTextDataProcessed; - wxPanel* m_panelItemsRemaining; - wxStaticText* m_staticTextRemainingObj; - wxStaticText* m_staticTextDataRemaining; - wxPanel* m_panelTimeRemaining; - wxStaticText* m_staticTextRemTime; - wxStaticText* m_staticTextTimeElapsed; - zen::Graph2D* m_panelGraphBytes; - zen::Graph2D* m_panelGraphItems; - wxNotebook* m_notebookResult; - wxStaticLine* m_staticlineFooter; - wxBoxSizer* bSizerStdButtons; - wxBoxSizer* bSizerExecFinished; - ExecFinishedBox* m_comboBoxExecFinished; - wxButton* m_buttonClose; - wxButton* m_buttonPause; - wxButton* m_buttonStop; - - SyncProgressPanelGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL ); - ~SyncProgressPanelGenerated(); - -}; - -/////////////////////////////////////////////////////////////////////////////// -/// Class LogPanelGenerated -/////////////////////////////////////////////////////////////////////////////// -class LogPanelGenerated : public wxPanel -{ -private: - -protected: - ToggleButton* m_bpButtonErrors; - ToggleButton* m_bpButtonWarnings; - ToggleButton* m_bpButtonInfo; - wxStaticLine* m_staticline13; - zen::Grid* m_gridMessages; - - // Virtual event handlers, overide them in your derived class - virtual void OnErrors( wxCommandEvent& event ) { event.Skip(); } - virtual void OnWarnings( wxCommandEvent& event ) { event.Skip(); } - virtual void OnInfo( wxCommandEvent& event ) { event.Skip(); } - - -public: - - LogPanelGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxTAB_TRAVERSAL ); - ~LogPanelGenerated(); - -}; - -/////////////////////////////////////////////////////////////////////////////// -/// Class BatchDlgGenerated -/////////////////////////////////////////////////////////////////////////////// -class BatchDlgGenerated : public wxDialog -{ -private: - -protected: - wxStaticBitmap* m_bitmapBatchJob; - wxStaticText* m_staticTextDescr; - wxStaticLine* m_staticline18; - wxPanel* m_panel35; - wxStaticText* m_staticText82; - wxToggleButton* m_toggleBtnErrorIgnore; - wxToggleButton* m_toggleBtnErrorPopup; - wxToggleButton* m_toggleBtnErrorStop; - wxStaticLine* m_staticline26; - wxCheckBox* m_checkBoxShowProgress; - wxStaticText* m_staticText81; - ExecFinishedBox* m_comboBoxExecFinished; - wxStaticLine* m_staticline25; - wxCheckBox* m_checkBoxGenerateLogfile; - wxPanel* m_panelLogfile; - wxButton* m_buttonSelectLogfileDir; - wxCheckBox* m_checkBoxLogfilesLimit; - wxSpinCtrl* m_spinCtrlLogfileLimit; - wxHyperlinkCtrl* m_hyperlink17; - wxStaticLine* m_staticline13; - wxBoxSizer* bSizerStdButtons; - wxButton* m_buttonSaveAs; - wxButton* m_buttonCancel; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnErrorIgnore( wxCommandEvent& event ) { event.Skip(); } - virtual void OnErrorPopup( wxCommandEvent& event ) { event.Skip(); } - virtual void OnErrorStop( 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(); } - - -public: - FolderHistoryBox* m_logfileDir; - - BatchDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Save as Batch Job"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~BatchDlgGenerated(); - -}; - -/////////////////////////////////////////////////////////////////////////////// -/// Class DeleteDlgGenerated -/////////////////////////////////////////////////////////////////////////////// -class DeleteDlgGenerated : public wxDialog -{ -private: - -protected: - wxStaticBitmap* m_bitmapDeleteType; - wxStaticText* m_staticTextHeader; - wxStaticLine* m_staticline91; - wxPanel* m_panel31; - wxStaticLine* m_staticline42; - wxTextCtrl* m_textCtrlFileList; - wxStaticLine* m_staticline9; - wxBoxSizer* bSizerStdButtons; - wxCheckBox* m_checkBoxUseRecycler; - wxCheckBox* m_checkBoxDeleteBothSides; - wxButton* m_buttonOK; - wxButton* m_buttonCancel; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnUseRecycler( wxCommandEvent& event ) { event.Skip(); } - virtual void OnDelOnBothSides( wxCommandEvent& event ) { event.Skip(); } - virtual void OnOK( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } - - -public: - - DeleteDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Delete Items"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); - ~DeleteDlgGenerated(); - -}; - -/////////////////////////////////////////////////////////////////////////////// -/// Class FilterDlgGenerated -/////////////////////////////////////////////////////////////////////////////// -class FilterDlgGenerated : public wxDialog -{ -private: - -protected: - wxStaticBitmap* m_bitmapFilter; - wxStaticText* m_staticText44; - wxStaticLine* m_staticline17; - wxPanel* m_panel38; - wxStaticBitmap* m_bitmapInclude; - wxStaticText* m_staticText78; - wxTextCtrl* m_textCtrlInclude; - wxStaticLine* m_staticline22; - wxStaticBitmap* m_bitmapExclude; - wxStaticText* m_staticText77; - wxHyperlinkCtrl* m_hyperlink17; - wxTextCtrl* m_textCtrlExclude; - wxStaticLine* m_staticline24; - wxStaticBitmap* m_bitmapFilterDate; - wxStaticText* m_staticText79; - wxSpinCtrl* m_spinCtrlTimespan; - wxChoice* m_choiceUnitTimespan; - wxStaticLine* m_staticline23; - wxStaticBitmap* m_bitmapFilterSize; - wxStaticText* m_staticText80; - wxStaticText* m_staticText101; - wxSpinCtrl* m_spinCtrlMinSize; - wxChoice* m_choiceUnitMinSize; - wxStaticText* m_staticText102; - wxSpinCtrl* m_spinCtrlMaxSize; - wxChoice* m_choiceUnitMaxSize; - wxStaticLine* m_staticline16; - wxBoxSizer* bSizerStdButtons; - wxButton* m_buttonClear; - wxButton* m_buttonOk; - wxButton* m_buttonCancel; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnUpdateNameFilter( wxCommandEvent& event ) { event.Skip(); } - virtual void OnHelpShowExamples( wxHyperlinkEvent& event ) { event.Skip(); } - virtual void OnUpdateChoice( wxCommandEvent& event ) { event.Skip(); } - virtual void OnClear( wxCommandEvent& event ) { event.Skip(); } - virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } - - -public: - - FilterDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); - ~FilterDlgGenerated(); - -}; - -/////////////////////////////////////////////////////////////////////////////// -/// Class GlobalSettingsDlgGenerated -/////////////////////////////////////////////////////////////////////////////// -class GlobalSettingsDlgGenerated : public wxDialog -{ -private: - -protected: - wxStaticBitmap* m_bitmapSettings; - wxStaticText* m_staticText44; - wxStaticLine* m_staticline20; - wxPanel* m_panel39; - wxCheckBox* m_checkBoxFailSafe; - wxStaticText* m_staticText91; - wxBoxSizer* bSizerLockedFiles; - wxCheckBox* m_checkBoxCopyLocked; - wxStaticText* m_staticText92; - wxCheckBox* m_checkBoxCopyPermissions; - wxStaticText* m_staticText93; - wxStaticLine* m_staticline39; - wxStaticText* m_staticText95; - wxStaticText* m_staticText96; - wxSpinCtrl* m_spinCtrlAutoRetryCount; - wxStaticText* m_staticTextAutoRetryDelay; - wxSpinCtrl* m_spinCtrlAutoRetryDelay; - wxStaticLine* m_staticline191; - wxStaticText* m_staticText85; - wxGrid* m_gridCustomCommand; - wxBitmapButton* m_bpButtonAddRow; - wxBitmapButton* m_bpButtonRemoveRow; - wxHyperlinkCtrl* m_hyperlink17; - wxStaticLine* m_staticline192; - zen::BitmapTextButton* m_buttonResetDialogs; - wxStaticLine* m_staticline40; - wxStaticLine* m_staticline36; - wxBoxSizer* bSizerStdButtons; - wxButton* m_buttonDefault; - wxButton* m_buttonOkay; - wxButton* m_buttonCancel; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnToggleAutoRetryCount( wxCommandEvent& event ) { event.Skip(); } - virtual void OnAddRow( wxCommandEvent& event ) { event.Skip(); } - virtual void OnRemoveRow( wxCommandEvent& event ) { event.Skip(); } - virtual void OnHelpShowExamples( wxHyperlinkEvent& event ) { event.Skip(); } - virtual void OnResetDialogs( wxCommandEvent& event ) { event.Skip(); } - virtual void OnDefault( wxCommandEvent& event ) { event.Skip(); } - virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } - - -public: - - GlobalSettingsDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Global Settings"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~GlobalSettingsDlgGenerated(); - -}; - -/////////////////////////////////////////////////////////////////////////////// -/// Class TooltipDialogGenerated -/////////////////////////////////////////////////////////////////////////////// -class TooltipDialogGenerated : public wxDialog -{ -private: - -protected: - -public: - wxStaticBitmap* m_bitmapLeft; - wxStaticText* m_staticTextMain; - - TooltipDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); - ~TooltipDialogGenerated(); - -}; - -/////////////////////////////////////////////////////////////////////////////// -/// Class SelectTimespanDlgGenerated -/////////////////////////////////////////////////////////////////////////////// -class SelectTimespanDlgGenerated : public wxDialog -{ -private: - -protected: - wxPanel* m_panel35; - wxCalendarCtrl* m_calendarFrom; - wxCalendarCtrl* m_calendarTo; - wxStaticLine* m_staticline21; - wxBoxSizer* bSizerStdButtons; - wxButton* m_buttonOkay; - wxButton* m_buttonCancel; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnChangeSelectionFrom( wxCalendarEvent& event ) { event.Skip(); } - virtual void OnChangeSelectionTo( wxCalendarEvent& event ) { event.Skip(); } - virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } - - -public: - - SelectTimespanDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Select Time Span"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); - ~SelectTimespanDlgGenerated(); - -}; - -/////////////////////////////////////////////////////////////////////////////// -/// Class AboutDlgGenerated -/////////////////////////////////////////////////////////////////////////////// -class AboutDlgGenerated : public wxDialog -{ -private: - -protected: - wxPanel* m_panel41; - wxStaticBitmap* m_bitmapLogo; - wxStaticLine* m_staticline341; - wxStaticText* m_staticText96; - wxHyperlinkCtrl* m_hyperlink11; - wxHyperlinkCtrl* m_hyperlink9; - wxHyperlinkCtrl* m_hyperlink10; - wxHyperlinkCtrl* m_hyperlink7; - wxHyperlinkCtrl* m_hyperlink14; - wxHyperlinkCtrl* m_hyperlink15; - wxHyperlinkCtrl* m_hyperlink13; - wxHyperlinkCtrl* m_hyperlink16; - wxHyperlinkCtrl* m_hyperlink12; - wxHyperlinkCtrl* m_hyperlink18; - wxPanel* m_panelDonate; - wxPanel* m_panel39; - wxAnimationCtrl* m_animCtrlWink; - wxStaticText* m_staticText83; - wxButton* m_buttonDonate; - wxStaticText* m_staticText94; - wxStaticBitmap* m_bitmap9; - wxHyperlinkCtrl* m_hyperlink1; - wxStaticBitmap* m_bitmap10; - wxHyperlinkCtrl* m_hyperlink2; - wxStaticLine* m_staticline34; - wxStaticText* m_staticText93; - wxStaticBitmap* m_bitmap13; - wxHyperlinkCtrl* m_hyperlink5; - wxStaticLine* m_staticline37; - wxStaticText* m_staticText54; - wxScrolledWindow* m_scrolledWindowTranslators; - wxFlexGridSizer* fgSizerTranslators; - wxStaticLine* m_staticline36; - wxBoxSizer* bSizerStdButtons; - wxButton* m_buttonClose; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnDonate( wxCommandEvent& event ) { event.Skip(); } - virtual void OnOK( wxCommandEvent& event ) { event.Skip(); } - - -public: - - AboutDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("About"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); - ~AboutDlgGenerated(); - -}; - -#endif //__GUI_GENERATED_H__ diff --git a/ui/gui_status_handler.cpp b/ui/gui_status_handler.cpp deleted file mode 100644 index f161b87b..00000000 --- a/ui/gui_status_handler.cpp +++ /dev/null @@ -1,479 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "gui_status_handler.h" -#include -#include -#include -#include -#include -#include "main_dlg.h" -#include "exec_finished_box.h" -#include "../lib/generate_logfile.h" -#include "../lib/resolve_path.h" -#include "../lib/status_handler_impl.h" - -using namespace zen; -using namespace xmlAccess; - - -CompareStatusHandler::CompareStatusHandler(MainDialog& dlg) : - mainDlg(dlg), - ignoreErrors(false) -{ - { -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(&mainDlg); //leads to GUI corruption problems on Linux/OS X! -#endif - - //display status panel during compare - mainDlg.compareStatus->init(*this); //clear old values before showing panel - mainDlg.auiMgr.GetPane(mainDlg.compareStatus->getAsWindow()).Show(); - mainDlg.auiMgr.Update(); - } - - mainDlg.Update(); //don't wait until idle event! - - //register keys - mainDlg.Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(CompareStatusHandler::OnKeyPressed), nullptr, this); - mainDlg.m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CompareStatusHandler::OnAbortCompare), nullptr, this); -} - - -CompareStatusHandler::~CompareStatusHandler() -{ - //unregister keys - mainDlg.Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(CompareStatusHandler::OnKeyPressed), nullptr, this); - mainDlg.m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CompareStatusHandler::OnAbortCompare), nullptr, this); - - mainDlg.compareStatus->finalize(); - mainDlg.auiMgr.GetPane(mainDlg.compareStatus->getAsWindow()).Hide(); - mainDlg.auiMgr.Update(); -} - - -void CompareStatusHandler::OnKeyPressed(wxKeyEvent& event) -{ - const int keyCode = event.GetKeyCode(); - if (keyCode == WXK_ESCAPE) - { - wxCommandEvent dummy; - OnAbortCompare(dummy); - } - - event.Skip(); -} - - -void CompareStatusHandler::initNewPhase(int objectsTotal, Int64 dataTotal, Phase phaseID) -{ - StatusHandler::initNewPhase(objectsTotal, dataTotal, phaseID); - - switch (currentPhase()) - { - case PHASE_NONE: - case PHASE_SYNCHRONIZING: - assert(false); - case PHASE_SCANNING: - break; - case PHASE_COMPARING_CONTENT: - { -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(&mainDlg); //leads to GUI corruption problems on Linux/OS X! -#endif - mainDlg.compareStatus->switchToCompareBytewise(); - mainDlg.Layout(); //show progress bar... - mainDlg.Refresh(); //remove distortion... - } - break; - } -} - - -ProcessCallback::Response CompareStatusHandler::reportError(const std::wstring& errorMessage, size_t retryNumber) -{ - //no need to implement auto-retry here: 1. user is watching 2. comparison is fast - //=> similar behavior like "ignoreErrors" which does not honor sync settings - - if (ignoreErrors) - return ProcessCallback::IGNORE_ERROR; - - forceUiRefresh(); - - bool ignoreNextErrors = false; - switch (showConfirmationDialog3(&mainDlg, DialogInfoType::ERROR2, PopupDialogCfg3(). - setDetailInstructions(errorMessage). - setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors"), ConfirmationButton3::DONT_DO_IT), - _("&Ignore"), _("&Retry"))) - { - case ConfirmationButton3::DO_IT: //ignore - ignoreErrors = ignoreNextErrors; - return ProcessCallback::IGNORE_ERROR; - - case ConfirmationButton3::DONT_DO_IT: //retry - return ProcessCallback::RETRY; - - case ConfirmationButton3::CANCEL: - abortThisProcess(); - break; - } - - assert(false); - return ProcessCallback::IGNORE_ERROR; //dummy return value -} - - -void CompareStatusHandler::reportFatalError(const std::wstring& errorMessage) -{ - forceUiRefresh(); - showNotificationDialog(&mainDlg, DialogInfoType::ERROR2, PopupDialogCfg().setTitle(_("Serious Error")).setDetailInstructions(errorMessage)); -} - - -void CompareStatusHandler::reportWarning(const std::wstring& warningMessage, bool& warningActive) -{ - if (!warningActive || ignoreErrors) //if errors are ignored, then warnings should also - return; - - forceUiRefresh(); - - //show pop-up and ask user how to handle warning - bool dontWarnAgain = false; - switch (showConfirmationDialog(&mainDlg, DialogInfoType::WARNING, - PopupDialogCfg().setDetailInstructions(warningMessage). - setCheckBox(dontWarnAgain, _("&Don't show this warning again")), - _("&Ignore"))) - { - case ConfirmationButton::DO_IT: - warningActive = !dontWarnAgain; - break; - case ConfirmationButton::CANCEL: - abortThisProcess(); - break; - } -} - - -void CompareStatusHandler::forceUiRefresh() -{ - mainDlg.compareStatus->updateStatusPanelNow(); -} - - -void CompareStatusHandler::OnAbortCompare(wxCommandEvent& event) -{ - requestAbortion(); -} - - -void CompareStatusHandler::abortThisProcess() -{ - requestAbortion(); //just make sure... - throw GuiAbortProcess(); -} - -//######################################################################################################## - -SyncStatusHandler::SyncStatusHandler(wxFrame* parentDlg, - size_t lastSyncsLogFileSizeMax, - OnGuiError handleError, - size_t automaticRetryCount, - size_t automaticRetryDelay, - const std::wstring& jobName, - const std::wstring& execWhenFinished, - std::vector& execFinishedHistory) : - progressDlg(createProgressDialog(*this, [this] { this->onProgressDialogTerminate(); }, *this, parentDlg, true, jobName, execWhenFinished, execFinishedHistory)), - lastSyncsLogFileSizeMax_(lastSyncsLogFileSizeMax), - handleError_(handleError), - automaticRetryCount_(automaticRetryCount), - automaticRetryDelay_(automaticRetryDelay), - jobName_(jobName) -{ - totalTime.Start(); //measure total time -} - - -SyncStatusHandler::~SyncStatusHandler() -{ - //------------ "on completion" command conceptually is part of the sync, not cleanup -------------------------------------- - - //decide whether to stay on status screen or exit immediately... - bool showFinalResults = true; - - if (progressDlg) - { - //execute "on completion" command (even in case of ignored errors) - if (!abortIsRequested()) //if aborted (manually), we don't execute the command - { - const std::wstring finalCommand = progressDlg->getExecWhenFinishedCommand(); //final value (after possible user modification) - if (!finalCommand.empty()) - { - if (isCloseProgressDlgCommand(finalCommand)) - showFinalResults = false; //take precedence over current visibility status - else - try - { - tryReportingError([&] { shellExecute2(expandMacros(utfCvrtTo(finalCommand)), EXEC_TYPE_SYNC); }, //throw FileError, throw X? - *this); - } - catch (...) {} - } - } - } - //------------ end of sync: begin of cleanup -------------------------------------- - - const int totalErrors = errorLog.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR); //evaluate before finalizing log - const int totalWarnings = errorLog.getItemCount(TYPE_WARNING); - - //finalize error log - std::wstring finalStatus; - if (abortIsRequested()) - { - finalStatus = _("Synchronization stopped"); - errorLog.logMsg(finalStatus, TYPE_ERROR); - } - else if (totalErrors > 0) - { - finalStatus = _("Synchronization completed with errors"); - errorLog.logMsg(finalStatus, TYPE_ERROR); - } - else if (totalWarnings > 0) - { - finalStatus = _("Synchronization completed with warnings"); - errorLog.logMsg(finalStatus, TYPE_WARNING); //give status code same warning priority as display category! - } - else - { - if (getObjectsTotal(PHASE_SYNCHRONIZING) == 0 && //we're past "initNewPhase(PHASE_SYNCHRONIZING)" at this point! - getDataTotal (PHASE_SYNCHRONIZING) == 0) - finalStatus = _("Nothing to synchronize"); //even if "ignored conflicts" occurred! - else - finalStatus = _("Synchronization completed successfully"); - errorLog.logMsg(finalStatus, TYPE_INFO); - } - - const SummaryInfo summary = - { - jobName_, finalStatus, - getObjectsCurrent(PHASE_SYNCHRONIZING), getDataCurrent(PHASE_SYNCHRONIZING), - getObjectsTotal (PHASE_SYNCHRONIZING), getDataTotal (PHASE_SYNCHRONIZING), - totalTime.Time() / 1000 - }; - - try - { - saveToLastSyncsLog(summary, errorLog, lastSyncsLogFileSizeMax_); //throw FileError - } - catch (FileError&) {} - - if (progressDlg) - { - //notify to progressDlg that current process has ended - if (showFinalResults) - { - if (abortIsRequested()) - progressDlg->processHasFinished(SyncProgressDialog::RESULT_ABORTED, errorLog); //enable okay and close events - else if (totalErrors > 0) - progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_ERROR, errorLog); - else if (totalWarnings > 0) - progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS, errorLog); - else - progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS, errorLog); - } - else - progressDlg->closeWindowDirectly(); - - //wait until progress dialog notified shutdown via onProgressDialogTerminate() - //-> required since it has our "this" pointer captured in lambda "notifyWindowTerminate"! - //-> nicely manages dialog lifetime - while (progressDlg) - { - wxTheApp->Yield(); //*first* refresh GUI (removing flicker) before sleeping! - boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL)); - } - } -} - - -void SyncStatusHandler::initNewPhase(int objectsTotal, Int64 dataTotal, Phase phaseID) -{ - assert(phaseID == PHASE_SYNCHRONIZING); - StatusHandler::initNewPhase(objectsTotal, dataTotal, phaseID); - if (progressDlg) - progressDlg->initNewPhase(); //call after "StatusHandler::initNewPhase" -} - - -void SyncStatusHandler::updateProcessedData(int objectsDelta, Int64 dataDelta) -{ - StatusHandler::updateProcessedData(objectsDelta, dataDelta); - if (progressDlg) - progressDlg->notifyProgressChange(); //noexcept - //note: this method should NOT throw in order to properly allow undoing setting of statistics! -} - - -void SyncStatusHandler::reportInfo(const std::wstring& text) -{ - StatusHandler::reportInfo(text); - errorLog.logMsg(text, TYPE_INFO); -} - - -ProcessCallback::Response SyncStatusHandler::reportError(const std::wstring& errorMessage, size_t retryNumber) -{ - //auto-retry - if (retryNumber < automaticRetryCount_) - { - errorLog.logMsg(errorMessage + L"\n=> " + - _P("Automatic retry in 1 second...", "Automatic retry in %x seconds...", automaticRetryDelay_), TYPE_INFO); - //delay - const int iterations = static_cast(1000 * automaticRetryDelay_ / UI_UPDATE_INTERVAL); //always round down: don't allow for negative remaining time below - for (int i = 0; i < iterations; ++i) - { - reportStatus(_("Error") + L": " + _P("Automatic retry in 1 second...", "Automatic retry in %x seconds...", - (1000 * automaticRetryDelay_ - i * UI_UPDATE_INTERVAL + 999) / 1000)); //integer round up - boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL)); - } - return ProcessCallback::RETRY; - } - - - //always, except for "retry": - zen::ScopeGuard guardWriteLog = zen::makeGuard([&] { errorLog.logMsg(errorMessage, TYPE_ERROR); }); - - switch (handleError_) - { - case ON_GUIERROR_POPUP: - { - if (!progressDlg) abortThisProcess(); - PauseTimers dummy(*progressDlg); - forceUiRefresh(); - - bool ignoreNextErrors = false; - switch (showConfirmationDialog3(progressDlg->getWindowIfVisible(), DialogInfoType::ERROR2, PopupDialogCfg3(). - setDetailInstructions(errorMessage). - setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors"), ConfirmationButton3::DONT_DO_IT), - _("&Ignore"), _("&Retry"))) - { - case ConfirmationButton3::DO_IT: //ignore - if (ignoreNextErrors) //falsify only - handleError_ = ON_GUIERROR_IGNORE; - return ProcessCallback::IGNORE_ERROR; - - case ConfirmationButton3::DONT_DO_IT: //retry - guardWriteLog.dismiss(); - errorLog.logMsg(errorMessage + L"\n=> " + _("Retrying operation..."), TYPE_INFO); //explain why there are duplicate "doing operation X" info messages in the log! - return ProcessCallback::RETRY; - - case ConfirmationButton3::CANCEL: - abortThisProcess(); - break; - } - } - break; - - case ON_GUIERROR_IGNORE: - return ProcessCallback::IGNORE_ERROR; - } - - assert(false); - return ProcessCallback::IGNORE_ERROR; //dummy value -} - - -void SyncStatusHandler::reportFatalError(const std::wstring& errorMessage) -{ - errorLog.logMsg(errorMessage, TYPE_FATAL_ERROR); - - switch (handleError_) - { - case ON_GUIERROR_POPUP: - { - if (!progressDlg) abortThisProcess(); - PauseTimers dummy(*progressDlg); - forceUiRefresh(); - - bool ignoreNextErrors = false; - switch (showConfirmationDialog(progressDlg->getWindowIfVisible(), DialogInfoType::ERROR2, - PopupDialogCfg().setTitle(_("Serious Error")). - setDetailInstructions(errorMessage). - setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors")), - _("&Ignore"))) - { - case ConfirmationButton::DO_IT: - if (ignoreNextErrors) //falsify only - handleError_ = ON_GUIERROR_IGNORE; - break; - case ConfirmationButton::CANCEL: - abortThisProcess(); - break; - } - } - break; - - case ON_GUIERROR_IGNORE: - break; - } -} - - -void SyncStatusHandler::reportWarning(const std::wstring& warningMessage, bool& warningActive) -{ - errorLog.logMsg(warningMessage, TYPE_WARNING); - - if (!warningActive) - return; - - switch (handleError_) - { - case ON_GUIERROR_POPUP: - { - if (!progressDlg) abortThisProcess(); - PauseTimers dummy(*progressDlg); - forceUiRefresh(); - - bool dontWarnAgain = false; - switch (showConfirmationDialog(progressDlg->getWindowIfVisible(), DialogInfoType::WARNING, - PopupDialogCfg().setDetailInstructions(warningMessage). - setCheckBox(dontWarnAgain, _("&Don't show this warning again")), - _("&Ignore"))) - { - case ConfirmationButton::DO_IT: - warningActive = !dontWarnAgain; - break; - case ConfirmationButton::CANCEL: - abortThisProcess(); - break; - } - } - break; - - case ON_GUIERROR_IGNORE: - break; //if errors are ignored, then warnings should be, too - } -} - - -void SyncStatusHandler::forceUiRefresh() -{ - if (progressDlg) - progressDlg->updateGui(); -} - - -void SyncStatusHandler::abortThisProcess() -{ - requestAbortion(); //just make sure... - throw GuiAbortProcess(); //abort can be triggered by progressDlg -} - - -void SyncStatusHandler::onProgressDialogTerminate() -{ - //it's responsibility of "progressDlg" to call requestAbortion() when closing dialog - progressDlg = nullptr; -} diff --git a/ui/gui_status_handler.h b/ui/gui_status_handler.h deleted file mode 100644 index fbbf827f..00000000 --- a/ui/gui_status_handler.h +++ /dev/null @@ -1,86 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef GUISTATUSHANDLER_H_INCLUDED -#define GUISTATUSHANDLER_H_INCLUDED - -#include -#include -#include -#include "progress_indicator.h" -#include "../lib/status_handler.h" -#include "../lib/process_xml.h" -#include "main_dlg.h" - - -//Exception class used to abort the "compare" and "sync" process -class GuiAbortProcess {}; - -//classes handling sync and compare error as well as status information - -//CompareStatusHandler(CompareProgressDialog) will internally process Window messages! disable GUI controls to avoid unexpected callbacks! -class CompareStatusHandler : private wxEvtHandler, public zen::StatusHandler //throw GuiAbortProcess -{ -public: - CompareStatusHandler(MainDialog& dlg); - ~CompareStatusHandler(); - - virtual void initNewPhase(int objectsTotal, zen::Int64 dataTotal, Phase phaseID); - virtual void forceUiRefresh(); - - virtual Response reportError(const std::wstring& text, size_t retryNumber); - virtual void reportFatalError(const std::wstring& errorMessage); - virtual void reportWarning(const std::wstring& warningMessage, bool& warningActive); - -private: - void OnKeyPressed(wxKeyEvent& event); - void OnAbortCompare(wxCommandEvent& event); //handle abort button click - virtual void abortThisProcess(); //throw GuiAbortProcess - - MainDialog& mainDlg; - bool ignoreErrors; -}; - - -//SyncStatusHandler(SyncProgressDialog) will internally process Window messages! disable GUI controls to avoid unexpected callbacks! -class SyncStatusHandler : public zen::StatusHandler -{ -public: - SyncStatusHandler(wxFrame* parentDlg, - size_t lastSyncsLogFileSizeMax, - xmlAccess::OnGuiError handleError, - size_t automaticRetryCount, - size_t automaticRetryDelay, - const std::wstring& jobName, - const std::wstring& execWhenFinished, - std::vector& execFinishedHistory); - ~SyncStatusHandler(); - - virtual void initNewPhase (int objectsTotal, zen::Int64 dataTotal, Phase phaseID); - virtual void updateProcessedData(int objectsDelta, zen::Int64 dataDelta); - virtual void reportInfo(const std::wstring& text); - virtual void forceUiRefresh(); - - virtual Response reportError(const std::wstring& text, size_t retryNumber); - virtual void reportFatalError(const std::wstring& errorMessage); - virtual void reportWarning(const std::wstring& warningMessage, bool& warningActive); - -private: - virtual void abortThisProcess(); //throw GuiAbortProcess - void onProgressDialogTerminate(); - - SyncProgressDialog* progressDlg; //managed to have shorter lifetime than this handler! - const size_t lastSyncsLogFileSizeMax_; - xmlAccess::OnGuiError handleError_; - zen::ErrorLog errorLog; - const size_t automaticRetryCount_; - const size_t automaticRetryDelay_; - const std::wstring jobName_; - wxStopWatch totalTime; -}; - - -#endif // GUISTATUSHANDLER_H_INCLUDED diff --git a/ui/main_dlg.cpp b/ui/main_dlg.cpp deleted file mode 100644 index 8ca9cc0b..00000000 --- a/ui/main_dlg.cpp +++ /dev/null @@ -1,4547 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "main_dlg.h" -#include -#include -#include -//#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "check_version.h" -#include "gui_status_handler.h" -#include "sync_cfg.h" -#include "small_dlgs.h" -#include "progress_indicator.h" -#include "folder_pair.h" -#include "search.h" -#include "batch_config.h" -#include "triple_splitter.h" -#include "app_icon.h" -//#include "config_history.h" -#include "../comparison.h" -#include "../synchronization.h" -#include "../algorithm.h" -#include "../lib/resolve_path.h" -#include "../lib/ffs_paths.h" -#include "../lib/help_provider.h" -#include "../lib/lock_holder.h" -#include "../lib/localization.h" - -#ifdef ZEN_MAC -#include -#endif - -using namespace zen; -using namespace std::rel_ops; - - -namespace -{ -struct wxClientHistoryData : public wxClientData //we need a wxClientData derived class to tell wxWidgets to take object ownership! -{ - wxClientHistoryData(const Zstring& cfgFile, int lastUseIndex) : cfgFile_(cfgFile), lastUseIndex_(lastUseIndex) {} - - Zstring cfgFile_; - int lastUseIndex_; //support sorting history by last usage, the higher the index the more recent the usage -}; - -IconBuffer::IconSize convert(xmlAccess::FileIconSize isize) -{ - using namespace xmlAccess; - switch (isize) - { - case ICON_SIZE_SMALL: - return IconBuffer::SIZE_SMALL; - case ICON_SIZE_MEDIUM: - return IconBuffer::SIZE_MEDIUM; - case ICON_SIZE_LARGE: - return IconBuffer::SIZE_LARGE; - } - return IconBuffer::SIZE_SMALL; -} -} - - -class DirectoryNameMainImpl : public DirectoryName -{ -public: - DirectoryNameMainImpl(MainDialog& mainDlg, - wxWindow& dropWindow1, - Grid& dropGrid, - wxButton& dirSelectButton, - FolderHistoryBox& dirName, - wxStaticText& staticText) : - DirectoryName(dropWindow1, dirSelectButton, dirName, &staticText, &dropGrid.getMainWin()), - mainDlg_(mainDlg) {} - - virtual bool acceptDrop(const std::vector& droppedFiles, const wxPoint& clientPos, const wxWindow& wnd) - { - if (std::any_of(droppedFiles.begin(), droppedFiles.end(), [](const wxString& filename) - { - using namespace xmlAccess; - switch (getXmlType(utfCvrtTo(filename))) //throw() - { - case XML_TYPE_GUI: - case XML_TYPE_BATCH: - return true; - case XML_TYPE_GLOBAL: - case XML_TYPE_OTHER: - break; - } - return false; - })) - { - mainDlg_.loadConfiguration(toZ(droppedFiles)); - return false; - } - - //=> return true: change directory selection via drag and drop - return true; - } - -private: - DirectoryNameMainImpl(const DirectoryNameMainImpl&); - DirectoryNameMainImpl& operator=(const DirectoryNameMainImpl&); - - MainDialog& mainDlg_; -}; - -//------------------------------------------------------------------ -/* class hierarchy: - - template<> - FolderPairPanelBasic - /|\ - | - template<> - FolderPairCallback FolderPairPanelGenerated - /|\ /|\ - _________|________ ________| - | | | - FolderPairFirst FolderPairPanel -*/ - -template -class FolderPairCallback : public FolderPairPanelBasic //implements callback functionality to MainDialog as imposed by FolderPairPanelBasic -{ -public: - FolderPairCallback(GuiPanel& basicPanel, MainDialog& mainDialog) : - FolderPairPanelBasic(basicPanel), //pass FolderPairPanelGenerated part... - mainDlg(mainDialog) {} - -private: - virtual MainConfiguration getMainConfig() const { return mainDlg.getConfig().mainCfg; } - virtual wxWindow* getParentWindow() { return &mainDlg; } - virtual std::unique_ptr& getFilterCfgOnClipboardRef() { return mainDlg.filterCfgOnClipboard; } - - virtual void onAltCompCfgChange () { mainDlg.applyCompareConfig(); } - virtual void onAltSyncCfgChange () { mainDlg.applySyncConfig(); } - virtual void onLocalFilterCfgChange() { mainDlg.applyFilterConfig(); } //re-apply filter - - MainDialog& mainDlg; -}; - - -class FolderPairPanel : - public FolderPairPanelGenerated, //FolderPairPanel "owns" FolderPairPanelGenerated! - public FolderPairCallback -{ -public: - FolderPairPanel(wxWindow* parent, MainDialog& mainDialog) : - FolderPairPanelGenerated(parent), - FolderPairCallback(static_cast(*this), mainDialog), //pass FolderPairPanelGenerated part... - dirNameLeft (*m_panelLeft, *m_buttonSelectDirLeft, *m_directoryLeft), - dirNameRight(*m_panelRight, *m_buttonSelectDirRight, *m_directoryRight) - { - dirNameLeft .Connect(EVENT_ON_DIR_SELECTED, wxCommandEventHandler(MainDialog::onDirSelected), nullptr, &mainDialog); - dirNameRight.Connect(EVENT_ON_DIR_SELECTED, wxCommandEventHandler(MainDialog::onDirSelected), nullptr, &mainDialog); - - dirNameLeft .Connect(EVENT_ON_DIR_MANUAL_CORRECTION, wxCommandEventHandler(MainDialog::onDirManualCorrection), nullptr, &mainDialog); - dirNameRight.Connect(EVENT_ON_DIR_MANUAL_CORRECTION, wxCommandEventHandler(MainDialog::onDirManualCorrection), nullptr, &mainDialog); - } - - void setValues(const Zstring& leftDir, - const Zstring& rightDir, - AltCompCfgPtr cmpCfg, - AltSyncCfgPtr syncCfg, - const FilterConfig& filter) - { - setConfig(cmpCfg, syncCfg, filter); - dirNameLeft .setName(toWx(leftDir)); - dirNameRight.setName(toWx(rightDir)); - } - Zstring getLeftDir () const { return toZ(dirNameLeft .getName()); } - Zstring getRightDir() const { return toZ(dirNameRight.getName()); } - -private: - //support for drag and drop - DirectoryName dirNameLeft; - DirectoryName dirNameRight; -}; - - -class FolderPairFirst : public FolderPairCallback -{ -public: - FolderPairFirst(MainDialog& mainDialog) : - FolderPairCallback(mainDialog, mainDialog), - - //prepare drag & drop - dirNameLeft(mainDialog, - *mainDialog.m_panelTopLeft, - *mainDialog.m_gridMainL, - *mainDialog.m_buttonSelectDirLeft, - *mainDialog.m_directoryLeft, - *mainDialog.m_staticTextResolvedPathL), - dirNameRight(mainDialog, - *mainDialog.m_panelTopRight, - *mainDialog.m_gridMainR, - *mainDialog.m_buttonSelectDirRight, - *mainDialog.m_directoryRight, - *mainDialog.m_staticTextResolvedPathR) - { - dirNameLeft .Connect(EVENT_ON_DIR_SELECTED, wxCommandEventHandler(MainDialog::onDirSelected), nullptr, &mainDialog); - dirNameRight.Connect(EVENT_ON_DIR_SELECTED, wxCommandEventHandler(MainDialog::onDirSelected), nullptr, &mainDialog); - - dirNameLeft .Connect(EVENT_ON_DIR_MANUAL_CORRECTION, wxCommandEventHandler(MainDialog::onDirManualCorrection), nullptr, &mainDialog); - dirNameRight.Connect(EVENT_ON_DIR_MANUAL_CORRECTION, wxCommandEventHandler(MainDialog::onDirManualCorrection), nullptr, &mainDialog); - } - - void setValues(const Zstring& leftDir, - const Zstring& rightDir, - AltCompCfgPtr cmpCfg, - AltSyncCfgPtr syncCfg, - const FilterConfig& filter) - { - setConfig(cmpCfg, syncCfg, filter); - dirNameLeft .setName(toWx(leftDir)); - dirNameRight.setName(toWx(rightDir)); - } - Zstring getLeftDir () const { return toZ(dirNameLeft .getName()); } - Zstring getRightDir() const { return toZ(dirNameRight.getName()); } - -private: - //support for drag and drop - DirectoryNameMainImpl dirNameLeft; - DirectoryNameMainImpl dirNameRight; -}; - - -#ifdef ZEN_WIN -class PanelMoveWindow : public MouseMoveWindow -{ -public: - PanelMoveWindow(MainDialog& mainDlg) : - MouseMoveWindow(mainDlg, false), //don't include main dialog itself, thereby prevent various mouse capture lost issues - mainDlg_(mainDlg) {} - - virtual bool allowMove(const wxMouseEvent& event) - { - if (wxPanel* panel = dynamic_cast(event.GetEventObject())) - { - const wxAuiPaneInfo& paneInfo = mainDlg_.auiMgr.GetPane(panel); - if (paneInfo.IsOk() && - paneInfo.IsFloating()) - return false; //prevent main dialog move - } - - return true; //allow dialog move - } - -private: - MainDialog& mainDlg_; -}; -#endif - - -namespace -{ -//workaround for wxWidgets bug failing to update menu item bitmaps (affects Windows- and Linux-build) -void setMenuItemImage(wxMenuItem*& menuItem, const wxBitmap& bmp) -{ - assert(menuItem->GetKind() == wxITEM_NORMAL); - - //support polling - if (isEqual(bmp, menuItem->GetBitmap())) - return; - - if (wxMenu* menu = menuItem->GetMenu()) - { - int pos = menu->GetMenuItems().IndexOf(menuItem); - if (pos != wxNOT_FOUND) - { - /* - menu->Remove(menuItem); ->this simple sequence crashes on Kubuntu x64, wxWidgets 2.9.2 - menu->Insert(pos, menuItem); - */ - const bool enabled = menuItem->IsEnabled(); - wxMenuItem* newItem = new wxMenuItem(menu, menuItem->GetId(), menuItem->GetItemLabel()); - - newItem->SetBitmap(bmp); -#ifdef __WXMSW__ //not availabe on wxGTK or wxOSX - //for some inconceivable reason wxWidgets is not consistent with itself and renders disabled icons - //just greyscale instead of brightened like bitmap buttons; much better: - newItem->SetDisabledBitmap(bmp.ConvertToDisabled()); -#endif - bool isDestroyed = menu->Destroy(menuItem); //actual workaround - assert(isDestroyed); - (void)isDestroyed; - menuItem = menu->Insert(pos, newItem); //don't forget to update input item pointer! - - if (!enabled) - menuItem->Enable(false); //do not enable BEFORE appending item! wxWidgets screws up for yet another crappy reason - } - } -} - -//################################################################################################################################## - -xmlAccess::XmlGlobalSettings retrieveGlobalCfgFromDisk() //blocks on GUI on errors! -{ - using namespace xmlAccess; - XmlGlobalSettings globalCfg; - try - { - if (fileExists(getGlobalConfigFile())) - readConfig(globalCfg); //throw FfsXmlError - //else: globalCfg already has default values - } - catch (const FfsXmlError& e) - { - assert(false); - if (e.getSeverity() != FfsXmlError::WARNING) //ignore parsing errors: should be migration problems only *cross-fingers* - showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); //no parent window: main dialog not yet created! - } - return globalCfg; -} -} - - -void MainDialog::create() -{ - using namespace xmlAccess; - const XmlGlobalSettings globalSettings = retrieveGlobalCfgFromDisk(); - - std::vector filenames = globalSettings.gui.lastUsedConfigFiles; //2. now try last used files - - //------------------------------------------------------------------------------------------ - //check existence of all files in parallel: - RunUntilFirstHit findFirstMissing; - - std::for_each(filenames.begin(), filenames.end(), [&](const Zstring& filename) - { - findFirstMissing.addJob([=] { return filename.empty() /*ever empty??*/ || !fileExists(filename) ? zen::make_unique() : nullptr; }); - }); - //potentially slow network access: give all checks 500ms to finish - const bool allFilesExist = findFirstMissing.timedWait(boost::posix_time::milliseconds(500)) && //false: time elapsed - !findFirstMissing.get(); //no missing - if (!allFilesExist) - filenames.clear(); //we do NOT want to show an error due to last config file missing on application start! - //------------------------------------------------------------------------------------------ - - if (filenames.empty()) - { - if (zen::fileExists(lastRunConfigName())) //3. try to load auto-save config - filenames.push_back(lastRunConfigName()); - } - - XmlGuiConfig guiCfg; //structure to receive gui settings with default values - - if (filenames.empty()) - { - //add default exclusion filter: this is only ever relevant when creating new configurations! - //a default XmlGuiConfig does not need these user-specific exclusions! - Zstring& excludeFilter = guiCfg.mainCfg.globalFilter.excludeFilter; - if (!excludeFilter.empty() && !endsWith(excludeFilter, Zstr("\n"))) - excludeFilter += Zstr("\n"); - excludeFilter += globalSettings.gui.defaultExclusionFilter; - } - else - try - { - readAnyConfig(filenames, guiCfg); //throw FfsXmlError - } - catch (const FfsXmlError& e) - { - if (e.getSeverity() == FfsXmlError::WARNING) - showNotificationDialog(nullptr, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(e.toString())); - //what about simulating changed config on parsing errors???? - else - showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); - } - - //------------------------------------------------------------------------------------------ - - create(guiCfg, filenames, &globalSettings, false); -} - - -void MainDialog::create(const xmlAccess::XmlGuiConfig& guiCfg, - const std::vector& referenceFiles, - const xmlAccess::XmlGlobalSettings* globalSettings, - bool startComparison) -{ - const xmlAccess::XmlGlobalSettings& globSett = globalSettings ? *globalSettings : retrieveGlobalCfgFromDisk(); - try - { - //we need to set language *before* creating MainDialog! - setLanguage(globSett.programLanguage); //throw FileError - } - catch (const FileError& e) - { - showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); - //continue! - } - - MainDialog* frame = new MainDialog(guiCfg, referenceFiles, globSett, startComparison); - frame->Show(); -#ifdef ZEN_MAC - ProcessSerialNumber psn = { 0, kCurrentProcess }; - ::SetFrontProcess(&psn); //call before TransformProcessType() so that OSX menu is updated correctly - ::TransformProcessType(&psn, kProcessTransformToForegroundApplication); //show dock icon, even if we're not an application bundle - //if the executable is not yet in a bundle or if it is called through a launcher, we need to set focus manually: -#endif -} - - -MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg, - const std::vector& referenceFiles, - const xmlAccess::XmlGlobalSettings& globalSettings, - bool startComparison) : - MainDialogGenerated(nullptr), - folderHistoryLeft (std::make_shared()), //make sure it is always bound - folderHistoryRight(std::make_shared()), // - focusWindowAfterSearch(nullptr) -{ - m_directoryLeft ->init(folderHistoryLeft); - m_directoryRight->init(folderHistoryRight); - - //setup sash: detach + reparent: - m_splitterMain->SetSizer(nullptr); //alas wxFormbuilder doesn't allow us to have child windows without a sizer, so we have to remove it here - m_splitterMain->setupWindows(m_gridMainL, m_gridMainC, m_gridMainR); - -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! -#endif - - setRelativeFontSize(*m_buttonCompare, 1.4); - setRelativeFontSize(*m_buttonSync, 1.4); - setRelativeFontSize(*m_buttonCancel, 1.4); - - //---------------- support for dockable gui style -------------------------------- - bSizerPanelHolder->Detach(m_panelTopButtons); - bSizerPanelHolder->Detach(m_panelDirectoryPairs); - bSizerPanelHolder->Detach(m_gridNavi); - bSizerPanelHolder->Detach(m_panelCenter); - bSizerPanelHolder->Detach(m_panelConfig); - bSizerPanelHolder->Detach(m_panelFilter); - bSizerPanelHolder->Detach(m_panelViewFilter); - bSizerPanelHolder->Detach(m_panelStatistics); - - auiMgr.SetManagedWindow(this); - auiMgr.SetFlags(wxAUI_MGR_DEFAULT | wxAUI_MGR_LIVE_RESIZE); - - compareStatus = make_unique(*this); //integrate the compare status panel (in hidden state) - - //caption required for all panes that can be manipulated by the users => used by context menu - auiMgr.AddPane(m_panelCenter, - wxAuiPaneInfo().Name(L"Panel3").CenterPane().PaneBorder(false)); - - auiMgr.AddPane(m_panelDirectoryPairs, - wxAuiPaneInfo().Name(L"Panel2").Layer(2).Top().Caption(_("Folder Pairs")).CaptionVisible(false).PaneBorder(false).Gripper()); - - auiMgr.AddPane(m_panelSearch, - wxAuiPaneInfo().Name(L"PanelFind").Layer(2).Bottom().Caption(_("Find")).CaptionVisible(false).PaneBorder(false).Gripper().MinSize(200, m_bpButtonHideSearch->GetSize().GetHeight()).Hide()); - - auiMgr.AddPane(m_gridNavi, - wxAuiPaneInfo().Name(L"Panel10").Layer(3).Left().Position(1).Caption(_("Overview")).MinSize(300, m_gridNavi->GetSize().GetHeight())); //MinSize(): just default size, see comment below - - auiMgr.AddPane(m_panelConfig, - wxAuiPaneInfo().Name(L"Panel4").Layer(3).Left().Position(2).Caption(_("Configuration")).MinSize(m_listBoxHistory->GetSize().GetWidth(), m_panelConfig->GetSize().GetHeight())); - - auiMgr.AddPane(m_panelTopButtons, - wxAuiPaneInfo().Name(L"Panel1").Layer(4).Top().Row(1).Caption(_("Main Bar")).CaptionVisible(false).PaneBorder(false).Gripper().MinSize(-1, m_panelTopButtons->GetSize().GetHeight())); - //note: min height is calculated incorrectly by wxAuiManager if panes with and without caption are in the same row => use smaller min-size - - auiMgr.AddPane(compareStatus->getAsWindow(), - wxAuiPaneInfo().Name(L"Panel9").Layer(4).Top().Row(2).CaptionVisible(false).PaneBorder(false).Hide()); - - auiMgr.AddPane(m_panelFilter, - wxAuiPaneInfo().Name(L"Panel5").Layer(4).Bottom().Position(1).Caption(_("Filter Files")).MinSize(m_bpButtonFilter->GetSize().GetWidth(), m_panelFilter->GetSize().GetHeight())); - - auiMgr.AddPane(m_panelViewFilter, - wxAuiPaneInfo().Name(L"Panel6").Layer(4).Bottom().Position(2).Caption(_("Select View")).MinSize(m_bpButtonShowDoNothing->GetSize().GetWidth(), m_panelViewFilter->GetSize().GetHeight())); - - auiMgr.AddPane(m_panelStatistics, - wxAuiPaneInfo().Name(L"Panel7").Layer(4).Bottom().Position(3).Caption(_("Statistics")).MinSize(m_bitmapData->GetSize().GetWidth() + m_staticTextData->GetSize().GetWidth(), m_panelStatistics->GetSize().GetHeight())); - - auiMgr.Update(); - - //give panel captions bold typeface - if (wxAuiDockArt* artProvider = auiMgr.GetArtProvider()) - { - wxFont font = artProvider->GetFont(wxAUI_DOCKART_CAPTION_FONT); - font.SetWeight(wxFONTWEIGHT_BOLD); - font.SetPointSize(wxNORMAL_FONT->GetPointSize()); //= larger than the wxAuiDockArt default; looks better on OS X - artProvider->SetFont(wxAUI_DOCKART_CAPTION_FONT, font); - - //accessibility: fix wxAUI drawing black text on black background on high-contrast color schemes: - artProvider->SetColor(wxAUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - } - - auiMgr.GetPane(m_gridNavi).MinSize(-1, -1); //we successfully tricked wxAuiManager into setting an initial Window size :> incomplete API anyone?? - auiMgr.Update(); // - - defaultPerspective = auiMgr.SavePerspective(); - //---------------------------------------------------------------------------------- - //register view layout context menu - m_panelTopButtons->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(MainDialog::OnContextSetLayout), nullptr, this); - m_panelConfig ->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(MainDialog::OnContextSetLayout), nullptr, this); - m_panelFilter ->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(MainDialog::OnContextSetLayout), nullptr, this); - m_panelViewFilter->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(MainDialog::OnContextSetLayout), nullptr, this); - m_panelStatistics->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(MainDialog::OnContextSetLayout), nullptr, this); - m_panelStatusBar ->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(MainDialog::OnContextSetLayout), nullptr, this); - //---------------------------------------------------------------------------------- - - //sort grids - m_gridMainL->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickL ), nullptr, this); - m_gridMainC->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickC ), nullptr, this); - m_gridMainR->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickR ), nullptr, this); - - m_gridMainL->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextL ), nullptr, this); - m_gridMainC->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextC ), nullptr, this); - m_gridMainR->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextR ), nullptr, this); - - //grid context menu - m_gridMainL->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onMainGridContextL), nullptr, this); - m_gridMainC->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onMainGridContextC), nullptr, this); - m_gridMainR->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onMainGridContextR), nullptr, this); - m_gridNavi ->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onNaviGridContext ), nullptr, this); - - m_gridMainL->Connect(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEventHandler(MainDialog::onGridDoubleClickL), nullptr, this ); - m_gridMainR->Connect(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEventHandler(MainDialog::onGridDoubleClickR), nullptr, this ); - - m_gridNavi->Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(MainDialog::onNaviSelection), nullptr, this); - //---------------------------------------------------------------------------------- - - m_panelSearch->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::OnSearchPanelKeyPressed), nullptr, this); - - //set tool tips with (non-translated!) short cut hint - m_bpButtonOpen ->SetToolTip(_("Open...") + L" (Ctrl+O)"); - m_bpButtonSave ->SetToolTip(_("Save") + L" (Ctrl+S)"); - m_buttonCompare ->SetToolTip(_("Compare both sides") + L" (F5)"); - m_bpButtonCmpConfig ->SetToolTip(_("Comparison settings") + L" (F6)"); - m_bpButtonSyncConfig->SetToolTip(_("Synchronization settings") + L" (F7)"); - m_buttonSync ->SetToolTip(_("Start synchronization") + L" (F8)"); - - gridDataView = std::make_shared(); - treeDataView = std::make_shared(); - - cleanedUp = false; - processingGlobalKeyEvent = false; - -#ifdef ZEN_WIN - new PanelMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere... //ownership passed to "this" -#endif - - //set icons for this dialog - SetIcon(getFfsIcon()); //set application icon - - m_bpButtonSyncConfig->SetBitmapLabel(getResourceImage(L"cfg_sync")); - m_bpButtonCmpConfig ->SetBitmapLabel(getResourceImage(L"cfg_compare")); - m_bpButtonOpen ->SetBitmapLabel(getResourceImage(L"load")); - m_bpButtonBatchJob ->SetBitmapLabel(getResourceImage(L"batch")); - m_bpButtonAddPair ->SetBitmapLabel(getResourceImage(L"item_add")); - m_bpButtonHideSearch->SetBitmapLabel(getResourceImage(L"close_panel")); - - //we can't use a wxButton for cancel: it's rendered smaller on OS X than a wxBitmapButton! - setBitmapTextLabel(*m_buttonCancel, wxImage(), m_buttonCancel->GetLabel()); - - { - IconBuffer tmp(IconBuffer::SIZE_SMALL); - const wxBitmap& bmpFile = tmp.genericFileIcon(); - const wxBitmap& bmpDir = tmp.genericDirIcon(); - - m_bitmapSmallDirectoryLeft ->SetBitmap(bmpDir); - m_bitmapSmallFileLeft ->SetBitmap(bmpFile); - m_bitmapSmallDirectoryRight->SetBitmap(bmpDir); - m_bitmapSmallFileRight ->SetBitmap(bmpFile); - } - - //menu icons: workaround for wxWidgets: small hack to update menu items: actually this is a wxWidgets bug (affects Windows- and Linux-build) - setMenuItemImage(m_menuItem10, getResourceImage(L"compare_small")); - setMenuItemImage(m_menuItem11, getResourceImage(L"sync_small")); - - setMenuItemImage(m_menuItemLoad, getResourceImage(L"load_small")); - setMenuItemImage(m_menuItemSave, getResourceImage(L"save_small")); - - setMenuItemImage(m_menuItemGlobSett, getResourceImage(L"settings_small")); - setMenuItemImage(m_menuItem7, getResourceImage(L"batch_small")); - - setMenuItemImage(m_menuItemManual, getResourceImage(L"help_small")); - setMenuItemImage(m_menuItemAbout, getResourceImage(L"about_small")); - - if (!manualProgramUpdateRequired()) - { - m_menuItemCheckVersionNow ->Enable(false); - m_menuItemCheckVersionAuto->Enable(false); - - //wxFormbuilder doesn't give us a wxMenuItem for m_menuCheckVersion, so we need this abomination: - wxMenuItemList& items = m_menuHelp->GetMenuItems(); - for (auto it = items.begin(); it != items.end(); ++it) - if ((*it)->GetSubMenu() == m_menuCheckVersion) - (*it)->Enable(false); - } - - //create language selection menu - std::for_each(zen::ExistingTranslations::get().begin(), ExistingTranslations::get().end(), - [&](const ExistingTranslations::Entry& entry) - { - wxMenuItem* newItem = new wxMenuItem(m_menuLanguages, wxID_ANY, entry.languageName); - newItem->SetBitmap(getResourceImage(entry.languageFlag)); - - //map menu item IDs with language IDs: evaluated when processing event handler - languageMenuItemMap.insert(std::make_pair(newItem->GetId(), entry.languageID)); - - //connect event - this->Connect(newItem->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnMenuLanguageSwitch)); - m_menuLanguages->Append(newItem); - }); - - //notify about (logical) application main window => program won't quit, but stay on this dialog - zen::setMainWindow(this); - - //init handling of first folder pair - firstFolderPair = make_unique(*this); - - initViewFilterButtons(); - - //init grid settings - gridview::init(*m_gridMainL, *m_gridMainC, *m_gridMainR, gridDataView); - treeview::init(*m_gridNavi, treeDataView); - //config_history::init(*m_gridConfigHistory, lastRunConfigName()); - - //initialize and load configuration - setGlobalCfgOnInit(globalSettings); - setConfig(guiCfg, referenceFiles); - - //support for CTRL + C and DEL on grids - m_gridMainL->getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::onGridButtonEventL), nullptr, this); - m_gridMainC->getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::onGridButtonEventC), nullptr, this); - m_gridMainR->getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::onGridButtonEventR), nullptr, this); - - m_gridNavi->getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::onTreeButtonEvent), nullptr, this); - - //register global hotkeys (without explicit menu entry) - wxTheApp->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::OnGlobalKeyEvent), nullptr, this); - wxTheApp->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::OnGlobalKeyEvent), nullptr, this); //capture direction keys - - //drag & drop on navi panel - setupFileDrop(*m_gridNavi); - m_gridNavi->Connect(EVENT_DROP_FILE, FileDropEventHandler(MainDialog::onNaviPanelFilesDropped), nullptr, this); - - timerForAsyncTasks.Connect(wxEVT_TIMER, wxEventHandler(MainDialog::onProcessAsyncTasks), nullptr, this); - - //Connect(wxEVT_SIZE, wxSizeEventHandler(MainDialog::OnResize), nullptr, this); - //Connect(wxEVT_MOVE, wxSizeEventHandler(MainDialog::OnResize), nullptr, this); - - //calculate witdh of folder pair manually (if scrollbars are visible) - m_panelTopLeft->Connect(wxEVT_SIZE, wxEventHandler(MainDialog::OnResizeLeftFolderWidth), nullptr, this); - - //dynamically change sizer direction depending on size - m_panelConfig ->Connect(wxEVT_SIZE, wxEventHandler(MainDialog::OnResizeConfigPanel), nullptr, this); - m_panelViewFilter->Connect(wxEVT_SIZE, wxEventHandler(MainDialog::OnResizeViewPanel), nullptr, this); - m_panelStatistics->Connect(wxEVT_SIZE, wxEventHandler(MainDialog::OnResizeStatisticsPanel), nullptr, this); - wxSizeEvent dummy3; - OnResizeConfigPanel (dummy3); //call once on window creation - OnResizeViewPanel (dummy3); // - OnResizeStatisticsPanel(dummy3); // - - //event handler for manual (un-)checking of rows and setting of sync direction - m_gridMainC->Connect(EVENT_GRID_CHECK_ROWS, CheckRowsEventHandler (MainDialog::onCheckRows), nullptr, this); - m_gridMainC->Connect(EVENT_GRID_SYNC_DIRECTION, SyncDirectionEventHandler(MainDialog::onSetSyncDirection), nullptr, this); - - //mainly to update row label sizes... - updateGui(); - - //register regular check for update on next idle event - Connect(wxEVT_IDLE, wxIdleEventHandler(MainDialog::OnRegularUpdateCheck), nullptr, this); - - //asynchronous call to wxWindow::Layout(): fix superfluous frame on right and bottom when FFS is started in fullscreen mode - Connect(wxEVT_IDLE, wxIdleEventHandler(MainDialog::OnLayoutWindowAsync), nullptr, this); - wxCommandEvent evtDummy; //call once before OnLayoutWindowAsync() - OnResizeLeftFolderWidth(evtDummy); // - - //---------------------------------------------------------------------------------------------------------------------------------------------------------------- - //some convenience: if FFS is started with a *.ffs_gui file as commandline parameter AND all directories contained exist, comparison shall be started right away - if (startComparison) - { - const zen::MainConfiguration currMainCfg = getConfig().mainCfg; - - //------------------------------------------------------------------------------------------ - //check existence of all directories in parallel! - RunUntilFirstHit findFirstMissing; - - //harmonize checks with comparison.cpp:: checkForIncompleteInput() - //we're really doing two checks: 1. check directory existence 2. check config validity -> don't mix them! - bool havePartialPair = false; - bool haveFullPair = false; - - auto addDirCheck = [&](const FolderPairEnh& fp) - { - const Zstring dirLeft = getFormattedDirectoryName(fp.leftDirectory ); //should not block!? - const Zstring dirRight = getFormattedDirectoryName(fp.rightDirectory); // - - if (dirLeft.empty() != dirRight.empty()) //only skip check if both sides are empty! - havePartialPair = true; - else if (!dirLeft.empty()) - haveFullPair = true; - - if (!dirLeft.empty()) - findFirstMissing.addJob([=] { return !dirExists(dirLeft ) ? zen::make_unique() : nullptr; }); - if (!dirRight.empty()) - findFirstMissing.addJob([=] { return !dirExists(dirRight) ? zen::make_unique() : nullptr; }); - }; - - addDirCheck(currMainCfg.firstPair); - std::for_each(currMainCfg.additionalPairs.begin(), currMainCfg.additionalPairs.end(), addDirCheck); - //------------------------------------------------------------------------------------------ - - if (havePartialPair != haveFullPair) //either all pairs full or all half-filled -> validity check! - { - //potentially slow network access: give all checks 500ms to finish - const bool allFilesExist = findFirstMissing.timedWait(boost::posix_time::milliseconds(500)) && //true: have result - !findFirstMissing.get(); //no missing - if (allFilesExist) - if (wxEvtHandler* evtHandler = m_buttonCompare->GetEventHandler()) - { - wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); - evtHandler->AddPendingEvent(dummy2); //simulate button click on "compare" - } - } - } -} - - -MainDialog::~MainDialog() -{ - try //save "GlobalSettings.xml" - { - xmlAccess::writeConfig(getGlobalCfgBeforeExit()); //throw FfsXmlError - } - catch (const xmlAccess::FfsXmlError& e) - { - showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); - } - - try //save "LastRun.ffs_gui" - { - xmlAccess::writeConfig(getConfig(), lastRunConfigName()); //throw FfsXmlError - } - //don't annoy users on read-only drives: it's enough to show a single error message when saving global config - catch (const xmlAccess::FfsXmlError&) {} - - //important! event source wxTheApp is NOT dependent on this instance -> disconnect! - wxTheApp->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::OnGlobalKeyEvent), nullptr, this); - wxTheApp->Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::OnGlobalKeyEvent), nullptr, this); - -#ifdef ZEN_MAC - //more (non-portable) wxWidgets crap: wxListBox leaks wxClientData, both of the following functions fail to clean up: - // src/common/ctrlsub.cpp:: wxItemContainer::~wxItemContainer() -> empty function body!!! - // src/osx/listbox_osx.cpp: wxListBox::~wxListBox() - //=> finally a manual wxItemContainer::Clear() will render itself useful: - m_listBoxHistory->Clear(); -#endif - - auiMgr.UnInit(); - - //no need for wxEventHandler::Disconnect() here; event sources are components of this window and are destroyed, too -} - -//------------------------------------------------------------------------------------------------------------------------------------- - -void MainDialog::onQueryEndSession() -{ - try { xmlAccess::writeConfig(getGlobalCfgBeforeExit()); } - catch (const xmlAccess::FfsXmlError&) {} //we try our best do to something useful in this extreme situation - no reason to notify or even log errors here! - - try { xmlAccess::writeConfig(getConfig(), lastRunConfigName()); } - catch (const xmlAccess::FfsXmlError&) {} -} - - -void MainDialog::setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSettings) -{ - globalCfg = globalSettings; - - //caveat set/get language asymmmetry! setLanguage(globalSettings.programLanguage); //throw FileError - //we need to set langugabe before creating this class! - - //set dialog size and position: - // - width/height are invalid if the window is minimized (eg x,y == -32000; height = 28, width = 160) - // - multi-monitor setups: dialog may be placed on second monitor which is currently turned off - if (globalSettings.gui.dlgSize.GetWidth () > 0 && - globalSettings.gui.dlgSize.GetHeight() > 0) - { - //calculate how much of the dialog will be visible on screen - const int dialogAreaTotal = globalSettings.gui.dlgSize.GetWidth() * globalSettings.gui.dlgSize.GetHeight(); - int dialogAreaVisible = 0; - - const int monitorCount = wxDisplay::GetCount(); - for (int i = 0; i < monitorCount; ++i) - { - wxRect intersection = wxDisplay(i).GetClientArea().Intersect(wxRect(globalSettings.gui.dlgPos, globalSettings.gui.dlgSize)); - dialogAreaVisible = std::max(dialogAreaVisible, intersection.GetWidth() * intersection.GetHeight()); - } - - if (dialogAreaVisible > 0.1 * dialogAreaTotal) //at least 10% of the dialog should be visible! - SetSize(wxRect(globalSettings.gui.dlgPos, globalSettings.gui.dlgSize)); - else - { - SetSize(wxRect(globalSettings.gui.dlgSize)); - Centre(); - } - } - else - Centre(); - - Maximize(globalSettings.gui.isMaximized); - - //set column attributes - m_gridMainL ->setColumnConfig(gridview::convertConfig(globalSettings.gui.columnAttribLeft)); - m_gridMainR ->setColumnConfig(gridview::convertConfig(globalSettings.gui.columnAttribRight)); - m_splitterMain->setSashOffset(globalSettings.gui.sashOffset); - - m_gridNavi->setColumnConfig(treeview::convertConfig(globalSettings.gui.columnAttribNavi)); - treeview::setShowPercentage(*m_gridNavi, globalSettings.gui.showPercentBar); - - treeDataView->setSortDirection(globalSettings.gui.naviLastSortColumn, globalSettings.gui.naviLastSortAscending); - - //-------------------------------------------------------------------------------- - //load list of last used configuration files - //config_history::setItems(*m_gridConfigHistory, globalSettings.gui.lastUsedConfigFiles2); - - std::vector cfgFileNames; - std::transform(globalSettings.gui.cfgFileHistory.rbegin(), globalSettings.gui.cfgFileHistory.rend(), std::back_inserter(cfgFileNames), - [](const ConfigHistoryItem& item) { return item.configFile; }); - //list is stored with last used files first in xml, however addFileToCfgHistory() needs them last!!! - - cfgFileNames.push_back(lastRunConfigName()); //make sure is always part of history list (if existing) - addFileToCfgHistory(cfgFileNames); - - removeObsoleteCfgHistoryItems(cfgFileNames); //remove non-existent items (we need this only on startup) - //-------------------------------------------------------------------------------- - - //load list of last used folders - *folderHistoryLeft = FolderHistory(globalSettings.gui.folderHistoryLeft, globalSettings.gui.folderHistMax); - *folderHistoryRight = FolderHistory(globalSettings.gui.folderHistoryRight, globalSettings.gui.folderHistMax); - - //show/hide file icons - gridview::setupIcons(*m_gridMainL, *m_gridMainC, *m_gridMainR, globalSettings.gui.showIcons, convert(globalSettings.gui.iconSize)); - - //------------------------------------------------------------------------------------------------ - m_checkBoxMatchCase->SetValue(globalCfg.gui.textSearchRespectCase); - - //wxAuiManager erroneously loads panel captions, we don't want that - typedef std::vector> CaptionNameMapping; - CaptionNameMapping captionNameMap; - const wxAuiPaneInfoArray& paneArray = auiMgr.GetAllPanes(); - for (size_t i = 0; i < paneArray.size(); ++i) - captionNameMap.push_back(std::make_pair(paneArray[i].caption, paneArray[i].name)); - - auiMgr.LoadPerspective(globalSettings.gui.guiPerspectiveLast); - - //restore original captions - for (auto it = captionNameMap.begin(); it != captionNameMap.end(); ++it) - auiMgr.GetPane(it->second).Caption(it->first); - - //if MainDialog::onQueryEndSession() is called while comparison is active, this panel is saved and restored as "visible" - auiMgr.GetPane(compareStatus->getAsWindow()).Hide(); - - auiMgr.GetPane(m_panelSearch).Hide(); //no need to show it on startup - - m_menuItemCheckVersionAuto->Check(globalCfg.gui.lastUpdateCheck != -1); - - auiMgr.Update(); -} - - -xmlAccess::XmlGlobalSettings MainDialog::getGlobalCfgBeforeExit() -{ - Freeze(); //no need to Thaw() again!! - - xmlAccess::XmlGlobalSettings globalSettings = globalCfg; - - globalSettings.programLanguage = getLanguage(); - - //retrieve column attributes - globalSettings.gui.columnAttribLeft = gridview::convertConfig(m_gridMainL->getColumnConfig()); - globalSettings.gui.columnAttribRight = gridview::convertConfig(m_gridMainR->getColumnConfig()); - globalSettings.gui.sashOffset = m_splitterMain->getSashOffset(); - - globalSettings.gui.columnAttribNavi = treeview::convertConfig(m_gridNavi->getColumnConfig()); - globalSettings.gui.showPercentBar = treeview::getShowPercentage(*m_gridNavi); - - const std::pair sortInfo = treeDataView->getSortDirection(); - globalSettings.gui.naviLastSortColumn = sortInfo.first; - globalSettings.gui.naviLastSortAscending = sortInfo.second; - - //-------------------------------------------------------------------------------- - //write list of last used configuration files - std::map historyDetail; //(cfg-file/last use index) - for (unsigned int i = 0; i < m_listBoxHistory->GetCount(); ++i) - if (auto clientString = dynamic_cast(m_listBoxHistory->GetClientObject(i))) - historyDetail.insert(std::make_pair(clientString->lastUseIndex_, clientString->cfgFile_)); - else - assert(false); - - //sort by last use; put most recent items *first* (looks better in xml than the reverse) - std::vector history; - std::transform(historyDetail.rbegin(), historyDetail.rend(), std::back_inserter(history), [](const std::pair& item) { return ConfigHistoryItem(item.second); }); - - if (history.size() > globalSettings.gui.cfgFileHistMax) //erase oldest elements - history.resize(globalSettings.gui.cfgFileHistMax); - - globalSettings.gui.cfgFileHistory = history; - //-------------------------------------------------------------------------------- - globalSettings.gui.lastUsedConfigFiles = activeConfigFiles; - //globalSettings.gui.lastUsedConfigFiles2 = config_history::getItems(*m_gridConfigHistory); - - //write list of last used folders - globalSettings.gui.folderHistoryLeft = folderHistoryLeft ->getList(); - globalSettings.gui.folderHistoryRight = folderHistoryRight->getList(); - - globalSettings.gui.textSearchRespectCase = m_checkBoxMatchCase->GetValue(); - - globalSettings.gui.guiPerspectiveLast = auiMgr.SavePerspective(); - - //we need to portably retrieve non-iconized, non-maximized size and position (non-portable: GetWindowPlacement()) - //call *after* wxAuiManager::SavePerspective()! - if (IsIconized()) - Iconize(false); - - globalSettings.gui.isMaximized = IsMaximized(); //evaluate AFTER uniconizing! - - if (IsMaximized()) - Maximize(false); - - globalSettings.gui.dlgSize = GetSize(); - globalSettings.gui.dlgPos = GetPosition(); - - return globalSettings; -} - - -void MainDialog::setSyncDirManually(const std::vector& selection, SyncDirection direction) -{ - if (!selection.empty()) - { - std::for_each(selection.begin(), selection.end(), - [&](FileSystemObject* fsObj) - { - setSyncDirectionRec(direction, *fsObj); //set new direction (recursively) - zen::setActiveStatus(true, *fsObj); //works recursively for directories - }); - - updateGui(); - } -} - - -void MainDialog::setFilterManually(const std::vector& selection, bool setIncluded) -{ - //if hidefiltered is active, there should be no filtered elements on screen => current element was filtered out - assert(!currentCfg.hideExcludedItems || !setIncluded); - - if (!selection.empty()) - { - std::for_each(selection.begin(), selection.end(), - [&](FileSystemObject* fsObj) { zen::setActiveStatus(setIncluded, *fsObj); }); //works recursively for directories - - updateGuiDelayedIf(currentCfg.hideExcludedItems); //show update GUI before removing rows - } -} - - -namespace -{ -//perf: wxString doesn't model exponential growth and so is unusable for large data sets -typedef Zbase zxString; //guaranteed exponential growth -} - -void MainDialog::copySelectionToClipboard(const std::vector& gridRefs) -{ - try - { - zxString clipboardString; - - auto addSelection = [&](const Grid& grid) - { - if (auto prov = grid.getDataProvider()) - { - std::vector colAttr = grid.getColumnConfig(); - vector_remove_if(colAttr, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); - if (!colAttr.empty()) - { - const std::vector selection = grid.getSelectedRows(); - std::for_each(selection.begin(), selection.end(), - [&](size_t row) - { - std::for_each(colAttr.begin(), colAttr.end() - 1, - [&](const Grid::ColumnAttribute& ca) - { - clipboardString += copyStringTo(prov->getValue(row, ca.type_)); - clipboardString += L'\t'; - }); - clipboardString += copyStringTo(prov->getValue(row, colAttr.back().type_)); - clipboardString += L'\n'; - }); - } - } - }; - - for (auto it = gridRefs.begin(); it != gridRefs.end(); ++it) - addSelection(**it); - - //finally write to clipboard - if (!clipboardString.empty()) - if (wxClipboard::Get()->Open()) - { - ZEN_ON_SCOPE_EXIT(wxClipboard::Get()->Close()); - wxClipboard::Get()->SetData(new wxTextDataObject(copyStringTo(clipboardString))); //ownership passed - } - } - catch (const std::bad_alloc& e) - { - showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setMainInstructions(_("Out of memory.") + L" " + utfCvrtTo(e.what()))); - } -} - - -std::vector MainDialog::getGridSelection(bool fromLeft, bool fromRight) const -{ - std::set selectedRows; - - auto addSelection = [&](const Grid& grid) - { - const std::vector& sel = grid.getSelectedRows(); - selectedRows.insert(sel.begin(), sel.end()); - }; - - if (fromLeft) - addSelection(*m_gridMainL); - - if (fromRight) - addSelection(*m_gridMainR); - - return gridDataView->getAllFileRef(selectedRows); -} - - -std::vector MainDialog::getTreeSelection() const -{ - std::vector output; - - const std::vector& sel = m_gridNavi->getSelectedRows(); - std::for_each(sel.begin(), sel.end(), - [&](size_t row) - { - if (std::unique_ptr node = treeDataView->getLine(row)) - { - if (auto root = dynamic_cast(node.get())) - { - //select first level of child elements - for (auto& fsObj : root->baseDirObj_.refSubDirs ()) output.push_back(&fsObj); - for (auto& fsObj : root->baseDirObj_.refSubFiles()) output.push_back(&fsObj); - for (auto& fsObj : root->baseDirObj_.refSubLinks()) output.push_back(&fsObj); - } - else if (auto dir = dynamic_cast(node.get())) - output.push_back(&(dir->dirObj_)); - else if (auto file = dynamic_cast(node.get())) - output.insert(output.end(), file->filesAndLinks_.begin(), file->filesAndLinks_.end()); - } - }); - return output; -} - - -//Exception class used to abort the "compare" and "sync" process -class AbortDeleteProcess {}; - -class ManualDeletionHandler : private wxEvtHandler, public DeleteFilesHandler //throw AbortDeleteProcess -{ -public: - ManualDeletionHandler(MainDialog& main) : - mainDlg(main), - abortRequested(false), - ignoreErrors(false) - { - mainDlg.disableAllElements(true); //disable everything except abort button - - //register abort button - mainDlg.m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ManualDeletionHandler::OnAbortDeletion), nullptr, this ); - mainDlg.Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(ManualDeletionHandler::OnKeyPressed), nullptr, this); - } - - ~ManualDeletionHandler() - { - //de-register abort button - mainDlg.Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(ManualDeletionHandler::OnKeyPressed), nullptr, this); - mainDlg.m_buttonCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ManualDeletionHandler::OnAbortDeletion ), nullptr, this ); - - mainDlg.enableAllElements(); - } - - virtual Response reportError(const std::wstring& msg) - { - if (ignoreErrors) - return DeleteFilesHandler::IGNORE_ERROR; - - forceUiRefresh(); - bool ignoreNextErrors = false; - switch (showConfirmationDialog3(&mainDlg, DialogInfoType::ERROR2, PopupDialogCfg3(). - setDetailInstructions(msg). - setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors"), ConfirmationButton3::DONT_DO_IT), - _("&Ignore"), _("&Retry"))) - { - case ConfirmationButton3::DO_IT: //ignore - ignoreErrors = ignoreNextErrors; - return DeleteFilesHandler::IGNORE_ERROR; - - case ConfirmationButton3::DONT_DO_IT: //retry - return DeleteFilesHandler::RETRY; - - case ConfirmationButton3::CANCEL: - throw AbortDeleteProcess(); - } - - assert (false); - return DeleteFilesHandler::IGNORE_ERROR; //dummy return value - } - - virtual void reportWarning(const std::wstring& msg, bool& warningActive) - { - if (!warningActive || ignoreErrors) - return; - - forceUiRefresh(); - bool dontWarnAgain = false; - switch (showConfirmationDialog(&mainDlg, DialogInfoType::WARNING, PopupDialogCfg(). - setDetailInstructions(msg). - setCheckBox(dontWarnAgain, _("&Don't show this warning again")), _("&Ignore"))) - { - case ConfirmationButton::DO_IT: - warningActive = !dontWarnAgain; - break; - case ConfirmationButton::CANCEL: - throw AbortDeleteProcess(); - } - } - - virtual void reportStatus (const std::wstring& msg) - { - statusMsg = msg; - requestUiRefresh(); - } - -private: - virtual void requestUiRefresh() - { - if (updateUiIsAllowed()) //test if specific time span between ui updates is over - forceUiRefresh(); - - if (abortRequested) //test after (implicit) call to wxApp::Yield() - throw AbortDeleteProcess(); - } - - void forceUiRefresh() - { - //std::wstring msg = toGuiString(deletionCount) + - mainDlg.setStatusBarFullText(statusMsg); - wxTheApp->Yield(); - } - - //context: C callstack message loop => throw()! - void OnAbortDeletion(wxCommandEvent& event) //handle abort button click - { - abortRequested = true; //don't throw exceptions in a GUI-Callback!!! (throw zen::AbortThisProcess()) - } - - void OnKeyPressed(wxKeyEvent& event) - { - const int keyCode = event.GetKeyCode(); - if (keyCode == WXK_ESCAPE) - { - abortRequested = true; //don't throw exceptions in a GUI-Callback!!! (throw zen::AbortThisProcess()) - return; - } - - event.Skip(); - } - - MainDialog& mainDlg; - - bool abortRequested; - bool ignoreErrors; - //size_t deletionCount; // - std::wstring statusMsg; //status reporting -}; - - -void MainDialog::deleteSelectedFiles(const std::vector& selectionLeft, - const std::vector& selectionRight) -{ - bool deleteOnBothSides = false; //let's keep this disabled by default -> don't save - //=> clenup empty selection on either side: - std::vector selectionLeftTmp; - std::vector selectionRightTmp; - std::copy_if(selectionLeft .begin(), selectionLeft .end(), std::back_inserter(selectionLeftTmp ), [](const FileSystemObject* fsObj) { return !fsObj->isEmpty(); }); - std::copy_if(selectionRight.begin(), selectionRight.end(), std::back_inserter(selectionRightTmp), [](const FileSystemObject* fsObj) { return !fsObj->isEmpty(); }); - - if (!selectionLeftTmp.empty() || !selectionRightTmp.empty()) - { - wxWindow* oldFocus = wxWindow::FindFocus(); - ZEN_ON_SCOPE_EXIT(if (oldFocus) oldFocus->SetFocus();) - - if (zen::showDeleteDialog(this, - selectionLeftTmp, - selectionRightTmp, - deleteOnBothSides, - globalCfg.gui.useRecyclerForManualDeletion) == ReturnSmallDlg::BUTTON_OKAY) - { - //wxBusyCursor dummy; -> redundant: progress already shown in status bar! - try - { - //handle errors when deleting files/folders - ManualDeletionHandler statusHandler(*this); - - zen::deleteFromGridAndHD(selectionLeftTmp, - selectionRightTmp, - folderCmp, - extractDirectionCfg(getConfig().mainCfg), - deleteOnBothSides, - globalCfg.gui.useRecyclerForManualDeletion, - statusHandler, - globalCfg.optDialogs.warningRecyclerMissing); - - gridview::clearSelection(*m_gridMainL, *m_gridMainC, *m_gridMainR); //do not clear, if aborted! - } - catch (AbortDeleteProcess&) {} - - //remove rows that are empty: just a beautification, invalid rows shouldn't cause issues - gridDataView->removeInvalidRows(); - - //redraw grid neccessary to update new dimensions and for UI-Backend data linkage - updateGui(); //call immediately after deleteFromGridAndHD!!! - } - } -} - - -namespace -{ -template -Zstring getExistingParentFolder(const FileSystemObject& fsObj) -{ - const DirPair* dirObj = dynamic_cast(&fsObj); - if (!dirObj) - dirObj = dynamic_cast(&fsObj.parent()); - - while (dirObj) - { - if (!dirObj->isEmpty()) - return dirObj->getFullName(); - - dirObj = dynamic_cast(&dirObj->parent()); - } - return fsObj.getBaseDirPf(); -} -} - -void MainDialog::openExternalApplication(const wxString& commandline, const std::vector& selection, bool leftSide) -{ - if (commandline.empty()) - return; - - auto selectionTmp = selection; - - const bool openFileBrowserRequested = [&]() -> bool - { - xmlAccess::XmlGlobalSettings::Gui dummy; - return !dummy.externelApplications.empty() && dummy.externelApplications[0].second == commandline; - }(); - - //support fallback instead of an error in this special case - if (openFileBrowserRequested) - { - if (selectionTmp.size() > 1) //do not open more than one explorer instance! - selectionTmp.resize(1); // - - if (selectionTmp.empty() || - (leftSide && selectionTmp[0]->isEmpty()) || - (!leftSide && selectionTmp[0]->isEmpty())) - { - Zstring fallbackDir; - if (selectionTmp.empty()) - fallbackDir = leftSide ? - getFormattedDirectoryName(firstFolderPair->getLeftDir()) : - getFormattedDirectoryName(firstFolderPair->getRightDir()); - - else - fallbackDir = leftSide ? - getExistingParentFolder(*selectionTmp[0]) : - getExistingParentFolder(*selectionTmp[0]); - - try - { -#ifdef ZEN_WIN - shellExecute2(L"\"" + fallbackDir + L"\"", EXEC_TYPE_ASYNC); //throw FileError -#elif defined ZEN_LINUX - shellExecute2("xdg-open \"" + fallbackDir + "\"", EXEC_TYPE_ASYNC); // -#elif defined ZEN_MAC - shellExecute2("open \"" + fallbackDir + "\"", EXEC_TYPE_ASYNC); // -#endif - } - catch (const FileError& e) { showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); } - return; - } - } - - const size_t massInvokeThreshold = 10; //more than this is likely a user mistake - - if (selectionTmp.size() > massInvokeThreshold) - if (globalCfg.optDialogs.confirmExternalCommandMassInvoke) - { - bool dontAskAgain = false; - switch (showConfirmationDialog(this, DialogInfoType::WARNING, PopupDialogCfg(). - setTitle(_("Confirm")). - setMainInstructions(replaceCpy(_P("Do you really want to execute the command %y for one item?", - "Do you really want to execute the command %y for %x items?", selectionTmp.size()), - L"%y", L'\"' + commandline + L'\"')). - setCheckBox(dontAskAgain, _("&Don't show this warning again")), - _("&Execute"))) - { - case ConfirmationButton::DO_IT: - globalCfg.optDialogs.confirmExternalCommandMassInvoke = !dontAskAgain; - break; - case ConfirmationButton::CANCEL: - return; - } - } - - //regular command evaluation - for (const FileSystemObject* fsObj : selectionTmp) //context menu calls this function only if selection is not empty! - { - Zstring path1 = fsObj->getBaseDirPf() + fsObj->getObjRelativeName(); //full path, even if item is not existing! - Zstring dir1 = beforeLast(path1, FILE_NAME_SEPARATOR); //Win: wrong for root paths like "C:\file.txt" - - Zstring path2 = fsObj->getBaseDirPf() + fsObj->getObjRelativeName(); - Zstring dir2 = beforeLast(path2, FILE_NAME_SEPARATOR); - - if (!leftSide) - { - std::swap(path1, path2); - std::swap(dir1, dir2); - } - - Zstring command = utfCvrtTo(commandline); - replace(command, Zstr("%item_path%"), path1); - replace(command, Zstr("%item2_path%"), path2); - replace(command, Zstr("%item_folder%"), dir1 ); - replace(command, Zstr("%item2_folder%"), dir2 ); - - auto cmdExp = expandMacros(command); - try - { - //caveat: spawning too many threads asynchronously can easily kill a user's desktop session on Ubuntu! - shellExecute2(cmdExp, selectionTmp.size() > massInvokeThreshold ? EXEC_TYPE_SYNC : EXEC_TYPE_ASYNC); //throw FileError - } - catch (const FileError& e) { showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); } - } -} - - -void MainDialog::setStatusBarFileStatistics(size_t filesOnLeftView, - size_t foldersOnLeftView, - size_t filesOnRightView, - size_t foldersOnRightView, - zen::UInt64 filesizeLeftView, - zen::UInt64 filesizeRightView) -{ -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(m_panelStatusBar); //leads to GUI corruption problems on Linux/OS X! -#endif - - //select state - bSizerFileStatus->Show(true); - m_staticTextFullStatus->Hide(); - - //update status information - bSizerStatusLeftDirectories->Show(foldersOnLeftView > 0); - bSizerStatusLeftFiles ->Show(filesOnLeftView > 0); - - setText(*m_staticTextStatusLeftDirs, _P("1 directory", "%x directories", foldersOnLeftView)); - setText(*m_staticTextStatusLeftFiles, _P("1 file", "%x files", filesOnLeftView)); - setText(*m_staticTextStatusLeftBytes, L"(" + filesizeToShortString(to(filesizeLeftView)) + L")"); - //------------------------------------------------------------------------------ - bSizerStatusRightDirectories->Show(foldersOnRightView > 0); - bSizerStatusRightFiles ->Show(filesOnRightView > 0); - - setText(*m_staticTextStatusRightDirs, _P("1 directory", "%x directories", foldersOnRightView)); - setText(*m_staticTextStatusRightFiles, _P("1 file", "%x files", filesOnRightView)); - setText(*m_staticTextStatusRightBytes, L"(" + filesizeToShortString(to(filesizeRightView)) + L")"); - //------------------------------------------------------------------------------ - wxString statusMiddleNew; - if (gridDataView->rowsTotal() > 0) - { - statusMiddleNew = _P("%y of 1 row in view", "%y of %x rows in view", gridDataView->rowsTotal()); - replace(statusMiddleNew, L"%y", toGuiString(gridDataView->rowsOnView()), false); //%x is already used as plural form placeholder! - } - - //fill middle text (considering flashStatusInformation()) - if (oldStatusMsgs.empty()) - setText(*m_staticTextStatusMiddle, statusMiddleNew); - else - oldStatusMsgs.front() = statusMiddleNew; - - m_panelStatusBar->Layout(); -} - - -void MainDialog::setStatusBarFullText(const wxString& msg) -{ - const bool needLayoutUpdate = !m_staticTextFullStatus->IsShown(); - //select state - bSizerFileStatus->Show(false); - m_staticTextFullStatus->Show(); - - //update status information - setText(*m_staticTextFullStatus, msg); - m_panelStatusBar->Layout(); - - if (needLayoutUpdate) - auiMgr.Update(); //fix status bar height (needed on OS X) -} - - -void MainDialog::flashStatusInformation(const wxString& text) -{ - oldStatusMsgs.push_back(m_staticTextStatusMiddle->GetLabel()); - - m_staticTextStatusMiddle->SetLabel(text); - m_staticTextStatusMiddle->SetForegroundColour(wxColour(31, 57, 226)); //highlight color: blue - m_staticTextStatusMiddle->SetFont(m_staticTextStatusMiddle->GetFont().Bold()); - - m_panelStatusBar->Layout(); - //if (needLayoutUpdate) auiMgr.Update(); -> not needed here, this is called anyway in updateGui() - - processAsync2([] { boost::this_thread::sleep(boost::posix_time::millisec(2500)); }, - [this] { this->restoreStatusInformation(); }); -} - - -void MainDialog::restoreStatusInformation() -{ - if (!oldStatusMsgs.empty()) - { - wxString oldMsg = oldStatusMsgs.back(); - oldStatusMsgs.pop_back(); - - if (oldStatusMsgs.empty()) //restore original status text - { - m_staticTextStatusMiddle->SetLabel(oldMsg); - m_staticTextStatusMiddle->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); //reset color - - wxFont fnt = m_staticTextStatusMiddle->GetFont(); - fnt.SetWeight(wxFONTWEIGHT_NORMAL); - m_staticTextStatusMiddle->SetFont(fnt); - - m_panelStatusBar->Layout(); - } - } -} - - -void MainDialog::onProcessAsyncTasks(wxEvent& event) -{ - //schedule and run long-running tasks asynchronously - asyncTasks.evalResults(); //process results on GUI queue - if (asyncTasks.empty()) - timerForAsyncTasks.Stop(); -} - - -void MainDialog::disableAllElements(bool enableAbort) -{ - //disables all elements (except abort button) that might receive user input during long-running processes: - //when changing consider: comparison, synchronization, manual deletion - - EnableCloseButton(false); //not allowed for synchronization! progress indicator is top window! -> not honored on OS X! - - //OS X: wxWidgets portability promise is again a mess: http://wxwidgets.10942.n7.nabble.com/Disable-panel-and-appropriate-children-windows-linux-macos-td35357.html - - m_menubar1->EnableTop(0, false); - m_menubar1->EnableTop(1, false); - m_menubar1->EnableTop(2, false); - m_bpButtonCmpConfig ->Disable(); - m_bpButtonSyncConfig ->Disable(); - m_buttonSync ->Disable(); - m_panelDirectoryPairs->Disable(); - m_splitterMain ->Disable(); //includes m_panelCenter, but not m_panelStatusBar! - m_panelViewFilter ->Disable(); - m_panelFilter ->Disable(); - m_panelConfig ->Disable(); - m_panelStatistics ->Disable(); - m_gridNavi ->Disable(); - - if (enableAbort) - { - //show abort button - m_buttonCancel->Enable(); - m_buttonCancel->Show(); - if (m_buttonCancel->IsShownOnScreen()) - m_buttonCancel->SetFocus(); - m_buttonCompare->Disable(); - m_buttonCompare->Hide(); - m_panelTopButtons->Layout(); - } - else - m_panelTopButtons->Disable(); -} - - -void MainDialog::enableAllElements() -{ - //wxGTK, yet another QOI issue: some stupid bug, keeps moving main dialog to top!! - - EnableCloseButton(true); - - m_menubar1->EnableTop(0, true); - m_menubar1->EnableTop(1, true); - m_menubar1->EnableTop(2, true); - m_bpButtonCmpConfig ->Enable(); - m_bpButtonSyncConfig ->Enable(); - m_buttonSync ->Enable(); - m_panelDirectoryPairs->Enable(); - m_splitterMain ->Enable(); - m_panelViewFilter ->Enable(); - m_panelFilter ->Enable(); - m_panelConfig ->Enable(); - m_panelStatistics ->Enable(); - m_gridNavi ->Enable(); - - //show compare button - m_buttonCancel->Disable(); - m_buttonCancel->Hide(); - m_buttonCompare->Enable(); - m_buttonCompare->Show(); - - m_panelTopButtons->Enable(); - m_panelTopButtons->Layout(); - - //at least wxWidgets on OS X fails to do this after enabling: - Refresh(); -} - - -namespace -{ -void updateSizerOrientation(wxBoxSizer& sizer, wxWindow& window, double horizontalWeight) -{ - const int newOrientation = window.GetSize().GetWidth() * horizontalWeight > window.GetSize().GetHeight() ? wxHORIZONTAL : wxVERTICAL; //check window NOT sizer width! - if (sizer.GetOrientation() != newOrientation) - { - sizer.SetOrientation(newOrientation); - window.Layout(); - } -} -} - - -void MainDialog::OnResizeConfigPanel(wxEvent& event) -{ - updateSizerOrientation(*bSizerConfig, *m_panelConfig, 0.5); - event.Skip(); -} - - -void MainDialog::OnResizeViewPanel(wxEvent& event) -{ - updateSizerOrientation(*bSizerViewFilter, *m_panelViewFilter, 1.0); - event.Skip(); -} - - -void MainDialog::OnResizeStatisticsPanel(wxEvent& event) -{ - //updateSizerOrientation(*bSizerStatistics, *m_panelStatistics); - - //we need something more fancy: - const int parentOrient = m_panelStatistics->GetSize().GetWidth() > m_panelStatistics->GetSize().GetHeight() ? wxHORIZONTAL : wxVERTICAL; //check window NOT sizer width! - if (bSizerStatistics->GetOrientation() != parentOrient) - { - bSizerStatistics->SetOrientation(parentOrient); - - //apply opposite orientation for child sizers - const int childOrient = parentOrient == wxHORIZONTAL ? wxVERTICAL : wxHORIZONTAL; - wxSizerItemList& sl = bSizerStatistics->GetChildren(); - for (auto it = sl.begin(); it != sl.end(); ++it) //yet another wxWidgets bug keeps us from using std::for_each - { - wxSizerItem& szItem = **it; - if (auto sizerChild = dynamic_cast(szItem.GetSizer())) - if (sizerChild->GetOrientation() != childOrient) - sizerChild->SetOrientation(childOrient); - } - m_panelStatistics->Layout(); - } - - event.Skip(); -} - - -void MainDialog::OnResizeLeftFolderWidth(wxEvent& event) -{ - //adapt left-shift display distortion caused by scrollbars for multiple folder pairs - const int width = m_panelTopLeft->GetSize().GetWidth(); - std::for_each(additionalFolderPairs.begin(), additionalFolderPairs.end(), - [&](FolderPairPanel* panel) - { - panel->m_panelLeft->SetMinSize(wxSize(width, -1)); - }); - - event.Skip(); -} - - -void MainDialog::onTreeButtonEvent(wxKeyEvent& event) -{ - int keyCode = event.GetKeyCode(); - if (m_gridNavi->GetLayoutDirection() == wxLayout_RightToLeft) - { - if (keyCode == WXK_LEFT) - keyCode = WXK_RIGHT; - else if (keyCode == WXK_RIGHT) - keyCode = WXK_LEFT; - else if (keyCode == WXK_NUMPAD_LEFT) - keyCode = WXK_NUMPAD_RIGHT; - else if (keyCode == WXK_NUMPAD_RIGHT) - keyCode = WXK_NUMPAD_LEFT; - } - - if (event.ControlDown()) - switch (keyCode) - { - case 'C': - case WXK_INSERT: //CTRL + C || CTRL + INS - { - std::vector gridRefs; - gridRefs.push_back(m_gridNavi); - copySelectionToClipboard(gridRefs); - } - return; - } - else if (event.AltDown()) - switch (keyCode) - { - case WXK_NUMPAD_LEFT: - case WXK_LEFT: //ALT + <- - setSyncDirManually(getTreeSelection(), SyncDirection::LEFT); - return; - - case WXK_NUMPAD_RIGHT: - case WXK_RIGHT: //ALT + -> - setSyncDirManually(getTreeSelection(), SyncDirection::RIGHT); - return; - - case WXK_NUMPAD_UP: - case WXK_NUMPAD_DOWN: - case WXK_UP: /* ALT + /|\ */ - case WXK_DOWN: /* ALT + \|/ */ - setSyncDirManually(getTreeSelection(), SyncDirection::NONE); - return; - } - - else - switch (keyCode) - { - case WXK_SPACE: - case WXK_NUMPAD_SPACE: - { - const std::vector& selection = getTreeSelection(); - if (!selection.empty()) - setFilterManually(selection, !selection[0]->isActive()); - } - return; - - case WXK_DELETE: - case WXK_NUMPAD_DELETE: - deleteSelectedFiles(getTreeSelection(), getTreeSelection()); - return; - } - - event.Skip(); //unknown keypress: propagate -} - - -void MainDialog::onGridButtonEventL(wxKeyEvent& event) -{ - onGridButtonEvent(event, *m_gridMainL, true); -} -void MainDialog::onGridButtonEventC(wxKeyEvent& event) -{ - onGridButtonEvent(event, *m_gridMainC, true); -} -void MainDialog::onGridButtonEventR(wxKeyEvent& event) -{ - onGridButtonEvent(event, *m_gridMainR, false); -} - -void MainDialog::onGridButtonEvent(wxKeyEvent& event, Grid& grid, bool leftSide) -{ - int keyCode = event.GetKeyCode(); - if (grid.GetLayoutDirection() == wxLayout_RightToLeft) - { - if (keyCode == WXK_LEFT) - keyCode = WXK_RIGHT; - else if (keyCode == WXK_RIGHT) - keyCode = WXK_LEFT; - else if (keyCode == WXK_NUMPAD_LEFT) - keyCode = WXK_NUMPAD_RIGHT; - else if (keyCode == WXK_NUMPAD_RIGHT) - keyCode = WXK_NUMPAD_LEFT; - } - - if (event.ControlDown()) - switch (keyCode) - { - case 'C': - case WXK_INSERT: //CTRL + C || CTRL + INS - { - std::vector gridRefs; - gridRefs.push_back(m_gridMainL); - gridRefs.push_back(m_gridMainR); - copySelectionToClipboard(gridRefs); - } - return; // -> swallow event! don't allow default grid commands! - } - - else if (event.AltDown()) - switch (keyCode) - { - case WXK_NUMPAD_LEFT: - case WXK_LEFT: //ALT + <- - setSyncDirManually(getGridSelection(), SyncDirection::LEFT); - return; - - case WXK_NUMPAD_RIGHT: - case WXK_RIGHT: //ALT + -> - setSyncDirManually(getGridSelection(), SyncDirection::RIGHT); - return; - - case WXK_NUMPAD_UP: - case WXK_NUMPAD_DOWN: - case WXK_UP: /* ALT + /|\ */ - case WXK_DOWN: /* ALT + \|/ */ - setSyncDirManually(getGridSelection(), SyncDirection::NONE); - return; - } - - else - switch (keyCode) - { - case WXK_DELETE: - case WXK_NUMPAD_DELETE: - deleteSelectedFiles(getGridSelection(true, false), - getGridSelection(false, true)); - return; - - case WXK_SPACE: - case WXK_NUMPAD_SPACE: - { - const std::vector& selection = getGridSelection(); - if (!selection.empty()) - setFilterManually(selection, !selection[0]->isActive()); - } - return; - - case WXK_RETURN: - case WXK_NUMPAD_ENTER: - if (!globalCfg.gui.externelApplications.empty()) - openExternalApplication(globalCfg.gui.externelApplications[0].second, //open with first external application - getGridSelection(), leftSide); - return; - } - - event.Skip(); //unknown keypress: propagate -} - - -bool isComponentOf(const wxWindow* child, const wxWindow* top) -{ - for (const wxWindow* wnd = child; wnd != nullptr; wnd = wnd->GetParent()) - if (wnd == top) - return true; - return false; -} - - -void MainDialog::OnGlobalKeyEvent(wxKeyEvent& event) //process key events without explicit menu entry :) -{ - //avoid recursion!!! -> this ugly construct seems to be the only (portable) way to avoid re-entrancy - //recursion may happen in multiple situations: e.g. modal dialogs, wxGrid::ProcessEvent()! - if (processingGlobalKeyEvent || - !IsShown() || - !IsActive() || - !IsEnabled() || - !m_gridMainL->IsEnabled() || // - !m_gridMainC->IsEnabled() || //only handle if main window is in use - !m_gridMainR->IsEnabled()) // - { - event.Skip(); - return; - } - - processingGlobalKeyEvent = true; - ZEN_ON_SCOPE_EXIT(processingGlobalKeyEvent = false;) - //---------------------------------------------------- - - const int keyCode = event.GetKeyCode(); - - //CTRL + X - //if (event.ControlDown()) - // switch (keyCode) - // { - // case 'F': //CTRL + F - // showFindPanel(); - // return; //-> swallow event! - // } - - switch (keyCode) - { - case WXK_F3: - case WXK_NUMPAD_F3: - startFindNext(); - return; //-> swallow event! - - case WXK_F6: - { - wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); //simulate button click - if (wxEvtHandler* evtHandler = m_bpButtonCmpConfig->GetEventHandler()) - evtHandler->ProcessEvent(dummy2); //synchronous call - } - return; //-> swallow event! - - case WXK_F7: - { - wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); //simulate button click - if (wxEvtHandler* evtHandler = m_bpButtonSyncConfig->GetEventHandler()) - evtHandler->ProcessEvent(dummy2); //synchronous call - } - return; //-> swallow event! - - case WXK_F9: - setViewTypeSyncAction(!m_bpButtonViewTypeSyncAction->isActive()); - return; //-> swallow event! - - case WXK_F10: - { - wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); //simulate button click - if (wxEvtHandler* evtHandler = m_bpButtonFilter->GetEventHandler()) - evtHandler->ProcessEvent(dummy2); //synchronous call - } - return; //-> swallow event! - - //redirect certain (unhandled) keys directly to grid! - case WXK_UP: - case WXK_DOWN: - case WXK_LEFT: - case WXK_RIGHT: - case WXK_NUMPAD_UP: - case WXK_NUMPAD_DOWN: - case WXK_NUMPAD_LEFT: - case WXK_NUMPAD_RIGHT: - - case WXK_PAGEUP: - case WXK_PAGEDOWN: - case WXK_HOME: - case WXK_END: - case WXK_NUMPAD_PAGEUP: - case WXK_NUMPAD_PAGEDOWN: - case WXK_NUMPAD_HOME: - case WXK_NUMPAD_END: - { - const wxWindow* focus = wxWindow::FindFocus(); - if (!isComponentOf(focus, m_gridMainL ) && // - !isComponentOf(focus, m_gridMainC ) && //don't propagate keyboard commands if grid is already in focus - !isComponentOf(focus, m_gridMainR ) && // - !isComponentOf(focus, m_gridNavi ) && - !isComponentOf(focus, m_listBoxHistory) && //don't propagate if selecting config - !isComponentOf(focus, m_directoryLeft ) && //don't propagate if changing directory field - !isComponentOf(focus, m_directoryRight) && - !isComponentOf(focus, m_panelSearch ) && - !isComponentOf(focus, m_scrolledWindowFolderPairs)) - if (wxEvtHandler* evtHandler = m_gridMainL->getMainWin().GetEventHandler()) - { - m_gridMainL->SetFocus(); - - event.SetEventType(wxEVT_KEY_DOWN); //the grid event handler doesn't expect wxEVT_CHAR_HOOK! - evtHandler->ProcessEvent(event); //propagating event catched at wxTheApp to child leads to recursion, but we prevented it... - event.Skip(false); //definitively handled now! - return; - } - } - break; - } - - event.Skip(); -} - - -void MainDialog::onNaviSelection(GridRangeSelectEvent& event) -{ - //scroll m_gridMain to user's new selection on m_gridNavi - ptrdiff_t leadRow = -1; - if (event.rowFirst_ != event.rowLast_) - if (std::unique_ptr node = treeDataView->getLine(event.rowFirst_)) - { - if (const TreeView::RootNode* root = dynamic_cast(node.get())) - leadRow = gridDataView->findRowFirstChild(&(root->baseDirObj_)); - else if (const TreeView::DirNode* dir = dynamic_cast(node.get())) - { - leadRow = gridDataView->findRowDirect(&(dir->dirObj_)); - if (leadRow < 0) //directory was filtered out! still on tree view (but NOT on grid view) - leadRow = gridDataView->findRowFirstChild(&(dir->dirObj_)); - } - else if (const TreeView::FilesNode* files = dynamic_cast(node.get())) - { - assert(!files->filesAndLinks_.empty()); - if (!files->filesAndLinks_.empty()) - leadRow = gridDataView->findRowDirect(files->filesAndLinks_[0]->getId()); - } - } - - if (leadRow >= 0) - { - leadRow = std::max(0, leadRow - 1); //scroll one more row - - m_gridMainL->scrollTo(leadRow); //scroll all of them (includes the "scroll master") - m_gridMainC->scrollTo(leadRow); // - m_gridMainR->scrollTo(leadRow); // - - m_gridNavi->getMainWin().Update(); //draw cursor immediately rather than on next idle event (required for slow CPUs, netbook) - } - - //get selection on navigation tree and set corresponding markers on main grid - hash_set markedFilesAndLinks; //mark files/symlinks directly - hash_set markedContainer; //mark full container including child-objects - - const std::vector& selection = m_gridNavi->getSelectedRows(); - std::for_each(selection.begin(), selection.end(), - [&](size_t row) - { - if (std::unique_ptr node = treeDataView->getLine(row)) - { - if (const TreeView::RootNode* root = dynamic_cast(node.get())) - markedContainer.insert(&(root->baseDirObj_)); - else if (const TreeView::DirNode* dir = dynamic_cast(node.get())) - markedContainer.insert(&(dir->dirObj_)); - else if (const TreeView::FilesNode* files = dynamic_cast(node.get())) - markedFilesAndLinks.insert(files->filesAndLinks_.begin(), files->filesAndLinks_.end()); - } - }); - - gridview::setNavigationMarker(*m_gridMainL, std::move(markedFilesAndLinks), std::move(markedContainer)); - - event.Skip(); -} - - -void MainDialog::onNaviGridContext(GridClickEvent& event) -{ - const auto& selection = getTreeSelection(); //referenced by lambdas! - ContextMenu menu; - - //---------------------------------------------------------------------------------------------------- - if (!selection.empty()) - //std::any_of(selection.begin(), selection.end(), [](const FileSystemObject* fsObj){ return fsObj->getSyncOperation() != SO_EQUAL; })) -> doesn't consider directories - { - auto getImage = [&](SyncDirection dir, SyncOperation soDefault) - { - return mirrorIfRtl(getSyncOpImage(selection[0]->getSyncOperation() != SO_EQUAL ? - selection[0]->testSyncOperation(dir) : soDefault)); - }; - const wxBitmap opRight = getImage(SyncDirection::RIGHT, SO_OVERWRITE_RIGHT); - const wxBitmap opNone = getImage(SyncDirection::NONE, SO_DO_NOTHING ); - const wxBitmap opLeft = getImage(SyncDirection::LEFT, SO_OVERWRITE_LEFT ); - - wxString shortCutLeft = L"\tAlt+Left"; - wxString shortCutRight = L"\tAlt+Right"; - if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft) - std::swap(shortCutLeft, shortCutRight); - - menu.addItem(_("Set direction:") + L" ->" + shortCutRight, [this, &selection] { setSyncDirManually(selection, SyncDirection::RIGHT); }, &opRight); - menu.addItem(_("Set direction:") + L" -" L"\tAlt+Down", [this, &selection] { setSyncDirManually(selection, SyncDirection::NONE); }, &opNone); - menu.addItem(_("Set direction:") + L" <-" + shortCutLeft, [this, &selection] { setSyncDirManually(selection, SyncDirection::LEFT); }, &opLeft); - //Gtk needs a direction, "<-", because it has no context menu icons! - //Gtk requires "no spaces" for shortcut identifiers! - menu.addSeparator(); - } - - //---------------------------------------------------------------------------------------------------- - //FILE FILTER - auto addFilterMenu = [&](const std::wstring& label, const wxString& iconName, bool include) - { - if (selection.size() == 1) - { - ContextMenu submenu; - - const bool isDir = dynamic_cast(selection[0]) != nullptr; - - //by short name - Zstring labelShort = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + selection[0]->getObjShortName(); - if (isDir) - labelShort += Zstring(FILE_NAME_SEPARATOR) + Zstr("*"); - submenu.addItem(utfCvrtTo(labelShort), [this, &selection, include] { filterShortname(*selection[0], include); }); - - //by relative path - Zstring labelRel = FILE_NAME_SEPARATOR + selection[0]->getObjRelativeName(); - if (isDir) - labelRel += Zstring(FILE_NAME_SEPARATOR) + Zstr("*"); - submenu.addItem(utfCvrtTo(labelRel), [this, &selection, include] { filterItems(selection, include); }); - - menu.addSubmenu(label, submenu, &getResourceImage(iconName)); - } - else if (selection.size() > 1) - { - //by relative path - menu.addItem(label + L" <" + _("multiple selection") + L">", - [this, &selection, include] { filterItems(selection, include); }, &getResourceImage(iconName)); - } - }; - addFilterMenu(_("Include via filter:"), L"filter_include_small", true); - addFilterMenu(_("Exclude via filter:"), L"filter_exclude_small", false); - - //---------------------------------------------------------------------------------------------------- - if (!selection.empty()) - { - if (selection[0]->isActive()) - menu.addItem(_("Exclude temporarily") + L"\tSpace", [this, &selection] { setFilterManually(selection, false); }, &getResourceImage(L"checkboxFalse")); - else - menu.addItem(_("Include temporarily") + L"\tSpace", [this, &selection] { setFilterManually(selection, true); }, &getResourceImage(L"checkboxTrue")); - } - else - menu.addItem(_("Exclude temporarily") + L"\tSpace", [] {}, nullptr, false); - - //---------------------------------------------------------------------------------------------------- - //CONTEXT_DELETE_FILES - menu.addSeparator(); - - menu.addItem(_("Delete") + L"\tDel", [&] { deleteSelectedFiles(selection, selection); }, nullptr, !selection.empty()); - - menu.popup(*this); -} - - -void MainDialog::onMainGridContextC(GridClickEvent& event) -{ - ContextMenu menu; - - menu.addItem(_("Include all"), [&] - { - zen::setActiveStatus(true, folderCmp); - updateGui(); - }, nullptr, gridDataView->rowsTotal() > 0); - - menu.addItem(_("Exclude all"), [&] - { - zen::setActiveStatus(false, folderCmp); - updateGuiDelayedIf(currentCfg.hideExcludedItems); //show update GUI before removing rows - }, nullptr, gridDataView->rowsTotal() > 0); - - menu.popup(*this); -} - -void MainDialog::onMainGridContextL(GridClickEvent& event) -{ - onMainGridContextRim(true); -} - -void MainDialog::onMainGridContextR(GridClickEvent& event) -{ - onMainGridContextRim(false); -} - -void MainDialog::onMainGridContextRim(bool leftSide) -{ - const auto& selection = getGridSelection(); //referenced by lambdas! - ContextMenu menu; - - if (!selection.empty()) - { - auto getImage = [&](SyncDirection dir, SyncOperation soDefault) - { - return mirrorIfRtl(getSyncOpImage(selection[0]->getSyncOperation() != SO_EQUAL ? - selection[0]->testSyncOperation(dir) : soDefault)); - }; - const wxBitmap opRight = getImage(SyncDirection::RIGHT, SO_OVERWRITE_RIGHT); - const wxBitmap opNone = getImage(SyncDirection::NONE, SO_DO_NOTHING ); - const wxBitmap opLeft = getImage(SyncDirection::LEFT, SO_OVERWRITE_LEFT ); - - wxString shortCutLeft = L"\tAlt+Left"; - wxString shortCutRight = L"\tAlt+Right"; - if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft) - std::swap(shortCutLeft, shortCutRight); - - menu.addItem(_("Set direction:") + L" ->" + shortCutRight, [this, &selection] { setSyncDirManually(selection, SyncDirection::RIGHT); }, &opRight); - menu.addItem(_("Set direction:") + L" -" L"\tAlt+Down", [this, &selection] { setSyncDirManually(selection, SyncDirection::NONE); }, &opNone); - menu.addItem(_("Set direction:") + L" <-" + shortCutLeft, [this, &selection] { setSyncDirManually(selection, SyncDirection::LEFT); }, &opLeft); - //Gtk needs a direction, "<-", because it has no context menu icons! - //Gtk requires "no spaces" for shortcut identifiers! - menu.addSeparator(); - } - - //---------------------------------------------------------------------------------------------------- - //FILE FILTER - auto addFilterMenu = [&](const wxString& label, const wxString& iconName, bool include) - { - if (selection.size() == 1) - { - ContextMenu submenu; - - const bool isDir = dynamic_cast(selection[0]) != nullptr; - - //by extension - if (!isDir) - { - const Zstring filename = afterLast(selection[0]->getObjRelativeName(), FILE_NAME_SEPARATOR); - if (contains(filename, Zchar('.'))) //be careful: afterLast returns the whole string if '.' is not found! - { - const Zstring extension = afterLast(filename, Zchar('.')); - submenu.addItem(L"*." + utfCvrtTo(extension), - [this, extension, include] { filterExtension(extension, include); }); - } - } - - //by short name - Zstring labelShort = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + selection[0]->getObjShortName(); - if (isDir) - labelShort += Zstring(FILE_NAME_SEPARATOR) + Zstr("*"); - submenu.addItem(utfCvrtTo(labelShort), [this, &selection, include] { filterShortname(*selection[0], include); }); - - //by relative path - Zstring labelRel = FILE_NAME_SEPARATOR + selection[0]->getObjRelativeName(); - if (isDir) - labelRel += Zstring(FILE_NAME_SEPARATOR) + Zstr("*"); - submenu.addItem(utfCvrtTo(labelRel), [this, &selection, include] { filterItems(selection, include); }); - - menu.addSubmenu(label, submenu, &getResourceImage(iconName)); - } - else if (selection.size() > 1) - { - //by relative path - menu.addItem(label + L" <" + _("multiple selection") + L">", - [this, &selection, include] { filterItems(selection, include); }, &getResourceImage(iconName)); - } - }; - addFilterMenu(_("Include via filter:"), L"filter_include_small", true); - addFilterMenu(_("Exclude via filter:"), L"filter_exclude_small", false); - - //---------------------------------------------------------------------------------------------------- - if (!selection.empty()) - { - if (selection[0]->isActive()) - menu.addItem(_("Exclude temporarily") + L"\tSpace", [this, &selection] { setFilterManually(selection, false); }, &getResourceImage(L"checkboxFalse")); - else - menu.addItem(_("Include temporarily") + L"\tSpace", [this, &selection] { setFilterManually(selection, true); }, &getResourceImage(L"checkboxTrue")); - } - else - menu.addItem(_("Exclude temporarily") + L"\tSpace", [] {}, nullptr, false); - - //---------------------------------------------------------------------------------------------------- - //CONTEXT_EXTERNAL_APP - if (!globalCfg.gui.externelApplications.empty()) - { - menu.addSeparator(); - - for (auto it = globalCfg.gui.externelApplications.begin(); - it != globalCfg.gui.externelApplications.end(); - ++it) - { - //translate default external apps on the fly: 1. "open in explorer" 2. "start directly" - wxString description = zen::implementation::translate(it->first); - if (description.empty()) - description = L" "; //wxWidgets doesn't like empty items - - const wxString command = it->second; //COPY into lambda - - auto openApp = [this, &selection, leftSide, command] { openExternalApplication(command, selection, leftSide); }; - - if (it == globalCfg.gui.externelApplications.begin()) - description += L"\tEnter"; - - menu.addItem(description, openApp, nullptr, !selection.empty()); - } - } - //---------------------------------------------------------------------------------------------------- - //CONTEXT_DELETE_FILES - menu.addSeparator(); - - menu.addItem(_("Delete") + L"\tDel", [this] - { - deleteSelectedFiles( - getGridSelection(true, false), - getGridSelection(false, true)); - }, nullptr, !selection.empty()); - - menu.popup(*this); -} - - - -void MainDialog::filterPhrase(const Zstring& phrase, bool include, bool addNewLine) -{ - Zstring& filterString = [&]() -> Zstring& - { - if (include) - { - Zstring& includeFilter = currentCfg.mainCfg.globalFilter.includeFilter; - if (NameFilter::isNull(includeFilter, FilterConfig().excludeFilter)) //fancy way of checking for "*" include - includeFilter.clear(); - return includeFilter; - } - else - return currentCfg.mainCfg.globalFilter.excludeFilter; - }(); - - if (addNewLine) - { - if (!filterString.empty() && !endsWith(filterString, Zstr("\n"))) - filterString += Zstr("\n"); - filterString += phrase; - } - else - { - if (!filterString.empty() && !endsWith(filterString, Zstr("\n")) && !endsWith(filterString, Zstr(";"))) - filterString += Zstr("\n"); - filterString += phrase + Zstr(";"); //';' is appended to 'mark' that next exclude extension entry won't write to new line - } - - updateGlobalFilterButton(); - if (include) - applyFilterConfig(); //user's temporary exclusions lost! - else //do not fully apply filter, just exclude new items: preserve user's temporary exclusions - { - std::for_each(begin(folderCmp), end(folderCmp), [&](BaseDirPair& baseDirObj) { addHardFiltering(baseDirObj, phrase); }); - updateGui(); - } -} - - -void MainDialog::filterExtension(const Zstring& extension, bool include) -{ - assert(!extension.empty()); - filterPhrase(Zstr("*.") + extension, include, false); -} - - -void MainDialog::filterShortname(const FileSystemObject& fsObj, bool include) -{ - Zstring phrase = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + fsObj.getObjShortName(); - const bool isDir = dynamic_cast(&fsObj) != nullptr; - if (isDir) - phrase += Zstring(FILE_NAME_SEPARATOR) + Zstr("*"); //include filter: * required; exclude filter: * optional, but let's still apply it! - - filterPhrase(phrase, include, true); -} - - -void MainDialog::filterItems(const std::vector& selection, bool include) -{ - if (!selection.empty()) - { - Zstring phrase; - for (auto it = selection.begin(); it != selection.end(); ++it) - { - FileSystemObject* fsObj = *it; - - if (it != selection.begin()) - phrase += Zstr("\n"); - - //#pragma warning(suppress: 6011) -> fsObj bound in this context! - phrase += FILE_NAME_SEPARATOR + fsObj->getObjRelativeName(); - - const bool isDir = dynamic_cast(fsObj) != nullptr; - if (isDir) - phrase += Zstring(FILE_NAME_SEPARATOR) + Zstr("*"); //include filter: * required; exclude filter: * optional, but let's still apply it! - } - filterPhrase(phrase, include, true); - } -} - - -void MainDialog::onGridLabelContextC(GridClickEvent& event) -{ - ContextMenu menu; - - const bool actionView = m_bpButtonViewTypeSyncAction->isActive(); - menu.addRadio(_("Category") + (actionView ? L"\tF9" : L""), [&] { setViewTypeSyncAction(false); }, !actionView); - menu.addRadio(_("Action") + (!actionView ? L"\tF9" : L""), [&] { setViewTypeSyncAction(true ); }, actionView); - - //menu.addItem(_("Category") + L"\tF9", [&] { setViewTypeSyncAction(false); }, m_bpButtonViewTypeSyncAction->isActive() ? nullptr : &getResourceImage(L"compare_small")); - //menu.addItem(_("Action"), [&] { setViewTypeSyncAction(true ); }, m_bpButtonViewTypeSyncAction->isActive() ? &getResourceImage(L"sync_small") : nullptr); - menu.popup(*this); -} - - -void MainDialog::onGridLabelContextL(GridClickEvent& event) -{ - onGridLabelContext(*m_gridMainL, static_cast(event.colType_), getDefaultColumnAttributesLeft()); -} -void MainDialog::onGridLabelContextR(GridClickEvent& event) -{ - onGridLabelContext(*m_gridMainR, static_cast(event.colType_), getDefaultColumnAttributesRight()); -} - - -void MainDialog::onGridLabelContext(Grid& grid, ColumnTypeRim type, const std::vector& defaultColumnAttributes) -{ - ContextMenu menu; - - auto toggleColumn = [&](ColumnType ct) - { - auto colAttr = grid.getColumnConfig(); - - for (Grid::ColumnAttribute& ca : colAttr) - if (ca.type_ == ct) - { - ca.visible_ = !ca.visible_; - grid.setColumnConfig(colAttr); - return; - } - }; - - if (const GridData* prov = grid.getDataProvider()) - for (const Grid::ColumnAttribute& ca : grid.getColumnConfig()) - menu.addCheckBox(prov->getColumnLabel(ca.type_), [ca, toggleColumn] { toggleColumn(ca.type_); }, - ca.visible_, ca.type_ != static_cast(COL_TYPE_FILENAME)); //do not allow user to hide file name column! - //---------------------------------------------------------------------------------------------- - menu.addSeparator(); - - auto setDefault = [&] - { - grid.setColumnConfig(gridview::convertConfig(defaultColumnAttributes)); - }; - menu.addItem(_("&Default"), setDefault); //'&' -> reuse text from "default" buttons elsewhere - //---------------------------------------------------------------------------------------------- - menu.addSeparator(); - menu.addCheckBox(_("Show icons:"), [&] - { - globalCfg.gui.showIcons = !globalCfg.gui.showIcons; - gridview::setupIcons(*m_gridMainL, *m_gridMainC, *m_gridMainR, globalCfg.gui.showIcons, convert(globalCfg.gui.iconSize)); - - }, globalCfg.gui.showIcons); - - auto setIconSize = [&](xmlAccess::FileIconSize sz) - { - globalCfg.gui.iconSize = sz; - gridview::setupIcons(*m_gridMainL, *m_gridMainC, *m_gridMainR, globalCfg.gui.showIcons, convert(sz)); - }; - auto addSizeEntry = [&](const wxString& label, xmlAccess::FileIconSize sz) - { - auto setIconSize2 = setIconSize; //bring into scope - menu.addRadio(label, [sz, setIconSize2] { setIconSize2(sz); }, globalCfg.gui.iconSize == sz, globalCfg.gui.showIcons); - }; - addSizeEntry(L" " + _("Small" ), xmlAccess::ICON_SIZE_SMALL ); - addSizeEntry(L" " + _("Medium"), xmlAccess::ICON_SIZE_MEDIUM); - addSizeEntry(L" " + _("Large" ), xmlAccess::ICON_SIZE_LARGE ); - //---------------------------------------------------------------------------------------------- - if (type == COL_TYPE_DATE) - { - menu.addSeparator(); - - auto selectTimeSpan = [&] - { - if (showSelectTimespanDlg(this, manualTimeSpanFrom, manualTimeSpanTo) == ReturnSmallDlg::BUTTON_OKAY) - { - applyTimeSpanFilter(folderCmp, manualTimeSpanFrom, manualTimeSpanTo); //overwrite current active/inactive settings - //updateGuiDelayedIf(currentCfg.hideExcludedItems); //show update GUI before removing rows - updateGui(); - } - }; - menu.addItem(_("Select time span..."), selectTimeSpan); - } - - menu.popup(*this); -} - - -void MainDialog::OnContextSetLayout(wxMouseEvent& event) -{ - ContextMenu menu; - - menu.addItem(_("Default view"), [&] - { - m_splitterMain->setSashOffset(0); - auiMgr.LoadPerspective(defaultPerspective); - updateGuiForFolderPair(); - }); - //---------------------------------------------------------------------------------------- - std::vector> captionNameMap; //(caption, identifier) - - const wxAuiPaneInfoArray& paneArray = auiMgr.GetAllPanes(); - for (size_t i = 0; i < paneArray.size(); ++i) - if (!paneArray[i].IsShown() && !paneArray[i].name.empty() && - paneArray[i].window != compareStatus->getAsWindow() && - paneArray[i].window != m_panelSearch) - captionNameMap.push_back(std::make_pair(paneArray[i].caption, paneArray[i].name)); - - if (!captionNameMap.empty()) - menu.addSeparator(); - - auto showPanel = [&](const wxString& identifier) - { - auiMgr.GetPane(identifier).Show(); - auiMgr.Update(); - }; - - for (auto it = captionNameMap.begin(); it != captionNameMap.end(); ++it) - { - const wxString label = replaceCpy(_("Show \"%x\""), L"%x", it->first); - const wxString identifier = it->second; - - menu.addItem(label, [showPanel, identifier] { showPanel(identifier); }); - } - - menu.popup(*this); -} - - -void MainDialog::OnCompSettingsContext(wxMouseEvent& event) -{ - ContextMenu menu; - - auto setVariant = [&](CompareVariant var) - { - currentCfg.mainCfg.cmpConfig.compareVar = var; - applyCompareConfig(true); //true: switchMiddleGrid - }; - - auto currentVar = getConfig().mainCfg.cmpConfig.compareVar; - - menu.addRadio(_("File time and size"), [&] { setVariant(CMP_BY_TIME_SIZE); }, currentVar == CMP_BY_TIME_SIZE); - menu.addRadio(_("File content" ), [&] { setVariant(CMP_BY_CONTENT); }, currentVar == CMP_BY_CONTENT); - - menu.popup(*this); -} - - -void MainDialog::OnSyncSettingsContext(wxMouseEvent& event) -{ - ContextMenu menu; - - auto setVariant = [&](DirectionConfig::Variant var) - { - currentCfg.mainCfg.syncCfg.directionCfg.var = var; - applySyncConfig(); - }; - - const auto currentVar = getConfig().mainCfg.syncCfg.directionCfg.var; - - menu.addRadio(L"<- " + _("Two way") + L" ->" , [&] { setVariant(DirectionConfig::TWOWAY); }, currentVar == DirectionConfig::TWOWAY); - menu.addRadio( _("Mirror") + L" ->>", [&] { setVariant(DirectionConfig::MIRROR); }, currentVar == DirectionConfig::MIRROR); - menu.addRadio( _("Update") + L" ->" , [&] { setVariant(DirectionConfig::UPDATE); }, currentVar == DirectionConfig::UPDATE); - menu.addRadio( _("Custom") , [&] { setVariant(DirectionConfig::CUSTOM); }, currentVar == DirectionConfig::CUSTOM); - - menu.popup(*this); -} - - -void MainDialog::onNaviPanelFilesDropped(FileDropEvent& event) -{ - loadConfiguration(toZ(event.getFiles())); - event.Skip(); -} - - -void MainDialog::onDirSelected(wxCommandEvent& event) -{ - //left and right directory text-control and dirpicker are synchronized by MainFolderDragDrop automatically - clearGrid(); //disable the sync button - event.Skip(); -} - - -void MainDialog::onDirManualCorrection(wxCommandEvent& event) -{ - updateUnsavedCfgStatus(); - event.Skip(); -} - - -wxString getFormattedHistoryElement(const Zstring& filename) -{ - Zstring output = afterLast(filename, FILE_NAME_SEPARATOR); - if (endsWith(output, Zstr(".ffs_gui"))) - output = beforeLast(output, Zstr('.')); - return utfCvrtTo(output); -} - - -void MainDialog::addFileToCfgHistory(const std::vector& filenames) -{ - //determine highest "last use" index number of m_listBoxHistory - int lastUseIndexMax = 0; - for (unsigned int i = 0; i < m_listBoxHistory->GetCount(); ++i) - if (auto histData = dynamic_cast(m_listBoxHistory->GetClientObject(i))) - if (histData->lastUseIndex_ > lastUseIndexMax) - lastUseIndexMax = histData->lastUseIndex_; - - std::deque selections(m_listBoxHistory->GetCount()); //items to select after update of history list - - for (auto it = filenames.begin(); it != filenames.end(); ++it) - { - const Zstring& filename = *it; - - //Do we need to additionally check for aliases of the same physical files here? (and aliases for lastRunConfigName?) - - const auto itemPos = [&]() -> std::pair - { - for (unsigned int i = 0; i < m_listBoxHistory->GetCount(); ++i) - if (auto histData = dynamic_cast(m_listBoxHistory->GetClientObject(i))) - { - if (EqualFilename()(filename, histData->cfgFile_)) - return std::make_pair(histData, i); - } - else - assert(false); - return std::make_pair(nullptr, 0); - }(); - - if (itemPos.first) //update - { - itemPos.first->lastUseIndex_ = ++lastUseIndexMax; - selections[itemPos.second] = true; - } - else //insert - { - wxString label; - unsigned int newPos = 0; - - if (EqualFilename()(filename, lastRunConfigName())) - label = L"<" + _("Last session") + L">"; - else - { - //workaround wxWidgets 2.9 bug on GTK screwing up the client data if the list box is sorted: - label = getFormattedHistoryElement(filename); - //"linear insertion sort": - for (; newPos < m_listBoxHistory->GetCount(); ++newPos) - if (label.CmpNoCase(m_listBoxHistory->GetString(newPos)) < 0) - break; - } - - assert(!m_listBoxHistory->IsSorted()); - m_listBoxHistory->Insert(label, newPos, new wxClientHistoryData(filename, ++lastUseIndexMax)); - - selections.insert(selections.begin() + newPos, true); - } - } - - assert(selections.size() == m_listBoxHistory->GetCount()); - - //do not apply selections immediately but only when needed! - //this prevents problems with m_listBoxHistory losing keyboard selection focus if identical selection is redundantly reapplied - for (int pos = 0; pos < static_cast(selections.size()); ++pos) - if (m_listBoxHistory->IsSelected(pos) != selections[pos]) - m_listBoxHistory->SetSelection(pos, selections[pos]); -} - - -void MainDialog::removeObsoleteCfgHistoryItems(const std::vector& filenames) -{ - //don't use wxString: NOT thread-safe! (e.g. non-atomic ref-count) - - auto getMissingFilesAsync = [filenames]() -> std::vector - { - //boost::this_thread::sleep(boost::posix_time::millisec(5000)); - - //check existence of all config files in parallel! - std::list> fileEx; - - for (auto it = filenames.begin(); it != filenames.end(); ++it) //avoid VC11 compiler issues with std::for_each - { - const Zstring filename = *it; //don't reference iterator in lambda! - fileEx.push_back(zen::async2([=] { return fileExists(filename); })); - } - - //potentially slow network access => limit maximum wait time! - wait_for_all_timed(fileEx.begin(), fileEx.end(), boost::posix_time::milliseconds(1000)); - - std::vector missingFiles; - - auto itFut = fileEx.begin(); - for (auto it = filenames.begin(); it != filenames.end(); ++it, (void)++itFut) //void: prevent ADL from dragging in boost's ,-overload: "MSVC warning C4913: user defined binary operator ',' exists but no overload could convert all operands" - if (itFut->is_ready() && !itFut->get()) //remove only files that are confirmed to be non-existent - missingFiles.push_back(*it); - - return missingFiles; - }; - - processAsync(getMissingFilesAsync, [this](const std::vector& files) { removeCfgHistoryItems(files); }); -} - - -void MainDialog::removeCfgHistoryItems(const std::vector& filenames) -{ - std::for_each(filenames.begin(), filenames.end(), [&](const Zstring& filename) - { - const int histSize = m_listBoxHistory->GetCount(); - for (int i = 0; i < histSize; ++i) - if (auto histData = dynamic_cast(m_listBoxHistory->GetClientObject(i))) - if (EqualFilename()(filename, histData->cfgFile_)) - { - m_listBoxHistory->Delete(i); - break; - } - }); -} - - -void MainDialog::updateUnsavedCfgStatus() -{ - const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : Zstring(); - - const bool haveUnsavedCfg = lastConfigurationSaved != getConfig(); - - //update save config button - const bool allowSave = haveUnsavedCfg || - activeConfigFiles.size() > 1; - - auto makeBrightGrey = [](const wxBitmap& bmp) -> wxBitmap - { - wxImage img = bmp.ConvertToImage().ConvertToGreyscale(1.0/3, 1.0/3, 1.0/3); //treat all channels equally! - brighten(img, 80); - return img; - }; - //setImage(*m_bpButtonSave, greyScale(getResourceImage(L"save"))); - - setImage(*m_bpButtonSave, allowSave ? getResourceImage(L"save") : makeBrightGrey(getResourceImage(L"save"))); - m_bpButtonSave->Enable(allowSave); - m_menuItemSave->Enable(allowSave); //bitmap is automatically greyscaled on Win7 (introducing a crappy looking shift), but not on XP - - //set main dialog title - wxString title; - if (haveUnsavedCfg) - title += L'*'; - - if (!activeCfgFilename.empty()) - title += toWx(activeCfgFilename); - else if (activeConfigFiles.size() > 1) - { - const wchar_t* EM_DASH = L" \u2014 "; - title += xmlAccess::extractJobName(activeConfigFiles[0]); - std::for_each(activeConfigFiles.begin() + 1, activeConfigFiles.end(), [&](const Zstring& filename) { title += EM_DASH + xmlAccess::extractJobName(filename); }); - } - else - title += L"FreeFileSync - " + _("Folder Comparison and Synchronization"); - - SetTitle(title); -} - - -void MainDialog::OnConfigSave(wxCommandEvent& event) -{ - const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : Zstring(); - - //if we work on a single named configuration document: save directly if changed - //else: always show file dialog - if (!activeCfgFilename.empty()) - { - using namespace xmlAccess; - - switch (getXmlType(activeCfgFilename)) //throw() - { - case XML_TYPE_GUI: - trySaveConfig(&activeCfgFilename); - return; - case XML_TYPE_BATCH: - trySaveBatchConfig(&activeCfgFilename); - return; - case XML_TYPE_GLOBAL: - case XML_TYPE_OTHER: - assert(false); - return; - } - } - - trySaveConfig(nullptr); -} - - -void MainDialog::OnConfigSaveAs(wxCommandEvent& event) -{ - trySaveConfig(nullptr); -} - - -void MainDialog::OnSaveAsBatchJob(wxCommandEvent& event) -{ - trySaveBatchConfig(nullptr); -} - - -bool MainDialog::trySaveConfig(const Zstring* fileNameGui) //return true if saved successfully -{ - Zstring targetFilename; - - if (fileNameGui) - { - targetFilename = *fileNameGui; - assert(endsWith(targetFilename, Zstr(".ffs_gui"))); - } - else - { - Zstring defaultFileName = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : Zstr("SyncSettings.ffs_gui"); - //attention: activeConfigFiles may be an imported *.ffs_batch file! We don't want to overwrite it with a GUI config! - if (endsWith(defaultFileName, Zstr(".ffs_batch"))) - replace(defaultFileName, Zstr(".ffs_batch"), Zstr(".ffs_gui"), false); - - wxFileDialog filePicker(this, //put modal dialog on stack: creating this on freestore leads to memleak! - wxEmptyString, - //OS X really needs dir/file separated like this: - utfCvrtTo(beforeLast(defaultFileName, FILE_NAME_SEPARATOR)), //default dir; empty string if / not found - utfCvrtTo(afterLast (defaultFileName, FILE_NAME_SEPARATOR)), //default file; whole string if / not found - wxString(L"FreeFileSync (*.ffs_gui)|*.ffs_gui") + L"|" +_("All files") + L" (*.*)|*", - wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - if (filePicker.ShowModal() != wxID_OK) - return false; - targetFilename = toZ(filePicker.GetPath()); - } - - const xmlAccess::XmlGuiConfig guiCfg = getConfig(); - - try - { - xmlAccess::writeConfig(guiCfg, targetFilename); //throw FfsXmlError - setLastUsedConfig(targetFilename, guiCfg); - - flashStatusInformation(_("Configuration saved")); - return true; - } - catch (const xmlAccess::FfsXmlError& e) - { - showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); - return false; - } -} - - -bool MainDialog::trySaveBatchConfig(const Zstring* fileNameBatch) -{ - //essentially behave like trySaveConfig(): the collateral damage of not saving GUI-only settings "hideExcludedItems, m_bpButtonViewTypeSyncAction" is negliable - - const xmlAccess::XmlGuiConfig guiCfg = getConfig(); - - Zstring targetFilename; - xmlAccess::XmlBatchConfig batchCfg; - - if (fileNameBatch) - { - targetFilename = *fileNameBatch; - batchCfg = convertGuiToBatchPreservingExistingBatch(guiCfg, *fileNameBatch); - assert(endsWith(targetFilename, Zstr(".ffs_batch"))); - } - else - { - const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : Zstring(); - batchCfg = convertGuiToBatchPreservingExistingBatch(guiCfg, activeCfgFilename); - - //let user change batch config: this should change batch-exclusive settings only, else the "setLastUsedConfig" below would be somewhat of a lie - if (!customizeBatchConfig(this, - batchCfg, //in/out - globalCfg.gui.onCompletionHistory, - globalCfg.gui.onCompletionHistoryMax)) - return false; - - Zstring defaultFileName = !activeCfgFilename.empty() ? activeCfgFilename : Zstr("BatchRun.ffs_batch"); - //attention: activeConfigFiles may be a *.ffs_gui file! We don't want to overwrite it with a BATCH config! - if (endsWith(defaultFileName, Zstr(".ffs_gui"))) - replace(defaultFileName, Zstr(".ffs_gui"), Zstr(".ffs_batch")); - - wxFileDialog filePicker(this, //put modal dialog on stack: creating this on freestore leads to memleak! - wxEmptyString, - //OS X really needs dir/file separated like this: - utfCvrtTo(beforeLast(defaultFileName, FILE_NAME_SEPARATOR)), //default dir; empty string if / not found - utfCvrtTo(afterLast (defaultFileName, FILE_NAME_SEPARATOR)), //default file; whole string if / not found - _("FreeFileSync batch") + L" (*.ffs_batch)|*.ffs_batch" + L"|" +_("All files") + L" (*.*)|*", - wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - if (filePicker.ShowModal() != wxID_OK) - return false; - targetFilename = toZ(filePicker.GetPath()); - } - - try - { - xmlAccess::writeConfig(batchCfg, targetFilename); //throw FfsXmlError - - setLastUsedConfig(targetFilename, guiCfg); //[!] behave as if we had saved guiCfg - flashStatusInformation(_("Configuration saved")); - return true; - } - catch (const xmlAccess::FfsXmlError& e) - { - showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); - return false; - } -} - - -bool MainDialog::saveOldConfig() //return false on user abort -{ - if (lastConfigurationSaved != getConfig()) - { - const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : Zstring(); - - //notify user about changed settings - if (globalCfg.optDialogs.popupOnConfigChange) - if (!activeCfgFilename.empty()) - //only if check is active and non-default config file loaded - { - bool neverSaveChanges = false; - switch (showConfirmationDialog3(this, DialogInfoType::INFO, PopupDialogCfg3(). - setTitle(toWx(activeCfgFilename)). - setMainInstructions(replaceCpy(_("Do you want to save changes to %x?"), L"%x", fmtFileName(afterLast(activeCfgFilename, FILE_NAME_SEPARATOR)))). - setCheckBox(neverSaveChanges, _("Never save &changes"), ConfirmationButton3::DO_IT), - _("&Save"), _("Do&n't save"))) - { - case ConfirmationButton3::DO_IT: //save - using namespace xmlAccess; - switch (getXmlType(activeCfgFilename)) //throw() - { - case XML_TYPE_GUI: - return trySaveConfig(&activeCfgFilename); - case XML_TYPE_BATCH: - return trySaveBatchConfig(&activeCfgFilename); - case XML_TYPE_GLOBAL: - case XML_TYPE_OTHER: - assert(false); - return false; - } - break; - - case ConfirmationButton3::DONT_DO_IT: //don't save - globalCfg.optDialogs.popupOnConfigChange = !neverSaveChanges; - break; - - case ConfirmationButton3::CANCEL: - return false; - } - } - - //discard current reference file(s), this ensures next app start will load instead of the original non-modified config selection - setLastUsedConfig(std::vector(), lastConfigurationSaved); - //this seems to make theoretical sense also: the job of this function is to make sure current (volatile) config and reference file name are in sync - // => if user does not save cfg, it is not attached to a physical file names anymore! - } - return true; -} - - -void MainDialog::OnConfigLoad(wxCommandEvent& event) -{ - const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : Zstring(); - - wxFileDialog filePicker(this, - wxEmptyString, - utfCvrtTo(beforeLast(activeCfgFilename, FILE_NAME_SEPARATOR)), //set default dir: empty string if "activeConfigFiles" is empty or has no path separator - wxEmptyString, - wxString(L"FreeFileSync (*.ffs_gui; *.ffs_batch)|*.ffs_gui;*.ffs_batch") + L"|" +_("All files") + L" (*.*)|*", - wxFD_OPEN | wxFD_MULTIPLE); - - if (filePicker.ShowModal() == wxID_OK) - { - wxArrayString tmp; - filePicker.GetPaths(tmp); - std::vector filenames(tmp.begin(), tmp.end()); - - loadConfiguration(toZ(filenames)); - } -} - - -void MainDialog::OnConfigNew(wxCommandEvent& event) -{ - if (!saveOldConfig()) //notify user about changed settings - return; - - xmlAccess::XmlGuiConfig newConfig; - - //add default exclusion filter: this is only ever relevant when creating new configurations! - //a default XmlGuiConfig does not need these user-specific exclusions! - Zstring& excludeFilter = newConfig.mainCfg.globalFilter.excludeFilter; - if (!excludeFilter.empty() && !endsWith(excludeFilter, Zstr("\n"))) - excludeFilter += Zstr("\n"); - excludeFilter += globalCfg.gui.defaultExclusionFilter; - - setConfig(newConfig, std::vector()); -} - - -void MainDialog::OnLoadFromHistory(wxCommandEvent& event) -{ - wxArrayInt selections; - m_listBoxHistory->GetSelections(selections); - - std::vector filenames; - std::for_each(selections.begin(), selections.end(), - [&](int pos) - { - if (auto histData = dynamic_cast(m_listBoxHistory->GetClientObject(pos))) - filenames.push_back(histData->cfgFile_); - else - assert(false); - }); - - if (!filenames.empty()) - loadConfiguration(filenames); - - //user changed m_listBoxHistory selection so it's this method's responsibility to synchronize with activeConfigFiles: - //- if user cancelled saving old config - //- there's an error loading new config - //- filenames is empty and user tried to unselect the current config - addFileToCfgHistory(activeConfigFiles); -} - - -void MainDialog::OnLoadFromHistoryDoubleClick(wxCommandEvent& event) -{ - wxArrayInt selections; - m_listBoxHistory->GetSelections(selections); - - std::vector filenames; - std::for_each(selections.begin(), selections.end(), [&](int pos) - { - if (auto histData = dynamic_cast(m_listBoxHistory->GetClientObject(pos))) - filenames.push_back(histData->cfgFile_); - else - assert(false); - }); - - if (!filenames.empty()) - if (loadConfiguration(filenames)) - { - //simulate button click on "compare" - wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); - if (wxEvtHandler* evtHandler = m_buttonCompare->GetEventHandler()) - evtHandler->ProcessEvent(dummy2); //synchronous call - } - - //synchronize m_listBoxHistory and activeConfigFiles, see OnLoadFromHistory() - addFileToCfgHistory(activeConfigFiles); -} - - -bool MainDialog::loadConfiguration(const std::vector& filenames) -{ - if (filenames.empty()) - return true; - - if (!saveOldConfig()) - return false; //cancelled by user - - //load XML - xmlAccess::XmlGuiConfig newGuiCfg; //structure to receive gui settings, already defaulted!! - try - { - //allow reading batch configurations also - xmlAccess::readAnyConfig(filenames, newGuiCfg); //throw FfsXmlError - - setConfig(newGuiCfg, filenames); - //flashStatusInformation(("Configuration loaded")); -> irrelevant!? - return true; - } - catch (const xmlAccess::FfsXmlError& e) - { - if (e.getSeverity() == xmlAccess::FfsXmlError::WARNING) - { - showNotificationDialog(this, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(e.toString())); - setConfig(newGuiCfg, filenames); - setLastUsedConfig(filenames, xmlAccess::XmlGuiConfig()); //simulate changed config due to parsing errors - } - else - showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); - return false; - } -} - -void MainDialog::deleteSelectedCfgHistoryItems() -{ - wxArrayInt tmp; - m_listBoxHistory->GetSelections(tmp); - - std::set selections(tmp.begin(), tmp.end()); //sort ascending! - //delete starting with high positions: - std::for_each(selections.rbegin(), selections.rend(), [&](int pos) { m_listBoxHistory->Delete(pos); }); - - //set active selection on next element to allow "batch-deletion" by holding down DEL key - if (!selections.empty() && m_listBoxHistory->GetCount() > 0) - { - int newSelection = *selections.begin(); - if (newSelection >= static_cast(m_listBoxHistory->GetCount())) - newSelection = m_listBoxHistory->GetCount() - 1; - m_listBoxHistory->SetSelection(newSelection); - } -} - - -void MainDialog::OnCfgHistoryRightClick(wxMouseEvent& event) -{ - ContextMenu menu; - menu.addItem(_("Delete") + L"\tDel", [this] { deleteSelectedCfgHistoryItems(); }); - menu.popup(*this); -} - - -void MainDialog::OnCfgHistoryKeyEvent(wxKeyEvent& event) -{ - const int keyCode = event.GetKeyCode(); - if (keyCode == WXK_DELETE || keyCode == WXK_NUMPAD_DELETE) - { - deleteSelectedCfgHistoryItems(); - return; //"swallow" event - } - event.Skip(); -} - - -void MainDialog::OnClose(wxCloseEvent& event) -{ - //attention: system shutdown: is handled in onQueryEndSession()! - - //regular destruction handling - if (event.CanVeto()) - { - const bool cancelled = !saveOldConfig(); //notify user about changed settings - if (cancelled) - { - //attention: this Veto() will NOT cancel system shutdown since saveOldConfig() blocks on modal dialog - - event.Veto(); - return; - } - } - - Destroy(); -} - - -void MainDialog::onCheckRows(CheckRowsEvent& event) -{ - std::set selectedRows; - - const size_t rowLast = std::min(event.rowLast_, gridDataView->rowsOnView()); //consider dummy rows - for (size_t i = event.rowFirst_; i < rowLast; ++i) - selectedRows.insert(i); - - if (!selectedRows.empty()) - { - std::vector objects = gridDataView->getAllFileRef(selectedRows); - setFilterManually(objects, event.setIncluded_); - } -} - - -void MainDialog::onSetSyncDirection(SyncDirectionEvent& event) -{ - std::set selectedRows; - - const size_t rowLast = std::min(event.rowLast_, gridDataView->rowsOnView()); //consider dummy rows - for (size_t i = event.rowFirst_; i < rowLast; ++i) - selectedRows.insert(i); - - if (!selectedRows.empty()) - { - std::vector objects = gridDataView->getAllFileRef(selectedRows); - setSyncDirManually(objects, event.direction_); - } -} - - -void MainDialog::setLastUsedConfig(const Zstring& filename, const xmlAccess::XmlGuiConfig& guiConfig) -{ - std::vector filenames; - filenames.push_back(filename); - setLastUsedConfig(filenames, guiConfig); -} - - -void MainDialog::setLastUsedConfig(const std::vector& filenames, - const xmlAccess::XmlGuiConfig& guiConfig) -{ - activeConfigFiles = filenames; - lastConfigurationSaved = guiConfig; - - addFileToCfgHistory(activeConfigFiles); //put filename on list of last used config files - - updateUnsavedCfgStatus(); -} - - -void MainDialog::setConfig(const xmlAccess::XmlGuiConfig& newGuiCfg, const std::vector& referenceFiles) -{ - currentCfg = newGuiCfg; - - //evaluate new settings... - - //(re-)set view filter buttons - setViewFilterDefault(); - - updateGlobalFilterButton(); - - //set first folder pair - firstFolderPair->setValues(currentCfg.mainCfg.firstPair.leftDirectory, - currentCfg.mainCfg.firstPair.rightDirectory, - currentCfg.mainCfg.firstPair.altCmpConfig, - currentCfg.mainCfg.firstPair.altSyncConfig, - currentCfg.mainCfg.firstPair.localFilter); - - //folderHistoryLeft->addItem(currentCfg.mainCfg.firstPair.leftDirectory); - //folderHistoryRight->addItem(currentCfg.mainCfg.firstPair.rightDirectory); - - setAddFolderPairs(currentCfg.mainCfg.additionalPairs); - - //read GUI layout - m_checkBoxHideExcluded->SetValue(currentCfg.hideExcludedItems); - - setViewTypeSyncAction(currentCfg.highlightSyncAction); - - clearGrid(); //+ update GUI! - - setLastUsedConfig(referenceFiles, newGuiCfg); -} - - -inline -FolderPairEnh getEnhancedPair(const FolderPairPanel* panel) -{ - return FolderPairEnh(panel->getLeftDir(), - panel->getRightDir(), - panel->getAltCompConfig(), - panel->getAltSyncConfig(), - panel->getAltFilterConfig()); -} - - -xmlAccess::XmlGuiConfig MainDialog::getConfig() const -{ - xmlAccess::XmlGuiConfig guiCfg = currentCfg; - - //load settings whose ownership lies not in currentCfg: - - //first folder pair - guiCfg.mainCfg.firstPair = FolderPairEnh(firstFolderPair->getLeftDir(), - firstFolderPair->getRightDir(), - firstFolderPair->getAltCompConfig(), - firstFolderPair->getAltSyncConfig(), - firstFolderPair->getAltFilterConfig()); - - //add additional pairs - guiCfg.mainCfg.additionalPairs.clear(); - std::transform(additionalFolderPairs.begin(), additionalFolderPairs.end(), - std::back_inserter(guiCfg.mainCfg.additionalPairs), getEnhancedPair); - - //sync preview - guiCfg.highlightSyncAction = m_bpButtonViewTypeSyncAction->isActive(); - - return guiCfg; -} - - -const Zstring& MainDialog::lastRunConfigName() -{ - static Zstring instance = zen::getConfigDir() + Zstr("LastRun.ffs_gui"); - return instance; -} - - -void MainDialog::updateGuiDelayedIf(bool condition) -{ - const int delay = 400; - - if (condition) - { - gridview::refresh(*m_gridMainL, *m_gridMainC, *m_gridMainR); - m_gridMainL->Update(); - m_gridMainC->Update(); - m_gridMainR->Update(); - - wxMilliSleep(delay); //some delay to show the changed GUI before removing rows from sight - } - - updateGui(); -} - - -void MainDialog::OnShowExcluded(wxCommandEvent& event) -{ - //toggle showing filtered rows - currentCfg.hideExcludedItems = !currentCfg.hideExcludedItems; - //make sure, checkbox and value are in sync - m_checkBoxHideExcluded->SetValue(currentCfg.hideExcludedItems); - - updateGui(); -} - - -void MainDialog::OnConfigureFilter(wxCommandEvent& event) -{ - if (showFilterDialog(this, currentCfg.mainCfg.globalFilter, _("Filter")) == ReturnSmallDlg::BUTTON_OKAY) - { - updateGlobalFilterButton(); //refresh global filter icon - applyFilterConfig(); //re-apply filter - } - - //event.Skip() -} - - -void MainDialog::OnGlobalFilterContext(wxMouseEvent& event) -{ - auto clearFilter = [&] - { - currentCfg.mainCfg.globalFilter = FilterConfig(); - updateGlobalFilterButton(); //refresh global filter icon - applyFilterConfig(); //re-apply filter - }; - auto copyFilter = [&] { filterCfgOnClipboard = make_unique(currentCfg.mainCfg.globalFilter); }; - auto pasteFilter = [&] - { - if (filterCfgOnClipboard) - { - currentCfg.mainCfg.globalFilter = *filterCfgOnClipboard; - updateGlobalFilterButton(); //refresh global filter icon - applyFilterConfig(); //re-apply filter - } - }; - - ContextMenu menu; - menu.addItem( _("Clear filter settings"), clearFilter, nullptr, !isNullFilter(currentCfg.mainCfg.globalFilter)); - menu.addSeparator(); - menu.addItem( _("Copy"), copyFilter, nullptr, !isNullFilter(currentCfg.mainCfg.globalFilter)); - menu.addItem( _("Paste"), pasteFilter, nullptr, filterCfgOnClipboard.get() != nullptr); - menu.popup(*this); -} - - -void MainDialog::OnToggleViewType(wxCommandEvent& event) -{ - setViewTypeSyncAction(!m_bpButtonViewTypeSyncAction->isActive()); //toggle view -} - - -void MainDialog::OnToggleViewButton(wxCommandEvent& event) -{ - if (auto button = dynamic_cast(event.GetEventObject())) - { - button->toggle(); - updateGui(); - } - else - assert(false); -} - - -inline -wxBitmap buttonPressed(const std::string& name) -{ - wxBitmap background = getResourceImage(L"buttonPressed"); - return mirrorIfRtl( - layOver(getResourceImage(utfCvrtTo(name)), background)); -} - - -inline -wxBitmap buttonReleased(const std::string& name) -{ - wxImage output = getResourceImage(utfCvrtTo(name)).ConvertToImage().ConvertToGreyscale(1.0/3, 1.0/3, 1.0/3); //treat all channels equally! - //zen::moveImage(output, 1, 0); //move image right one pixel - - brighten(output, 80); - return mirrorIfRtl(output); -} - - -void MainDialog::initViewFilterButtons() -{ - m_bpButtonViewTypeSyncAction->init(getResourceImage(L"viewtype_sync_action"), getResourceImage(L"viewtype_cmp_result")); - //tooltip is updated dynamically in setViewTypeSyncAction() - - auto initButton = [](ToggleButton& btn, const char* imgName, const wxString& tooltip) { btn.init(buttonPressed(imgName), buttonReleased(imgName)); btn.SetToolTip(tooltip); }; - - //compare result buttons - initButton(*m_bpButtonShowLeftOnly, "cat_left_only", _("Show files that exist on left side only")); - initButton(*m_bpButtonShowRightOnly, "cat_right_only", _("Show files that exist on right side only")); - initButton(*m_bpButtonShowLeftNewer, "cat_left_newer", _("Show files that are newer on left")); - initButton(*m_bpButtonShowRightNewer, "cat_right_newer", _("Show files that are newer on right")); - initButton(*m_bpButtonShowEqual, "cat_equal", _("Show files that are equal")); - initButton(*m_bpButtonShowDifferent, "cat_different", _("Show files that are different")); - initButton(*m_bpButtonShowConflict, "cat_conflict", _("Show conflicts")); - - //sync preview buttons - initButton(*m_bpButtonShowCreateLeft, "so_create_left", _("Show files that will be created on the left side")); - initButton(*m_bpButtonShowCreateRight, "so_create_right", _("Show files that will be created on the right side")); - initButton(*m_bpButtonShowDeleteLeft, "so_delete_left", _("Show files that will be deleted on the left side")); - initButton(*m_bpButtonShowDeleteRight, "so_delete_right", _("Show files that will be deleted on the right side")); - initButton(*m_bpButtonShowUpdateLeft, "so_update_left", _("Show files that will be overwritten on left side")); - initButton(*m_bpButtonShowUpdateRight, "so_update_right", _("Show files that will be overwritten on right side")); - initButton(*m_bpButtonShowDoNothing, "so_none", _("Show files that won't be copied")); -} - - -void MainDialog::setViewFilterDefault() -{ - auto setButton = [](ToggleButton* tb, bool value) { tb->setActive(value); }; - - const auto& def = globalCfg.gui.viewFilterDefault; - setButton(m_bpButtonShowLeftOnly, def.leftOnly); - setButton(m_bpButtonShowRightOnly, def.rightOnly); - setButton(m_bpButtonShowLeftNewer, def.leftNewer); - setButton(m_bpButtonShowRightNewer, def.rightNewer); - setButton(m_bpButtonShowEqual, def.equal); - setButton(m_bpButtonShowDifferent, def.different); - setButton(m_bpButtonShowConflict, def.conflict); - - setButton(m_bpButtonShowCreateLeft, def.createLeft); - setButton(m_bpButtonShowCreateRight,def.createRight); - setButton(m_bpButtonShowUpdateLeft, def.updateLeft); - setButton(m_bpButtonShowUpdateRight,def.updateRight); - setButton(m_bpButtonShowDeleteLeft, def.deleteLeft); - setButton(m_bpButtonShowDeleteRight,def.deleteRight); - setButton(m_bpButtonShowDoNothing, def.doNothing); -} - - -void MainDialog::OnViewButtonRightClick(wxMouseEvent& event) -{ - auto setButtonDefault = [](const ToggleButton* tb, bool& defaultValue) - { - if (tb->IsShown()) - defaultValue = tb->isActive(); - }; - - auto setDefault = [&] - { - auto& def = globalCfg.gui.viewFilterDefault; - setButtonDefault(m_bpButtonShowLeftOnly, def.leftOnly); - setButtonDefault(m_bpButtonShowRightOnly, def.rightOnly); - setButtonDefault(m_bpButtonShowLeftNewer, def.leftNewer); - setButtonDefault(m_bpButtonShowRightNewer, def.rightNewer); - setButtonDefault(m_bpButtonShowEqual, def.equal); - setButtonDefault(m_bpButtonShowDifferent, def.different); - setButtonDefault(m_bpButtonShowConflict, def.conflict); - - setButtonDefault(m_bpButtonShowCreateLeft, def.createLeft); - setButtonDefault(m_bpButtonShowCreateRight, def.createRight); - setButtonDefault(m_bpButtonShowUpdateLeft, def.updateLeft); - setButtonDefault(m_bpButtonShowUpdateRight, def.updateRight); - setButtonDefault(m_bpButtonShowDeleteLeft, def.deleteLeft); - setButtonDefault(m_bpButtonShowDeleteRight, def.deleteRight); - setButtonDefault(m_bpButtonShowDoNothing, def.doNothing); - }; - - ContextMenu menu; - menu.addItem( _("Set as default"), setDefault); - menu.popup(*this); -} - - -void MainDialog::updateGlobalFilterButton() -{ - //global filter: test for Null-filter - if (!isNullFilter(currentCfg.mainCfg.globalFilter)) - { - setImage(*m_bpButtonFilter, getResourceImage(L"filter")); - m_bpButtonFilter->SetToolTip(_("Filter") + L" (F10) (" + _("Active") + L")"); - } - else - { - setImage(*m_bpButtonFilter, greyScale(getResourceImage(L"filter"))); - m_bpButtonFilter->SetToolTip(_("Filter") + L" (F10) (" + _("None") + L")"); - } -} - - -void MainDialog::OnCompare(wxCommandEvent& event) -{ - //PERF_START; - - //wxBusyCursor dummy; -> redundant: progress already shown in progress dialog! - - wxWindow* oldFocus = wxWindow::FindFocus(); - ZEN_ON_SCOPE_EXIT(if (oldFocus) oldFocus->SetFocus();); //e.g. keep focus on main grid after pressing F5 - - int scrollPosX = 0; - int scrollPosY = 0; - m_gridMainL->GetViewStart(&scrollPosX, &scrollPosY); //preserve current scroll position - ZEN_ON_SCOPE_EXIT( - m_gridMainL->Scroll(scrollPosX, scrollPosY); // - m_gridMainR->Scroll(scrollPosX, scrollPosY); //restore - m_gridMainC->Scroll(-1, scrollPosY); ) // - - clearGrid(); //avoid memory peak by clearing old data first - - disableAllElements(true); //CompareStatusHandler will internally process Window messages, so avoid unexpected callbacks! - auto app = wxTheApp; //fix lambda/wxWigets/VC fuck up - ZEN_ON_SCOPE_EXIT(app->Yield(); enableAllElements()); //ui update before enabling buttons again: prevent strange behaviour of delayed button clicks - - try - { - //class handling status display and error messages - CompareStatusHandler statusHandler(*this); - - const std::vector cmpConfig = zen::extractCompareCfg(getConfig().mainCfg); - - //GUI mode: place directory locks on directories isolated(!) during both comparison and synchronization - std::unique_ptr dirLocks; - - //COMPARE DIRECTORIES - compare(globalCfg.fileTimeTolerance, - globalCfg.optDialogs, - true, //allow pw prompt - globalCfg.runWithBackgroundPriority, - globalCfg.createLockFile, - dirLocks, - cmpConfig, - folderCmp, - statusHandler); //throw GuiAbortProcess - } - catch (GuiAbortProcess&) - { - // if (m_buttonCompare->IsShownOnScreen()) m_buttonCompare->SetFocus(); - updateGui(); //refresh grid in ANY case! (also on abort) - return; - } - - gridDataView->setData(folderCmp); //update view on data - treeDataView->setData(folderCmp); // - updateGui(); - - // if (m_buttonSync->IsShownOnScreen()) m_buttonSync->SetFocus(); - - gridview::clearSelection(*m_gridMainL, *m_gridMainC, *m_gridMainR); - m_gridNavi->clearSelection(); - - //play (optional) sound notification after sync has completed (GUI and batch mode) - //const Zstring soundFile = zen::getResourceDir() + Zstr("Compare_Complete.wav"); - //if (fileExists(soundFile)) - // wxSound::Play(toWx(soundFile), wxSOUND_ASYNC); - - //add to folder history after successful comparison only - folderHistoryLeft ->addItem(toZ(m_directoryLeft ->GetValue())); - folderHistoryRight->addItem(toZ(m_directoryRight->GetValue())); - - //prepare status information - if (allElementsEqual(folderCmp)) - flashStatusInformation(_("All folders are in sync")); -} - - -void MainDialog::updateTopButtonImages() -{ - auto updateButton = [&](wxBitmapButton& btn, const wxBitmap& bmp, const wxString& variantName, bool makeGrey) - { - wxImage labelImage = createImageFromText(btn.GetLabel(), btn.GetFont(), wxSystemSettings::GetColour(makeGrey ? wxSYS_COLOUR_GRAYTEXT : wxSYS_COLOUR_BTNTEXT)); - wxImage variantImage = createImageFromText(variantName, wxFont(wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxBOLD), wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); - - wxImage descrImage = stackImages(labelImage, variantImage, ImageStackLayout::VERTICAL, ImageStackAlignment::CENTER); - const wxImage& iconImage = makeGrey ? greyScale(bmp.ConvertToImage()) : bmp.ConvertToImage(); - - wxImage dynImage = btn.GetLayoutDirection() != wxLayout_RightToLeft ? - stackImages(iconImage, descrImage, ImageStackLayout::HORIZONTAL, ImageStackAlignment::CENTER, 5) : - stackImages(descrImage, iconImage, ImageStackLayout::HORIZONTAL, ImageStackAlignment::CENTER, 5); - - //SetMinSize() instead of SetSize() is needed here for wxWindows layout determination to work corretly - wxSize minSize = dynImage.GetSize() + wxSize(10, 10); //add border space - minSize.x = std::max(minSize.x, 180); - btn.SetMinSize(minSize); - - btn.SetBitmapLabel(wxBitmap(dynImage)); - //SetLabel() calls confuse wxBitmapButton in the disabled state and it won't show the image! workaround: - btn.SetBitmapDisabled(wxBitmap(dynImage.ConvertToDisabled())); - }; - - updateButton(*m_buttonCompare, getResourceImage(L"compare"), getConfig().mainCfg.getCompVariantName(), false); - updateButton(*m_buttonSync, getResourceImage(L"sync"), getConfig().mainCfg.getSyncVariantName(), folderCmp.empty()); - - m_panelTopButtons->Layout(); -} - - -void MainDialog::updateGui() -{ - updateGridViewData(); //update gridDataView and write status information - - updateStatistics(); - - updateUnsavedCfgStatus(); - - updateTopButtonImages(); - - auiMgr.Update(); //fix small display distortion, if view filter panel is empty -} - - -void MainDialog::clearGrid() -{ - folderCmp.clear(); - gridDataView->setData(folderCmp); - treeDataView->setData(folderCmp); - - updateGui(); -} - - -void MainDialog::updateStatistics() -{ - //update preview of item count and bytes to be transferred: - const SyncStatistics st(folderCmp); - - setText(*m_staticTextData, filesizeToShortString(st.getDataToProcess())); - if (st.getDataToProcess() == 0) - m_bitmapData->SetBitmap(greyScale(getResourceImage(L"data"))); - else - m_bitmapData->SetBitmap(getResourceImage(L"data")); - - auto setValue = [](wxStaticText& txtControl, int value, wxStaticBitmap& bmpControl, const wchar_t* bmpName) - { - setText(txtControl, toGuiString(value)); - - if (value == 0) - bmpControl.SetBitmap(greyScale(mirrorIfRtl(getResourceImage(bmpName)))); - else - bmpControl.SetBitmap(mirrorIfRtl(getResourceImage(bmpName))); - }; - - setValue(*m_staticTextCreateLeft, st.getCreate(), *m_bitmapCreateLeft, L"so_create_left_small"); - setValue(*m_staticTextUpdateLeft, st.getUpdate(), *m_bitmapUpdateLeft, L"so_update_left_small"); - setValue(*m_staticTextDeleteLeft, st.getDelete(), *m_bitmapDeleteLeft, L"so_delete_left_small"); - setValue(*m_staticTextCreateRight, st.getCreate(), *m_bitmapCreateRight, L"so_create_right_small"); - setValue(*m_staticTextUpdateRight, st.getUpdate(), *m_bitmapUpdateRight, L"so_update_right_small"); - setValue(*m_staticTextDeleteRight, st.getDelete(), *m_bitmapDeleteRight, L"so_delete_right_small"); - - m_panelStatistics->Layout(); - m_panelStatistics->Refresh(); //fix small mess up on RTL layout -} - - -void MainDialog::OnSyncSettings(wxCommandEvent& event) -{ - ExecWhenFinishedCfg ewfCfg = { ¤tCfg.mainCfg.onCompletion, - &globalCfg.gui.onCompletionHistory, - globalCfg.gui.onCompletionHistoryMax - }; - - if (showSyncConfigDlg(this, - currentCfg.mainCfg.cmpConfig.compareVar, - currentCfg.mainCfg.syncCfg, - _("Synchronization Settings"), - ¤tCfg.handleError, - &ewfCfg) == ReturnSyncConfig::BUTTON_OKAY) //optional input parameter - { - applySyncConfig(); - } -} - - -void MainDialog::applyCompareConfig(bool switchMiddleGrid) -{ - clearGrid(); //+ GUI update - - //convenience: change sync view - if (switchMiddleGrid) - switch (currentCfg.mainCfg.cmpConfig.compareVar) - { - case CMP_BY_TIME_SIZE: - setViewTypeSyncAction(true); - break; - case CMP_BY_CONTENT: - setViewTypeSyncAction(false); - break; - } -} - - -void MainDialog::OnCmpSettings(wxCommandEvent& event) -{ - //show window right next to the compare-config button - //wxPoint windowPos = m_bpButtonCmpConfig->GetScreenPosition(); - //windowPos.x += m_bpButtonCmpConfig->GetSize().GetWidth() + 5; - - CompConfig cmpConfigNew = currentCfg.mainCfg.cmpConfig; - - if (zen::showCompareCfgDialog(this, cmpConfigNew, _("Comparison Settings")) == ReturnSmallDlg::BUTTON_OKAY && - //check if settings were changed at all - cmpConfigNew != currentCfg.mainCfg.cmpConfig) - { - currentCfg.mainCfg.cmpConfig = cmpConfigNew; - - applyCompareConfig(true); //true: switchMiddleGrid - } -} - - -void MainDialog::OnStartSync(wxCommandEvent& event) -{ - if (folderCmp.empty()) - { - //quick sync: simulate button click on "compare" - wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); - if (wxEvtHandler* evtHandler = m_buttonCompare->GetEventHandler()) - evtHandler->ProcessEvent(dummy2); //synchronous call - - if (folderCmp.empty()) //check if user aborted or error occurred, ect... - return; - } - - //show sync preview/confirmation dialog - if (globalCfg.optDialogs.confirmSyncStart) - { - bool dontShowAgain = false; - - if (zen::showSyncConfirmationDlg(this, - getConfig().mainCfg.getSyncVariantName(), - zen::SyncStatistics(folderCmp), - dontShowAgain) != ReturnSmallDlg::BUTTON_OKAY) - return; - - globalCfg.optDialogs.confirmSyncStart = !dontShowAgain; - } - - try - { - //PERF_START; - const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : Zstring(); - - const auto& guiCfg = getConfig(); - - disableAllElements(false); //SyncStatusHandler will internally process Window messages, so avoid unexpected callbacks! - ZEN_ON_SCOPE_EXIT(enableAllElements()); - - //class handling status updates and error messages - SyncStatusHandler statusHandler(this, //throw GuiAbortProcess - globalCfg.lastSyncsLogFileSizeMax, - currentCfg.handleError, - globalCfg.automaticRetryCount, - globalCfg.automaticRetryDelay, - xmlAccess::extractJobName(activeCfgFilename), - guiCfg.mainCfg.onCompletion, - globalCfg.gui.onCompletionHistory); - - //wxBusyCursor dummy; -> redundant: progress already shown in progress dialog! - - //GUI mode: place directory locks on directories isolated(!) during both comparison and synchronization - std::unique_ptr dirLocks; - if (globalCfg.createLockFile) - { - std::set dirnamesExisting; - for (auto it = begin(folderCmp); it != end(folderCmp); ++it) - { - if (it->isExisting()) //do NOT check directory existence again! - dirnamesExisting.insert(it->getBaseDirPf()); - if (it->isExisting()) - dirnamesExisting.insert(it->getBaseDirPf()); - } - dirLocks = make_unique(dirnamesExisting, globalCfg.optDialogs.warningDirectoryLockFailed, statusHandler); - } - - //START SYNCHRONIZATION - const std::vector syncProcessCfg = zen::extractSyncCfg(guiCfg.mainCfg); - if (syncProcessCfg.size() != folderCmp.size()) - throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo(__LINE__)); - //should never happen: sync button is deactivated if they are not in sync - - synchronize(localTime(), - globalCfg.optDialogs, - globalCfg.verifyFileCopy, - globalCfg.copyLockedFiles, - globalCfg.copyFilePermissions, - globalCfg.failsafeFileCopy, - globalCfg.runWithBackgroundPriority, - syncProcessCfg, - folderCmp, - statusHandler); - } - catch (GuiAbortProcess&) - { - //do NOT disable the sync button: user might want to try to sync the REMAINING rows - } //enableSynchronization(false); - - //remove empty rows: just a beautification, invalid rows shouldn't cause issues - gridDataView->removeInvalidRows(); - - updateGui(); -} - - -void MainDialog::onGridDoubleClickL(GridClickEvent& event) -{ - onGridDoubleClickRim(event.row_, true); -} -void MainDialog::onGridDoubleClickR(GridClickEvent& event) -{ - onGridDoubleClickRim(event.row_, false); -} - -void MainDialog::onGridDoubleClickRim(size_t row, bool leftSide) -{ - if (!globalCfg.gui.externelApplications.empty()) - { - std::vector selection; - if (FileSystemObject* fsObj = gridDataView->getObject(row)) //selection must be a list of BOUND pointers! - selection.push_back(fsObj); - - openExternalApplication(globalCfg.gui.externelApplications[0].second, selection, leftSide); - } -} - - -void MainDialog::onGridLabelLeftClick(bool onLeft, ColumnTypeRim type) -{ - auto sortInfo = gridDataView->getSortInfo(); - - bool sortAscending = GridView::getDefaultSortDirection(type); - if (sortInfo && sortInfo->onLeft_ == onLeft && sortInfo->type_ == type) - sortAscending = !sortInfo->ascending_; - - gridDataView->sortView(type, onLeft, sortAscending); - - gridview::clearSelection(*m_gridMainL, *m_gridMainC, *m_gridMainR); - updateGui(); //refresh gridDataView -} - -void MainDialog::onGridLabelLeftClickL(GridClickEvent& event) -{ - onGridLabelLeftClick(true, static_cast(event.colType_)); -} -void MainDialog::onGridLabelLeftClickR(GridClickEvent& event) -{ - onGridLabelLeftClick(false, static_cast(event.colType_)); -} - - -void MainDialog::onGridLabelLeftClickC(GridClickEvent& event) -{ - //sorting middle grid is more or less useless: therefore let's toggle view instead! - setViewTypeSyncAction(!m_bpButtonViewTypeSyncAction->isActive()); //toggle view -} - - -void MainDialog::OnSwapSides(wxCommandEvent& event) -{ - //swap directory names: first pair - firstFolderPair->setValues(firstFolderPair->getRightDir(), // swap directories - firstFolderPair->getLeftDir(), // - firstFolderPair->getAltCompConfig(), - firstFolderPair->getAltSyncConfig(), - firstFolderPair->getAltFilterConfig()); - - //additional pairs - for (auto it = additionalFolderPairs.begin(); it != additionalFolderPairs.end(); ++it) - { - FolderPairPanel* panel = *it; - panel->setValues(panel->getRightDir(), // swap directories - panel->getLeftDir(), // - panel->getAltCompConfig(), - panel->getAltSyncConfig(), - panel->getAltFilterConfig()); - } - - //swap view filter - bool tmp = m_bpButtonShowLeftOnly->isActive(); - m_bpButtonShowLeftOnly->setActive(m_bpButtonShowRightOnly->isActive()); - m_bpButtonShowRightOnly->setActive(tmp); - - tmp = m_bpButtonShowLeftNewer->isActive(); - m_bpButtonShowLeftNewer->setActive(m_bpButtonShowRightNewer->isActive()); - m_bpButtonShowRightNewer->setActive(tmp); - - /* for sync preview and "mirror" variant swapping may create strange effect: - tmp = m_bpButtonShowCreateLeft->isActive(); - m_bpButtonShowCreateLeft->setActive(m_bpButtonShowCreateRight->isActive()); - m_bpButtonShowCreateRight->setActive(tmp); - - tmp = m_bpButtonShowDeleteLeft->isActive(); - m_bpButtonShowDeleteLeft->setActive(m_bpButtonShowDeleteRight->isActive()); - m_bpButtonShowDeleteRight->setActive(tmp); - - tmp = m_bpButtonShowUpdateLeft->isActive(); - m_bpButtonShowUpdateLeft->setActive(m_bpButtonShowUpdateRight->isActive()); - m_bpButtonShowUpdateRight->setActive(tmp); - */ - - //swap grid information - zen::swapGrids(getConfig().mainCfg, folderCmp); - - updateGui(); -} - - -void MainDialog::updateGridViewData() -{ - size_t filesOnLeftView = 0; - size_t foldersOnLeftView = 0; - size_t filesOnRightView = 0; - size_t foldersOnRightView = 0; - zen::UInt64 filesizeLeftView; - zen::UInt64 filesizeRightView; - - //disable all buttons per default - m_bpButtonShowLeftOnly ->Show(false); - m_bpButtonShowRightOnly ->Show(false); - m_bpButtonShowLeftNewer ->Show(false); - m_bpButtonShowRightNewer->Show(false); - m_bpButtonShowDifferent ->Show(false); - m_bpButtonShowEqual ->Show(false); - m_bpButtonShowConflict ->Show(false); - - m_bpButtonShowCreateLeft ->Show(false); - m_bpButtonShowCreateRight->Show(false); - m_bpButtonShowDeleteLeft ->Show(false); - m_bpButtonShowDeleteRight->Show(false); - m_bpButtonShowUpdateLeft ->Show(false); - m_bpButtonShowUpdateRight->Show(false); - m_bpButtonShowDoNothing ->Show(false); - - if (m_bpButtonViewTypeSyncAction->isActive()) - { - const GridView::StatusSyncPreview result = gridDataView->updateSyncPreview(currentCfg.hideExcludedItems, - m_bpButtonShowCreateLeft ->isActive(), - m_bpButtonShowCreateRight->isActive(), - m_bpButtonShowDeleteLeft ->isActive(), - m_bpButtonShowDeleteRight->isActive(), - m_bpButtonShowUpdateLeft ->isActive(), - m_bpButtonShowUpdateRight->isActive(), - m_bpButtonShowDoNothing ->isActive(), - m_bpButtonShowEqual ->isActive(), - m_bpButtonShowConflict ->isActive()); - filesOnLeftView = result.filesOnLeftView; - foldersOnLeftView = result.foldersOnLeftView; - filesOnRightView = result.filesOnRightView; - foldersOnRightView = result.foldersOnRightView; - filesizeLeftView = result.filesizeLeftView; - filesizeRightView = result.filesizeRightView; - - - //sync preview buttons - m_bpButtonShowCreateLeft ->Show(result.existsSyncCreateLeft); - m_bpButtonShowCreateRight ->Show(result.existsSyncCreateRight); - m_bpButtonShowDeleteLeft ->Show(result.existsSyncDeleteLeft); - m_bpButtonShowDeleteRight ->Show(result.existsSyncDeleteRight); - m_bpButtonShowUpdateLeft ->Show(result.existsSyncDirLeft); - m_bpButtonShowUpdateRight ->Show(result.existsSyncDirRight); - m_bpButtonShowDoNothing ->Show(result.existsSyncDirNone); - m_bpButtonShowEqual ->Show(result.existsSyncEqual); - m_bpButtonShowConflict ->Show(result.existsConflict); - - const bool anyViewFilterButtonShown = m_bpButtonShowCreateLeft ->IsShown() || - m_bpButtonShowCreateRight->IsShown() || - m_bpButtonShowDeleteLeft ->IsShown() || - m_bpButtonShowDeleteRight->IsShown() || - m_bpButtonShowUpdateLeft ->IsShown() || - m_bpButtonShowUpdateRight->IsShown() || - m_bpButtonShowDoNothing ->IsShown() || - m_bpButtonShowEqual ->IsShown() || - m_bpButtonShowConflict ->IsShown(); - m_bpButtonViewTypeSyncAction->Show(anyViewFilterButtonShown); - - if (anyViewFilterButtonShown) - { - m_panelViewFilter->Show(); - m_panelViewFilter->Layout(); - } - else - m_panelViewFilter->Hide(); - } - else - { - const GridView::StatusCmpResult result = gridDataView->updateCmpResult(currentCfg.hideExcludedItems, - m_bpButtonShowLeftOnly ->isActive(), - m_bpButtonShowRightOnly ->isActive(), - m_bpButtonShowLeftNewer ->isActive(), - m_bpButtonShowRightNewer->isActive(), - m_bpButtonShowDifferent ->isActive(), - m_bpButtonShowEqual ->isActive(), - m_bpButtonShowConflict ->isActive()); - filesOnLeftView = result.filesOnLeftView; - foldersOnLeftView = result.foldersOnLeftView; - filesOnRightView = result.filesOnRightView; - foldersOnRightView = result.foldersOnRightView; - filesizeLeftView = result.filesizeLeftView; - filesizeRightView = result.filesizeRightView; - - //comparison result view buttons - m_bpButtonShowLeftOnly ->Show(result.existsLeftOnly); - m_bpButtonShowRightOnly ->Show(result.existsRightOnly); - m_bpButtonShowLeftNewer ->Show(result.existsLeftNewer); - m_bpButtonShowRightNewer->Show(result.existsRightNewer); - m_bpButtonShowDifferent ->Show(result.existsDifferent); - m_bpButtonShowEqual ->Show(result.existsEqual); - m_bpButtonShowConflict ->Show(result.existsConflict); - - const bool anyViewFilterButtonShown = m_bpButtonShowLeftOnly ->IsShown() || - m_bpButtonShowRightOnly ->IsShown() || - m_bpButtonShowLeftNewer ->IsShown() || - m_bpButtonShowRightNewer->IsShown() || - m_bpButtonShowDifferent ->IsShown() || - m_bpButtonShowEqual ->IsShown() || - m_bpButtonShowConflict ->IsShown(); - m_bpButtonViewTypeSyncAction->Show(anyViewFilterButtonShown); - - if (anyViewFilterButtonShown) - { - m_panelViewFilter->Show(); - m_panelViewFilter->Layout(); - } - else - m_panelViewFilter->Hide(); - } - //all three grids retrieve their data directly via gridDataView - gridview::refresh(*m_gridMainL, *m_gridMainC, *m_gridMainR); - - //navigation tree - if (m_bpButtonViewTypeSyncAction->isActive()) - treeDataView->updateSyncPreview(currentCfg.hideExcludedItems, - m_bpButtonShowCreateLeft ->isActive(), - m_bpButtonShowCreateRight->isActive(), - m_bpButtonShowDeleteLeft ->isActive(), - m_bpButtonShowDeleteRight->isActive(), - m_bpButtonShowUpdateLeft ->isActive(), - m_bpButtonShowUpdateRight->isActive(), - m_bpButtonShowDoNothing ->isActive(), - m_bpButtonShowEqual ->isActive(), - m_bpButtonShowConflict ->isActive()); - else - treeDataView->updateCmpResult(currentCfg.hideExcludedItems, - m_bpButtonShowLeftOnly ->isActive(), - m_bpButtonShowRightOnly ->isActive(), - m_bpButtonShowLeftNewer ->isActive(), - m_bpButtonShowRightNewer->isActive(), - m_bpButtonShowDifferent ->isActive(), - m_bpButtonShowEqual ->isActive(), - m_bpButtonShowConflict ->isActive()); - m_gridNavi->Refresh(); - - //update status bar information - setStatusBarFileStatistics(filesOnLeftView, - foldersOnLeftView, - filesOnRightView, - foldersOnRightView, - filesizeLeftView, - filesizeRightView); -} - - -void MainDialog::applyFilterConfig() -{ - applyFiltering(folderCmp, getConfig().mainCfg); - updateGui(); - //updateGuiDelayedIf(currentCfg.hideExcludedItems); //show update GUI before removing rows -} - - -void MainDialog::applySyncConfig() -{ - zen::redetermineSyncDirection(getConfig().mainCfg, folderCmp, - [&](const std::wstring& warning) - { - bool& warningActive = globalCfg.optDialogs.warningDatabaseError; - if (warningActive) - { - bool dontWarnAgain = false; - - showNotificationDialog(this, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(warning).setCheckBox(dontWarnAgain, _("&Don't show this warning again"))); - warningActive = !dontWarnAgain; - } - }); - - updateGui(); -} - - -void MainDialog::OnMenuFindItem(wxCommandEvent& event) //CTRL + F -{ - showFindPanel(); -} - - -void MainDialog::OnSearchGridEnter(wxCommandEvent& event) -{ - startFindNext(); -} - - -void MainDialog::OnHideSearchPanel(wxCommandEvent& event) -{ - hideFindPanel(); -} - - -void MainDialog::OnSearchPanelKeyPressed(wxKeyEvent& event) -{ - switch (event.GetKeyCode()) - { - case WXK_RETURN: - case WXK_NUMPAD_ENTER: //catches ENTER keys while focus is on *any* part of m_panelSearch! Seems to obsolete OnHideSearchPanel()! - startFindNext(); - return; - case WXK_ESCAPE: - hideFindPanel(); - return; - } - event.Skip(); -} - - -void MainDialog::showFindPanel() //CTRL + F or F3 with empty search phrase -{ - auiMgr.GetPane(m_panelSearch).Show(); - auiMgr.Update(); - - 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) - m_textCtrlSearchTxt->SetFocus(); -} - - -void MainDialog::hideFindPanel() -{ - auiMgr.GetPane(m_panelSearch).Hide(); - auiMgr.Update(); - - if (focusWindowAfterSearch) - { - focusWindowAfterSearch->SetFocus(); - focusWindowAfterSearch = nullptr; - } -} - - -void MainDialog::startFindNext() //F3 or ENTER in m_textCtrlSearchTxt -{ - const wxString& searchString = m_textCtrlSearchTxt->GetValue(); - - if (searchString.empty()) - showFindPanel(); - else - { - Grid* grid1 = m_gridMainL; - Grid* grid2 = m_gridMainR; - - wxWindow* focus = wxWindow::FindFocus(); - if ((isComponentOf(focus, m_panelSearch) ? focusWindowAfterSearch : focus) == &m_gridMainR->getMainWin()) - std::swap(grid1, grid2); //select side to start search at grid cursor position - - wxBeginBusyCursor(wxHOURGLASS_CURSOR); - const std::pair result = findGridMatch(*grid1, *grid2, searchString, - m_checkBoxMatchCase->GetValue()); //parameter owned by GUI, *not* globalCfg structure! => we should better implement a getGlocalCfg()! - wxEndBusyCursor(); - - if (Grid* grid = const_cast(result.first)) //grid wasn't const when passing to findAndSelectNext(), so this is safe - { - assert(result.second >= 0); - - gridview::setScrollMaster(*grid); - grid->setGridCursor(result.second); - - focusWindowAfterSearch = &grid->getMainWin(); - - if (!isComponentOf(wxWindow::FindFocus(), m_panelSearch)) - grid->getMainWin().SetFocus(); - } - else - { - showFindPanel(); - showNotificationDialog(this, DialogInfoType::INFO, PopupDialogCfg(). - setTitle(_("Find")). - setMainInstructions(replaceCpy(_("Cannot find %x"), L"%x", L"\"" + searchString + L"\"", false))); - } - } -} - - -void MainDialog::OnAddFolderPair(wxCommandEvent& event) -{ -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! -#endif - - std::vector newPairs; - newPairs.push_back(getConfig().mainCfg.firstPair); - - //clear first pair - const FolderPairEnh cfgEmpty; - firstFolderPair->setValues(cfgEmpty.leftDirectory, - cfgEmpty.rightDirectory, - cfgEmpty.altCmpConfig, - cfgEmpty.altSyncConfig, - cfgEmpty.localFilter); - - //keep sequence to update GUI as last step - addAddFolderPair(newPairs, true); //add pair in front of additonal pairs -} - - -void MainDialog::OnRemoveTopFolderPair(wxCommandEvent& event) -{ - if (!additionalFolderPairs.empty()) - { -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! -#endif - - //get settings from second folder pair - const FolderPairEnh cfgSecond = getEnhancedPair(additionalFolderPairs[0]); - - //reset first pair - firstFolderPair->setValues(cfgSecond.leftDirectory, - cfgSecond.rightDirectory, - cfgSecond.altCmpConfig, - cfgSecond.altSyncConfig, - cfgSecond.localFilter); - - removeAddFolderPair(0); //remove second folder pair (first of additional folder pairs) - } -} - - -void MainDialog::OnRemoveFolderPair(wxCommandEvent& event) -{ -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! -#endif - - const wxObject* const eventObj = event.GetEventObject(); //find folder pair originating the event - for (auto it = additionalFolderPairs.begin(); it != additionalFolderPairs.end(); ++it) - if (eventObj == (*it)->m_bpButtonRemovePair) - { - removeAddFolderPair(it - additionalFolderPairs.begin()); - break; - } -} - - -void MainDialog::updateGuiForFolderPair() -{ -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! -#endif - - //adapt delete top folder pair button - m_bpButtonRemovePair->Show(!additionalFolderPairs.empty()); - m_panelTopLeft->Layout(); - - //adapt local filter and sync cfg for first folder pair - const bool showLocalCfgFirstPair = !additionalFolderPairs.empty() || - firstFolderPair->getAltCompConfig().get() != nullptr || - firstFolderPair->getAltSyncConfig().get() != nullptr || - !isNullFilter(firstFolderPair->getAltFilterConfig()); - - m_bpButtonAltCompCfg ->Show(showLocalCfgFirstPair); - m_bpButtonAltSyncCfg ->Show(showLocalCfgFirstPair); - m_bpButtonLocalFilter->Show(showLocalCfgFirstPair); - setImage(*m_bpButtonSwapSides, getResourceImage(showLocalCfgFirstPair ? L"swap_slim" : L"swap")); - - //update sub-panel sizes for calculations below!!! - m_panelTopMiddle->GetSizer()->SetSizeHints(m_panelTopMiddle); //~=Fit() + SetMinSize() - - int addPairMinimalHeight = 0; - int addPairOptimalHeight = 0; - if (!additionalFolderPairs.empty()) - { - const int pairHeight = additionalFolderPairs[0]->GetSize().GetHeight(); - addPairMinimalHeight = std::min(1.5, additionalFolderPairs.size()) * pairHeight; //have 1.5 * height indicate that more folders are there - addPairOptimalHeight = std::min(globalCfg.gui.maxFolderPairsVisible - 1 + 0.5, //subtract first/main folder pair and add 0.5 to indicate additional folders - additionalFolderPairs.size()) * pairHeight; - - addPairOptimalHeight = std::max(addPairOptimalHeight, addPairMinimalHeight); //implicitly handle corrupted values for "maxFolderPairsVisible" - } - - const int firstPairHeight = std::max(m_panelDirectoryPairs->ClientToWindowSize(m_panelTopLeft ->GetSize()).GetHeight(), //include m_panelDirectoryPairs window borders! - m_panelDirectoryPairs->ClientToWindowSize(m_panelTopMiddle->GetSize()).GetHeight()); // - - //######################################################################################################################## - //wxAUI hack: set minimum height to desired value, then call wxAuiPaneInfo::Fixed() to apply it - auiMgr.GetPane(m_panelDirectoryPairs).MinSize(-1, firstPairHeight + addPairOptimalHeight); - auiMgr.GetPane(m_panelDirectoryPairs).Fixed(); - auiMgr.Update(); - - //now make resizable again - auiMgr.GetPane(m_panelDirectoryPairs).Resizable(); - auiMgr.Update(); - //######################################################################################################################## - - //make sure user cannot fully shrink additional folder pairs - auiMgr.GetPane(m_panelDirectoryPairs).MinSize(-1, firstPairHeight + addPairMinimalHeight); - auiMgr.Update(); - - //it seems there is no GetSizer()->SetSizeHints(this)/Fit() required due to wxAui "magic" - //=> *massive* perf improvement on OS X! -} - - -void MainDialog::addAddFolderPair(const std::vector& newPairs, bool addFront) -{ -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(m_panelDirectoryPairs); //leads to GUI corruption problems on Linux/OS X! -#endif - - std::vector newEntries; - - std::for_each(newPairs.begin(), newPairs.end(), - [&](const FolderPairEnh& enhPair) - { - //add new folder pair - FolderPairPanel* newPair = new FolderPairPanel(m_scrolledWindowFolderPairs, *this); - - //init dropdown history - newPair->m_directoryLeft ->init(folderHistoryLeft); - newPair->m_directoryRight->init(folderHistoryRight); - - //set width of left folder panel - const int width = m_panelTopLeft->GetSize().GetWidth(); - newPair->m_panelLeft->SetMinSize(wxSize(width, -1)); - - if (addFront) - { - bSizerAddFolderPairs->Insert(0, newPair, 0, wxEXPAND); - additionalFolderPairs.insert(additionalFolderPairs.begin(), newPair); - } - else - { - bSizerAddFolderPairs->Add(newPair, 0, wxEXPAND); - additionalFolderPairs.push_back(newPair); - } - newEntries.push_back(newPair); - - //register events - newPair->m_bpButtonRemovePair->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MainDialog::OnRemoveFolderPair), nullptr, this); - }); - - updateGuiForFolderPair(); - - //wxComboBox screws up miserably if width/height is smaller than the magic number 4! Problem occurs when trying to set tooltip - //so we have to update window sizes before setting configuration: - for (auto it = newPairs.begin(); it != newPairs.end(); ++it)//set alternate configuration - newEntries[it - newPairs.begin()]->setValues(it->leftDirectory, - it->rightDirectory, - it->altCmpConfig, - it->altSyncConfig, - it->localFilter); - clearGrid(); //+ GUI update -} - - -void MainDialog::removeAddFolderPair(size_t pos) -{ -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(m_panelDirectoryPairs); //leads to GUI corruption problems on Linux/OS X! -#endif - - if (pos < additionalFolderPairs.size()) - { - FolderPairPanel* panel = additionalFolderPairs[pos]; - - bSizerAddFolderPairs->Detach(panel); //Remove() does not work on Window*, so do it manually - additionalFolderPairs.erase(additionalFolderPairs.begin() + pos); - //more (non-portable) wxWidgets bullshit: on OS X wxWindow::Destroy() screws up and calls "operator delete" directly rather than - //the deferred deletion it is expected to do (and which is implemented correctly on Windows and Linux) - //http://bb10.com/python-wxpython-devel/2012-09/msg00004.html - //=> since we're in a mouse button callback of a sub-component of "panel" we need to delay deletion ourselves: - processAsync2([] {}, [panel] { panel->Destroy(); }); - } - - updateGuiForFolderPair(); - - clearGrid(); //+ GUI update -} - - -void MainDialog::setAddFolderPairs(const std::vector& newPairs) -{ -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(m_panelDirectoryPairs); //leads to GUI corruption problems on Linux/OS X! -#endif - - additionalFolderPairs.clear(); - bSizerAddFolderPairs->Clear(true); - - //updateGuiForFolderPair(); -> already called in addAddFolderPair() - - addAddFolderPair(newPairs); -} - - -//######################################################################################################## - - -//menu events -void MainDialog::OnMenuGlobalSettings(wxCommandEvent& event) -{ - zen::showGlobalSettingsDlg(this, globalCfg); -} - - -void MainDialog::OnMenuExportFileList(wxCommandEvent& event) -{ - //get a filename - wxFileDialog filePicker(this, //creating this on freestore leads to memleak! - wxEmptyString, - wxEmptyString, - L"FileList.csv", //default file name - _("Comma-separated values") + L" (*.csv)|*.csv" + L"|" +_("All files") + L" (*.*)|*", - wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - if (filePicker.ShowModal() != wxID_OK) - return; - - wxBusyCursor dummy; - - const Zstring filename = utfCvrtTo(filePicker.GetPath()); - - //http://en.wikipedia.org/wiki/Comma-separated_values - const lconv* localInfo = ::localeconv(); //always bound according to doc - const bool haveCommaAsDecimalSep = std::string(localInfo->decimal_point) == ","; - - const char CSV_SEP = haveCommaAsDecimalSep ? ';' : ','; - - auto fmtValue = [&](const wxString& val) -> Utf8String - { - Utf8String&& tmp = utfCvrtTo(val); - - if (contains(tmp, CSV_SEP)) - return '\"' + tmp + '\"'; - else - return tmp; - }; - - Utf8String header; //perf: wxString doesn't model exponential growth and so is out, std::string doesn't give performance guarantee! - header += BYTE_ORDER_MARK_UTF8; - - //base folders - header += fmtValue(_("Folder Pairs")) + '\n' ; - std::for_each(begin(folderCmp), end(folderCmp), - [&](BaseDirPair& baseDirObj) - { - header += utfCvrtTo(baseDirObj.getBaseDirPf()) + CSV_SEP; - header += utfCvrtTo(baseDirObj.getBaseDirPf()) + '\n'; - }); - header += '\n'; - - //write header - auto provLeft = m_gridMainL->getDataProvider(); - auto provMiddle = m_gridMainC->getDataProvider(); - auto provRight = m_gridMainR->getDataProvider(); - - auto colAttrLeft = m_gridMainL->getColumnConfig(); - auto colAttrMiddle = m_gridMainC->getColumnConfig(); - auto colAttrRight = m_gridMainR->getColumnConfig(); - - vector_remove_if(colAttrLeft , [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); - vector_remove_if(colAttrMiddle, [](const Grid::ColumnAttribute& ca) { return !ca.visible_ || static_cast(ca.type_) == COL_TYPE_CHECKBOX; }); - vector_remove_if(colAttrRight , [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); - - if (provLeft && provMiddle && provRight) - { - std::for_each(colAttrLeft.begin(), colAttrLeft.end(), - [&](const Grid::ColumnAttribute& ca) - { - header += fmtValue(provLeft->getColumnLabel(ca.type_)); - header += CSV_SEP; - }); - std::for_each(colAttrMiddle.begin(), colAttrMiddle.end(), - [&](const Grid::ColumnAttribute& ca) - { - header += fmtValue(provMiddle->getColumnLabel(ca.type_)); - header += CSV_SEP; - }); - if (!colAttrRight.empty()) - { - std::for_each(colAttrRight.begin(), colAttrRight.end() - 1, - [&](const Grid::ColumnAttribute& ca) - { - header += fmtValue(provRight->getColumnLabel(ca.type_)); - header += CSV_SEP; - }); - header += fmtValue(provRight->getColumnLabel(colAttrRight.back().type_)); - } - header += '\n'; - - try - { - //write file - FileOutput fileOut(filename, zen::FileOutput::ACC_OVERWRITE); //throw FileError - - replace(header, '\n', LINE_BREAK); - fileOut.write(&*header.begin(), header.size()); //throw FileError - - //main grid: write rows one after the other instead of creating one big string: memory allocation might fail; think 1 million rows! - /* - performance test case "export 600.000 rows" to CSV: - aproach 1. assemble single temporary string, then write file: 4.6s - aproach 2. write to buffered file output directly for each row: 6.4s - */ - const size_t rowCount = m_gridMainL->getRowCount(); - for (size_t row = 0; row < rowCount; ++row) - { - Utf8String tmp; - - std::for_each(colAttrLeft.begin(), colAttrLeft.end(), - [&](const Grid::ColumnAttribute& ca) - { - tmp += fmtValue(provLeft->getValue(row, ca.type_)); - tmp += CSV_SEP; - }); - std::for_each(colAttrMiddle.begin(), colAttrMiddle.end(), - [&](const Grid::ColumnAttribute& ca) - { - tmp += fmtValue(provMiddle->getValue(row, ca.type_)); - tmp += CSV_SEP; - }); - std::for_each(colAttrRight.begin(), colAttrRight.end(), - [&](const Grid::ColumnAttribute& ca) - { - tmp += fmtValue(provRight->getValue(row, ca.type_)); - tmp += CSV_SEP; - }); - tmp += '\n'; - - replace(tmp, '\n', LINE_BREAK); - fileOut.write(&*tmp.begin(), tmp.size()); //throw FileError - } - - flashStatusInformation(_("File list exported")); - } - catch (const FileError& e) - { - showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); - } - } -} - - -void MainDialog::OnMenuCheckVersion(wxCommandEvent& event) -{ - zen::checkForUpdateNow(this); -} - - -void MainDialog::OnMenuCheckVersionAutomatically(wxCommandEvent& event) -{ - globalCfg.gui.lastUpdateCheck = globalCfg.gui.lastUpdateCheck == -1 ? 0 : -1; - m_menuItemCheckVersionAuto->Check(globalCfg.gui.lastUpdateCheck != -1); - zen::checkForUpdatePeriodically(this, globalCfg.gui.lastUpdateCheck, [&] { flashStatusInformation(_("Searching for program updates...")); }); -} - - -void MainDialog::OnRegularUpdateCheck(wxIdleEvent& event) -{ - //execute just once per startup! - Disconnect(wxEVT_IDLE, wxIdleEventHandler(MainDialog::OnRegularUpdateCheck), nullptr, this); - - if (manualProgramUpdateRequired()) - zen::checkForUpdatePeriodically(this, globalCfg.gui.lastUpdateCheck, [&] { flashStatusInformation(_("Searching for program updates...")); }); -} - - -void MainDialog::OnLayoutWindowAsync(wxIdleEvent& event) -{ - //execute just once per startup! - Disconnect(wxEVT_IDLE, wxIdleEventHandler(MainDialog::OnLayoutWindowAsync), nullptr, this); - -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! -#endif - - //adjust folder pair distortion on startup - std::for_each(additionalFolderPairs.begin(), additionalFolderPairs.end(), - [](FolderPairPanel* panel) { panel->Layout(); }); - - m_panelTopButtons->Layout(); - Layout(); //strangely this layout call works if called in next idle event only - auiMgr.Update(); //fix view filter distortion -} - - -void MainDialog::OnMenuAbout(wxCommandEvent& event) -{ - zen::showAboutDialog(this); -} - - -void MainDialog::OnShowHelp(wxCommandEvent& event) -{ - zen::displayHelpEntry(this); -} - -//######################################################################################################### - -//language selection -void MainDialog::switchProgramLanguage(int langID) -{ - //create new dialog with respect to new language - xmlAccess::XmlGlobalSettings newGlobalCfg = getGlobalCfgBeforeExit(); - newGlobalCfg.programLanguage = langID; - - //show new dialog, then delete old one - MainDialog::create(getConfig(), activeConfigFiles, &newGlobalCfg, false); - - //we don't use Close(): - //1. we don't want to show the prompt to save current config in OnClose() - //2. after getGlobalCfgBeforeExit() the old main dialog is invalid so we want to force deletion - Destroy(); -} - - -void MainDialog::OnMenuLanguageSwitch(wxCommandEvent& event) -{ - std::map::const_iterator it = languageMenuItemMap.find(event.GetId()); - if (it != languageMenuItemMap.end()) - switchProgramLanguage(it->second); -} - -//######################################################################################################### - -void MainDialog::setViewTypeSyncAction(bool value) -{ - //if (m_bpButtonViewTypeSyncAction->isActive() == value) return; support polling -> what about initialization? - - m_bpButtonViewTypeSyncAction->setActive(value); - m_bpButtonViewTypeSyncAction->SetToolTip((value ? _("Action") : _("Category")) + L" (F9)"); - - //toggle display of sync preview in middle grid - gridview::highlightSyncAction(*m_gridMainC, value); - - updateGui(); -} diff --git a/ui/main_dlg.h b/ui/main_dlg.h deleted file mode 100644 index 110f42b6..00000000 --- a/ui/main_dlg.h +++ /dev/null @@ -1,314 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef MAINDIALOG_H -#define MAINDIALOG_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include "gui_generated.h" -#include "custom_grid.h" -#include "tree_view.h" -#include "folder_history_box.h" -#include "../lib/process_xml.h" - -class FolderPairFirst; -class FolderPairPanel; -class CompareProgressDialog; - - -class MainDialog : public MainDialogGenerated -{ -public: - //default behavior, application start, restores last used config - static void create(); - - //when loading dynamically assembled config, - //when switching language, - //or switching from batch run to GUI on warnings - static void create(const xmlAccess::XmlGuiConfig& guiCfg, - const std::vector& referenceFiles, - const xmlAccess::XmlGlobalSettings* globalSettings, //optional: take over ownership => save on exit - bool startComparison); - - void disableAllElements(bool enableAbort); //dis-/enables all elements (except abort button) that might receive user input - void enableAllElements(); //during long-running processes: comparison, deletion - - void onQueryEndSession(); //last chance to do something useful before killing the application! - -private: - MainDialog(const xmlAccess::XmlGuiConfig& guiCfg, - const std::vector& referenceFiles, - const xmlAccess::XmlGlobalSettings& globalSettings, //take over ownership => save on exit - bool startComparison); - ~MainDialog(); - - friend class CompareStatusHandler; - friend class SyncStatusHandler; - friend class ManualDeletionHandler; - friend class FolderPairFirst; - friend class FolderPairPanel; - friend class DirectoryNameMainImpl; - template - friend class FolderPairCallback; - friend class PanelMoveWindow; - - //configuration load/save - void setLastUsedConfig(const Zstring& filename, const xmlAccess::XmlGuiConfig& guiConfig); - void setLastUsedConfig(const std::vector& filenames, const xmlAccess::XmlGuiConfig& guiConfig); - - xmlAccess::XmlGuiConfig getConfig() const; - void setConfig(const xmlAccess::XmlGuiConfig& newGuiCfg, const std::vector& referenceFiles); - - void setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSettings); //messes with Maximize(), window sizes, so call just once! - xmlAccess::XmlGlobalSettings getGlobalCfgBeforeExit(); //destructive "get" thanks to "Iconize(false), Maximize(false)" - - bool loadConfiguration(const std::vector& filenames); //return true if loaded successfully - - bool trySaveConfig (const Zstring* fileNameGui); //return true if saved successfully - bool trySaveBatchConfig(const Zstring* fileNameBatch); // - bool saveOldConfig(); //return false on user abort - - static const Zstring& lastRunConfigName(); - - xmlAccess::XmlGuiConfig lastConfigurationSaved; //support for: "Save changed configuration?" dialog - //used when saving configuration - std::vector activeConfigFiles; //name of currently loaded config file (may be more than 1) - - void updateGlobalFilterButton(); - - void initViewFilterButtons(); - void setViewFilterDefault(); - - void addFileToCfgHistory(const std::vector& filenames); //= update/insert + apply selection - void removeObsoleteCfgHistoryItems(const std::vector& filenames); - void removeCfgHistoryItems(const std::vector& filenames); - - void addAddFolderPair(const std::vector& newPairs, bool addFront = false); - void removeAddFolderPair(size_t pos); - void setAddFolderPairs(const std::vector& newPairs); - - void updateGuiForFolderPair(); //helper method: add usability by showing/hiding buttons related to folder pairs - - //main method for putting gridDataView on UI: updates data respecting current view settings - void updateGui(); //kitchen-sink update - void updateGuiDelayedIf(bool condition); // 400 ms delay - - void updateGridViewData(); // - void updateStatistics(); // more fine-grained updaters - void updateUnsavedCfgStatus(); // - void updateTopButtonImages(); // - - //context menu functions - std::vector getGridSelection(bool fromLeft = true, bool fromRight = true) const; - std::vector getTreeSelection() const; - - void setSyncDirManually(const std::vector& selection, zen::SyncDirection direction); - void setFilterManually(const std::vector& selection, bool setIncluded); - void copySelectionToClipboard(const std::vector& gridRefs); - void deleteSelectedFiles(const std::vector& selectionLeft, - const std::vector& selectionRight); - - void openExternalApplication(const wxString& commandline, const std::vector& selection, bool leftSide); //selection may be empty - - //don't use wxWidgets idle handling => repeated idle requests/consumption hogs 100% cpu! - void onProcessAsyncTasks(wxEvent& event); - - template - void processAsync(Fun doAsync, Fun2 evalOnGui) { asyncTasks.add(doAsync, evalOnGui); timerForAsyncTasks.Start(50); /*timer interval in [ms] */ } - template - void processAsync2(Fun doAsync, Fun2 evalOnGui) { asyncTasks.add2(doAsync, evalOnGui); timerForAsyncTasks.Start(50); /*timer interval in [ms] */ } - - //status bar supports one of the following two states at a time: - void setStatusBarFullText(const wxString& msg); - void setStatusBarFileStatistics(size_t filesOnLeftView, size_t foldersOnLeftView, size_t filesOnRightView, size_t foldersOnRightView, zen::UInt64 filesizeLeftView, zen::UInt64 filesizeRightView); - - void flashStatusInformation(const wxString& msg); //temporarily show different status (only valid for setStatusBarFullText) - void restoreStatusInformation(); //called automatically after a few seconds - - //events - void onGridButtonEventL(wxKeyEvent& event); - void onGridButtonEventC(wxKeyEvent& event); - void onGridButtonEventR(wxKeyEvent& event); - void onGridButtonEvent (wxKeyEvent& event, zen::Grid& grid, bool leftSide); - - void onTreeButtonEvent (wxKeyEvent& event); - void OnContextSetLayout(wxMouseEvent& event); - void OnGlobalKeyEvent (wxKeyEvent& event); - - virtual void OnCompSettingsContext(wxMouseEvent& event) override; - virtual void OnSyncSettingsContext(wxMouseEvent& event) override; - virtual void OnGlobalFilterContext(wxMouseEvent& event) override; - virtual void OnViewButtonRightClick(wxMouseEvent& event) override; - - void applyCompareConfig(bool switchMiddleGrid = false); - - //context menu handler methods - void onMainGridContextL(zen::GridClickEvent& event); - void onMainGridContextC(zen::GridClickEvent& event); - void onMainGridContextR(zen::GridClickEvent& event); - void onMainGridContextRim(bool leftSide); - - void onNaviGridContext(zen::GridClickEvent& event); - - void onNaviSelection(zen::GridRangeSelectEvent& event); - - void onNaviPanelFilesDropped(zen::FileDropEvent& event); - - void onDirSelected(wxCommandEvent& event); - void onDirManualCorrection(wxCommandEvent& event); - - void onCheckRows (zen::CheckRowsEvent& event); - void onSetSyncDirection(zen::SyncDirectionEvent& event); - - void onGridDoubleClickL(zen::GridClickEvent& event); - void onGridDoubleClickR(zen::GridClickEvent& event); - void onGridDoubleClickRim(size_t row, bool leftSide); - - void onGridLabelLeftClickC(zen::GridClickEvent& event); - void onGridLabelLeftClickL(zen::GridClickEvent& event); - void onGridLabelLeftClickR(zen::GridClickEvent& event); - void onGridLabelLeftClick(bool onLeft, zen::ColumnTypeRim type); - - void onGridLabelContextL(zen::GridClickEvent& event); - void onGridLabelContextC(zen::GridClickEvent& event); - void onGridLabelContextR(zen::GridClickEvent& event); - void onGridLabelContext(zen::Grid& grid, zen::ColumnTypeRim type, const std::vector& defaultColumnAttributes); - - virtual void OnToggleViewType (wxCommandEvent& event) override; - virtual void OnToggleViewButton(wxCommandEvent& event) override; - - virtual void OnConfigNew (wxCommandEvent& event) override; - virtual void OnConfigSave (wxCommandEvent& event) override; - virtual void OnConfigSaveAs (wxCommandEvent& event) override; - virtual void OnSaveAsBatchJob (wxCommandEvent& event) override; - virtual void OnConfigLoad (wxCommandEvent& event) override; - virtual void OnLoadFromHistory(wxCommandEvent& event) override; - virtual void OnLoadFromHistoryDoubleClick(wxCommandEvent& event); - - void deleteSelectedCfgHistoryItems(); - - virtual void OnCfgHistoryRightClick(wxMouseEvent& event) override; - void OnCfgHistoryKeyEvent (wxKeyEvent& event) override; - void OnRegularUpdateCheck (wxIdleEvent& event); - void OnLayoutWindowAsync (wxIdleEvent& event); - - void OnResizeLeftFolderWidth(wxEvent& event); - void OnResizeConfigPanel (wxEvent& event); - void OnResizeViewPanel (wxEvent& event); - void OnResizeStatisticsPanel(wxEvent& event); - virtual void OnShowExcluded (wxCommandEvent& event) override; - virtual void OnConfigureFilter (wxCommandEvent& event) override; - virtual void OnSwapSides (wxCommandEvent& event) override; - virtual void OnCompare (wxCommandEvent& event) override; - virtual void OnSyncSettings (wxCommandEvent& event) override; - virtual void OnCmpSettings (wxCommandEvent& event) override; - virtual void OnStartSync (wxCommandEvent& event) override; - virtual void OnClose (wxCloseEvent& event) override; - - void filterExtension(const Zstring& extension, bool include); - void filterShortname(const zen::FileSystemObject& fsObj, bool include); - void filterItems(const std::vector& selection, bool include); - void filterPhrase(const Zstring& phrase, bool include, bool addNewLine); - - virtual void OnAddFolderPair (wxCommandEvent& event) override; - void OnRemoveFolderPair (wxCommandEvent& event); - virtual void OnRemoveTopFolderPair(wxCommandEvent& event) override; - - void applyFilterConfig(); - void applySyncConfig(); - - void showFindPanel(); //CTRL + F - void hideFindPanel(); - void startFindNext(); //F3 - - virtual void OnSearchGridEnter(wxCommandEvent& event) override; - virtual void OnHideSearchPanel(wxCommandEvent& event) override; - void OnSearchPanelKeyPressed(wxKeyEvent& event); - - //menu events - virtual void OnMenuGlobalSettings(wxCommandEvent& event) override; - virtual void OnMenuExportFileList(wxCommandEvent& event) override; - virtual void OnMenuFindItem (wxCommandEvent& event) override; - virtual void OnMenuCheckVersion (wxCommandEvent& event) override; - virtual void OnMenuCheckVersionAutomatically(wxCommandEvent& event) override; - virtual void OnMenuAbout (wxCommandEvent& event) override; - virtual void OnShowHelp (wxCommandEvent& event) override; - virtual void OnMenuQuit (wxCommandEvent& event) override { Close(); } - - void OnMenuLanguageSwitch(wxCommandEvent& event); - - void switchProgramLanguage(int langID); - - typedef int MenuItemID; - typedef int LanguageID; - std::map languageMenuItemMap; //needed to attach menu item events - - //*********************************************** - //application variables are stored here: - - //global settings shared by GUI and batch mode - xmlAccess::XmlGlobalSettings globalCfg; - - //UI view of FolderComparison structure (partially owns folderCmp) - std::shared_ptr gridDataView; //always bound! - std::shared_ptr treeDataView; // - - //the prime data structure of this tool *bling*: - zen::FolderComparison folderCmp; //optional!: sync button not available if empty - - void clearGrid(); - - //------------------------------------- - //program configuration - xmlAccess::XmlGuiConfig currentCfg; - - //folder pairs: - std::unique_ptr firstFolderPair; //always bound!!! - std::vector additionalFolderPairs; //additional pairs to the first pair - //------------------------------------- - - //*********************************************** - //status information - std::list oldStatusMsgs; //the first one is the original/non-flash status message - - //compare status panel (hidden on start, shown when comparing) - std::unique_ptr compareStatus; //always bound - - bool cleanedUp; - - bool processingGlobalKeyEvent; //indicator to notify recursion in OnGlobalKeyEvent() - - //toggle to display configuration preview instead of comparison result: - //for read access use: m_bpButtonViewTypeSyncAction->isActive() - //when changing value use: - void setViewTypeSyncAction(bool value); - - wxAuiManager auiMgr; //implement dockable GUI design - - wxString defaultPerspective; - - zen::Int64 manualTimeSpanFrom, manualTimeSpanTo; //buffer manual time span selection at session level - - std::shared_ptr folderHistoryLeft; //shared by all wxComboBox dropdown controls - std::shared_ptr folderHistoryRight; //always bound! - - //schedule and run long-running tasks asynchronously, but process results on GUI queue - zen::AsyncTasks asyncTasks; - wxTimer timerForAsyncTasks; //don't use wxWidgets idle handling => repeated idle requests/consumption hogs 100% cpu! - - std::unique_ptr filterCfgOnClipboard; //copy/paste of filter config - - wxWindow* focusWindowAfterSearch; //used to restore focus after search panel is closed -}; - -#endif // MAINDIALOG_H diff --git a/ui/osx_dock.h b/ui/osx_dock.h deleted file mode 100644 index 62237524..00000000 --- a/ui/osx_dock.h +++ /dev/null @@ -1,17 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef OSX_DOCK_837210847312534 -#define OSX_DOCK_837210847312534 - -#include - -namespace osx -{ -void dockIconSetText(const char* str); //throw SysError -} - -#endif //OSX_DOCK_837210847312534 diff --git a/ui/osx_dock.mm b/ui/osx_dock.mm deleted file mode 100644 index 8f42ae88..00000000 --- a/ui/osx_dock.mm +++ /dev/null @@ -1,24 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "osx_dock.h" -#include -#include - - -void osx::dockIconSetText(const char* str) //throw SysError -{ - @try - { - NSString* label = [NSString stringWithCString:str encoding:NSUTF8StringEncoding]; - //stringWithCString returns string which is already set to autorelease! - [[NSApp dockTile] setBadgeLabel:label]; //label may be nil - } - @catch (NSException* e) - { - throwSysError(e); //throw SysError - } -} diff --git a/ui/progress_indicator.cpp b/ui/progress_indicator.cpp deleted file mode 100644 index efbb62f0..00000000 --- a/ui/progress_indicator.cpp +++ /dev/null @@ -1,2004 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "progress_indicator.h" -#include -#include -#include -#include -#include -#include -#include -#include //wxTextDataObject -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "gui_generated.h" -#include "../lib/ffs_paths.h" -#include "../lib/perf_check.h" -#include "tray_icon.h" -#include "taskbar.h" -#include "exec_finished_box.h" -#include "app_icon.h" -#ifdef ZEN_MAC -#include -#endif - -using namespace zen; - - -namespace -{ -const int GAUGE_FULL_RANGE = 50000; - -//window size used for statistics in milliseconds -const int WINDOW_REMAINING_TIME = 60000; //some scenarios have dropouts of 40 seconds -> 60 sec. window size handles them well -const int WINDOW_BYTES_PER_SEC = 5000; // -} - - -class CompareProgressDialog::Pimpl : public CompareProgressDlgGenerated -{ -public: - Pimpl(wxFrame& parentWindow); - - void init(const Statistics& syncStat); //constructor/destructor semantics, but underlying Window is reused - void finalize(); // - - void switchToCompareBytewise(); - void updateStatusPanelNow(); - -private: - wxFrame& parentWindow_; - wxString titleTextBackup; - - wxStopWatch timeElapsed; - long binCompStartMs; //begin of binary comparison phase in [ms] - - const Statistics* syncStat_; //only bound while sync is running - - std::unique_ptr taskbar_; - std::unique_ptr perf; //estimate remaining time - - long lastStatCallSpeed; //used for calculating intervals between showing and collecting perf samples -}; - - -CompareProgressDialog::Pimpl::Pimpl(wxFrame& parentWindow) : - CompareProgressDlgGenerated(&parentWindow), - parentWindow_(parentWindow), - binCompStartMs(0), - syncStat_(nullptr), - lastStatCallSpeed(-1000000) //some big number -{ - //init(); -> needed? -} - - -void CompareProgressDialog::Pimpl::init(const Statistics& syncStat) -{ - syncStat_ = &syncStat; - titleTextBackup = parentWindow_.GetTitle(); - - try //try to get access to Windows 7/Ubuntu taskbar - { - taskbar_ = make_unique(parentWindow_); - } - catch (const TaskbarNotAvailable&) {} - - //initialize gauge - m_gauge2->SetRange(GAUGE_FULL_RANGE); - m_gauge2->SetValue(0); - - perf.reset(); - timeElapsed.Start(); //measure total time - - //initially hide status that's relevant for comparing bytewise only - bSizerFilesFound ->Show(true); - bSizerFilesRemaining->Show(false); - sSizerSpeed ->Show(false); - sSizerTimeRemaining ->Show(false); - - updateStatusPanelNow(); - - m_gauge2->Hide(); - bSizer42->Layout(); - Layout(); -} - - -void CompareProgressDialog::Pimpl::finalize() -{ - syncStat_ = nullptr; - parentWindow_.SetTitle(titleTextBackup); - taskbar_.reset(); -} - - -void CompareProgressDialog::Pimpl::switchToCompareBytewise() -{ - //start to measure perf - perf = make_unique(WINDOW_REMAINING_TIME, WINDOW_BYTES_PER_SEC); - lastStatCallSpeed = -1000000; //some big number - - binCompStartMs = timeElapsed.Time(); - - //show status for comparing bytewise - bSizerFilesFound ->Show(false); - bSizerFilesRemaining->Show(true); - sSizerSpeed ->Show(true); - sSizerTimeRemaining ->Show(true); - - m_gauge2->Show(); - bSizer42->Layout(); - Layout(); -} - - -void CompareProgressDialog::Pimpl::updateStatusPanelNow() -{ - if (!syncStat_) //no comparison running!! - return; - - const wxString& scannedObjects = toGuiString(syncStat_->getObjectsCurrent(ProcessCallback::PHASE_SCANNING)); - - auto setTitle = [&](const wxString& title) - { - if (parentWindow_.GetTitle() != title) - parentWindow_.SetTitle(title); - }; - - bool layoutChanged = false; //avoid screen flicker by calling layout() only if necessary - const long timeNow = timeElapsed.Time(); - - //status texts - setText(*m_textCtrlStatus, replaceCpy(syncStat_->currentStatusText(), L'\n', L' ')); //no layout update for status texts! - - //write status information to taskbar, parent title ect. - switch (syncStat_->currentPhase()) - { - case ProcessCallback::PHASE_NONE: - case ProcessCallback::PHASE_SCANNING: - //dialog caption, taskbar - setTitle(scannedObjects + L" - " + _("Scanning...")); - if (taskbar_.get()) //support Windows 7 taskbar - taskbar_->setStatus(Taskbar::STATUS_INDETERMINATE); - break; - - case ProcessCallback::PHASE_COMPARING_CONTENT: - { - auto objectsCurrent = syncStat_->getObjectsCurrent(ProcessCallback::PHASE_COMPARING_CONTENT); - auto objectsTotal = syncStat_->getObjectsTotal (ProcessCallback::PHASE_COMPARING_CONTENT); - auto dataCurrent = syncStat_->getDataCurrent (ProcessCallback::PHASE_COMPARING_CONTENT); - auto dataTotal = syncStat_->getDataTotal (ProcessCallback::PHASE_COMPARING_CONTENT); - - //add both data + obj-count, to handle "deletion-only" cases - const double fraction = dataTotal + objectsTotal == 0 ? 0 : std::max(0.0, to(dataCurrent + objectsCurrent) / to(dataTotal + objectsTotal)); - - //dialog caption, taskbar - setTitle(fractionToString(fraction) + wxT(" - ") + _("Comparing content...")); - if (taskbar_.get()) - { - taskbar_->setProgress(fraction); - taskbar_->setStatus(Taskbar::STATUS_NORMAL); - } - - //progress indicator, shown for binary comparison only - m_gauge2->SetValue(numeric::round(fraction * GAUGE_FULL_RANGE)); - - //remaining objects and bytes for file comparison - setText(*m_staticTextFilesRemaining, toGuiString(objectsTotal - objectsCurrent), &layoutChanged); - setText(*m_staticTextDataRemaining, L"(" + filesizeToShortString(dataTotal - dataCurrent) + L")", &layoutChanged); - - //remaining time and speed: only visible during binary comparison - assert(perf); - if (perf) - if (numeric::dist(lastStatCallSpeed, timeNow) >= 500) - { - lastStatCallSpeed = timeNow; - - if (numeric::dist(binCompStartMs, timeNow) >= 1000) //discard stats for first second: probably messy - perf->addSample(objectsCurrent, to(dataCurrent), timeNow); - - //current speed -> Win 7 copy uses 1 sec update interval instead - Opt bps = perf->getBytesPerSecond(); - setText(*m_staticTextSpeed, bps ? *bps : L"-", &layoutChanged); - - //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 rt = perf->getRemainingTime(to(dataTotal - dataCurrent)); - setText(*m_staticTextRemTime, rt ? *rt : L"-", &layoutChanged); - } - } - break; - - case ProcessCallback::PHASE_SYNCHRONIZING: - assert(false); - break; - } - - //nr of scanned objects - setText(*m_staticTextScanned, scannedObjects, &layoutChanged); - - //time elapsed - const long timeElapSec = timeNow / 1000; - setText(*m_staticTextTimeElapsed, - timeElapSec < 3600 ? - wxTimeSpan::Seconds(timeElapSec).Format( L"%M:%S") : - wxTimeSpan::Seconds(timeElapSec).Format(L"%H:%M:%S"), &layoutChanged); - - if (layoutChanged) - bSizer42->Layout(); - - //do the ui update - wxTheApp->Yield(); -} - -//######################################################################################## - -//redirect to implementation -CompareProgressDialog::CompareProgressDialog(wxFrame& parentWindow) : - pimpl(new Pimpl(parentWindow)) {} //owned by parentWindow - -wxWindow* CompareProgressDialog::getAsWindow() -{ - return pimpl; -} - -void CompareProgressDialog::init(const Statistics& syncStat) -{ - pimpl->init(syncStat); -} - -void CompareProgressDialog::finalize() -{ - pimpl->finalize(); -} - -void CompareProgressDialog::switchToCompareBytewise() -{ - pimpl->switchToCompareBytewise(); -} - -void CompareProgressDialog::updateStatusPanelNow() -{ - pimpl->updateStatusPanelNow(); -} -//######################################################################################## - -namespace -{ -inline -wxBitmap buttonPressed(const std::string& name) -{ - wxBitmap background = getResourceImage(L"log button pressed"); - return layOver(getResourceImage(utfCvrtTo(name)), background); -} - - -inline -wxBitmap buttonReleased(const std::string& name) -{ - wxImage output = greyScale(getResourceImage(utfCvrtTo(name))).ConvertToImage(); - //getResourceImage(utfCvrtTo(name)).ConvertToImage().ConvertToGreyscale(1.0/3, 1.0/3, 1.0/3); //treat all channels equally! - //brighten(output, 30); - - //zen::moveImage(output, 1, 0); //move image right one pixel - return output; -} - - -//a vector-view on ErrorLog considering multi-line messages: prepare consumption by Grid -class MessageView -{ -public: - MessageView(const ErrorLog& log) : log_(log) {} - - size_t rowsOnView() const { return viewRef.size(); } - - struct LogEntryView - { - time_t time; - MessageType type; - MsgString messageLine; - bool firstLine; //if LogEntry::message spans multiple rows - }; - bool getEntry(size_t row, LogEntryView& out) const - { - if (row < viewRef.size()) - { - const Line& line = viewRef[row]; - out.time = line.entry_->time; - out.type = line.entry_->type; - out.messageLine = extractLine(line.entry_->message, line.rowNumber_); - out.firstLine = line.rowNumber_ == 0; //this is virtually always correct, unless first line of the original message is empty! - return true; - } - return false; - } - - void updateView(int includedTypes) //TYPE_INFO | TYPE_WARNING, ect. see error_log.h - { - viewRef.clear(); - - for (auto it = log_.begin(); it != log_.end(); ++it) - if (it->type & includedTypes) - { - assert_static((IsSameType::Type, wchar_t>::value)); - assert(!startsWith(it->message, L'\n')); - - size_t rowNumber = 0; - bool lastCharNewline = true; - for (const wchar_t c : it->message) - if (c == L'\n') - { - if (!lastCharNewline) //do not reference empty lines! - viewRef.push_back(Line(&*it, rowNumber)); - ++rowNumber; - lastCharNewline = true; - } - else - lastCharNewline = false; - - if (!lastCharNewline) - viewRef.push_back(Line(&*it, rowNumber)); - } - } - -private: - static MsgString extractLine(const MsgString& message, size_t textRow) - { - auto iter1 = message.begin(); - for (;;) - { - auto iter2 = std::find_if(iter1, message.end(), [](wchar_t c) { return c == L'\n'; }); - if (textRow == 0) - return iter1 == message.end() ? MsgString() : MsgString(&*iter1, iter2 - iter1); //must not dereference iterator pointing to "end"! - - if (iter2 == message.end()) - { - assert(false); - return MsgString(); - } - - iter1 = iter2 + 1; //skip newline - --textRow; - } - } - - struct Line - { - Line(const LogEntry* entry, size_t rowNumber) : entry_(entry), rowNumber_(rowNumber) {} - const LogEntry* entry_; //always bound! - size_t rowNumber_; //LogEntry::message may span multiple rows - }; - - std::vector viewRef; //partial view on log_ - /* /|\ - | updateView() - | */ - const ErrorLog log_; -}; - -//----------------------------------------------------------------------------- - -enum ColumnTypeMsg -{ - COL_TYPE_MSG_TIME, - COL_TYPE_MSG_CATEGORY, - COL_TYPE_MSG_TEXT, -}; - -//Grid data implementation referencing MessageView -class GridDataMessages : public GridData -{ -public: - GridDataMessages(const std::shared_ptr& msgView) : msgView_(msgView) {} - - virtual size_t getRowCount() const { return msgView_ ? msgView_->rowsOnView() : 0; } - - virtual wxString getValue(size_t row, ColumnType colType) const - { - MessageView::LogEntryView entry = {}; - if (msgView_ && msgView_->getEntry(row, entry)) - switch (static_cast(colType)) - { - case COL_TYPE_MSG_TIME: - if (entry.firstLine) - return formatTime(FORMAT_TIME, localTime(entry.time)); - break; - - case COL_TYPE_MSG_CATEGORY: - if (entry.firstLine) - switch (entry.type) - { - case TYPE_INFO: - return _("Info"); - case TYPE_WARNING: - return _("Warning"); - case TYPE_ERROR: - return _("Error"); - case TYPE_FATAL_ERROR: - return _("Serious Error"); - } - break; - - case COL_TYPE_MSG_TEXT: - return copyStringTo(entry.messageLine); - } - return wxEmptyString; - } - - virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool selected) override - { - wxRect rectTmp = rect; - - const wxColor colorGridLine = wxColour(192, 192, 192); //light grey - - wxDCPenChanger dummy2(dc, wxPen(colorGridLine, 1, wxSOLID)); - const bool drawBottomLine = [&]() -> bool //don't separate multi-line messages - { - MessageView::LogEntryView nextEntry = {}; - if (msgView_ && msgView_->getEntry(row + 1, nextEntry)) - return nextEntry.firstLine; - return true; - }(); - - if (drawBottomLine) - { - dc.DrawLine(rect.GetBottomLeft(), rect.GetBottomRight() + wxPoint(1, 0)); - --rectTmp.height; - } - - //-------------------------------------------------------- - - MessageView::LogEntryView entry = {}; - if (msgView_ && msgView_->getEntry(row, entry)) - switch (static_cast(colType)) - { - case COL_TYPE_MSG_TIME: - drawCellText(dc, rectTmp, getValue(row, colType), true, wxALIGN_CENTER); - break; - - case COL_TYPE_MSG_CATEGORY: - if (entry.firstLine) - switch (entry.type) - { - case TYPE_INFO: - dc.DrawLabel(wxString(), getResourceImage(L"msg_info_small"), rectTmp, wxALIGN_CENTER); - break; - case TYPE_WARNING: - dc.DrawLabel(wxString(), getResourceImage(L"msg_warning_small"), rectTmp, wxALIGN_CENTER); - break; - case TYPE_ERROR: - case TYPE_FATAL_ERROR: - dc.DrawLabel(wxString(), getResourceImage(L"msg_error_small"), rectTmp, wxALIGN_CENTER); - break; - } - break; - - case COL_TYPE_MSG_TEXT: - { - rectTmp.x += COLUMN_GAP_LEFT; - rectTmp.width -= COLUMN_GAP_LEFT; - drawCellText(dc, rectTmp, getValue(row, colType), true); - } - break; - } - } - - virtual int getBestSize(wxDC& dc, size_t row, ColumnType colType) override - { - // -> synchronize renderCell() <-> getBestSize() - - MessageView::LogEntryView entry = {}; - if (msgView_ && msgView_->getEntry(row, entry)) - switch (static_cast(colType)) - { - case COL_TYPE_MSG_TIME: - return 2 * COLUMN_GAP_LEFT + dc.GetTextExtent(getValue(row, colType)).GetWidth(); - - case COL_TYPE_MSG_CATEGORY: - return getResourceImage(L"msg_info_small").GetWidth(); - - case COL_TYPE_MSG_TEXT: - return COLUMN_GAP_LEFT + dc.GetTextExtent(getValue(row, colType)).GetWidth(); - } - return 0; - } - - static int getColumnTimeDefaultWidth(Grid& grid) - { - wxClientDC dc(&grid.getMainWin()); - dc.SetFont(grid.getMainWin().GetFont()); - return 2 * COLUMN_GAP_LEFT + dc.GetTextExtent(formatTime(FORMAT_TIME)).GetWidth(); - } - - static int getColumnCategoryDefaultWidth() - { - return getResourceImage(L"msg_info_small").GetWidth(); - } - - static int getRowDefaultHeight(const Grid& grid) - { - return std::max(getResourceImage(L"msg_info_small").GetHeight(), grid.getMainWin().GetCharHeight() + 2) + 1; //+ some space + bottom border - } - - virtual wxString getToolTip(size_t row, ColumnType colType) const override - { - MessageView::LogEntryView entry = {}; - if (msgView_ && msgView_->getEntry(row, entry)) - switch (static_cast(colType)) - { - case COL_TYPE_MSG_TIME: - case COL_TYPE_MSG_TEXT: - break; - - case COL_TYPE_MSG_CATEGORY: - return getValue(row, colType); - } - return wxEmptyString; - } - - virtual wxString getColumnLabel(ColumnType colType) const { return wxEmptyString; } - -private: - const std::shared_ptr msgView_; -}; -} - - -class LogPanel : public LogPanelGenerated -{ -public: - LogPanel(wxWindow* parent, const ErrorLog& log) : LogPanelGenerated(parent), - msgView(std::make_shared(log)) - { - const int errorCount = log.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR); - const int warningCount = log.getItemCount(TYPE_WARNING); - const int infoCount = log.getItemCount(TYPE_INFO); - - auto initButton = [](ToggleButton& btn, const char* imgName, const wxString& tooltip) { btn.init(buttonPressed(imgName), buttonReleased(imgName)); btn.SetToolTip(tooltip); }; - - initButton(*m_bpButtonErrors, "msg_error", _("Error" ) + wxString::Format(L" (%d)", errorCount )); - initButton(*m_bpButtonWarnings, "msg_warning", _("Warning") + wxString::Format(L" (%d)", warningCount)); - initButton(*m_bpButtonInfo, "msg_info", _("Info" ) + wxString::Format(L" (%d)", infoCount )); - - m_bpButtonErrors ->setActive(true); - m_bpButtonWarnings->setActive(true); - m_bpButtonInfo ->setActive(errorCount + warningCount == 0); - - m_bpButtonErrors ->Show(errorCount != 0); - m_bpButtonWarnings->Show(warningCount != 0); - m_bpButtonInfo ->Show(infoCount != 0); - - //init grid, determine default sizes - const int rowHeight = GridDataMessages::getRowDefaultHeight(*m_gridMessages); - const int colMsgTimeWidth = GridDataMessages::getColumnTimeDefaultWidth(*m_gridMessages); - const int colMsgCategoryWidth = GridDataMessages::getColumnCategoryDefaultWidth(); - - m_gridMessages->setDataProvider(std::make_shared(msgView)); - m_gridMessages->setColumnLabelHeight(0); - m_gridMessages->showRowLabel(false); - m_gridMessages->setRowHeight(rowHeight); - std::vector attr; - attr.push_back(Grid::ColumnAttribute(static_cast(COL_TYPE_MSG_TIME ), colMsgTimeWidth, 0)); - attr.push_back(Grid::ColumnAttribute(static_cast(COL_TYPE_MSG_CATEGORY), colMsgCategoryWidth, 0)); - attr.push_back(Grid::ColumnAttribute(static_cast(COL_TYPE_MSG_TEXT ), -colMsgTimeWidth - colMsgCategoryWidth, 1)); - m_gridMessages->setColumnConfig(attr); - - //support for CTRL + C - m_gridMessages->getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(LogPanel::onGridButtonEvent), nullptr, this); - - m_gridMessages->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(LogPanel::onMsgGridContext), nullptr, this); - - updateGrid(); - } - -private: - virtual void OnErrors(wxCommandEvent& event) - { - m_bpButtonErrors->toggle(); - updateGrid(); - } - - virtual void OnWarnings(wxCommandEvent& event) - { - m_bpButtonWarnings->toggle(); - updateGrid(); - } - - virtual void OnInfo(wxCommandEvent& event) - { - m_bpButtonInfo->toggle(); - updateGrid(); - } - - void updateGrid() - { - int includedTypes = 0; - if (m_bpButtonErrors->isActive()) - includedTypes |= TYPE_ERROR | TYPE_FATAL_ERROR; - - if (m_bpButtonWarnings->isActive()) - includedTypes |= TYPE_WARNING; - - if (m_bpButtonInfo->isActive()) - includedTypes |= TYPE_INFO; - - msgView->updateView(includedTypes); //update MVC "model" - m_gridMessages->Refresh(); //update MVC "view" - } - - void onGridButtonEvent(wxKeyEvent& event) - { - int keyCode = event.GetKeyCode(); - - if (event.ControlDown()) - switch (keyCode) - { - case 'C': - case WXK_INSERT: //CTRL + C || CTRL + INS - copySelectionToClipboard(); - return; // -> swallow event! don't allow default grid commands! - } - - event.Skip(); //unknown keypress: propagate - } - - void onMsgGridContext(GridClickEvent& event) - { - const std::vector selection = m_gridMessages->getSelectedRows(); - - ContextMenu menu; - menu.addItem(_("Copy") + L"\tCtrl+C", [this] { copySelectionToClipboard(); }, nullptr, !selection.empty()); - menu.popup(*this); - } - - void copySelectionToClipboard() - { - try - { - typedef Zbase zxString; //guaranteed exponential growth, unlike wxString - zxString clipboardString; - - if (auto prov = m_gridMessages->getDataProvider()) - { - std::vector colAttr = m_gridMessages->getColumnConfig(); - vector_remove_if(colAttr, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); - if (!colAttr.empty()) - { - const std::vector selection = m_gridMessages->getSelectedRows(); - std::for_each(selection.begin(), selection.end(), - [&](size_t row) - { -#ifdef _MSC_VER - typedef zxString zxString; //workaround MSVC compiler bug! -#endif - std::for_each(colAttr.begin(), --colAttr.end(), - [&](const Grid::ColumnAttribute& ca) - { - clipboardString += copyStringTo(prov->getValue(row, ca.type_)); - clipboardString += L'\t'; - }); - clipboardString += copyStringTo(prov->getValue(row, colAttr.back().type_)); - clipboardString += L'\n'; - }); - } - } - - //finally write to clipboard - if (!clipboardString.empty()) - if (wxClipboard::Get()->Open()) - { - ZEN_ON_SCOPE_EXIT(wxClipboard::Get()->Close()); - wxClipboard::Get()->SetData(new wxTextDataObject(copyStringTo(clipboardString))); //ownership passed - } - } - catch (const std::bad_alloc& e) - { - showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setMainInstructions(_("Out of memory.") + L" " + utfCvrtTo(e.what()))); - } - } - - std::shared_ptr msgView; //bound! -}; - -//######################################################################################## - -namespace -{ -class CurveDataStatistics : public SparseCurveData -{ -public: - CurveDataStatistics() : SparseCurveData(true), /*true: add steps*/ timeNow(0) {} - - void clear() { samples.clear(); timeNow = 0; } - - void addRecord(long timeNowMs, double value) - { - //samples.clear(); - //samples.insert(std::make_pair(-1000, 0)); - //samples.insert(std::make_pair(0, 0)); - //samples.insert(std::make_pair(1, 1)); - //samples.insert(std::make_pair(1000, 0)); - //return; - - timeNow = timeNowMs; - - //don't allow for more samples per second than there are UI updates (handles duplicate inserts, too!) - if (!samples.empty() && timeNowMs / UI_UPDATE_INTERVAL == samples.rbegin()->first / UI_UPDATE_INTERVAL) - { - samples.rbegin()->second = value; - return; - } - - samples.insert(samples.end(), std::make_pair(timeNowMs, value)); //time is "expected" to be monotonously ascending - //documentation differs about whether "hint" should be before or after the to be inserted element! - //however "std::map<>::end()" is interpreted correctly by GCC and VS2010 - - if (samples.size() > MAX_BUFFER_SIZE) //limit buffer size - samples.erase(samples.begin()); - } - -private: - virtual std::pair getRangeX() const override - { - if (samples.empty()) return std::make_pair(0.0, 0.0); - - double upperEndMs = std::max(timeNow, samples.rbegin()->first); - - //report some additional width by 5% elapsed time to make graph recalibrate before hitting the right border - //caveat: graph for batch mode binary comparison does NOT start at elapsed time 0!! PHASE_COMPARING_CONTENT and PHASE_SYNCHRONIZING! - //=> consider width of current sample set! - upperEndMs += 0.05 *(upperEndMs - samples.begin()->first); - - return std::make_pair(samples.begin()->first / 1000.0, //need not start with 0, e.g. "binary comparison, graph reset, followed by sync" - upperEndMs / 1000.0); - } - - virtual Opt getLessEq(double x) const override //x: seconds since begin - { - const long timex = std::floor(x * 1000); - //------ add artifical last sample value ------- - if (!samples.empty() && samples.rbegin()->first < timeNow) - if (timeNow <= timex) - return CurvePoint(timeNow / 1000.0, samples.rbegin()->second); - //-------------------------------------------------- - - //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(); - //=> samples not empty in this context - --it; - return CurvePoint(it->first / 1000.0, it->second); - } - - virtual Opt getGreaterEq(double x) const override - { - const long timex = std::ceil(x * 1000); - //------ add artifical last sample value ------- - if (!samples.empty() && samples.rbegin()->first < timeNow) - if (samples.rbegin()->first < timex && timex <= timeNow) - return CurvePoint(timeNow / 1000.0, samples.rbegin()->second); - //-------------------------------------------------- - - auto it = samples.lower_bound(timex); - if (it == samples.end()) - return NoValue(); - return CurvePoint(it->first / 1000.0, it->second); - } - - static const size_t MAX_BUFFER_SIZE = 2500000; //sizeof(single node) worst case ~ 3 * 8 byte ptr + 16 byte key/value = 40 byte - - std::map samples; //time, unit: [ms] !don't use std::multimap, see getLessEq() - long timeNow; //help create an artificial record at the end of samples to visualize current time! -}; - - -class CurveDataCurrentValue : public CurveData -{ -public: - CurveDataCurrentValue() : x(0), yCurrent_(0), yTotal_(0) {} - - void setValue(long xTimeNowMs, double yCurrent, double yTotal) { x = xTimeNowMs / 1000.0; yCurrent_ = yCurrent; yTotal_ = yTotal; } - -private: - virtual std::pair getRangeX() const override { return std::make_pair(x, x); } //conceptually just a vertical line! - - virtual void getPoints(double minX, double maxX, int pixelWidth, std::vector& points) const override - { - //points.push_back(CurvePoint(-1, 0)); - //points.push_back(CurvePoint(0, 0)); - //points.push_back(CurvePoint(0.0001, 1)); - //points.push_back(CurvePoint(1, 0)); - //return; - - if (x <= maxX) - { - points.push_back(CurvePoint(x, 0)); - points.push_back(CurvePoint(x, yCurrent_)); - points.push_back(CurvePoint(maxX, yCurrent_)); - points.push_back(CurvePoint(x, yCurrent_)); - points.push_back(CurvePoint(x, yTotal_)); - } - } - - double x; //time elapsed in seconds - double yCurrent_; //current bytes/items - double yTotal_; -}; - - -class CurveDataTotalValue : public CurveData -{ -public: - CurveDataTotalValue () : x(0), yTotal_(0) {} - - void setValue(long xTimeNowMs, double yTotal) { x = xTimeNowMs / 1000.0; yTotal_ = yTotal; } - -private: - virtual std::pair getRangeX() const override { return std::make_pair(x, x); } //conceptually just a vertical line! - - virtual void getPoints(double minX, double maxX, int pixelWidth, std::vector& points) const override - { - if (x <= maxX) - { - points.push_back(CurvePoint(x, yTotal_)); - points.push_back(CurvePoint(maxX, yTotal_)); - } - } - - double x; //time elapsed in seconds - double yTotal_; -}; - - -const double stretchDefaultBlockSize = 1.4; //enlarge block default size - - -struct LabelFormatterBytes : public LabelFormatter -{ - virtual double getOptimalBlockSize(double bytesProposed) const - { - bytesProposed *= stretchDefaultBlockSize; //enlarge block default size - - if (bytesProposed <= 1) //never smaller than 1 byte - return 1; - - //round to next number which is a convenient to read block size - const double k = std::floor(std::log(bytesProposed) / std::log(2.0)); - const double e = std::pow(2.0, k); - if (numeric::isNull(e)) - return 0; - const double a = bytesProposed / e; //bytesProposed = a * 2^k with a in [1, 2) - assert(1 <= a && a < 2); - const double steps[] = { 1, 2 }; - return e * numeric::nearMatch(a, std::begin(steps), std::end(steps)); - } - - virtual wxString formatText(double value, double optimalBlockSize) const { return filesizeToShortString(Int64(value)); }; -}; - - -struct LabelFormatterItemCount : public LabelFormatter -{ - virtual double getOptimalBlockSize(double itemsProposed) const - { - itemsProposed *= stretchDefaultBlockSize; //enlarge block default size - - const double steps[] = { 1, 2, 5, 10 }; - if (itemsProposed <= 10) - return numeric::nearMatch(itemsProposed, std::begin(steps), std::end(steps)); //similar to nextNiceNumber(), but without the 2.5 step! - return nextNiceNumber(itemsProposed); - } - - virtual wxString formatText(double value, double optimalBlockSize) const - { - return toGuiString(numeric::round(value)); //not enough room for a "%x items" representation - }; -}; - - -struct LabelFormatterTimeElapsed : public LabelFormatter -{ - LabelFormatterTimeElapsed(bool drawLabel) : drawLabel_(drawLabel) {} - - virtual double getOptimalBlockSize(double secProposed) const - { - const double stepsSec[] = { 20, 30, 60 }; //20 sec: minimum block size; (no 15: avoid flicker between 10<->15<->20 sec blocks) - if (secProposed <= 60) - return numeric::nearMatch(secProposed, std::begin(stepsSec), std::end(stepsSec)); - - const double stepsMin[] = { 1, 2, 5, 10, 15, 20, 30, 60 }; //nice numbers for minutes - if (secProposed <= 3600) - return 60.0 * numeric::nearMatch(secProposed / 60, std::begin(stepsMin), std::end(stepsMin)); - - if (secProposed <= 3600 * 24) - return nextNiceNumber(secProposed / 3600) * 3600; //round up to full hours - - return nextNiceNumber(secProposed / (24 * 3600)) * 24 * 3600; //round to full days - } - - virtual wxString formatText(double timeElapsed, double optimalBlockSize) const - { - if (!drawLabel_) return wxString(); - return timeElapsed < 60 ? - _P("1 sec", "%x sec", numeric::round(timeElapsed)) : - timeElapsed < 3600 ? - wxTimeSpan::Seconds(timeElapsed).Format( L"%M:%S") : - wxTimeSpan::Seconds(timeElapsed).Format(L"%H:%M:%S"); - } - -private: - bool drawLabel_; -}; -} - - -template //can be a wxFrame or wxDialog -class SyncProgressDialogImpl : public TopLevelDialog, public SyncProgressDialog -/*we need derivation, not composition! - 1. SyncProgressDialogImpl IS a wxFrame/wxDialog - 2. implement virtual ~wxFrame() - 3. event handling below assumes lifetime is larger-equal than wxFrame's -*/ -{ -public: - SyncProgressDialogImpl(long style, //wxFrame/wxDialog style - const std::function& getTaskbarFrame, - AbortCallback& abortCb, - const std::function& notifyWindowTerminate, - const Statistics& syncStat, - wxFrame* parentFrame, - bool showProgress, - const wxString& jobName, - const std::wstring& execWhenFinished, - std::vector& execFinishedHistory); - virtual ~SyncProgressDialogImpl(); - - //call this in StatusUpdater derived class destructor at the LATEST(!) to prevent access to currentStatusUpdater - virtual void processHasFinished(SyncResult resultId, const ErrorLog& log); - virtual void closeWindowDirectly(); - - virtual wxWindow* getWindowIfVisible() { return this->IsShown() ? this : nullptr; } - //workaround OS X bug: if "this" is used as parent window for a modal dialog then this dialog will erroneously un-hide its parent! - - virtual void initNewPhase(); - virtual void notifyProgressChange(); - virtual void updateGui() { updateGuiInt(true); } - - virtual std::wstring getExecWhenFinishedCommand() const { return pnl.m_comboBoxExecFinished->getValue(); } - - virtual void stopTimer() //halt all internal counters! - { - pnl.m_animCtrlSyncing->Stop(); - timeElapsed.Pause (); - } - virtual void resumeTimer() - { - pnl.m_animCtrlSyncing->Play(); - timeElapsed.Resume(); - } - -private: - void updateGuiInt(bool allowYield); - - void OnKeyPressed(wxKeyEvent& event); - void OnOkay (wxCommandEvent& event); - void OnPause (wxCommandEvent& event); - void OnCancel (wxCommandEvent& event); - void OnClose (wxCloseEvent& event); - void OnIconize(wxIconizeEvent& event); - void OnMinimizeToTray(wxCommandEvent& event) { minimizeToTray(); } - - void minimizeToTray(); - void resumeFromSystray(); - - void updateDialogStatus(); - void setExternalStatus(const wxString& status, const wxString& progress); //progress may be empty! - - SyncProgressPanelGenerated& pnl; //wxPanel containing the GUI controls of *this - - const wxString jobName_; - wxStopWatch timeElapsed; - - wxFrame* parentFrame_; //optional - - std::function notifyWindowTerminate_; //call once in OnClose(), NOT in destructor which is called far too late somewhere in wxWidgets main loop! - - bool wereDead; //after wxWindow::Delete, which is equal to "delete this" on OS X! - - //status variables - const Statistics* syncStat_; // - AbortCallback* abortCb_; //valid only while sync is running - bool paused_; //valid only while sync is running - SyncResult finalResult; //set after sync - - //remaining time - std::unique_ptr perf; - long lastStatCallSpeed; //used for calculating intervals between collecting perf samples - //help calculate total speed - long phaseStartMs; //begin of current phase in [ms] - - std::shared_ptr curveDataBytes; - std::shared_ptr curveDataItems; - - std::shared_ptr curveDataBytesTotal; - std::shared_ptr curveDataBytesCurrent; - std::shared_ptr curveDataItemsTotal; - std::shared_ptr curveDataItemsCurrent; - - wxString parentFrameTitleBackup; - std::unique_ptr trayIcon; //optional: if filled all other windows should be hidden and conversely - std::unique_ptr taskbar_; -}; - - -template -SyncProgressDialogImpl::SyncProgressDialogImpl(long style, //wxFrame/wxDialog style - const std::function& getTaskbarFrame, - AbortCallback& abortCb, - const std::function& notifyWindowTerminate, - const Statistics& syncStat, - wxFrame* parentFrame, - bool showProgress, - const wxString& jobName, - const std::wstring& execWhenFinished, - std::vector& execFinishedHistory) : - TopLevelDialog(parentFrame, wxID_ANY, wxString(), wxDefaultPosition, wxDefaultSize, style), //title is overwritten anyway in setExternalStatus() - pnl(*new SyncProgressPanelGenerated(this)), //ownership passed to "this" - jobName_ (jobName), - parentFrame_(parentFrame), - notifyWindowTerminate_(notifyWindowTerminate), - wereDead(false), - syncStat_ (&syncStat), - abortCb_ (&abortCb), - paused_ (false), - finalResult(RESULT_ABORTED), //dummy value - lastStatCallSpeed(-1000000), //some big number - phaseStartMs(0) -{ - assert_static((IsSameType::value || - IsSameType::value)); - assert((IsSameType::value == !parentFrame)); - - //finish construction of this dialog: - this->SetMinSize(wxSize(470, 280)); //== minimum size! no idea why SetMinSize() is not used... - wxBoxSizer* bSizer170 = new wxBoxSizer(wxVERTICAL); - bSizer170->Add(&pnl, 1, wxEXPAND); - this->SetSizer(bSizer170); //pass ownership - //this->Layout(); - //bSizer170->Fit(this); - this->Centre(wxBOTH); - - //lifetime of event sources is subset of this instance's lifetime => no wxEvtHandler::Disconnect() needed - this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler (SyncProgressDialogImpl::OnClose)); - this->Connect(wxEVT_ICONIZE, wxIconizeEventHandler(SyncProgressDialogImpl::OnIconize)); - this->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(SyncProgressDialogImpl::OnKeyPressed), nullptr, this); - pnl.m_buttonClose->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SyncProgressDialogImpl::OnOkay ), NULL, this); - pnl.m_buttonPause->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SyncProgressDialogImpl::OnPause ), NULL, this); - pnl.m_buttonStop ->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SyncProgressDialogImpl::OnCancel), NULL, this); - pnl.m_bpButtonMinimizeToTray->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SyncProgressDialogImpl::OnMinimizeToTray), NULL, this); - -#ifdef ZEN_WIN - new MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" -#endif - - assert(pnl.m_buttonClose->GetId() == wxID_OK); //we cannot use wxID_CLOSE else Esc key won't work: yet another wxWidgets bug?? - - setRelativeFontSize(*pnl.m_staticTextPhase, 1.5); - - if (parentFrame_) - parentFrameTitleBackup = parentFrame_->GetTitle(); //save old title (will be used as progress indicator) - - pnl.m_animCtrlSyncing->SetAnimation(getResourceAnimation(L"working")); - pnl.m_animCtrlSyncing->Play(); - - this->EnableCloseButton(false); //this is NOT honored on OS X or during system shutdown on Windows! - - timeElapsed.Start(); //measure total time - - if (wxFrame* frame = getTaskbarFrame(*this)) - try //try to get access to Windows 7/Ubuntu taskbar - { - taskbar_ = make_unique(*frame); //throw TaskbarNotAvailable - } - catch (const TaskbarNotAvailable&) {} - - //hide "processed" statistics until end of process - pnl.m_notebookResult ->Hide(); - pnl.m_panelItemsProcessed->Hide(); - pnl.m_buttonClose ->Show(false); - //set std order after button visibility was set - setStandardButtonOrder(*pnl.bSizerStdButtons, StdButtons().setAffirmative(pnl.m_buttonPause).setCancel(pnl.m_buttonStop)); - - pnl.m_bpButtonMinimizeToTray->SetBitmapLabel(getResourceImage(L"minimize_to_tray")); - - //init graph - curveDataBytes = std::make_shared(); - curveDataItems = std::make_shared(); - curveDataBytesTotal = std::make_shared(); - curveDataBytesCurrent = std::make_shared(); - curveDataItemsTotal = std::make_shared(); - curveDataItemsCurrent = std::make_shared(); - - const int xLabelHeight = 18; //we need to use the same height for both graphs to make sure they stretch evenly - const int yLabelWidth = 70; - pnl.m_panelGraphBytes->setAttributes(Graph2D::MainAttributes(). - setLabelX(Graph2D::X_LABEL_BOTTOM, xLabelHeight, std::make_shared(true)). - setLabelY(Graph2D::Y_LABEL_RIGHT, yLabelWidth, std::make_shared()). - setSelectionMode(Graph2D::SELECT_NONE)); - - pnl.m_panelGraphItems->setAttributes(Graph2D::MainAttributes(). - setLabelX(Graph2D::X_LABEL_BOTTOM, xLabelHeight, std::make_shared(false)). - setLabelY(Graph2D::Y_LABEL_RIGHT, yLabelWidth, std::make_shared()). - setSelectionMode(Graph2D::SELECT_NONE)); - - //pnl.m_panelGraphBytes->setAttributes(Graph2D::MainAttributes().setMinX(-1).setMaxX(1).setMinY(-1).setMaxY(1)); - - pnl.m_panelGraphBytes->setCurve(curveDataBytes, Graph2D::CurveAttributes().setLineWidth(2). - fillCurveArea(wxColor(205, 255, 202)). //faint green - setColor (wxColor( 20, 200, 0))); //medium green - - pnl.m_panelGraphItems->setCurve(curveDataItems, Graph2D::CurveAttributes().setLineWidth(2). - fillCurveArea(wxColor(198, 206, 255)). //faint blue - setColor (wxColor( 90, 120, 255))); //medium blue - - pnl.m_panelGraphBytes->addCurve(curveDataBytesTotal, Graph2D::CurveAttributes().setLineWidth(2).setColor(wxColor(12, 128, 0))); //dark green - pnl.m_panelGraphBytes->addCurve(curveDataBytesCurrent, Graph2D::CurveAttributes().setLineWidth(1).setColor(wxColor(12, 128, 0))); // - - pnl.m_panelGraphItems->addCurve(curveDataItemsTotal, Graph2D::CurveAttributes().setLineWidth(2).setColor(wxColor(53, 25, 255))); //dark blue - pnl.m_panelGraphItems->addCurve(curveDataItemsCurrent, Graph2D::CurveAttributes().setLineWidth(1).setColor(wxColor(53, 25, 255))); // - - //allow changing on completion command - pnl.m_comboBoxExecFinished->initHistory(execFinishedHistory, execFinishedHistory.size()); //-> we won't use addItemHistory() later - pnl.m_comboBoxExecFinished->setValue(execWhenFinished); - - updateDialogStatus(); //null-status will be shown while waiting for dir locks - - this->GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() - pnl.Layout(); - - if (showProgress) - { - this->Show(); -#ifdef ZEN_MAC - ProcessSerialNumber psn = { 0, kCurrentProcess }; - ::SetFrontProcess(&psn); //call before TransformProcessType() so that OSX menu is updated correctly - ::TransformProcessType(&psn, kProcessTransformToForegroundApplication); //show dock icon (consider non-silent batch mode) -#endif - - pnl.m_buttonStop->SetFocus(); //don't steal focus when starting in sys-tray! - - //clear gui flicker, remove dummy texts: window must be visible to make this work! - updateGuiInt(true); //at least on OS X a real Yield() is required to flush pending GUI updates; Update() is not enough - } - else - minimizeToTray(); -} - - -template -SyncProgressDialogImpl::~SyncProgressDialogImpl() -{ - if (parentFrame_) - { - parentFrame_->SetTitle(parentFrameTitleBackup); //restore title text - - //make sure main dialog is shown again if still "minimized to systray"! see SyncProgressDialog::closeWindowDirectly() - parentFrame_->Show(); -#ifdef ZEN_MAC - ProcessSerialNumber psn = { 0, kCurrentProcess }; - ::SetFrontProcess(&psn); //call before TransformProcessType() so that OSX menu is updated correctly - //why isn't this covered by wxWindows::Raise()?? - ::TransformProcessType(&psn, kProcessTransformToForegroundApplication); //show dock icon (consider GUI mode with "close progress dialog") -#endif - //if (parentFrame_->IsIconized()) //caveat: if window is maximized calling Iconize(false) will erroneously un-maximize! - // parentFrame_->Iconize(false); - } - - //our client is NOT expecting a second call via notifyWindowTerminate_()! -} - - -template -void SyncProgressDialogImpl::OnKeyPressed(wxKeyEvent& event) -{ - const int keyCode = event.GetKeyCode(); - if (keyCode == WXK_ESCAPE) - { - wxCommandEvent dummy(wxEVT_COMMAND_BUTTON_CLICKED); - - //simulate click on abort button - if (pnl.m_buttonStop->IsShown()) //delegate to "cancel" button if available - { - if (wxEvtHandler* handler = pnl.m_buttonStop->GetEventHandler()) - handler->ProcessEvent(dummy); - return; - } - else if (pnl.m_buttonClose->IsShown()) - { - if (wxEvtHandler* handler = pnl.m_buttonClose->GetEventHandler()) - handler->ProcessEvent(dummy); - return; - } - } - - event.Skip(); -} - - -template -void SyncProgressDialogImpl::initNewPhase() -{ - updateDialogStatus(); //evaluates "syncStat_->currentPhase()" - - //reset graphs (e.g. after binary comparison) - curveDataBytesTotal ->setValue(0, 0); - curveDataBytesCurrent->setValue(0, 0, 0); - curveDataItemsTotal ->setValue(0, 0); - curveDataItemsCurrent->setValue(0, 0, 0); - curveDataBytes->clear(); - curveDataItems->clear(); - - notifyProgressChange(); - - //start new measurement - perf = make_unique(WINDOW_REMAINING_TIME, WINDOW_BYTES_PER_SEC); - lastStatCallSpeed = -1000000; //some big number - - phaseStartMs = timeElapsed.Time(); - - updateGuiInt(false); -} - - -template -void SyncProgressDialogImpl::notifyProgressChange() //noexcept! -{ - if (syncStat_) //sync running - switch (syncStat_->currentPhase()) - { - case ProcessCallback::PHASE_NONE: - assert(false); - case ProcessCallback::PHASE_SCANNING: - break; - case ProcessCallback::PHASE_COMPARING_CONTENT: - case ProcessCallback::PHASE_SYNCHRONIZING: - curveDataBytes->addRecord(timeElapsed.Time(), to(syncStat_->getDataCurrent (syncStat_->currentPhase()))); - curveDataItems->addRecord(timeElapsed.Time(), syncStat_->getObjectsCurrent(syncStat_->currentPhase())); - break; - } -} - - -namespace -{ -#ifdef ZEN_WIN -enum Zorder -{ - ZORDER_CORRECT, - ZORDER_WRONG, - ZORDER_INDEFINITE, -}; - -Zorder evaluateZorder(const wxWindow& top, const wxWindow& bottom) -{ - HWND hTop = static_cast(top.GetHWND()); - HWND hBottom = static_cast(bottom.GetHWND()); - assert(hTop && hBottom); - - for (HWND hAbove = hBottom; hAbove; hAbove = ::GetNextWindow(hAbove, GW_HWNDPREV)) //GW_HWNDPREV means "to foreground" - if (hAbove == hTop) - return ZORDER_CORRECT; - - for (HWND hAbove = hTop; hAbove; hAbove = ::GetNextWindow(hAbove, GW_HWNDPREV)) - if (hAbove == hBottom) - return ZORDER_WRONG; - - return ZORDER_INDEFINITE; -} -#endif - - -std::wstring getDialogPhaseText(const Statistics* syncStat, bool paused, SyncProgressDialog::SyncResult finalResult) -{ - if (syncStat) //sync running - { - if (paused) - return _("Paused"); - else - switch (syncStat->currentPhase()) - { - case ProcessCallback::PHASE_NONE: - return _("Initializing..."); //dialog is shown *before* sync starts, so this text may be visible! - case ProcessCallback::PHASE_SCANNING: - return _("Scanning..."); - case ProcessCallback::PHASE_COMPARING_CONTENT: - return _("Comparing content..."); - case ProcessCallback::PHASE_SYNCHRONIZING: - return _("Synchronizing..."); - } - } - else //sync finished - switch (finalResult) - { - case SyncProgressDialog::RESULT_ABORTED: - return _("Stopped"); - case SyncProgressDialog::RESULT_FINISHED_WITH_ERROR: - case SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS: - case SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS: - return _("Completed"); - } - return std::wstring(); -} -} - - -template -void SyncProgressDialogImpl::setExternalStatus(const wxString& status, const wxString& progress) //progress may be empty! -{ - //sys tray: order "top-down": jobname, status, progress - wxString systrayTooltip = jobName_.empty() ? status : L"\"" + jobName_ + L"\"\n" + status; - if (!progress.empty()) - systrayTooltip += L" " + progress; - - //window caption/taskbar; inverse order: progress, status, jobname - wxString title = progress.empty() ? status : progress + L" - " + status; - if (!jobName_.empty()) - title += L" - \"" + jobName_ + L"\""; - - //systray tooltip, if window is minimized - if (trayIcon.get()) - trayIcon->setToolTip(systrayTooltip); - - //show text in dialog title (and at the same time in taskbar) - if (parentFrame_) - { - if (parentFrame_->GetTitle() != title) - parentFrame_->SetTitle(title); - } - - //always set a title: we don't wxGTK to show "nameless window" instead - if (this->GetTitle() != title) - this->SetTitle(title); -} - - -template -void SyncProgressDialogImpl::updateGuiInt(bool allowYield) -{ - if (!syncStat_) //sync not running - return; - - bool layoutChanged = false; //avoid screen flicker by calling layout() only if necessary - const long timeNow = timeElapsed.Time(); - - //sync status text - setText(*pnl.m_staticTextStatus, replaceCpy(syncStat_->currentStatusText(), L'\n', L' ')); //no layout update for status texts! - - switch (syncStat_->currentPhase()) //no matter if paused or not - { - case ProcessCallback::PHASE_NONE: - case ProcessCallback::PHASE_SCANNING: - //dialog caption, taskbar, systray tooltip - setExternalStatus(getDialogPhaseText(syncStat_, paused_, finalResult), toGuiString(syncStat_->getObjectsCurrent(ProcessCallback::PHASE_SCANNING))); //status text may be "paused"! - - //progress indicators - if (trayIcon.get()) trayIcon->setProgress(1); //100% = regular FFS logo - - //ignore graphs: should already have been cleared in initNewPhase() - - //remaining objects and data - setText(*pnl.m_staticTextRemainingObj , L"-", &layoutChanged); - setText(*pnl.m_staticTextDataRemaining, L"", &layoutChanged); - - //remaining time and speed - setText(*pnl.m_staticTextRemTime, L"-", &layoutChanged); - pnl.m_panelGraphBytes->setAttributes(pnl.m_panelGraphBytes->getAttributes().setCornerText(wxString(), Graph2D::CORNER_TOP_LEFT)); - pnl.m_panelGraphItems->setAttributes(pnl.m_panelGraphItems->getAttributes().setCornerText(wxString(), Graph2D::CORNER_TOP_LEFT)); - break; - - case ProcessCallback::PHASE_COMPARING_CONTENT: - case ProcessCallback::PHASE_SYNCHRONIZING: - { - const int itemsCurrent = syncStat_->getObjectsCurrent(syncStat_->currentPhase()); - const int itemsTotal = syncStat_->getObjectsTotal (syncStat_->currentPhase()); - const Int64 dataCurrent = syncStat_->getDataCurrent (syncStat_->currentPhase()); - const Int64 dataTotal = syncStat_->getDataTotal (syncStat_->currentPhase()); - - //add both data + obj-count, to handle "deletion-only" cases - const double fraction = dataTotal + itemsTotal == 0 ? 1 : std::max(0.0, to(dataCurrent + itemsCurrent) / to(dataTotal + itemsTotal)); - //yes, this may legitimately become < 0: failed rename operation falls-back to copy + delete, reducing "dataCurrent" to potentially < 0! - //---------------------------------------------------------------------------------------------------- - - //dialog caption, taskbar, systray tooltip - setExternalStatus(getDialogPhaseText(syncStat_, paused_, finalResult), fractionToString(fraction)); //status text may be "paused"! - - //progress indicators - if (trayIcon.get()) trayIcon->setProgress(fraction); - if (taskbar_.get()) taskbar_->setProgress(fraction); - - //constant line graph - curveDataBytesTotal ->setValue(timeNow, to(dataTotal)); - curveDataBytesCurrent->setValue(timeNow, to(dataCurrent), to(dataTotal)); - curveDataItemsTotal ->setValue(timeNow, itemsTotal); - curveDataItemsCurrent->setValue(timeNow, itemsCurrent, itemsTotal); - //even though notifyProgressChange() already set the latest data, let's add another sample to have all curves consider "timeNow" - //no problem with adding too many records: CurveDataStatistics will remove duplicate entries! - curveDataBytes->addRecord(timeNow, to(dataCurrent)); - curveDataItems->addRecord(timeNow, itemsCurrent); - - //remaining objects and data - setText(*pnl.m_staticTextRemainingObj, toGuiString(itemsTotal - itemsCurrent), &layoutChanged); - setText(*pnl.m_staticTextDataRemaining, L"(" + filesizeToShortString(dataTotal - dataCurrent) + L")", &layoutChanged); - //it's possible data remaining becomes shortly negative if last file synced has ADS data and the dataTotal was not yet corrected! - - //remaining time and speed - assert(perf); - if (perf) - if (numeric::dist(lastStatCallSpeed, timeNow) >= 500) - { - lastStatCallSpeed = timeNow; - - if (numeric::dist(phaseStartMs, timeNow) >= 1000) //discard stats for first second: probably messy - perf->addSample(itemsCurrent, to(dataCurrent), timeNow); - - //current speed -> Win 7 copy uses 1 sec update interval instead - Opt bps = perf->getBytesPerSecond(); - Opt 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)); - //setText(*pnl.m_staticTextSpeed, perf->getBytesPerSecond(), &layoutChanged); - - //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 rt = perf->getRemainingTime(to(dataTotal - dataCurrent)); - setText(*pnl.m_staticTextRemTime, rt ? *rt : L"-", &layoutChanged); - } - } - break; - } - - pnl.m_panelGraphBytes->Refresh(); - pnl.m_panelGraphItems->Refresh(); - - //time elapsed - const long timeElapSec = timeNow / 1000; - setText(*pnl.m_staticTextTimeElapsed, - timeElapSec < 3600 ? - wxTimeSpan::Seconds(timeElapSec).Format( L"%M:%S") : - wxTimeSpan::Seconds(timeElapSec).Format(L"%H:%M:%S"), &layoutChanged); - - //adapt layout after content changes above - if (layoutChanged) - { - pnl.m_panelProgress->Layout(); - //small statistics panels: - //pnl.m_panelItemsProcessed->Layout(); - pnl.m_panelItemsRemaining->Layout(); - pnl.m_panelTimeRemaining ->Layout(); - //pnl.m_panelTimeElapsed->Layout(); -> needed? - } - -#ifdef ZEN_WIN - //workaround Windows 7 bug messing up z-order after temporary application hangs: https://sourceforge.net/tracker/index.php?func=detail&aid=3376523&group_id=234430&atid=1093080 - //This is still needed no matter if wxDialog or wxPanel is used! (2013-07) - if (parentFrame_) - if (evaluateZorder(*this, *parentFrame_) == ZORDER_WRONG) - { - HWND hProgress = static_cast(this->GetHWND()); - if (::IsWindowVisible(hProgress)) - { - ::ShowWindow(hProgress, SW_HIDE); //make Windows recalculate z-order - ::ShowWindow(hProgress, SW_SHOW); // - } - } -#endif - - if (allowYield) - { - //support for pause button - if (paused_) - { - /* - ZEN_ON_SCOPE_EXIT(resumeTimer()); -> crashes on Fedora; WHY??? - => likely compiler bug!!! - 1. no crash on Fedora for: ZEN_ON_SCOPE_EXIT(this->resumeTimer()); - 1. no crash if we derive from wxFrame instead of template "TopLevelDialog" - 2. no crash on Ubuntu GCC - 3. following makes GCC crash already during compilation: auto dfd = zen::makeGuard([this]{ resumeTimer(); }); - */ - - stopTimer(); - - while (paused_) - { - wxTheApp->Yield(); //receive UI message that end pause OR forceful termination! - //*first* refresh GUI (removing flicker) before sleeping! - boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL)); - } - //if SyncProgressDialogImpl::OnClose() already wxWindow::Destroy() on OS X then this instance is already toast! - if (wereDead) - return; //GTFO and don't call this->resumeTimer() - - resumeTimer(); - } - else - /* - /|\ - | keep this sequence to ensure one full progress update before entering pause mode! - \|/ - */ - wxTheApp->Yield(); //receive UI message that sets pause status OR forceful termination! - } - else - this->Update(); //don't wait until next idle event (who knows what blocking process comes next?) -} - - -template -void SyncProgressDialogImpl::updateDialogStatus() //depends on "syncStat_, paused_, finalResult" -{ - const wxString dlgStatusTxt = getDialogPhaseText(syncStat_, paused_, finalResult); - - pnl.m_staticTextPhase->SetLabel(dlgStatusTxt); - - //status bitmap - if (syncStat_) //sync running - { - auto setStatusBitmap = [&](const wchar_t* bmpName) - { - pnl.m_animCtrlSyncing->Hide(); - pnl.m_bitmapStatus->SetBitmap(getResourceImage(bmpName)); - pnl.m_bitmapStatus->SetToolTip(dlgStatusTxt); - pnl.m_bitmapStatus->Show(); - }; - - if (paused_) - setStatusBitmap(L"status_pause"); - else - switch (syncStat_->currentPhase()) - { - case ProcessCallback::PHASE_NONE: - pnl.m_animCtrlSyncing->Hide(); - pnl.m_bitmapStatus->Hide(); - break; - - case ProcessCallback::PHASE_SCANNING: - setStatusBitmap(L"status_scanning"); - break; - - case ProcessCallback::PHASE_COMPARING_CONTENT: - setStatusBitmap(L"status_binary_compare"); - break; - - case ProcessCallback::PHASE_SYNCHRONIZING: - pnl.m_bitmapStatus->SetBitmap(getResourceImage(L"status_syncing")); - pnl.m_bitmapStatus->SetToolTip(dlgStatusTxt); - pnl.m_bitmapStatus->Show(); - pnl.m_animCtrlSyncing->Show(); - pnl.m_animCtrlSyncing->SetToolTip(dlgStatusTxt); - break; - } - } - else //sync finished - { - auto setStatusBitmap = [&](const wchar_t* bmpName, const std::wstring& tooltip) - { - pnl.m_animCtrlSyncing->Hide(); - pnl.m_bitmapStatus->SetBitmap(getResourceImage(bmpName)); - pnl.m_bitmapStatus->Show(); - pnl.m_bitmapStatus->SetToolTip(tooltip); - }; - switch (finalResult) - { - case RESULT_ABORTED: - setStatusBitmap(L"status_aborted", _("Synchronization stopped")); - break; - - case RESULT_FINISHED_WITH_ERROR: - setStatusBitmap(L"status_finished_errors", _("Synchronization completed with errors")); - break; - - case RESULT_FINISHED_WITH_WARNINGS: - setStatusBitmap(L"status_finished_warnings", _("Synchronization completed with warnings")); - break; - - case RESULT_FINISHED_WITH_SUCCESS: - setStatusBitmap(L"status_finished_success", _("Synchronization completed successfully")); - break; - } - } - - //show status on Windows 7 taskbar - if (taskbar_.get()) - { - if (syncStat_) //sync running - { - if (paused_) - taskbar_->setStatus(Taskbar::STATUS_PAUSED); - else - switch (syncStat_->currentPhase()) - { - case ProcessCallback::PHASE_NONE: - case ProcessCallback::PHASE_SCANNING: - taskbar_->setStatus(Taskbar::STATUS_INDETERMINATE); - break; - - case ProcessCallback::PHASE_COMPARING_CONTENT: - case ProcessCallback::PHASE_SYNCHRONIZING: - taskbar_->setStatus(Taskbar::STATUS_NORMAL); - break; - } - } - else //sync finished - switch (finalResult) - { - case RESULT_ABORTED: - case RESULT_FINISHED_WITH_ERROR: - taskbar_->setStatus(Taskbar::STATUS_ERROR); - break; - - case RESULT_FINISHED_WITH_WARNINGS: - case RESULT_FINISHED_WITH_SUCCESS: - taskbar_->setStatus(Taskbar::STATUS_NORMAL); - break; - } - } - - //pause button - if (syncStat_) //sync running - pnl.m_buttonPause->SetLabel(paused_ ? _("&Continue") : _("&Pause")); - - pnl.Layout(); - this->Refresh(); //a few pixels below the status text need refreshing -} - - -template -void SyncProgressDialogImpl::closeWindowDirectly() //this should really be called: do not call back + schedule deletion -{ - paused_ = false; //you never know? - //ATTENTION: dialog may live a little longer, so watch callbacks! - //e.g. wxGTK calls OnIconize after wxWindow::Close() (better not ask why) and before physical destruction! => indirectly calls updateDialogStatus(), which reads syncStat_!!! - syncStat_ = nullptr; - abortCb_ = nullptr; - //resumeFromSystray(); -> NO, instead ~SyncProgressDialogImpl() makes sure that main dialog is shown again! - - this->Close(); //generate close event: do NOT destroy window unconditionally! -} - - -template -void SyncProgressDialogImpl::processHasFinished(SyncResult resultId, const ErrorLog& log) //essential to call this in StatusHandler derived class destructor -{ - //at the LATEST(!) to prevent access to currentStatusHandler - //enable okay and close events; may be set in this method ONLY - -#if (defined __WXGTK__ || defined __WXOSX__) - //In wxWidgets 2.9.3 upwards, the wxWindow::Reparent() below fails on GTK and OS X if window is frozen! http://forums.codeblocks.org/index.php?topic=13388.45 -#else - wxWindowUpdateLocker dummy(this); //badly needed on Windows -#endif - - paused_ = false; //you never know? - - //update numbers one last time (as if sync were still running) - notifyProgressChange(); //make one last graph entry at the *current* time - updateGuiInt(false); - - switch (syncStat_->currentPhase()) //no matter if paused or not - { - case ProcessCallback::PHASE_NONE: - case ProcessCallback::PHASE_SCANNING: - //set overall speed -> not needed - //items processed -> not needed - break; - - case ProcessCallback::PHASE_COMPARING_CONTENT: - case ProcessCallback::PHASE_SYNCHRONIZING: - { - const int itemsCurrent = syncStat_->getObjectsCurrent(syncStat_->currentPhase()); - const int itemsTotal = syncStat_->getObjectsTotal (syncStat_->currentPhase()); - const Int64 dataCurrent = syncStat_->getDataCurrent (syncStat_->currentPhase()); - const Int64 dataTotal = syncStat_->getDataTotal (syncStat_->currentPhase()); - assert(dataCurrent <= dataTotal); - - //set overall speed (instead of current speed) - const long timeDelta = timeElapsed.Time() - phaseStartMs; //we need to consider "time within current phase" not total "timeElapsed"! - - const wxString overallBytesPerSecond = timeDelta == 0 ? wxString() : filesizeToShortString(dataCurrent * 1000 / timeDelta) + _("/sec"); - const wxString overallItemsPerSecond = timeDelta == 0 ? wxString() : replaceCpy(_("%x items/sec"), L"%x", formatThreeDigitPrecision(itemsCurrent * 1000.0 / timeDelta)); - - pnl.m_panelGraphBytes->setAttributes(pnl.m_panelGraphBytes->getAttributes().setCornerText(overallBytesPerSecond, Graph2D::CORNER_TOP_LEFT)); - pnl.m_panelGraphItems->setAttributes(pnl.m_panelGraphItems->getAttributes().setCornerText(overallItemsPerSecond, Graph2D::CORNER_TOP_LEFT)); - - //show new element "items processed" - pnl.m_panelItemsProcessed->Show(); - pnl.m_staticTextProcessedObj ->SetLabel(toGuiString(itemsCurrent)); - pnl.m_staticTextDataProcessed->SetLabel(L"(" + filesizeToShortString(dataCurrent) + L")"); - - //hide remaining elements... - if (itemsCurrent == itemsTotal && //...if everything was processed successfully - dataCurrent == dataTotal) - pnl.m_panelItemsRemaining->Hide(); - } - break; - } - - //------- change class state ------- - finalResult = resultId; - - syncStat_ = nullptr; - abortCb_ = nullptr; - //---------------------------------- - - updateDialogStatus(); - setExternalStatus(getDialogPhaseText(syncStat_, paused_, finalResult), wxString()); - - resumeFromSystray(); //if in tray mode... - - this->EnableCloseButton(true); - - pnl.m_bpButtonMinimizeToTray->Hide(); - pnl.m_buttonStop->Disable(); - pnl.m_buttonStop->Hide(); - pnl.m_buttonPause->Disable(); - pnl.m_buttonPause->Hide(); - pnl.m_buttonClose->Show(); - pnl.m_buttonClose->Enable(); - - pnl.m_buttonClose->SetFocus(); - - pnl.bSizerExecFinished->Show(false); - - //set std order after button visibility was set - setStandardButtonOrder(*pnl.bSizerStdButtons, StdButtons().setAffirmative(pnl.m_buttonClose)); - - //hide current operation status - pnl.bSizerStatusText->Show(false); - - //show and prepare final statistics - pnl.m_notebookResult->Show(); - -#if defined ZEN_WIN || defined ZEN_LINUX - pnl.m_staticlineFooter->Hide(); //win: m_notebookResult already has a window frame -#endif - - //hide remaining time - pnl.m_panelTimeRemaining->Hide(); - - //1. re-arrange graph into results listbook - const bool wasDetached = pnl.bSizerRoot->Detach(pnl.m_panelProgress); - assert(wasDetached); - (void)wasDetached; - pnl.m_panelProgress->Reparent(pnl.m_notebookResult); - pnl.m_notebookResult->AddPage(pnl.m_panelProgress, _("Statistics"), true); - - //2. log file - const size_t posLog = 1; - assert(pnl.m_notebookResult->GetPageCount() == 1); - LogPanel* logPanel = new LogPanel(pnl.m_notebookResult, log); //owned by m_notebookResult - pnl.m_notebookResult->AddPage(logPanel, _("Log"), false); - //bSizerHoldStretch->Insert(0, logPanel, 1, wxEXPAND); - - //show log instead of graph if errors occurred! (not required for ignored warnings) - if (log.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR) > 0) - pnl.m_notebookResult->ChangeSelection(posLog); - - //GetSizer()->SetSizeHints(this); //~=Fit() //not a good idea: will shrink even if window is maximized or was enlarged by the user - pnl.Layout(); - - pnl.m_panelProgress->Layout(); - //small statistics panels: - pnl.m_panelItemsProcessed->Layout(); - pnl.m_panelItemsRemaining->Layout(); - //pnl.m_panelTimeRemaining->Layout(); - //pnl.m_panelTimeElapsed->Layout(); -> needed? - - //play (optional) sound notification after sync has completed -> only play when waiting on results dialog, seems to be pointless otherwise! - switch (finalResult) - { - case SyncProgressDialog::RESULT_ABORTED: - break; - case SyncProgressDialog::RESULT_FINISHED_WITH_ERROR: - case SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS: - case SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS: - { - const Zstring soundFile = getResourceDir() + Zstr("Sync_Complete.wav"); - if (fileExists(soundFile)) - wxSound::Play(utfCvrtTo(soundFile), wxSOUND_ASYNC); //warning: this may fail and show a wxWidgets error message! => must not play when running FFS as a service! - } - break; - } - - //Raise(); -> don't! user may be watching a movie in the meantime ;) note: resumeFromSystray() also calls Raise()! -} - - -template -void SyncProgressDialogImpl::OnOkay(wxCommandEvent& event) -{ - this->Close(); //generate close event: do NOT destroy window unconditionally! -} - - -template -void SyncProgressDialogImpl::OnCancel(wxCommandEvent& event) -{ - paused_ = false; - updateDialogStatus(); //update status + pause button - - //no Layout() or UI-update here to avoid cascaded Yield()-call - if (abortCb_) - abortCb_->requestAbortion(); -} - - -template -void SyncProgressDialogImpl::OnPause(wxCommandEvent& event) -{ - paused_ = !paused_; - updateDialogStatus(); //update status + pause button -} - - -template -void SyncProgressDialogImpl::OnClose(wxCloseEvent& event) -{ - //this event handler may be called *during* sync, e.g. due to a system shutdown (Windows), anytime (OS X) - //try to stop sync gracefully and cross fingers: - if (abortCb_) - abortCb_->requestAbortion(); - //Note: we must NOT veto dialog destruction, else we will cancel system shutdown if this dialog is application main window (like in batch mode) - - notifyWindowTerminate_(); //don't wait until delayed "Destroy()" finally calls destructor -> avoid calls to processHasFinished()/closeWindowDirectly() - - paused_ = false; //[!] we could be pausing here! - - //now that we notified window termination prematurely, and since processHasFinished()/closeWindowDirectly() won't be called, make sure we don't call back, too! - //e.g. the second notifyWindowTerminate_() in ~SyncProgressDialogImpl()!!! - syncStat_ = nullptr; - abortCb_ = nullptr; - - wereDead = true; - this->Destroy(); //wxWidgets OS X: simple "delete"!!!!!!! -} - - -template -void SyncProgressDialogImpl::OnIconize(wxIconizeEvent& event) -{ - /* - propagate progress dialog minimize/maximize to parent - ----------------------------------------------------- - Fedora/Debian/Ubuntu: - - wxDialog cannot be minimized - - worse, wxGTK sends stray iconize events *after* wxDialog::Destroy() - - worse, on Fedora an iconize event is issued directly after calling Close() - - worse, even wxDialog::Hide() causes iconize event! - => nothing to do - SUSE: - - wxDialog can be minimized (it just vanishes!) and in general also minimizes parent: except for our progress wxDialog!!! - - worse, wxDialog::Hide() causes iconize event - - probably the same issues with stray iconize events like Fedora/Debian/Ubuntu - - minimize button is always shown, even if wxMINIMIZE_BOX is omitted! - => nothing to do - Mac OS X: - - wxDialog can be minimized and automatically minimizes parent - - no iconize events seen by wxWidgets! - => nothing to do - Windows: - - wxDialog can be minimized but does not also minimize parent - - iconize events only seen for manual minimize - => propagate event to parent - */ -#ifdef ZEN_WIN - if (parentFrame_) - if (parentFrame_->IsIconized() != event.IsIconized()) //caveat: if window is maximized calling Iconize(false) will erroneously un-maximize! - parentFrame_->Iconize(event.IsIconized()); -#endif - event.Skip(); -} - - -template -void SyncProgressDialogImpl::minimizeToTray() -{ - if (!trayIcon.get()) - { - trayIcon = make_unique([this] { this->resumeFromSystray(); }); //FfsTrayIcon lifetime is a subset of "this"'s lifetime! - //we may destroy FfsTrayIcon even while in the FfsTrayIcon callback!!!! - - updateGuiInt(false); //set tray tooltip + progress: e.g. no updates while paused - - this->Hide(); - if (parentFrame_) - parentFrame_->Hide(); -#ifdef ZEN_MAC - //hide dock icon: else user is able to forcefully show the hidden main dialog by clicking on the icon!! - ProcessSerialNumber psn = { 0, kCurrentProcess }; - ::TransformProcessType(&psn, kProcessTransformToUIElementApplication); -#endif - } -} - - -template -void SyncProgressDialogImpl::resumeFromSystray() -{ - if (trayIcon) - { - trayIcon.reset(); - - if (parentFrame_) - { - //if (parentFrame_->IsIconized()) //caveat: if window is maximized calling Iconize(false) will erroneously un-maximize! - // parentFrame_->Iconize(false); - parentFrame_->Show(); - parentFrame_->Raise(); - } - - //if (IsIconized()) //caveat: if window is maximized calling Iconize(false) will erroneously un-maximize! - // Iconize(false); - this->Show(); - this->Raise(); - this->SetFocus(); - - updateDialogStatus(); //restore Windows 7 task bar status (e.g. required in pause mode) - updateGuiInt(false); //restore Windows 7 task bar progress (e.g. required in pause mode) - -#ifdef ZEN_MAC - ProcessSerialNumber psn = { 0, kCurrentProcess }; - ::SetFrontProcess(&psn); //call before TransformProcessType() so that OSX menu is updated correctly - //why isn't this covered by wxWindows::Raise()?? - ::TransformProcessType(&psn, kProcessTransformToForegroundApplication); //show dock icon again -#endif - } -} - -//######################################################################################## - -SyncProgressDialog* createProgressDialog(zen::AbortCallback& abortCb, - const std::function& notifyWindowTerminate, //note: user closing window cannot be prevented on OS X! (And neither on Windows during system shutdown!) - const zen::Statistics& syncStat, - wxFrame* parentWindow, //may be nullptr - bool showProgress, - const wxString& jobName, - const std::wstring& execWhenFinished, - std::vector& execFinishedHistory) -{ - if (parentWindow) //sync from GUI - return new SyncProgressDialogImpl(wxDEFAULT_DIALOG_STYLE | wxMAXIMIZE_BOX | wxMINIMIZE_BOX | wxRESIZE_BORDER, - [&](wxDialog& progDlg) { return parentWindow; }, abortCb, - notifyWindowTerminate, syncStat, parentWindow, showProgress, jobName, execWhenFinished, execFinishedHistory); - else //FFS batch job - { - auto dlg = new SyncProgressDialogImpl(wxDEFAULT_FRAME_STYLE, - [](wxFrame& progDlg) { return &progDlg; }, abortCb, - notifyWindowTerminate, syncStat, parentWindow, showProgress, jobName, execWhenFinished, execFinishedHistory); - - //only top level windows should have an icon: - dlg->SetIcon(getFfsIcon()); - return dlg; - } -} diff --git a/ui/progress_indicator.h b/ui/progress_indicator.h deleted file mode 100644 index e2dbb99c..00000000 --- a/ui/progress_indicator.h +++ /dev/null @@ -1,92 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef PROGRESSINDICATOR_H_INCLUDED -#define PROGRESSINDICATOR_H_INCLUDED - -#include -#include -//#include -#include -#include "../lib/status_handler.h" -//#include "main_dlg.h" - - -class CompareProgressDialog -{ -public: - CompareProgressDialog(wxFrame& parentWindow); //CompareProgressDialog will be owned by parentWindow! - - wxWindow* getAsWindow(); //convenience! don't abuse! - - void init(const zen::Statistics& syncStat); //begin of sync: make visible, set pointer to "syncStat", initialize all status values - void finalize(); //end of sync: hide again, clear pointer to "syncStat" - - void switchToCompareBytewise(); - void updateStatusPanelNow(); - -private: - class Pimpl; - Pimpl* const pimpl; -}; - - -//SyncStatusHandler will internally process Window messages => disable GUI controls to avoid unexpected callbacks! - -struct SyncProgressDialog -{ - enum SyncResult - { - RESULT_ABORTED, - RESULT_FINISHED_WITH_ERROR, - RESULT_FINISHED_WITH_WARNINGS, - RESULT_FINISHED_WITH_SUCCESS - }; - //essential to call one of these two methods in StatusUpdater derived class' destructor at the LATEST(!) - //to prevent access to callback to updater (e.g. request abort) - virtual void processHasFinished(SyncResult resultId, const zen::ErrorLog& log) = 0; //sync finished, still dialog may live on - virtual void closeWindowDirectly() = 0; //don't wait for user - - //--------------------------------------------------------------------------- - - virtual wxWindow* getWindowIfVisible() = 0; //may be nullptr; don't abuse, use as parent for modal dialogs only! - - virtual void initNewPhase() = 0; //call after "StatusHandler::initNewPhase" - virtual void notifyProgressChange() = 0; //throw (), required by graph! - virtual void updateGui() = 0; //update GUI and process Window messages - - virtual std::wstring getExecWhenFinishedCommand() const = 0; //final value (after possible user modification) - - virtual void stopTimer() = 0; //halt all internal timers! - virtual void resumeTimer() = 0; // - -protected: - ~SyncProgressDialog() {} -}; - - -SyncProgressDialog* createProgressDialog(zen::AbortCallback& abortCb, - const std::function& notifyWindowTerminate, //note: user closing window cannot be prevented on OS X! (And neither on Windows during system shutdown!) - const zen::Statistics& syncStat, - wxFrame* parentWindow, //may be nullptr - bool showProgress, - const wxString& jobName, - const std::wstring& execWhenFinished, - std::vector& execFinishedHistory); //changing parameter! -//DON'T delete the pointer! it will be deleted by the user clicking "OK/Cancel"/wxWindow::Destroy() after processHasFinished() or closeWindowDirectly() - - -class PauseTimers -{ -public: - PauseTimers(SyncProgressDialog& ss) : ss_(ss) { ss_.stopTimer(); } - ~PauseTimers() { ss_.resumeTimer(); } -private: - SyncProgressDialog& ss_; -}; - - -#endif // PROGRESSINDICATOR_H_INCLUDED diff --git a/ui/search.cpp b/ui/search.cpp deleted file mode 100644 index c834b934..00000000 --- a/ui/search.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "search.h" -#include - - -using namespace zen; - -namespace -{ -template -class ContainsMatch -{ -public: - ContainsMatch(const wxString& textToFind) : textToFind_(textToFind) {} - bool operator()(const wxString& phrase) const { return contains(phrase, textToFind_); } - -private: - wxString textToFind_; -}; - - -template <> -class ContainsMatch -{ -public: - ContainsMatch(const wxString& textToFind) : textToFind_(textToFind) { textToFind_.MakeUpper(); } - bool operator()(wxString&& phrase) const - { - //wxWidgets::MakeUpper() is inefficient! But performance is not THAT important for this high-level search functionality - phrase.MakeUpper(); - return contains(phrase, textToFind_); - } - -private: - wxString textToFind_; -}; - -//########################################################################################### - -template -ptrdiff_t findRow(const Grid& grid, //return -1 if no matching row found - const wxString& searchString, - size_t rowFirst, //specify area to search: - size_t rowLast) // [rowFirst, rowLast) -{ - if (auto prov = grid.getDataProvider()) - { - std::vector colAttr = grid.getColumnConfig(); - vector_remove_if(colAttr, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); - if (!colAttr.empty()) - { - const ContainsMatch containsMatch(searchString); - - for (size_t row = rowFirst; row < rowLast; ++row) - for (auto iterCol = colAttr.begin(); iterCol != colAttr.end(); ++iterCol) - if (containsMatch(prov->getValue(row, iterCol->type_))) - return row; - } - } - return -1; -} -} - - -std::pair zen::findGridMatch(const Grid& grid1, const Grid& grid2, const wxString& searchString, bool respectCase) -{ - const size_t rowCountL = grid1.getRowCount(); - const size_t rowCountR = grid2.getRowCount(); - auto cursorPos = grid1.getGridCursor(); //(row, component pos) - - std::pair result(nullptr, -1); - - size_t cursorRowL = cursorPos.first; - if (cursorRowL >= rowCountL) - cursorRowL = 0; - { - auto finishSearch = [&](const Grid& grid, size_t rowFirst, size_t rowLast) -> bool - { - const ptrdiff_t targetRow = respectCase ? - findRow( grid, searchString, rowFirst, rowLast) : - findRow(grid, searchString, rowFirst, rowLast); - if (targetRow >= 0) - { - result = std::make_pair(&grid, targetRow); - return true; - } - return false; - }; - - if (!finishSearch(grid1, cursorRowL + 1, rowCountL)) - if (!finishSearch(grid2, 0, rowCountR)) - finishSearch(grid1, 0, cursorRowL + 1); - } - return result; -} \ No newline at end of file diff --git a/ui/search.h b/ui/search.h deleted file mode 100644 index 1adf3d01..00000000 --- a/ui/search.h +++ /dev/null @@ -1,18 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef SEARCH_H_423905762345342526587 -#define SEARCH_H_423905762345342526587 - -#include - -namespace zen -{ -std::pair findGridMatch(const Grid& grid1, const Grid& grid2, const wxString& searchString, bool respectCase); -//returns (grid/row) where the value was found, (nullptr, -1) if not found -} - -#endif //SEARCH_H_423905762345342526587 diff --git a/ui/small_dlgs.cpp b/ui/small_dlgs.cpp deleted file mode 100644 index df2e3e7a..00000000 --- a/ui/small_dlgs.cpp +++ /dev/null @@ -1,1049 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "small_dlgs.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "gui_generated.h" -#include "custom_grid.h" -#include "../algorithm.h" -#include "../synchronization.h" -#include "../lib/help_provider.h" -#include "../lib/hard_filter.h" -#include "../version/version.h" - -using namespace zen; - - -class AboutDlg : public AboutDlgGenerated -{ -public: - AboutDlg(wxWindow* parent); - -private: - virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnOK (wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_OKAY); } - virtual void OnDonate(wxCommandEvent& event) { wxLaunchDefaultBrowser(L"http://freefilesync.sourceforge.net/donate.php"); } -}; - - -AboutDlg::AboutDlg(wxWindow* parent) : AboutDlgGenerated(parent) -{ - setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonClose)); - - setRelativeFontSize(*m_buttonDonate, 1.25); - - assert(m_buttonClose->GetId() == wxID_OK); //we cannot use wxID_CLOSE else Esc key won't work: yet another wxWidgets bug?? - - m_bitmap9 ->SetBitmap(getResourceImage(L"website")); - m_bitmap10->SetBitmap(getResourceImage(L"email")); - m_bitmap13->SetBitmap(getResourceImage(L"gpl")); - //m_bitmapSmiley->SetBitmap(getResourceImage(L"smiley")); - - m_animCtrlWink->SetAnimation(getResourceAnimation(L"wink")); - m_animCtrlWink->Play(); - - //create language credits - for (auto it = ExistingTranslations::get().begin(); it != ExistingTranslations::get().end(); ++it) - { - //flag - wxStaticBitmap* staticBitmapFlag = new wxStaticBitmap(m_scrolledWindowTranslators, wxID_ANY, getResourceImage(it->languageFlag), wxDefaultPosition, wxSize(-1, 11), 0 ); - fgSizerTranslators->Add(staticBitmapFlag, 0, wxALIGN_CENTER); - - //translator name - wxStaticText* staticTextTranslator = new wxStaticText(m_scrolledWindowTranslators, wxID_ANY, it->translatorName, wxDefaultPosition, wxDefaultSize, 0 ); - staticTextTranslator->Wrap(-1); - fgSizerTranslators->Add(staticTextTranslator, 0, wxALIGN_CENTER_VERTICAL); - - staticBitmapFlag ->SetToolTip(it->languageName); - staticTextTranslator->SetToolTip(it->languageName); - } - fgSizerTranslators->Fit(m_scrolledWindowTranslators); - -#ifdef ZEN_WIN - new zen::MouseMoveWindow(*this); //-> put *after* creating credits -#endif - - //build information - wxString build = __TDATE__; -#if wxUSE_UNICODE - build += L" - Unicode"; -#else - build += L" - ANSI"; -#endif //wxUSE_UNICODE - - //compile time info about 32/64-bit build - if (zen::is64BitBuild) - build += L" x64"; - else - build += L" x86"; - assert_static(zen::is32BitBuild || zen::is64BitBuild); - - GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() - - //generate logo: put *after* first Fit() - Layout(); //make sure m_panelLogo has final width (required by wxGTK) - - wxBitmap bmpLogo; - { - wxImage tmp = getResourceImage(L"logo").ConvertToImage(); - tmp.Resize(wxSize(GetClientSize().GetWidth(), tmp.GetHeight()), wxPoint(0, 0), 255, 255, 255); //enlarge to fit full width - bmpLogo = wxBitmap(tmp); - } - { - wxMemoryDC dc(bmpLogo); - - wxImage labelImage = createImageFromText(wxString(L"FreeFileSync ") + zen::currentVersion, - wxFont(wxNORMAL_FONT->GetPointSize() * 1.8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, L"Tahoma"), - *wxBLACK); //accessibility: align foreground/background colors! - wxImage buildImage = createImageFromText(replaceCpy(_("Build: %x"), L"%x", build), - *wxNORMAL_FONT, - *wxBLACK); - wxImage dynImage = stackImages(labelImage, buildImage, ImageStackLayout::VERTICAL, ImageStackAlignment::CENTER, 0); - - dc.DrawBitmap(dynImage, wxPoint((bmpLogo.GetWidth () - dynImage.GetWidth ()) / 2, - (bmpLogo.GetHeight() - dynImage.GetHeight()) / 2)); - } - m_bitmapLogo->SetBitmap(bmpLogo); - - GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() - //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! - - m_buttonClose->SetFocus(); //on GTK ESC is only associated with wxID_OK correctly if we set at least *any* focus at all!!! -} - - -void zen::showAboutDialog(wxWindow* parent) -{ - AboutDlg aboutDlg(parent); - aboutDlg.ShowModal(); -} - -//######################################################################################## - -class FilterDlg : public FilterDlgGenerated -{ -public: - FilterDlg(wxWindow* parent, - FilterConfig& filter, - const wxString& title); - ~FilterDlg() {} - -private: - virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnHelpShowExamples(wxHyperlinkEvent& event) { displayHelpEntry(L"html/Exclude Items.html", this); } - virtual void OnClear (wxCommandEvent& event); - virtual void OnOkay (wxCommandEvent& event); - virtual void OnUpdateChoice(wxCommandEvent& event) { updateGui(); } - virtual void OnUpdateNameFilter(wxCommandEvent& event) { updateGui(); } - - void updateGui(); - void setFilter(const FilterConfig& filter); - FilterConfig getFilter() const; - void onKeyEvent(wxKeyEvent& event); - - FilterConfig& outputRef; - - EnumDescrList enumTimeDescr; - EnumDescrList enumSizeDescr; -}; - - -FilterDlg::FilterDlg(wxWindow* parent, - FilterConfig& filter, - const wxString& title) : - FilterDlgGenerated(parent), - outputRef(filter) //just hold reference -{ -#ifdef ZEN_WIN - new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" -#endif - setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonOk).setCancel(m_buttonCancel)); - - SetTitle(title); - -#ifndef __WXGTK__ //wxWidgets holds portability promise by not supporting for multi-line controls...not - m_textCtrlInclude->SetMaxLength(0); //allow large filter entries! - m_textCtrlExclude->SetMaxLength(0); // -#endif - - m_textCtrlInclude->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(FilterDlg::onKeyEvent), nullptr, this); - m_textCtrlExclude->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(FilterDlg::onKeyEvent), nullptr, this); - - enumTimeDescr. - add(UTIME_NONE, L"(" + _("None") + L")"). //meta options should be enclosed in parentheses - add(UTIME_TODAY, _("Today")). - // add(UTIME_THIS_WEEK, _("This week")). - add(UTIME_THIS_MONTH, _("This month")). - add(UTIME_THIS_YEAR, _("This year")). - add(UTIME_LAST_X_DAYS, _("Last x days")); - - enumSizeDescr. - add(USIZE_NONE, L"(" + _("None") + L")"). //meta options should be enclosed in parentheses - add(USIZE_BYTE, _("Byte")). - add(USIZE_KB, _("KB")). - add(USIZE_MB, _("MB")); - - m_bitmapFilter->SetBitmap(getResourceImage(L"filter")); - - setFilter(filter); - - m_buttonOk->SetFocus(); - - GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() - //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! - - Layout(); -} - - -void FilterDlg::onKeyEvent(wxKeyEvent& event) -{ - const int keyCode = event.GetKeyCode(); - - if (event.ControlDown()) - switch (keyCode) - { - case 'A': //CTRL + A - if (auto textCtrl = dynamic_cast(event.GetEventObject())) - textCtrl->SetSelection(-1, -1); //select all - return; - } - event.Skip(); -} - - -void FilterDlg::updateGui() -{ - FilterConfig activeCfg = getFilter(); - - auto setStatusBitmap = [&](wxStaticBitmap& staticBmp, const wxString& bmpName, bool active) - { - if (active) - staticBmp.SetBitmap(getResourceImage(bmpName)); - else - staticBmp.SetBitmap(greyScale(getResourceImage(bmpName))); - }; - setStatusBitmap(*m_bitmapInclude, L"filter_include", !NameFilter::isNull(activeCfg.includeFilter, FilterConfig().excludeFilter)); - setStatusBitmap(*m_bitmapExclude, L"filter_exclude", !NameFilter::isNull(FilterConfig().includeFilter, activeCfg.excludeFilter)); - setStatusBitmap(*m_bitmapFilterDate, L"clock", activeCfg.unitTimeSpan != UTIME_NONE); - setStatusBitmap(*m_bitmapFilterSize, L"size", activeCfg.unitSizeMin != USIZE_NONE || activeCfg.unitSizeMax != USIZE_NONE); - - m_spinCtrlTimespan->Enable(activeCfg.unitTimeSpan == UTIME_LAST_X_DAYS); - m_spinCtrlMinSize ->Enable(activeCfg.unitSizeMin != USIZE_NONE); - m_spinCtrlMaxSize ->Enable(activeCfg.unitSizeMax != USIZE_NONE); - - m_buttonClear->Enable(!(activeCfg == FilterConfig())); -} - - -void FilterDlg::setFilter(const FilterConfig& filter) -{ - m_textCtrlInclude->ChangeValue(utfCvrtTo(filter.includeFilter)); - m_textCtrlExclude->ChangeValue(utfCvrtTo(filter.excludeFilter)); - - setEnumVal(enumTimeDescr, *m_choiceUnitTimespan, filter.unitTimeSpan); - setEnumVal(enumSizeDescr, *m_choiceUnitMinSize, filter.unitSizeMin); - setEnumVal(enumSizeDescr, *m_choiceUnitMaxSize, filter.unitSizeMax); - - m_spinCtrlTimespan->SetValue(static_cast(filter.timeSpan)); - m_spinCtrlMinSize ->SetValue(static_cast(filter.sizeMin)); - m_spinCtrlMaxSize ->SetValue(static_cast(filter.sizeMax)); - - updateGui(); -} - - -FilterConfig FilterDlg::getFilter() const -{ - return FilterConfig(utfCvrtTo(m_textCtrlInclude->GetValue()), - utfCvrtTo(m_textCtrlExclude->GetValue()), - m_spinCtrlTimespan->GetValue(), - getEnumVal(enumTimeDescr, *m_choiceUnitTimespan), - m_spinCtrlMinSize->GetValue(), - getEnumVal(enumSizeDescr, *m_choiceUnitMinSize), - m_spinCtrlMaxSize->GetValue(), - getEnumVal(enumSizeDescr, *m_choiceUnitMaxSize)); -} - - -void FilterDlg::OnClear(wxCommandEvent& event) -{ - setFilter(FilterConfig()); -} - - -void FilterDlg::OnOkay(wxCommandEvent& event) -{ - FilterConfig cfg = getFilter(); - - //parameter validation: - - //include filter must not be empty: - { - Zstring tmp = cfg.includeFilter; - trim(tmp); - if (tmp.empty()) - cfg.includeFilter = FilterConfig().includeFilter; //no need to show error message, just correct user input - } - - //apply config: - outputRef = cfg; - - //when leaving dialog: filter and redraw grid, if filter is active - EndModal(ReturnSmallDlg::BUTTON_OKAY); -} - - -ReturnSmallDlg::ButtonPressed zen::showFilterDialog(wxWindow* parent, FilterConfig& filter, const wxString& title) -{ - FilterDlg filterDlg(parent, - filter, - title); - return static_cast(filterDlg.ShowModal()); -} - -//######################################################################################## - -class DeleteDialog : public DeleteDlgGenerated -{ -public: - DeleteDialog(wxWindow* parent, - const std::vector& rowsOnLeft, - const std::vector& rowsOnRight, - bool& deleteOnBothSides, - bool& useRecycleBin); - -private: - virtual void OnOK(wxCommandEvent& event); - virtual void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnDelOnBothSides(wxCommandEvent& event); - virtual void OnUseRecycler(wxCommandEvent& event); - - void updateGui(); - - const std::vector& rowsToDeleteOnLeft; - const std::vector& rowsToDeleteOnRight; - bool& outRefdeleteOnBothSides; - bool& outRefuseRecycleBin; - const TickVal tickCountStartup; -}; - - -DeleteDialog::DeleteDialog(wxWindow* parent, - const std::vector& rowsOnLeft, - const std::vector& rowsOnRight, - bool& deleteOnBothSides, - bool& useRecycleBin) : - DeleteDlgGenerated(parent), - rowsToDeleteOnLeft(rowsOnLeft), - rowsToDeleteOnRight(rowsOnRight), - outRefdeleteOnBothSides(deleteOnBothSides), - outRefuseRecycleBin(useRecycleBin), - tickCountStartup(getTicks()) -{ -#ifdef ZEN_WIN - new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" -#endif - setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonOK).setCancel(m_buttonCancel)); - - setMainInstructionFont(*m_staticTextHeader); - - m_checkBoxDeleteBothSides->SetValue(deleteOnBothSides); - m_checkBoxUseRecycler->SetValue(useRecycleBin); - - //if both sides contain same rows this checkbox is superfluous - if (rowsToDeleteOnLeft == rowsToDeleteOnRight) - { - m_checkBoxDeleteBothSides->Show(false); - m_checkBoxDeleteBothSides->SetValue(true); - } - -#ifndef __WXGTK__ //wxWidgets holds portability promise by not supporting for multi-line controls...not - m_textCtrlFileList->SetMaxLength(0); //allow large entries! -#endif - - updateGui(); - - GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() - //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! - - Layout(); - - m_buttonOK->SetFocus(); -} - - -void DeleteDialog::updateGui() -{ -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! -#endif - - const std::pair delInfo = zen::deleteFromGridAndHDPreview(rowsToDeleteOnLeft, - rowsToDeleteOnRight, - m_checkBoxDeleteBothSides->GetValue()); - wxString header; - if (m_checkBoxUseRecycler->GetValue()) - { - header = _P("Do you really want to move the following item to the recycle bin?", - "Do you really want to move the following %x items to the recycle bin?", delInfo.second); - m_bitmapDeleteType->SetBitmap(getResourceImage(L"delete_recycler")); - m_buttonOK->SetLabel(_("Move")); //no access key needed: use ENTER! - } - else - { - header = _P("Do you really want to delete the following item?", - "Do you really want to delete the following %x items?", delInfo.second); - m_bitmapDeleteType->SetBitmap(getResourceImage(L"delete_permanently")); - m_buttonOK->SetLabel(_("Delete")); - } - m_staticTextHeader->SetLabel(header); - //it seems like Wrap() needs to be reapplied after SetLabel() - m_staticTextHeader->Wrap(460); - - const wxString& fileList = utfCvrtTo(delInfo.first); - m_textCtrlFileList->ChangeValue(fileList); - /* - There is a nasty bug on wxGTK under Ubuntu: If a multi-line wxTextCtrl contains so many lines that scrollbars are shown, - it re-enables all windows that are supposed to be disabled during the current modal loop! - This only affects Ubuntu/wxGTK! No such issue on Debian/wxGTK or Suse/wxGTK - => another Unity problem like the following? - http://trac.wxwidgets.org/ticket/14823 "Menu not disabled when showing modal dialogs in wxGTK under Unity" - */ - - Layout(); - Refresh(); //needed after m_buttonOK label change -} - - -void DeleteDialog::OnOK(wxCommandEvent& event) -{ - //additional safety net, similar to Windows Explorer: time delta between DEL and ENTER must be at least 50ms to avoid accidental deletion! - const TickVal now = getTicks(); //0 on error - std::int64_t tps = ticksPerSec(); // - if (now.isValid() && tickCountStartup.isValid() && tps != 0) - if (dist(tickCountStartup, now) * 1000 / tps < 50) - return; - - outRefuseRecycleBin = m_checkBoxUseRecycler->GetValue(); - if (rowsToDeleteOnLeft != rowsToDeleteOnRight) - outRefdeleteOnBothSides = m_checkBoxDeleteBothSides->GetValue(); - - EndModal(ReturnSmallDlg::BUTTON_OKAY); -} - -void DeleteDialog::OnDelOnBothSides(wxCommandEvent& event) -{ - updateGui(); -} - -void DeleteDialog::OnUseRecycler(wxCommandEvent& event) -{ - updateGui(); -} - - -ReturnSmallDlg::ButtonPressed zen::showDeleteDialog(wxWindow* parent, - const std::vector& rowsOnLeft, - const std::vector& rowsOnRight, - bool& deleteOnBothSides, - bool& useRecycleBin) -{ - DeleteDialog confirmDeletion(parent, - rowsOnLeft, - rowsOnRight, - deleteOnBothSides, - useRecycleBin); - return static_cast(confirmDeletion.ShowModal()); -} - -//######################################################################################## - -class SyncConfirmationDlg : public SyncConfirmationDlgGenerated -{ -public: - SyncConfirmationDlg(wxWindow* parent, - const wxString& variantName, - const zen::SyncStatistics& st, - bool& dontShowAgain); -private: - virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnStartSync(wxCommandEvent& event); - - bool& m_dontShowAgain; -}; - - -SyncConfirmationDlg::SyncConfirmationDlg(wxWindow* parent, - const wxString& variantName, - const SyncStatistics& st, - bool& dontShowAgain) : - SyncConfirmationDlgGenerated(parent), - m_dontShowAgain(dontShowAgain) -{ -#ifdef ZEN_WIN - new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" -#endif - setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonStartSync).setCancel(m_buttonCancel)); - - setMainInstructionFont(*m_staticTextHeader); - m_bitmapSync->SetBitmap(getResourceImage(L"sync")); - - m_staticTextVariant->SetLabel(variantName); - m_checkBoxDontShowAgain->SetValue(dontShowAgain); - - //update preview of item count and bytes to be transferred: - setText(*m_staticTextData, filesizeToShortString(st.getDataToProcess())); - if (st.getDataToProcess() == 0) - m_bitmapData->SetBitmap(greyScale(getResourceImage(L"data"))); - else - m_bitmapData->SetBitmap(getResourceImage(L"data")); - - auto setValue = [](wxStaticText& txtControl, int value, wxStaticBitmap& bmpControl, const wchar_t* bmpName) - { - setText(txtControl, toGuiString(value)); - - if (value == 0) - bmpControl.SetBitmap(greyScale(mirrorIfRtl(getResourceImage(bmpName)))); - else - bmpControl.SetBitmap(mirrorIfRtl(getResourceImage(bmpName))); - }; - - setValue(*m_staticTextCreateLeft, st.getCreate(), *m_bitmapCreateLeft, L"so_create_left_small"); - setValue(*m_staticTextUpdateLeft, st.getUpdate(), *m_bitmapUpdateLeft, L"so_update_left_small"); - setValue(*m_staticTextDeleteLeft, st.getDelete(), *m_bitmapDeleteLeft, L"so_delete_left_small"); - setValue(*m_staticTextCreateRight, st.getCreate(), *m_bitmapCreateRight, L"so_create_right_small"); - setValue(*m_staticTextUpdateRight, st.getUpdate(), *m_bitmapUpdateRight, L"so_update_right_small"); - setValue(*m_staticTextDeleteRight, st.getDelete(), *m_bitmapDeleteRight, L"so_delete_right_small"); - - m_panelStatistics->Layout(); - - GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() - //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! - - m_buttonStartSync->SetFocus(); -} - - -void SyncConfirmationDlg::OnStartSync(wxCommandEvent& event) -{ - m_dontShowAgain = m_checkBoxDontShowAgain->GetValue(); - EndModal(ReturnSmallDlg::BUTTON_OKAY); -} - - -ReturnSmallDlg::ButtonPressed zen::showSyncConfirmationDlg(wxWindow* parent, - const wxString& variantName, - const zen::SyncStatistics& statistics, - bool& dontShowAgain) -{ - SyncConfirmationDlg dlg(parent, - variantName, - statistics, - dontShowAgain); - return static_cast(dlg.ShowModal()); -} - -//######################################################################################## - -class CompareCfgDialog : public CmpCfgDlgGenerated -{ -public: - CompareCfgDialog(wxWindow* parent, CompConfig& cmpConfig, const wxString& caption); - -private: - virtual void OnOkay(wxCommandEvent& event); - virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnHelpComparisonSettings(wxHyperlinkEvent& event) { displayHelpEntry(L"html/Comparison Settings.html", this); } - - virtual void OnTimeSize(wxCommandEvent& event) { compareVar = CMP_BY_TIME_SIZE; updateGui(); } - virtual void OnContent (wxCommandEvent& event) { compareVar = CMP_BY_CONTENT; updateGui(); } - - virtual void OnTimeSizeDouble(wxMouseEvent& event); - virtual void OnContentDouble(wxMouseEvent& event); - - void updateGui(); - - CompConfig& cmpConfigOut; //for output only - CompareVariant compareVar; - zen::EnumDescrList enumDescrHandleSyml; -}; - - -CompareCfgDialog::CompareCfgDialog(wxWindow* parent, - CompConfig& cmpConfig, const wxString& title) : - CmpCfgDlgGenerated(parent), - cmpConfigOut(cmpConfig), - compareVar(cmpConfig.compareVar) -{ -#ifdef ZEN_WIN - new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" -#endif - setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonOkay).setCancel(m_buttonCancel)); - - setRelativeFontSize(*m_toggleBtnTimeSize, 1.25); - setRelativeFontSize(*m_toggleBtnContent, 1.25); - - SetTitle(title); - - enumDescrHandleSyml. - add(SYMLINK_EXCLUDE, _("Exclude")). - add(SYMLINK_USE_DIRECTLY, _("Direct")). - add(SYMLINK_FOLLOW_LINK, _("Follow")); - - setEnumVal(enumDescrHandleSyml, *m_choiceHandleSymlinks, cmpConfig.handleSymlinks); - - //move dialog up so that compare-config button and first config-variant are on same level - // Move(wxPoint(position.x, std::max(0, position.y - (m_buttonTimeSize->GetScreenPosition() - GetScreenPosition()).y))); - - updateGui(); - - GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() - //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! - - m_buttonOkay->SetFocus(); -} - - -void CompareCfgDialog::updateGui() -{ - //update toggle buttons -> they have no parameter-ownership at all! - m_toggleBtnTimeSize->SetValue(false); - m_toggleBtnContent ->SetValue(false); - - switch (compareVar) - { - case CMP_BY_TIME_SIZE: - m_toggleBtnTimeSize->SetValue(true); - break; - case CMP_BY_CONTENT: - m_toggleBtnContent->SetValue(true); - break; - } - - auto setBitmap = [](wxStaticBitmap& bmpCtrl, bool active, const wxBitmap& bmp) - { - if (active) - bmpCtrl.SetBitmap(bmp); - else - bmpCtrl.SetBitmap(greyScale(bmp)); - }; - setBitmap(*m_bitmapByTime, compareVar == CMP_BY_TIME_SIZE, getResourceImage(L"clock")); - setBitmap(*m_bitmapByContent, compareVar == CMP_BY_CONTENT, getResourceImage(L"cmpByContent")); -} - - -void CompareCfgDialog::OnOkay(wxCommandEvent& event) -{ - cmpConfigOut.compareVar = compareVar; - cmpConfigOut.handleSymlinks = getEnumVal(enumDescrHandleSyml, *m_choiceHandleSymlinks); - - EndModal(ReturnSmallDlg::BUTTON_OKAY); -} - - -void CompareCfgDialog::OnTimeSizeDouble(wxMouseEvent& event) -{ - wxCommandEvent dummy; - OnTimeSize(dummy); - OnOkay(dummy); -} - - -void CompareCfgDialog::OnContentDouble(wxMouseEvent& event) -{ - wxCommandEvent dummy; - OnContent(dummy); - OnOkay(dummy); -} - - -ReturnSmallDlg::ButtonPressed zen::showCompareCfgDialog(wxWindow* parent, CompConfig& cmpConfig, const wxString& title) -{ - CompareCfgDialog syncDlg(parent, cmpConfig, title); - return static_cast(syncDlg.ShowModal()); -} - -//######################################################################################## - -class GlobalSettingsDlg : public GlobalSettingsDlgGenerated -{ -public: - GlobalSettingsDlg(wxWindow* parent, xmlAccess::XmlGlobalSettings& globalSettings); - -private: - virtual void OnOkay(wxCommandEvent& event); - virtual void OnResetDialogs(wxCommandEvent& event); - virtual void OnDefault(wxCommandEvent& event); - virtual void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnAddRow(wxCommandEvent& event); - virtual void OnRemoveRow(wxCommandEvent& event); - virtual void OnHelpShowExamples(wxHyperlinkEvent& event) { displayHelpEntry(L"html/External Applications.html", this); } - void onResize(wxSizeEvent& event); - void updateGui(); - - virtual void OnToggleAutoRetryCount(wxCommandEvent& event) { updateGui(); } - - void setExtApp(const xmlAccess::ExternalApps& extApp); - xmlAccess::ExternalApps getExtApp(); - - xmlAccess::XmlGlobalSettings& settings; -}; - - -GlobalSettingsDlg::GlobalSettingsDlg(wxWindow* parent, xmlAccess::XmlGlobalSettings& globalSettings) : - GlobalSettingsDlgGenerated(parent), - settings(globalSettings) -{ -#ifdef ZEN_WIN - new zen::MouseMoveWindow(*this); //allow moving dialog by clicking (nearly) anywhere...; ownership passed to "this" -#endif - setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonOkay).setCancel(m_buttonCancel)); - - //setMainInstructionFont(*m_staticTextHeader); - - m_bitmapSettings ->SetBitmap (getResourceImage(L"settings")); - m_bpButtonAddRow ->SetBitmapLabel(getResourceImage(L"item_add")); - m_bpButtonRemoveRow ->SetBitmapLabel(getResourceImage(L"item_remove")); - setBitmapTextLabel(*m_buttonResetDialogs, getResourceImage(L"reset_dialogs").ConvertToImage(), m_buttonResetDialogs->GetLabel()); - - m_checkBoxFailSafe ->SetValue(globalSettings.failsafeFileCopy); - m_checkBoxCopyLocked ->SetValue(globalSettings.copyLockedFiles); - m_checkBoxCopyPermissions->SetValue(globalSettings.copyFilePermissions); - - m_spinCtrlAutoRetryCount->SetValue(globalSettings.automaticRetryCount); - m_spinCtrlAutoRetryDelay->SetValue(globalSettings.automaticRetryDelay); - - setExtApp(globalSettings.gui.externelApplications); - - updateGui(); - -#ifdef ZEN_WIN - m_checkBoxCopyPermissions->SetLabel(_("Copy NTFS permissions")); -#elif defined ZEN_LINUX || defined ZEN_MAC - bSizerLockedFiles->Show(false); -#endif - - const wxString toolTip = wxString(_("Integrate external applications into context menu. The following macros are available:")) + L"\n\n" + - L"%item_path% \t" + _("- full file or folder name") + L"\n" + - L"%item_folder% \t" + _("- folder part only") + L"\n" + - L"%item2_path% \t" + _("- Other side's counterpart to %item_path%") + L"\n" + - L"%item2_folder% \t" + _("- Other side's counterpart to %item_folder%"); - - m_gridCustomCommand->GetGridWindow()->SetToolTip(toolTip); - m_gridCustomCommand->GetGridColLabelWindow()->SetToolTip(toolTip); - m_gridCustomCommand->SetMargins(0, 0); - - GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() - //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! - - Layout(); - - //automatically fit column width to match total grid width - Connect(wxEVT_SIZE, wxSizeEventHandler(GlobalSettingsDlg::onResize), nullptr, this); - wxSizeEvent dummy; - onResize(dummy); - - m_buttonOkay->SetFocus(); -} - - -void GlobalSettingsDlg::onResize(wxSizeEvent& event) -{ - const int widthTotal = m_gridCustomCommand->GetGridWindow()->GetClientSize().GetWidth(); - - if (widthTotal >= 0 && m_gridCustomCommand->GetCols() == 2) - { - const int w0 = widthTotal * 2 / 5; //ratio 2 : 3 - const int w1 = widthTotal - w0; - m_gridCustomCommand->SetColumnWidth(0, w0); - m_gridCustomCommand->SetColumnWidth(1, w1); - - m_gridCustomCommand->Refresh(); //required on Ubuntu - } - - event.Skip(); -} - - -void GlobalSettingsDlg::updateGui() -{ - const bool autoRetryActive = m_spinCtrlAutoRetryCount->GetValue() > 0; - m_staticTextAutoRetryDelay->Enable(autoRetryActive); - m_spinCtrlAutoRetryDelay->Enable(autoRetryActive); -} - - -void GlobalSettingsDlg::OnOkay(wxCommandEvent& event) -{ - //write global settings only when okay-button is pressed! - settings.failsafeFileCopy = m_checkBoxFailSafe->GetValue(); - settings.copyLockedFiles = m_checkBoxCopyLocked->GetValue(); - settings.copyFilePermissions = m_checkBoxCopyPermissions->GetValue(); - - settings.automaticRetryCount = m_spinCtrlAutoRetryCount->GetValue(); - settings.automaticRetryDelay = m_spinCtrlAutoRetryDelay->GetValue(); - - settings.gui.externelApplications = getExtApp(); - - EndModal(ReturnSmallDlg::BUTTON_OKAY); -} - - -void GlobalSettingsDlg::OnResetDialogs(wxCommandEvent& event) -{ - switch (showConfirmationDialog(this, DialogInfoType::INFO, - PopupDialogCfg().setMainInstructions(_("Restore all hidden windows and warnings?")), - _("&Restore"))) - { - case ConfirmationButton::DO_IT: - settings.optDialogs.resetDialogs(); - break; - case ConfirmationButton::CANCEL: - break; - } -} - - -void GlobalSettingsDlg::OnDefault(wxCommandEvent& event) -{ - xmlAccess::XmlGlobalSettings defaultCfg; - - m_checkBoxFailSafe ->SetValue(defaultCfg.failsafeFileCopy); - m_checkBoxCopyLocked ->SetValue(defaultCfg.copyLockedFiles); - m_checkBoxCopyPermissions->SetValue(defaultCfg.copyFilePermissions); - - m_spinCtrlAutoRetryCount->SetValue(defaultCfg.automaticRetryCount); - m_spinCtrlAutoRetryDelay->SetValue(defaultCfg.automaticRetryDelay); - - setExtApp(defaultCfg.gui.externelApplications); - - updateGui(); -} - - -void GlobalSettingsDlg::setExtApp(const xmlAccess::ExternalApps& extApp) -{ - auto extAppTmp = extApp; - vector_remove_if(extAppTmp, [](decltype(extAppTmp[0])& entry) { return entry.first.empty() && entry.second.empty(); }); - - extAppTmp.resize(extAppTmp.size() + 1); //append empty row to facilitate insertions - - const int rowCount = m_gridCustomCommand->GetNumberRows(); - if (rowCount > 0) - m_gridCustomCommand->DeleteRows(0, rowCount); - - m_gridCustomCommand->AppendRows(static_cast(extAppTmp.size())); - for (auto it = extAppTmp.begin(); it != extAppTmp.end(); ++it) - { - const int row = it - extAppTmp.begin(); - m_gridCustomCommand->SetCellValue(row, 0, it->first); //description - m_gridCustomCommand->SetCellValue(row, 1, it->second); //commandline - } -} - - -xmlAccess::ExternalApps GlobalSettingsDlg::getExtApp() -{ - xmlAccess::ExternalApps output; - for (int i = 0; i < m_gridCustomCommand->GetNumberRows(); ++i) - { - auto description = copyStringTo(m_gridCustomCommand->GetCellValue(i, 0)); - auto commandline = copyStringTo(m_gridCustomCommand->GetCellValue(i, 1)); - - if (!description.empty() || !commandline.empty()) - output.push_back(std::make_pair(description, commandline)); - } - return output; -} - - -void GlobalSettingsDlg::OnAddRow(wxCommandEvent& event) -{ -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! -#endif - - const int selectedRow = m_gridCustomCommand->GetGridCursorRow(); - if (0 <= selectedRow && selectedRow < m_gridCustomCommand->GetNumberRows()) - m_gridCustomCommand->InsertRows(selectedRow); - else - m_gridCustomCommand->AppendRows(); -} - - -void GlobalSettingsDlg::OnRemoveRow(wxCommandEvent& event) -{ - if (m_gridCustomCommand->GetNumberRows() > 0) - { -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! -#endif - - const int selectedRow = m_gridCustomCommand->GetGridCursorRow(); - if (0 <= selectedRow && selectedRow < m_gridCustomCommand->GetNumberRows()) - m_gridCustomCommand->DeleteRows(selectedRow); - else - m_gridCustomCommand->DeleteRows(m_gridCustomCommand->GetNumberRows() - 1); - } -} - - -ReturnSmallDlg::ButtonPressed zen::showGlobalSettingsDlg(wxWindow* parent, xmlAccess::XmlGlobalSettings& globalSettings) -{ - GlobalSettingsDlg settingsDlg(parent, globalSettings); - return static_cast(settingsDlg.ShowModal()); -} - -//######################################################################################## - -class SelectTimespanDlg : public SelectTimespanDlgGenerated -{ -public: - SelectTimespanDlg(wxWindow* parent, Int64& timeFrom, Int64& timeTo); - -private: - virtual void OnOkay(wxCommandEvent& event); - virtual void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - - virtual void OnChangeSelectionFrom(wxCalendarEvent& event) - { - if (m_calendarFrom->GetDate() > m_calendarTo->GetDate()) - m_calendarTo->SetDate(m_calendarFrom->GetDate()); - } - virtual void OnChangeSelectionTo(wxCalendarEvent& event) - { - if (m_calendarFrom->GetDate() > m_calendarTo->GetDate()) - m_calendarFrom->SetDate(m_calendarTo->GetDate()); - } - - Int64& timeFrom_; - Int64& timeTo_; -}; - - -wxDateTime utcToLocalDateTime(time_t utcTime) -{ - //wxDateTime models local(!) time (in contrast to what documentation says), but this constructor takes time_t UTC - return wxDateTime(utcTime); -} - -time_t localDateTimeToUtc(const wxDateTime& localTime) -{ - return localTime.GetTicks(); -} - - -SelectTimespanDlg::SelectTimespanDlg(wxWindow* parent, Int64& timeFrom, Int64& timeTo) : - SelectTimespanDlgGenerated(parent), - timeFrom_(timeFrom), - timeTo_(timeTo) -{ -#ifdef ZEN_WIN - new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" -#endif - setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonOkay).setCancel(m_buttonCancel)); - - long style = wxCAL_SHOW_HOLIDAYS | wxCAL_SHOW_SURROUNDING_WEEKS; - -#ifdef ZEN_WIN - DWORD firstDayOfWeek = 0; - if (::GetLocaleInfo(LOCALE_USER_DEFAULT, //__in LCID Locale, - LOCALE_IFIRSTDAYOFWEEK | // first day of week specifier, 0-6, 0=Monday, 6=Sunday - LOCALE_RETURN_NUMBER, //__in LCTYPE LCType, - reinterpret_cast(&firstDayOfWeek), //__out LPTSTR lpLCData, - sizeof(firstDayOfWeek) / sizeof(TCHAR)) > 0 && //__in int cchData - firstDayOfWeek == 6) - style |= wxCAL_SUNDAY_FIRST; - else //default -#endif - style |= wxCAL_MONDAY_FIRST; - - m_calendarFrom->SetWindowStyleFlag(style); - m_calendarTo ->SetWindowStyleFlag(style); - - //set default values - if (timeTo_ == 0) - timeTo_ = wxGetUTCTime(); // - if (timeFrom_ == 0) - timeFrom_ = timeTo_ - 7 * 24 * 3600; //default time span: one week from "now" - - m_calendarFrom->SetDate(utcToLocalDateTime(to(timeFrom_))); - m_calendarTo ->SetDate(utcToLocalDateTime(to(timeTo_))); - -#if wxCHECK_VERSION(2, 9, 5) - //doesn't seem to be a problem here: -#else - //wxDatePickerCtrl::BestSize() does not respect year field and trims it, both wxMSW/wxGTK - why isn't there anybody testing this wxWidgets stuff??? - wxSize minSz = m_calendarFrom->GetBestSize(); - minSz.x += 30; - m_calendarFrom->SetMinSize(minSz); - m_calendarTo ->SetMinSize(minSz); -#endif - - GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() - //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! - - m_buttonOkay->SetFocus(); -} - - -void SelectTimespanDlg::OnOkay(wxCommandEvent& event) -{ - wxDateTime from = m_calendarFrom->GetDate(); - wxDateTime to = m_calendarTo ->GetDate(); - - //align to full days - from.ResetTime(); - to += wxTimeSpan::Day(); - to.ResetTime(); //reset local(!) time - to -= wxTimeSpan::Second(); //go back to end of previous day - - timeFrom_ = localDateTimeToUtc(from); - timeTo_ = localDateTimeToUtc(to); - - /* - { - time_t current = zen::to(timeFrom_); - struct tm* tdfewst = ::localtime(¤t); - int budfk = 3; - } - { - time_t current = zen::to(timeTo_); - struct tm* tdfewst = ::localtime(¤t); - int budfk = 3; - } - */ - - EndModal(ReturnSmallDlg::BUTTON_OKAY); -} - - -ReturnSmallDlg::ButtonPressed zen::showSelectTimespanDlg(wxWindow* parent, Int64& timeFrom, Int64& timeTo) -{ - SelectTimespanDlg timeSpanDlg(parent, timeFrom, timeTo); - return static_cast(timeSpanDlg.ShowModal()); -} diff --git a/ui/small_dlgs.h b/ui/small_dlgs.h deleted file mode 100644 index 311a7d14..00000000 --- a/ui/small_dlgs.h +++ /dev/null @@ -1,52 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef SMALLDIALOGS_H_INCLUDED -#define SMALLDIALOGS_H_INCLUDED - -#include -//#include "../file_hierarchy.h" -#include "../lib/process_xml.h" -#include "../synchronization.h" - -namespace zen -{ -//parent window, optional: support correct dialog placement above parent on multiple monitor systems - -struct ReturnSmallDlg -{ - enum ButtonPressed - { - BUTTON_CANCEL, - BUTTON_OKAY = 1 - }; -}; - -void showAboutDialog(wxWindow* parent); - -ReturnSmallDlg::ButtonPressed showFilterDialog(wxWindow* parent, FilterConfig& filter, const wxString& caption); - -ReturnSmallDlg::ButtonPressed showDeleteDialog(wxWindow* parent, - const std::vector& rowsOnLeft, - const std::vector& rowsOnRight, - bool& deleteOnBothSides, - bool& useRecycleBin); - -ReturnSmallDlg::ButtonPressed showSyncConfirmationDlg(wxWindow* parent, - const wxString& variantName, - const SyncStatistics& statistics, - bool& dontShowAgain); - -ReturnSmallDlg::ButtonPressed showCompareCfgDialog(wxWindow* parent, CompConfig& cmpConfig, const wxString& title); - -ReturnSmallDlg::ButtonPressed showGlobalSettingsDlg(wxWindow* parent, xmlAccess::XmlGlobalSettings& globalSettings); - -ReturnSmallDlg::ButtonPressed showSelectTimespanDlg(wxWindow* parent, Int64& timeFrom, Int64& timeTo); -} - -#endif // SMALLDIALOGS_H_INCLUDED - - diff --git a/ui/sorting.h b/ui/sorting.h deleted file mode 100644 index 46477d0f..00000000 --- a/ui/sorting.h +++ /dev/null @@ -1,193 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef SORTING_H_INCLUDED -#define SORTING_H_INCLUDED - -#include -#include -#include "../file_hierarchy.h" - -namespace zen -{ -namespace -{ -struct CompileTimeReminder : public FSObjectVisitor -{ - virtual void visit(const FilePair& fileObj) {} - virtual void visit(const SymlinkPair& linkObj) {} - virtual void visit(const DirPair& dirObj ) {} -} checkDymanicCasts; //just a compile-time reminder to manually check dynamic casts in this file when needed -} - -inline -bool isDirectoryPair(const FileSystemObject& fsObj) -{ - return dynamic_cast(&fsObj) != nullptr; -} - - -template inline -bool lessShortFileName(const FileSystemObject& a, const FileSystemObject& b) -{ - //presort types: first files, then directories then empty rows - if (a.isEmpty()) - return false; //empty rows always last - else if (b.isEmpty()) - return true; //empty rows always last - - - if (isDirectoryPair(a)) //sort directories by relative name - { - if (isDirectoryPair(b)) - return LessFilename()(a.getRelativeName(), b.getRelativeName()); - else - return false; - } - else - { - if (isDirectoryPair(b)) - return true; - else - return makeSortDirection(LessFilename(), Int2Type())(a.getShortName(), b.getShortName()); - } -} - - -template //side currently unused! -bool lessRelativeName(const FileSystemObject& a, const FileSystemObject& b) -{ - const bool isDirectoryA = isDirectoryPair(a); - const Zstring& relDirNameA = isDirectoryA ? - a.getObjRelativeName() : //directory - beforeLast(a.getObjRelativeName(), FILE_NAME_SEPARATOR); //returns empty string if ch not found - - const bool isDirectoryB = isDirectoryPair(b); - const Zstring& relDirNameB = isDirectoryB ? - b.getObjRelativeName() : //directory - beforeLast(b.getObjRelativeName(), FILE_NAME_SEPARATOR); //returns empty string if ch not found - - //compare relative names without filenames first - const int rv = cmpFileName(relDirNameA, relDirNameB); - if (rv != 0) - return makeSortDirection(std::less(), Int2Type())(rv, 0); - else //compare the filenames - { - if (isDirectoryB) //directories shall appear before files - return false; - else if (isDirectoryA) - return true; - - return LessFilename()(a.getObjShortName(), b.getObjShortName()); - } -} - - -template inline -bool lessFilesize(const FileSystemObject& a, const FileSystemObject& b) -{ - //empty rows always last - if (a.isEmpty()) - return false; - else if (b.isEmpty()) - return true; - - const bool isDirA = dynamic_cast(&a) != nullptr; - const bool isDirB = dynamic_cast(&b) != nullptr; - - //directories second last - if (isDirA) - return false; - else if (isDirB) - return true; - - const FilePair* fileObjA = dynamic_cast(&a); - const FilePair* fileObjB = dynamic_cast(&b); - - //then symlinks - if (!fileObjA) - return false; - else if (!fileObjB) - return true; - - //return list beginning with largest files first - return makeSortDirection(std::less(), Int2Type())(fileObjA->getFileSize(), fileObjB->getFileSize()); -} - - -template inline -bool lessFiletime(const FileSystemObject& a, const FileSystemObject& b) -{ - if (a.isEmpty()) - return false; //empty rows always last - else if (b.isEmpty()) - return true; //empty rows always last - - - const FilePair* fileObjA = dynamic_cast(&a); - const FilePair* fileObjB = dynamic_cast(&b); - - const SymlinkPair* linkObjA = dynamic_cast(&a); - const SymlinkPair* linkObjB = dynamic_cast(&b); - - if (!fileObjA && !linkObjA) - return false; //directories last - else if (!fileObjB && !linkObjB) - return true; //directories last - - zen::Int64 dateA = fileObjA ? fileObjA->getLastWriteTime() : linkObjA->getLastWriteTime(); - zen::Int64 dateB = fileObjB ? fileObjB->getLastWriteTime() : linkObjB->getLastWriteTime(); - - //return list beginning with newest files first - return makeSortDirection(std::less(), Int2Type())(dateA, dateB); -} - - -template inline -bool lessExtension(const FileSystemObject& a, const FileSystemObject& b) -{ - if (a.isEmpty()) - return false; //empty rows always last - else if (b.isEmpty()) - return true; //empty rows always last - - if (dynamic_cast(&a)) - return false; //directories last - else if (dynamic_cast(&b)) - return true; //directories last - - auto getExtension = [&](const FileSystemObject& fsObj) -> Zstring - { - const Zstring& shortName = fsObj.getShortName(); - const size_t pos = shortName.rfind(Zchar('.')); - return pos == Zstring::npos ? Zstring() : Zstring(shortName.c_str() + pos + 1); - }; - - return makeSortDirection(LessFilename(), Int2Type())(getExtension(a), getExtension(b)); -} - - -template inline -bool lessCmpResult(const FileSystemObject& a, const FileSystemObject& b) -{ - //presort result: equal shall appear at end of list - if (a.getCategory() == FILE_EQUAL) - return false; - if (b.getCategory() == FILE_EQUAL) - return true; - - return makeSortDirection(std::less(), Int2Type())(a.getCategory(), b.getCategory()); -} - - -template inline -bool lessSyncDirection(const FileSystemObject& a, const FileSystemObject& b) -{ - return makeSortDirection(std::less(), Int2Type())(a.getSyncOperation(), b.getSyncOperation()); -} -} - -#endif // SORTING_H_INCLUDED diff --git a/ui/switch_to_gui.h b/ui/switch_to_gui.h deleted file mode 100644 index 20fe81de..00000000 --- a/ui/switch_to_gui.h +++ /dev/null @@ -1,40 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef SWITCHTOGUI_H_INCLUDED -#define SWITCHTOGUI_H_INCLUDED - -#include "../lib/process_xml.h" -#include "main_dlg.h" //in "application.cpp" we have this dependency anyway! - -namespace zen -{ -//switch from FreeFileSync Batch to GUI modus: opens a new FreeFileSync GUI session asynchronously -class SwitchToGui -{ -public: - SwitchToGui(const Zstring& referenceFile, - const xmlAccess::XmlBatchConfig& batchCfg, - xmlAccess::XmlGlobalSettings& globalSettings) : - guiCfg(xmlAccess::convertBatchToGui(batchCfg)), - globalSettings_(globalSettings) - { - referenceFiles.push_back(referenceFile); - } - - void execute() const - { - MainDialog::create(guiCfg, referenceFiles, &globalSettings_, true); //new toplevel window - } - -private: - std::vector referenceFiles; - const xmlAccess::XmlGuiConfig guiCfg; - xmlAccess::XmlGlobalSettings& globalSettings_; -}; -} - -#endif // SWITCHTOGUI_H_INCLUDED diff --git a/ui/sync_cfg.cpp b/ui/sync_cfg.cpp deleted file mode 100644 index 38e583fc..00000000 --- a/ui/sync_cfg.cpp +++ /dev/null @@ -1,640 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "sync_cfg.h" -#include -//#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "gui_generated.h" -#include "exec_finished_box.h" -#include "dir_name.h" -#include "../file_hierarchy.h" -#include "../lib/help_provider.h" - -using namespace zen; -using namespace xmlAccess; - - -class SyncCfgDialog : public SyncCfgDlgGenerated -{ -public: - SyncCfgDialog(wxWindow* parent, - CompareVariant compareVar, - SyncConfig& syncCfg, - const wxString& caption, - xmlAccess::OnGuiError* handleError, // - ExecWhenFinishedCfg* execWhenFinished); //optional input parameter - -private: - virtual void OnSyncTwoWay(wxCommandEvent& event) { directionCfg.var = DirectionConfig::TWOWAY; updateGui(); } - virtual void OnSyncMirror(wxCommandEvent& event) { directionCfg.var = DirectionConfig::MIRROR; updateGui(); } - virtual void OnSyncUpdate(wxCommandEvent& event) { directionCfg.var = DirectionConfig::UPDATE; updateGui(); } - virtual void OnSyncCustom(wxCommandEvent& event) { directionCfg.var = DirectionConfig::CUSTOM; updateGui(); } - - virtual void OnToggleDetectMovedFiles(wxCommandEvent& event) { directionCfg.detectMovedFiles = !directionCfg.detectMovedFiles; updateGui(); } - - virtual void OnSyncTwoWayDouble(wxMouseEvent& event); - virtual void OnSyncMirrorDouble(wxMouseEvent& event); - virtual void OnSyncUpdateDouble(wxMouseEvent& event); - virtual void OnSyncCustomDouble(wxMouseEvent& event); - - virtual void OnExLeftSideOnly (wxCommandEvent& event); - virtual void OnExRightSideOnly(wxCommandEvent& event); - virtual void OnLeftNewer (wxCommandEvent& event); - virtual void OnRightNewer (wxCommandEvent& event); - virtual void OnDifferent (wxCommandEvent& event); - virtual void OnConflict (wxCommandEvent& event); - - virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSyncConfig::BUTTON_CANCEL); } - virtual void OnCancel(wxCommandEvent& event) { EndModal(ReturnSyncConfig::BUTTON_CANCEL); } - virtual void OnOkay (wxCommandEvent& event); - - virtual void OnParameterChange(wxCommandEvent& event) { updateGui(); } - - virtual void OnDeletionPermanent (wxCommandEvent& event) { handleDeletion = DELETE_PERMANENTLY; updateGui(); } - virtual void OnDeletionRecycler (wxCommandEvent& event) { handleDeletion = DELETE_TO_RECYCLER; updateGui(); } - virtual void OnDeletionVersioning (wxCommandEvent& event) { handleDeletion = DELETE_TO_VERSIONING; updateGui(); } - - virtual void OnErrorPopup (wxCommandEvent& event) { onGuiError = ON_GUIERROR_POPUP; updateGui(); } - virtual void OnErrorIgnore(wxCommandEvent& event) { onGuiError = ON_GUIERROR_IGNORE; updateGui(); } - - virtual void OnHelpVersioning(wxHyperlinkEvent& event) { displayHelpEntry(L"html/Versioning.html", this); } - - struct Config - { - SyncConfig syncCfg; - xmlAccess::OnGuiError onGuiError; - std::wstring onCompletion; - }; - void setConfig(const Config& cfg); - Config getConfig() const; - - void updateGui(); - - //parameters with ownership NOT within GUI controls! - DirectionConfig directionCfg; - DeletionPolicy handleDeletion; //use Recycler, delete permanently or move to user-defined location - OnGuiError onGuiError; - - //output data - SyncConfig& outSyncCfg; - xmlAccess::OnGuiError* outOptOnGuiError; - ExecWhenFinishedCfg* outOptExecWhenFinished; - - CompareVariant compareVar_; - DirectoryName versioningFolder; - - EnumDescrList enumVersioningStyle; -}; - - -void updateConfigIcons(const DirectionConfig& directionCfg, - wxBitmapButton* buttonLeftOnly, - wxBitmapButton* buttonRightOnly, - wxBitmapButton* buttonLeftNewer, - wxBitmapButton* buttonRightNewer, - wxBitmapButton* buttonDifferent, - wxBitmapButton* buttonConflict) -{ - if (directionCfg.var != DirectionConfig::TWOWAY) //automatic mode needs no sync-directions - { - const DirectionSet dirCfg = extractDirections(directionCfg); - - switch (dirCfg.exLeftSideOnly) - { - case SyncDirection::RIGHT: - buttonLeftOnly->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_create_right"))); - buttonLeftOnly->SetToolTip(getSyncOpDescription(SO_CREATE_NEW_RIGHT)); - break; - case SyncDirection::LEFT: - buttonLeftOnly->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_delete_left"))); - buttonLeftOnly->SetToolTip(getSyncOpDescription(SO_DELETE_LEFT)); - break; - case SyncDirection::NONE: - buttonLeftOnly->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_none"))); - buttonLeftOnly->SetToolTip(getSyncOpDescription(SO_DO_NOTHING)); - break; - } - - switch (dirCfg.exRightSideOnly) - { - case SyncDirection::RIGHT: - buttonRightOnly->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_delete_right"))); - buttonRightOnly->SetToolTip(getSyncOpDescription(SO_DELETE_RIGHT)); - break; - case SyncDirection::LEFT: - buttonRightOnly->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_create_left"))); - buttonRightOnly->SetToolTip(getSyncOpDescription(SO_CREATE_NEW_LEFT)); - break; - case SyncDirection::NONE: - buttonRightOnly->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_none"))); - buttonRightOnly->SetToolTip(getSyncOpDescription(SO_DO_NOTHING)); - break; - } - - switch (dirCfg.leftNewer) - { - case SyncDirection::RIGHT: - buttonLeftNewer->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_right"))); - buttonLeftNewer->SetToolTip(getSyncOpDescription(SO_OVERWRITE_RIGHT)); - break; - case SyncDirection::LEFT: - buttonLeftNewer->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_left"))); - buttonLeftNewer->SetToolTip(getSyncOpDescription(SO_OVERWRITE_LEFT)); - break; - case SyncDirection::NONE: - buttonLeftNewer->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_none"))); - buttonLeftNewer->SetToolTip(getSyncOpDescription(SO_DO_NOTHING)); - break; - } - - switch (dirCfg.rightNewer) - { - case SyncDirection::RIGHT: - buttonRightNewer->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_right"))); - buttonRightNewer->SetToolTip(getSyncOpDescription(SO_OVERWRITE_RIGHT)); - break; - case SyncDirection::LEFT: - buttonRightNewer->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_left"))); - buttonRightNewer->SetToolTip(getSyncOpDescription(SO_OVERWRITE_LEFT)); - break; - case SyncDirection::NONE: - buttonRightNewer->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_none"))); - buttonRightNewer->SetToolTip(getSyncOpDescription(SO_DO_NOTHING)); - break; - } - - switch (dirCfg.different) - { - case SyncDirection::RIGHT: - buttonDifferent->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_right"))); - buttonDifferent->SetToolTip(getSyncOpDescription(SO_OVERWRITE_RIGHT)); - break; - case SyncDirection::LEFT: - buttonDifferent->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_left"))); - buttonDifferent->SetToolTip(getSyncOpDescription(SO_OVERWRITE_LEFT)); - break; - case SyncDirection::NONE: - buttonDifferent->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_none"))); - buttonDifferent->SetToolTip(getSyncOpDescription(SO_DO_NOTHING)); - break; - } - - switch (dirCfg.conflict) - { - case SyncDirection::RIGHT: - buttonConflict->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_right"))); - buttonConflict->SetToolTip(getSyncOpDescription(SO_OVERWRITE_RIGHT)); - break; - case SyncDirection::LEFT: - buttonConflict->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_left"))); - buttonConflict->SetToolTip(getSyncOpDescription(SO_OVERWRITE_LEFT)); - break; - case SyncDirection::NONE: - buttonConflict->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"cat_conflict"))); //silent dependency to algorithm.cpp::Redetermine!!! - buttonConflict->SetToolTip(_("Leave as unresolved conflict")); - break; - } - } -} - - -SyncCfgDialog::SyncCfgDialog(wxWindow* parent, - CompareVariant compareVar, - SyncConfig& syncCfg, - const wxString& title, - xmlAccess::OnGuiError* handleError, - ExecWhenFinishedCfg* execWhenFinished) : - SyncCfgDlgGenerated(parent), - handleDeletion(DELETE_TO_RECYCLER), // - onGuiError(ON_GUIERROR_POPUP), //dummy init - outSyncCfg(syncCfg), - outOptOnGuiError(handleError), - outOptExecWhenFinished(execWhenFinished), - compareVar_(compareVar), - versioningFolder(*m_panelVersioning, *m_buttonSelectDirVersioning, *m_versioningFolder/*, m_staticTextResolvedPath*/) -{ -#ifdef ZEN_WIN - new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" -#endif - setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonOK).setCancel(m_buttonCancel)); - - SetTitle(title); - - //set icons for this dialog - m_bitmapLeftOnly ->SetBitmap(mirrorIfRtl(greyScale(getResourceImage(L"cat_left_only" )))); - m_bitmapRightOnly ->SetBitmap(mirrorIfRtl(greyScale(getResourceImage(L"cat_right_only" )))); - m_bitmapLeftNewer ->SetBitmap(mirrorIfRtl(greyScale(getResourceImage(L"cat_left_newer" )))); - m_bitmapRightNewer->SetBitmap(mirrorIfRtl(greyScale(getResourceImage(L"cat_right_newer")))); - m_bitmapDifferent ->SetBitmap(mirrorIfRtl(greyScale(getResourceImage(L"cat_different" )))); - m_bitmapConflict ->SetBitmap(mirrorIfRtl(greyScale(getResourceImage(L"cat_conflict" )))); - m_bitmapDatabase ->SetBitmap(getResourceImage(L"database")); - - m_toggleBtnTwoWay->SetLabel(L"<- " + _("Two way") + L" ->"); - m_toggleBtnMirror->SetLabel( _("Mirror") + L" ->>"); - m_toggleBtnUpdate->SetLabel( _("Update") + L" ->"); - - setRelativeFontSize(*m_toggleBtnTwoWay, 1.25); - setRelativeFontSize(*m_toggleBtnMirror, 1.25); - setRelativeFontSize(*m_toggleBtnUpdate, 1.25); - setRelativeFontSize(*m_toggleBtnCustom, 1.25); - - enumVersioningStyle. - add(VER_STYLE_REPLACE, _("Replace"), _("Move files and replace if existing")). - add(VER_STYLE_ADD_TIMESTAMP, _("Time stamp"), _("Append a timestamp to each file name")); - - //hide controls for optional parameters - if (!handleError && !execWhenFinished) //currently either both or neither are bound! - { - bSizerExtraConfig->Show(false); - Layout(); - } - - if (execWhenFinished) - m_comboBoxExecFinished->initHistory(*execWhenFinished->history, execWhenFinished->historyMax); - - Config newCfg = { syncCfg, - handleError ?* handleError : ON_GUIERROR_POPUP, - execWhenFinished ?* execWhenFinished->command : std::wstring() - }; - setConfig(newCfg); - - GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() - //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! - - m_buttonOK->SetFocus(); -} - -//################################################################################################################# - -void SyncCfgDialog::setConfig(const Config& cfg) -{ - directionCfg = cfg.syncCfg.directionCfg; //make working copy; ownership *not* on GUI - handleDeletion = cfg.syncCfg.handleDeletion; - - versioningFolder.setName(utfCvrtTo(cfg.syncCfg.versioningDirectory)); - setEnumVal(enumVersioningStyle, *m_choiceVersioningStyle, cfg.syncCfg.versioningStyle); - - ////map single parameter "version limit" to both checkbox and spin ctrl: - //m_checkBoxVersionsLimit->SetValue(cfg.syncCfg.versionCountLimit >= 0); - //m_spinCtrlVersionsLimit->SetValue(cfg.syncCfg.versionCountLimit >= 0 ? cfg.syncCfg.versionCountLimit : 10 /*SyncConfig().versionCountLimit*/); - - onGuiError = cfg.onGuiError; - - m_comboBoxExecFinished->setValue(cfg.onCompletion); - - updateGui(); -} - - -SyncCfgDialog::Config SyncCfgDialog::getConfig() const -{ - Config output; - - //write configuration to main dialog - output.syncCfg.directionCfg = directionCfg; - output.syncCfg.handleDeletion = handleDeletion; - output.syncCfg.versioningDirectory = utfCvrtTo(versioningFolder.getName()); - output.syncCfg.versioningStyle = getEnumVal(enumVersioningStyle, *m_choiceVersioningStyle), - - //get single parameter "version limit" from both checkbox and spin ctrl: - // output.syncCfg.versionCountLimit = m_checkBoxVersionsLimit->GetValue() ? m_spinCtrlVersionsLimit->GetValue() : -1; - - output.onGuiError = onGuiError; - - output.onCompletion = m_comboBoxExecFinished->getValue(); - return output; -} - - -void SyncCfgDialog::updateGui() -{ -#ifdef ZEN_WIN - wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X! - wxWindowUpdateLocker dummy2(m_panelVersioning); - wxWindowUpdateLocker dummy3(m_bpButtonLeftOnly); - wxWindowUpdateLocker dummy4(m_bpButtonRightOnly); - wxWindowUpdateLocker dummy5(m_bpButtonLeftNewer); - wxWindowUpdateLocker dummy6(m_bpButtonRightNewer); - wxWindowUpdateLocker dummy7(m_bpButtonDifferent); - wxWindowUpdateLocker dummy8(m_bpButtonConflict); -#endif - - const Config cfg = getConfig(); //resolve parameter ownership: some on GUI controls, others member variables - - updateConfigIcons(cfg.syncCfg.directionCfg, - m_bpButtonLeftOnly, - m_bpButtonRightOnly, - m_bpButtonLeftNewer, - m_bpButtonRightNewer, - m_bpButtonDifferent, - m_bpButtonConflict); - - //selecting "detect move files" does not always make sense: - m_checkBoxDetectMove->Enable(detectMovedFilesSelectable(directionCfg)); - m_checkBoxDetectMove->SetValue(detectMovedFilesEnabled(directionCfg)); //parameter NOT owned by checkbox! - - //display only relevant sync options - m_bitmapDatabase ->Show(cfg.syncCfg.directionCfg.var == DirectionConfig::TWOWAY); - sbSizerSyncDirections->Show(cfg.syncCfg.directionCfg.var != DirectionConfig::TWOWAY); - - switch (compareVar_) //sbSizerSyncDirections->Show resets child sizers! - { - case CMP_BY_TIME_SIZE: - bSizerDifferent ->Show(false); - break; - - case CMP_BY_CONTENT: - bSizerLeftNewer ->Show(false); - bSizerRightNewer->Show(false); - break; - } - bSizerConfig->Layout(); //[!] - - //update toggle buttons -> they have no parameter-ownership at all! - m_staticTextAutomatic->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); - m_staticTextMirror ->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); - m_staticTextUpdate ->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); - m_staticTextCustom ->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); - - m_toggleBtnTwoWay->SetValue(false); - m_toggleBtnMirror->SetValue(false); - m_toggleBtnUpdate->SetValue(false); - m_toggleBtnCustom->SetValue(false); - - switch (cfg.syncCfg.directionCfg.var) - { - case DirectionConfig::TWOWAY: - m_toggleBtnTwoWay->SetValue(true); - m_staticTextAutomatic->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - break; - case DirectionConfig::MIRROR: - m_toggleBtnMirror->SetValue(true); - m_staticTextMirror->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - break; - case DirectionConfig::UPDATE: - m_toggleBtnUpdate->SetValue(true); - m_staticTextUpdate->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - break; - case DirectionConfig::CUSTOM: - m_toggleBtnCustom->SetValue(true); - m_staticTextCustom->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - break; - } - - m_toggleBtnPermanent ->SetValue(false); - m_toggleBtnRecycler ->SetValue(false); - m_toggleBtnVersioning->SetValue(false); - switch (cfg.syncCfg.handleDeletion) - { - case DELETE_PERMANENTLY: - m_toggleBtnPermanent->SetValue(true); - break; - case DELETE_TO_RECYCLER: - m_toggleBtnRecycler->SetValue(true); - break; - case DELETE_TO_VERSIONING: - m_toggleBtnVersioning->SetValue(true); - break; - } - - const bool versioningSelected = cfg.syncCfg.handleDeletion == DELETE_TO_VERSIONING; - m_panelVersioning->Show(versioningSelected); - - if (versioningSelected) - { - updateTooltipEnumVal(enumVersioningStyle, *m_choiceVersioningStyle); - - const std::wstring pathSep = utfCvrtTo(FILE_NAME_SEPARATOR); - switch (cfg.syncCfg.versioningStyle) - { - case VER_STYLE_REPLACE: - setText(*m_staticTextNamingCvtPart1, pathSep + _("Folder") + pathSep + _("File") + L".doc"); - setText(*m_staticTextNamingCvtPart2Bold, L""); - setText(*m_staticTextNamingCvtPart3, L""); - break; - - case VER_STYLE_ADD_TIMESTAMP: - setText(*m_staticTextNamingCvtPart1, pathSep + _("Folder") + pathSep + _("File") + L".doc "); - setText(*m_staticTextNamingCvtPart2Bold, _("YYYY-MM-DD hhmmss")); - setText(*m_staticTextNamingCvtPart3, L".doc"); - break; - } - } - - //m_spinCtrlVersionsLimit->Enable(m_checkBoxVersionsLimit->GetValue()); //enabled status is *not* directly dependent from resolved config! (but transitively) - - m_toggleBtnErrorIgnore->SetValue(false); - m_toggleBtnErrorPopup ->SetValue(false); - switch (cfg.onGuiError) - { - case ON_GUIERROR_IGNORE: - m_toggleBtnErrorIgnore->SetValue(true); - break; - case ON_GUIERROR_POPUP: - m_toggleBtnErrorPopup->SetValue(true); - break; - } - - Layout(); - Refresh(); //removes a few artifacts when toggling display of versioning folder - - GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() - //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!! -} - - -void SyncCfgDialog::OnOkay(wxCommandEvent& event) -{ - const Config cfg = getConfig(); - - //parameter validation: - - //check if user-defined directory for deletion was specified - if (cfg.syncCfg.handleDeletion == zen::DELETE_TO_VERSIONING) - { - Zstring versioningDir = cfg.syncCfg.versioningDirectory; - trim(versioningDir); - if (versioningDir.empty()) - { - showNotificationDialog(this, DialogInfoType::INFO, PopupDialogCfg().setMainInstructions(_("Please enter a target folder for versioning."))); - //don't show error icon to follow "Windows' encouraging tone" - m_panelVersioning->SetFocus(); - return; - } - } - - //apply config: - outSyncCfg = cfg.syncCfg; - - if (outOptOnGuiError) - *outOptOnGuiError = cfg.onGuiError; - - if (outOptExecWhenFinished) - { - *outOptExecWhenFinished->command = cfg.onCompletion; - //a good place to commit current "on completion" history item - m_comboBoxExecFinished->addItemHistory(); - } - - EndModal(ReturnSyncConfig::BUTTON_OKAY); -} - - -void SyncCfgDialog::OnSyncTwoWayDouble(wxMouseEvent& event) -{ - wxCommandEvent dummy; - OnSyncTwoWay(dummy); - OnOkay(dummy); -} - -void SyncCfgDialog::OnSyncMirrorDouble(wxMouseEvent& event) -{ - wxCommandEvent dummy; - OnSyncMirror(dummy); - OnOkay(dummy); -} - -void SyncCfgDialog::OnSyncUpdateDouble(wxMouseEvent& event) -{ - wxCommandEvent dummy; - OnSyncUpdate(dummy); - OnOkay(dummy); -} - -void SyncCfgDialog::OnSyncCustomDouble(wxMouseEvent& event) -{ - wxCommandEvent dummy; - OnSyncCustom(dummy); - OnOkay(dummy); -} - -namespace -{ -void toggleSyncDirection(SyncDirection& current) -{ - switch (current) - { - case SyncDirection::RIGHT: - current = SyncDirection::LEFT; - break; - case SyncDirection::LEFT: - current = SyncDirection::NONE; - break; - case SyncDirection::NONE: - current = SyncDirection::RIGHT; - break; - } -} - - -void pressCustomDir(DirectionConfig& directionCfg, SyncDirection& syncdir) -{ - switch (directionCfg.var) - { - case DirectionConfig::TWOWAY: - assert(false); - break; - case DirectionConfig::MIRROR: - case DirectionConfig::UPDATE: - directionCfg.custom = extractDirections(directionCfg); - directionCfg.var = DirectionConfig::CUSTOM; - toggleSyncDirection(syncdir); - break; - case DirectionConfig::CUSTOM: - toggleSyncDirection(syncdir); - - //some config optimization: if custom settings happen to match "mirror" or "update", just switch variant - const DirectionSet setMirror = [] - { - DirectionConfig mirrorCfg; - mirrorCfg.var = DirectionConfig::MIRROR; - return extractDirections(mirrorCfg); - }(); - - const DirectionSet setUpdate = [] - { - DirectionConfig updateCfg; - updateCfg.var = DirectionConfig::UPDATE; - return extractDirections(updateCfg); - }(); - - const DirectionSet currentSet = extractDirections(directionCfg); - if (currentSet == setMirror) - directionCfg.var = DirectionConfig::MIRROR; - else if (currentSet == setUpdate) - directionCfg.var = DirectionConfig::UPDATE; - break; - } -} -} - -void SyncCfgDialog::OnExLeftSideOnly(wxCommandEvent& event ) -{ - pressCustomDir(directionCfg, directionCfg.custom.exLeftSideOnly); - updateGui(); -} - - -void SyncCfgDialog::OnExRightSideOnly(wxCommandEvent& event ) -{ - pressCustomDir(directionCfg, directionCfg.custom.exRightSideOnly); - updateGui(); -} - - -void SyncCfgDialog::OnLeftNewer(wxCommandEvent& event ) -{ - pressCustomDir(directionCfg, directionCfg.custom.leftNewer); - updateGui(); -} - - -void SyncCfgDialog::OnRightNewer(wxCommandEvent& event ) -{ - pressCustomDir(directionCfg, directionCfg.custom.rightNewer); - updateGui(); -} - - -void SyncCfgDialog::OnDifferent(wxCommandEvent& event ) -{ - pressCustomDir(directionCfg, directionCfg.custom.different); - updateGui(); -} - - -void SyncCfgDialog::OnConflict(wxCommandEvent& event) -{ - pressCustomDir(directionCfg, directionCfg.custom.conflict); - updateGui(); -} - - -ReturnSyncConfig::ButtonPressed zen::showSyncConfigDlg(wxWindow* parent, - CompareVariant compareVar, - SyncConfig& syncCfg, - const wxString& title, - xmlAccess::OnGuiError* handleError, // - ExecWhenFinishedCfg* execWhenFinished) //optional input parameter -{ - SyncCfgDialog syncDlg(parent, - compareVar, - syncCfg, - title, - handleError, - execWhenFinished); - return static_cast(syncDlg.ShowModal()); -} diff --git a/ui/sync_cfg.h b/ui/sync_cfg.h deleted file mode 100644 index e56a533b..00000000 --- a/ui/sync_cfg.h +++ /dev/null @@ -1,41 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef SYNCCONFIG_H_INCLUDED -#define SYNCCONFIG_H_INCLUDED - -#include -#include "../lib/process_xml.h" - - -namespace zen -{ -struct ReturnSyncConfig -{ - enum ButtonPressed - { - BUTTON_CANCEL, - BUTTON_OKAY - }; -}; - -struct ExecWhenFinishedCfg -{ - std::wstring* command; //*must* be bound! - std::vector* history; // - size_t historyMax; -}; - - -ReturnSyncConfig::ButtonPressed showSyncConfigDlg(wxWindow* parent, - CompareVariant compareVar, - SyncConfig& syncCfg, - const wxString& title, - xmlAccess::OnGuiError* handleError, // - ExecWhenFinishedCfg* execWhenFinished); //optional input parameter -} - -#endif // SYNCCONFIG_H_INCLUDED diff --git a/ui/taskbar.cpp b/ui/taskbar.cpp deleted file mode 100644 index e6aadc90..00000000 --- a/ui/taskbar.cpp +++ /dev/null @@ -1,176 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "taskbar.h" - -#ifdef ZEN_WIN -#include -#include -#include "Taskbar_Seven/taskbar.h" - -#elif defined HAVE_UBUNTU_UNITY -#include - -#elif defined ZEN_MAC -#include -#include -#include "osx_dock.h" -#endif - -using namespace zen; - - -#ifdef ZEN_WIN -using namespace tbseven; - - -class Taskbar::Pimpl //throw TaskbarNotAvailable -{ -public: - Pimpl(const wxFrame& window) : - assocWindow(window.GetHWND()), - setStatus_ (getDllName(), funName_setStatus), - setProgress_(getDllName(), funName_setProgress) - { - if (!assocWindow || !setProgress_ || !setStatus_) - throw TaskbarNotAvailable(); - - if (!zen::win7OrLater()) - throw TaskbarNotAvailable(); - } - - ~Pimpl() { setStatus_(assocWindow, tbseven::STATUS_NOPROGRESS); } - - void setStatus(Status status) - { - TaskBarStatus tbSevenStatus = tbseven::STATUS_NORMAL; - switch (status) - { - case Taskbar::STATUS_INDETERMINATE: - tbSevenStatus = tbseven::STATUS_INDETERMINATE; - break; - case Taskbar::STATUS_NORMAL: - tbSevenStatus = tbseven::STATUS_NORMAL; - break; - case Taskbar::STATUS_ERROR: - tbSevenStatus = tbseven::STATUS_ERROR; - break; - case Taskbar::STATUS_PAUSED: - tbSevenStatus = tbseven::STATUS_PAUSED; - break; - } - - setStatus_(assocWindow, tbSevenStatus); - } - - void setProgress(double fraction) - { - setProgress_(assocWindow, fraction * 100000, 100000); - } - -private: - void* assocWindow; //HWND - const DllFun setStatus_; - const DllFun setProgress_; -}; - -#elif defined HAVE_UBUNTU_UNITY //Ubuntu unity -namespace -{ -const char FFS_DESKTOP_FILE[] = "freefilesync.desktop"; -} - -class Taskbar::Pimpl //throw (TaskbarNotAvailable) -{ -public: - Pimpl(const wxFrame& window) : - tbEntry(unity_launcher_entry_get_for_desktop_id(FFS_DESKTOP_FILE)) - //tbEntry(unity_launcher_entry_get_for_app_uri("application://freefilesync.desktop")) - { - if (!tbEntry) - throw TaskbarNotAvailable(); - } - - ~Pimpl() { setStatus(STATUS_INDETERMINATE); } //it seems UnityLauncherEntry* does not need destruction - - void setStatus(Status status) - { - switch (status) - { - case Taskbar::STATUS_ERROR: - unity_launcher_entry_set_urgent(tbEntry, true); - break; - - case Taskbar::STATUS_INDETERMINATE: - unity_launcher_entry_set_urgent(tbEntry, false); - unity_launcher_entry_set_progress_visible(tbEntry, false); - break; - - case Taskbar::STATUS_NORMAL: - unity_launcher_entry_set_urgent(tbEntry, false); - unity_launcher_entry_set_progress_visible(tbEntry, true); - break; - - case Taskbar::STATUS_PAUSED: - unity_launcher_entry_set_urgent(tbEntry, false); - break; - } - } - - void setProgress(double fraction) - { - unity_launcher_entry_set_progress(tbEntry, fraction); - } - -private: - UnityLauncherEntry* tbEntry; -}; - -#elif defined ZEN_MAC -class Taskbar::Pimpl -{ -public: - Pimpl(const wxFrame& window) {} - - ~Pimpl() { setDockText(""); } - - void setStatus(Status status) {} - - void setProgress(double fraction) - { - //no decimal places to make output less noisy - setDockText((numberTo(numeric::round(fraction * 100.0)) + '%').c_str()); //no need to internationalize fraction!? - } - -private: - void setDockText(const char* str) - { - try - { - osx::dockIconSetText(str); //throw SysError - } - catch (const zen::SysError& e) { assert(false); } - } -}; - - -#else //no taskbar support -class Taskbar::Pimpl -{ -public: - Pimpl(const wxFrame& window) { throw TaskbarNotAvailable(); } - void setStatus(Status status) {} - void setProgress(double fraction) {} -}; -#endif - -//######################################################################################################## - -Taskbar::Taskbar(const wxFrame& window) : pimpl_(new Pimpl(window)) {} //throw TaskbarNotAvailable -Taskbar::~Taskbar() {} - -void Taskbar::setStatus(Status status) { pimpl_->setStatus(status); } -void Taskbar::setProgress(double fraction) { pimpl_->setProgress(fraction); } diff --git a/ui/taskbar.h b/ui/taskbar.h deleted file mode 100644 index 82e08656..00000000 --- a/ui/taskbar.h +++ /dev/null @@ -1,51 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef TASKBARPROGRESS_H_INCLUDED -#define TASKBARPROGRESS_H_INCLUDED - -#include -#include - -/* -Windows 7; show progress in windows superbar via ITaskbarList3 Interface: http://msdn.microsoft.com/en-us/library/dd391692(VS.85).aspx - -Ubuntu: use Unity interface (optional) - -Define HAVE_UBUNTU_UNITY and set: - Compiler flag: `pkg-config --cflags unity` - Linker flag: `pkg-config --libs unity` -*/ - -namespace zen -{ -class TaskbarNotAvailable {}; - -class Taskbar -{ -public: - Taskbar(const wxFrame& window); //throw TaskbarNotAvailable - ~Taskbar(); - - enum Status - { - STATUS_INDETERMINATE, - STATUS_NORMAL, - STATUS_ERROR, - STATUS_PAUSED - }; - - void setStatus(Status status); - void setProgress(double fraction); //between [0, 1] - -private: - class Pimpl; - std::unique_ptr pimpl_; -}; - -} - -#endif // TASKBARPROGRESS_H_INCLUDED diff --git a/ui/tray_icon.cpp b/ui/tray_icon.cpp deleted file mode 100644 index 28842a80..00000000 --- a/ui/tray_icon.cpp +++ /dev/null @@ -1,236 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "tray_icon.h" -#include -#include -#include -#include -#include //req. by Linux -#include -#include - -using namespace zen; - - -namespace -{ -void fillRange(wxImage& img, int pixelFirst, int pixelLast, const wxColor& col) //tolerant input range -{ - if (img.IsOk()) - { - const int width = img.GetWidth (); - const int height = img.GetHeight(); - - if (width > 0 && height > 0) - { - pixelFirst = std::max(pixelFirst, 0); - pixelLast = std::min(pixelLast, width * height); - - if (pixelFirst < pixelLast) - { - unsigned char* const bytesBegin = img.GetData() + pixelFirst * 3; - unsigned char* const bytesEnd = img.GetData() + pixelLast * 3; - - for (unsigned char* bytePos = bytesBegin; bytePos < bytesEnd; bytePos += 3) - { - bytePos[0] = col.Red (); - bytePos[1] = col.Green(); - bytePos[2] = col.Blue (); - } - - if (img.HasAlpha()) //make progress indicator fully opaque: - std::fill(img.GetAlpha() + pixelFirst, img.GetAlpha() + pixelLast, wxIMAGE_ALPHA_OPAQUE); - } - } - } -} - - -wxIcon generateProgressIcon(const wxImage& logo, double fraction) //generate icon with progress indicator -{ - if (!logo.IsOk() || logo.GetWidth() <= 0 || logo.GetHeight() <= 0) - return wxIcon(); - - const int pixelCount = logo.GetWidth() * logo.GetHeight(); - const int startFillPixel = numeric::confineCpy(numeric::round(fraction * pixelCount), 0, pixelCount); - - //minor optimization - static std::pair buffer = std::make_pair(-1, wxNullIcon); - - if (buffer.first != startFillPixel) - { - wxImage genImage(logo.Copy()); //workaround wxWidgets' screwed-up design from hell: their copy-construction implements reference-counting WITHOUT copy-on-write! - - //gradually make FFS icon brighter while nearing completion - zen::brighten(genImage, -200 * (1 - fraction)); - - //fill black border row - if (startFillPixel <= pixelCount - genImage.GetWidth()) - { - /* - -------- - ---bbbbb - bbbbSyyy S : start yellow remainder - yyyyyyyy - */ - int bStart = startFillPixel - genImage.GetWidth(); - if (bStart % genImage.GetWidth() != 0) //add one more black pixel, see ascii-art - --bStart; - fillRange(genImage, bStart, startFillPixel, *wxBLACK); - } - else if (startFillPixel < pixelCount) - { - //special handling for last row - /* - -------- - -------- - ---bbbbb - ---bSyyy S : start yellow remainder - */ - int bStart = startFillPixel - genImage.GetWidth() - 1; - int bEnd = (bStart / genImage.GetWidth() + 1) * genImage.GetWidth(); - - fillRange(genImage, bStart, bEnd, *wxBLACK); - fillRange(genImage, startFillPixel - 1, startFillPixel, *wxBLACK); - } - - //fill yellow remainder - fillRange(genImage, startFillPixel, pixelCount, wxColour(240, 200, 0)); - - buffer.second.CopyFromBitmap(wxBitmap(genImage)); - } - - return buffer.second; -} - -//------------------------------------------------------------------------------------------------ - -enum Selection -{ - CONTEXT_RESTORE = 1 //wxWidgets: "A MenuItem ID of zero does not work under Mac" -}; -} - - -class FfsTrayIcon::TaskBarImpl : public wxTaskBarIcon -{ -public: - TaskBarImpl(const std::function& onRequestResume) : onRequestResume_(onRequestResume) - { - Connect(wxEVT_TASKBAR_LEFT_DCLICK, wxEventHandler(TaskBarImpl::OnDoubleClick), nullptr, this); - - //Windows User Experience Guidelines: show the context menu rather than doing *nothing* on single left clicks; however: - //MSDN: "Double-clicking the left mouse button actually generates a sequence of four messages: WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, and WM_LBUTTONUP." - //Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/ms645606%28v=vs.85%29.aspx - //=> the only way to distinguish single left click and double-click is to wait wxSystemSettings::GetMetric(wxSYS_DCLICK_MSEC) (480ms) which is way too long! - } - - //virtual ~TaskBarImpl(){} - - void dontCallbackAnymore() { onRequestResume_ = nullptr; } - -private: - virtual wxMenu* CreatePopupMenu() - { - if (!onRequestResume_) - return nullptr; - - wxMenu* contextMenu = new wxMenu; - - wxMenuItem* defaultItem = new wxMenuItem(contextMenu, CONTEXT_RESTORE, _("&Restore")); - //wxWidgets font messup: - //1. font must be set *before* wxMenu::Append()! - //2. don't use defaultItem->GetFont(); making it bold creates a huge font size for some reason -#ifdef ZEN_WIN //no wxMenuItem::SetFont() on Linux and OS X: wasn't wxWidgets supposed to be *portable* at some point in time????? - defaultItem->SetFont(wxNORMAL_FONT->Bold()); //make default selection bold/align with double-click -#endif - contextMenu->Append(defaultItem); - - //event handling - contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TaskBarImpl::OnContextMenuSelection), nullptr, this); - - return contextMenu; //ownership transferred to caller - } - - void OnContextMenuSelection(wxCommandEvent& event) - { - switch (static_cast(event.GetId())) - { - case CONTEXT_RESTORE: - if (onRequestResume_) - onRequestResume_(); - break; - } - } - - void OnDoubleClick(wxEvent& event) - { - if (onRequestResume_) - onRequestResume_(); - } - - //void OnLeftDownClick(wxEvent& event) - //{ - // //copied from wxTaskBarIconBase::OnRightButtonDown() - // if (wxMenu* menu = CreatePopupMenu()) - // { - // PopupMenu(menu); - // delete menu; - // } - //} - - std::function onRequestResume_; -}; - - -FfsTrayIcon::FfsTrayIcon(const std::function& onRequestResume) : - trayIcon(new TaskBarImpl(onRequestResume)), - activeFraction(1), //show FFS logo by default -#if defined ZEN_WIN || defined ZEN_MAC //16x16 seems to be the only size that is shown correctly on OS X - logo(getResourceImage(L"FFS_tray_16x16").ConvertToImage()) -#elif defined ZEN_LINUX - logo(getResourceImage(L"FFS_tray_24x24").ConvertToImage()) -#endif -{ - trayIcon->SetIcon(generateProgressIcon(logo, activeFraction), L"FreeFileSync"); -} - - -FfsTrayIcon::~FfsTrayIcon() -{ - trayIcon->dontCallbackAnymore(); //TaskBarImpl has longer lifetime than FfsTrayIcon: avoid callback! - - /* - This is not working correctly on OS X! It seems both wxTaskBarIcon::RemoveIcon() and ~wxTaskBarIcon() are broken and do NOT immediately - remove the icon from the system tray! Only some time later in the event loop which called these functions they will be removed. - Maybe some system component has still shared ownership? Objective C auto release pools are freed at the end of the current event loop... - Anyway, wxWidgets fails to disconnect the wxTaskBarIcon event handlers before calling "[m_statusitem release]"! - - => !!!clicking on the icon after ~wxTaskBarIcon ran crashes the application!!! - - - if ~wxTaskBarIcon() ran from the SyncProgressDialog::updateGui() event loop (e.g. user manually clicking the icon) => icon removed on return - - if ~wxTaskBarIcon() ran from SyncProgressDialog::closeWindowDirectly() => leaves the icon dangling until user closes this dialog and outter event loop runs! - */ - - trayIcon->RemoveIcon(); //required on Windows: unlike on OS X, wxPendingDelete does not kick in before main event loop! - //use wxWidgets delayed destruction: delete during next idle loop iteration (handle late window messages, e.g. when double-clicking) - wxPendingDelete.Append(trayIcon); //identical to wxTaskBarIconBase::Destroy() in wxWidgets 2.9.5 -} - - -void FfsTrayIcon::setToolTip(const wxString& toolTip) -{ - activeToolTip = toolTip; - trayIcon->SetIcon(generateProgressIcon(logo, activeFraction), activeToolTip); //another wxWidgets design bug: non-orthogonal method! -} - - -void FfsTrayIcon::setProgress(double fraction) -{ - activeFraction = fraction; - trayIcon->SetIcon(generateProgressIcon(logo, activeFraction), activeToolTip); -} diff --git a/ui/tray_icon.h b/ui/tray_icon.h deleted file mode 100644 index 24c97eb0..00000000 --- a/ui/tray_icon.h +++ /dev/null @@ -1,45 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef TRAYICON_H_84217830427534285 -#define TRAYICON_H_84217830427534285 - -#include -#include - -/* -show tray icon with progress during lifetime of this instance - -ATTENTION: wxWidgets never assumes that an object indirectly destroys itself while processing an event! - this includes wxEvtHandler-derived objects!!! - it seems ProcessEvent() works (on Windows), but AddPendingEvent() will crash since it uses "this" after the event processing! - -=> don't derive from wxEvtHandler or any other wxWidgets object here!!!!!! -=> use simple std::function as callback instead => instance may now be safely deleted in callback! -*/ - -class FfsTrayIcon -{ -public: - FfsTrayIcon(const std::function& onRequestResume); //callback only held during lifetime of this instance - ~FfsTrayIcon(); - - void setToolTip(const wxString& toolTip); - void setProgress(double fraction); //number between [0, 1], for small progress indicator - -private: - FfsTrayIcon(const FfsTrayIcon&); //=delete - FfsTrayIcon& operator=(const FfsTrayIcon&); //=delete - - class TaskBarImpl; - TaskBarImpl* trayIcon; - - wxString activeToolTip; - double activeFraction; - wxImage logo; -}; - -#endif //TRAYICON_H_84217830427534285 diff --git a/ui/tree_view.cpp b/ui/tree_view.cpp deleted file mode 100644 index 01eb8696..00000000 --- a/ui/tree_view.cpp +++ /dev/null @@ -1,1334 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include -#include "tree_view.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../lib/icon_buffer.h" - -using namespace zen; - - -inline -void TreeView::compressNode(Container& cont) //remove single-element sub-trees -> gain clarity + usability (call *after* inclusion check!!!) -{ - if (cont.subDirs.empty() || //single files node or... - (cont.firstFileId == nullptr && //single dir node... - cont.subDirs.size() == 1 && // - cont.subDirs[0].firstFileId == nullptr && //...that is empty - cont.subDirs[0].subDirs.empty())) // - { - cont.subDirs.clear(); - cont.firstFileId = nullptr; - } -} - - -template //(const FileSystemObject&) -> bool -void TreeView::extractVisibleSubtree(HierarchyObject& hierObj, //in - TreeView::Container& cont, //out - Function pred) -{ - auto getBytes = [](const FilePair& fileObj) -> UInt64 //MSVC screws up miserably if we put this lambda into std::for_each - { - //give accumulated bytes the semantics of a sync preview! - if (fileObj.isActive()) - switch (fileObj.getSyncDir()) - { - case SyncDirection::LEFT: - return fileObj.getFileSize(); - case SyncDirection::RIGHT: - return fileObj.getFileSize(); - case SyncDirection::NONE: - break; - } - return std::max(fileObj.getFileSize(), fileObj.getFileSize()); - }; - - cont.firstFileId = nullptr; - for (FilePair& fileObj : hierObj.refSubFiles()) - if (pred(fileObj)) - { - cont.bytesNet += getBytes(fileObj); - ++cont.itemCountNet; - - if (!cont.firstFileId) - cont.firstFileId = fileObj.getId(); - } - - for (SymlinkPair& linkObj : hierObj.refSubLinks()) - if (pred(linkObj)) - { - ++cont.itemCountNet; - - if (!cont.firstFileId) - cont.firstFileId = linkObj.getId(); - } - - cont.bytesGross += cont.bytesNet; - cont.itemCountGross += cont.itemCountNet; - - cont.subDirs.reserve(hierObj.refSubDirs().size()); //avoid expensive reallocations! - - for (DirPair& subDirObj : hierObj.refSubDirs()) - { - const bool included = pred(subDirObj); - - cont.subDirs.push_back(TreeView::DirNodeImpl()); // - auto& subDirView = cont.subDirs.back(); - TreeView::extractVisibleSubtree(subDirObj, subDirView, pred); - if (included) - ++subDirView.itemCountGross; - - cont.bytesGross += subDirView.bytesGross; - cont.itemCountGross += subDirView.itemCountGross; - - if (!included && !subDirView.firstFileId && subDirView.subDirs.empty()) - cont.subDirs.pop_back(); - else - { - subDirView.objId = subDirObj.getId(); - compressNode(subDirView); - } - } -} - - -namespace -{ -//generate nice percentage numbers which precisely sum up to 100 -void calcPercentage(std::vector>& workList) -{ - const UInt64 total = std::accumulate(workList.begin(), workList.end(), UInt64(), - [](UInt64 sum, const std::pair& pair) { return sum + pair.first; }); - - if (total == 0U) //this case doesn't work with the error minimizing algorithm below - { - for (std::pair& pair : workList) - *pair.second = 0; - return; - } - - int remainingPercent = 100; - for (std::pair& pair : workList) - { - *pair.second = to(pair.first * 100U / total); //round down - remainingPercent -= *pair.second; - } - assert(remainingPercent >= 0); - assert(remainingPercent < static_cast(workList.size())); - - //distribute remaining percent so that overall error is minimized as much as possible: - remainingPercent = std::min(remainingPercent, static_cast(workList.size())); - if (remainingPercent > 0) - { - std::nth_element(workList.begin(), workList.begin() + remainingPercent - 1, workList.end(), - [total](const std::pair& lhs, const std::pair& rhs) - { - return lhs.first * 100U % total > rhs.first * 100U % total; - }); - - std::for_each(workList.begin(), workList.begin() + remainingPercent, [&](std::pair& pair) { ++*pair.second; }); - } -} -} - - -template -struct TreeView::LessShortName -{ - bool operator()(const TreeLine& lhs, const TreeLine& rhs) - { - //files last (irrespective of sort direction) - if (lhs.type_ == TreeView::TYPE_FILES) - return false; - else if (rhs.type_ == TreeView::TYPE_FILES) - return true; - - if (lhs.type_ != rhs.type_) // - return lhs.type_ < rhs.type_; //shouldn't happen! Root nodes are never sorted - - switch (lhs.type_) - { - case TreeView::TYPE_ROOT: - return false; - - case TreeView::TYPE_DIRECTORY: - { - const auto* dirObjL = dynamic_cast(FileSystemObject::retrieve(static_cast(lhs.node_)->objId)); - const auto* dirObjR = dynamic_cast(FileSystemObject::retrieve(static_cast(rhs.node_)->objId)); - - if (!dirObjL) //might be pathologic, but it's covered - return false; - else if (!dirObjR) - return true; - - return makeSortDirection(LessFilename(), Int2Type())(dirObjL->getObjShortName(), dirObjR->getObjShortName()); - } - - case TreeView::TYPE_FILES: - break; - } - assert(false); - return false; //:= all equal - } -}; - - -template -void TreeView::sortSingleLevel(std::vector& items, ColumnTypeNavi columnType) -{ - auto getBytes = [](const TreeLine& line) -> UInt64 - { - switch (line.type_) - { - case TreeView::TYPE_ROOT: - case TreeView::TYPE_DIRECTORY: - return line.node_->bytesGross; - case TreeView::TYPE_FILES: - return line.node_->bytesNet; - } - assert(false); - return 0U; - }; - - auto getCount = [](const TreeLine& line) -> int - { - switch (line.type_) - { - case TreeView::TYPE_ROOT: - case TreeView::TYPE_DIRECTORY: - return line.node_->itemCountGross; - - case TreeView::TYPE_FILES: - return line.node_->itemCountNet; - } - assert(false); - return 0; - }; - - const auto lessBytes = [&](const TreeLine& lhs, const TreeLine& rhs) { return getBytes(lhs) < getBytes(rhs); }; - const auto lessCount = [&](const TreeLine& lhs, const TreeLine& rhs) { return getCount(lhs) < getCount(rhs); }; - - switch (columnType) - { - case COL_TYPE_NAVI_BYTES: - std::sort(items.begin(), items.end(), makeSortDirection(lessBytes, Int2Type())); - break; - - case COL_TYPE_NAVI_DIRECTORY: - std::sort(items.begin(), items.end(), LessShortName()); - break; - - case COL_TYPE_NAVI_ITEM_COUNT: - std::sort(items.begin(), items.end(), makeSortDirection(lessCount, Int2Type())); - break; - } -} - - -void TreeView::getChildren(const Container& cont, unsigned int level, std::vector& output) -{ - output.clear(); - output.reserve(cont.subDirs.size() + 1); //keep pointers in "workList" valid - std::vector> workList; - - std::for_each(cont.subDirs.begin(), cont.subDirs.end(), - [&output, level, &workList](const DirNodeImpl& subDir) - { - output.push_back(TreeView::TreeLine(level, 0, &subDir, TreeView::TYPE_DIRECTORY)); - workList.push_back(std::make_pair(subDir.bytesGross, &output.back().percent_)); - }); - - if (cont.firstFileId) - { - output.push_back(TreeLine(level, 0, &cont, TreeView::TYPE_FILES)); - workList.push_back(std::make_pair(cont.bytesNet, &output.back().percent_)); - } - calcPercentage(workList); - - if (sortAscending) - sortSingleLevel(output, sortColumn); - else - sortSingleLevel(output, sortColumn); -} - - -void TreeView::applySubView(std::vector&& newView) -{ - //preserve current node expansion status - auto getHierAlias = [](const TreeView::TreeLine& tl) -> const HierarchyObject* - { - switch (tl.type_) - { - case TreeView::TYPE_ROOT: - return static_cast(tl.node_)->baseDirObj.get(); - - case TreeView::TYPE_DIRECTORY: - if (auto dirObj = dynamic_cast(FileSystemObject::retrieve(static_cast(tl.node_)->objId))) - return dirObj; - break; - - case TreeView::TYPE_FILES: - break; //none!!! - } - return nullptr; - }; - - zen::hash_set expandedNodes; - if (!flatTree.empty()) - { - auto it = flatTree.begin(); - for (auto iterNext = flatTree.begin() + 1; iterNext != flatTree.end(); ++iterNext, ++it) - if (it->level_ < iterNext->level_) - if (auto hierObj = getHierAlias(*it)) - expandedNodes.insert(hierObj); - } - - //update view on full data - folderCmpView.swap(newView); //newView may be an alias for folderCmpView! see sorting! - - //set default flat tree - flatTree.clear(); - - if (folderCmp.size() == 1) //single folder pair case (empty pairs were already removed!) do NOT use folderCmpView for this check! - { - if (!folderCmpView.empty()) //it may really be! - getChildren(folderCmpView[0], 0, flatTree); //do not show root - } - else - { - std::vector> workList; - flatTree.reserve(folderCmpView.size()); //keep pointers in "workList" valid - - std::for_each(folderCmpView.begin(), folderCmpView.end(), - [&](const RootNodeImpl& root) - { - flatTree.push_back(TreeView::TreeLine(0, 0, &root, TreeView::TYPE_ROOT)); - workList.push_back(std::make_pair(root.bytesGross, &flatTree.back().percent_)); - }); - - calcPercentage(workList); - } - - //restore node expansion status - for (size_t row = 0; row < flatTree.size(); ++row) //flatTree size changes during loop! - { - const TreeLine& line = flatTree[row]; - - if (auto hierObj = getHierAlias(line)) - if (expandedNodes.find(hierObj) != expandedNodes.end()) - { - std::vector newLines; - getChildren(*line.node_, line.level_ + 1, newLines); - - flatTree.insert(flatTree.begin() + row + 1, newLines.begin(), newLines.end()); - } - } -} - - -template -void TreeView::updateView(Predicate pred) -{ - //update view on full data - std::vector newView; - newView.reserve(folderCmp.size()); //avoid expensive reallocations! - - std::for_each(folderCmp.begin(), folderCmp.end(), - [&](const std::shared_ptr& baseObj) - { - newView.push_back(TreeView::RootNodeImpl()); - RootNodeImpl& root = newView.back(); - this->extractVisibleSubtree(*baseObj, root, pred); //"this->" is bogus for a static method, but GCC screws this one up - - //warning: the following lines are almost 1:1 copy from extractVisibleSubtree: - //however we *cannot* reuse code here; this were only possible if we could replace "std::vector" by "Container"! - if (!root.firstFileId && root.subDirs.empty()) - newView.pop_back(); - else - { - root.baseDirObj = baseObj; - this->compressNode(root); //"this->" required by two-pass lookup as enforced by GCC 4.7 - } - }); - - lastViewFilterPred = pred; - applySubView(std::move(newView)); -} - - -void TreeView::setSortDirection(ColumnTypeNavi colType, bool ascending) //apply permanently! -{ - sortColumn = colType; - sortAscending = ascending; - - //reapply current view - applySubView(std::move(folderCmpView)); -} - - -bool TreeView::getDefaultSortDirection(ColumnTypeNavi colType) -{ - switch (colType) - { - case COL_TYPE_NAVI_BYTES: - return false; - case COL_TYPE_NAVI_DIRECTORY: - return true; - case COL_TYPE_NAVI_ITEM_COUNT: - return false; - } - assert(false); - return true; -} - - -TreeView::NodeStatus TreeView::getStatus(size_t row) const -{ - if (row < flatTree.size()) - { - if (row + 1 < flatTree.size() && flatTree[row + 1].level_ > flatTree[row].level_) - return TreeView::STATUS_EXPANDED; - - //it's either reduced or empty - switch (flatTree[row].type_) - { - case TreeView::TYPE_DIRECTORY: - case TreeView::TYPE_ROOT: - return flatTree[row].node_->firstFileId || !flatTree[row].node_->subDirs.empty() ? TreeView::STATUS_REDUCED : TreeView::STATUS_EMPTY; - - case TreeView::TYPE_FILES: - return TreeView::STATUS_EMPTY; - } - } - return TreeView::STATUS_EMPTY; -} - - -void TreeView::expandNode(size_t row) -{ - if (getStatus(row) != TreeView::STATUS_REDUCED) - { - assert(false); - return; - } - - if (row < flatTree.size()) - { - std::vector newLines; - - switch (flatTree[row].type_) - { - case TreeView::TYPE_ROOT: - case TreeView::TYPE_DIRECTORY: - getChildren(*flatTree[row].node_, flatTree[row].level_ + 1, newLines); - break; - case TreeView::TYPE_FILES: - break; - } - flatTree.insert(flatTree.begin() + row + 1, newLines.begin(), newLines.end()); - } -} - - -void TreeView::reduceNode(size_t row) -{ - if (row < flatTree.size()) - { - const unsigned int parentLevel = flatTree[row].level_; - - bool done = false; - flatTree.erase(std::remove_if(flatTree.begin() + row + 1, flatTree.end(), - [&](const TreeLine& line) -> bool - { - if (done) - return false; - if (line.level_ > parentLevel) - return true; - else - { - done = true; - return false; - } - }), flatTree.end()); - } -} - - -ptrdiff_t TreeView::getParent(size_t row) const -{ - if (row < flatTree.size()) - { - const auto level = flatTree[row].level_; - - while (row-- > 0) - if (flatTree[row].level_ < level) - return row; - } - return -1; -} - - -void TreeView::updateCmpResult(bool hideFiltered, - bool leftOnlyFilesActive, - bool rightOnlyFilesActive, - bool leftNewerFilesActive, - bool rightNewerFilesActive, - bool differentFilesActive, - bool equalFilesActive, - bool conflictFilesActive) -{ - updateView([hideFiltered, //make sure the predicate can be stored safely! - leftOnlyFilesActive, - rightOnlyFilesActive, - leftNewerFilesActive, - rightNewerFilesActive, - differentFilesActive, - equalFilesActive, - conflictFilesActive](const FileSystemObject& fsObj) -> bool - { - if (hideFiltered && !fsObj.isActive()) - return false; - - switch (fsObj.getCategory()) - { - case FILE_LEFT_SIDE_ONLY: - return leftOnlyFilesActive; - case FILE_RIGHT_SIDE_ONLY: - return rightOnlyFilesActive; - case FILE_LEFT_NEWER: - return leftNewerFilesActive; - case FILE_RIGHT_NEWER: - return rightNewerFilesActive; - case FILE_DIFFERENT: - return differentFilesActive; - case FILE_EQUAL: - case FILE_DIFFERENT_METADATA: //= sub-category of equal - return equalFilesActive; - case FILE_CONFLICT: - return conflictFilesActive; - } - assert(false); - return true; - }); -} - - -void TreeView::updateSyncPreview(bool hideFiltered, - bool syncCreateLeftActive, - bool syncCreateRightActive, - bool syncDeleteLeftActive, - bool syncDeleteRightActive, - bool syncDirOverwLeftActive, - bool syncDirOverwRightActive, - bool syncDirNoneActive, - bool syncEqualActive, - bool conflictFilesActive) -{ - updateView([hideFiltered, //make sure the predicate can be stored safely! - syncCreateLeftActive, - syncCreateRightActive, - syncDeleteLeftActive, - syncDeleteRightActive, - syncDirOverwLeftActive, - syncDirOverwRightActive, - syncDirNoneActive, - syncEqualActive, - conflictFilesActive](const FileSystemObject& fsObj) -> bool - { - if (hideFiltered && !fsObj.isActive()) - return false; - - switch (fsObj.getSyncOperation()) - { - case SO_CREATE_NEW_LEFT: - return syncCreateLeftActive; - case SO_CREATE_NEW_RIGHT: - return syncCreateRightActive; - case SO_DELETE_LEFT: - return syncDeleteLeftActive; - case SO_DELETE_RIGHT: - return syncDeleteRightActive; - case SO_OVERWRITE_RIGHT: - case SO_COPY_METADATA_TO_RIGHT: - case SO_MOVE_RIGHT_SOURCE: - case SO_MOVE_RIGHT_TARGET: - return syncDirOverwRightActive; - case SO_OVERWRITE_LEFT: - case SO_COPY_METADATA_TO_LEFT: - case SO_MOVE_LEFT_SOURCE: - case SO_MOVE_LEFT_TARGET: - return syncDirOverwLeftActive; - case SO_DO_NOTHING: - return syncDirNoneActive; - case SO_EQUAL: - return syncEqualActive; - case SO_UNRESOLVED_CONFLICT: - return conflictFilesActive; - } - assert(false); - return true; - }); -} - - -void TreeView::setData(FolderComparison& newData) -{ - std::vector().swap(flatTree); //free mem - std::vector().swap(folderCmpView); // - folderCmp = newData; - - //remove truly empty folder pairs as early as this: we want to distinguish single/multiple folder pair cases by looking at "folderCmp" - vector_remove_if(folderCmp, [](const std::shared_ptr& baseObj) - { - return baseObj->getBaseDirPf().empty() && - baseObj->getBaseDirPf().empty(); - }); -} - - -std::unique_ptr TreeView::getLine(size_t row) const -{ - if (row < flatTree.size()) - { - const auto level = flatTree[row].level_; - const int percent = flatTree[row].percent_; - - switch (flatTree[row].type_) - { - case TreeView::TYPE_ROOT: - { - const auto* root = static_cast(flatTree[row].node_); - return make_unique(percent, root->bytesGross, root->itemCountGross, getStatus(row), *(root->baseDirObj)); - } - break; - - case TreeView::TYPE_DIRECTORY: - { - const auto* dir = static_cast(flatTree[row].node_); - if (auto dirObj = dynamic_cast(FileSystemObject::retrieve(dir->objId))) - return make_unique(percent, dir->bytesGross, dir->itemCountGross, level, getStatus(row), *dirObj); - } - break; - - case TreeView::TYPE_FILES: - { - const auto* parentDir = flatTree[row].node_; - if (auto firstFile = FileSystemObject::retrieve(parentDir->firstFileId)) - { - std::vector filesAndLinks; - HierarchyObject& parent = firstFile->parent(); - - //lazy evaluation: recheck "lastViewFilterPred" again rather than buffer and bloat "lastViewFilterPred" - for (FileSystemObject& fsObj : parent.refSubFiles()) - if (lastViewFilterPred(fsObj)) - filesAndLinks.push_back(&fsObj); - - for (FileSystemObject& fsObj : parent.refSubLinks()) - if (lastViewFilterPred(fsObj)) - filesAndLinks.push_back(&fsObj); - - return make_unique(percent, parentDir->bytesNet, parentDir->itemCountNet, level, filesAndLinks); - } - } - break; - } - } - return nullptr; -} - -//########################################################################################################## - -namespace -{ -wxString getShortDisplayNameForFolderPair(const Zstring& dirLeftPf, const Zstring& dirRightPf) //post-fixed with separator -{ - assert(endsWith(dirLeftPf, FILE_NAME_SEPARATOR) || dirLeftPf .empty()); - assert(endsWith(dirRightPf, FILE_NAME_SEPARATOR) || dirRightPf.empty()); - - auto itL = dirLeftPf .end(); - auto itR = dirRightPf.end(); - - for (;;) - { - auto itLPrev = find_last(dirLeftPf .begin(), itL, FILE_NAME_SEPARATOR); - auto itRPrev = find_last(dirRightPf.begin(), itR, FILE_NAME_SEPARATOR); - - if (itLPrev == itL || - itRPrev == itR) - { - if (itLPrev == itL) - itLPrev = dirLeftPf.begin(); - else - ++itLPrev; //skip separator - if (itRPrev == itR) - itRPrev = dirRightPf.begin(); - else - ++itRPrev; - - if (equal(itLPrev, itL, itRPrev, itR)) - { - itL = itLPrev; - itR = itRPrev; - } - break; - } - - if (!equal(itLPrev, itL, itRPrev, itR)) - break; - itL = itLPrev; - itR = itRPrev; - } - - Zstring commonPostfix(itL, dirLeftPf.end()); - if (startsWith(commonPostfix, FILE_NAME_SEPARATOR)) - commonPostfix = afterFirst(commonPostfix, FILE_NAME_SEPARATOR); - if (endsWith(commonPostfix, FILE_NAME_SEPARATOR)) - commonPostfix.resize(commonPostfix.size() - 1); - - if (commonPostfix.empty()) - { - auto getLastComponent = [](const Zstring& dirPf) { return utfCvrtTo(afterLast(beforeLast(dirPf, FILE_NAME_SEPARATOR), FILE_NAME_SEPARATOR)); }; //returns the whole string if term not found - if (dirLeftPf.empty()) - return getLastComponent(dirRightPf); - else if (dirRightPf.empty()) - return getLastComponent(dirLeftPf); - else - return getLastComponent(dirLeftPf) + L" \u2212 " + //= unicode minus - getLastComponent(dirRightPf); - } - return utfCvrtTo(commonPostfix); -} - - -const wxColour COLOR_LEVEL0(0xcc, 0xcc, 0xff); -const wxColour COLOR_LEVEL1(0xcc, 0xff, 0xcc); -const wxColour COLOR_LEVEL2(0xff, 0xff, 0x99); - -const wxColour COLOR_LEVEL3(0xcc, 0xcc, 0xcc); -const wxColour COLOR_LEVEL4(0xff, 0xcc, 0xff); -const wxColour COLOR_LEVEL5(0x99, 0xff, 0xcc); - -const wxColour COLOR_LEVEL6(0xcc, 0xcc, 0x99); -const wxColour COLOR_LEVEL7(0xff, 0xcc, 0xcc); -const wxColour COLOR_LEVEL8(0xcc, 0xff, 0x99); - -const wxColour COLOR_LEVEL9 (0xff, 0xff, 0xcc); -const wxColour COLOR_LEVEL10(0xcc, 0xff, 0xff); -const wxColour COLOR_LEVEL11(0xff, 0xcc, 0x99); - -const wxColour COLOR_PERCENTAGE_BORDER (198, 198, 198); -const wxColour COLOR_PERCENTAGE_BACKGROUND(0xf8, 0xf8, 0xf8); - -//const wxColor COLOR_TREE_SELECTION_GRADIENT_FROM = wxColor( 89, 255, 99); //green: HSV: 88, 255, 172 -//const wxColor COLOR_TREE_SELECTION_GRADIENT_TO = wxColor(225, 255, 227); // HSV: 88, 255, 240 -const wxColor COLOR_TREE_SELECTION_GRADIENT_FROM = getColorSelectionGradientFrom(); -const wxColor COLOR_TREE_SELECTION_GRADIENT_TO = getColorSelectionGradientTo (); - -const int iconSizeSmall = IconBuffer::getSize(IconBuffer::SIZE_SMALL); - -class GridDataNavi : private wxEvtHandler, public GridData -{ -public: - GridDataNavi(Grid& grid, const std::shared_ptr& treeDataView) : treeDataView_(treeDataView), - fileIcon(IconBuffer(IconBuffer::SIZE_SMALL).genericFileIcon()), - dirIcon (IconBuffer(IconBuffer::SIZE_SMALL).genericDirIcon ()), - rootBmp(getResourceImage(L"rootFolder").ConvertToImage().Scale(iconSizeSmall, iconSizeSmall, wxIMAGE_QUALITY_HIGH)), - widthNodeIcon(iconSizeSmall), - widthLevelStep(widthNodeIcon), - widthNodeStatus(getResourceImage(L"nodeExpanded").GetWidth()), - grid_(grid), - showPercentBar(true) - { - grid.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(GridDataNavi::onKeyDown), nullptr, this); - grid.Connect(EVENT_GRID_MOUSE_LEFT_DOWN, GridClickEventHandler(GridDataNavi::onMouseLeft ), nullptr, this); - grid.Connect(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEventHandler(GridDataNavi::onMouseLeftDouble ), nullptr, this); - grid.Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(GridDataNavi::onGridLabelContext), nullptr, this ); - grid.Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(GridDataNavi::onGridLabelLeftClick ), nullptr, this ); - } - - void setShowPercentage(bool value) { showPercentBar = value; grid_.Refresh(); } - bool getShowPercentage() const { return showPercentBar; } - -private: - virtual size_t getRowCount() const { return treeDataView_ ? treeDataView_->linesTotal() : 0; } - - virtual wxString getToolTip(size_t row, ColumnType colType) const override - { - switch (static_cast(colType)) - { - case COL_TYPE_NAVI_BYTES: - case COL_TYPE_NAVI_ITEM_COUNT: - break; - - case COL_TYPE_NAVI_DIRECTORY: - if (treeDataView_) - if (std::unique_ptr node = treeDataView_->getLine(row)) - if (const TreeView::RootNode* root = dynamic_cast(node.get())) - { - const wxString& dirLeft = utfCvrtTo(root->baseDirObj_.getBaseDirPf()); - const wxString& dirRight = utfCvrtTo(root->baseDirObj_.getBaseDirPf()); - if (dirLeft.empty()) - return dirRight; - else if (dirRight.empty()) - return dirLeft; - return dirLeft + L" \u2212 \n" + dirRight; //\u2212 = unicode minus - } - break; - } - return wxString(); - } - - virtual wxString getValue(size_t row, ColumnType colType) const - { - if (treeDataView_) - { - if (std::unique_ptr node = treeDataView_->getLine(row)) - switch (static_cast(colType)) - { - case COL_TYPE_NAVI_BYTES: - return filesizeToShortString(to(node->bytes_)); - - case COL_TYPE_NAVI_DIRECTORY: - if (const TreeView::RootNode* root = dynamic_cast(node.get())) - return getShortDisplayNameForFolderPair(root->baseDirObj_.getBaseDirPf(), - root->baseDirObj_.getBaseDirPf()); - else if (const TreeView::DirNode* dir = dynamic_cast(node.get())) - return utfCvrtTo(dir->dirObj_.getObjShortName()); - else if (dynamic_cast(node.get())) - return _("Files"); - break; - - case COL_TYPE_NAVI_ITEM_COUNT: - return toGuiString(node->itemCount_); - } - } - return wxString(); - } - - virtual void renderColumnLabel(Grid& tree, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted) override - { - wxRect rectInside = drawColumnLabelBorder(dc, rect); - drawColumnLabelBackground(dc, rectInside, highlighted); - - rectInside.x += COLUMN_GAP_LEFT; - rectInside.width -= COLUMN_GAP_LEFT; - drawColumnLabelText(dc, rectInside, getColumnLabel(colType)); - - if (treeDataView_) //draw sort marker - { - auto sortInfo = treeDataView_->getSortDirection(); - if (colType == static_cast(sortInfo.first)) - { - const wxBitmap& marker = getResourceImage(sortInfo.second ? L"sortAscending" : L"sortDescending"); - wxPoint markerBegin = rectInside.GetTopLeft() + wxPoint((rectInside.width - marker.GetWidth()) / 2, 0); - dc.DrawBitmap(marker, markerBegin, true); //respect 2-pixel gap - } - } - } - - static const int GAP_SIZE = 2; - - virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) override - { - if (enabled) - { - if (selected) - dc.GradientFillLinear(rect, COLOR_TREE_SELECTION_GRADIENT_FROM, COLOR_TREE_SELECTION_GRADIENT_TO, wxEAST); - //ignore focus - else - clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - } - else - clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); - } - - virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool selected) override - { - //wxRect rectTmp= drawCellBorder(dc, rect); - wxRect rectTmp = rect; - - // Partitioning: - // ________________________________________________________________________________ - // | space | gap | percentage bar | 2 x gap | node status | gap |icon | gap | rest | - // -------------------------------------------------------------------------------- - // -> synchronize renderCell() <-> getBestSize() <-> onMouseLeft() - - if (static_cast(colType) == COL_TYPE_NAVI_DIRECTORY && treeDataView_) - { - if (std::unique_ptr node = treeDataView_->getLine(row)) - { - ////clear first secion: - //clearArea(dc, wxRect(rect.GetTopLeft(), wxSize( - // node->level_ * widthLevelStep + GAP_SIZE + //width - // (showPercentBar ? widthPercentBar + 2 * GAP_SIZE : 0) + // - // widthNodeStatus + GAP_SIZE + widthNodeIcon + GAP_SIZE, // - // rect.height)), wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - - //consume space - rectTmp.x += static_cast(node->level_) * widthLevelStep; - rectTmp.width -= static_cast(node->level_) * widthLevelStep; - - rectTmp.x += GAP_SIZE; - rectTmp.width -= GAP_SIZE; - - if (rectTmp.width > 0) - { - //percentage bar - if (showPercentBar) - { - const wxColour brushCol = [&]() -> wxColour - { - switch (node->level_ % 12) - { - case 0: - return COLOR_LEVEL0; - case 1: - return COLOR_LEVEL1; - case 2: - return COLOR_LEVEL2; - case 3: - return COLOR_LEVEL3; - case 4: - return COLOR_LEVEL4; - case 5: - return COLOR_LEVEL5; - case 6: - return COLOR_LEVEL6; - case 7: - return COLOR_LEVEL7; - case 8: - return COLOR_LEVEL8; - case 9: - return COLOR_LEVEL9; - case 10: - return COLOR_LEVEL10; - default: - return COLOR_LEVEL11; - } - }(); - - const wxRect areaPerc(rectTmp.x, rectTmp.y + 2, widthPercentBar, rectTmp.height - 4); - { - //clear background - wxDCPenChanger dummy (dc, COLOR_PERCENTAGE_BORDER); - wxDCBrushChanger dummy2(dc, COLOR_PERCENTAGE_BACKGROUND); - dc.DrawRectangle(areaPerc); - - //inner area - dc.SetPen (brushCol); - dc.SetBrush(brushCol); - - wxRect areaPercTmp = areaPerc; - areaPercTmp.Deflate(1); //do not include border - areaPercTmp.width = numeric::round(areaPercTmp.width * node->percent_ / 100.0); - dc.DrawRectangle(areaPercTmp); - } - - wxDCTextColourChanger dummy3(dc, *wxBLACK); //accessibility: always set both foreground AND background colors! - dc.DrawLabel(numberTo(node->percent_) + L"%", areaPerc, wxALIGN_CENTER); - - rectTmp.x += widthPercentBar + 2 * GAP_SIZE; - rectTmp.width -= widthPercentBar + 2 * GAP_SIZE; - } - if (rectTmp.width > 0) - { - //node status - auto drawStatus = [&](const wchar_t* image) - { - const wxBitmap& bmp = getResourceImage(image); - - wxRect rectStat(rectTmp.GetTopLeft(), wxSize(bmp.GetWidth(), bmp.GetHeight())); - rectStat.y += (rectTmp.height - rectStat.height) / 2; - - //clearArea(dc, rectStat, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - clearArea(dc, rectStat, *wxWHITE); //accessibility: always set both foreground AND background colors! - drawBitmapRtlMirror(dc, bmp, rectStat, wxALIGN_CENTER, buffer); - }; - - switch (node->status_) - { - case TreeView::STATUS_EXPANDED: - drawStatus(L"nodeExpanded"); - break; - case TreeView::STATUS_REDUCED: - drawStatus(L"nodeReduced"); - break; - case TreeView::STATUS_EMPTY: - break; - } - - rectTmp.x += widthNodeStatus + GAP_SIZE; - rectTmp.width -= widthNodeStatus + GAP_SIZE; - if (rectTmp.width > 0) - { - wxBitmap nodeIcon; - bool isActive = true; - //icon - if (dynamic_cast(node.get())) - nodeIcon = rootBmp; - else if (auto dir = dynamic_cast(node.get())) - { - nodeIcon = dirIcon; - isActive = dir->dirObj_.isActive(); - } - else if (dynamic_cast(node.get())) - nodeIcon = fileIcon; - - if (isActive) - drawBitmapRtlNoMirror(dc, nodeIcon, rectTmp, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, buffer); - - else - drawBitmapRtlNoMirror(dc, wxBitmap(nodeIcon.ConvertToImage().ConvertToGreyscale(1.0 / 3, 1.0 / 3, 1.0 / 3)), //treat all channels equally! - rectTmp, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, buffer); - - rectTmp.x += widthNodeIcon + GAP_SIZE; - rectTmp.width -= widthNodeIcon + GAP_SIZE; - - if (rectTmp.width > 0) - drawCellText(dc, rectTmp, getValue(row, colType), isActive, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); - } - } - } - } - } - else - { - int alignment = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL; - - //have file size and item count right-justified (but don't change for RTL languages) - if ((static_cast(colType) == COL_TYPE_NAVI_BYTES || - static_cast(colType) == COL_TYPE_NAVI_ITEM_COUNT) && grid_.GetLayoutDirection() != wxLayout_RightToLeft) - { - rectTmp.width -= 2 * GAP_SIZE; - alignment = wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL; - } - else //left-justified - { - rectTmp.x += 2 * GAP_SIZE; - rectTmp.width -= 2 * GAP_SIZE; - } - - drawCellText(dc, rectTmp, getValue(row, colType), true, alignment); - } - } - - virtual int getBestSize(wxDC& dc, size_t row, ColumnType colType) override - { - // -> synchronize renderCell() <-> getBestSize() <-> onMouseLeft() - - if (static_cast(colType) == COL_TYPE_NAVI_DIRECTORY && treeDataView_) - { - if (std::unique_ptr node = treeDataView_->getLine(row)) - return node->level_ * widthLevelStep + GAP_SIZE + (showPercentBar ? widthPercentBar + 2 * GAP_SIZE : 0) + widthNodeStatus + GAP_SIZE - + widthNodeIcon + GAP_SIZE + dc.GetTextExtent(getValue(row, colType)).GetWidth() + - GAP_SIZE; //additional gap from right - else - return 0; - } - else - return 2 * GAP_SIZE + dc.GetTextExtent(getValue(row, colType)).GetWidth() + - 2 * GAP_SIZE; //include gap from right! - } - - virtual wxString getColumnLabel(ColumnType colType) const - { - switch (static_cast(colType)) - { - case COL_TYPE_NAVI_BYTES: - return _("Size"); - case COL_TYPE_NAVI_DIRECTORY: - return _("Name"); - case COL_TYPE_NAVI_ITEM_COUNT: - return _("Items"); - } - return wxEmptyString; - } - - void onMouseLeft(GridClickEvent& event) - { - if (treeDataView_) - { - bool clickOnNodeStatus = false; - if (static_cast(event.colType_) == COL_TYPE_NAVI_DIRECTORY) - if (std::unique_ptr node = treeDataView_->getLine(event.row_)) - { - const int absX = grid_.CalcUnscrolledPosition(event.GetPosition()).x; - const wxRect cellArea = grid_.getCellArea(event.row_, event.colType_); - if (cellArea.width > 0 && cellArea.height > 0) - { - const int tolerance = 1; - const int xNodeStatusFirst = -tolerance + cellArea.x + static_cast(node->level_) * widthLevelStep + GAP_SIZE + (showPercentBar ? widthPercentBar + 2 * GAP_SIZE : 0); - const int xNodeStatusLast = (xNodeStatusFirst + tolerance) + widthNodeStatus + tolerance; - // -> synchronize renderCell() <-> getBestSize() <-> onMouseLeft() - - if (xNodeStatusFirst <= absX && absX < xNodeStatusLast) - clickOnNodeStatus = true; - } - } - //-------------------------------------------------------------------------------------------------- - - if (clickOnNodeStatus) - switch (treeDataView_->getStatus(event.row_)) - { - case TreeView::STATUS_EXPANDED: - return reduceNode(event.row_); - case TreeView::STATUS_REDUCED: - return expandNode(event.row_); - case TreeView::STATUS_EMPTY: - break; - } - } - event.Skip(); - } - - void onMouseLeftDouble(GridClickEvent& event) - { - if (treeDataView_) - switch (treeDataView_->getStatus(event.row_)) - { - case TreeView::STATUS_EXPANDED: - return reduceNode(event.row_); - case TreeView::STATUS_REDUCED: - return expandNode(event.row_); - case TreeView::STATUS_EMPTY: - break; - } - event.Skip(); - } - - void onKeyDown(wxKeyEvent& event) - { - int keyCode = event.GetKeyCode(); - if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft) - { - if (keyCode == WXK_LEFT) - keyCode = WXK_RIGHT; - else if (keyCode == WXK_RIGHT) - keyCode = WXK_LEFT; - else if (keyCode == WXK_NUMPAD_LEFT) - keyCode = WXK_NUMPAD_RIGHT; - else if (keyCode == WXK_NUMPAD_RIGHT) - keyCode = WXK_NUMPAD_LEFT; - } - - const size_t rowCount = grid_.getRowCount(); - if (rowCount == 0) return; - - size_t row = grid_.getGridCursor().first; - if (event.ShiftDown()) - ; - else if (event.ControlDown()) - ; - else - switch (keyCode) - { - case WXK_LEFT: - case WXK_NUMPAD_LEFT: - case WXK_NUMPAD_SUBTRACT: //http://msdn.microsoft.com/en-us/library/ms971323.aspx#atg_keyboardshortcuts_windows_shortcut_keys - if (treeDataView_) - switch (treeDataView_->getStatus(row)) - { - case TreeView::STATUS_EXPANDED: - return reduceNode(row); - case TreeView::STATUS_REDUCED: - case TreeView::STATUS_EMPTY: - - const int parentRow = treeDataView_->getParent(row); - if (parentRow >= 0) - grid_.setGridCursor(parentRow); - break; - } - return; //swallow event - - case WXK_RIGHT: - case WXK_NUMPAD_RIGHT: - case WXK_NUMPAD_ADD: - if (treeDataView_) - switch (treeDataView_->getStatus(row)) - { - case TreeView::STATUS_EXPANDED: - grid_.setGridCursor(std::min(rowCount - 1, row + 1)); - break; - case TreeView::STATUS_REDUCED: - return expandNode(row); - case TreeView::STATUS_EMPTY: - break; - } - return; //swallow event - } - - event.Skip(); - } - - void onGridLabelContext(GridClickEvent& event) - { - ContextMenu menu; - - //-------------------------------------------------------------------------------------------------------- - menu.addCheckBox(_("Percentage"), [this] { setShowPercentage(!getShowPercentage()); }, getShowPercentage()); - //-------------------------------------------------------------------------------------------------------- - auto toggleColumn = [&](const Grid::ColumnAttribute& ca) - { - auto colAttr = grid_.getColumnConfig(); - - for (auto it = colAttr.begin(); it != colAttr.end(); ++it) - if (it->type_ == ca.type_) - { - it->visible_ = !ca.visible_; - grid_.setColumnConfig(colAttr); - return; - } - }; - - const auto& colAttr = grid_.getColumnConfig(); - for (auto it = colAttr.begin(); it != colAttr.end(); ++it) - { - const Grid::ColumnAttribute& ca = *it; - - menu.addCheckBox(getColumnLabel(ca.type_), [ca, toggleColumn]() { toggleColumn(ca); }, - ca.visible_, ca.type_ != static_cast(COL_TYPE_NAVI_DIRECTORY)); //do not allow user to hide file name column! - } - //-------------------------------------------------------------------------------------------------------- - menu.addSeparator(); - - auto setDefaultColumns = [&] - { - setShowPercentage(defaultValueShowPercentage); - grid_.setColumnConfig(treeview::convertConfig(getDefaultColumnAttributesNavi())); - }; - menu.addItem(_("&Default"), setDefaultColumns); //'&' -> reuse text from "default" buttons elsewhere - - menu.popup(grid_); - - event.Skip(); - } - - void onGridLabelLeftClick(GridClickEvent& event) - { - if (treeDataView_) - { - const auto colTypeNavi = static_cast(event.colType_); - bool sortAscending = TreeView::getDefaultSortDirection(colTypeNavi); - - const auto sortInfo = treeDataView_->getSortDirection(); - if (sortInfo.first == colTypeNavi) - sortAscending = !sortInfo.second; - - treeDataView_->setSortDirection(colTypeNavi, sortAscending); - grid_.clearSelection(); - grid_.Refresh(); - } - } - - void expandNode(size_t row) - { - treeDataView_->expandNode(row); - grid_.Refresh(); //implicitly clears selection (changed row count after expand) - grid_.setGridCursor(row); - //grid_.autoSizeColumns(); -> doesn't look as good as expected - } - - void reduceNode(size_t row) - { - treeDataView_->reduceNode(row); - grid_.Refresh(); - grid_.setGridCursor(row); - } - - std::shared_ptr treeDataView_; - const wxBitmap fileIcon; - const wxBitmap dirIcon; - const wxBitmap rootBmp; - std::unique_ptr buffer; //avoid costs of recreating this temporal variable - const int widthNodeIcon; - const int widthLevelStep; - const int widthNodeStatus; - static const int widthPercentBar = 60; - Grid& grid_; - bool showPercentBar; -}; -} - - -void treeview::init(Grid& grid, const std::shared_ptr& treeDataView) -{ - grid.setDataProvider(std::make_shared(grid, treeDataView)); - grid.showRowLabel(false); - - const int rowHeight = std::max(IconBuffer::getSize(IconBuffer::SIZE_SMALL), grid.getMainWin().GetCharHeight()) + 2; //allow 1 pixel space on top and bottom; dearly needed on OS X! - grid.setRowHeight(rowHeight); -} - - -void treeview::setShowPercentage(Grid& grid, bool value) -{ - if (auto* prov = dynamic_cast(grid.getDataProvider())) - prov->setShowPercentage(value); - else - assert(false); -} - - -bool treeview::getShowPercentage(const Grid& grid) -{ - if (auto* prov = dynamic_cast(grid.getDataProvider())) - return prov->getShowPercentage(); - assert(false); - return true; -} - - -namespace -{ -std::vector makeConsistent(const std::vector& attribs) -{ - std::set usedTypes; - - std::vector output; - //remove duplicates - std::copy_if(attribs.begin(), attribs.end(), std::back_inserter(output), - [&](const ColumnAttributeNavi& a) { return usedTypes.insert(a.type_).second; }); - - //make sure each type is existing! - const auto& defAttr = getDefaultColumnAttributesNavi(); - std::copy_if(defAttr.begin(), defAttr.end(), std::back_inserter(output), - [&](const ColumnAttributeNavi& a) { return usedTypes.insert(a.type_).second; }); - - return output; -} -} - -std::vector treeview::convertConfig(const std::vector& attribs) -{ - const auto& attribClean = makeConsistent(attribs); - - std::vector output; - std::transform(attribClean.begin(), attribClean.end(), std::back_inserter(output), - [&](const ColumnAttributeNavi& ca) { return Grid::ColumnAttribute(static_cast(ca.type_), ca.offset_, ca.stretch_, ca.visible_); }); - - return output; -} - - -std::vector treeview::convertConfig(const std::vector& attribs) -{ - std::vector output; - - std::transform(attribs.begin(), attribs.end(), std::back_inserter(output), - [&](const Grid::ColumnAttribute& ca) { return ColumnAttributeNavi(static_cast(ca.type_), ca.offset_, ca.stretch_, ca.visible_); }); - - return makeConsistent(output); -} diff --git a/ui/tree_view.h b/ui/tree_view.h deleted file mode 100644 index 719212dd..00000000 --- a/ui/tree_view.h +++ /dev/null @@ -1,186 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef TREE_H_INCLUDED_841703190201835280256673425 -#define TREE_H_INCLUDED_841703190201835280256673425 - -#include -#include -#include -#include "column_attr.h" -#include "../file_hierarchy.h" - -namespace zen -{ -//tree view of FolderComparison -class TreeView -{ -public: - TreeView() : - sortColumn(defaultValueLastSortColumn), - sortAscending(defaultValueLastSortAscending) {} - - void setData(FolderComparison& newData); //set data, taking (partial) ownership - - //apply view filter: comparison results - void updateCmpResult(bool hideFiltered, - bool leftOnlyFilesActive, - bool rightOnlyFilesActive, - bool leftNewerFilesActive, - bool rightNewerFilesActive, - bool differentFilesActive, - bool equalFilesActive, - bool conflictFilesActive); - - //apply view filter: synchronization preview - void updateSyncPreview(bool hideFiltered, - bool syncCreateLeftActive, - bool syncCreateRightActive, - bool syncDeleteLeftActive, - bool syncDeleteRightActive, - bool syncDirOverwLeftActive, - bool syncDirOverwRightActive, - bool syncDirNoneActive, - bool syncEqualActive, - bool conflictFilesActive); - - enum NodeStatus - { - STATUS_EXPANDED, - STATUS_REDUCED, - STATUS_EMPTY - }; - - //--------------------------------------------------------------------- - struct Node - { - Node(int percent, UInt64 bytes, int itemCount, unsigned int level, NodeStatus status) : - percent_(percent), level_(level), status_(status), bytes_(bytes), itemCount_(itemCount) {} - virtual ~Node() {} - - const int percent_; //[0, 100] - const unsigned int level_; - const NodeStatus status_; - const UInt64 bytes_; - const int itemCount_; - }; - - struct FilesNode : public Node - { - FilesNode(int percent, UInt64 bytes, int itemCount, unsigned int level, const std::vector& filesAndLinks) : Node(percent, bytes, itemCount, level, STATUS_EMPTY), filesAndLinks_(filesAndLinks) {} - std::vector filesAndLinks_; //files or symlinks; pointers are bound! - }; - - struct DirNode : public Node - { - DirNode(int percent, UInt64 bytes, int itemCount, unsigned int level, NodeStatus status, DirPair& dirObj) : Node(percent, bytes, itemCount, level, status), dirObj_(dirObj) {} - DirPair& dirObj_; - }; - - struct RootNode : public Node - { - RootNode(int percent, UInt64 bytes, int itemCount, NodeStatus status, BaseDirPair& baseDirObj) : Node(percent, bytes, itemCount, 0, status), baseDirObj_(baseDirObj) {} - BaseDirPair& baseDirObj_; - }; - - std::unique_ptr getLine(size_t row) const; //return nullptr on error - size_t linesTotal() const { return flatTree.size(); } - - void expandNode(size_t row); - void reduceNode(size_t row); - NodeStatus getStatus(size_t row) const; - ptrdiff_t getParent(size_t row) const; //return < 0 if none - - void setSortDirection(ColumnTypeNavi colType, bool ascending); //apply permanently! - std::pair getSortDirection() { return std::make_pair(sortColumn, sortAscending); } - static bool getDefaultSortDirection(ColumnTypeNavi colType); //ascending? - -private: - struct DirNodeImpl; - - struct Container - { - Container() : itemCountGross(), itemCountNet(), firstFileId(nullptr) {} - UInt64 bytesGross; - UInt64 bytesNet; //bytes for files on view in this directory only - int itemCountGross; - int itemCountNet; //number of files on view for in this directory only - - std::vector subDirs; - FileSystemObject::ObjectId firstFileId; //weak pointer to first FilePair or SymlinkPair - //- "compress" algorithm may hide file nodes for directories with a single included file, i.e. itemCountGross == itemCountNet == 1 - //- a HierarchyObject* would a better fit, but we need weak pointer semantics! - //- a std::vector would be a better design, but we don't want a second memory structure as large as custom grid! - }; - - struct DirNodeImpl : public Container - { - DirNodeImpl() : objId(nullptr) {} - FileSystemObject::ObjectId objId; //weak pointer to DirPair - }; - - struct RootNodeImpl : public Container - { - RootNodeImpl() {} - std::shared_ptr baseDirObj; - }; - - enum NodeType - { - TYPE_ROOT, //-> RootNodeImpl - TYPE_DIRECTORY, //-> DirNodeImpl - TYPE_FILES //-> Container - }; - - struct TreeLine - { - TreeLine(unsigned int level, int percent, const Container* node, enum NodeType type) : level_(level), percent_(percent), node_(node), type_(type) {} - - unsigned int level_; - int percent_; //[0, 100] - const Container* node_; // - NodeType type_; //we choose to increase size of "flatTree" rather than "folderCmpView" by not using dynamic polymorphism! - }; - - static void compressNode(Container& cont); - template - static void extractVisibleSubtree(HierarchyObject& hierObj, Container& cont, Function includeObject); - void getChildren(const Container& cont, unsigned int level, std::vector& output); - template void updateView(Predicate pred); - void applySubView(std::vector&& newView); - - template static void sortSingleLevel(std::vector& items, ColumnTypeNavi columnType); - template struct LessShortName; - - std::vector flatTree; //collapsable/expandable sub-tree of folderCmpView -> always sorted! - /* /|\ - | (update...) - | */ - std::vector folderCmpView; //partial view on folderCmp -> unsorted (cannot be, because files are not a separate entity) - std::function lastViewFilterPred; //buffer view filter predicate for lazy evaluation of files/symlinks corresponding to a TYPE_FILES node - /* /|\ - | (update...) - | */ - std::vector> folderCmp; //full raw data - - ColumnTypeNavi sortColumn; - bool sortAscending; -}; - - -namespace treeview -{ -void init(Grid& grid, const std::shared_ptr& treeDataView); - -void setShowPercentage(Grid& grid, bool value); -bool getShowPercentage(const Grid& grid); - -std::vector convertConfig(const std::vector& attribs); //+ make consistent -std::vector convertConfig(const std::vector& attribs); // -} -} - -#endif //TREE_H_INCLUDED_841703190201835280256673425 diff --git a/ui/triple_splitter.cpp b/ui/triple_splitter.cpp deleted file mode 100644 index fbdd22d7..00000000 --- a/ui/triple_splitter.cpp +++ /dev/null @@ -1,241 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "triple_splitter.h" -#include - -using namespace zen; - - -namespace -{ -//------------ Grid Constants ------------------------------- -const int SASH_HIT_TOLERANCE = 5; //currently only a placebo! -const int SASH_SIZE = 10; -const double SASH_GRAVITY = 0.5; //value within [0, 1]; 1 := resize left only, 0 := resize right only -const int CHILD_WINDOW_MIN_SIZE = 50; //min. size of managed windows - -const wxColor COLOR_SASH_GRADIENT_FROM = wxColour(192, 192, 192); //light grey -const wxColor COLOR_SASH_GRADIENT_TO = *wxWHITE; -} - -TripleSplitter::TripleSplitter(wxWindow* parent, - wxWindowID id, - const wxPoint& pos, - const wxSize& size, - long style) : wxWindow(parent, id, pos, size, style | wxTAB_TRAVERSAL), //tab between windows - centerOffset(0), - windowL(nullptr), - windowC(nullptr), - windowR(nullptr) -{ - Connect(wxEVT_PAINT, wxPaintEventHandler(TripleSplitter::onPaintEvent ), nullptr, this); - Connect(wxEVT_SIZE, wxSizeEventHandler (TripleSplitter::onSizeEvent ), nullptr, this); - //http://wiki.wxwidgets.org/Flicker-Free_Drawing - Connect(wxEVT_ERASE_BACKGROUND, wxEraseEventHandler(TripleSplitter::onEraseBackGround), nullptr, this); - - SetBackgroundStyle(wxBG_STYLE_PAINT); - - Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(TripleSplitter::onMouseLeftDown ), nullptr, this); - Connect(wxEVT_LEFT_UP, wxMouseEventHandler(TripleSplitter::onMouseLeftUp ), nullptr, this); - Connect(wxEVT_MOTION, wxMouseEventHandler(TripleSplitter::onMouseMovement ), nullptr, this); - Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(TripleSplitter::onLeaveWindow ), nullptr, this); - Connect(wxEVT_MOUSE_CAPTURE_LOST, wxMouseCaptureLostEventHandler(TripleSplitter::onMouseCaptureLost), nullptr, this); - Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(TripleSplitter::onMouseLeftDouble), nullptr, this); -} - - -TripleSplitter::~TripleSplitter() {} //make sure correct destructor gets created for std::unique_ptr - - -void TripleSplitter::updateWindowSizes() -{ - if (windowL && windowC && windowR) - { - const int centerPosX = getCenterPosX(); - const int centerWidth = getCenterWidth(); - - const wxRect clientRect = GetClientRect(); - - const int widthL = centerPosX; - const int windowRposX = widthL + centerWidth; - const int widthR = clientRect.width - windowRposX; - - windowL->SetSize(0, 0, widthL, clientRect.height); - windowC->SetSize(widthL + SASH_SIZE, 0, windowC->GetSize().GetWidth(), clientRect.height); - windowR->SetSize(windowRposX, 0, widthR, clientRect.height); - - wxClientDC dc(this); - drawSash(dc); - } -} - - -class TripleSplitter::SashMove -{ -public: - SashMove(wxWindow& wnd, int mousePosX, int centerOffset) : wnd_(wnd), mousePosX_(mousePosX), centerOffset_(centerOffset) - { - wnd_.SetCursor(wxCURSOR_SIZEWE); - wnd_.CaptureMouse(); - } - ~SashMove() - { - wnd_.SetCursor(*wxSTANDARD_CURSOR); - if (wnd_.HasCapture()) - wnd_.ReleaseMouse(); - } - int getMousePosXStart () const { return mousePosX_; } - int getCenterOffsetStart() const { return centerOffset_; } - -private: - wxWindow& wnd_; - const int mousePosX_; - const int centerOffset_; -}; - - -inline -int TripleSplitter::getCenterWidth() const -{ - return 2 * SASH_SIZE + (windowC ? windowC->GetSize().GetWidth() : 0); -} - - -int TripleSplitter::getCenterPosXOptimal() const -{ - const wxRect clientRect = GetClientRect(); - const int centerWidth = getCenterWidth(); - return (clientRect.width - centerWidth) * SASH_GRAVITY; //allowed to be negative for extreme client widths! -} - - -int TripleSplitter::getCenterPosX() const -{ - const wxRect clientRect = GetClientRect(); - const int centerWidth = getCenterWidth(); - const int centerPosXOptimal = getCenterPosXOptimal(); - - //normalize "centerPosXOptimal + centerOffset" - if (clientRect.width < 2 * CHILD_WINDOW_MIN_SIZE + centerWidth) - //use fixed "centeroffset" when "clientRect.width == 2 * CHILD_WINDOW_MIN_SIZE + centerWidth" - return centerPosXOptimal + CHILD_WINDOW_MIN_SIZE - static_cast(2 * CHILD_WINDOW_MIN_SIZE * SASH_GRAVITY); //avoid rounding error - //make sure transition between conditional branches is continuous! - return std::max(CHILD_WINDOW_MIN_SIZE, //make sure centerPosXOptimal + offset is within bounds - std::min(centerPosXOptimal + centerOffset, clientRect.width - CHILD_WINDOW_MIN_SIZE - centerWidth)); -} - - -void TripleSplitter::drawSash(wxDC& dc) -{ - const int centerPosX = getCenterPosX(); - const int centerWidth = getCenterWidth(); - - auto draw = [&](wxRect rect) - { - const int sash2ndHalf = 3; - rect.width -= sash2ndHalf; - dc.GradientFillLinear(rect, COLOR_SASH_GRADIENT_FROM, COLOR_SASH_GRADIENT_TO, wxEAST); - - rect.x += rect.width; - rect.width = sash2ndHalf; - dc.GradientFillLinear(rect, COLOR_SASH_GRADIENT_FROM, COLOR_SASH_GRADIENT_TO, wxWEST); - - static_assert(SASH_SIZE > sash2ndHalf, ""); - }; - - const wxRect rectSashL(centerPosX, 0, SASH_SIZE, GetClientRect().height); - const wxRect rectSashR(centerPosX + centerWidth - SASH_SIZE, 0, SASH_SIZE, GetClientRect().height); - - draw(rectSashL); - draw(rectSashR); -} - - -bool TripleSplitter::hitOnSashLine(int posX) const -{ - const int centerPosX = getCenterPosX(); - const int centerWidth = getCenterWidth(); - - //we don't get events outside of sash, so SASH_HIT_TOLERANCE is currently *useless* - auto hitSash = [&](int sashX) { return sashX - SASH_HIT_TOLERANCE <= posX && posX < sashX + SASH_SIZE + SASH_HIT_TOLERANCE; }; - - return hitSash(centerPosX) || hitSash(centerPosX + centerWidth - SASH_SIZE); //hit one of the two sash lines -} - - -void TripleSplitter::onMouseLeftDown(wxMouseEvent& event) -{ - activeMove.reset(); - - const int posX = event.GetPosition().x; - if (hitOnSashLine(posX)) - activeMove.reset(new SashMove(*this, posX, centerOffset)); - event.Skip(); -} - - -void TripleSplitter::onMouseLeftUp(wxMouseEvent& event) -{ - activeMove.reset(); //nothing else to do, actual work done by onMouseMovement() - event.Skip(); -} - - -void TripleSplitter::onMouseMovement(wxMouseEvent& event) -{ - if (activeMove) - { - centerOffset = activeMove->getCenterOffsetStart() + event.GetPosition().x - activeMove->getMousePosXStart(); - - //CAVEAT: function getCenterPosX() normalizes centerPosX *not* centerOffset! - //This can lead to the strange effect of window not immediately resizing when centerOffset is extremely off limits - //=> normalize centerOffset right here - centerOffset = getCenterPosX() - getCenterPosXOptimal(); - - updateWindowSizes(); - Update(); //no time to wait until idle event! - } - else - { - //we receive those only while above the sash, not the managed windows (except when the managed windows are disabled!) - const int posX = event.GetPosition().x; - if (hitOnSashLine(posX)) - SetCursor(wxCURSOR_SIZEWE); //set window-local only! - else - SetCursor(*wxSTANDARD_CURSOR); - } - event.Skip(); -} - - -void TripleSplitter::onLeaveWindow(wxMouseEvent& event) -{ - //even called when moving from sash over to managed windows! - if (!activeMove) - SetCursor(*wxSTANDARD_CURSOR); - event.Skip(); -} - - -void TripleSplitter::onMouseCaptureLost(wxMouseCaptureLostEvent& event) -{ - activeMove.reset(); - updateWindowSizes(); - //event.Skip(); -> we DID handle it! -} - - -void TripleSplitter::onMouseLeftDouble(wxMouseEvent& event) -{ - const int posX = event.GetPosition().x; - if (hitOnSashLine(posX)) - { - centerOffset = 0; //reset sash according to gravity - updateWindowSizes(); - } - event.Skip(); -} diff --git a/ui/triple_splitter.h b/ui/triple_splitter.h deleted file mode 100644 index 0577dabf..00000000 --- a/ui/triple_splitter.h +++ /dev/null @@ -1,89 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef TRIPPLE_SPLIT_HEADER_8257804292846842573942534254 -#define TRIPPLE_SPLIT_HEADER_8257804292846842573942534254 - -#include -#include -#include -#include - -//a not-so-crappy splitter window - -/* manage three contained windows: - 1. left and right window are stretched - 2. middle window is fixed size - 3. middle window position can be changed via mouse with two sash lines - ----------------- - | | | | - | | | | - | | | | - ----------------- -*/ - -namespace zen -{ -class TripleSplitter : public wxWindow -{ -public: - TripleSplitter(wxWindow* parent, - wxWindowID id = wxID_ANY, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - long style = 0); - - ~TripleSplitter(); - - void setupWindows(wxWindow* winL, wxWindow* winC, wxWindow* winR) - { - assert(winL->GetParent() == this && winC->GetParent() == this && winR->GetParent() == this && !GetSizer()); - windowL = winL; - windowC = winC; - windowR = winR; - updateWindowSizes(); - } - - int getSashOffset() const { return centerOffset; } - void setSashOffset(int off) { centerOffset = off; updateWindowSizes(); } - -private: - void onEraseBackGround(wxEraseEvent& event) {} - void onSizeEvent(wxSizeEvent& event) { updateWindowSizes(); event.Skip(); } - - void onPaintEvent(wxPaintEvent& event) - { - wxPaintDC dc(this); - drawSash(dc); - } - - void updateWindowSizes(); - int getCenterWidth() const; - int getCenterPosX() const; //return normalized posX - int getCenterPosXOptimal() const; - - void drawSash(wxDC& dc); - bool hitOnSashLine(int posX) const; - - void onMouseLeftDown(wxMouseEvent& event); - void onMouseLeftUp(wxMouseEvent& event); - void onMouseMovement(wxMouseEvent& event); - void onLeaveWindow(wxMouseEvent& event); - void onMouseCaptureLost(wxMouseCaptureLostEvent& event); - void onMouseLeftDouble(wxMouseEvent& event); - - class SashMove; - std::unique_ptr activeMove; - - int centerOffset; //offset to add after "gravity" stretching - - wxWindow* windowL; - wxWindow* windowC; - wxWindow* windowR; -}; -} - -#endif //TRIPPLE_SPLIT_HEADER_8257804292846842573942534254 diff --git a/ui/wx_form_build_hide_warnings.h b/ui/wx_form_build_hide_warnings.h deleted file mode 100644 index 71f28340..00000000 --- a/ui/wx_form_build_hide_warnings.h +++ /dev/null @@ -1,22 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef WX_FORM_BUILD_230948324234234 -#define WX_FORM_BUILD_230948324234234 - -//pamper over wxFormBuilder "sub-optimal" code - -#ifdef __GNUC__ -#pragma GCC diagnostic ignored "-Wunused-variable" -#ifndef __clang__ //clang seems to define __GNUC__, but doesn't support this warning -#pragma GCC diagnostic ignored "-Wunused-but-set-variable" -#endif - -#elif defined _MSC_VER -#pragma warning(disable: 4189) -#endif - -#endif //WX_FORM_BUILD_230948324234234 diff --git a/version/version.h b/version/version.h deleted file mode 100644 index 1eff0d52..00000000 --- a/version/version.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef VERSION_HEADER_434343489702544325 -#define VERSION_HEADER_434343489702544325 - -namespace zen -{ -const wchar_t currentVersion[] = L"5.22"; //internal linkage! -} - -#endif diff --git a/wx+/font_size.h b/wx+/font_size.h index 7bfc62fc..4639d194 100644 --- a/wx+/font_size.h +++ b/wx+/font_size.h @@ -48,7 +48,7 @@ void setMainInstructionFont(wxWindow& control) { wxFont font = control.GetFont(); #ifdef ZEN_WIN //http://msdn.microsoft.com/de-DE/library/windows/desktop/aa974176#fonts - font.SetPointSize(numeric::round(wxNORMAL_FONT->GetPointSize() * 4.0 / 3)); + font.SetPointSize(wxNORMAL_FONT->GetPointSize() * 4 / 3); //integer round down //get main instruction color: don't hard-code, respect accessibility! typedef HTHEME (WINAPI* OpenThemeDataFun )(HWND hwnd, LPCWSTR pszClassList); diff --git a/wx+/graph.cpp b/wx+/graph.cpp index 2f5b3775..9298b10b 100644 --- a/wx+/graph.cpp +++ b/wx+/graph.cpp @@ -721,13 +721,13 @@ void Graph2D::render(wxDC& dc) const *low = *high = (*low + *high) / 2; }; - for (auto it = allSelections.begin(); it != allSelections.end(); ++it) + for (const SelectionBlock& sel : allSelections) { //harmonize with active mouse selection above - double screenFromX = cvrtX.realToScreen(it->from.x); - double screenFromY = cvrtY.realToScreen(it->from.y); - double screenToX = cvrtX.realToScreen(it->to.x); - double screenToY = cvrtY.realToScreen(it->to.y); + double screenFromX = cvrtX.realToScreen(sel.from.x); + double screenFromY = cvrtY.realToScreen(sel.from.y); + double screenToX = cvrtX.realToScreen(sel.to.x); + double screenToY = cvrtY.realToScreen(sel.to.y); shrink(&screenFromX, &screenToX); shrink(&screenFromY, &screenToY); diff --git a/wx+/grid.cpp b/wx+/grid.cpp index 184302bf..87f81dda 100644 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -297,6 +297,7 @@ protected: SetToolTip(new wxToolTip(L"a b\n\ a b")); //ugly, but is working (on Windows) tt = GetToolTip(); //should be bound by now + assert(tt); if (tt) tt->SetTip(text); } @@ -542,8 +543,8 @@ namespace class ColumnResizing { public: - ColumnResizing(wxWindow& wnd, size_t col, size_t compPos, int startWidth, int clientPosX) : - wnd_(wnd), col_(col), compPos_(compPos), startWidth_(startWidth), clientPosX_(clientPosX) + ColumnResizing(wxWindow& wnd, size_t col, int startWidth, int clientPosX) : + wnd_(wnd), col_(col), startWidth_(startWidth), clientPosX_(clientPosX) { wnd_.CaptureMouse(); } @@ -554,14 +555,12 @@ public: } size_t getColumn () const { return col_; } - size_t getComponentPos() const { return compPos_; } int getStartWidth () const { return startWidth_; } int getStartPosX () const { return clientPosX_; } private: wxWindow& wnd_; const size_t col_; - const size_t compPos_; const int startWidth_; const int clientPosX_; }; @@ -570,10 +569,9 @@ private: class ColumnMove { public: - ColumnMove(wxWindow& wnd, size_t colFrom, size_t compPos, int clientPosX) : + ColumnMove(wxWindow& wnd, size_t colFrom, int clientPosX) : wnd_(wnd), colFrom_(colFrom), - compPos_(compPos), colTo_(colFrom), clientPosX_(clientPosX), singleClick_(true) { wnd_.CaptureMouse(); } @@ -581,7 +579,6 @@ public: size_t getColumnFrom() const { return colFrom_; } size_t& refColumnTo() { return colTo_; } - size_t getComponentPos() const { return compPos_; } int getStartPosX () const { return clientPosX_; } bool isRealMove() const { return !singleClick_; } @@ -590,7 +587,6 @@ public: private: wxWindow& wnd_; const size_t colFrom_; - const size_t compPos_; size_t colTo_; const int clientPosX_; bool singleClick_; @@ -625,36 +621,35 @@ private: wxPoint labelAreaTL(refParent().CalcScrolledPosition(wxPoint(0, 0)).x, 0); //client coordinates - std::vector> compAbsWidths = refParent().getColWidths(); //resolve stretched widths - for (auto iterComp = compAbsWidths.begin(); iterComp != compAbsWidths.end(); ++iterComp) - for (auto iterCol = iterComp->begin(); iterCol != iterComp->end(); ++iterCol) - { - const size_t col = iterCol - iterComp->begin(); - const int width = iterCol->width_; //don't use unsigned for calculations! - - if (labelAreaTL.x > rect.GetRight()) - return; //done - if (labelAreaTL.x + width > rect.x) - drawColumnLabel(dc, wxRect(labelAreaTL, wxSize(width, colLabelHeight)), col, iterCol->type_, iterComp - compAbsWidths.begin()); - labelAreaTL.x += width; - } + std::vector absWidths = refParent().getColWidths(); //resolve stretched widths + for (auto it = absWidths.begin(); it != absWidths.end(); ++it) + { + const size_t col = it - absWidths.begin(); + const int width = it->width_; //don't use unsigned for calculations! + + if (labelAreaTL.x > rect.GetRight()) + return; //done + if (labelAreaTL.x + width > rect.x) + drawColumnLabel(dc, wxRect(labelAreaTL, wxSize(width, colLabelHeight)), col, it->type_); + labelAreaTL.x += width; + } } - void drawColumnLabel(wxDC& dc, const wxRect& rect, size_t col, ColumnType colType, size_t compPos) + void drawColumnLabel(wxDC& dc, const wxRect& rect, size_t col, ColumnType colType) { - if (auto dataView = refParent().getDataProvider(compPos)) + if (auto dataView = refParent().getDataProvider()) { - const bool isHighlighted = activeResizing ? col == activeResizing->getColumn () && compPos == activeResizing->getComponentPos() : //highlight column on mouse-over - activeMove ? col == activeMove ->getColumnFrom() && compPos == activeMove ->getComponentPos() : - highlight ? col == highlight->first && compPos == highlight->second : + const bool isHighlighted = activeResizing ? col == activeResizing->getColumn () : //highlight column on mouse-over + activeMove ? col == activeMove ->getColumnFrom() : + highlightCol ? col == *highlightCol : false; RecursiveDcClipper clip(dc, rect); dataView->renderColumnLabel(refParent(), dc, rect, colType, isHighlighted); //draw move target location - if (refParent().columnMoveAllowed(compPos)) - if (activeMove && activeMove->isRealMove() && activeMove->getComponentPos() == compPos) + if (refParent().allowColumnMove) + if (activeMove && activeMove->isRealMove()) { if (col + 1 == activeMove->refColumnTo()) //handle pos 1, 2, .. up to "at end" position dc.GradientFillLinear(wxRect(rect.GetTopRight(), rect.GetBottomRight() + wxPoint(-2, 0)), COLOR_LABEL_GRADIENT_FROM, *wxBLUE, wxSOUTH); @@ -677,11 +672,11 @@ private: 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, action->compPos)) - activeResizing.reset(new ColumnResizing(*this, action->col, action->compPos, *colWidth, event.GetPosition().x)); + if (Opt colWidth = refParent().getColWidth(action->col)) + activeResizing.reset(new ColumnResizing(*this, action->col, *colWidth, event.GetPosition().x)); } else //a move or single click - activeMove.reset(new ColumnMove(*this, action->col, action->compPos, event.GetPosition().x)); + activeMove.reset(new ColumnMove(*this, action->col, event.GetPosition().x)); } event.Skip(); } @@ -692,10 +687,9 @@ private: if (activeMove) { - const size_t compPos = activeMove->getComponentPos(); if (activeMove->isRealMove()) { - if (refParent().columnMoveAllowed(compPos)) + if (refParent().allowColumnMove) { const auto colFrom = activeMove->getColumnFrom(); auto colTo = activeMove->refColumnTo(); @@ -703,13 +697,13 @@ private: if (colTo > colFrom) //simulate "colFrom" deletion --colTo; - refParent().moveColumn(colFrom, colTo, compPos); + refParent().moveColumn(colFrom, colTo); } } else //notify single label click { - if (const Opt colType = refParent().colToType(activeMove->getColumnFrom(), compPos)) - sendEventNow(GridClickEvent(EVENT_GRID_COL_LABEL_MOUSE_LEFT, event, -1, *colType, compPos)); + if (const Opt colType = refParent().colToType(activeMove->getColumnFrom())) + sendEventNow(GridClickEvent(EVENT_GRID_COL_LABEL_MOUSE_LEFT, event, -1, *colType)); } activeMove.reset(); } @@ -733,10 +727,10 @@ private: if (action->wantResize) { //auto-size visible range on double-click - const int bestWidth = refParent().getBestColumnSize(action->col, action->compPos); //return -1 on error + const int bestWidth = refParent().getBestColumnSize(action->col); //return -1 on error if (bestWidth >= 0) { - refParent().setColWidthAndNotify(bestWidth, action->col, action->compPos); + refParent().setColumnWidth(bestWidth, action->col, ALLOW_GRID_EVENT); refParent().Refresh(); //refresh main grid as well! } } @@ -748,16 +742,15 @@ private: if (activeResizing) { const auto col = activeResizing->getColumn(); - const auto compPos = activeResizing->getComponentPos(); const int newWidth = activeResizing->getStartWidth() + event.GetPosition().x - activeResizing->getStartPosX(); //set width tentatively - refParent().setColWidthAndNotify(newWidth, col, compPos); + refParent().setColumnWidth(newWidth, col, ALLOW_GRID_EVENT); //check if there's a small gap after last column, if yes, fill it int gapWidth = GetClientSize().GetWidth() - refParent().getColWidthsSum(GetClientSize().GetWidth()); if (std::abs(gapWidth) < COLUMN_FILL_GAP_TOLERANCE) - refParent().setColWidthAndNotify(newWidth + gapWidth, col, compPos); + refParent().setColumnWidth(newWidth + gapWidth, col, ALLOW_GRID_EVENT); refParent().Refresh(); //refresh columns on main grid as well! } @@ -768,7 +761,7 @@ private: { activeMove->setRealMove(); - const auto col = refParent().clientPosToMoveTargetColumn(event.GetPosition(), activeMove->getComponentPos()); + const ptrdiff_t col = refParent().clientPosToMoveTargetColumn(event.GetPosition()); if (col >= 0) activeMove->refColumnTo() = col; } @@ -777,7 +770,7 @@ private: { if (const Opt action = refParent().clientPosToColumnAction(event.GetPosition())) { - highlight.reset(new std::pair(action->col, action->compPos)); + highlightCol.reset(new size_t(action->col)); if (action->wantResize) SetCursor(wxCURSOR_SIZEWE); //set window-local only! :) @@ -786,7 +779,7 @@ private: } else { - highlight.reset(); + highlightCol.reset(); SetCursor(*wxSTANDARD_CURSOR); } } @@ -795,11 +788,9 @@ private: const wxString toolTip = [&]() -> wxString { const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition()); - if (const auto colInfo = refParent().getColumnAtPos(absPos.x)) //returns (column type, compPos) - { - if (auto prov = refParent().getDataProvider(colInfo->second)) - return prov->getToolTip(colInfo->first); - } + if (const Opt ct = refParent().getColumnAtPos(absPos.x)) + if (auto prov = refParent().getDataProvider()) + return prov->getToolTip(*ct); return wxString(); }(); setToolTip(toolTip); @@ -810,7 +801,7 @@ private: virtual void onLeaveWindow(wxMouseEvent& event) { - highlight.reset(); //wxEVT_LEAVE_WINDOW does not respect mouse capture! -> however highlight is drawn unconditionally during move/resize! + highlightCol.reset(); //wxEVT_LEAVE_WINDOW does not respect mouse capture! -> however highlight is drawn unconditionally during move/resize! Refresh(); event.Skip(); } @@ -819,15 +810,15 @@ private: { if (const Opt action = refParent().clientPosToColumnAction(event.GetPosition())) { - if (const Opt colType = refParent().colToType(action->col, action->compPos)) - sendEventNow(GridClickEvent(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, event, -1, *colType, action->compPos)); //notify right click + if (const Opt colType = refParent().colToType(action->col)) + sendEventNow(GridClickEvent(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, event, -1, *colType)); //notify right click } event.Skip(); } std::unique_ptr activeResizing; std::unique_ptr activeMove; - std::unique_ptr> highlight; //(column, component) mouse-over + std::unique_ptr highlightCol; //column during mouse-over }; //---------------------------------------------------------------------------------------------------------------- @@ -893,13 +884,13 @@ public: } } - void setCursor(size_t row, size_t compPos) + void setCursor(size_t row) { - cursor = std::make_pair(row, compPos); + cursorRow = row; activeSelection.reset(); //e.g. user might search with F3 while holding down left mouse button selectionAnchor = row; } - std::pair getCursor() const { return cursor; } // (row, component position) + size_t getCursor() const { return cursorRow; } private: virtual void render(wxDC& dc, const wxRect& rect) @@ -924,27 +915,25 @@ private: wxPoint cellAreaTL(refParent().CalcScrolledPosition(wxPoint(0, 0))); //client coordinates - std::vector> compAbsWidths = refParent().getColWidths(); //resolve stretched widths - for (auto iterComp = compAbsWidths.begin(); iterComp != compAbsWidths.end(); ++iterComp) + std::vector absWidths = refParent().getColWidths(); //resolve stretched widths { - int compWidth = 0; - for (const ColumnWidth& cw : *iterComp) - compWidth += cw.width_; + int totalWidth = 0; + for (const ColumnWidth& cw : absWidths) + totalWidth += cw.width_; - const size_t compPos = iterComp - compAbsWidths.begin(); - if (auto prov = refParent().getDataProvider(compPos)) + if (auto prov = refParent().getDataProvider()) { //draw background lines for (int row = rowFirst; row < rowLast; ++row) { - const wxRect rowRect(cellAreaTL + wxPoint(0, row * rowHeight), wxSize(compWidth, rowHeight)); + const wxRect rowRect(cellAreaTL + wxPoint(0, row * rowHeight), wxSize(totalWidth, rowHeight)); RecursiveDcClipper dummy2(dc, rowRect); //solve issues with drawBackground() painting in area outside of rect //(which is not also refreshed by renderCell()) -> keep small scope! - prov->renderRowBackgound(dc, rowRect, row, refParent().IsThisEnabled(), drawAsSelected(row, compPos)); + prov->renderRowBackgound(dc, rowRect, row, refParent().IsThisEnabled(), drawAsSelected(row)); } //draw single cells, column by column - for (const ColumnWidth& cw : *iterComp) + for (const ColumnWidth& cw : absWidths) { if (cellAreaTL.x > rect.GetRight()) return; //done @@ -955,27 +944,25 @@ private: const wxRect cellRect(cellAreaTL.x, cellAreaTL.y + row * rowHeight, cw.width_, rowHeight); RecursiveDcClipper clip(dc, cellRect); - prov->renderCell(dc, cellRect, row, cw.type_, drawAsSelected(row, compPos)); + prov->renderCell(dc, cellRect, row, cw.type_, drawAsSelected(row)); } cellAreaTL.x += cw.width_; } } - else - cellAreaTL.x += compWidth; } } - bool drawAsSelected(size_t row, size_t compPos) const + bool drawAsSelected(size_t row) const { if (activeSelection) //check if user is currently selecting with mouse { const size_t rowFrom = std::min(activeSelection->getStartRow(), activeSelection->getCurrentRow()); const size_t rowTo = std::max(activeSelection->getStartRow(), activeSelection->getCurrentRow()); - if (compPos == activeSelection->getComponentPos() && rowFrom <= row && row <= rowTo) + if (rowFrom <= row && row <= rowTo) return activeSelection->isPositiveSelect(); //overwrite default } - return refParent().isSelected(row, compPos); + return refParent().isSelected(row); } virtual void onMouseLeftDown (wxMouseEvent& event) { onMouseDown(event); } @@ -989,12 +976,10 @@ private: const auto row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range if (row >= 0) { - const auto colInfo = refParent().getColumnAtPos(absPos.x); //returns (column type, compPos) - - const ColumnType colType = colInfo ? colInfo->first : DUMMY_COLUMN_TYPE; - const ptrdiff_t compPos = colInfo ? colInfo->second : -1; + const Opt ct = refParent().getColumnAtPos(absPos.x); + const ColumnType colType = ct ? *ct : DUMMY_COLUMN_TYPE; //client is interested in all double-clicks, even those outside of the grid! - sendEventNow(GridClickEvent(EVENT_GRID_MOUSE_LEFT_DOUBLE, event, row, colType, compPos)); + sendEventNow(GridClickEvent(EVENT_GRID_MOUSE_LEFT_DOUBLE, event, row, colType)); } event.Skip(); } @@ -1008,34 +993,28 @@ private: const ptrdiff_t row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range if (row >= 0) { - const auto colInfo = refParent().getColumnAtPos(absPos.x); //returns (column type, compPos) - const ColumnType colType = colInfo ? colInfo->first : DUMMY_COLUMN_TYPE; - const ptrdiff_t compPos = colInfo ? colInfo->second : -1; + const Opt ct = refParent().getColumnAtPos(absPos.x); + const ColumnType colType = ct ? *ct : DUMMY_COLUMN_TYPE; - if (!event.RightDown() || !refParent().isSelected(row, compPos)) //do NOT start a new selection if user right-clicks on a selected area! + if (!event.RightDown() || !refParent().isSelected(row)) //do NOT start a new selection if user right-clicks on a selected area! { if (event.ControlDown()) - { - if (compPos >= 0) - activeSelection.reset(new MouseSelection(*this, row, compPos, !refParent().isSelected(row, compPos))); - } + activeSelection.reset(new MouseSelection(*this, row, !refParent().isSelected(row))); else if (event.ShiftDown()) { - if (compPos >= 0) - activeSelection.reset(new MouseSelection(*this, selectionAnchor, compPos, true)); - refParent().clearSelectionAllAndNotify(); + activeSelection.reset(new MouseSelection(*this, selectionAnchor, true)); + refParent().clearSelection(ALLOW_GRID_EVENT); } else { - if (compPos >= 0) - activeSelection.reset(new MouseSelection(*this, row, compPos, true)); - refParent().clearSelectionAllAndNotify(); + activeSelection.reset(new MouseSelection(*this, row, true)); + refParent().clearSelection(ALLOW_GRID_EVENT); } } - //notify event *after* potential "clearSelectionAllAndNotify()" above: a client should first receive a GridRangeSelectEvent for clearing the grid, if necessary, + //notify event *after* potential "clearSelection(true)" above: a client should first receive a GridRangeSelectEvent for clearing the grid, if necessary, //then GridClickEvent and the associated GridRangeSelectEvent one after the other - GridClickEvent mouseEvent(event.RightDown() ? EVENT_GRID_MOUSE_RIGHT_DOWN : EVENT_GRID_MOUSE_LEFT_DOWN, event, row, colType, compPos); + GridClickEvent mouseEvent(event.RightDown() ? EVENT_GRID_MOUSE_RIGHT_DOWN : EVENT_GRID_MOUSE_LEFT_DOWN, event, row, colType); sendEventNow(mouseEvent); Refresh(); @@ -1052,22 +1031,21 @@ private: { if (activeSelection->getCurrentRow() < rowCount) { - cursor.first = activeSelection->getCurrentRow(); + cursorRow = activeSelection->getCurrentRow(); selectionAnchor = activeSelection->getStartRow(); //allowed to be "out of range" } else if (activeSelection->getStartRow() < rowCount) //don't change cursor if "to" and "from" are out of range { - cursor.first = rowCount - 1; + cursorRow = rowCount - 1; selectionAnchor = activeSelection->getStartRow(); //allowed to be "out of range" } else //total selection "out of range" - selectionAnchor = cursor.first; + selectionAnchor = cursorRow; } //slight deviation from Explorer: change cursor while dragging mouse! -> unify behavior with shift + direction keys refParent().selectRangeAndNotify(activeSelection->getStartRow (), //from activeSelection->getCurrentRow(), //to - activeSelection->getComponentPos(), activeSelection->isPositiveSelect()); activeSelection.reset(); } @@ -1075,14 +1053,12 @@ private: //this one may point to row which is not in visible area! const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition()); - const auto row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range - const auto colInfo = refParent().getColumnAtPos(absPos.x); //returns optional pair (column type, compPos) - - const ColumnType colType = colInfo ? colInfo->first : DUMMY_COLUMN_TYPE; //we probably should notify even if colInfo is invalid! - const ptrdiff_t compPos = colInfo ? colInfo->second : -1; + const ptrdiff_t row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range + const Opt ct = refParent().getColumnAtPos(absPos.x); + const ColumnType colType = ct ? *ct : DUMMY_COLUMN_TYPE; //we probably should notify even if colInfo is invalid! //notify click event after the range selection! e.g. this makes sure the selection is applied before showing a context menu - sendEventNow(GridClickEvent(event.RightUp() ? EVENT_GRID_MOUSE_RIGHT_UP : EVENT_GRID_MOUSE_LEFT_UP, event, row, colType, compPos)); + sendEventNow(GridClickEvent(event.RightUp() ? EVENT_GRID_MOUSE_RIGHT_UP : EVENT_GRID_MOUSE_LEFT_UP, event, row, colType)); Refresh(); event.Skip(); //allow changing focus @@ -1106,13 +1082,11 @@ private: const ptrdiff_t rowCount = refParent().getRowCount(); const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition()); - const auto row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range - const auto colInfo = refParent().getColumnAtPos(absPos.x); //returns (column type, compPos) - if (colInfo && 0 <= row && row < rowCount) - { - if (auto prov = refParent().getDataProvider(colInfo->second)) - return prov->getToolTip(row, colInfo->first); - } + const ptrdiff_t row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range + const Opt ct = refParent().getColumnAtPos(absPos.x); + if (ct && 0 <= row && row < rowCount) + if (auto prov = refParent().getDataProvider()) + return prov->getToolTip(row, *ct); return wxString(); }(); @@ -1137,17 +1111,16 @@ private: } const ptrdiff_t rowCount = refParent().getRowCount(); - if (rowCount <= 0 || refParent().comp.empty()) + if (rowCount <= 0) { event.Skip(); return; } - auto setSingleSelection = [&](ptrdiff_t row, ptrdiff_t compPos) + auto setSingleSelection = [&](ptrdiff_t row) { - numeric::confine(row, 0, rowCount - 1); - numeric::confine(compPos, 0, refParent().comp.size() - 1); - refParent().setGridCursor(row, compPos); //behave like an "external" set cursor! + numeric::confine(row, 0, rowCount - 1); + refParent().setGridCursor(row); //behave like an "external" set cursor! }; auto setSelectionRange = [&](ptrdiff_t row) @@ -1156,11 +1129,10 @@ private: numeric::confine(row, 0, rowCount - 1); - for (Grid::Component& c : refParent().comp) - c.selection.clear(); //clear selection, do NOT fire event - refParent().selectRangeAndNotify(selectionAnchor, row, cursor.second); //set new selection + fire event + refParent().selection.clear(); //clear selection, do NOT fire event + refParent().selectRangeAndNotify(selectionAnchor, row); //set new selection + fire event - cursor.first = row; //don't call setCursor() since it writes to "selectionAnchor"! + cursorRow = row; //don't call setCursor() since it writes to "selectionAnchor"! this->makeRowVisible(row); refParent().Refresh(); }; @@ -1170,21 +1142,21 @@ private: case WXK_UP: case WXK_NUMPAD_UP: if (event.ShiftDown()) - setSelectionRange(cursor.first - 1); + setSelectionRange(cursorRow - 1); else if (event.ControlDown()) refParent().scrollDelta(0, -1); else - setSingleSelection(cursor.first - 1, cursor.second); + setSingleSelection(cursorRow - 1); return; //swallow event: wxScrolledWindow, wxWidgets 2.9.3 on Kubuntu x64 processes arrow keys: prevent this! case WXK_DOWN: case WXK_NUMPAD_DOWN: if (event.ShiftDown()) - setSelectionRange(cursor.first + 1); + setSelectionRange(cursorRow + 1); else if (event.ControlDown()) refParent().scrollDelta(0, 1); else - setSingleSelection(cursor.first + 1, cursor.second); + setSingleSelection(cursorRow + 1); return; //swallow event case WXK_LEFT: @@ -1194,7 +1166,7 @@ private: else if (event.ShiftDown()) ; else - setSingleSelection(cursor.first, cursor.second - 1); + setSingleSelection(cursorRow); return; case WXK_RIGHT: @@ -1204,7 +1176,7 @@ private: else if (event.ShiftDown()) ; else - setSingleSelection(cursor.first, cursor.second + 1); + setSingleSelection(cursorRow); return; case WXK_HOME: @@ -1212,9 +1184,9 @@ private: if (event.ShiftDown()) setSelectionRange(0); else if (event.ControlDown()) - setSingleSelection(0, 0); + setSingleSelection(0); else - setSingleSelection(0, cursor.second); + setSingleSelection(0); return; case WXK_END: @@ -1222,39 +1194,39 @@ private: if (event.ShiftDown()) setSelectionRange(rowCount - 1); else if (event.ControlDown()) - setSingleSelection(rowCount - 1, refParent().comp.size() - 1); + setSingleSelection(rowCount - 1); else - setSingleSelection(rowCount - 1, cursor.second); + setSingleSelection(rowCount - 1); return; case WXK_PAGEUP: case WXK_NUMPAD_PAGEUP: if (event.ShiftDown()) - setSelectionRange(cursor.first - GetClientSize().GetHeight() / rowLabelWin_.getRowHeight()); + setSelectionRange(cursorRow - GetClientSize().GetHeight() / rowLabelWin_.getRowHeight()); else if (event.ControlDown()) ; else - setSingleSelection(cursor.first - GetClientSize().GetHeight() / rowLabelWin_.getRowHeight(), cursor.second); + setSingleSelection(cursorRow - GetClientSize().GetHeight() / rowLabelWin_.getRowHeight()); return; case WXK_PAGEDOWN: case WXK_NUMPAD_PAGEDOWN: if (event.ShiftDown()) - setSelectionRange(cursor.first + GetClientSize().GetHeight() / rowLabelWin_.getRowHeight()); + setSelectionRange(cursorRow + GetClientSize().GetHeight() / rowLabelWin_.getRowHeight()); else if (event.ControlDown()) ; else - setSingleSelection(cursor.first + GetClientSize().GetHeight() / rowLabelWin_.getRowHeight(), cursor.second); + setSingleSelection(cursorRow + GetClientSize().GetHeight() / rowLabelWin_.getRowHeight()); return; case 'A': //Ctrl + A - select all if (event.ControlDown()) - refParent().selectRangeAndNotify(0, rowCount, cursor.second); + refParent().selectRangeAndNotify(0, rowCount); break; case WXK_NUMPAD_ADD: //CTRL + '+' - auto-size all if (event.ControlDown()) - refParent().autoSizeColumns(cursor.second); + refParent().autoSizeColumns(ALLOW_GRID_EVENT); return; } @@ -1266,8 +1238,8 @@ private: class MouseSelection : private wxEvtHandler { public: - MouseSelection(MainWin& wnd, size_t rowStart, size_t compPos, bool positiveSelect) : - wnd_(wnd), rowStart_(rowStart), compPos_(compPos), rowCurrent_(rowStart), positiveSelect_(positiveSelect), toScrollX(0), toScrollY(0), + MouseSelection(MainWin& wnd, size_t rowStart, bool positiveSelect) : + wnd_(wnd), rowStart_(rowStart), rowCurrent_(rowStart), positiveSelect_(positiveSelect), toScrollX(0), toScrollY(0), tickCountLast(getTicks()), ticksPerSec_(ticksPerSec()) { @@ -1279,7 +1251,6 @@ private: ~MouseSelection() { if (wnd_.HasCapture()) wnd_.ReleaseMouse(); } size_t getStartRow () const { return rowStart_; } - size_t getComponentPos () const { return compPos_; } size_t getCurrentRow () const { return rowCurrent_; } bool isPositiveSelect() const { return positiveSelect_; } //are we selecting or unselecting? @@ -1352,7 +1323,6 @@ private: MainWin& wnd_; const size_t rowStart_; - const size_t compPos_; ptrdiff_t rowCurrent_; const bool positiveSelect_; wxTimer timer; @@ -1397,7 +1367,7 @@ private: std::unique_ptr activeSelection; //bound while user is selecting with mouse - std::pair cursor; //(row, component position), always valid! still unsigned type to facilitate "onKeyDown()" + ptrdiff_t cursorRow; size_t selectionAnchor; bool gridUpdatePending; }; @@ -1415,7 +1385,8 @@ Grid::Grid(wxWindow* parent, showScrollbarY(SB_SHOW_AUTOMATIC), colLabelHeight(0), //dummy init drawRowLabel(true), - comp(1), + allowColumnMove(true), + allowColumnResize(true), rowCountOld(0) { cornerWin_ = new CornerWin (*this); // @@ -1617,23 +1588,31 @@ void Grid::showRowLabel(bool show) } -std::vector Grid::getSelectedRows(size_t compPos) const +void Grid::selectAllRows(GridEventPolicy rangeEventPolicy) { - if (compPos < comp.size()) - return comp[compPos].selection.get(); - assert(false); - return std::vector(); + selection.selectAll(); + mainWin_->Refresh(); + + if (rangeEventPolicy == ALLOW_GRID_EVENT) //notify event, even if we're not triggered by user interaction + { + GridRangeSelectEvent selEvent(0, getRowCount(), true); + if (wxEvtHandler* evtHandler = GetEventHandler()) + evtHandler->ProcessEvent(selEvent); + } } -void Grid::setSelectedRows(const std::vector& sel, size_t compPos) +void Grid::clearSelection(GridEventPolicy rangeEventPolicy) { - if (compPos < comp.size()) + selection.clear(); + mainWin_->Refresh(); + + if (rangeEventPolicy == ALLOW_GRID_EVENT) //notify event, even if we're not triggered by user interaction { - comp[compPos].selection.set(sel); - Refresh(); + GridRangeSelectEvent unselectionEvent(0, getRowCount(), false); + if (wxEvtHandler* evtHandler = GetEventHandler()) + evtHandler->ProcessEvent(unselectionEvent); } - else assert(false); } @@ -1667,7 +1646,7 @@ void Grid::redirectRowLabelEvent(wxMouseEvent& event) size_t Grid::getRowCount() const { - return comp.empty() ? 0 : comp.front().dataView_ ? comp.front().dataView_->getRowCount() : 0; + return dataView_ ? dataView_->getRowCount() : 0; } @@ -1680,9 +1659,8 @@ void Grid::Refresh(bool eraseBackground, const wxRect* rect) updateWindowSizes(); } - for (Component& c : comp) - if (c.selection.size() != rowCountNew) //clear selection only when needed (consider setSelectedRows()) - c.selection.init(rowCountNew); + if (selection.size() != rowCountNew) //clear selection only when needed (consider setSelectedRows()) + selection.init(rowCountNew); wxScrolledWindow::Refresh(eraseBackground, rect); } @@ -1696,56 +1674,49 @@ void Grid::setRowHeight(int height) } -void Grid::setColumnConfig(const std::vector& attr, size_t compPos) +void Grid::setColumnConfig(const std::vector& attr) { - if (compPos < comp.size()) - { - //hold ownership of non-visible columns - comp[compPos].oldColAttributes = attr; + //hold ownership of non-visible columns + oldColAttributes = attr; - std::vector visibleCols; - for (const ColumnAttribute& ca : attr) - if (ca.visible_) - visibleCols.push_back(VisibleColumn(ca.type_, ca.offset_, ca.stretch_)); + std::vector visCols; + for (const ColumnAttribute& ca : attr) + if (ca.visible_) + visCols.push_back(VisibleColumn(ca.type_, ca.offset_, ca.stretch_)); - //"ownership" of visible columns is now within Grid - comp[compPos].visibleCols = visibleCols; + //"ownership" of visible columns is now within Grid + visibleCols = visCols; - updateWindowSizes(); - Refresh(); - } + updateWindowSizes(); + Refresh(); } -std::vector Grid::getColumnConfig(size_t compPos) const +std::vector Grid::getColumnConfig() const { - if (compPos < comp.size()) - { - //get non-visible columns (+ outdated visible ones) - std::vector output = comp[compPos].oldColAttributes; + //get non-visible columns (+ outdated visible ones) + std::vector output = oldColAttributes; - auto iterVcols = comp[compPos].visibleCols.begin(); - auto iterVcolsend = comp[compPos].visibleCols.end(); + auto iterVcols = visibleCols.begin(); + auto iterVcolsend = visibleCols.end(); - //update visible columns but keep order of non-visible ones! - for (ColumnAttribute& ca : output) - if (ca.visible_) + //update visible columns but keep order of non-visible ones! + for (ColumnAttribute& ca : output) + if (ca.visible_) + { + if (iterVcols != iterVcolsend) { - if (iterVcols != iterVcolsend) - { - ca.type_ = iterVcols->type_; - ca.stretch_ = iterVcols->stretch_; - ca.offset_ = iterVcols->offset_; - ++iterVcols; - } - else - assert(false); + ca.type_ = iterVcols->type_; + ca.stretch_ = iterVcols->stretch_; + ca.offset_ = iterVcols->offset_; + ++iterVcols; } - assert(iterVcols == iterVcolsend); + else + assert(false); + } + assert(iterVcols == iterVcolsend); - return output; - } - return std::vector(); + return output; } @@ -1841,34 +1812,6 @@ void Grid::SetScrollbar(int orientation, int position, int thumbSize, int range, } #endif -//get rid of scrollbars, but preserve scrolling behavior! -#ifdef ZEN_WIN -WXLRESULT Grid::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) -{ - //we land here if wxWindowMSW::MSWWindowProc() couldn't handle the message - //http://msdn.microsoft.com/en-us/library/windows/desktop/ms645614(v=vs.85).aspx - if (nMsg == WM_MOUSEHWHEEL) //horizontal wheel - { - const int distance = GET_WHEEL_DELTA_WPARAM(wParam); - const int delta = WHEEL_DELTA; - int rotations = distance / delta; - - if (GetLayoutDirection() == wxLayout_RightToLeft) - rotations = -rotations; - - static int linesPerRotation = -1; - if (linesPerRotation < 0) - if (!::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerRotation, 0)) - linesPerRotation = 3; - - scrollDelta(rotations * linesPerRotation, 0); //in scroll units - return 0; //"If an application processes this message, it should return zero." - } - - return wxScrolledWindow::MSWDefWindowProc(nMsg, wParam, lParam); -} -#endif - wxWindow& Grid::getCornerWin () { return *cornerWin_; } wxWindow& Grid::getRowLabelWin() { return *rowLabelWin_; } @@ -1877,27 +1820,20 @@ wxWindow& Grid::getMainWin () { return *mainWin_; } const wxWindow& Grid::getMainWin() const { return *mainWin_; } -wxRect Grid::getColumnLabelArea(ColumnType colType, size_t compPos) const +wxRect Grid::getColumnLabelArea(ColumnType colType) const { - std::vector> compAbsWidths = getColWidths(); //resolve negative/stretched widths - if (compPos < compAbsWidths.size()) - { - auto iterComp = compAbsWidths.begin() + compPos; - - auto iterCol = std::find_if(iterComp->begin(), iterComp->end(), [&](const ColumnWidth& cw) { return cw.type_ == colType; }); - if (iterCol != iterComp->end()) - { - ptrdiff_t posX = 0; - for (auto it = compAbsWidths.begin(); it != iterComp; ++it) - for (const ColumnWidth& cw : *it) - posX += cw.width_; + std::vector absWidths = getColWidths(); //resolve negative/stretched widths - for (auto it = iterComp->begin(); it != iterCol; ++it) - posX += it->width_; + auto iterCol = std::find_if(absWidths.begin(), absWidths.end(), [&](const ColumnWidth& cw) { return cw.type_ == colType; }); + if (iterCol != absWidths.end()) + { + ptrdiff_t posX = 0; + for (auto it = absWidths.begin(); it != iterCol; ++it) + posX += it->width_; - return wxRect(wxPoint(posX, 0), wxSize(iterCol->width_, colLabelHeight)); - } + return wxRect(wxPoint(posX, 0), wxSize(iterCol->width_, colLabelHeight)); } + return wxRect(); } @@ -1907,32 +1843,26 @@ Opt Grid::clientPosToColumnAction(const wxPoint& pos) const const int absPosX = CalcUnscrolledPosition(pos).x; if (absPosX >= 0) { - int accuWidth = 0; + const int resizeTolerance = allowColumnResize ? COLUMN_RESIZE_TOLERANCE : 0; + std::vector absWidths = getColWidths(); //resolve stretched widths - std::vector> compAbsWidths = getColWidths(); //resolve stretched widths - for (size_t compPos = 0; compPos < compAbsWidths.size(); ++compPos) + int accuWidth = 0; + for (size_t col = 0; col < absWidths.size(); ++col) { - const int resizeTolerance = columnResizeAllowed(compPos) ? COLUMN_RESIZE_TOLERANCE : 0; - - for (size_t col = 0; col < compAbsWidths[compPos].size(); ++col) + accuWidth += absWidths[col].width_; + if (std::abs(absPosX - accuWidth) < resizeTolerance) { - accuWidth += compAbsWidths[compPos][col].width_; - if (std::abs(absPosX - accuWidth) < resizeTolerance) - { - ColAction out = {}; - out.wantResize = true; - out.col = col; - out.compPos = compPos; - return out; - } - else if (absPosX < accuWidth) - { - ColAction out = {}; - out.wantResize = false; - out.col = col; - out.compPos = compPos; - return out; - } + ColAction out = {}; + out.wantResize = true; + out.col = col; + return out; + } + else if (absPosX < accuWidth) + { + ColAction out = {}; + out.wantResize = false; + out.col = col; + return out; } } } @@ -1940,54 +1870,42 @@ Opt Grid::clientPosToColumnAction(const wxPoint& pos) const } -void Grid::moveColumn(size_t colFrom, size_t colTo, size_t compPos) +void Grid::moveColumn(size_t colFrom, size_t colTo) { - if (compPos < comp.size()) + if (colFrom < visibleCols.size() && + colTo < visibleCols.size() && + colTo != colFrom) { - auto& visibleCols = comp[compPos].visibleCols; - if (colFrom < visibleCols.size() && - colTo < visibleCols.size() && - colTo != colFrom) - { - const auto colAtt = visibleCols[colFrom]; - visibleCols.erase (visibleCols.begin() + colFrom); - visibleCols.insert(visibleCols.begin() + colTo, colAtt); - } + const auto colAtt = visibleCols[colFrom]; + visibleCols.erase (visibleCols.begin() + colFrom); + visibleCols.insert(visibleCols.begin() + colTo, colAtt); } } -ptrdiff_t Grid::clientPosToMoveTargetColumn(const wxPoint& pos, size_t compPos) const +ptrdiff_t Grid::clientPosToMoveTargetColumn(const wxPoint& pos) const { - std::vector> compAbsWidths = getColWidths(); //resolve negative/stretched widths - if (compPos < compAbsWidths.size()) - { - auto iterComp = compAbsWidths.begin() + compPos; - const int absPosX = CalcUnscrolledPosition(pos).x; + std::vector absWidths = getColWidths(); //resolve negative/stretched widths - int accuWidth = 0; - for (auto it = compAbsWidths.begin(); it != iterComp; ++it) - for (const ColumnWidth& cw : *it) - accuWidth += cw.width_; + const int absPosX = CalcUnscrolledPosition(pos).x; - for (auto iterCol = iterComp->begin(); iterCol != iterComp->end(); ++iterCol) - { - const int width = iterCol->width_; //beware dreaded unsigned conversions! - accuWidth += width; + int accuWidth = 0; + for (auto iterCol = absWidths.begin(); iterCol != absWidths.end(); ++iterCol) + { + const int width = iterCol->width_; //beware dreaded unsigned conversions! + accuWidth += width; - if (absPosX < accuWidth - width / 2) - return iterCol - iterComp->begin(); - } - return iterComp->size(); + if (absPosX < accuWidth - width / 2) + return iterCol - absWidths.begin(); } - return -1; + return absWidths.size(); } -Opt Grid::colToType(size_t col, size_t compPos) const +Opt Grid::colToType(size_t col) const { - if (compPos < comp.size() && col < comp[compPos].visibleCols.size()) - return comp[compPos].visibleCols[col].type_; + if (col < visibleCols.size()) + return visibleCols[col].type_; return NoValue(); } @@ -1995,96 +1913,61 @@ Opt Grid::colToType(size_t col, size_t compPos) const ptrdiff_t Grid::getRowAtPos(int posY) const { return rowLabelWin_->getRowAtPos(posY); } -Opt> Grid::getColumnAtPos(int posX) const +Opt Grid::getColumnAtPos(int posX) const { if (posX >= 0) { - std::vector> compAbsWidths = getColWidths(); //resolve negative/stretched widths - int accWidth = 0; - for (size_t compPos = 0; compPos < compAbsWidths.size(); ++compPos) - for (const ColumnWidth& cw : compAbsWidths[compPos]) - { - accWidth += cw.width_; - if (posX < accWidth) - return std::make_pair(cw.type_, compPos); - } + for (const ColumnWidth& cw : getColWidths()) + { + accWidth += cw.width_; + if (posX < accWidth) + return cw.type_; + } } return NoValue(); } -wxRect Grid::getCellArea(size_t row, ColumnType colType, size_t compPos) const +wxRect Grid::getCellArea(size_t row, ColumnType colType) const { - const wxRect& colArea = getColumnLabelArea(colType, compPos); + const wxRect& colArea = getColumnLabelArea(colType); const wxRect& rowArea = rowLabelWin_->getRowLabelArea(row); return wxRect(wxPoint(colArea.x, rowArea.y), wxSize(colArea.width, rowArea.height)); } -void Grid::setGridCursor(size_t row, size_t compPos) +void Grid::setGridCursor(size_t row) { - if (compPos < comp.size()) - { - mainWin_->setCursor(row, compPos); - mainWin_->makeRowVisible(row); + mainWin_->setCursor(row); + mainWin_->makeRowVisible(row); - for (Grid::Component& c : comp) - c.selection.clear(); //clear selection, do NOT fire event - selectRangeAndNotify(row, row, compPos); //set new selection + fire event + selection.clear(); //clear selection, do NOT fire event + selectRangeAndNotify(row, row); //set new selection + fire event - mainWin_->Refresh(); - rowLabelWin_->Refresh(); //row labels! (Kubuntu) - } + mainWin_->Refresh(); + rowLabelWin_->Refresh(); //row labels! (Kubuntu) } -void Grid::selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, size_t compPos, bool positive) +void Grid::selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positive) { - if (compPos < comp.size()) - { - //sort + convert to half-open range - auto rowFirst = std::min(rowFrom, rowTo); - auto rowLast = std::max(rowFrom, rowTo) + 1; - - const size_t rowCount = getRowCount(); - numeric::confine(rowFirst, 0, rowCount); - numeric::confine(rowLast, 0, rowCount); - - comp[compPos].selection.selectRange(rowFirst, rowLast, positive); - - //notify event - GridRangeSelectEvent selectionEvent(rowFirst, rowLast, compPos, positive); - if (wxEvtHandler* evtHandler = GetEventHandler()) - evtHandler->ProcessEvent(selectionEvent); - - mainWin_->Refresh(); - } -} + //sort + convert to half-open range + auto rowFirst = std::min(rowFrom, rowTo); + auto rowLast = std::max(rowFrom, rowTo) + 1; + const size_t rowCount = getRowCount(); + numeric::confine(rowFirst, 0, rowCount); + numeric::confine(rowLast, 0, rowCount); -void Grid::clearSelection(bool emitSelectRangeEvent, size_t compPos) -{ - if (compPos < comp.size()) - { - comp[compPos].selection.clear(); - mainWin_->Refresh(); - - if (emitSelectRangeEvent) - { - //notify event, even if we're not triggered by user interaction - GridRangeSelectEvent unselectionEvent(0, 0, compPos, false); - if (wxEvtHandler* evtHandler = GetEventHandler()) - evtHandler->ProcessEvent(unselectionEvent); - } - } -} + selection.selectRange(rowFirst, rowLast, positive); + //notify event + GridRangeSelectEvent selectionEvent(rowFirst, rowLast, positive); + if (wxEvtHandler* evtHandler = GetEventHandler()) + evtHandler->ProcessEvent(selectionEvent); -void Grid::clearSelectionAllAndNotify() -{ - for (size_t compPos = 0; compPos < comp.size(); ++compPos) - clearSelection(true, compPos); + mainWin_->Refresh(); } @@ -2113,46 +1996,41 @@ void Grid::scrollTo(size_t row) } -std::pair Grid::getGridCursor() const +size_t Grid::getGridCursor() const { return mainWin_->getCursor(); } -int Grid::getBestColumnSize(size_t col, size_t compPos) const +int Grid::getBestColumnSize(size_t col) const { - if (compPos < comp.size()) + if (dataView_ && col < visibleCols.size()) { - const auto& visibleCols = comp[compPos].visibleCols; - auto dataView = comp[compPos].dataView_; - if (dataView && col < visibleCols.size()) - { - const ColumnType type = visibleCols[col].type_; + const ColumnType type = visibleCols[col].type_; - wxClientDC dc(mainWin_); - dc.SetFont(mainWin_->GetFont()); //harmonize with MainWin::render() + wxClientDC dc(mainWin_); + dc.SetFont(mainWin_->GetFont()); //harmonize with MainWin::render() - int maxSize = 0; + int maxSize = 0; - auto rowRange = rowLabelWin_->getRowsOnClient(mainWin_->GetClientRect()); //returns range [begin, end) - for (auto row = rowRange.first; row < rowRange.second; ++row) - maxSize = std::max(maxSize, dataView->getBestSize(dc, row, type)); + auto rowRange = rowLabelWin_->getRowsOnClient(mainWin_->GetClientRect()); //returns range [begin, end) + for (auto row = rowRange.first; row < rowRange.second; ++row) + maxSize = std::max(maxSize, dataView_->getBestSize(dc, row, type)); - return maxSize; - } + return maxSize; } return -1; } -void Grid::setColWidthAndNotify(int width, size_t col, size_t compPos, bool notifyAsync) +void Grid::setColumnWidth(int width, size_t col, GridEventPolicy columnResizeEventPolicy, bool notifyAsync) { - if (compPos < comp.size() && col < comp[compPos].visibleCols.size()) + if (col < visibleCols.size()) { - VisibleColumn& vcRs = comp[compPos].visibleCols[col]; + VisibleColumn& vcRs = visibleCols[col]; - const std::vector> stretchedWidths = getColStretchedWidths(mainWin_->GetClientSize().GetWidth()); - if (stretchedWidths.size() != comp.size() || stretchedWidths[compPos].size() != comp[compPos].visibleCols.size()) + const std::vector stretchedWidths = getColStretchedWidths(mainWin_->GetClientSize().GetWidth()); + if (stretchedWidths.size() != visibleCols.size()) { assert(false); return; @@ -2163,7 +2041,7 @@ void Grid::setColWidthAndNotify(int width, size_t col, size_t compPos, bool noti //unusual delay when enlarging the column again later width = std::max(width, COLUMN_MIN_WIDTH); - vcRs.offset_ = width - stretchedWidths[compPos][col]; //width := stretchedWidth + offset + vcRs.offset_ = width - stretchedWidths[col]; //width := stretchedWidth + offset //III. resizing any column should normalize *all* other stretched columns' offsets considering current mainWinWidth! // test case: @@ -2171,21 +2049,20 @@ void Grid::setColWidthAndNotify(int width, size_t col, size_t compPos, bool noti //2. shrink main window width so that horizontal scrollbars are shown despite the streched column //3. shrink a fixed-size column so that the scrollbars vanish and columns cover full width again //4. now verify that the stretched column is resizing immediately if main window is enlarged again - for (size_t compPos2 = 0; compPos2 < comp.size(); ++compPos2) - { - auto& visibleCols = comp[compPos2].visibleCols; - for (size_t col2 = 0; col2 < visibleCols.size(); ++col2) - if (visibleCols[col2].stretch_ > 0) //normalize stretched columns only - visibleCols[col2].offset_ = std::max(visibleCols[col2].offset_, COLUMN_MIN_WIDTH - stretchedWidths[compPos2][col2]); - } + for (size_t col2 = 0; col2 < visibleCols.size(); ++col2) + if (visibleCols[col2].stretch_ > 0) //normalize stretched columns only + visibleCols[col2].offset_ = std::max(visibleCols[col2].offset_, COLUMN_MIN_WIDTH - stretchedWidths[col2]); - GridColumnResizeEvent sizeEvent(vcRs.offset_, vcRs.type_, compPos); - if (wxEvtHandler* evtHandler = GetEventHandler()) + if (columnResizeEventPolicy == ALLOW_GRID_EVENT) { - if (notifyAsync) - evtHandler->AddPendingEvent(sizeEvent); - else - evtHandler->ProcessEvent(sizeEvent); + GridColumnResizeEvent sizeEvent(vcRs.offset_, vcRs.type_); + if (wxEvtHandler* evtHandler = GetEventHandler()) + { + if (notifyAsync) + evtHandler->AddPendingEvent(sizeEvent); + else + evtHandler->ProcessEvent(sizeEvent); + } } } else @@ -2193,16 +2070,15 @@ void Grid::setColWidthAndNotify(int width, size_t col, size_t compPos, bool noti } -void Grid::autoSizeColumns(size_t compPos) +void Grid::autoSizeColumns(GridEventPolicy columnResizeEventPolicy) { - if (compPos < comp.size() && comp[compPos].allowColumnResize) + if (allowColumnResize) { - auto& visibleCols = comp[compPos].visibleCols; for (size_t col = 0; col < visibleCols.size(); ++col) { - const int bestWidth = getBestColumnSize(col, compPos); //return -1 on error + const int bestWidth = getBestColumnSize(col); //return -1 on error if (bestWidth >= 0) - setColWidthAndNotify(bestWidth, col, compPos, true); + setColumnWidth(bestWidth, col, columnResizeEventPolicy, true); } updateWindowSizes(); Refresh(); @@ -2210,91 +2086,71 @@ void Grid::autoSizeColumns(size_t compPos) } -std::vector> Grid::getColStretchedWidths(int clientWidth) const //final width = (normalized) (stretchedWidth + offset) +std::vector Grid::getColStretchedWidths(int clientWidth) const //final width = (normalized) (stretchedWidth + offset) { assert(clientWidth >= 0); clientWidth = std::max(clientWidth, 0); int stretchTotal = 0; - for (const Component& c : comp) - for (const VisibleColumn& vc : c.visibleCols) - { - assert(vc.stretch_ >= 0); - stretchTotal += vc.stretch_; - } + for (const VisibleColumn& vc : visibleCols) + { + assert(vc.stretch_ >= 0); + stretchTotal += vc.stretch_; + } int remainingWidth = clientWidth; - std::vector> output; - for (const Component& c : comp) - { - output.push_back(std::vector()); - auto& compWidths = output.back(); + std::vector output; - if (stretchTotal <= 0) - compWidths.resize(c.visibleCols.size()); //fill with zeros - else - for (const VisibleColumn& vc : c.visibleCols) - { - const int width = clientWidth * vc.stretch_ / stretchTotal; //rounds down! - compWidths.push_back(width); - remainingWidth -= width; - } - } + if (stretchTotal <= 0) + output.resize(visibleCols.size()); //fill with zeros + else + for (const VisibleColumn& vc : visibleCols) + { + const int width = clientWidth * vc.stretch_ / stretchTotal; //rounds down! + output.push_back(width); + remainingWidth -= width; + } //distribute *all* of clientWidth: should suffice to enlarge the first few stretched columns; no need to minimize total absolute error of distribution if (stretchTotal > 0) if (remainingWidth > 0) { - for (size_t compPos2 = 0; compPos2 < comp.size(); ++compPos2) - { - auto& visibleCols = comp[compPos2].visibleCols; - for (size_t col2 = 0; col2 < visibleCols.size(); ++col2) - if (visibleCols[col2].stretch_ > 0) - { - ++output[compPos2][col2]; - if (--remainingWidth == 0) - return output; - } - } + for (size_t col2 = 0; col2 < visibleCols.size(); ++col2) + if (visibleCols[col2].stretch_ > 0) + { + ++output[col2]; + if (--remainingWidth == 0) + return output; + } assert(false); } return output; } -std::vector> Grid::getColWidths() const +std::vector Grid::getColWidths() const { return getColWidths(mainWin_->GetClientSize().GetWidth()); } -std::vector> Grid::getColWidths(int mainWinWidth) const //evaluate stretched columns; structure matches "comp" +std::vector Grid::getColWidths(int mainWinWidth) const //evaluate stretched columns { - const std::vector> stretchedWidths = getColStretchedWidths(mainWinWidth); - assert(stretchedWidths.size() == comp.size()); + const std::vector stretchedWidths = getColStretchedWidths(mainWinWidth); + assert(stretchedWidths.size() == visibleCols.size()); - std::vector> output; - - for (size_t compPos2 = 0; compPos2 < comp.size(); ++compPos2) + std::vector output; + for (size_t col2 = 0; col2 < visibleCols.size(); ++col2) { - assert(stretchedWidths[compPos2].size() == comp[compPos2].visibleCols.size()); - - output.push_back(std::vector()); - auto& compWidths = output.back(); - - auto& visibleCols = comp[compPos2].visibleCols; - for (size_t col2 = 0; col2 < visibleCols.size(); ++col2) - { - const auto& vc = visibleCols[col2]; - int width = stretchedWidths[compPos2][col2] + vc.offset_; + const auto& vc = visibleCols[col2]; + int width = stretchedWidths[col2] + vc.offset_; - if (vc.stretch_ > 0) - width = std::max(width, COLUMN_MIN_WIDTH); //normalization really needed here: e.g. smaller main window would result in negative width - else - width = std::max(width, 0); //support smaller width than COLUMN_MIN_WIDTH if set via configuration + if (vc.stretch_ > 0) + width = std::max(width, COLUMN_MIN_WIDTH); //normalization really needed here: e.g. smaller main window would result in negative width + else + width = std::max(width, 0); //support smaller width than COLUMN_MIN_WIDTH if set via configuration - compWidths.push_back(ColumnWidth(vc.type_, width)); - } + output.push_back(ColumnWidth(vc.type_, width)); } return output; } @@ -2303,8 +2159,7 @@ std::vector> Grid::getColWidths(int mainWinWidth) int Grid::getColWidthsSum(int mainWinWidth) const { int sum = 0; - for (const std::vector& cols : getColWidths(mainWinWidth)) - for (const ColumnWidth& cw : cols) - sum += cw.width_; + for (const ColumnWidth& cw : getColWidths(mainWinWidth)) + sum += cw.width_; return sum; }; diff --git a/wx+/grid.h b/wx+/grid.h index e8f62a12..9bbb6c99 100644 --- a/wx+/grid.h +++ b/wx+/grid.h @@ -38,32 +38,30 @@ extern const wxEventType EVENT_GRID_SELECT_RANGE; //generates: GridRangeSelectEv struct GridClickEvent : public wxMouseEvent { - GridClickEvent(wxEventType et, const wxMouseEvent& me, ptrdiff_t row, ColumnType colType, size_t compPos) : wxMouseEvent(me), row_(row), colType_(colType), compPos_(compPos) { SetEventType(et); } + GridClickEvent(wxEventType et, const wxMouseEvent& me, ptrdiff_t row, ColumnType colType) : wxMouseEvent(me), row_(row), colType_(colType) { SetEventType(et); } virtual wxEvent* Clone() const { return new GridClickEvent(*this); } + const ptrdiff_t row_; //-1 for invalid position, >= rowCount if out of range const ColumnType colType_; - const size_t compPos_; }; struct GridColumnResizeEvent : public wxCommandEvent { - GridColumnResizeEvent(int offset, ColumnType colType, size_t compPos) : wxCommandEvent(EVENT_GRID_COL_RESIZE), colType_(colType), offset_(offset), compPos_(compPos) {} + GridColumnResizeEvent(int offset, ColumnType colType) : wxCommandEvent(EVENT_GRID_COL_RESIZE), colType_(colType), offset_(offset) {} virtual wxEvent* Clone() const { return new GridColumnResizeEvent(*this); } const ColumnType colType_; const int offset_; - const size_t compPos_; }; struct GridRangeSelectEvent : public wxCommandEvent { - GridRangeSelectEvent(size_t rowFirst, size_t rowLast, size_t compPos, bool positive) : wxCommandEvent(EVENT_GRID_SELECT_RANGE), rowFirst_(rowFirst), rowLast_(rowLast), compPos_(compPos), positive_(positive) { assert(rowFirst <= rowLast); } + GridRangeSelectEvent(size_t rowFirst, size_t rowLast, bool positive) : wxCommandEvent(EVENT_GRID_SELECT_RANGE), positive_(positive), rowFirst_(rowFirst), rowLast_(rowLast) { assert(rowFirst <= rowLast); } virtual wxEvent* Clone() const { return new GridRangeSelectEvent(*this); } + const bool positive_; //"false" when clearing selection! const size_t rowFirst_; //selected range: [rowFirst_, rowLast_) - const size_t rowLast_; //range is empty when clearing selection - const size_t compPos_; - const bool positive_; + const size_t rowLast_; }; typedef void (wxEvtHandler::*GridClickEventFunction )(GridClickEvent&); @@ -90,7 +88,7 @@ class GridData public: virtual ~GridData() {} - virtual size_t getRowCount() const = 0; //if there are multiple grid components, only the first one will be polled for row count! + virtual size_t getRowCount() const = 0; //grid area virtual wxString getValue(size_t row, ColumnType colType) const = 0; @@ -116,6 +114,12 @@ protected: //optional helper routines static void drawColumnLabelText (wxDC& dc, const wxRect& rect, const wxString& text); }; +enum GridEventPolicy +{ + ALLOW_GRID_EVENT, + DENY_GRID_EVENT +}; + class Grid : public wxScrolledWindow { @@ -131,10 +135,6 @@ public: void setRowHeight(int height); - //grid component := a grid is divided into multiple components each of which is essentially a set of connected columns - void setComponentCount(size_t count) { comp.resize(count); updateWindowSizes(); } - size_t getComponentCount() const { return comp.size(); } - struct ColumnAttribute { ColumnAttribute(ColumnType type, int offset, int stretch, bool visible = true) : type_(type), visible_(visible), stretch_(std::max(stretch, 0)), offset_(offset) { assert(stretch >=0 ); } @@ -146,12 +146,12 @@ public: int offset_; }; - void setColumnConfig(const std::vector& attr, size_t compPos = 0); //set column count + widths - std::vector getColumnConfig(size_t compPos = 0) const; + void setColumnConfig(const std::vector& attr); //set column count + widths + std::vector getColumnConfig() const; - void setDataProvider(const std::shared_ptr& dataView, size_t compPos = 0) { if (compPos < comp.size()) comp[compPos].dataView_ = dataView; } - /**/ GridData* getDataProvider(size_t compPos = 0) { return compPos < comp.size() ? comp[compPos].dataView_.get() : nullptr; } - const GridData* getDataProvider(size_t compPos = 0) const { return compPos < comp.size() ? comp[compPos].dataView_.get() : nullptr; } + void setDataProvider(const std::shared_ptr& dataView) { dataView_ = dataView; } + /**/ GridData* getDataProvider() { return dataView_.get(); } + const GridData* getDataProvider() const { return dataView_.get(); } //----------------------------------------------------------------------------- void setColumnLabelHeight(int height); @@ -166,9 +166,9 @@ public: //alternative until wxScrollHelper::ShowScrollbars() becomes available in wxWidgets 2.9 void showScrollBars(ScrollBarStatus horizontal, ScrollBarStatus vertical); - std::vector getSelectedRows(size_t compPos = 0) const; - void setSelectedRows(const std::vector& sel, size_t compPos = 0); - void clearSelection(bool emitSelectRangeEvent = true, size_t compPos = 0); //turn off range selection event when calling this function in an event handler to avoid recursion! + std::vector getSelectedRows() const { return selection.get(); } + void selectAllRows (GridEventPolicy rangeEventPolicy); + void clearSelection(GridEventPolicy rangeEventPolicy); //turn off range selection event when calling this function in an event handler to avoid recursion! void scrollDelta(int deltaX, int deltaY); //in scroll units @@ -179,21 +179,20 @@ public: const wxWindow& getMainWin() const; ptrdiff_t getRowAtPos(int posY) const; //return -1 for invalid position, >= rowCount if out of range; absolute coordinates! - Opt> getColumnAtPos(int posX) const; //returns (column type, component pos) + Opt getColumnAtPos(int posX) const; - wxRect getCellArea(size_t row, ColumnType colType, size_t compPos = 0) const; //returns empty rect if column not found; absolute coordinates! + wxRect getCellArea(size_t row, ColumnType colType) const; //returns empty rect if column not found; absolute coordinates! - void enableColumnMove (bool value, size_t compPos = 0) { if (compPos < comp.size()) comp[compPos].allowColumnMove = value; } - void enableColumnResize(bool value, size_t compPos = 0) { if (compPos < comp.size()) comp[compPos].allowColumnResize = value; } + void enableColumnMove (bool value) { allowColumnMove = value; } + void enableColumnResize(bool value) { allowColumnResize = value; } - void setGridCursor(size_t row, size_t compPos = 0); //set + show + select cursor (+ emit range selection event) - std::pair getGridCursor() const; //(row, component pos) + void setGridCursor(size_t row); //set + show + select cursor (+ emit range selection event) + size_t getGridCursor() const; //returns row void scrollTo(size_t row); virtual void Refresh(bool eraseBackground = true, const wxRect* rect = nullptr); virtual bool Enable( bool enable = true) { Refresh(); return wxScrolledWindow::Enable(enable); } - void autoSizeColumns(size_t compPos = 0); //############################################################################################################ private: @@ -211,11 +210,9 @@ private: virtual void SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh); //get rid of scrollbars, but preserve scrolling behavior! #endif -#ifdef ZEN_WIN - virtual WXLRESULT MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam); //support horizontal mouse wheel -#endif + int getBestColumnSize(size_t col) const; //return -1 on error - int getBestColumnSize(size_t col, size_t compPos) const; //return -1 on error + void autoSizeColumns(GridEventPolicy columnResizeEventPolicy); friend class GridData; class SubWindow; @@ -240,15 +237,8 @@ private: return selection; } - void set(const std::vector& newSel) - { - clear(); - for (size_t row : newSel) - if (row < rowSelectionValue.size()) - rowSelectionValue[row] = true; - } - - void clear() { selectRange(0, rowSelectionValue.size(), false); } + void selectAll() { selectRange(0, rowSelectionValue.size(), true); } + void clear () { selectRange(0, rowSelectionValue.size(), false); } bool isSelected(size_t row) const { return row < rowSelectionValue.size() ? rowSelectionValue[row] != 0 : false; } @@ -276,70 +266,52 @@ private: int offset_; }; - struct Component - { - Component() : allowColumnMove(true), allowColumnResize(true) {} - - std::shared_ptr dataView_; - Selection selection; - bool allowColumnMove; - bool allowColumnResize; - - std::vector visibleCols; //individual widths, type and total column count - std::vector oldColAttributes; //visible + nonvisible columns; use for conversion in setColumnConfig()/getColumnConfig() *only*! - }; - struct ColumnWidth { ColumnWidth(ColumnType type, int width) : type_(type), width_(width) {} ColumnType type_; int width_; }; - std::vector> getColWidths() const; // - std::vector> getColWidths(int mainWinWidth) const; //evaluate stretched columns; structure matches "comp" + std::vector getColWidths() const; // + std::vector getColWidths(int mainWinWidth) const; //evaluate stretched columns int getColWidthsSum(int mainWinWidth) const; - std::vector> getColStretchedWidths(int clientWidth) const; //final width = (normalized) (stretchedWidth + offset) + std::vector getColStretchedWidths(int clientWidth) const; //final width = (normalized) (stretchedWidth + offset) - Opt getColWidth(size_t col, size_t compPos) const + Opt getColWidth(size_t col) const { const auto& widths = getColWidths(); - if (compPos < widths.size() && col < widths[compPos].size()) - return widths[compPos][col].width_; + if (col < widths.size()) + return widths[col].width_; return NoValue(); } - void setColWidthAndNotify(int width, size_t col, size_t compPos, bool notifyAsync = false); + void setColumnWidth(int width, size_t col, GridEventPolicy columnResizeEventPolicy, bool notifyAsync = false); - wxRect getColumnLabelArea(ColumnType colType, size_t compPos) const; //returns empty rect if column not found + wxRect getColumnLabelArea(ColumnType colType) const; //returns empty rect if column not found - void selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, size_t compPos, bool positive = true); //select inclusive range [rowFrom, rowTo] + notify event! + void selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positive = true); //select inclusive range [rowFrom, rowTo] + notify event! - void clearSelectionAllAndNotify(); //clear selection + notify event - - bool isSelected(size_t row, size_t compPos) const { return compPos < comp.size() ? comp[compPos].selection.isSelected(row) : false; } - - bool columnMoveAllowed (size_t compPos) const { return compPos < comp.size() ? comp[compPos].allowColumnMove : false; } - bool columnResizeAllowed(size_t compPos) const { return compPos < comp.size() ? comp[compPos].allowColumnResize : false; } + bool isSelected(size_t row) const { return selection.isSelected(row); } struct ColAction { bool wantResize; //"!wantResize" means "move" or "single click" size_t col; - size_t compPos; }; Opt clientPosToColumnAction(const wxPoint& pos) const; - void moveColumn(size_t colFrom, size_t colTo, size_t compPos); - ptrdiff_t clientPosToMoveTargetColumn(const wxPoint& pos, size_t compPos) const; //return < 0 on error + void moveColumn(size_t colFrom, size_t colTo); + ptrdiff_t clientPosToMoveTargetColumn(const wxPoint& pos) const; //return < 0 on error - Opt colToType(size_t col, size_t compPos) const; + Opt colToType(size_t col) const; /* Visual layout: - ------------------------------------------------ - |CornerWin | ColLabelWin: | - |-------------------------- Comp1 | Comp2 ... | row label and main window are vertically tiled into one or more "components" - |RowLabelWin | MainWin: | - ------------------------------------------------ + -------------------------------- + |CornerWin | ColLabelWin | + |------------------------------| + |RowLabelWin | MainWin | + | | | + -------------------------------- */ CornerWin* cornerWin_; RowLabelWin* rowLabelWin_; @@ -352,7 +324,14 @@ private: int colLabelHeight; bool drawRowLabel; - std::vector comp; + std::shared_ptr dataView_; + Selection selection; + bool allowColumnMove; + bool allowColumnResize; + + std::vector visibleCols; //individual widths, type and total column count + std::vector oldColAttributes; //visible + nonvisible columns; use for conversion in setColumnConfig()/getColumnConfig() *only*! + size_t rowCountOld; //at the time of last Grid::Refresh() }; } diff --git a/wx+/popup_dlg.cpp b/wx+/popup_dlg.cpp index 27526922..2c7887fa 100644 --- a/wx+/popup_dlg.cpp +++ b/wx+/popup_dlg.cpp @@ -14,7 +14,6 @@ using namespace zen; - namespace { void setAsStandard(wxButton& btn) @@ -62,7 +61,12 @@ void setBestInitialSize(wxTextCtrl& ctrl, const wxString& text, wxSize maxSize) it = itEnd + 1; } - const wxSize bestSize(bestWidth + scrollbarWidth, std::min(rowCount * rowHeight, maxSize.y)); +#if defined ZEN_WIN || defined ZEN_LINUX + const int rowGap = 0; +#elif defined ZEN_MAC + const int rowGap = 1; +#endif + const wxSize bestSize(bestWidth + scrollbarWidth, std::min(rowCount * (rowHeight + rowGap), maxSize.y)); ctrl.SetMinSize(bestSize); //alas, SetMinClientSize() is just not working! } } diff --git a/wx+/popup_dlg_generated.cpp b/wx+/popup_dlg_generated.cpp index b7618545..70c5cb20 100644 --- a/wx+/popup_dlg_generated.cpp +++ b/wx+/popup_dlg_generated.cpp @@ -11,79 +11,79 @@ PopupDialogGenerated::PopupDialogGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer24; - bSizer24 = new wxBoxSizer( wxVERTICAL ); - - m_panel33 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panel33->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer165; - bSizer165 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapMsgType = new wxStaticBitmap( m_panel33, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizer165->Add( m_bitmapMsgType, 0, wxALL, 10 ); - - wxBoxSizer* bSizer16; - bSizer16 = new wxBoxSizer( wxVERTICAL ); - - m_staticTextMain = new wxStaticText( m_panel33, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextMain->Wrap( -1 ); - bSizer16->Add( m_staticTextMain, 0, wxTOP|wxBOTTOM|wxRIGHT, 15 ); - - m_textCtrlTextDetail = new wxTextCtrl( m_panel33, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER ); - bSizer16->Add( m_textCtrlTextDetail, 1, wxEXPAND, 5 ); - - - bSizer165->Add( bSizer16, 1, wxEXPAND, 5 ); - - - m_panel33->SetSizer( bSizer165 ); - m_panel33->Layout(); - bSizer165->Fit( m_panel33 ); - bSizer24->Add( m_panel33, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - m_staticline6 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer24->Add( m_staticline6, 0, wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 ); - - wxBoxSizer* bSizer25; - bSizer25 = new wxBoxSizer( wxVERTICAL ); - - m_checkBoxCustom = new wxCheckBox( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer25->Add( m_checkBoxCustom, 0, wxALIGN_CENTER_HORIZONTAL|wxTOP|wxRIGHT|wxLEFT, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - m_buttonAffirmative = new wxButton( this, wxID_YES, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - bSizerStdButtons->Add( m_buttonAffirmative, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - m_buttonNegative = new wxButton( this, wxID_NO, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - bSizerStdButtons->Add( m_buttonNegative, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); - bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer25->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); - - - bSizer24->Add( bSizer25, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - - this->SetSizer( bSizer24 ); - this->Layout(); - bSizer24->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( PopupDialogGenerated::OnClose ) ); - m_checkBoxCustom->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnCheckBoxClick ), NULL, this ); - m_buttonAffirmative->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonAffirmative ), NULL, this ); - m_buttonNegative->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonNegative ), NULL, this ); - m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnCancel ), NULL, this ); + this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer24; + bSizer24 = new wxBoxSizer( wxVERTICAL ); + + m_panel33 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panel33->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer165; + bSizer165 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapMsgType = new wxStaticBitmap( m_panel33, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizer165->Add( m_bitmapMsgType, 0, wxALL, 10 ); + + wxBoxSizer* bSizer16; + bSizer16 = new wxBoxSizer( wxVERTICAL ); + + m_staticTextMain = new wxStaticText( m_panel33, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextMain->Wrap( -1 ); + bSizer16->Add( m_staticTextMain, 0, wxTOP|wxBOTTOM|wxRIGHT, 15 ); + + m_textCtrlTextDetail = new wxTextCtrl( m_panel33, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER ); + bSizer16->Add( m_textCtrlTextDetail, 1, wxEXPAND, 5 ); + + + bSizer165->Add( bSizer16, 1, wxEXPAND, 5 ); + + + m_panel33->SetSizer( bSizer165 ); + m_panel33->Layout(); + bSizer165->Fit( m_panel33 ); + bSizer24->Add( m_panel33, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + m_staticline6 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer24->Add( m_staticline6, 0, wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 ); + + wxBoxSizer* bSizer25; + bSizer25 = new wxBoxSizer( wxVERTICAL ); + + m_checkBoxCustom = new wxCheckBox( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer25->Add( m_checkBoxCustom, 0, wxALIGN_CENTER_HORIZONTAL|wxTOP|wxRIGHT|wxLEFT, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonAffirmative = new wxButton( this, wxID_YES, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + bSizerStdButtons->Add( m_buttonAffirmative, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_buttonNegative = new wxButton( this, wxID_NO, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + bSizerStdButtons->Add( m_buttonNegative, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer25->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); + + + bSizer24->Add( bSizer25, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + + this->SetSizer( bSizer24 ); + this->Layout(); + bSizer24->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( PopupDialogGenerated::OnClose ) ); + m_checkBoxCustom->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnCheckBoxClick ), NULL, this ); + m_buttonAffirmative->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonAffirmative ), NULL, this ); + m_buttonNegative->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonNegative ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnCancel ), NULL, this ); } PopupDialogGenerated::~PopupDialogGenerated() diff --git a/wx+/popup_dlg_generated.h b/wx+/popup_dlg_generated.h index 5aab9f68..3aab2257 100644 --- a/wx+/popup_dlg_generated.h +++ b/wx+/popup_dlg_generated.h @@ -37,35 +37,35 @@ /////////////////////////////////////////////////////////////////////////////// /// Class PopupDialogGenerated /////////////////////////////////////////////////////////////////////////////// -class PopupDialogGenerated : public wxDialog +class PopupDialogGenerated : public wxDialog { - private: - - protected: - wxPanel* m_panel33; - wxStaticBitmap* m_bitmapMsgType; - wxStaticText* m_staticTextMain; - wxTextCtrl* m_textCtrlTextDetail; - wxStaticLine* m_staticline6; - wxCheckBox* m_checkBoxCustom; - wxBoxSizer* bSizerStdButtons; - wxButton* m_buttonAffirmative; - wxButton* m_buttonNegative; - wxButton* m_buttonCancel; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnCheckBoxClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnButtonAffirmative( wxCommandEvent& event ) { event.Skip(); } - virtual void OnButtonNegative( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } - - - public: - - PopupDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~PopupDialogGenerated(); - +private: + +protected: + wxPanel* m_panel33; + wxStaticBitmap* m_bitmapMsgType; + wxStaticText* m_staticTextMain; + wxTextCtrl* m_textCtrlTextDetail; + wxStaticLine* m_staticline6; + wxCheckBox* m_checkBoxCustom; + wxBoxSizer* bSizerStdButtons; + wxButton* m_buttonAffirmative; + wxButton* m_buttonNegative; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnCheckBoxClick( wxCommandEvent& event ) { event.Skip(); } + virtual void OnButtonAffirmative( wxCommandEvent& event ) { event.Skip(); } + virtual void OnButtonNegative( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } + + +public: + + PopupDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~PopupDialogGenerated(); + }; #endif //__POPUP_DLG_GENERATED_H__ diff --git a/zen/FindFilePlus/FindFilePlus.vcxproj b/zen/FindFilePlus/FindFilePlus.vcxproj index 56650735..eb5c672e 100644 --- a/zen/FindFilePlus/FindFilePlus.vcxproj +++ b/zen/FindFilePlus/FindFilePlus.vcxproj @@ -19,7 +19,7 @@ - {70394AEF-5897-4911-AFA1-82EAF0581EFA} + {814047ED-7701-494D-BBAF-AFEDF43EDC4E} ShadowDll Win32Proj @@ -47,8 +47,6 @@ v120_xp - - @@ -61,188 +59,109 @@ - <_ProjectFileVersion>10.0.30319.1 - OBJ\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - OBJ\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ + ..\..\FreeFileSync\Build\Bin\ + ..\..\FreeFileSync\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + ..\..\FreeFileSync\Build\Bin\ + ..\..\FreeFileSync\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + ..\..\FreeFileSync\Build\Bin\ + ..\..\FreeFileSync\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ false - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ + ..\..\FreeFileSync\Build\Bin\ + ..\..\FreeFileSync\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ false - FindFilePlus_$(Platform) - FindFilePlus_$(Platform) - FindFilePlus_$(Platform) - FindFilePlus_$(Platform) + $(ProjectName)_$(Platform) + $(ProjectName)_$(Platform) + $(ProjectName)_$(Platform) + $(ProjectName)_$(Platform) C:\Data\C++\WinDDK\inc\ddk;C:\Data\C++\WinDDK\inc\api;C:\Data\C++\WinDDK\inc\crt;$(WindowsSdkDir)\include;$(VCInstallDir)include C:\Data\C++\WinDDK\inc\ddk;C:\Data\C++\WinDDK\inc\api;C:\Data\C++\WinDDK\inc\crt;$(WindowsSdkDir)\include;$(VCInstallDir)include C:\Data\C++\WinDDK\inc\ddk;C:\Data\C++\WinDDK\inc\api;C:\Data\C++\WinDDK\inc\crt;$(WindowsSdkDir)\include;$(VCInstallDir)include C:\Data\C++\WinDDK\inc\ddk;C:\Data\C++\WinDDK\inc\api;C:\Data\C++\WinDDK\inc\crt;$(WindowsSdkDir)\include;$(VCInstallDir)include - - $(IntDir)Build.html - Disabled _X86_;_DEBUG;_WINDOWS;_USRDLL;FIND_FILE_PLUS_DLL_EXPORTS;%(PreprocessorDefinitions) - false EnableFastChecks - MultiThreadedDebugDLL - - + MultiThreadedDebug Level4 - true - EditAndContinue - 4100 - ../.. + 4100;4996;4512 + C:\Data\Projects; true true NoExtensions $(OutDir)$(TargetName)$(TargetExt) - true true - $(IntDir)$(TargetName).pdb Windows - - $(IntDir)$(TargetName).lib - MachineX86 - %(AdditionalDependencies) - - $(IntDir)Build.html - - - X64 - Disabled _AMD64_;_DEBUG;_WINDOWS;_USRDLL;FIND_FILE_PLUS_DLL_EXPORTS;%(PreprocessorDefinitions) - false EnableFastChecks - MultiThreadedDebugDLL - - + MultiThreadedDebug Level4 - true - ProgramDatabase - 4100 - ../.. + 4100;4996;4512 + C:\Data\Projects; true true $(OutDir)$(TargetName)$(TargetExt) - true true - $(IntDir)$(TargetName).pdb Windows - - $(IntDir)$(TargetName).lib - MachineX64 - %(AdditionalDependencies) - - $(IntDir)Build.html - MaxSpeed - true _X86_;NDEBUG;_WINDOWS;_USRDLL;FIND_FILE_PLUS_DLL_EXPORTS;%(PreprocessorDefinitions) MultiThreaded - true - - Level4 - true ProgramDatabase - 4100 + 4100;4996;4512 Speed - ../.. + C:\Data\Projects; true NoExtensions $(OutDir)$(TargetName)$(TargetExt) - true true Windows - true - true UseLinkTimeCodeGeneration - - $(IntDir)$(TargetName).lib - MachineX86 - %(AdditionalDependencies) - - $(IntDir)Build.html - - - X64 - MaxSpeed - true _AMD64_;NDEBUG;_WINDOWS;_USRDLL;FIND_FILE_PLUS_DLL_EXPORTS;%(PreprocessorDefinitions) MultiThreaded - true - - Level4 - true - ProgramDatabase - 4100 + 4100;4996;4512 Speed - ../.. + C:\Data\Projects; true $(OutDir)$(TargetName)$(TargetExt) - true true Windows - true - true UseLinkTimeCodeGeneration - - $(IntDir)$(TargetName).lib - MachineX64 - %(AdditionalDependencies) - - - - false - - - false - - - false - - - false - + @@ -251,6 +170,4 @@ - - \ No newline at end of file diff --git a/zen/IFileOperation/FileOperation.vcxproj b/zen/IFileOperation/FileOperation.vcxproj new file mode 100644 index 00000000..3f6923f3 --- /dev/null +++ b/zen/IFileOperation/FileOperation.vcxproj @@ -0,0 +1,179 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + FileOperation + {F6D3A51C-15EF-4710-BB67-3FCE9C0B5D92} + ShadowDll + Win32Proj + + + + DynamicLibrary + Unicode + true + v120_xp + + + DynamicLibrary + Unicode + v120_xp + + + DynamicLibrary + Unicode + true + v120_xp + + + DynamicLibrary + Unicode + v120_xp + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + ..\..\FreeFileSync\Build\Bin\ + ..\..\FreeFileSync\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + ..\..\FreeFileSync\Build\Bin\ + ..\..\FreeFileSync\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + ..\..\FreeFileSync\Build\Bin\ + ..\..\FreeFileSync\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + false + ..\..\FreeFileSync\Build\Bin\ + ..\..\FreeFileSync\Obj\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\ + false + $(ProjectName)_$(Platform) + $(ProjectName)_$(Platform) + $(ProjectName)_$(Platform) + $(ProjectName)_$(Platform) + + + + Disabled + WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;_DEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + Level4 + 4100;4996;4512 + C:\Data\Projects;C:\Data\C++\boost + true + zen/warn_static.h + true + NoExtensions + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + C:\Data\C++\Boost\stage\lib + $(IntDir)$(TargetName).lib + + + + + Disabled + WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;_DEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + Level4 + 4100;4996;4512 + C:\Data\Projects;C:\Data\C++\boost + true + zen/warn_static.h + true + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + C:\Data\C++\Boost\stage_x64\lib + $(IntDir)$(TargetName).lib + + + + + MaxSpeed + WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;NDEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions) + MultiThreaded + Level4 + Speed + 4100;4996;4512 + C:\Data\Projects;C:\Data\C++\boost + zen/warn_static.h + true + NoExtensions + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + UseLinkTimeCodeGeneration + C:\Data\C++\Boost\stage\lib + $(IntDir)$(TargetName).lib + + + + + MaxSpeed + WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;NDEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions) + MultiThreaded + Level4 + Speed + 4100;4996;4512 + C:\Data\Projects;C:\Data\C++\boost + zen/warn_static.h + true + + + $(OutDir)$(TargetName)$(TargetExt) + true + Windows + UseLinkTimeCodeGeneration + C:\Data\C++\Boost\stage_x64\lib + $(IntDir)$(TargetName).lib + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/zen/IFileOperation/FileOperation_Vista.vcxproj b/zen/IFileOperation/FileOperation_Vista.vcxproj deleted file mode 100644 index 3ff45843..00000000 --- a/zen/IFileOperation/FileOperation_Vista.vcxproj +++ /dev/null @@ -1,250 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - Vista IFileOperation - {70394AEF-5897-4911-AFA1-82EAF0581EFA} - ShadowDll - Win32Proj - - - - DynamicLibrary - Unicode - true - v120_xp - - - DynamicLibrary - Unicode - v120_xp - - - DynamicLibrary - Unicode - true - v120_xp - - - DynamicLibrary - Unicode - v120_xp - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - OBJ\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - OBJ\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - false - .\ - OBJ\$(ProjectName)_$(Configuration)_$(Platform)\ - false - FileOperation_$(Platform) - FileOperation_$(Platform) - FileOperation_$(Platform) - FileOperation_$(Platform) - - - - $(IntDir)Build.html - - - Disabled - WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;_DEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebugDLL - - - Level4 - true - EditAndContinue - 4100;4996;4512 - ../..;C:\Data\C++\boost - true - zen/warn_static.h - true - NoExtensions - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - $(IntDir)$(TargetName).pdb - Windows - - - $(IntDir)$(TargetName).lib - MachineX86 - C:\Data\C++\Boost\stage\lib - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - X64 - - - Disabled - WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;_DEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebugDLL - - - Level4 - true - ProgramDatabase - 4100;4996;4512 - ../..;C:\Data\C++\boost - true - zen/warn_static.h - true - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - $(IntDir)$(TargetName).pdb - Windows - - - $(IntDir)$(TargetName).lib - MachineX64 - C:\Data\C++\Boost\stage_x64\lib - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - MaxSpeed - true - WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;NDEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions) - MultiThreaded - true - - - Level4 - true - ProgramDatabase - Speed - 4100;4996;4512 - ../..;C:\Data\C++\boost - zen/warn_static.h - true - NoExtensions - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - Windows - true - true - UseLinkTimeCodeGeneration - - - $(IntDir)$(TargetName).lib - MachineX86 - C:\Data\C++\Boost\stage\lib - %(AdditionalDependencies) - - - - - $(IntDir)Build.html - - - X64 - - - MaxSpeed - true - WXINTL_NO_GETTEXT_MACRO;ZEN_WIN;NDEBUG;_WINDOWS;_USRDLL;FILE_OP_DLL_EXPORTS;%(PreprocessorDefinitions) - MultiThreaded - true - - - Level4 - true - ProgramDatabase - Speed - 4100;4996;4512 - ../..;C:\Data\C++\boost - zen/warn_static.h - true - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - Windows - true - true - UseLinkTimeCodeGeneration - - - $(IntDir)$(TargetName).lib - MachineX64 - C:\Data\C++\Boost\stage_x64\lib - %(AdditionalDependencies) - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/zen/debug_minidump.h b/zen/debug_minidump.h index e2605038..2ef43039 100644 --- a/zen/debug_minidump.h +++ b/zen/debug_minidump.h @@ -21,11 +21,11 @@ Minidumps http://msdn.microsoft.com/en-us/library/windows/desktop/ee416349(v=vs. ---------------------------------------------------------------------------------------- 1. Compile "debug_minidump.cpp" 2. Compile "release" build with: + - C/C++ -> General: Debug Information Format: "Program Database" (/Zi). + - C/C++ -> Optimization: Omit Frame Pointers: No (/Oy-) - avoid call stack mess up! - Linker -> Debugging: Generate Debug Info: Yes (/DEBUG) - Linker -> Optimization: References: Yes (/OPT:REF). - Linker -> Optimization: Enable COMDAT Folding: Yes (/OPT:ICF). - - C/C++ -> General: Debug Information Format: "Program Database" (/Zi). - - C/C++ -> Optimization: Omit Frame Pointers: No (/Oy-) - avoid call stack mess up! Optional: - C/C++ -> Optimization: Disabled (/Od) */ diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index 17efda00..1945ada7 100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -22,7 +22,7 @@ #elif defined ZEN_MAC //#include -#include +//#include //#include #include "file_traverser.h" #endif @@ -519,6 +519,13 @@ std::vector DirWatcher::getChanges(const std::function DirWatcher::getChanges(const std::function&) { return std::vector(); } + +#if 0 namespace { class DirsOnlyTraverser : public zen::TraverseCallback @@ -694,3 +701,6 @@ std::vector DirWatcher::getChanges(const std::function not really needed here, creation time is set anyway at the end of copyFileWindowsDefault()! //#################### copy NTFS compressed attribute ######################### const bool sourceIsCompressed = (cbd.fileInfoSrc.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; @@ -1945,7 +1925,7 @@ const bool supportNonEncryptedDestination = winXpOrLater(); //encrypted destinat void copyFileWindowsDefault(const Zstring& sourceFile, const Zstring& targetFile, CallbackCopyFile* callback, - FileAttrib* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked, ErrorShouldCopyAsSparse + InSyncAttributes* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked, ErrorShouldCopyAsSparse { //try to get backup read and write privileges: who knows, maybe this helps solve some obscure "access denied" errors try { activatePrivilege(SE_BACKUP_NAME); } @@ -2034,21 +2014,44 @@ void copyFileWindowsDefault(const Zstring& sourceFile, if (newAttrib) { newAttrib->fileSize = UInt64(cbd.fileInfoSrc.nFileSizeLow, cbd.fileInfoSrc.nFileSizeHigh); - newAttrib->modificationTime = toTimeT(cbd.fileInfoSrc.ftLastWriteTime); //no DST hack (yet) + //newAttrib->modificationTime = -> set further below newAttrib->sourceFileId = extractFileID(cbd.fileInfoSrc); newAttrib->targetFileId = extractFileID(cbd.fileInfoTrg); } { - //DST hack - const Int64 modTime = getFileTime(sourceFile, SYMLINK_FOLLOW); //throw FileError - setFileTime(targetFile, modTime, SYMLINK_FOLLOW); //throw FileError - //caveat: - ::CopyFileEx() silently *ignores* failure to set modification time!!! => we need to set it again but with proper error checking! - // - this sequence leads to a loss of precision of up to 1 sec! - // - perf-loss on USB sticks with many small files of about 30%! damn! + FILETIME creationtime = cbd.fileInfoSrc.ftCreationTime; + FILETIME lastWriteTimeRaw = cbd.fileInfoSrc.ftLastWriteTime; + //####################################### DST hack ########################################### + if (dst::isFatDrive(sourceFile)) //throw(); hacky: does not consider symlinks pointing to FAT! + { + const dst::RawTime rawTime(creationtime, lastWriteTimeRaw); + if (dst::fatHasUtcEncoded(rawTime)) //throw std::runtime_error + { + lastWriteTimeRaw = dst::fatDecodeUtcTime(rawTime); //return last write time in real UTC, throw (std::runtime_error) + ::GetSystemTimeAsFileTime(&creationtime); //real creation time information is not available... + } + } + //####################################### DST hack ########################################### if (newAttrib) - newAttrib->modificationTime = modTime; + newAttrib->modificationTime = toTimeT(lastWriteTimeRaw); + + //caveat: - ::CopyFileEx() silently *ignores* failure to set modification time!!! => we always need to set it again but with proper error checking! + // - perf-loss on USB sticks with many small files of about 30%! + FILETIME creationTimeOut = creationtime; + FILETIME lastWriteTimeOut = lastWriteTimeRaw; + + //####################################### DST hack ########################################### + if (dst::isFatDrive(targetFile)) //throw(); target cannot be a symlink in this context! + { + const dst::RawTime encodedTime = dst::fatEncodeUtcTime(lastWriteTimeRaw); //throw std::runtime_error + creationTimeOut = encodedTime.createTimeRaw; + lastWriteTimeOut = encodedTime.writeTimeRaw; + } + //####################################### DST hack ########################################### + + setFileTimeRaw(targetFile, creationTimeOut, lastWriteTimeOut, SYMLINK_FOLLOW); //throw FileError } guardTarget.dismiss(); //target has been created successfully! @@ -2057,7 +2060,7 @@ void copyFileWindowsDefault(const Zstring& sourceFile, //another layer to support copying sparse files inline -void copyFileWindowsSelectRoutine(const Zstring& sourceFile, const Zstring& targetFile, CallbackCopyFile* callback, FileAttrib* sourceAttr) +void copyFileWindowsSelectRoutine(const Zstring& sourceFile, const Zstring& targetFile, CallbackCopyFile* callback, InSyncAttributes* sourceAttr) { try { @@ -2072,7 +2075,7 @@ void copyFileWindowsSelectRoutine(const Zstring& sourceFile, const Zstring& targ //another layer of indirection solving 8.3 name clashes inline -void copyFileWindows(const Zstring& sourceFile, const Zstring& targetFile, CallbackCopyFile* callback, FileAttrib* sourceAttr) +void copyFileWindows(const Zstring& sourceFile, const Zstring& targetFile, CallbackCopyFile* callback, InSyncAttributes* sourceAttr) { try { @@ -2096,7 +2099,7 @@ void copyFileWindows(const Zstring& sourceFile, const Zstring& targetFile, Callb void copyFileLinuxMac(const Zstring& sourceFile, const Zstring& targetFile, CallbackCopyFile* callback, - FileAttrib* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting + InSyncAttributes* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting { //open sourceFile for reading FileInputUnbuffered fileIn(sourceFile); //throw FileError @@ -2187,7 +2190,7 @@ copyFileWindowsDefault(::CopyFileEx) copyFileWindowsSparse(::BackupRead/::Backu */ inline -void copyFileSelectOs(const Zstring& sourceFile, const Zstring& targetFile, CallbackCopyFile* callback, FileAttrib* sourceAttr) +void copyFileSelectOs(const Zstring& sourceFile, const Zstring& targetFile, CallbackCopyFile* callback, InSyncAttributes* sourceAttr) { #ifdef ZEN_WIN copyFileWindows(sourceFile, targetFile, callback, sourceAttr); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked @@ -2204,7 +2207,7 @@ void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPath bool copyFilePermissions, bool transactionalCopy, CallbackCopyFile* callback, - FileAttrib* sourceAttr) + InSyncAttributes* sourceAttr) { if (transactionalCopy) { diff --git a/zen/file_handling.h b/zen/file_handling.h index 2c4f7938..b3d5ca1a 100644 --- a/zen/file_handling.h +++ b/zen/file_handling.h @@ -52,7 +52,7 @@ void makeDirectory(const Zstring& directory, bool failIfExists = false); //throw //templateDir may be empty void makeDirectoryPlain(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing -struct FileAttrib +struct InSyncAttributes { UInt64 fileSize; Int64 modificationTime; //time_t UTC compatible @@ -65,7 +65,7 @@ void copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPathMissi bool copyFilePermissions, bool transactionalCopy, CallbackCopyFile* callback, //may be nullptr - FileAttrib* newAttrib = nullptr); //return current attributes at the time of copy + InSyncAttributes* newAttrib = nullptr); //return current attributes at the time of copy //Note: it MAY happen that copyFile() leaves temp files behind, e.g. temporary network drop. // => clean them up at an appropriate time (automatically set sync directions to delete them). They have the following ending: const Zstring TEMP_FILE_ENDING = Zstr(".ffs_tmp"); diff --git a/zen/file_id.cpp b/zen/file_id.cpp deleted file mode 100644 index 310390da..00000000 --- a/zen/file_id.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "file_id.h" - -#ifdef ZEN_WIN -#include "win.h" //includes "windows.h" -#include "long_path_prefix.h" -#include "scope_guard.h" - -#elif defined ZEN_LINUX || defined ZEN_MAC -#include -#endif - - -zen::FileId zen::getFileID(const Zstring& filename) -{ -#ifdef ZEN_WIN - //WARNING: CreateFile() is SLOW, while GetFileInformationByHandle() is cheap! http://msdn.microsoft.com/en-us/library/aa363788(VS.85).aspx - //privilege SE_BACKUP_NAME doesn't seem to be required here at all - - const HANDLE hFile = ::CreateFile(zen::applyLongPathPrefix(filename).c_str(), - 0, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - nullptr, - OPEN_EXISTING, - FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, //FILE_FLAG_BACKUP_SEMANTICS needed to open a directory - nullptr); - if (hFile != INVALID_HANDLE_VALUE) - { - ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); - - BY_HANDLE_FILE_INFORMATION fileInfo = {}; - if (::GetFileInformationByHandle(hFile, &fileInfo)) - return extractFileID(fileInfo); - } - -#elif defined ZEN_LINUX || defined ZEN_MAC - struct ::stat fileInfo = {}; - if (::lstat(filename.c_str(), &fileInfo) == 0) - return extractFileID(fileInfo); -#endif - - return zen::FileId(); -} - -//test whether two distinct paths point to the same file or directory: -// true: paths point to same files/dirs -// false: error occurred OR point to different files/dirs -//bool zen::samePhysicalFile(const Zstring& file1, const Zstring& file2) -//{ -// if (EqualFilename()(file1, file2)) //quick check -// return true; -// -// const auto id1 = getFileID(file1); -// const auto id2 = getFileID(file2); -// -// if (id1 == zen::FileId() || id2 == zen::FileId()) -// return false; -// -// return id1 == id2; -//} diff --git a/zen/file_id.h b/zen/file_id.h deleted file mode 100644 index 0ff6d7ec..00000000 --- a/zen/file_id.h +++ /dev/null @@ -1,22 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef FILEID_H_INCLUDED_32q87634289562345 -#define FILEID_H_INCLUDED_32q87634289562345 - -#include "file_id_def.h" -#include "zstring.h" - -//unique file identifier - -namespace zen -{ -//get unique file id (symbolic link handling: opens the link!!!) -//returns initial FileId() on error! -FileId getFileID(const Zstring& filename); -} - -#endif //FILEID_H_INCLUDED_32q87634289562345 diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index 959a6071..0e06d6c5 100644 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -464,7 +464,7 @@ private: //even at this point it's not sure whether data was written correctly, again cloud storages tend to lie about success status if (filesToValidate-- > 0) { - const dst::RawTime encodedTime = dst::fatEncodeUtcTime(tofiletime(it->second)); //throw std::runtime_error + const dst::RawTime encodedTime = dst::fatEncodeUtcTime(toFileTime(it->second)); //throw std::runtime_error //dst hack: verify data written; attention: this check may fail for "sync.ffs_lock" WIN32_FILE_ATTRIBUTE_DATA debugeAttr = {}; diff --git a/zen/fixed_list.h b/zen/fixed_list.h index d38dbae5..7e35f012 100644 --- a/zen/fixed_list.h +++ b/zen/fixed_list.h @@ -34,7 +34,7 @@ class FixedList public: FixedList() : - first(nullptr), + firstInsert(nullptr), lastInsert(nullptr), sz(0) {} @@ -60,17 +60,17 @@ public: typedef T& reference; typedef const T& const_reference; - iterator begin() { return first; } + iterator begin() { return firstInsert; } iterator end() { return iterator(); } - const_iterator begin() const { return first; } + const_iterator begin() const { return firstInsert; } const_iterator end () const { return const_iterator(); } - const_iterator cbegin() const { return first; } + const_iterator cbegin() const { return firstInsert; } const_iterator cend () const { return const_iterator(); } - reference front() { return first->val; } - const_reference front() const { return first->val; } + reference front() { return firstInsert->val; } + const_reference front() const { return firstInsert->val; } reference& back() { return lastInsert->val; } const_reference& back() const { return lastInsert->val; } @@ -88,18 +88,20 @@ public: void remove_if(Predicate pred) { Node* prev = nullptr; - Node* ptr = first; + Node* ptr = firstInsert; while (ptr) if (pred(ptr->val)) { - Node* tmp = ptr->next; + Node* next = ptr->next; deleteNode(ptr); + ptr = next; + if (prev) - prev->next = ptr = tmp; + prev->next = next; else - first = ptr = tmp; - if (!tmp) + firstInsert = next; + if (!next) lastInsert = prev; } else @@ -111,19 +113,19 @@ public: void clear() { - Node* ptr = first; + Node* ptr = firstInsert; while (ptr) { - Node* tmp = ptr; - ptr = ptr->next; - delete tmp; + Node* next = ptr->next; + deleteNode(ptr); + ptr = next; } - first = lastInsert = nullptr; - sz = 0; + firstInsert = lastInsert = nullptr; + assert(sz == 0); } - bool empty() const { return first == nullptr; } + bool empty() const { return firstInsert == nullptr; } size_t size() const { return sz; } private: @@ -132,11 +134,10 @@ private: void pushNode(Node* newNode) //throw() { - ++sz; if (lastInsert == nullptr) { - assert(first == nullptr); - first = lastInsert = newNode; + assert(firstInsert == nullptr && sz == 0); + firstInsert = lastInsert = newNode; } else { @@ -144,6 +145,7 @@ private: lastInsert->next = newNode; lastInsert = newNode; } + ++sz; } void deleteNode(Node* oldNode) @@ -153,7 +155,7 @@ private: delete oldNode; } - Node* first; + Node* firstInsert; Node* lastInsert; //point to last insertion; required by efficient emplace_back() size_t sz; }; diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp index 4b39d5a9..1568b616 100644 --- a/zen/format_unit.cpp +++ b/zen/format_unit.cpp @@ -287,11 +287,12 @@ std::wstring zen::utcToLocalTimeString(Int64 utcTime) auto errorMsg = [&] { return _("Error") + L" (time_t: " + numberTo(utcTime) + L")"; }; #ifdef ZEN_WIN - FILETIME lastWriteTimeUtc = tofiletime(utcTime); //convert ansi C time to FILETIME + FILETIME lastWriteTimeUtc = toFileTime(utcTime); //convert ansi C time to FILETIME SYSTEMTIME systemTimeLocal = {}; - if (useNewLocalTimeCalculation) //use DST setting from source date (like in Windows 7, see http://msdn.microsoft.com/en-us/library/ms724277(VS.85).aspx + //http://msdn.microsoft.com/en-us/library/ms724277(VS.85).aspx + if (useNewLocalTimeCalculation) //DST conversion like in Windows 7: NTFS stays fixed, but FAT jumps by one hour { SYSTEMTIME systemTimeUtc = {}; if (!::FileTimeToSystemTime(&lastWriteTimeUtc, //__in const FILETIME *lpFileTime, @@ -303,7 +304,7 @@ std::wstring zen::utcToLocalTimeString(Int64 utcTime) &systemTimeLocal)) //__out LPSYSTEMTIME lpLocalTime return errorMsg(); } - else //use DST setting (like in Windows 2000 and XP) + else //DST conversion like in Windows 2000 and XP: FAT times stay fixed, while NTFS jumps { FILETIME fileTimeLocal = {}; if (!::FileTimeToLocalFileTime(&lastWriteTimeUtc, //_In_ const FILETIME *lpFileTime, diff --git a/zen/int64.h b/zen/int64.h index 7d03f35d..6901ebeb 100644 --- a/zen/int64.h +++ b/zen/int64.h @@ -219,7 +219,7 @@ Int64 toTimeT(const FILETIME& ft) } inline -FILETIME tofiletime(const Int64& utcTime) +FILETIME toFileTime(const Int64& utcTime) { const UInt64 fileTimeLong = to(utcTime + Int64(3054539008UL, 2)) * 10000000U; const FILETIME output = { fileTimeLong.getLo(), fileTimeLong.getHi() }; diff --git a/zen/recycler.cpp b/zen/recycler.cpp index 20e1a4af..a1353f0b 100644 --- a/zen/recycler.cpp +++ b/zen/recycler.cpp @@ -95,7 +95,7 @@ void zen::recycleOrDelete(const std::vector& filenames, CallbackRecycli replaceCpy(_("Cannot load file %x."), L"%x", fmtFileName(getDllName()))); std::vector cNames; - for (auto it = filenames.begin(); it != filenames.end(); ++it) //caution to not create temporary strings here!! + for (auto it = filenames.begin(); it != filenames.end(); ++it) //CAUTION: to not create temporary strings here!! cNames.push_back(it->c_str()); CallbackData cbd(callback); @@ -114,9 +114,9 @@ void zen::recycleOrDelete(const std::vector& filenames, CallbackRecycli else //regular recycle bin usage: available since XP { Zstring filenamesDoubleNull; - for (auto it = filenames.begin(); it != filenames.end(); ++it) + for (const Zstring& filename : filenames) { - filenamesDoubleNull += *it; + filenamesDoubleNull += filename; filenamesDoubleNull += L'\0'; } diff --git a/zenXml/Changelog.txt b/zenXml/Changelog.txt new file mode 100644 index 00000000..8fb65daa --- /dev/null +++ b/zenXml/Changelog.txt @@ -0,0 +1,79 @@ +=========== +|Changelog| +=========== + +zen::Xml 2.0 +------------ +Skip XML comments while parsing +Added move constructor for XmlDoc +zen::parse and zen::load directly return XmlDoc +New macros to specify platform: ZEN_WIN, ZEN_LINUX, ZEN_MAC +Support serializing all integer limits (INT_MIN, INT_MAX, ect.) + + +zen::Xml 1.9 +------------ +Mark failed UTF conversions with replacement character +zen library update +revised documentation + + +zen::Xml 1.8 +------------ +Allow element values to contain non-escaped quotation marks +Respect Linux/Mac/Window line endings when calculating parsing error location +Map all end-of-line characters (Win/Linux/Mac) to \n + + +zen::Xml 1.7 +------------ +zen library update +revised documentation + + +zen::Xml 1.6 +------------ +Fixed header file circular dependency +Improved performance of FILE* based I/O +Updated zen libraries + + +zen::Xml 1.5 +------------ +Replaced sscanf/sprintf hex conversions: speedup of factors 3000 and 3 for large inputs +MinGW: Improved char-based string to floating point conversion by factor 10 +Fixed crash in UTF8 conversion when processing corrupted data +Fixed MSVC security warnings + + +zen::Xml 1.4 +------------ +Added missing include + + +zen::Xml 1.3 +------------ +Improved template metaprogramming implementation +Support unusual string classes: Glib::ustring, wxString (wxWidgets >= 2.9) +A number of documentation fixes + + +zen::Xml 1.2 +------------ +Design improvements and minor refactoring +Removed Loki dependency + + +zen::Xml 1.1 +------------ +A few minor fixes + + +zen::Xml 1.0 +------------ +Initial release: +- straightforward XML serialization +- automatic UTF-8 conversion +- convert all string-like types +- convert all built-in numbers +- support STL containers diff --git a/zenxml/LICENSE_1_0.txt b/zenxml/LICENSE_1_0.txt new file mode 100644 index 00000000..36b7cd93 --- /dev/null +++ b/zenxml/LICENSE_1_0.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/zenxml/bind.h b/zenxml/bind.h deleted file mode 100644 index abeff452..00000000 --- a/zenxml/bind.h +++ /dev/null @@ -1,390 +0,0 @@ -// ************************************************************************** -// * This file is part of the zen::Xml project. It is distributed under the * -// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef ZEN_XML_BIND_HEADER_9081740816593478258435 -#define ZEN_XML_BIND_HEADER_9081740816593478258435 - -#include -#include "cvrt_struc.h" -#include "parser.h" -#include "io.h" - -namespace zen -{ -/** -\file -\brief Map user data types to XML -*/ - -///Load XML document from a file -/** -Convenience function that does nothing more than calling loadStream() and parse(). - -\tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... -\param filename Input file name -\returns The loaded XML document -\throw XmlFileError -\throw XmlParsingError -*/ -template inline -XmlDoc load(const String& filename) //throw XmlFileError, XmlParsingError -{ - std::string stream = loadStream(filename); //throw XmlFileError - return parse(stream); //throw XmlParsingError -} - - -///Save XML document to a file -/** -Convenience function that does nothing more than calling serialize() and saveStream(). - -\tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... -\param doc The XML document to save -\param filename Output file name -\param lineBreak Line break, default: carriage return + new line -\param indent Indentation, default: four space characters -\throw XmlFileError -*/ -template inline -void save(const XmlDoc& doc, - const String& filename, - const std::string& lineBreak = "\r\n", - const std::string& indent = " ") //throw XmlFileError -{ - std::string stream = serialize(doc, lineBreak, indent); //throw () - saveStream(stream, filename); //throw XmlFileError -} - - -///Proxy class to conveniently convert user data into XML structure -class XmlOut -{ -public: - ///Construct an output proxy for an XML document - /** - \code - zen::XmlDoc doc; - - zen::XmlOut out(doc); - out["elem1"]( 1); // - out["elem2"]( 2); //write data into XML elements - out["elem3"](-3); // - - save(doc, "out.xml"); //throw XmlFileError - \endcode - Output: - \verbatim - - - 1 - 2 - -3 - - \endverbatim - */ - XmlOut(XmlDoc& doc) : ref_(&doc.root()) {} - ///Construct an output proxy for a single XML element - /** - \sa XmlOut(XmlDoc& doc) - */ - XmlOut(XmlElement& element) : ref_(&element) {} - - ///Retrieve a handle to an XML child element for writing - /** - The child element will be created if it is not yet existing. - \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... - \param name The name of the child element - */ - template - XmlOut operator[](const String& name) const - { - const std::string utf8name = utfCvrtTo(name); - XmlElement* child = ref_->getChild(utf8name); - return child ? *child : ref_->addChild(utf8name); - } - - ///Write user data to the underlying XML element - /** - This conversion requires a specialization of zen::writeText() or zen::writeStruc() for type T. - \tparam T User type that is converted into an XML element value. - */ - template - void operator()(const T& value) { writeStruc(value, *ref_); } - - ///Write user data to an XML attribute - /** - This conversion requires a specialization of zen::writeText() for type T. - \code - zen::XmlDoc doc; - - zen::XmlOut out(doc); - out["elem"].attribute("attr1", 1); // - out["elem"].attribute("attr2", 2); //write data into XML attributes - out["elem"].attribute("attr3", -3); // - - save(doc, "out.xml"); //throw XmlFileError - \endcode - Output: - \verbatim - - - - - \endverbatim - - \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... - \tparam T String-convertible user data type: e.g. any string-like type, all built-in arithmetic numbers - \sa XmlElement::setAttribute() - */ - template - void attribute(const String& name, const T& value) { ref_->setAttribute(name, value); } - - ///Return a reference to the underlying Xml element - XmlElement& ref() { return *ref_; } - -private: - XmlElement* ref_; //always bound! -}; - - -///Proxy class to conveniently convert XML structure to user data -class XmlIn -{ - class ErrorLog; - struct ConversionToBool { int dummy; }; - -public: - ///Construct an input proxy for an XML document - /** - \code - zen::XmlDoc doc; - ... //load document - zen::XmlIn in(doc); - in["elem1"](value1); // - in["elem2"](value2); //read data from XML elements into variables "value1", "value2", "value3" - in["elem3"](value3); // - \endcode - */ - XmlIn(const XmlDoc& doc) : refIndex(0), log(std::make_shared()) { refList.push_back(&doc.root()); } - ///Construct an input proxy for a single XML element, may be nullptr - /** - \sa XmlIn(const XmlDoc& doc) - */ - XmlIn(const XmlElement* element) : refIndex(0), log(std::make_shared()) { refList.push_back(element); } - ///Construct an input proxy for a single XML element - /** - \sa XmlIn(const XmlDoc& doc) - */ - XmlIn(const XmlElement& element) : refIndex(0), log(std::make_shared()) { refList.push_back(&element); } - - ///Retrieve a handle to an XML child element for reading - /** - It is \b not an error if the child element does not exist, but only later if a conversion to user data is attempted. - \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... - \param name The name of the child element - */ - template - XmlIn operator[](const String& name) const - { - std::vector childList; - - if (refIndex < refList.size()) - { - auto iterPair = refList[refIndex]->getChildren(name); - std::for_each(iterPair.first, iterPair.second, - [&](const XmlElement& child) { childList.push_back(&child); }); - } - - return XmlIn(childList, childList.empty() ? getChildNameFormatted(name) : std::string(), log); - } - - ///Refer to next sibling element with the same name - /** - Example: Loop over all XML child elements named "Item" - \verbatim - - - 1 - 3 - 5 - - \endverbatim - - \code - zen::XmlIn in(doc); - ... - for (zen::XmlIn child = in["Item"]; child; child.next()) - { - ... - } - \endcode - */ - void next() { ++refIndex; } - - ///Read user data from the underlying XML element - /** - This conversion requires a specialization of zen::readText() or zen::readStruc() for type T. - \tparam T User type that receives the data - \return "true" if data was read successfully - */ - template - bool operator()(T& value) const - { - if (refIndex < refList.size()) - { - bool success = readStruc(*refList[refIndex], value); - if (!success) - log->notifyConversionError(getNameFormatted()); - return success; - } - else - { - log->notifyMissingElement(getNameFormatted()); - return false; - } - } - - ///Read user data from an XML attribute - /** - This conversion requires a specialization of zen::readText() for type T. - - \code - zen::XmlDoc doc; - ... //load document - zen::XmlIn in(doc); - in["elem"].attribute("attr1", value1); // - in["elem"].attribute("attr2", value2); //read data from XML attributes into variables "value1", "value2", "value3" - in["elem"].attribute("attr3", value3); // - \endcode - - \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... - \tparam T String-convertible user data type: e.g. any string-like type, all built-in arithmetic numbers - \returns "true" if the attribute was found and the conversion to the output value was successful. - \sa XmlElement::getAttribute() - */ - template - bool attribute(const String& name, T& value) const - { - if (refIndex < refList.size()) - { - bool success = refList[refIndex]->getAttribute(name, value); - if (!success) - log->notifyMissingAttribute(getNameFormatted(), utfCvrtTo(name)); - return success; - } - else - { - log->notifyMissingElement(getNameFormatted()); - return false; - } - } - - ///Return a pointer to the underlying Xml element, may be nullptr - const XmlElement* get() const { return refIndex < refList.size() ? refList[refIndex] : nullptr; } - - ///Test whether the underlying XML element exists - /** - \code - XmlIn in(doc); - XmlIn child = in["elem1"]; - if (child) - ... - \endcode - Use member pointer as implicit conversion to bool (C++ Templates - Vandevoorde/Josuttis; chapter 20) - */ - operator int ConversionToBool::* () const { return get() ? &ConversionToBool::dummy : nullptr; } - - ///Notifies errors while mapping the XML to user data - /** - Error logging is shared by each hiearchy of XmlIn proxy instances that are created from each other. Consequently it doesn't matter which instance you query for errors: - \code - XmlIn in(doc); - XmlIn inItem = in["item1"]; - - int value = 0; - inItem(value); //let's assume this conversion failed - - assert(in.errorsOccured() == inItem.errorsOccured()); - assert(in.getErrorsAs() == inItem.getErrorsAs()); - \endcode - - Note that error logging is \b NOT global, but owned by all instances of a hierarchy of XmlIn proxies. - Therefore it's safe to use unrelated XmlIn proxies in multiple threads. - \n\n - However be aware that the chain of connected proxy instances will be broken once you call XmlIn::get() to retrieve the underlying pointer. - Errors that occur when working with this pointer are not logged by the original set of related instances. - */ - bool errorsOccured() const { return !log->elementList().empty(); } - - ///Get a list of XML element and attribute names which failed to convert to user data. - /** - \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ... - \returns A list of XML element and attribute names, empty list if no errors occured. - */ - template - std::vector getErrorsAs() const - { - std::vector output; - const auto& elements = log->elementList(); - std::transform(elements.begin(), elements.end(), std::back_inserter(output), [](const std::string& str) { return utfCvrtTo(str); }); - return output; - } - -private: - XmlIn(const std::vector& siblingList, const std::string& elementNameFmt, const std::shared_ptr& sharedlog) : - refList(siblingList), refIndex(0), formattedName(elementNameFmt), log(sharedlog) - { assert((!siblingList.empty() && elementNameFmt.empty()) || (siblingList.empty() && !elementNameFmt.empty())); } - - static std::string getNameFormatted(const XmlElement& elem) //" " - { - return (elem.parent() ? getNameFormatted(*elem.parent()) + " " : std::string()) + "<" + elem.getNameAs() + ">"; - } - - std::string getNameFormatted() const - { - if (refIndex < refList.size()) - { - assert(formattedName.empty()); - return getNameFormatted(*refList[refIndex]); - } - else - return formattedName; - } - - std::string getChildNameFormatted(const std::string& childName) const - { - std::string parentName = getNameFormatted(); - return (parentName.empty() ? std::string() : (parentName + " ")) + "<" + childName + ">"; - } - - class ErrorLog - { - public: - void notifyConversionError (const std::string& formattedName) { insert(formattedName); } - void notifyMissingElement (const std::string& formattedName) { insert(formattedName); } - void notifyMissingAttribute(const std::string& formattedName, const std::string& attribName) { insert(formattedName + " @" + attribName); } - - const std::vector& elementList() const { return failedElements; } - - private: - void insert(const std::string& newVal) - { - if (usedElements.insert(newVal).second) - failedElements.push_back(newVal); - } - - std::vector failedElements; //unique list of failed elements - std::set usedElements; - }; - - std::vector refList; //all sibling elements with same name (all pointers bound!) - size_t refIndex; //this sibling's index in refList - std::string formattedName; //contains full and formatted element name if (and only if) refList is empty - std::shared_ptr log; //always bound -}; -} - -#endif //ZEN_XML_BIND_HEADER_9081740816593478258435 diff --git a/zenxml/cvrt_struc.h b/zenxml/cvrt_struc.h deleted file mode 100644 index 690edacb..00000000 --- a/zenxml/cvrt_struc.h +++ /dev/null @@ -1,211 +0,0 @@ -// ************************************************************************** -// * This file is part of the zen::Xml project. It is distributed under the * -// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef ZEN_XML_CONVERT_STRUC_HEADER_018727409908342709743 -#define ZEN_XML_CONVERT_STRUC_HEADER_018727409908342709743 - -#include "dom.h" - -namespace zen -{ -/** -\file -\brief Handle conversion of arbitrary types to and from XML elements. -See comments in cvrt_text.h -*/ - -///Convert XML element to structured user data -/** - \param input The input XML element. - \param value Conversion target value. - \return "true" if value was read successfully. -*/ -template bool readStruc(const XmlElement& input, T& value); -///Convert structured user data into an XML element -/** - \param value The value to be converted. - \param output The output XML element. -*/ -template void writeStruc(const T& value, XmlElement& output); - - - - - - - - - - - - - - - - - -//------------------------------ implementation ------------------------------------- -namespace impl_2384343 -{ -ZEN_INIT_DETECT_MEMBER_TYPE(value_type); -ZEN_INIT_DETECT_MEMBER_TYPE(iterator); -ZEN_INIT_DETECT_MEMBER_TYPE(const_iterator); - -ZEN_INIT_DETECT_MEMBER(begin) // -ZEN_INIT_DETECT_MEMBER(end) //we don't know the exact declaration of the member attribute: may be in a base class! -ZEN_INIT_DETECT_MEMBER(insert) // -} - -template -struct IsStlContainer : - StaticBool< - impl_2384343::HasMemberType_value_type ::value&& - impl_2384343::HasMemberType_iterator ::value&& - impl_2384343::HasMemberType_const_iterator::value&& - impl_2384343::HasMember_begin ::value&& - impl_2384343::HasMember_end ::value&& - impl_2384343::HasMember_insert ::value> {}; - - -namespace impl_2384343 -{ -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 -struct IsStlPair : - StaticBool< - impl_2384343::HasMemberType_first_type ::value&& - impl_2384343::HasMemberType_second_type::value&& - impl_2384343::HasMember_first ::value&& - impl_2384343::HasMember_second ::value> {}; - -//###################################################################################### - -//Conversion from arbitrary types to an XML element -enum ValueType -{ - VALUE_TYPE_STL_CONTAINER, - VALUE_TYPE_STL_PAIR, - VALUE_TYPE_OTHER, -}; - -template -struct GetValueType : StaticEnum::value != TEXT_TYPE_OTHER ? VALUE_TYPE_OTHER : //some string classes are also STL containers, so check this first - IsStlContainer::value ? VALUE_TYPE_STL_CONTAINER : - IsStlPair::value ? VALUE_TYPE_STL_PAIR : - VALUE_TYPE_OTHER> {}; - - -template -struct ConvertElement; -/* -> expected interface -{ - void writeStruc(const T& value, XmlElement& output) const; - bool readStruc(const XmlElement& input, T& value) const; -}; -*/ - - -//partial specialization: handle conversion for all STL-container types! -template -struct ConvertElement -{ - void writeStruc(const T& value, XmlElement& output) const - { - std::for_each(value.begin(), value.end(), - [&](const typename T::value_type & childVal) - { - XmlElement& newChild = output.addChild("Item"); - zen::writeStruc(childVal, newChild); - }); - } - bool readStruc(const XmlElement& input, T& value) const - { - bool success = true; - value.clear(); - - auto iterPair = input.getChildren("Item"); - for (auto iter = iterPair.first; iter != iterPair.second; ++iter) - { - typename T::value_type childVal; //MSVC 2010 bug: cannot put this into a lambda body - if (zen::readStruc(*iter, childVal)) - value.insert(value.end(), childVal); - else - success = false; - } - return success; - } -}; - - -//partial specialization: handle conversion for std::pair -template -struct ConvertElement -{ - void writeStruc(const T& value, XmlElement& output) const - { - XmlElement& child1 = output.addChild("one"); //don't use "1st/2nd", this will confuse a few pedantic XML parsers - zen::writeStruc(value.first, child1); - - XmlElement& child2 = output.addChild("two"); - zen::writeStruc(value.second, child2); - } - bool readStruc(const XmlElement& input, T& value) const - { - bool success = true; - const XmlElement* child1 = input.getChild("one"); - if (!child1 || !zen::readStruc(*child1, value.first)) - success = false; - - const XmlElement* child2 = input.getChild("two"); - if (!child2 || !zen::readStruc(*child2, value.second)) - success = false; - - return success; - } -}; - - -//partial specialization: not a pure structured type, try text conversion (thereby respect user specializations of writeText()/readText()) -template -struct ConvertElement -{ - void writeStruc(const T& value, XmlElement& output) const - { - std::string tmp; - writeText(value, tmp); - output.setValue(tmp); - } - bool readStruc(const XmlElement& input, T& value) const - { - std::string rawStr; - input.getValue(rawStr); - return readText(rawStr, value); - } -}; - - -template inline -void writeStruc(const T& value, XmlElement& output) -{ - ConvertElement::value>().writeStruc(value, output); -} - - -template inline -bool readStruc(const XmlElement& input, T& value) -{ - return ConvertElement::value>().readStruc(input, value); -} -} - -#endif //ZEN_XML_CONVERT_STRUC_HEADER_018727409908342709743 diff --git a/zenxml/cvrt_text.h b/zenxml/cvrt_text.h deleted file mode 100644 index 80664317..00000000 --- a/zenxml/cvrt_text.h +++ /dev/null @@ -1,222 +0,0 @@ -// ************************************************************************** -// * This file is part of the zen::Xml project. It is distributed under the * -// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef ZEN_XML_CONVERT_TEXT_HEADER_018727339083427097434 -#define ZEN_XML_CONVERT_TEXT_HEADER_018727339083427097434 - -#include -#include - -namespace zen -{ -/** -\file -\brief Handle conversion of string-convertible types to and from std::string. - -It is \b not required to call these functions directly. They are implicitly used by zen::XmlElement::getValue(), -zen::XmlElement::setValue(), zen::XmlElement::getAttribute() and zen::XmlElement::setAttribute(). -\n\n -Conversions for the following user types are supported by default: - - strings - std::string, std::wstring, char*, wchar_t*, char, wchar_t, ect..., all STL-compatible-string-classes - - numbers - int, double, float, bool, long, ect..., all built-in numbers - - STL containers - std::map, std::set, std::vector, std::list, ect..., all STL-compatible-containers - - std::pair - -You can add support for additional types via template specialization. \n\n -Specialize zen::readStruc() and zen::writeStruc() to enable conversion from structured user types to XML elements. -Specialize zen::readText() and zen::writeText() to enable conversion from string-convertible user types to std::string. -Prefer latter if possible since it does not only enable conversions from XML elements to user data, but also from and to XML attributes. -\n\n - Example: type "bool" -\code -namespace zen -{ -template <> inline -void writeText(const bool& value, std::string& output) -{ - output = value ? "true" : "false"; -} - -template <> inline -bool readText(const std::string& input, bool& value) -{ - std::string tmp = input; - zen::trim(tmp); - if (tmp == "true") - value = true; - else if (tmp == "false") - value = false; - else - return false; - return true; -} -} -\endcode -*/ - - -///Convert text to user data - used by XML elements and attributes -/** - \param input Input text. - \param value Conversion target value. - \return "true" if value was read successfully. -*/ -template bool readText(const std::string& input, T& value); -///Convert user data into text - used by XML elements and attributes -/** - \param value The value to be converted. - \param output Output text. -*/ -template void writeText(const T& value, std::string& output); - - -/* Different classes of data types: - ------------------------------ -| structured | readStruc/writeStruc - e.g. string-convertible types, STL containers, std::pair, structured user types -| ------------------------- | -| | to-string-convertible | | readText/writeText - e.g. string-like types, all built-in arithmetic numbers, bool -| | --------------- | | -| | | string-like | | | utfCvrtTo - e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... -| | --------------- | | -| ------------------------- | ------------------------------ -*/ - - - - - - - - - - - - - - - -//------------------------------ implementation ------------------------------------- - -//Conversion from arbitrary types to text (for use with XML elements and attributes) -enum TextType -{ - TEXT_TYPE_BOOL, - TEXT_TYPE_NUMBER, - TEXT_TYPE_STRING, - TEXT_TYPE_OTHER, -}; - -template -struct GetTextType : StaticEnum::value ? TEXT_TYPE_BOOL : - IsStringLike::value ? TEXT_TYPE_STRING : //string before number to correctly handle char/wchar_t -> this was an issue with Loki only! - IsArithmetic::value ? TEXT_TYPE_NUMBER : // - TEXT_TYPE_OTHER> {}; - -//###################################################################################### - -template -struct ConvertText; -/* -> expected interface -{ - void writeText(const T& value, std::string& output) const; - bool readText(const std::string& input, T& value) const; -}; -*/ - -//partial specialization: type bool -template -struct ConvertText -{ - void writeText(bool value, std::string& output) const - { - output = value ? "true" : "false"; - } - bool readText(const std::string& input, bool& value) const - { - std::string tmp = input; - zen::trim(tmp); - if (tmp == "true") - value = true; - else if (tmp == "false") - value = false; - else - return false; - return true; - } -}; - -//partial specialization: handle conversion for all built-in arithmetic types! -template -struct ConvertText -{ - void writeText(const T& value, std::string& output) const - { - output = numberTo(value); - } - bool readText(const std::string& input, T& value) const - { - value = stringTo(input); - return true; - } -}; - -//partial specialization: handle conversion for all string-like types! -template -struct ConvertText -{ - void writeText(const T& value, std::string& output) const - { - output = utfCvrtTo(value); - } - bool readText(const std::string& input, T& value) const - { - value = utfCvrtTo(input); - return true; - } -}; - - -//partial specialization: unknown type -template -struct ConvertText -{ - //########################################################################################################################################### - assert_static(sizeof(T) == -1); - /* - ATTENTION: The data type T is yet unknown to the zen::Xml framework! - - Please provide a specialization for T of the following two functions in order to handle conversions to XML elements and attributes - - template <> void zen::writeText(const T& value, std::string& output) - template <> bool zen::readText(const std::string& input, T& value) - - If T is structured and cannot be converted to a text representation specialize these two functions to allow at least for conversions to XML elements: - - template <> void zen::writeStruc(const T& value, XmlElement& output) - template <> bool zen::readStruc(const XmlElement& input, T& value) - */ - //########################################################################################################################################### -}; - - -template inline -void writeText(const T& value, std::string& output) -{ - ConvertText::value>().writeText(value, output); -} - - -template inline -bool readText(const std::string& input, T& value) -{ - return ConvertText::value>().readText(input, value); -} -} - -#endif //ZEN_XML_CONVERT_TEXT_HEADER_018727339083427097434 diff --git a/zenxml/doc/annotated.html b/zenxml/doc/annotated.html new file mode 100644 index 00000000..b5e05616 --- /dev/null +++ b/zenxml/doc/annotated.html @@ -0,0 +1,120 @@ + + + + + +zen::Xml: Class List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + +
    + + + + +
    + +
    + +
    +
    +
    Class List
    +
    +
    +
    Here are the classes, structs, unions and interfaces with brief descriptions:
    + + + + + + + +
    zen::XmlDocThe complete XML document
    zen::XmlElementAn XML element
    zen::XmlErrorException base class for zen::Xml
    zen::XmlFileErrorException thrown due to failed file I/O
    zen::XmlInProxy class to conveniently convert XML structure to user data
    zen::XmlOutProxy class to conveniently convert user data into XML structure
    zen::XmlParsingErrorException thrown due to an XML parsing error
    +
    + + + + + + diff --git a/zenxml/doc/bc_s.png b/zenxml/doc/bc_s.png new file mode 100644 index 00000000..51ba0066 Binary files /dev/null and b/zenxml/doc/bc_s.png differ diff --git a/zenxml/doc/bdwn.png b/zenxml/doc/bdwn.png new file mode 100644 index 00000000..d0b575b7 Binary files /dev/null and b/zenxml/doc/bdwn.png differ diff --git a/zenxml/doc/bind_8h_source.html b/zenxml/doc/bind_8h_source.html new file mode 100644 index 00000000..300a10aa --- /dev/null +++ b/zenxml/doc/bind_8h_source.html @@ -0,0 +1,319 @@ + + + + + +zen::Xml: bind.h Source File + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + +
    + + + + +
    + +
    + +
    +
    +
    bind.h
    +
    +
    +
    00001 // **************************************************************************
    +00002 // * This file is part of the zen::Xml project. It is distributed under the *
    +00003 // * Boost Software License: http://www.boost.org/LICENSE_1_0.txt           *
    +00004 // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved        *
    +00005 // **************************************************************************
    +00006 
    +00007 #ifndef ZEN_XML_BIND_HEADER_9081740816593478258435
    +00008 #define ZEN_XML_BIND_HEADER_9081740816593478258435
    +00009 
    +00010 #include <set>
    +00011 #include "cvrt_struc.h"
    +00012 #include "parser.h"
    +00013 #include "io.h"
    +00014 
    +00015 namespace zen
    +00016 {
    +00022 
    +00023 
    +00032 template <class String> inline
    +00033 void load(const String& filename, XmlDoc& doc) //throw XmlFileError, XmlParsingError
    +00034 {
    +00035     std::string stream = loadStream(filename); //throw XmlFileError
    +00036     parse(stream, doc); //throw XmlParsingError
    +00037 }
    +00038 
    +00039 
    +00041 
    +00051 template <class String> inline
    +00052 void save(const XmlDoc& doc,
    +00053           const String& filename,
    +00054           const std::string& lineBreak = "\r\n",
    +00055           const std::string& indent = "    ") //throw XmlFileError
    +00056 {
    +00057     std::string stream = serialize(doc, lineBreak, indent); //throw ()
    +00058     saveStream(stream, filename); //throw XmlFileError
    +00059 }
    +00060 
    +00061 
    +00063 class XmlOut
    +00064 {
    +00065 public:
    +00067 
    +00088     XmlOut(XmlDoc& doc) : ref_(&doc.root()) {}
    +00090 
    +00093     XmlOut(XmlElement& element) : ref_(&element) {}
    +00094 
    +00096 
    +00101     template <class String>
    +00102     XmlOut operator[](const String& name) const
    +00103     {
    +00104         const std::string utf8name = utfCvrtTo<std::string>(name);
    +00105         XmlElement* child = ref_->getChild(utf8name);
    +00106         return child ? *child : ref_->addChild(utf8name);
    +00107     }
    +00108 
    +00110 
    +00114     template <class T>
    +00115     void operator()(const T& value) { writeStruc(value, *ref_); }
    +00116 
    +00118 
    +00142     template <class String, class T>
    +00143     void attribute(const String& name, const T& value) { ref_->setAttribute(name, value); }
    +00144 
    +00146     XmlElement& ref() { return *ref_; }
    +00147 
    +00148 private:
    +00149     XmlElement* ref_; //always bound!
    +00150 };
    +00151 
    +00152 
    +00154 class XmlIn
    +00155 {
    +00156     class ErrorLog;
    +00157     struct ConversionToBool { int dummy; };
    +00158 
    +00159 public:
    +00161 
    +00171     XmlIn(const XmlDoc& doc) : refIndex(0), log(std::make_shared<ErrorLog>()) { refList.push_back(&doc.root()); }
    +00173 
    +00176     XmlIn(const XmlElement* element) : refIndex(0), log(std::make_shared<ErrorLog>()) { refList.push_back(element); }
    +00178 
    +00181     XmlIn(const XmlElement& element) : refIndex(0), log(std::make_shared<ErrorLog>()) { refList.push_back(&element); }
    +00182 
    +00184 
    +00189     template <class String>
    +00190     XmlIn operator[](const String& name) const
    +00191     {
    +00192         std::vector<const XmlElement*> childList;
    +00193 
    +00194         if (refIndex < refList.size())
    +00195         {
    +00196             auto iterPair = refList[refIndex]->getChildren(name);
    +00197             std::for_each(iterPair.first, iterPair.second,
    +00198             [&](const XmlElement& child) { childList.push_back(&child); });
    +00199         }
    +00200 
    +00201         return XmlIn(childList, childList.empty() ? getChildNameFormatted(name) : std::string(), log);
    +00202     }
    +00203 
    +00205 
    +00225     void next() { ++refIndex; }
    +00226 
    +00228 
    +00233     template <class T>
    +00234     bool operator()(T& value) const
    +00235     {
    +00236         if (refIndex < refList.size())
    +00237         {
    +00238             bool success = readStruc(*refList[refIndex], value);
    +00239             if (!success)
    +00240                 log->notifyConversionError(getNameFormatted());
    +00241             return success;
    +00242         }
    +00243         else
    +00244         {
    +00245             log->notifyMissingElement(getNameFormatted());
    +00246             return false;
    +00247         }
    +00248     }
    +00249 
    +00251 
    +00268     template <class String, class T>
    +00269     bool attribute(const String& name, T& value) const
    +00270     {
    +00271         if (refIndex < refList.size())
    +00272         {
    +00273             bool success = refList[refIndex]->getAttribute(name, value);
    +00274             if (!success)
    +00275                 log->notifyMissingAttribute(getNameFormatted(), utfCvrtTo<std::string>(name));
    +00276             return success;
    +00277         }
    +00278         else
    +00279         {
    +00280             log->notifyMissingElement(getNameFormatted());
    +00281             return false;
    +00282         }
    +00283     }
    +00284 
    +00286     const XmlElement* get() const { return refIndex < refList.size() ? refList[refIndex] : nullptr; }
    +00287 
    +00289 
    +00298     operator int ConversionToBool::* () const { return get() ? &ConversionToBool::dummy : nullptr; }
    +00299 
    +00301 
    +00320     bool errorsOccured() const { return !log->elementList().empty(); }
    +00321 
    +00323 
    +00327     template <class String>
    +00328     std::vector<String> getErrorsAs() const
    +00329     {
    +00330         std::vector<String> output;
    +00331         const auto& elements = log->elementList();
    +00332         std::transform(elements.begin(), elements.end(), std::back_inserter(output), [](const std::string& str) { return utfCvrtTo<String>(str); });
    +00333         return output;
    +00334     }
    +00335 
    +00336 private:
    +00337     XmlIn(const std::vector<const XmlElement*>& siblingList, const std::string& elementNameFmt, const std::shared_ptr<ErrorLog>& sharedlog) :
    +00338         refList(siblingList), refIndex(0), formattedName(elementNameFmt), log(sharedlog)
    +00339     { assert((!siblingList.empty() && elementNameFmt.empty()) || (siblingList.empty() && !elementNameFmt.empty())); }
    +00340 
    +00341     static std::string getNameFormatted(const XmlElement& elem) //"<Root> <Level1> <Level2>"
    +00342     {
    +00343         return (elem.parent() ? getNameFormatted(*elem.parent()) + " " : std::string()) + "<" + elem.getNameAs<std::string>() + ">";
    +00344     }
    +00345 
    +00346     std::string getNameFormatted() const
    +00347     {
    +00348         if (refIndex < refList.size())
    +00349         {
    +00350             assert(formattedName.empty());
    +00351             return getNameFormatted(*refList[refIndex]);
    +00352         }
    +00353         else
    +00354             return formattedName;
    +00355     }
    +00356 
    +00357     std::string getChildNameFormatted(const std::string& childName) const
    +00358     {
    +00359         std::string parentName = getNameFormatted();
    +00360         return (parentName.empty() ? std::string() : (parentName + " ")) + "<" + childName + ">";
    +00361     }
    +00362 
    +00363     class ErrorLog
    +00364     {
    +00365     public:
    +00366         void notifyConversionError (const std::string& formattedName)  { insert(formattedName); }
    +00367         void notifyMissingElement  (const std::string& formattedName)  { insert(formattedName); }
    +00368         void notifyMissingAttribute(const std::string& formattedName, const std::string& attribName) { insert(formattedName + " @" + attribName); }
    +00369 
    +00370         const std::vector<std::string>& elementList() const { return failedElements; }
    +00371 
    +00372     private:
    +00373         void insert(const std::string& newVal)
    +00374         {
    +00375             if (usedElements.insert(newVal).second)
    +00376                 failedElements.push_back(newVal);
    +00377         }
    +00378 
    +00379         std::vector<std::string> failedElements; //unique list of failed elements
    +00380         std::set<std::string>    usedElements;
    +00381     };
    +00382 
    +00383     std::vector<const XmlElement*> refList; //all sibling elements with same name (all pointers bound!)
    +00384     size_t refIndex;                        //this sibling's index in refList
    +00385     std::string formattedName;     //contains full and formatted element name if (and only if) refList is empty
    +00386     std::shared_ptr<ErrorLog> log; //always bound
    +00387 };
    +00388 }
    +00389 
    +00390 #endif //ZEN_XML_BIND_HEADER_9081740816593478258435
    +
    + + + + + + diff --git a/zenxml/doc/classes.html b/zenxml/doc/classes.html new file mode 100644 index 00000000..4670ccf3 --- /dev/null +++ b/zenxml/doc/classes.html @@ -0,0 +1,120 @@ + + + + + +zen::Xml: Class Index + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + +
    + + + + +
    + +
    + +
    +
    +
    Class Index
    +
    +
    + + + + + + +
      X  
    +
    XmlElement (zen)   XmlFileError (zen)   XmlOut (zen)   
    XmlError (zen)   XmlIn (zen)   XmlParsingError (zen)   
    XmlDoc (zen)   
    + +
    + + + + + + diff --git a/zenxml/doc/classzen_1_1_xml_doc-members.html b/zenxml/doc/classzen_1_1_xml_doc-members.html new file mode 100644 index 00000000..4a936065 --- /dev/null +++ b/zenxml/doc/classzen_1_1_xml_doc-members.html @@ -0,0 +1,128 @@ + + + + + +zen::Xml: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + + + + + +
    + +
    + + +
    +
    +
    +
    zen::XmlDoc Member List
    +
    +
    +This is the complete list of members for zen::XmlDoc, including all inherited members. + + + + + + + + + + +
    getEncodingAs() const zen::XmlDoc
    getStandaloneAs() const zen::XmlDoc
    getVersionAs() const zen::XmlDoc
    root() const zen::XmlDoc
    root()zen::XmlDoc
    setEncoding(const String &encoding)zen::XmlDoc
    setStandalone(const String &standalone)zen::XmlDoc
    setVersion(const String &version)zen::XmlDoc
    XmlDoc()zen::XmlDoc
    XmlDoc(String rootName)zen::XmlDoc
    + + + + + + diff --git a/zenxml/doc/classzen_1_1_xml_doc.html b/zenxml/doc/classzen_1_1_xml_doc.html new file mode 100644 index 00000000..80e05844 --- /dev/null +++ b/zenxml/doc/classzen_1_1_xml_doc.html @@ -0,0 +1,353 @@ + + + + + +zen::Xml: zen::XmlDoc Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + + + + + +
    + +
    + + +
    +
    + +
    +
    zen::XmlDoc Class Reference
    +
    +
    + +

    The complete XML document. + More...

    + +

    #include <dom.h>

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

    XmlDoc ()
     Default constructor setting up an empty XML document with a standard declaration: <?xml version="1.0" encoding="UTF-8" ?>
    template<class String >
     XmlDoc (String rootName)
    +const XmlElementroot () const
     Get a const reference to the document's root element.
    +XmlElementroot ()
     Get a reference to the document's root element.
    template<class String >
    String getVersionAs () const
     Get the version used in the XML declaration.
    template<class String >
    void setVersion (const String &version)
     Set the version used in the XML declaration.
    template<class String >
    String getEncodingAs () const
     Get the encoding used in the XML declaration.
    template<class String >
    void setEncoding (const String &encoding)
     Set the encoding used in the XML declaration.
    template<class String >
    String getStandaloneAs () const
     Get the standalone string used in the XML declaration.
    template<class String >
    void setStandalone (const String &standalone)
     Set the standalone string used in the XML declaration.
    +

    Detailed Description

    +

    The complete XML document.

    +

    Constructor & Destructor Documentation

    + +
    +
    +
    +template<class String >
    + + + + + + + + +
    zen::XmlDoc::XmlDoc (String rootName)
    +
    +
    +
    Template Parameters:
    + + +
    StringArbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
    +
    +
    +
    Parameters:
    + + +
    rootNameThe name of the XML document's root element.
    +
    +
    + +
    +
    +

    Member Function Documentation

    + +
    +
    +
    +template<class String >
    + + + + + + + +
    String zen::XmlDoc::getEncodingAs () const
    +
    +
    + +

    Get the encoding used in the XML declaration.

    +
    Template Parameters:
    + + +
    StringArbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ...
    +
    +
    + +
    +
    + +
    +
    +
    +template<class String >
    + + + + + + + +
    String zen::XmlDoc::getStandaloneAs () const
    +
    +
    + +

    Get the standalone string used in the XML declaration.

    +
    Template Parameters:
    + + +
    StringArbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ...
    +
    +
    + +
    +
    + +
    +
    +
    +template<class String >
    + + + + + + + +
    String zen::XmlDoc::getVersionAs () const
    +
    +
    + +

    Get the version used in the XML declaration.

    +
    Template Parameters:
    + + +
    StringArbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ...
    +
    +
    + +
    +
    + +
    +
    +
    +template<class String >
    + + + + + + + + +
    void zen::XmlDoc::setEncoding (const String & encoding)
    +
    +
    + +

    Set the encoding used in the XML declaration.

    +
    Template Parameters:
    + + +
    StringArbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
    +
    +
    + +
    +
    + +
    +
    +
    +template<class String >
    + + + + + + + + +
    void zen::XmlDoc::setStandalone (const String & standalone)
    +
    +
    + +

    Set the standalone string used in the XML declaration.

    +
    Template Parameters:
    + + +
    StringArbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
    +
    +
    + +
    +
    + +
    +
    +
    +template<class String >
    + + + + + + + + +
    void zen::XmlDoc::setVersion (const String & version)
    +
    +
    + +

    Set the version used in the XML declaration.

    +
    Template Parameters:
    + + +
    StringArbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
    +
    +
    + +
    +
    +
    + + + + + + diff --git a/zenxml/doc/classzen_1_1_xml_element-members.html b/zenxml/doc/classzen_1_1_xml_element-members.html new file mode 100644 index 00000000..bccd92d1 --- /dev/null +++ b/zenxml/doc/classzen_1_1_xml_element-members.html @@ -0,0 +1,133 @@ + + + + + +zen::Xml: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + + + + + +
    + +
    + + +
    +
    +
    +
    zen::XmlElement Member List
    +
    +
    +This is the complete list of members for zen::XmlElement, including all inherited members. + + + + + + + + + + + + + + + +
    addChild(const String &name)zen::XmlElement
    getAttribute(const String &name, T &value) const zen::XmlElement
    getChild(const String &name) const zen::XmlElement
    getChild(const String &name)zen::XmlElement
    getChildren(const String &name) const zen::XmlElement
    getChildren(const String &name)zen::XmlElement
    getChildren() const zen::XmlElement
    getChildren()zen::XmlElement
    getNameAs() const zen::XmlElement
    getValue(T &value) const zen::XmlElement
    parent()zen::XmlElement
    parent() const zen::XmlElement
    removeAttribute(const String &name)zen::XmlElement
    setAttribute(const String &name, const T &value)zen::XmlElement
    setValue(const T &value)zen::XmlElement
    + + + + + + diff --git a/zenxml/doc/classzen_1_1_xml_element.html b/zenxml/doc/classzen_1_1_xml_element.html new file mode 100644 index 00000000..fc87396a --- /dev/null +++ b/zenxml/doc/classzen_1_1_xml_element.html @@ -0,0 +1,552 @@ + + + + + +zen::Xml: zen::XmlElement Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + + + + + +
    + +
    + + +
    +
    + +
    +
    zen::XmlElement Class Reference
    +
    +
    + +

    An XML element. + More...

    + +

    #include <dom.h>

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

    template<class String >
    String getNameAs () const
     Retrieve the name of this XML element.
    template<class T >
    bool getValue (T &value) const
     Get the value of this element as a user type.
    template<class T >
    void setValue (const T &value)
     Set the value of this element.
    template<class String , class T >
    bool getAttribute (const String &name, T &value) const
     Retrieve an attribute by name.
    template<class String , class T >
    void setAttribute (const String &name, const T &value)
     Create or update an XML attribute.
    template<class String >
    void removeAttribute (const String &name)
     Remove the attribute with the given name.
    template<class String >
    XmlElementaddChild (const String &name)
     Create a new child element and return a reference to it.
    template<class String >
    const XmlElementgetChild (const String &name) const
     Retrieve a child element with the given name.
    template<class String >
    XmlElementgetChild (const String &name)
    template<class String >
    std::pair< ChildIterConst2,
    +ChildIterConst2 > 
    getChildren (const String &name) const
     Access all child elements with the given name via STL iterators.
    template<class String >
    std::pair< ChildIter2, ChildIter2 > getChildren (const String &name)
    std::pair< ChildIterConst,
    +ChildIterConst > 
    getChildren () const
     Access all child elements sequentially via STL iterators.
    std::pair< ChildIter, ChildIter > getChildren ()
    +XmlElementparent ()
     Get parent XML element, may be nullptr for root element.
    +const XmlElementparent () const
     Get parent XML element, may be nullptr for root element.
    +

    Detailed Description

    +

    An XML element.

    +

    Member Function Documentation

    + +
    +
    +
    +template<class String >
    + + + + + + + + +
    XmlElement& zen::XmlElement::addChild (const String & name)
    +
    +
    + +

    Create a new child element and return a reference to it.

    +
    Template Parameters:
    + + +
    StringArbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
    +
    +
    +
    Parameters:
    + + +
    nameThe name of the child element to be created.
    +
    +
    + +
    +
    + +
    +
    +
    +template<class String , class T >
    + + + + + + + + + + + + + + + + + + +
    bool zen::XmlElement::getAttribute (const String & name,
    T & value 
    ) const
    +
    +
    + +

    Retrieve an attribute by name.

    +
    Template Parameters:
    + + + +
    StringArbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
    TString-convertible user data type: e.g. any string class, all built-in arithmetic numbers
    +
    +
    +
    Parameters:
    + + + +
    nameThe name of the attribute to retrieve.
    valueThe value of the attribute converted to T.
    +
    +
    +
    Returns:
    "true" if value was retrieved successfully.
    + +
    +
    + +
    +
    +
    +template<class String >
    + + + + + + + + +
    const XmlElement* zen::XmlElement::getChild (const String & name) const
    +
    +
    + +

    Retrieve a child element with the given name.

    +
    Template Parameters:
    + + +
    StringArbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
    +
    +
    +
    Parameters:
    + + +
    nameThe name of the child element to be retrieved.
    +
    +
    +
    Returns:
    A pointer to the child element or nullptr if none was found.
    + +
    +
    + +
    +
    +
    +template<class String >
    + + + + + + + + +
    XmlElement* zen::XmlElement::getChild (const String & name)
    +
    +
    +
    See also:
    getChild
    + +
    +
    + +
    +
    +
    +template<class String >
    + + + + + + + + +
    std::pair<ChildIterConst2, ChildIterConst2> zen::XmlElement::getChildren (const String & name) const
    +
    +
    + +

    Access all child elements with the given name via STL iterators.

    +
          auto iterPair = elem.getChildren("Item");
    +      std::for_each(iterPair.first, iterPair.second,
    +            [](const XmlElement& child) { ... });
    +
    Parameters:
    + + +
    nameThe name of the child elements to be retrieved.
    +
    +
    +
    Returns:
    A pair of STL begin/end iterators to access the child elements sequentially.
    + +
    +
    + +
    +
    +
    +template<class String >
    + + + + + + + + +
    std::pair<ChildIter2, ChildIter2> zen::XmlElement::getChildren (const String & name)
    +
    +
    +
    See also:
    getChildren
    + +
    +
    + +
    +
    + + + + + + + +
    std::pair<ChildIterConst, ChildIterConst> zen::XmlElement::getChildren () const
    +
    +
    + +

    Access all child elements sequentially via STL iterators.

    +
          auto iterPair = elem.getChildren();
    +      std::for_each(iterPair.first, iterPair.second,
    +            [](const XmlElement& child) { ... });
    +
    Returns:
    A pair of STL begin/end iterators to access all child elements sequentially.
    + +
    +
    + +
    +
    + + + + + + + +
    std::pair<ChildIter, ChildIter> zen::XmlElement::getChildren ()
    +
    +
    +
    See also:
    getChildren
    + +
    +
    + +
    +
    +
    +template<class String >
    + + + + + + + +
    String zen::XmlElement::getNameAs () const
    +
    +
    + +

    Retrieve the name of this XML element.

    +
    Template Parameters:
    + + +
    StringArbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ...
    +
    +
    +
    Returns:
    Name of the XML element.
    + +
    +
    + +
    +
    +
    +template<class T >
    + + + + + + + + +
    bool zen::XmlElement::getValue (T & value) const
    +
    +
    + +

    Get the value of this element as a user type.

    +
    Template Parameters:
    + + +
    TArbitrary user data type: e.g. any string class, all built-in arithmetic numbers, STL container, ...
    +
    +
    +
    Returns:
    "true" if Xml element was successfully converted to value, cannot fail for string-like types
    + +
    +
    + +
    +
    +
    +template<class String >
    + + + + + + + + +
    void zen::XmlElement::removeAttribute (const String & name)
    +
    +
    + +

    Remove the attribute with the given name.

    +
    Template Parameters:
    + + +
    StringArbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
    +
    +
    + +
    +
    + +
    +
    +
    +template<class String , class T >
    + + + + + + + + + + + + + + + + + + +
    void zen::XmlElement::setAttribute (const String & name,
    const T & value 
    )
    +
    +
    + +

    Create or update an XML attribute.

    +
    Template Parameters:
    + + + +
    StringArbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
    TString-convertible user data type: e.g. any string-like type, all built-in arithmetic numbers
    +
    +
    +
    Parameters:
    + + + +
    nameThe name of the attribute to create or update.
    valueThe value to set.
    +
    +
    + +
    +
    + +
    +
    +
    +template<class T >
    + + + + + + + + +
    void zen::XmlElement::setValue (const T & value)
    +
    +
    + +

    Set the value of this element.

    +
    Template Parameters:
    + + +
    TArbitrary user data type: e.g. any string-like type, all built-in arithmetic numbers, STL container, ...
    +
    +
    + +
    +
    +
    + + + + + + diff --git a/zenxml/doc/classzen_1_1_xml_in-members.html b/zenxml/doc/classzen_1_1_xml_in-members.html new file mode 100644 index 00000000..794b180d --- /dev/null +++ b/zenxml/doc/classzen_1_1_xml_in-members.html @@ -0,0 +1,129 @@ + + + + + +zen::Xml: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + + + + + +
    + +
    + + +
    +
    +
    +
    zen::XmlIn Member List
    +
    +
    +This is the complete list of members for zen::XmlIn, including all inherited members. + + + + + + + + + + + +
    attribute(const String &name, T &value) const zen::XmlIn
    errorsOccured() const zen::XmlIn
    get() const zen::XmlIn
    getErrorsAs() const zen::XmlIn
    next()zen::XmlIn
    operator int ConversionToBool::*() const zen::XmlIn
    operator()(T &value) const zen::XmlIn
    operator[](const String &name) const zen::XmlIn
    XmlIn(const XmlDoc &doc)zen::XmlIn
    XmlIn(const XmlElement *element)zen::XmlIn
    XmlIn(const XmlElement &element)zen::XmlIn
    + + + + + + diff --git a/zenxml/doc/classzen_1_1_xml_in.html b/zenxml/doc/classzen_1_1_xml_in.html new file mode 100644 index 00000000..f1093061 --- /dev/null +++ b/zenxml/doc/classzen_1_1_xml_in.html @@ -0,0 +1,446 @@ + + + + + +zen::Xml: zen::XmlIn Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + + + + + +
    + +
    + + +
    +
    + +
    +
    zen::XmlIn Class Reference
    +
    +
    + +

    Proxy class to conveniently convert XML structure to user data. + More...

    + +

    #include <bind.h>

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Public Member Functions

     XmlIn (const XmlDoc &doc)
     Construct an input proxy for an XML document.
     XmlIn (const XmlElement *element)
     Construct an input proxy for a single XML element, may be nullptr.
     XmlIn (const XmlElement &element)
     Construct an input proxy for a single XML element.
    template<class String >
    XmlIn operator[] (const String &name) const
     Retrieve a handle to an XML child element for reading.
    void next ()
     Refer to next sibling element with the same name.
    template<class T >
    bool operator() (T &value) const
     Read user data from the underlying XML element.
    template<class String , class T >
    bool attribute (const String &name, T &value) const
     Read user data from an XML attribute.
    +const XmlElementget () const
     Return a pointer to the underlying Xml element, may be nullptr.
     operator int ConversionToBool::* () const
     Test whether the underlying XML element exists.
    bool errorsOccured () const
     Notifies errors while mapping the XML to user data.
    template<class String >
    std::vector< String > getErrorsAs () const
     Get a list of XML element and attribute names which failed to convert to user data.
    +

    Detailed Description

    +

    Proxy class to conveniently convert XML structure to user data.

    +

    Constructor & Destructor Documentation

    + +
    +
    + + + + + + + + +
    zen::XmlIn::XmlIn (const XmlDocdoc)
    +
    +
    + +

    Construct an input proxy for an XML document.

    +
            zen::XmlDoc doc;
    +          ... //load document
    +        zen::XmlIn in(doc);
    +        in["elem1"](value1); //
    +        in["elem2"](value2); //read data from XML elements into variables "value1", "value2", "value3"
    +        in["elem3"](value3); //
    +
    +
    +
    + +
    +
    + + + + + + + + +
    zen::XmlIn::XmlIn (const XmlElementelement)
    +
    +
    + +

    Construct an input proxy for a single XML element, may be nullptr.

    +
    See also:
    XmlIn(const XmlDoc& doc)
    + +
    +
    + +
    +
    + + + + + + + + +
    zen::XmlIn::XmlIn (const XmlElementelement)
    +
    +
    + +

    Construct an input proxy for a single XML element.

    +
    See also:
    XmlIn(const XmlDoc& doc)
    + +
    +
    +

    Member Function Documentation

    + +
    +
    +
    +template<class String , class T >
    + + + + + + + + + + + + + + + + + + +
    bool zen::XmlIn::attribute (const String & name,
    T & value 
    ) const
    +
    +
    + +

    Read user data from an XML attribute.

    +

    This conversion requires a specialization of zen::readText() for type T.

    +
            zen::XmlDoc doc;
    +          ... //load document
    +        zen::XmlIn in(doc);
    +        in["elem"].attribute("attr1", value1); //
    +        in["elem"].attribute("attr2", value2); //read data from XML attributes into variables "value1", "value2", "value3"
    +        in["elem"].attribute("attr3", value3); //
    +
    Template Parameters:
    + + + +
    StringArbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
    TString-convertible user data type: e.g. any string-like type, all built-in arithmetic numbers
    +
    +
    +
    Returns:
    "true" if the attribute was found and the conversion to the output value was successful.
    +
    See also:
    XmlElement::getAttribute()
    + +
    +
    + +
    +
    + + + + + + + +
    bool zen::XmlIn::errorsOccured () const
    +
    +
    + +

    Notifies errors while mapping the XML to user data.

    +

    Error logging is shared by each hiearchy of XmlIn proxy instances that are created from each other. Consequently it doesn't matter which instance you query for errors:

    +
            XmlIn in(doc);
    +        XmlIn inItem = in["item1"];
    +
    +        int value = 0;
    +        inItem(value); //let's assume this conversion failed
    +
    +        assert(in.errorsOccured() == inItem.errorsOccured());
    +        assert(in.getErrorsAs<std::string>() == inItem.getErrorsAs<std::string>());
    +

    Note that error logging is NOT global, but owned by all instances of a hierarchy of XmlIn proxies. Therefore it's safe to use unrelated XmlIn proxies in multiple threads.
    +
    + However be aware that the chain of connected proxy instances will be broken once you call XmlIn::get() to retrieve the underlying pointer. Errors that occur when working with this pointer are not logged by the original set of related instances.

    + +
    +
    + +
    +
    +
    +template<class String >
    + + + + + + + +
    std::vector<String> zen::XmlIn::getErrorsAs () const
    +
    +
    + +

    Get a list of XML element and attribute names which failed to convert to user data.

    +
    Template Parameters:
    + + +
    StringArbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ...
    +
    +
    +
    Returns:
    A list of XML element and attribute names, empty list if no errors occured.
    + +
    +
    + +
    +
    + + + + + + + +
    void zen::XmlIn::next ()
    +
    +
    + +

    Refer to next sibling element with the same name.

    +

    Example: Loop over all XML child elements named "Item"

    +
        <?xml version="1.0" encoding="UTF-8"?>
    +    <Root>
    +        <Item>1</Item>
    +        <Item>3</Item>
    +        <Item>5</Item>
    +    </Root>
        zen::XmlIn in(doc);
    +    ...
    +    for (zen::XmlIn child = in["Item"]; child; child.next())
    +    {
    +        ...
    +    }
    +
    +
    +
    + +
    +
    + + + + + + + +
    zen::XmlIn::operator int ConversionToBool::* () const
    +
    +
    + +

    Test whether the underlying XML element exists.

    +
             XmlIn in(doc);
    +         XmlIn child = in["elem1"];
    +         if (child)
    +           ...
    +

    Use member pointer as implicit conversion to bool (C++ Templates - Vandevoorde/Josuttis; chapter 20)

    + +
    +
    + +
    +
    +
    +template<class T >
    + + + + + + + + +
    bool zen::XmlIn::operator() (T & value) const
    +
    +
    + +

    Read user data from the underlying XML element.

    +

    This conversion requires a specialization of zen::readText() or zen::readStruc() for type T.

    +
    Template Parameters:
    + + +
    TUser type that receives the data
    +
    +
    +
    Returns:
    "true" if data was read successfully
    + +
    +
    + +
    +
    +
    +template<class String >
    + + + + + + + + +
    XmlIn zen::XmlIn::operator[] (const String & name) const
    +
    +
    + +

    Retrieve a handle to an XML child element for reading.

    +

    It is not an error if the child element does not exist, but only later if a conversion to user data is attempted.

    +
    Template Parameters:
    + + +
    StringArbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
    +
    +
    +
    Parameters:
    + + +
    nameThe name of the child element
    +
    +
    + +
    +
    +
    + + + + + + diff --git a/zenxml/doc/classzen_1_1_xml_out-members.html b/zenxml/doc/classzen_1_1_xml_out-members.html new file mode 100644 index 00000000..368b094f --- /dev/null +++ b/zenxml/doc/classzen_1_1_xml_out-members.html @@ -0,0 +1,124 @@ + + + + + +zen::Xml: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + + + + + +
    + +
    + + +
    +
    +
    +
    zen::XmlOut Member List
    +
    +
    +This is the complete list of members for zen::XmlOut, including all inherited members. + + + + + + +
    attribute(const String &name, const T &value)zen::XmlOut
    operator()(const T &value)zen::XmlOut
    operator[](const String &name) const zen::XmlOut
    ref()zen::XmlOut
    XmlOut(XmlDoc &doc)zen::XmlOut
    XmlOut(XmlElement &element)zen::XmlOut
    + + + + + + diff --git a/zenxml/doc/classzen_1_1_xml_out.html b/zenxml/doc/classzen_1_1_xml_out.html new file mode 100644 index 00000000..e7fea1d5 --- /dev/null +++ b/zenxml/doc/classzen_1_1_xml_out.html @@ -0,0 +1,317 @@ + + + + + +zen::Xml: zen::XmlOut Class Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + + + + + +
    + +
    + + +
    +
    + +
    +
    zen::XmlOut Class Reference
    +
    +
    + +

    Proxy class to conveniently convert user data into XML structure. + More...

    + +

    #include <bind.h>

    + +

    List of all members.

    + + + + + + + + + + + + + + + + + +

    +Public Member Functions

     XmlOut (XmlDoc &doc)
     Construct an output proxy for an XML document.
     XmlOut (XmlElement &element)
     Construct an output proxy for a single XML element.
    template<class String >
    XmlOut operator[] (const String &name) const
     Retrieve a handle to an XML child element for writing.
    template<class T >
    void operator() (const T &value)
     Write user data to the underlying XML element.
    template<class String , class T >
    void attribute (const String &name, const T &value)
     Write user data to an XML attribute.
    +XmlElementref ()
     Return a reference to the underlying Xml element.
    +

    Detailed Description

    +

    Proxy class to conveniently convert user data into XML structure.

    +

    Constructor & Destructor Documentation

    + +
    +
    + + + + + + + + +
    zen::XmlOut::XmlOut (XmlDocdoc)
    +
    +
    + +

    Construct an output proxy for an XML document.

    +
            zen::XmlDoc doc;
    +
    +        zen::XmlOut out(doc);
    +        out["elem1"]( 1); //
    +        out["elem2"]( 2); //write data into XML elements
    +        out["elem3"](-3); //
    +
    +        save(doc, "out.xml"); //throw XmlFileError
    +

    Output:

    +
          <?xml version="1.0" encoding="UTF-8"?>
    +      <Root>
    +          <elem1>1</elem1>
    +          <elem2>2</elem2>
    +          <elem3>-3</elem3>
    +      </Root>
    +
    +
    + +
    +
    + + + + + + + + +
    zen::XmlOut::XmlOut (XmlElementelement)
    +
    +
    + +

    Construct an output proxy for a single XML element.

    +
    See also:
    XmlOut(XmlDoc& doc)
    + +
    +
    +

    Member Function Documentation

    + +
    +
    +
    +template<class String , class T >
    + + + + + + + + + + + + + + + + + + +
    void zen::XmlOut::attribute (const String & name,
    const T & value 
    )
    +
    +
    + +

    Write user data to an XML attribute.

    +

    This conversion requires a specialization of zen::writeText() for type T.

    +
            zen::XmlDoc doc;
    +
    +        zen::XmlOut out(doc);
    +        out["elem"].attribute("attr1",  1); //
    +        out["elem"].attribute("attr2",  2); //write data into XML attributes
    +        out["elem"].attribute("attr3", -3); //
    +
    +        save(doc, "out.xml"); //throw XmlFileError
    +

    Output:

    +
          <?xml version="1.0" encoding="UTF-8"?>
    +      <Root>
    +          <elem attr1="1" attr2="2" attr3="-3"/>
    +      </Root>
    Template Parameters:
    + + + +
    StringArbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
    TString-convertible user data type: e.g. any string-like type, all built-in arithmetic numbers
    +
    +
    +
    See also:
    XmlElement::setAttribute()
    + +
    +
    + +
    +
    +
    +template<class T >
    + + + + + + + + +
    void zen::XmlOut::operator() (const T & value)
    +
    +
    + +

    Write user data to the underlying XML element.

    +

    This conversion requires a specialization of zen::writeText() or zen::writeStruc() for type T.

    +
    Template Parameters:
    + + +
    TUser type that is converted into an XML element value.
    +
    +
    + +
    +
    + +
    +
    +
    +template<class String >
    + + + + + + + + +
    XmlOut zen::XmlOut::operator[] (const String & name) const
    +
    +
    + +

    Retrieve a handle to an XML child element for writing.

    +

    The child element will be created if it is not yet existing.

    +
    Template Parameters:
    + + +
    StringArbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
    +
    +
    +
    Parameters:
    + + +
    nameThe name of the child element
    +
    +
    + +
    +
    +
    + + + + + + diff --git a/zenxml/doc/closed.png b/zenxml/doc/closed.png new file mode 100644 index 00000000..b7d4bd9f Binary files /dev/null and b/zenxml/doc/closed.png differ diff --git a/zenxml/doc/cvrt__struc_8h_source.html b/zenxml/doc/cvrt__struc_8h_source.html new file mode 100644 index 00000000..6bd4e7af --- /dev/null +++ b/zenxml/doc/cvrt__struc_8h_source.html @@ -0,0 +1,305 @@ + + + + + +zen::Xml: cvrt_struc.h Source File + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + +
    + + + + +
    + +
    + +
    +
    +
    cvrt_struc.h
    +
    +
    +
    00001 // **************************************************************************
    +00002 // * This file is part of the zen::Xml project. It is distributed under the *
    +00003 // * Boost Software License: http://www.boost.org/LICENSE_1_0.txt           *
    +00004 // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved        *
    +00005 // **************************************************************************
    +00006 
    +00007 #ifndef ZEN_XML_CONVERT_STRUC_HEADER_018727409908342709743
    +00008 #define ZEN_XML_CONVERT_STRUC_HEADER_018727409908342709743
    +00009 
    +00010 #include "dom.h"
    +00011 
    +00012 namespace zen
    +00013 {
    +00020 
    +00021 
    +00026 template <class T> bool readStruc(const XmlElement& input, T& value);
    +00028 
    +00032 template <class T> void writeStruc(const T& value, XmlElement& output);
    +00033 
    +00034 
    +00035 
    +00036 
    +00037 
    +00038 
    +00039 
    +00040 
    +00041 
    +00042 
    +00043 
    +00044 
    +00045 
    +00046 
    +00047 
    +00048 
    +00049 
    +00050 //------------------------------ implementation -------------------------------------
    +00051 namespace impl_2384343
    +00052 {
    +00053 ZEN_INIT_DETECT_MEMBER_TYPE(value_type);
    +00054 ZEN_INIT_DETECT_MEMBER_TYPE(iterator);
    +00055 ZEN_INIT_DETECT_MEMBER_TYPE(const_iterator);
    +00056 
    +00057 ZEN_INIT_DETECT_MEMBER(begin)  //
    +00058 ZEN_INIT_DETECT_MEMBER(end)    //we don't know the exact declaration of the member attribute: may be in a base class!
    +00059 ZEN_INIT_DETECT_MEMBER(insert) //
    +00060 }
    +00061 
    +00062 template <typename T>
    +00063 struct IsStlContainer :
    +00064         StaticBool<
    +00065         impl_2384343::HasMemberType_value_type    <T>::value&&
    +00066         impl_2384343::HasMemberType_iterator      <T>::value&&
    +00067         impl_2384343::HasMemberType_const_iterator<T>::value&&
    +00068         impl_2384343::HasMember_begin             <T>::value&&
    +00069         impl_2384343::HasMember_end               <T>::value&&
    +00070         impl_2384343::HasMember_insert            <T>::value> {};
    +00071 
    +00072 
    +00073 namespace impl_2384343
    +00074 {
    +00075 ZEN_INIT_DETECT_MEMBER_TYPE(first_type);
    +00076 ZEN_INIT_DETECT_MEMBER_TYPE(second_type);
    +00077 
    +00078 ZEN_INIT_DETECT_MEMBER(first)   //we don't know the exact declaration of the member attribute: may be in a base class!
    +00079 ZEN_INIT_DETECT_MEMBER(second)  //
    +00080 }
    +00081 
    +00082 template <typename T>
    +00083 struct IsStlPair :
    +00084         StaticBool<
    +00085         impl_2384343::HasMemberType_first_type <T>::value&&
    +00086         impl_2384343::HasMemberType_second_type<T>::value&&
    +00087         impl_2384343::HasMember_first          <T>::value&&
    +00088         impl_2384343::HasMember_second         <T>::value> {};
    +00089 
    +00090 //######################################################################################
    +00091 
    +00092 //Conversion from arbitrary types to an XML element
    +00093 enum ValueType
    +00094 {
    +00095     VALUE_TYPE_STL_CONTAINER,
    +00096     VALUE_TYPE_STL_PAIR,
    +00097     VALUE_TYPE_OTHER,
    +00098 };
    +00099 
    +00100 template <class T>
    +00101 struct GetValueType : StaticEnum<ValueType,
    +00102         GetTextType<T>::value != TEXT_TYPE_OTHER ? VALUE_TYPE_OTHER : //some string classes are also STL containers, so check this first
    +00103         IsStlContainer<T>::value ? VALUE_TYPE_STL_CONTAINER :
    +00104         IsStlPair<T>::value      ? VALUE_TYPE_STL_PAIR :
    +00105         VALUE_TYPE_OTHER> {};
    +00106 
    +00107 
    +00108 template <class T, ValueType type>
    +00109 struct ConvertElement;
    +00110 /* -> expected interface
    +00111 {
    +00112     void writeStruc(const T& value, XmlElement& output) const;
    +00113     bool readStruc(const XmlElement& input, T& value) const;
    +00114 };
    +00115 */
    +00116 
    +00117 
    +00118 //partial specialization: handle conversion for all STL-container types!
    +00119 template <class T>
    +00120 struct ConvertElement<T, VALUE_TYPE_STL_CONTAINER>
    +00121 {
    +00122     void writeStruc(const T& value, XmlElement& output) const
    +00123     {
    +00124         std::for_each(value.begin(), value.end(),
    +00125                       [&](const typename T::value_type & childVal)
    +00126         {
    +00127             XmlElement& newChild = output.addChild("Item");
    +00128             zen::writeStruc(childVal, newChild);
    +00129         });
    +00130     }
    +00131     bool readStruc(const XmlElement& input, T& value) const
    +00132     {
    +00133         bool success = true;
    +00134         value.clear();
    +00135 
    +00136         auto iterPair = input.getChildren("Item");
    +00137         for (auto iter = iterPair.first; iter != iterPair.second; ++iter)
    +00138         {
    +00139             typename T::value_type childVal; //MSVC 2010 bug: cannot put this into a lambda body
    +00140             if (zen::readStruc(*iter, childVal))
    +00141                 value.insert(value.end(), childVal);
    +00142             else
    +00143                 success = false;
    +00144         }
    +00145         return success;
    +00146     }
    +00147 };
    +00148 
    +00149 
    +00150 //partial specialization: handle conversion for std::pair
    +00151 template <class T>
    +00152 struct ConvertElement<T, VALUE_TYPE_STL_PAIR>
    +00153 {
    +00154     void writeStruc(const T& value, XmlElement& output) const
    +00155     {
    +00156         XmlElement& child1 = output.addChild("one"); //don't use "1st/2nd", this will confuse a few pedantic XML parsers
    +00157         zen::writeStruc(value.first, child1);
    +00158 
    +00159         XmlElement& child2 = output.addChild("two");
    +00160         zen::writeStruc(value.second, child2);
    +00161     }
    +00162     bool readStruc(const XmlElement& input, T& value) const
    +00163     {
    +00164         bool success = true;
    +00165         const XmlElement* child1 = input.getChild("one");
    +00166         if (!child1 || !zen::readStruc(*child1, value.first))
    +00167             success = false;
    +00168 
    +00169         const XmlElement* child2 = input.getChild("two");
    +00170         if (!child2 || !zen::readStruc(*child2, value.second))
    +00171             success = false;
    +00172 
    +00173         return success;
    +00174     }
    +00175 };
    +00176 
    +00177 
    +00178 //partial specialization: not a pure structured type, try text conversion (thereby respect user specializations of writeText()/readText())
    +00179 template <class T>
    +00180 struct ConvertElement<T, VALUE_TYPE_OTHER>
    +00181 {
    +00182     void writeStruc(const T& value, XmlElement& output) const
    +00183     {
    +00184         std::string tmp;
    +00185         writeText(value, tmp);
    +00186         output.setValue(tmp);
    +00187     }
    +00188     bool readStruc(const XmlElement& input, T& value) const
    +00189     {
    +00190         std::string rawStr;
    +00191         input.getValue(rawStr);
    +00192         return readText(rawStr, value);
    +00193     }
    +00194 };
    +00195 
    +00196 
    +00197 template <class T> inline
    +00198 void writeStruc(const T& value, XmlElement& output)
    +00199 {
    +00200     ConvertElement<T, GetValueType<T>::value>().writeStruc(value, output);
    +00201 }
    +00202 
    +00203 
    +00204 template <class T> inline
    +00205 bool readStruc(const XmlElement& input, T& value)
    +00206 {
    +00207     return ConvertElement<T, GetValueType<T>::value>().readStruc(input, value);
    +00208 }
    +00209 }
    +00210 
    +00211 #endif //ZEN_XML_CONVERT_STRUC_HEADER_018727409908342709743
    +
    + + + + + + diff --git a/zenxml/doc/cvrt__text_8h_source.html b/zenxml/doc/cvrt__text_8h_source.html new file mode 100644 index 00000000..d10a3b8f --- /dev/null +++ b/zenxml/doc/cvrt__text_8h_source.html @@ -0,0 +1,276 @@ + + + + + +zen::Xml: cvrt_text.h Source File + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + +
    + + + + +
    + +
    + +
    +
    +
    cvrt_text.h
    +
    +
    +
    00001 // **************************************************************************
    +00002 // * This file is part of the zen::Xml project. It is distributed under the *
    +00003 // * Boost Software License: http://www.boost.org/LICENSE_1_0.txt           *
    +00004 // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved        *
    +00005 // **************************************************************************
    +00006 
    +00007 #ifndef ZEN_XML_CONVERT_TEXT_HEADER_018727339083427097434
    +00008 #define ZEN_XML_CONVERT_TEXT_HEADER_018727339083427097434
    +00009 
    +00010 #include <zen/utf.h>
    +00011 #include <zen/string_tools.h>
    +00012 
    +00013 namespace zen
    +00014 {
    +00061 
    +00062 
    +00067 template <class T> bool readText(const std::string& input, T& value);
    +00069 
    +00073 template <class T> void writeText(const T& value, std::string& output);
    +00074 
    +00075 
    +00076 /* Different classes of data types:
    +00077 
    +00078 -----------------------------
    +00079 | structured                |  readStruc/writeStruc - e.g. string-convertible types, STL containers, std::pair, structured user types
    +00080 | ------------------------- |
    +00081 | | to-string-convertible | |  readText/writeText   - e.g. string-like types, all built-in arithmetic numbers, bool
    +00082 | | ---------------       | |
    +00083 | | | string-like |       | |  utfCvrtTo            - e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
    +00084 | | ---------------       | |
    +00085 | ------------------------- |
    +00086 -----------------------------
    +00087 */
    +00088 
    +00089 
    +00090 
    +00091 
    +00092 
    +00093 
    +00094 
    +00095 
    +00096 
    +00097 
    +00098 
    +00099 
    +00100 
    +00101 
    +00102 
    +00103 //------------------------------ implementation -------------------------------------
    +00104 
    +00105 //Conversion from arbitrary types to text (for use with XML elements and attributes)
    +00106 enum TextType
    +00107 {
    +00108     TEXT_TYPE_BOOL,
    +00109     TEXT_TYPE_NUMBER,
    +00110     TEXT_TYPE_STRING,
    +00111     TEXT_TYPE_OTHER,
    +00112 };
    +00113 
    +00114 template <class T>
    +00115 struct GetTextType : StaticEnum<TextType,
    +00116         IsSameType<T, bool>::value ? TEXT_TYPE_BOOL   :
    +00117         IsStringLike<T>::value     ? TEXT_TYPE_STRING : //string before number to correctly handle char/wchar_t -> this was an issue with Loki only!
    +00118         IsArithmetic<T>::value     ? TEXT_TYPE_NUMBER : //
    +00119         TEXT_TYPE_OTHER> {};
    +00120 
    +00121 //######################################################################################
    +00122 
    +00123 template <class T, TextType type>
    +00124 struct ConvertText;
    +00125 /* -> expected interface
    +00126 {
    +00127     void writeText(const T& value, std::string& output) const;
    +00128     bool readText(const std::string& input, T& value) const;
    +00129 };
    +00130 */
    +00131 
    +00132 //partial specialization: type bool
    +00133 template <class T>
    +00134 struct ConvertText<T, TEXT_TYPE_BOOL>
    +00135 {
    +00136     void writeText(bool value, std::string& output) const
    +00137     {
    +00138         output = value ? "true" : "false";
    +00139     }
    +00140     bool readText(const std::string& input, bool& value) const
    +00141     {
    +00142         std::string tmp = input;
    +00143         zen::trim(tmp);
    +00144         if (tmp == "true")
    +00145             value = true;
    +00146         else if (tmp == "false")
    +00147             value = false;
    +00148         else
    +00149             return false;
    +00150         return true;
    +00151     }
    +00152 };
    +00153 
    +00154 //partial specialization: handle conversion for all built-in arithmetic types!
    +00155 template <class T>
    +00156 struct ConvertText<T, TEXT_TYPE_NUMBER>
    +00157 {
    +00158     void writeText(const T& value, std::string& output) const
    +00159     {
    +00160         output = numberTo<std::string>(value);
    +00161     }
    +00162     bool readText(const std::string& input, T& value) const
    +00163     {
    +00164         value = stringTo<T>(input);
    +00165         return true;
    +00166     }
    +00167 };
    +00168 
    +00169 //partial specialization: handle conversion for all string-like types!
    +00170 template <class T>
    +00171 struct ConvertText<T, TEXT_TYPE_STRING>
    +00172 {
    +00173     void writeText(const T& value, std::string& output) const
    +00174     {
    +00175         output = utfCvrtTo<std::string>(value);
    +00176     }
    +00177     bool readText(const std::string& input, T& value) const
    +00178     {
    +00179         value = utfCvrtTo<T>(input);
    +00180         return true;
    +00181     }
    +00182 };
    +00183 
    +00184 
    +00185 //partial specialization: unknown type
    +00186 template <class T>
    +00187 struct ConvertText<T, TEXT_TYPE_OTHER>
    +00188 {
    +00189     //###########################################################################################################################################
    +00190     assert_static(sizeof(T) == -1);
    +00191     /*
    +00192         ATTENTION: The data type T is yet unknown to the zen::Xml framework!
    +00193 
    +00194         Please provide a specialization for T of the following two functions in order to handle conversions to XML elements and attributes
    +00195 
    +00196         template <> void zen::writeText(const T& value, std::string& output)
    +00197         template <> bool zen::readText(const std::string& input, T& value)
    +00198 
    +00199         If T is structured and cannot be converted to a text representation specialize these two functions to allow at least for conversions to XML elements:
    +00200 
    +00201         template <> void zen::writeStruc(const T& value, XmlElement& output)
    +00202         template <> bool zen::readStruc(const XmlElement& input, T& value)
    +00203     */
    +00204     //###########################################################################################################################################
    +00205 };
    +00206 
    +00207 
    +00208 template <class T> inline
    +00209 void writeText(const T& value, std::string& output)
    +00210 {
    +00211     ConvertText<T, GetTextType<T>::value>().writeText(value, output);
    +00212 }
    +00213 
    +00214 
    +00215 template <class T> inline
    +00216 bool readText(const std::string& input, T& value)
    +00217 {
    +00218     return ConvertText<T, GetTextType<T>::value>().readText(input, value);
    +00219 }
    +00220 }
    +00221 
    +00222 #endif //ZEN_XML_CONVERT_TEXT_HEADER_018727339083427097434
    +
    + + + + + + diff --git a/zenxml/doc/dom_8h_source.html b/zenxml/doc/dom_8h_source.html new file mode 100644 index 00000000..e81e00a9 --- /dev/null +++ b/zenxml/doc/dom_8h_source.html @@ -0,0 +1,346 @@ + + + + + +zen::Xml: dom.h Source File + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + +
    + + + + +
    + +
    + +
    +
    +
    dom.h
    +
    +
    +
    00001 // **************************************************************************
    +00002 // * This file is part of the zen::Xml project. It is distributed under the *
    +00003 // * Boost Software License: http://www.boost.org/LICENSE_1_0.txt           *
    +00004 // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved        *
    +00005 // **************************************************************************
    +00006 
    +00007 #ifndef ZEN_XML_DOM_HEADER_82085720723894567204564256
    +00008 #define ZEN_XML_DOM_HEADER_82085720723894567204564256
    +00009 
    +00010 #include <string>
    +00011 #include <vector>
    +00012 #include <memory>
    +00013 #include <map>
    +00014 #include "cvrt_text.h" //"readText/writeText"
    +00015 
    +00016 namespace zen
    +00017 {
    +00018 class XmlDoc;
    +00019 
    +00021 class XmlElement
    +00022 {
    +00023     struct PrivateConstruction {};
    +00024 public:
    +00025     //Construct an empty XML element
    +00026     //This constructor should be private, however std::make_shared() requires public access
    +00027     //Therefore at least prevent users from calling it via private dummy type PrivateConstruction
    +00028     template <class String>
    +00029     XmlElement(const String& name, XmlElement* parentElement, PrivateConstruction) : name_(utfCvrtTo<std::string>(name)), parent_(parentElement) {}
    +00030 
    +00032 
    +00036     template <class String>
    +00037     String getNameAs() const { return utfCvrtTo<String>(name_); }
    +00038 
    +00040 
    +00044     template <class T>
    +00045     bool getValue(T& value) const { return readStruc(*this, value); }
    +00046 
    +00048 
    +00051     template <class T>
    +00052     void setValue(const T& value) { writeStruc(value, *this); }
    +00053 
    +00055 
    +00062     template <class String, class T>
    +00063     bool getAttribute(const String& name, T& value) const
    +00064     {
    +00065         auto it = attributes.find(utfCvrtTo<std::string>(name));
    +00066         return it == attributes.end() ? false : readText(it->second, value);
    +00067     }
    +00068 
    +00070 
    +00076     template <class String, class T>
    +00077     void setAttribute(const String& name, const T& value)
    +00078     {
    +00079         std::string attrValue;
    +00080         writeText(value, attrValue);
    +00081         attributes[utfCvrtTo<std::string>(name)] = attrValue;
    +00082     }
    +00083 
    +00085 
    +00088     template <class String>
    +00089     void removeAttribute(const String& name) { attributes.erase(utfCvrtTo<std::string>(name)); }
    +00090 
    +00092 
    +00096     template <class String>
    +00097     XmlElement& addChild(const String& name)
    +00098     {
    +00099         std::string utf8Name = utfCvrtTo<std::string>(name);
    +00100         auto newElement = std::make_shared<XmlElement>(utf8Name, this, PrivateConstruction());
    +00101         childElements.push_back(newElement);
    +00102         childElementsSorted.insert(std::make_pair(utf8Name, newElement));
    +00103         return *newElement;
    +00104     }
    +00105 
    +00107 
    +00112     template <class String>
    +00113     const XmlElement* getChild(const String& name) const
    +00114     {
    +00115         auto it = childElementsSorted.find(utfCvrtTo<std::string>(name));
    +00116         return it == childElementsSorted.end() ? nullptr : &*(it->second);
    +00117     }
    +00118 
    +00120     template <class String>
    +00121     XmlElement* getChild(const String& name)
    +00122     {
    +00123         return const_cast<XmlElement*>(static_cast<const XmlElement*>(this)->getChild(name));
    +00124     }
    +00125 
    +00126     template < class IterTy,     //underlying iterator type
    +00127              class T,            //target object type
    +00128              class AccessPolicy > //access policy: see AccessPtrMap
    +00129     class PtrIter : public std::iterator<std::input_iterator_tag, T>, private AccessPolicy //get rid of shared_ptr indirection
    +00130     {
    +00131     public:
    +00132         PtrIter(IterTy it) : it_(it) {}
    +00133         PtrIter(const PtrIter& other) : it_(other.it_) {}
    +00134         PtrIter& operator++() { ++it_; return *this; }
    +00135         PtrIter operator++(int) { PtrIter tmp(*this); operator++(); return tmp; }
    +00136         inline friend bool operator==(const PtrIter& lhs, const PtrIter& rhs) { return lhs.it_ == rhs.it_; }
    +00137         inline friend bool operator!=(const PtrIter& lhs, const PtrIter& rhs) { return !(lhs == rhs); }
    +00138         T& operator* () { return  AccessPolicy::template objectRef<T>(it_); }
    +00139         T* operator->() { return &AccessPolicy::template objectRef<T>(it_); }
    +00140     private:
    +00141         IterTy it_;
    +00142     };
    +00143 
    +00144     struct AccessPtrMap
    +00145     {
    +00146         template <class T, class IterTy>
    +00147         T& objectRef(const IterTy& it) { return *(it->second); }
    +00148     };
    +00149 
    +00150     typedef PtrIter<std::multimap<std::string, std::shared_ptr<XmlElement>>::iterator, XmlElement, AccessPtrMap> ChildIter2;
    +00151     typedef PtrIter<std::multimap<std::string, std::shared_ptr<XmlElement>>::const_iterator, const XmlElement, AccessPtrMap> ChildIterConst2;
    +00152 
    +00154 
    +00163     template <class String>
    +00164     std::pair<ChildIterConst2, ChildIterConst2> getChildren(const String& name) const { return childElementsSorted.equal_range(utfCvrtTo<std::string>(name)); }
    +00165 
    +00167     template <class String>
    +00168     std::pair<ChildIter2, ChildIter2> getChildren(const String& name) { return childElementsSorted.equal_range(utfCvrtTo<std::string>(name)); }
    +00169 
    +00170     struct AccessPtrVec
    +00171     {
    +00172         template <class T, class IterTy>
    +00173         T& objectRef(const IterTy& it) { return **it; }
    +00174     };
    +00175 
    +00176     typedef PtrIter<std::vector<std::shared_ptr<XmlElement>>::iterator, XmlElement, AccessPtrVec> ChildIter;
    +00177     typedef PtrIter<std::vector<std::shared_ptr<XmlElement>>::const_iterator, const XmlElement, AccessPtrVec> ChildIterConst;
    +00178 
    +00180 
    +00188     std::pair<ChildIterConst, ChildIterConst> getChildren() const { return std::make_pair(childElements.begin(), childElements.end()); }
    +00189 
    +00191     std::pair<ChildIter, ChildIter> getChildren() { return std::make_pair(childElements.begin(), childElements.end()); }
    +00192 
    +00194     XmlElement* parent() { return parent_; };
    +00196     const XmlElement* parent() const { return parent_; };
    +00197 
    +00198 
    +00199     typedef std::map<std::string, std::string>::const_iterator AttrIter;
    +00200 
    +00201     /* -> disabled documentation extraction
    +00202       \brief Get all attributes associated with the element.
    +00203       \code
    +00204         auto iterPair = elem.getAttributes();
    +00205         for (auto it = iterPair.first; it != iterPair.second; ++it)
    +00206            std::cout << "name: " << it->first << " value: " << it->second << "\n";
    +00207       \endcode
    +00208       \return A pair of STL begin/end iterators to access all attributes sequentially as a list of name/value pairs of std::string.
    +00209     */
    +00210     std::pair<AttrIter, AttrIter> getAttributes() const { return std::make_pair(attributes.begin(), attributes.end()); }
    +00211 
    +00212     //Transactionally swap two elements.  -> disabled documentation extraction
    +00213     void swap(XmlElement& other)
    +00214     {
    +00215         name_     .swap(other.name_);
    +00216         value_    .swap(other.value_);
    +00217         attributes.swap(other.attributes);
    +00218         childElements.swap(other.childElements);
    +00219         childElementsSorted.swap(other.childElementsSorted);
    +00220         //std::swap(parent_, other.parent_); -> parent is physical location; update children's parent reference instead:
    +00221         std::for_each(      childElements.begin(),       childElements.end(), [&](const std::shared_ptr<XmlElement>& child) { child->parent_ = this;   });
    +00222         std::for_each(other.childElements.begin(), other.childElements.end(), [&](const std::shared_ptr<XmlElement>& child) { child->parent_ = &other; });
    +00223     }
    +00224 
    +00225 private:
    +00226     friend class XmlDoc;
    +00227 
    +00228     XmlElement(const XmlElement&);            //not implemented
    +00229     XmlElement& operator=(const XmlElement&); //
    +00230 
    +00231     std::string name_;
    +00232     std::string value_;
    +00233     std::map<std::string, std::string> attributes;
    +00234     std::vector<std::shared_ptr<XmlElement>>                childElements;       //all child elements in order of creation
    +00235     std::multimap<std::string, std::shared_ptr<XmlElement>> childElementsSorted; //alternate key: sorted by element name
    +00236     XmlElement* parent_;
    +00237 };
    +00238 
    +00239 
    +00240 //XmlElement::setValue<T>() calls zen::writeStruc() which calls XmlElement::setValue() ... => these two specializations end the circle
    +00241 template <> inline
    +00242 void XmlElement::setValue(const std::string& value) { value_ = value; }
    +00243 
    +00244 template <> inline
    +00245 bool XmlElement::getValue(std::string& value) const { value = value_; return true; }
    +00246 
    +00247 
    +00249 class XmlDoc
    +00250 {
    +00251 public:
    +00253     XmlDoc() : version_("1.0"), encoding_("UTF-8"), rootElement("Root", nullptr, XmlElement::PrivateConstruction()) {}
    +00254 
    +00255     //Setup an empty XML document
    +00260     template <class String>
    +00261     XmlDoc(String rootName) : version_("1.0"), encoding_("UTF-8"), rootElement(rootName, nullptr, XmlElement::PrivateConstruction()) {}
    +00262 
    +00264     const XmlElement& root() const { return rootElement; }
    +00266     XmlElement& root() { return rootElement; }
    +00267 
    +00269 
    +00272     template <class String>
    +00273     String getVersionAs() const { return utfCvrtTo<String>(version_); }
    +00274 
    +00276 
    +00279     template <class String>
    +00280     void setVersion(const String& version) { version_ = utfCvrtTo<std::string>(version); }
    +00281 
    +00283 
    +00286     template <class String>
    +00287     String getEncodingAs() const { return utfCvrtTo<String>(encoding_); }
    +00288 
    +00290 
    +00293     template <class String>
    +00294     void setEncoding(const String& encoding) { encoding_ = utfCvrtTo<std::string>(encoding); }
    +00295 
    +00297 
    +00300     template <class String>
    +00301     String getStandaloneAs() const { return utfCvrtTo<String>(standalone_); }
    +00302 
    +00304 
    +00307     template <class String>
    +00308     void setStandalone(const String& standalone) { standalone_ = utfCvrtTo<std::string>(standalone); }
    +00309 
    +00310 private:
    +00311     XmlDoc(const XmlDoc&);        //not implemented, thanks to XmlElement::parent_
    +00312     XmlDoc& operator=(const XmlDoc&); //
    +00313 
    +00314     std::string version_;
    +00315     std::string encoding_;
    +00316     std::string standalone_;
    +00317 
    +00318     XmlElement rootElement;
    +00319 };
    +00320 
    +00321 }
    +00322 
    +00323 #endif //ZEN_XML_DOM_HEADER_82085720723894567204564256
    +
    + + + + + + diff --git a/zenxml/doc/doxygen.css b/zenxml/doc/doxygen.css new file mode 100644 index 00000000..c151fde3 --- /dev/null +++ b/zenxml/doc/doxygen.css @@ -0,0 +1,1012 @@ +/* The standard CSS for doxygen */ + +body, table, div, p, dl { + font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif; + font-size: 13px; + line-height: 1.3; +} + +/* @group Heading Levels */ + +h1 { + font-size: 150%; +} + +.title { + font-size: 150%; + font-weight: bold; + margin: 10px 2px; +} + +h2 { + font-size: 120%; +} + +h3 { + font-size: 100%; +} + +dt { + font-weight: bold; +} + +div.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; +} + +p.startli, p.startdd, p.starttd { + margin-top: 2px; +} + +p.endli { + margin-bottom: 0px; +} + +p.enddd { + margin-bottom: 4px; +} + +p.endtd { + margin-bottom: 2px; +} + +/* @end */ + +caption { + font-weight: bold; +} + +span.legend { + font-size: 70%; + text-align: center; +} + +h3.version { + font-size: 90%; + text-align: center; +} + +div.qindex, div.navtab{ + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; +} + +div.qindex, div.navpath { + width: 100%; + line-height: 140%; +} + +div.navtab { + margin-right: 15px; +} + +/* @group Link Styling */ + +a { + color: #3D578C; + font-weight: normal; + text-decoration: none; +} + +.contents a:visited { + color: #4665A2; +} + +a:hover { + text-decoration: underline; +} + +a.qindex { + font-weight: bold; +} + +a.qindexHL { + font-weight: bold; + background-color: #9CAFD4; + color: #ffffff; + border: 1px double #869DCA; +} + +.contents a.qindexHL:visited { + color: #ffffff; +} + +a.el { + font-weight: bold; +} + +a.elRef { +} + +a.code, a.code:visited { + color: #4665A2; +} + +a.codeRef, a.codeRef:visited { + color: #4665A2; +} + +/* @end */ + +dl.el { + margin-left: -1cm; +} + +.fragment { + font-family: monospace, fixed; + font-size: 105%; +} + +pre.fragment { + border: 1px solid #C4CFE5; + background-color: #FBFCFD; + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + font-size: 9pt; + line-height: 125%; +} + +div.ah { + background-color: black; + font-weight: bold; + color: #ffffff; + margin-bottom: 3px; + margin-top: 3px; + padding: 0.2em; + border: solid thin #333; + border-radius: 0.5em; + -webkit-border-radius: .5em; + -moz-border-radius: .5em; + box-shadow: 2px 2px 3px #999; + -webkit-box-shadow: 2px 2px 3px #999; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); + background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); +} + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; +} + +div.groupText { + margin-left: 16px; + font-style: italic; +} + +body { + background-color: white; + color: black; + margin: 0; +} + +div.contents { + margin-top: 10px; + margin-left: 8px; + margin-right: 8px; +} + +td.indexkey { + background-color: #EBEFF6; + font-weight: bold; + border: 1px solid #C4CFE5; + margin: 2px 0px 2px 0; + padding: 2px 10px; + white-space: nowrap; + vertical-align: top; +} + +td.indexvalue { + background-color: #EBEFF6; + border: 1px solid #C4CFE5; + padding: 2px 10px; + margin: 2px 0px; +} + +tr.memlist { + background-color: #EEF1F7; +} + +p.formulaDsp { + text-align: center; +} + +img.formulaDsp { + +} + +img.formulaInl { + vertical-align: middle; +} + +div.center { + text-align: center; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; +} + +div.center img { + border: 0px; +} + +address.footer { + text-align: right; + padding-right: 12px; +} + +img.footer { + border: 0px; + vertical-align: middle; +} + +/* @group Code Colorization */ + +span.keyword { + color: #008000 +} + +span.keywordtype { + color: #604020 +} + +span.keywordflow { + color: #e08000 +} + +span.comment { + color: #800000 +} + +span.preprocessor { + color: #806020 +} + +span.stringliteral { + color: #002080 +} + +span.charliteral { + color: #008080 +} + +span.vhdldigit { + color: #ff00ff +} + +span.vhdlchar { + color: #000000 +} + +span.vhdlkeyword { + color: #700070 +} + +span.vhdllogic { + color: #ff0000 +} + +blockquote { + background-color: #F7F8FB; + border-left: 2px solid #9CAFD4; + margin: 0 24px 0 4px; + padding: 0 12px 0 16px; +} + +/* @end */ + +/* +.search { + color: #003399; + font-weight: bold; +} + +form.search { + margin-bottom: 0px; + margin-top: 0px; +} + +input.search { + font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #e8eef2; +} +*/ + +td.tiny { + font-size: 75%; +} + +.dirtab { + padding: 4px; + border-collapse: collapse; + border: 1px solid #A3B4D7; +} + +th.dirtab { + background: #EBEFF6; + font-weight: bold; +} + +hr { + height: 0px; + border: none; + border-top: 1px solid #4A6AAA; +} + +hr.footer { + height: 1px; +} + +/* @group Member Descriptions */ + +table.memberdecls { + border-spacing: 0px; + padding: 0px; +} + +.mdescLeft, .mdescRight, +.memItemLeft, .memItemRight, +.memTemplItemLeft, .memTemplItemRight, .memTemplParams { + background-color: #F9FAFC; + border: none; + margin: 4px; + padding: 1px 0 0 8px; +} + +.mdescLeft, .mdescRight { + padding: 0px 8px 4px 8px; + color: #555; +} + +.memItemLeft, .memItemRight, .memTemplParams { + border-top: 1px solid #C4CFE5; +} + +.memItemLeft, .memTemplItemLeft { + white-space: nowrap; +} + +.memItemRight { + width: 100%; +} + +.memTemplParams { + color: #4665A2; + white-space: nowrap; +} + +/* @end */ + +/* @group Member Details */ + +/* Styles for detailed member documentation */ + +.memtemplate { + font-size: 80%; + color: #4665A2; + font-weight: normal; + margin-left: 9px; +} + +.memnav { + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} + +.mempage { + width: 100%; +} + +.memitem { + padding: 0; + margin-bottom: 10px; + margin-right: 5px; +} + +.memname { + white-space: nowrap; + font-weight: bold; + margin-left: 6px; +} + +.memproto, dl.reflist dt { + border-top: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 6px 0px 6px 0px; + color: #253555; + font-weight: bold; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + /* opera specific markup */ + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + border-top-right-radius: 8px; + border-top-left-radius: 8px; + /* firefox specific markup */ + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + -moz-border-radius-topright: 8px; + -moz-border-radius-topleft: 8px; + /* webkit specific markup */ + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + -webkit-border-top-right-radius: 8px; + -webkit-border-top-left-radius: 8px; + background-image:url('nav_f.png'); + background-repeat:repeat-x; + background-color: #E2E8F2; + +} + +.memdoc, dl.reflist dd { + border-bottom: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 2px 5px; + background-color: #FBFCFD; + border-top-width: 0; + /* opera specific markup */ + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + /* firefox specific markup */ + -moz-border-radius-bottomleft: 8px; + -moz-border-radius-bottomright: 8px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + background-image: -moz-linear-gradient(center top, #FFFFFF 0%, #FFFFFF 60%, #F7F8FB 95%, #EEF1F7); + /* webkit specific markup */ + -webkit-border-bottom-left-radius: 8px; + -webkit-border-bottom-right-radius: 8px; + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + background-image: -webkit-gradient(linear,center top,center bottom,from(#FFFFFF), color-stop(0.6,#FFFFFF), color-stop(0.60,#FFFFFF), color-stop(0.95,#F7F8FB), to(#EEF1F7)); +} + +dl.reflist dt { + padding: 5px; +} + +dl.reflist dd { + margin: 0px 0px 10px 0px; + padding: 5px; +} + +.paramkey { + text-align: right; +} + +.paramtype { + white-space: nowrap; +} + +.paramname { + color: #602020; + white-space: nowrap; +} +.paramname em { + font-style: normal; +} + +.params, .retval, .exception, .tparams { + border-spacing: 6px 2px; +} + +.params .paramname, .retval .paramname { + font-weight: bold; + vertical-align: top; +} + +.params .paramtype { + font-style: italic; + vertical-align: top; +} + +.params .paramdir { + font-family: "courier new",courier,monospace; + vertical-align: top; +} + + + + +/* @end */ + +/* @group Directory (tree) */ + +/* for the tree view */ + +.ftvtree { + font-family: sans-serif; + margin: 0px; +} + +/* these are for tree view when used as main index */ + +.directory { + font-size: 9pt; + font-weight: bold; + margin: 5px; +} + +.directory h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +/* +The following two styles can be used to replace the root node title +with an image of your choice. Simply uncomment the next two styles, +specify the name of your image and be sure to set 'height' to the +proper pixel height of your image. +*/ + +/* +.directory h3.swap { + height: 61px; + background-repeat: no-repeat; + background-image: url("yourimage.gif"); +} +.directory h3.swap span { + display: none; +} +*/ + +.directory > h3 { + margin-top: 0; +} + +.directory p { + margin: 0px; + white-space: nowrap; +} + +.directory div { + display: none; + margin: 0px; +} + +.directory img { + vertical-align: -30%; +} + +/* these are for tree view when not used as main index */ + +.directory-alt { + font-size: 100%; + font-weight: bold; +} + +.directory-alt h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +.directory-alt > h3 { + margin-top: 0; +} + +.directory-alt p { + margin: 0px; + white-space: nowrap; +} + +.directory-alt div { + display: none; + margin: 0px; +} + +.directory-alt img { + vertical-align: -30%; +} + +/* @end */ + +div.dynheader { + margin-top: 8px; +} + +address { + font-style: normal; + color: #2A3D61; +} + +table.doxtable { + border-collapse:collapse; + margin-top: 4px; + margin-bottom: 4px; +} + +table.doxtable td, table.doxtable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; +} + +table.doxtable th { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; +} + +table.fieldtable { + width: 100%; + margin-bottom: 10px; + border: 1px solid #A8B8D9; + border-spacing: 0px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); +} + +.fieldtable td, .fieldtable th { + padding: 3px 7px 2px; +} + +.fieldtable td.fieldtype, .fieldtable td.fieldname { + white-space: nowrap; + border-right: 1px solid #A8B8D9; + border-bottom: 1px solid #A8B8D9; + vertical-align: top; +} + +.fieldtable td.fielddoc { + border-bottom: 1px solid #A8B8D9; + width: 100%; +} + +.fieldtable tr:last-child td { + border-bottom: none; +} + +.fieldtable th { + background-image:url('nav_f.png'); + background-repeat:repeat-x; + background-color: #E2E8F2; + font-size: 90%; + color: #253555; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; + -moz-border-radius-topleft: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: 1px solid #A8B8D9; +} + + +.tabsearch { + top: 0px; + left: 10px; + height: 36px; + background-image: url('tab_b.png'); + z-index: 101; + overflow: hidden; + font-size: 13px; +} + +.navpath ul +{ + font-size: 11px; + background-image:url('tab_b.png'); + background-repeat:repeat-x; + height:30px; + line-height:30px; + color:#8AA0CC; + border:solid 1px #C2CDE4; + overflow:hidden; + margin:0px; + padding:0px; +} + +.navpath li +{ + list-style-type:none; + float:left; + padding-left:10px; + padding-right:15px; + background-image:url('bc_s.png'); + background-repeat:no-repeat; + background-position:right; + color:#364D7C; +} + +.navpath li.navelem a +{ + height:32px; + display:block; + text-decoration: none; + outline: none; +} + +.navpath li.navelem a:hover +{ + color:#6884BD; +} + +.navpath li.footer +{ + list-style-type:none; + float:right; + padding-left:10px; + padding-right:15px; + background-image:none; + background-repeat:no-repeat; + background-position:right; + color:#364D7C; + font-size: 8pt; +} + + +div.summary +{ + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; +} + +div.summary a +{ + white-space: nowrap; +} + +div.ingroups +{ + margin-left: 5px; + font-size: 8pt; + padding-left: 5px; + width: 50%; + text-align: left; +} + +div.ingroups a +{ + white-space: nowrap; +} + +div.header +{ + background-image:url('nav_h.png'); + background-repeat:repeat-x; + background-color: #F9FAFC; + margin: 0px; + border-bottom: 1px solid #C4CFE5; +} + +div.headertitle +{ + padding: 5px 5px 5px 7px; +} + +dl +{ + padding: 0 0 0 10px; +} + +/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug */ +dl.section +{ + border-left:4px solid; + padding: 0 0 0 6px; +} + +dl.note +{ + border-color: #D0C000; +} + +dl.warning, dl.attention +{ + border-color: #FF0000; +} + +dl.pre, dl.post, dl.invariant +{ + border-color: #00D000; +} + +dl.deprecated +{ + border-color: #505050; +} + +dl.todo +{ + border-color: #00C0E0; +} + +dl.test +{ + border-color: #3030E0; +} + +dl.bug +{ + border-color: #C08050; +} + +dl.section dd { + margin-bottom: 6px; +} + + +#projectlogo +{ + text-align: center; + vertical-align: bottom; + border-collapse: separate; +} + +#projectlogo img +{ + border: 0px none; +} + +#projectname +{ + font: 300% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 2px 0px; +} + +#projectbrief +{ + font: 120% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#projectnumber +{ + font: 50% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#titlearea +{ + padding: 0px; + margin: 0px; + width: 100%; + border-bottom: 1px solid #5373B4; +} + +.image +{ + text-align: center; +} + +.dotgraph +{ + text-align: center; +} + +.mscgraph +{ + text-align: center; +} + +.caption +{ + font-weight: bold; +} + +div.zoom +{ + border: 1px solid #90A5CE; +} + +dl.citelist { + margin-bottom:50px; +} + +dl.citelist dt { + color:#334975; + float:left; + font-weight:bold; + margin-right:10px; + padding:5px; +} + +dl.citelist dd { + margin:2px 0; + padding:5px 0; +} + +div.toc { + padding: 14px 25px; + background-color: #F4F6FA; + border: 1px solid #D8DFEE; + border-radius: 7px 7px 7px 7px; + float: right; + height: auto; + margin: 0 20px 10px 10px; + width: 200px; +} + +div.toc li { + background: url("bdwn.png") no-repeat scroll 0 5px transparent; + font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif; + margin-top: 5px; + padding-left: 10px; + padding-top: 2px; +} + +div.toc h3 { + font: bold 12px/1.2 Arial,FreeSans,sans-serif; + color: #4665A2; + border-bottom: 0 none; + margin: 0; +} + +div.toc ul { + list-style: none outside none; + border: medium none; + padding: 0px; +} + +div.toc li.level1 { + margin-left: 0px; +} + +div.toc li.level2 { + margin-left: 15px; +} + +div.toc li.level3 { + margin-left: 30px; +} + +div.toc li.level4 { + margin-left: 45px; +} + + +@media print +{ + #top { display: none; } + #side-nav { display: none; } + #nav-path { display: none; } + body { overflow:visible; } + h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } + .summary { display: none; } + .memitem { page-break-inside: avoid; } + #doc-content + { + margin-left:0 !important; + height:auto !important; + width:auto !important; + overflow:inherit; + display:inline; + } + pre.fragment + { + overflow: visible; + text-wrap: unrestricted; + white-space: -moz-pre-wrap; /* Moz */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + white-space: pre-wrap; /* CSS3 */ + word-wrap: break-word; /* IE 5.5+ */ + } +} + diff --git a/zenxml/doc/doxygen.png b/zenxml/doc/doxygen.png new file mode 100644 index 00000000..635ed52f Binary files /dev/null and b/zenxml/doc/doxygen.png differ diff --git a/zenxml/doc/error_8h_source.html b/zenxml/doc/error_8h_source.html new file mode 100644 index 00000000..8986a17b --- /dev/null +++ b/zenxml/doc/error_8h_source.html @@ -0,0 +1,126 @@ + + + + + +zen::Xml: error.h Source File + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + +
    + + + + +
    + +
    + +
    +
    +
    error.h
    +
    +
    +
    00001 // **************************************************************************
    +00002 // * This file is part of the zen::Xml project. It is distributed under the *
    +00003 // * Boost Software License: http://www.boost.org/LICENSE_1_0.txt           *
    +00004 // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved        *
    +00005 // **************************************************************************
    +00006 
    +00007 #ifndef ZEN_XML_ERROR_HEADER_018734618433021489473214873214
    +00008 #define ZEN_XML_ERROR_HEADER_018734618433021489473214873214
    +00009 
    +00010 namespace zen
    +00011 {
    +00013 struct XmlError
    +00014 {
    +00015     virtual ~XmlError() {}
    +00016 };
    +00017 }
    +00018 
    +00019 #endif //ZEN_XML_ERROR_HEADER_018734618433021489473214873214
    +
    + + + + + + diff --git a/zenxml/doc/files.html b/zenxml/doc/files.html new file mode 100644 index 00000000..31fcf4fd --- /dev/null +++ b/zenxml/doc/files.html @@ -0,0 +1,118 @@ + + + + + +zen::Xml: File List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + +
    + + + + +
    + +
    + +
    +
    +
    File List
    +
    +
    +
    Here is a list of all documented files with brief descriptions:
    + + + + + + + + +
    bind.h [code]Map user data types to XML
    cvrt_struc.h [code]Handle conversion of arbitrary types to and from XML elements. See comments in cvrt_text.h
    cvrt_text.h [code]Handle conversion of string-convertible types to and from std::string
    dom.h [code]
    error.h [code]
    io.h [code]Save and load byte streams from files
    parser.h [code]Convert an XML document object model (class XmlDoc) to and from a byte stream representation
    xml.h [code]
    +
    + + + + + + diff --git a/zenxml/doc/functions.html b/zenxml/doc/functions.html new file mode 100644 index 00000000..956af32d --- /dev/null +++ b/zenxml/doc/functions.html @@ -0,0 +1,272 @@ + + + + + +zen::Xml: Class Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + + + +
    + + + + +
    + +
    + +
    +
    Here is a list of all documented class members with links to the class documentation for each member:
    + +

    - a -

    + + +

    - c -

    + + +

    - e -

    + + +

    - g -

    + + +

    - l -

    + + +

    - n -

    + + +

    - o -

    + + +

    - p -

    + + +

    - r -

    + + +

    - s -

    + + +

    - x -

    +
    + + + + + + diff --git a/zenxml/doc/functions_func.html b/zenxml/doc/functions_func.html new file mode 100644 index 00000000..be8e5716 --- /dev/null +++ b/zenxml/doc/functions_func.html @@ -0,0 +1,253 @@ + + + + + +zen::Xml: Class Members - Functions + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + + + +
    + + + + +
    + +
    + +
    +  + +

    - a -

    + + +

    - e -

    + + +

    - g -

    + + +

    - n -

    + + +

    - o -

    + + +

    - p -

    + + +

    - r -

    + + +

    - s -

    + + +

    - x -

    +
    + + + + + + diff --git a/zenxml/doc/functions_vars.html b/zenxml/doc/functions_vars.html new file mode 100644 index 00000000..ba8364ef --- /dev/null +++ b/zenxml/doc/functions_vars.html @@ -0,0 +1,125 @@ + + + + + +zen::Xml: Class Members - Variables + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + + +
    + + + + +
    + +
    + +
    +
    + + + + + + diff --git a/zenxml/doc/hierarchy.html b/zenxml/doc/hierarchy.html new file mode 100644 index 00000000..363c134b --- /dev/null +++ b/zenxml/doc/hierarchy.html @@ -0,0 +1,122 @@ + + + + + +zen::Xml: Class Hierarchy + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + +
    + + + + +
    + +
    + +
    +
    +
    Class Hierarchy
    +
    +
    +
    This inheritance list is sorted roughly, but not completely, alphabetically:
    +
    + + + + + + diff --git a/zenxml/doc/index.html b/zenxml/doc/index.html new file mode 100644 index 00000000..d7245a6b --- /dev/null +++ b/zenxml/doc/index.html @@ -0,0 +1,679 @@ + + + + + +zen::Xml: Overview + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + +
    + + + + +
    + +
    + +
    +
    +
    Overview
    +
    +
    +
    +

    +Rationale

    +

    zen::Xml is an XML library serializing structured user data in a convenient way. Using compile-time information gathered by techniques of template metaprogramming it minimizes the manual overhead required and frees the user from implementing fundamental type conversions by himself. Basic data types such as

    +
      +
    • all built-in arithmetic numbers,
    • +
    • all kinds of string classes and "string-like" types,
    • +
    • all types defined as STL containers
    • +
    +

    are handled automatically. Thereby a large number of recurring problems is solved by the library:

    +
      +
    • generic number to string conversions
    • +
    • generic char to wchar_t conversions (UTF) for custom string classes in a platform independent manner
    • +
    • serialization of arbitrary STL container types
    • +
    • simple integration: header-only, no extra dependencies, fully portable
    • +
    • support arbitrary string classes everywhere: for file names, XML element names, attribute names, values, ...
    • +
    • XML library built on C++11 with focus on elegance, minimal code size, flexibility and performance
    • +
    • easily extensible API: allow for internationalization, fine-granular error handling, and custom file I/O
    • +
    +

    The design follows the philosophy of the Loki library:
    + http://loki-lib.sourceforge.net/index.php?n=Main.Philosophy

    +

    +Quick Start

    +

    1. Download zen::Xml: http://sourceforge.net/projects/zenxml

    +

    2. Setup a preprocessor macro for your project to identify the platform (this is only required if you use C-stream-based file IO)

    +
        ZEN_PLATFORM_WINDOWS
    +    or
    +    ZEN_PLATFORM_OTHER
    +

    3. For optimal performance define this global macro in release build: (following convention of the assert macro)

    +
        NDEBUG
    +

    4. Include the main header:

    +
    #include <zenxml/xml.h>
    +

    5. Start serializing user data:

    +
    size_t a = 10;
    +double b = 2.0;
    +int    c = -1;
    +
    zen::XmlDoc doc; //empty XML document
    +
    +zen::XmlOut out(doc); //the simplest way to fill the document is to use a data output proxy
    +out["elem1"](a); //
    +out["elem2"](b); //map data types to XML elements
    +out["elem3"](c); //
    +
    +try
    +{
    +    save(doc, "file.xml"); //throw zen::XmlFileError
    +}
    +catch (const zen::XmlFileError& e) { /* handle error */ }
    +

    The following XML file will be created:

    +
    <?xml version="1.0" encoding="UTF-8"?>
    +<Root>
    +    <elem1>10</elem1>
    +    <elem2>2.000000</elem2>
    +    <elem3>-1</elem3>
    +</Root>
    +

    Load an XML file and map its content to user data:

    +
    zen::XmlDoc doc; //empty XML document
    +
    +try
    +{
    +    load("file.xml", doc); //throw XmlFileError, XmlParsingError
    +}
    +catch (const zen::XmlError& e) { /* handle error */ }
    +
    +zen::XmlIn in(doc); //the simplest way to read the document is to use a data input proxy
    +in["elem1"](a); //
    +in["elem2"](b); //map XML elements into user data
    +in["elem3"](c); //
    +
    +//check for mapping errors, i.e. missing elements or conversion errors: you may consider these as warnings only
    +if (in.errorsOccured())
    +{
    +   std::vector<std::wstring> failedElements = in.getErrorsAs<std::wstring>();
    +   /* generate error message showing the XML element names that failed to convert */
    +}
    +

    +Supported Platforms

    +

    zen::Xml is written in a platform independent manner and runs on any rudimentary C++11 compliant compiler. It has been tested successfully under:

    +
      +
    • Windows:
        +
      1. Visual C++ 2010 - 32 bit
      2. +
      3. Visual C++ 2010 - 64 bit
      4. +
      5. MinGW: GCC 4.5.2 - 32 bit
      6. +
      +
    • +
    +
      +
    • Linux:
        +
      1. GCC 4.5.2 - 32 bit
      2. +
      3. GCC 4.5.2 - 64 bit
      4. +
      +
    • +
    +
      +
    • Mac OS X:
        +
      1. Clang 3.2 - 64 bit
      2. +
      +
    • +
    +

    Note: In order to enable C++11 features in GCC it is required to specify either of the following compiler options:

    +
    -std=c++11
    +-std=c++0x
    +-std=gnu++0x
    +

    +Flexible Programming Model

    +

    Depending on what granularity of control is required in a particular application, zen::Xml allows the user to choose between full control or simplicity.
    +
    + The library is structured into the following parts, each of which can be used in isolation:
    +
    + <File>
    + |
    + | io.h
    + |
    + <Byte Stream>
    + |
    + | parser.h
    + |
    + <Document Object Model>
    + |
    + | bind.h
    + |
    + <C++ user data>
    +
    +

    +
      +
    • Save an XML document to memory
      zen::XmlDoc doc;
      +    ... //fill it
      +std::string stream = serialize(doc); //throw ()
      +/* you now have a binary XML stream */
      +
      +saveStream(stream, "file.xml"); //throw XmlFileError
      +//if all you need is to store XmlDoc in a file direcly you can use zen::save() instead
      +
    • +
    +
      +
    • Load XML document from memory
      //get XML byte stream:
      +std::string stream = loadStream("file.xml"); //throw XmlFileError
      +
      +zen::XmlDoc doc;
      +//parse byte stream into an XML document:
      +parse(stream, doc); //throw XmlParsingError
      +//if all you need is to load an XmlDoc from a file you can use zen::load() directly
      +
    • +
    +
      +
    • Fine-granular error checking with the data input proxy
      zen::XmlIn in(doc);
      +//map XML elements into user data
      +if (!in["elem1"](a))
      +   throw MyCustomException();
      +if (!in["elem2"](b))
      +   throw MyCustomException();
      +if (!in["elem3"](c))
      +   throw MyCustomException();
      +
      +//if (in.errorsOccured()) ...  <- not required here: contains the same conversion errors checked manually before
      +
    • +
    +
      +
    • Access the Document Object Model directly (without input/output proxy)
      +
      + The full power of type conversions which is available via the input/output proxy classes zen::XmlIn and zen::XmlOut is also available for the document object model!
      using namespace zen;
      +
      +XmlDoc doc;
      +
      +XmlElement& child = doc.root().addChild("elem1");
      +child.setValue(1234);
      +
      +save(doc, "file.xml"); //throw XmlFileError
      +

      +
      using namespace zen;
      +
      +XmlDoc doc;
      +load("file.xml", doc); //throw XmlFileError, XmlParsingError
      +
      +XmlElement* child = doc.root().getChild("elem1");
      +if (child)
      +{
      +    int value = -1;
      +    if (!child->getValue(value))
      +        ... //handle conversion error
      +}
      +else
      +    ... //XML element not found
      +
    • +
    +

    +Structured XML element access

    +
    //write a value into one deeply nested XML element - note the different types used seamlessly: char[], wchar_t[], char, wchar_t, int
    +zen::XmlOut out(doc);
    +out["elemento1"][L"элемент2"][L"要素3"][L"στοιχείο4"]["elem5"][L"元素6"][L'元']['z'](-1234);
    +

    The resulting XML:

    +
    <?xml version="1.0" encoding="UTF-8"?>
    +<Root>
    +    <elemento1>
    +        <элемент2>
    +            <要素3>
    +                <στοιχείο4>
    +                    <elem5>
    +                        <元素6>
    +                            <元>
    +                                <z>-1234</z>
    +                            </元>
    +                        </元素6>
    +                    </elem5>
    +                </στοιχείο4>
    +            </要素3>
    +        </элемент2>
    +    </elemento1>
    +</Root>
    +

    +Access XML attributes

    +
    zen::XmlDoc doc;
    +
    +zen::XmlOut out(doc);
    +out["elem"].attribute("attr1",   -1); //
    +out["elem"].attribute("attr2",  2.1); //write data into XML attributes
    +out["elem"].attribute("attr3", true); //
    +
    +save(doc, "file.xml"); //throw XmlFileError
    +

    The resulting XML:

    +
    <?xml version="1.0" encoding="UTF-8"?>
    +<Root>
    +    <elem attr1="-1" attr2="2.1" attr3="true"/>
    +</Root>
    +

    +Automatic conversion for built-in arithmetic types

    +

    All built-in arithmetic types and bool are detected at compile time and a proper conversion is applied. Common conversions for integer-like types such as int, long, long long, ect. as well as floating point types are optimized for maximum performance.

    +
    zen::XmlOut out(doc);
    +
    +out["int"]   (-1234);
    +out["double"](1.23);
    +out["float"] (4.56f);
    +out["ulong"] (1234UL);
    +out["bool"]  (false);
    +

    The resulting XML:

    +
    <?xml version="1.0" encoding="UTF-8"?>
    +<Root>
    +    <int>-1234</int>
    +    <double>1.23</double>
    +    <float>4.56</float>
    +    <ulong>1234</ulong>
    +    <bool>false</bool>
    +</Root>
    +

    +Automatic conversion for string-like types

    +

    The document object model of zen::Xml internally stores all names and values as a std::string. Consequently everything that is not a std::string but is "string-like" is UTF-converted into a std::string representation. By default zen::Xml accepts all character arrays like char[], wchar_t[], char*, wchar_t*, single characters like char, wchar_t, standard string classes like std::string, std::wstring and user-defined string classes. If the input string is based on char, it will simply be copied and thereby preserves any local encodings. If the input string is based on wchar_t it will be converted to an UTF-8 encoded std::string. The correct wchar_t encoding of the system will be detected at compile time, for example UTF-16 on Windows, UTF-32 on most Linux distributions.

    +

    Note: User-defined string classes are automatically supported if they fulfill the following string concept by defining:

    +
      +
    1. A typedef named value_type for the underlying character type: must be char or wchar_t
    2. +
    3. A member function c_str() returning something that can be converted into a const value_type*
    4. +
    5. A member function length() returning the number of characters returned by c_str()
    6. +
    +
    std::string  elem1 = "elemento1";
    +std::wstring elem2 = L"элемент2";
    +wxString     elem3 = L"要素3";
    +MyString     elem4 = L"στοιχείο4";
    +
    +zen::XmlOut out(doc);
    +
    +out["string"]    (elem1);
    +out["wstring"]   (elem2);
    +out["wxString"]  (elem3);
    +out["MyString"]  (elem4);
    +out["char[6]"]   ("elem5");
    +out["wchar_t[4]"](L"元素6");
    +out["wchar_t"]   (L'元');
    +out["char"]      ('z');
    +

    The resulting XML:

    +
    <?xml version="1.0" encoding="UTF-8"?>
    +<Root>
    +    <string>elemento1</string>
    +    <wstring>элемент2</wstring>
    +    <wxString>要素3</wxString>
    +    <MyString>στοιχείο4</MyString>
    +    <char[6]>elem5</char[6]>
    +    <wchar_t[4]>元素6</wchar_t[4]>
    +    <wchar_t>元</wchar_t>
    +    <char>z</char>
    +</Root>
    +

    +Automatic conversion for STL container types

    +
      +
    • User-defined STL compatible types are automatically supported if they fulfill the following container concept by defining:
        +
      1. A typedef named value_type for the underlying element type of the container
      2. +
      3. A typedef named iterator for a non-const iterator into the container
      4. +
      5. A typedef named const_iterator for a const iterator into the container
        +
        +
      6. +
      7. A member function begin() returning an iterator pointing to the first element in the container
      8. +
      9. A member function end() returning an iterator pointing just after the last element in the container
      10. +
      11. A member function insert() with the signature iterator insert(iterator position, const value_type& x)
      12. +
      13. A member function clear() removing all elements from the container
      14. +
      +
    • +
    +
      +
    • In order to support combinations of user types and STL containers such as std::vector<MyType> or std::vector<std::list<MyType>> it is sufficient to only integrate MyType into zen::Xml.
      + See Support for user-defined types
    • +
    +
    std::deque   <float>         testDeque;
    +std::list    <size_t>        testList;
    +std::map     <double, char>  testMap;
    +std::multimap<short, double> testMultiMap;
    +std::set     <int>           testSet;
    +std::multiset<std::string>   testMultiSet;
    +std::vector  <wchar_t>       testVector;
    +std::vector  <std::list<wchar_t>> testVectorList;
    +std::pair    <char, wchar_t> testPair;
    +
    +/* fill container */
    +
    +zen::XmlOut out(doc);
    +
    +out["deque"]    (testDeque);
    +out["list"]     (testList);
    +out["map"]      (testMap);
    +out["multimap"] (testMultiMap);
    +out["set"]      (testSet);
    +out["multiset"] (testMultiSet);
    +out["vector"]   (testVector);
    +out["vect_list"](testVectorList);
    +out["pair" ]    (testPair);
    +

    The resulting XML:

    +
    <?xml version="1.0" encoding="UTF-8"?>
    +<Root>
    +    <deque>
    +        <Item>1.234</Item>
    +        <Item>5.678</Item>
    +    </deque>
    +    <list>
    +        <Item>1</Item>
    +        <Item>2</Item>
    +    </list>
    +    <map>
    +        <Item>
    +            <one>1.1</one>
    +            <two>a</two>
    +        </Item>
    +        <Item>
    +            <one>2.2</one>
    +            <two>b</two>
    +        </Item>
    +    </map>
    +    <multimap>
    +        <Item>
    +            <one>3</one>
    +            <two>99</two>
    +        </Item>
    +        <Item>
    +            <one>3</one>
    +            <two>100</two>
    +        </Item>
    +        <Item>
    +            <one>4</one>
    +            <two>101</two>
    +        </Item>
    +    </multimap>
    +    <set>
    +        <Item>1</Item>
    +        <Item>2</Item>
    +    </set>
    +    <multiset>
    +        <Item>1</Item>
    +        <Item>1</Item>
    +        <Item>2</Item>
    +    </multiset>
    +    <vector>
    +        <Item>Ä</Item>
    +        <Item>Ö</Item>
    +    </vector>
    +    <vect_list>
    +        <Item>
    +            <Item>ä</Item>
    +            <Item>ö</Item>
    +            <Item>ü</Item>
    +        </Item>
    +        <Item>
    +            <Item>ä</Item>
    +            <Item>ö</Item>
    +            <Item>ü</Item>
    +        </Item>
    +    </vect_list>
    +    <pair>
    +        <one>a</one>
    +        <two>â</two>
    +    </pair>
    +</Root>
    +

    +Support for user-defined types

    +

    User types can be integrated into zen::Xml by providing specializations of zen::readText() and zen::writeText() or zen::readStruc() and zen::writeStruc(). The first pair should be used for all non-structured types that can be represented as a simple text string. This specialization is then used to convert the type to XML elements and XML attributes. The second pair should be specialized for structured types that require an XML representation as a hierarchy of elements. This specialization is used when converting the type to XML elements only.
    +
    + See section Type Safety for a discussion of type categories.
    +
    + Example: Specialization for an enum type

    +
    enum UnitTime
    +{
    +    UNIT_SECOND,
    +    UNIT_MINUTE,
    +    UNIT_HOUR
    +};
    +
    +namespace zen
    +{
    +template <> inline
    +void writeText(const UnitTime& value, std::string& output)
    +{
    +    switch (value)
    +    {
    +        case UNIT_SECOND: output = "second"; break;
    +        case UNIT_MINUTE: output = "minute"; break;
    +        case UNIT_HOUR:   output = "hour"  ; break;
    +    }
    +}
    +
    +template <> inline
    +bool readText(const std::string& input, UnitTime& value)
    +{
    +    std::string tmp = input;
    +    zen::trim(tmp);
    +    if (tmp == "second")
    +        value = UNIT_SECOND;
    +    else if (tmp == "minute")
    +        value = UNIT_MINUTE;
    +    else if (tmp == "hour")
    +        value = UNIT_HOUR;
    +    else
    +        return false;
    +    return true;
    +}
    +}
    +

    Example: Brute-force specialization for an enum type

    +
    namespace zen
    +{
    +template <> inline
    +void writeText(const EnumType& value, std::string& output)
    +{
    +    output = zen::numberTo<std::string>(static_cast<int>(value)); //treat enum like an integer
    +}
    +
    +template <> inline
    +bool readText(const std::string& input, EnumType& value)
    +{
    +    value = static_cast<EnumType>(zen::stringTo<int>(input)); //treat enum like an integer
    +    return true;
    +}
    +}
    +

    Example: Specialization for a structured user type

    +
    struct Config
    +{
    +    int a;
    +    std::wstring b;
    +};
    +
    +namespace zen
    +{
    +template <> inline
    +void writeStruc(const Config& value, XmlElement& output)
    +{
    +    XmlOut out(output);
    +    out["number" ](value.a);
    +    out["address"](value.b);
    +}
    +
    +template <> inline
    +bool readStruc(const XmlElement& input, Config& value)
    +{
    +    XmlIn in(input);
    +    bool rv1 = in["number" ](value.a);
    +    bool rv2 = in["address"](value.b);
    +    return rv1 && rv2;
    +}
    +}
    +
    +int main()
    +{
    +    Config cfg = { 2,  L"Abc 3" };
    +
    +    std::vector<Config> cfgList;
    +    cfgList.push_back(cfg);
    +
    +    zen::XmlDoc doc;
    +    zen::XmlOut out(doc); //write to Xml via output proxy
    +    out["config"](cfgList);
    +    save(doc, "file.xml"); //throw XmlFileError
    +}
    +

    The resulting XML:

    +
    <?xml version="1.0" encoding="UTF-8"?>
    +<Root>
    +    <config>
    +        <Item>
    +            <number>2</number>
    +            <address>Abc 3</address>
    +        </Item>
    +    </config>
    +</Root>
    +

    +Structured user types

    +

    Although it is possible to enable conversion of structured user types by specializing zen::readStruc() and zen::writeStruc() (see Support for user-defined types), this approach has one drawback: If a mapping error occurs when converting an XML element to structured user data, for example a child-element is missing, the input proxy class zen::XmlIn is only able to detect that the whole conversion failed. It cannot say which child-elements in particular failed to convert.
    +
    + Therefore it may be appropriate to convert structured types by calling subroutines in order to enable fine-granular logging:

    +
    void readConfig(const zen::XmlIn& in, Config& cfg)
    +{
    +    in["number" ](value.a); //failed conversions will now be logged for each single item by XmlIn
    +    in["address"](value.b); //instead of only once for the complete Config type!
    +}
    +
    +
    +void loadConfig(const wxString& filename, Config& cfg)
    +{
    +    zen::XmlDoc doc; //empty XML document
    +
    +    try
    +    {
    +        load(filename, doc); //throw XmlFileError, XmlParsingError
    +    }
    +    catch (const zen::XmlError& e) { /* handle error */ }
    +
    +    zen::XmlIn in(doc); 
    + 
    +    zen::XmlIn inConfig = in["config"]; //get input proxy for child element "config"
    +  
    +    readConfig(inConfig, cfg); //map child element to user data by calling subroutine
    +
    +    //check for mapping errors: errors occuring in subroutines are considered, too!
    +    if (in.errorsOccured())
    +       /* show mapping errors */
    +}
    +

    +Type Safety

    +

    zen::Xml heavily uses methods of compile-time introspection in order to free the user from managing basic type conversions by himself. Thereby it is important to find the right balance between automatic conversions and type safety so that program correctness is not compromised. In the context of XML processing three fundamental type categories can be recognized:

    +
      +
    • string-like types: std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
    • +
    • to-string-convertible types: any string-like type, all built-in arithmetic numbers, bool
    • +
    • structured types: any to-string-convertible type, STL containers, std::pair, structured user types
    • +
    +

    These categories can be seen as a sequence of inclusive sets:

    +
    -----------------------------
    +| structured                |  Used as: XML element value
    +| ------------------------- |           Conversion via: readStruc(), writeStruc() - may be specialized for user-defined types!
    +| | to-string-convertible | |  Used as: XML element/attribute value
    +| | ---------------       | |           Conversion via: readText(), writeText() - may be specialized for user-defined types!
    +| | | string-like |       | |  Used as: XML element/attribute value or element name
    +| | ---------------       | |           Conversion via: utfCvrtTo<>()
    +| ------------------------- |
    +-----------------------------
    +

    A practical implication of this design is that conversions that do not make sense in a particular context simply lead to compile-time errors:

    +
    zen::XmlOut out(doc);
    +out[L'Z'](someValue); //fine: a wchar_t is acceptable as an element name
    +out[1234](someValue); //compiler error: an integer is NOT "string-like"!
    +


    +

    +
    int i = 0;
    +std::vector<int> v;
    +
    +zen::XmlOut out(doc);
    +out["elem1"](i); //fine: both i and v can be converted to an XML element
    +out["elem2"](v); //
    +
    +out["elem"].attribute("attr1", i); //fine: an integer can be converted to an XML attribute
    +out["elem"].attribute("attr2", v); //compiler error: a std::vector<int> is NOT "to-string-convertible"!
    +
    Author:
    Zenju
    +
    + Email: zenju AT gmx DOT de
    +
    + + + + + + diff --git a/zenxml/doc/io_8h_source.html b/zenxml/doc/io_8h_source.html new file mode 100644 index 00000000..09ec749d --- /dev/null +++ b/zenxml/doc/io_8h_source.html @@ -0,0 +1,213 @@ + + + + + +zen::Xml: io.h Source File + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + +
    + + + + +
    + +
    + +
    +
    +
    io.h
    +
    +
    +
    00001 // **************************************************************************
    +00002 // * This file is part of the zen::Xml project. It is distributed under the *
    +00003 // * Boost Software License: http://www.boost.org/LICENSE_1_0.txt           *
    +00004 // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved        *
    +00005 // **************************************************************************
    +00006 
    +00007 #ifndef ZEN_XML_IO_HEADER_8917640501480763248343343
    +00008 #define ZEN_XML_IO_HEADER_8917640501480763248343343
    +00009 
    +00010 #include <cstdio>
    +00011 #include <cerrno>
    +00012 #include <zen/scope_guard.h>
    +00013 #include <zen/utf.h>
    +00014 #include "error.h"
    +00015 
    +00016 namespace zen
    +00017 {
    +00023 #if !defined(ZEN_PLATFORM_WINDOWS) && !defined(ZEN_PLATFORM_OTHER)
    +00024 #error Please specify your platform: #define ZEN_PLATFORM_WINDOWS or ZEN_PLATFORM_OTHER
    +00025 #endif
    +00026 
    +00028 struct XmlFileError : public XmlError
    +00029 {
    +00030     typedef int ErrorCode;
    +00031 
    +00032     explicit XmlFileError(ErrorCode ec) : lastError(ec) {}
    +00034     ErrorCode lastError;
    +00035 };
    +00036 
    +00037 
    +00038 #ifdef ZEN_PLATFORM_WINDOWS
    +00039 namespace implemenation //sad but true
    +00040 {
    +00041 template <class String> inline
    +00042 FILE* fopen(const String& filename, const wchar_t* mode)
    +00043 {
    +00044 #ifdef _MSC_VER
    +00045     FILE* handle = nullptr;
    +00046     errno_t rv = ::_wfopen_s(&handle, utfCvrtTo<std::wstring>(filename).c_str(), mode); //more secure?
    +00047     (void)rv;
    +00048     return handle;
    +00049 #else
    +00050     return ::_wfopen(utfCvrtTo<std::wstring>(filename).c_str(), mode);
    +00051 #endif
    +00052 }
    +00053 }
    +00054 #endif
    +00055 
    +00056 
    +00058 
    +00064 template <class String>
    +00065 void saveStream(const std::string& stream, const String& filename) //throw XmlFileError
    +00066 {
    +00067 #ifdef ZEN_PLATFORM_WINDOWS
    +00068     FILE* handle = implemenation::fopen(utfCvrtTo<std::wstring>(filename).c_str(), L"wb");
    +00069 #else
    +00070     FILE* handle = ::fopen(utfCvrtTo<std::string>(filename).c_str(), "w");
    +00071 #endif
    +00072     if (handle == nullptr)
    +00073         throw XmlFileError(errno);
    +00074     ZEN_ON_SCOPE_EXIT(::fclose(handle));
    +00075 
    +00076     const size_t bytesWritten = ::fwrite(stream.c_str(), 1, stream.size(), handle);
    +00077     if (::ferror(handle) != 0)
    +00078         throw XmlFileError(errno);
    +00079 
    +00080     (void)bytesWritten;
    +00081     assert(bytesWritten == stream.size());
    +00082 }
    +00083 
    +00084 
    +00086 
    +00092 template <class String>
    +00093 std::string loadStream(const String& filename) //throw XmlFileError
    +00094 {
    +00095 #ifdef ZEN_PLATFORM_WINDOWS
    +00096     FILE* handle = implemenation::fopen(utfCvrtTo<std::wstring>(filename).c_str(), L"rb");
    +00097 #else
    +00098     FILE* handle = ::fopen(utfCvrtTo<std::string>(filename).c_str(), "r");
    +00099 #endif
    +00100     if (handle == nullptr)
    +00101         throw XmlFileError(errno);
    +00102     ZEN_ON_SCOPE_EXIT(::fclose(handle));
    +00103 
    +00104     std::string stream;
    +00105     const size_t blockSize = 64 * 1024;
    +00106     do
    +00107     {
    +00108         stream.resize(stream.size() + blockSize); //let's pray std::string implements exponential growth!
    +00109 
    +00110         const size_t bytesRead = ::fread(&*(stream.begin() + stream.size() - blockSize), 1, blockSize, handle);
    +00111         if (::ferror(handle))
    +00112             throw XmlFileError(errno);
    +00113         if (bytesRead > blockSize)
    +00114             throw XmlFileError(0);
    +00115         if (bytesRead < blockSize)
    +00116             stream.resize(stream.size() - (blockSize - bytesRead)); //caveat: unsigned arithmetics
    +00117     }
    +00118     while (!::feof(handle));
    +00119 
    +00120     return stream;
    +00121 }
    +00122 }
    +00123 
    +00124 #endif //ZEN_XML_IO_HEADER_8917640501480763248343343
    +
    + + + + + + diff --git a/zenxml/doc/jquery.js b/zenxml/doc/jquery.js new file mode 100644 index 00000000..90b3a2bc --- /dev/null +++ b/zenxml/doc/jquery.js @@ -0,0 +1,64 @@ +/* + * jQuery JavaScript Library v1.3.2 + * http://jquery.com/ + * + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License + * + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 + */ +(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
    "]||!O.indexOf("",""]||(!O.indexOf("",""]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div
    ","
    "]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); +/* + * Sizzle CSS Selector Engine - v0.9.3 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="

    ";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
    ";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0) +{I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
    ").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function() +{G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
    ';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); + +/* + * jQuery hashchange event - v1.3 - 7/21/2010 + * http://benalman.com/projects/jquery-hashchange-plugin/ + * + * Copyright (c) 2010 "Cowboy" Ben Alman + * Dual licensed under the MIT and GPL licenses. + * http://benalman.com/about/license/ + */ +(function($,e,b){var c="hashchange",h=document,f,g=$.event.special,i=h.documentMode,d="on"+c in e&&(i===b||i>7);function a(j){j=j||location.href;return"#"+j.replace(/^[^#]*#?(.*)$/,"$1")}$.fn[c]=function(j){return j?this.bind(c,j):this.trigger(c)};$.fn[c].delay=50;g[c]=$.extend(g[c],{setup:function(){if(d){return false}$(f.start)},teardown:function(){if(d){return false}$(f.stop)}});f=(function(){var j={},p,m=a(),k=function(q){return q},l=k,o=k;j.start=function(){p||n()};j.stop=function(){p&&clearTimeout(p);p=b};function n(){var r=a(),q=o(m);if(r!==m){l(m=r,q);$(e).trigger(c)}else{if(q!==m){location.href=location.href.replace(/#.*/,"")+q}}p=setTimeout(n,$.fn[c].delay)}$.browser.msie&&!d&&(function(){var q,r;j.start=function(){if(!q){r=$.fn[c].src;r=r&&r+a();q=$(' +
    + +
    +
    Here is a list of all documented namespace members with links to the namespaces they belong to:
      +
    • load() +: zen +
    • +
    • loadStream() +: zen +
    • +
    • parse() +: zen +
    • +
    • readStruc() +: zen +
    • +
    • readText() +: zen +
    • +
    • save() +: zen +
    • +
    • saveStream() +: zen +
    • +
    • serialize() +: zen +
    • +
    • writeStruc() +: zen +
    • +
    • writeText() +: zen +
    • +
    +
    + + + + + + diff --git a/zenxml/doc/namespacemembers_func.html b/zenxml/doc/namespacemembers_func.html new file mode 100644 index 00000000..dd473af8 --- /dev/null +++ b/zenxml/doc/namespacemembers_func.html @@ -0,0 +1,143 @@ + + + + + +zen::Xml: Namespace Members + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + + +
    + + + + +
    + +
    + +
      +
    • load() +: zen +
    • +
    • loadStream() +: zen +
    • +
    • parse() +: zen +
    • +
    • readStruc() +: zen +
    • +
    • readText() +: zen +
    • +
    • save() +: zen +
    • +
    • saveStream() +: zen +
    • +
    • serialize() +: zen +
    • +
    • writeStruc() +: zen +
    • +
    • writeText() +: zen +
    • +
    +
    + + + + + + diff --git a/zenxml/doc/namespaces.html b/zenxml/doc/namespaces.html new file mode 100644 index 00000000..6a17ec0a --- /dev/null +++ b/zenxml/doc/namespaces.html @@ -0,0 +1,112 @@ + + + + + +zen::Xml: Namespace List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + +
    + + + + +
    + +
    + +
    +
    +
    Namespace List
    +
    +
    +
    Here is a list of all documented namespaces with brief descriptions:
    + +
    zenThe zen::Xml namespace
    +
    + + + + + + diff --git a/zenxml/doc/namespacezen.html b/zenxml/doc/namespacezen.html new file mode 100644 index 00000000..c05f255c --- /dev/null +++ b/zenxml/doc/namespacezen.html @@ -0,0 +1,613 @@ + + + + + +zen::Xml: zen Namespace Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + + + + + +
    + +
    + +
    +
    + +
    +
    zen Namespace Reference
    +
    +
    + +

    The zen::Xml namespace. +More...

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Classes

    class  XmlOut
     Proxy class to conveniently convert user data into XML structure. More...
    class  XmlIn
     Proxy class to conveniently convert XML structure to user data. More...
    class  XmlElement
     An XML element. More...
    class  XmlDoc
     The complete XML document. More...
    struct  XmlError
     Exception base class for zen::Xml. More...
    struct  XmlFileError
     Exception thrown due to failed file I/O. More...
    struct  XmlParsingError
     Exception thrown due to an XML parsing error. More...

    +Functions

    template<class String >
    void load (const String &filename, XmlDoc &doc)
     Load XML document from a file.
    template<class String >
    void save (const XmlDoc &doc, const String &filename, const std::string &lineBreak="\r\n", const std::string &indent=" ")
     Save XML document to a file.
    template<class T >
    bool readStruc (const XmlElement &input, T &value)
     Convert XML element to structured user data.
    template<class T >
    void writeStruc (const T &value, XmlElement &output)
     Convert structured user data into an XML element.
    template<class T >
    bool readText (const std::string &input, T &value)
     Convert text to user data - used by XML elements and attributes.
    template<class T >
    void writeText (const T &value, std::string &output)
     Convert user data into text - used by XML elements and attributes.
    template<class String >
    void saveStream (const std::string &stream, const String &filename)
     Save byte stream to a file.
    template<class String >
    std::string loadStream (const String &filename)
     Load byte stream from a file.
    std::string serialize (const XmlDoc &doc, const std::string &lineBreak="\r\n", const std::string &indent=" ")
     Save XML document as a byte stream.
    void parse (const std::string &stream, XmlDoc &doc)
     Load XML document from a byte stream.
    +

    Detailed Description

    +

    The zen::Xml namespace.

    +

    Function Documentation

    + +
    +
    +
    +template<class String >
    + + + + + + + + + + + + + + + + + + +
    void zen::load (const String & filename,
    XmlDoc & doc 
    )
    +
    +
    + +

    Load XML document from a file.

    +

    Convenience function that does nothing more than calling loadStream() and parse().

    +
    Template Parameters:
    + + +
    StringArbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
    +
    +
    +
    Parameters:
    + + + +
    filenameInput file name
    docThe XML document to load
    +
    +
    +
    Exceptions:
    + + + +
    XmlFileError
    XmlParsingError
    +
    +
    + +
    +
    + +
    +
    +
    +template<class String >
    + + + + + + + + +
    std::string zen::loadStream (const String & filename)
    +
    +
    + +

    Load byte stream from a file.

    +
    Template Parameters:
    + + +
    StringArbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
    +
    +
    +
    Parameters:
    + + +
    filenameInput file name
    +
    +
    +
    Returns:
    Output byte stream
    +
    Exceptions:
    + + +
    XmlFileError
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + +
    void zen::parse (const std::string & stream,
    XmlDoc & doc 
    )
    +
    +
    + +

    Load XML document from a byte stream.

    +
    Parameters:
    + + + +
    streamInput byte stream
    docOutput XML document
    +
    +
    +
    Exceptions:
    + + +
    XmlParsingError
    +
    +
    + +
    +
    + +
    +
    +
    +template<class T >
    + + + + + + + + + + + + + + + + + + +
    bool zen::readStruc (const XmlElement & input,
    T & value 
    )
    +
    +
    + +

    Convert XML element to structured user data.

    +
    Parameters:
    + + + +
    inputThe input XML element.
    valueConversion target value.
    +
    +
    +
    Returns:
    "true" if value was read successfully.
    + +
    +
    + +
    +
    +
    +template<class T >
    + + + + + + + + + + + + + + + + + + +
    bool zen::readText (const std::string & input,
    T & value 
    )
    +
    +
    + +

    Convert text to user data - used by XML elements and attributes.

    +
    Parameters:
    + + + +
    inputInput text.
    valueConversion target value.
    +
    +
    +
    Returns:
    "true" if value was read successfully.
    + +
    +
    + +
    +
    +
    +template<class String >
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    void zen::save (const XmlDoc & doc,
    const String & filename,
    const std::string & lineBreak = "\r\n",
    const std::string & indent = "    " 
    )
    +
    +
    + +

    Save XML document to a file.

    +

    Convenience function that does nothing more than calling serialize() and saveStream().

    +
    Template Parameters:
    + + +
    StringArbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
    +
    +
    +
    Parameters:
    + + + + + +
    docThe XML document to save
    filenameOutput file name
    lineBreakLine break, default: carriage return + new line
    indentIndentation, default: four space characters
    +
    +
    +
    Exceptions:
    + + +
    XmlFileError
    +
    +
    + +
    +
    + +
    +
    +
    +template<class String >
    + + + + + + + + + + + + + + + + + + +
    void zen::saveStream (const std::string & stream,
    const String & filename 
    )
    +
    +
    + +

    Save byte stream to a file.

    +
    Template Parameters:
    + + +
    StringArbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ...
    +
    +
    +
    Parameters:
    + + + +
    streamInput byte stream
    filenameOutput file name
    +
    +
    +
    Exceptions:
    + + +
    XmlFileError
    +
    +
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    std::string zen::serialize (const XmlDoc & doc,
    const std::string & lineBreak = "\r\n",
    const std::string & indent = "    " 
    )
    +
    +
    + +

    Save XML document as a byte stream.

    +
    Parameters:
    + + + + +
    docInput XML document
    lineBreakLine break, default: carriage return + new line
    indentIndentation, default: four space characters
    +
    +
    +
    Returns:
    Output byte stream
    + +
    +
    + +
    +
    +
    +template<class T >
    + + + + + + + + + + + + + + + + + + +
    void zen::writeStruc (const T & value,
    XmlElement & output 
    )
    +
    +
    + +

    Convert structured user data into an XML element.

    +
    Parameters:
    + + + +
    valueThe value to be converted.
    outputThe output XML element.
    +
    +
    + +
    +
    + +
    +
    +
    +template<class T >
    + + + + + + + + + + + + + + + + + + +
    void zen::writeText (const T & value,
    std::string & output 
    )
    +
    +
    + +

    Convert user data into text - used by XML elements and attributes.

    +
    Parameters:
    + + + +
    valueThe value to be converted.
    outputOutput text.
    +
    +
    + +
    +
    +
    + + + + + + diff --git a/zenxml/doc/nav_f.png b/zenxml/doc/nav_f.png new file mode 100644 index 00000000..1b07a162 Binary files /dev/null and b/zenxml/doc/nav_f.png differ diff --git a/zenxml/doc/nav_h.png b/zenxml/doc/nav_h.png new file mode 100644 index 00000000..01f5fa6a Binary files /dev/null and b/zenxml/doc/nav_h.png differ diff --git a/zenxml/doc/open.png b/zenxml/doc/open.png new file mode 100644 index 00000000..7b35d2c2 Binary files /dev/null and b/zenxml/doc/open.png differ diff --git a/zenxml/doc/parser_8h_source.html b/zenxml/doc/parser_8h_source.html new file mode 100644 index 00000000..97bdc7e3 --- /dev/null +++ b/zenxml/doc/parser_8h_source.html @@ -0,0 +1,687 @@ + + + + + +zen::Xml: parser.h Source File + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + +
    + + + + +
    + +
    + +
    +
    +
    parser.h
    +
    +
    +
    00001 // **************************************************************************
    +00002 // * This file is part of the zen::Xml project. It is distributed under the *
    +00003 // * Boost Software License: http://www.boost.org/LICENSE_1_0.txt           *
    +00004 // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved        *
    +00005 // **************************************************************************
    +00006 
    +00007 #ifndef ZEN_XML_PARSER_HEADER_81248670213764583021432
    +00008 #define ZEN_XML_PARSER_HEADER_81248670213764583021432
    +00009 
    +00010 #include <cstdio>
    +00011 #include <cstddef> //ptrdiff_t; req. on Linux
    +00012 #include <zen/string_traits.h>
    +00013 #include "dom.h"
    +00014 #include "error.h"
    +00015 
    +00016 namespace zen
    +00017 {
    +00023 
    +00024 
    +00030 std::string serialize(const XmlDoc& doc,
    +00031                       const std::string& lineBreak = "\r\n",
    +00032                       const std::string& indent = "    "); //throw ()
    +00033 
    +00035 struct XmlParsingError : public XmlError
    +00036 {
    +00037     XmlParsingError(size_t rowNo, size_t colNo) : row(rowNo), col(colNo) {}
    +00039     size_t row; //beginning with 0
    +00041     size_t col; //
    +00042 };
    +00043 
    +00044 
    +00046 
    +00051 void parse(const std::string& stream, XmlDoc& doc); //throw XmlParsingError
    +00052 
    +00053 
    +00054 
    +00055 
    +00056 
    +00057 
    +00058 
    +00059 
    +00060 
    +00061 
    +00062 
    +00063 
    +00064 
    +00065 
    +00066 
    +00067 
    +00068 
    +00069 
    +00070 
    +00071 
    +00072 //---------------------------- implementation ----------------------------
    +00073 //see: http://www.w3.org/TR/xml/
    +00074 
    +00075 namespace implementation
    +00076 {
    +00077 inline
    +00078 std::pair<char, char> hexify(unsigned char c)
    +00079 {
    +00080     auto hexifyDigit = [](int num) -> char //input [0, 15], output 0-9, A-F
    +00081     {
    +00082         assert(0 <= num&&  num <= 15); //guaranteed by design below!
    +00083         return static_cast<char>(num <= 9 ? //no signed/unsigned char problem here!
    +00084         '0' + num :
    +00085         'A' + (num - 10));
    +00086     };
    +00087     return std::make_pair(hexifyDigit(c / 16), hexifyDigit(c % 16));
    +00088 }
    +00089 
    +00090 
    +00091 inline
    +00092 char unhexify(char high, char low)
    +00093 {
    +00094     auto unhexifyDigit = [](char hex) -> int //input 0-9, a-f, A-F; output range: [0, 15]
    +00095     {
    +00096         if ('0' <= hex && hex <= '9') //no signed/unsigned char problem here!
    +00097             return hex - '0';
    +00098         else if ('A' <= hex && hex <= 'F')
    +00099             return (hex - 'A') + 10;
    +00100         else if ('a' <= hex && hex <= 'f')
    +00101             return (hex - 'a') + 10;
    +00102         assert(false);
    +00103         return 0;
    +00104     };
    +00105     return static_cast<unsigned char>(16 * unhexifyDigit(high) + unhexifyDigit(low)); //[!] convert to unsigned char first, then to char (which may be signed)
    +00106 };
    +00107 
    +00108 
    +00109 template <class Predicate> inline
    +00110 std::string normalize(const std::string& str, Predicate pred) //pred: unary function taking a char, return true if value shall be encoded as hex
    +00111 {
    +00112     std::string output;
    +00113     std::for_each(str.begin(), str.end(),
    +00114                   [&](char c)
    +00115     {
    +00116         if (c == '&')      //
    +00117             output += "&amp;";
    +00118         else if (c == '<') //normalization mandatory: http://www.w3.org/TR/xml/#syntax
    +00119             output += "&lt;";
    +00120         else if (c == '>') //
    +00121             output += "&gt;";
    +00122         else if (pred(c))
    +00123         {
    +00124             if (c == '\'')
    +00125                 output += "&apos;";
    +00126             else if (c == '\"')
    +00127                 output += "&quot;";
    +00128             else
    +00129             {
    +00130                 output += "&#x";
    +00131                 const auto hexDigits = hexify(c); //hexify beats "printNumber<std::string>("&#x%02X;", c)" by a nice factor of 3!
    +00132                 output += hexDigits.first;
    +00133                 output += hexDigits.second;
    +00134                 output += ';';
    +00135             }
    +00136         }
    +00137         else
    +00138             output += c;
    +00139     });
    +00140     return output;
    +00141 }
    +00142 
    +00143 inline
    +00144 std::string normalizeName(const std::string& str)
    +00145 {
    +00146     return normalize(str, [](char c) { return isWhiteSpace(c) || c == '=' || c == '/' || c == '\'' || c == '\"'; });
    +00147 }
    +00148 
    +00149 inline
    +00150 std::string normalizeElementValue(const std::string& str)
    +00151 {
    +00152     return normalize(str, [](char c) { return static_cast<unsigned char>(c) < 32; });
    +00153 }
    +00154 
    +00155 inline
    +00156 std::string normalizeAttribValue(const std::string& str)
    +00157 {
    +00158     return normalize(str, [](char c) { return static_cast<unsigned char>(c) < 32 || c == '\'' || c == '\"'; });
    +00159 }
    +00160 
    +00161 
    +00162 template <class CharIterator, size_t N> inline
    +00163 bool checkEntity(CharIterator& first, CharIterator last, const char (&placeholder)[N])
    +00164 {
    +00165     assert(placeholder[N - 1] == 0);
    +00166     const ptrdiff_t strLen = N - 1; //don't count null-terminator
    +00167     if (last - first >= strLen && std::equal(first, first + strLen, placeholder))
    +00168     {
    +00169         first += strLen - 1;
    +00170         return true;
    +00171     }
    +00172     return false;
    +00173 }
    +00174 
    +00175 
    +00176 namespace
    +00177 {
    +00178 std::string denormalize(const std::string& str)
    +00179 {
    +00180     std::string output;
    +00181     for (auto it = str.begin(); it != str.end(); ++it)
    +00182     {
    +00183         const char c = *it;
    +00184 
    +00185         if (c == '&')
    +00186         {
    +00187             if (checkEntity(it, str.end(), "&amp;"))
    +00188                 output += '&';
    +00189             else if (checkEntity(it, str.end(), "&lt;"))
    +00190                 output += '<';
    +00191             else if (checkEntity(it, str.end(), "&gt;"))
    +00192                 output += '>';
    +00193             else if (checkEntity(it, str.end(), "&apos;"))
    +00194                 output += '\'';
    +00195             else if (checkEntity(it, str.end(), "&quot;"))
    +00196                 output += '\"';
    +00197             else if (str.end() - it >= 6 &&
    +00198                      it[1] == '#' &&
    +00199                      it[2] == 'x' &&
    +00200                      it[5] == ';')
    +00201             {
    +00202                 output += unhexify(it[3], it[4]); //unhexify beats "::sscanf(&it[3], "%02X", &tmp)" by a factor of 3000 for ~250000 calls!!!
    +00203                 it += 5;
    +00204             }
    +00205             else
    +00206                 output += c; //unexpected char!
    +00207         }
    +00208         else if (c == '\r') //map all end-of-line characters to \n http://www.w3.org/TR/xml/#sec-line-ends
    +00209         {
    +00210             auto itNext = it + 1;
    +00211             if (itNext != str.end() && *itNext == '\n')
    +00212                 ++it;
    +00213             output += '\n';
    +00214         }
    +00215         else
    +00216             output += c;
    +00217     };
    +00218     return output;
    +00219 }
    +00220 
    +00221 
    +00222 void serialize(const XmlElement& element, std::string& stream,
    +00223                const std::string& lineBreak,
    +00224                const std::string& indent,
    +00225                size_t indentLevel)
    +00226 {
    +00227     const std::string& nameFmt = normalizeName(element.getNameAs<std::string>());
    +00228 
    +00229     for (size_t i = 0; i < indentLevel; ++i)
    +00230         stream += indent;
    +00231 
    +00232     stream += '<' + nameFmt;
    +00233 
    +00234     auto attr = element.getAttributes();
    +00235     for (auto it = attr.first; it != attr.second; ++it)
    +00236         stream += ' ' + normalizeName(it->first) + "=\"" + normalizeAttribValue(it->second) + '\"';
    +00237 
    +00238     //no support for mixed-mode content
    +00239     auto iterPair = element.getChildren();
    +00240     if (iterPair.first != iterPair.second) //structured element
    +00241     {
    +00242         stream += '>' + lineBreak;
    +00243 
    +00244         std::for_each(iterPair.first, iterPair.second,
    +00245         [&](const XmlElement & el) { serialize(el, stream, lineBreak, indent, indentLevel + 1); });
    +00246 
    +00247         for (size_t i = 0; i < indentLevel; ++i)
    +00248             stream += indent;
    +00249         stream += "</" + nameFmt + '>' + lineBreak;
    +00250     }
    +00251     else
    +00252     {
    +00253         std::string value;
    +00254         element.getValue(value);
    +00255 
    +00256         if (!value.empty()) //value element
    +00257             stream += '>' + normalizeElementValue(value) + "</" + nameFmt + '>' + lineBreak;
    +00258         else //empty element
    +00259             stream += "/>" + lineBreak;
    +00260     }
    +00261 }
    +00262 
    +00263 std::string serialize(const XmlDoc& doc,
    +00264                       const std::string& lineBreak,
    +00265                       const std::string& indent)
    +00266 {
    +00267     std::string version = doc.getVersionAs<std::string>();
    +00268     if (!version.empty())
    +00269         version = " version=\"" + normalizeAttribValue(version) + '\"';
    +00270 
    +00271     std::string encoding = doc.getEncodingAs<std::string>();
    +00272     if (!encoding.empty())
    +00273         encoding = " encoding=\"" + normalizeAttribValue(encoding) + '\"';
    +00274 
    +00275     std::string standalone = doc.getStandaloneAs<std::string>();
    +00276     if (!standalone.empty())
    +00277         standalone = " standalone=\"" + normalizeAttribValue(standalone) + '\"';
    +00278 
    +00279     std::string output = "<?xml" + version + encoding + standalone + "?>" + lineBreak;
    +00280     serialize(doc.root(), output, lineBreak, indent, 0);
    +00281     return output;
    +00282 }
    +00283 }
    +00284 }
    +00285 
    +00286 inline
    +00287 std::string serialize(const XmlDoc& doc,
    +00288                       const std::string& lineBreak,
    +00289                       const std::string& indent) { return implementation::serialize(doc, lineBreak, indent); }
    +00290 
    +00291 /*
    +00292 Grammar for XML parser
    +00293 -------------------------------
    +00294 document-expression:
    +00295     <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    +00296     element-expression:
    +00297 
    +00298 element-expression:
    +00299     <string attributes-expression/>
    +00300     <string attributes-expression> pm-expression </string>
    +00301 
    +00302 element-list-expression:
    +00303     <empty>
    +00304     element-expression element-list-expression
    +00305 
    +00306 attributes-expression:
    +00307     <empty>
    +00308     string="string" attributes-expression
    +00309 
    +00310 pm-expression:
    +00311     string
    +00312     element-list-expression
    +00313 */
    +00314 
    +00315 namespace implementation
    +00316 {
    +00317 struct Token
    +00318 {
    +00319     enum Type
    +00320     {
    +00321         TK_LESS,
    +00322         TK_GREATER,
    +00323         TK_LESS_SLASH,
    +00324         TK_SLASH_GREATER,
    +00325         TK_EQUAL,
    +00326         TK_QUOTE,
    +00327         TK_DECL_BEGIN,
    +00328         TK_DECL_END,
    +00329         TK_NAME,
    +00330         TK_END
    +00331     };
    +00332 
    +00333     Token(Type t) : type(t) {}
    +00334     Token(const std::string& txt) : type(TK_NAME), name(txt) {}
    +00335 
    +00336     Type type;
    +00337     std::string name; //filled if type == TK_NAME
    +00338 };
    +00339 
    +00340 class Scanner
    +00341 {
    +00342 public:
    +00343     Scanner(const std::string& stream) : stream_(stream), pos(stream_.begin())
    +00344     {
    +00345         if (zen::startsWith(stream_, BYTE_ORDER_MARK_UTF8))
    +00346             pos += strLength(BYTE_ORDER_MARK_UTF8);
    +00347 
    +00348         tokens.push_back(std::make_pair("<?xml", Token::TK_DECL_BEGIN));
    +00349         tokens.push_back(std::make_pair("?>",    Token::TK_DECL_END));
    +00350         tokens.push_back(std::make_pair("</", Token::TK_LESS_SLASH));
    +00351         tokens.push_back(std::make_pair("/>", Token::TK_SLASH_GREATER));
    +00352         tokens.push_back(std::make_pair("<" , Token::TK_LESS)); //evaluate after TK_DECL_BEGIN!
    +00353         tokens.push_back(std::make_pair(">" , Token::TK_GREATER));
    +00354         tokens.push_back(std::make_pair("=" , Token::TK_EQUAL));
    +00355         tokens.push_back(std::make_pair("\"", Token::TK_QUOTE));
    +00356         tokens.push_back(std::make_pair("\'", Token::TK_QUOTE));
    +00357     }
    +00358 
    +00359     Token nextToken() //throw XmlParsingError
    +00360     {
    +00361         //skip whitespace
    +00362         pos = std::find_if(pos, stream_.end(), [](char c) { return !zen::isWhiteSpace(c); });
    +00363 
    +00364         if (pos == stream_.end())
    +00365             return Token::TK_END;
    +00366 
    +00367         for (auto it = tokens.begin(); it != tokens.end(); ++it)
    +00368             if (startsWith(pos, it->first))
    +00369             {
    +00370                 pos += it->first.size();
    +00371                 return it->second;
    +00372             }
    +00373 
    +00374         auto nameEnd = std::find_if(pos, stream_.end(), [](char c)
    +00375         {
    +00376             return c == '<'  ||
    +00377                    c == '>'  ||
    +00378                    c == '='  ||
    +00379                    c == '/'  ||
    +00380                    c == '\'' ||
    +00381                    c == '\"' ||
    +00382                    zen::isWhiteSpace(c);
    +00383         });
    +00384 
    +00385         if (nameEnd != pos)
    +00386         {
    +00387             std::string name(&*pos, nameEnd - pos);
    +00388             pos = nameEnd;
    +00389             return implementation::denormalize(name);
    +00390         }
    +00391 
    +00392         //unknown token
    +00393         throw XmlParsingError(posRow(), posCol());
    +00394     }
    +00395 
    +00396     std::string extractElementValue()
    +00397     {
    +00398         auto it = std::find_if(pos, stream_.end(), [](char c)
    +00399         {
    +00400             return c == '<'  ||
    +00401                    c == '>';
    +00402         });
    +00403         std::string output(pos, it);
    +00404         pos = it;
    +00405         return implementation::denormalize(output);
    +00406     }
    +00407 
    +00408     std::string extractAttributeValue()
    +00409     {
    +00410         auto it = std::find_if(pos, stream_.end(), [](char c)
    +00411         {
    +00412             return c == '<'  ||
    +00413                    c == '>'  ||
    +00414                    c == '\'' ||
    +00415                    c == '\"';
    +00416         });
    +00417         std::string output(pos, it);
    +00418         pos = it;
    +00419         return implementation::denormalize(output);
    +00420     }
    +00421 
    +00422     size_t posRow() const //current row beginning with 0
    +00423     {
    +00424         const size_t crSum = std::count(stream_.begin(), pos, '\r'); //carriage returns
    +00425         const size_t nlSum = std::count(stream_.begin(), pos, '\n'); //new lines
    +00426         assert(crSum == 0 || nlSum == 0 || crSum == nlSum);
    +00427         return std::max(crSum, nlSum); //be compatible with Linux/Mac/Win
    +00428     }
    +00429 
    +00430     size_t posCol() const //current col beginning with 0
    +00431     {
    +00432         //seek beginning of line
    +00433         for (auto it = pos; it != stream_.begin(); )
    +00434         {
    +00435             --it;
    +00436             if (*it == '\r' || *it == '\n')
    +00437                 return pos - it - 1;
    +00438         }
    +00439         return pos - stream_.begin();
    +00440     }
    +00441 
    +00442 private:
    +00443     Scanner(const Scanner&);
    +00444     Scanner& operator=(const Scanner&);
    +00445 
    +00446     bool startsWith(std::string::const_iterator it, const std::string& prefix) const
    +00447     {
    +00448         if (stream_.end() - it < static_cast<ptrdiff_t>(prefix.size()))
    +00449             return false;
    +00450         return std::equal(prefix.begin(), prefix.end(), it);
    +00451     }
    +00452 
    +00453     typedef std::vector<std::pair<std::string, Token::Type> > TokenList;
    +00454     TokenList tokens;
    +00455 
    +00456     const std::string stream_;
    +00457     std::string::const_iterator pos;
    +00458 };
    +00459 
    +00460 
    +00461 class XmlParser
    +00462 {
    +00463 public:
    +00464     XmlParser(const std::string& stream) :
    +00465         scn(stream),
    +00466         tk(scn.nextToken()) {}
    +00467 
    +00468     void parse(XmlDoc& doc) //throw XmlParsingError
    +00469     {
    +00470         //declaration (optional)
    +00471         if (token().type == Token::TK_DECL_BEGIN)
    +00472         {
    +00473             nextToken();
    +00474 
    +00475             while (token().type == Token::TK_NAME)
    +00476             {
    +00477                 std::string attribName = token().name;
    +00478                 nextToken();
    +00479 
    +00480                 consumeToken(Token::TK_EQUAL);
    +00481                 expectToken(Token::TK_QUOTE);
    +00482                 std::string attribValue = scn.extractAttributeValue();
    +00483                 nextToken();
    +00484 
    +00485                 consumeToken(Token::TK_QUOTE);
    +00486 
    +00487                 if (attribName == "version")
    +00488                     doc.setVersion(attribValue);
    +00489                 else if (attribName == "encoding")
    +00490                     doc.setEncoding(attribValue);
    +00491                 else if (attribName == "standalone")
    +00492                     doc.setStandalone(attribValue);
    +00493             }
    +00494             consumeToken(Token::TK_DECL_END);
    +00495         }
    +00496 
    +00497         XmlDoc dummy;
    +00498         XmlElement& elemTmp = dummy.root();
    +00499         parseChildElements(elemTmp);
    +00500 
    +00501         auto iterPair = elemTmp.getChildren();
    +00502         if (iterPair.first != iterPair.second)
    +00503             doc.root().swap(*iterPair.first);
    +00504 
    +00505         expectToken(Token::TK_END);
    +00506     };
    +00507 
    +00508 private:
    +00509     XmlParser(const XmlParser&);
    +00510     XmlParser& operator=(const XmlParser&);
    +00511 
    +00512     void parseChildElements(XmlElement& parent)
    +00513     {
    +00514         while (token().type == Token::TK_LESS)
    +00515         {
    +00516             nextToken();
    +00517 
    +00518             expectToken(Token::TK_NAME);
    +00519             std::string elementName = token().name;
    +00520             nextToken();
    +00521 
    +00522             XmlElement& newElement = parent.addChild(elementName);
    +00523 
    +00524             parseAttributes(newElement);
    +00525 
    +00526             if (token().type == Token::TK_SLASH_GREATER) //empty element
    +00527             {
    +00528                 nextToken();
    +00529                 continue;
    +00530             }
    +00531 
    +00532             expectToken(Token::TK_GREATER);
    +00533             std::string elementValue = scn.extractElementValue();
    +00534             nextToken();
    +00535 
    +00536             //no support for mixed-mode content
    +00537             if (token().type == Token::TK_LESS) //structured element
    +00538                 parseChildElements(newElement);
    +00539             else //value element
    +00540                 newElement.setValue(elementValue);
    +00541 
    +00542             consumeToken(Token::TK_LESS_SLASH);
    +00543 
    +00544             if (token().type != Token::TK_NAME ||
    +00545                 elementName != token().name)
    +00546                 throw XmlParsingError(scn.posRow(), scn.posCol());
    +00547             nextToken();
    +00548 
    +00549             consumeToken(Token::TK_GREATER);
    +00550         }
    +00551     };
    +00552 
    +00553     void parseAttributes(XmlElement& element)
    +00554     {
    +00555         while (token().type == Token::TK_NAME)
    +00556         {
    +00557             std::string attribName = token().name;
    +00558             nextToken();
    +00559 
    +00560             consumeToken(Token::TK_EQUAL);
    +00561             expectToken(Token::TK_QUOTE);
    +00562             std::string attribValue = scn.extractAttributeValue();
    +00563             nextToken();
    +00564 
    +00565             consumeToken(Token::TK_QUOTE);
    +00566             element.setAttribute(attribName, attribValue);
    +00567         }
    +00568     }
    +00569 
    +00570     const Token& token() const { return tk; }
    +00571     void nextToken() { tk = scn.nextToken(); }
    +00572 
    +00573     void consumeToken(Token::Type t) //throw XmlParsingError
    +00574     {
    +00575         expectToken(t); //throw XmlParsingError
    +00576         nextToken();
    +00577     }
    +00578 
    +00579     void expectToken(Token::Type t) //throw XmlParsingError
    +00580     {
    +00581         if (token().type != t)
    +00582             throw XmlParsingError(scn.posRow(), scn.posCol());
    +00583     }
    +00584 
    +00585     Scanner scn;
    +00586     Token tk;
    +00587 };
    +00588 }
    +00589 
    +00590 inline
    +00591 void parse(const std::string& stream, XmlDoc& doc) //throw XmlParsingError
    +00592 {
    +00593     implementation::XmlParser(stream).parse(doc);  //throw XmlParsingError
    +00594 }
    +00595 }
    +00596 
    +00597 #endif //ZEN_XML_PARSER_HEADER_81248670213764583021432
    +
    + + + + + + diff --git a/zenxml/doc/search/all_61.html b/zenxml/doc/search/all_61.html new file mode 100644 index 00000000..a3164d55 --- /dev/null +++ b/zenxml/doc/search/all_61.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/all_61.js b/zenxml/doc/search/all_61.js new file mode 100644 index 00000000..2b7cd296 --- /dev/null +++ b/zenxml/doc/search/all_61.js @@ -0,0 +1,5 @@ +var searchData= +[ + ['addchild',['addChild',['../classzen_1_1_xml_element.html#a653caffa6fad89db7d14f67f987ad0f9',1,'zen::XmlElement']]], + ['attribute',['attribute',['../classzen_1_1_xml_out.html#acaf9b71fe1d907dd63dd4b91e2e03805',1,'zen::XmlOut::attribute()'],['../classzen_1_1_xml_in.html#a971cd7054c551c4460d5220f6ec5cf01',1,'zen::XmlIn::attribute()']]] +]; diff --git a/zenxml/doc/search/all_63.html b/zenxml/doc/search/all_63.html new file mode 100644 index 00000000..56b5ad1e --- /dev/null +++ b/zenxml/doc/search/all_63.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/all_63.js b/zenxml/doc/search/all_63.js new file mode 100644 index 00000000..3c8f3573 --- /dev/null +++ b/zenxml/doc/search/all_63.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['col',['col',['../structzen_1_1_xml_parsing_error.html#a4a37dc48883337499804a9dc791669fd',1,'zen::XmlParsingError']]] +]; diff --git a/zenxml/doc/search/all_65.html b/zenxml/doc/search/all_65.html new file mode 100644 index 00000000..66cc8348 --- /dev/null +++ b/zenxml/doc/search/all_65.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/all_65.js b/zenxml/doc/search/all_65.js new file mode 100644 index 00000000..46ba186a --- /dev/null +++ b/zenxml/doc/search/all_65.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['errorsoccured',['errorsOccured',['../classzen_1_1_xml_in.html#a33b5dd504d3165aa3f923f6b33e9991a',1,'zen::XmlIn']]] +]; diff --git a/zenxml/doc/search/all_67.html b/zenxml/doc/search/all_67.html new file mode 100644 index 00000000..41a459ae --- /dev/null +++ b/zenxml/doc/search/all_67.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/all_67.js b/zenxml/doc/search/all_67.js new file mode 100644 index 00000000..029444b8 --- /dev/null +++ b/zenxml/doc/search/all_67.js @@ -0,0 +1,13 @@ +var searchData= +[ + ['get',['get',['../classzen_1_1_xml_in.html#a647b468b7c6c95b25f2e43627184177f',1,'zen::XmlIn']]], + ['getattribute',['getAttribute',['../classzen_1_1_xml_element.html#af90ac6f435b03ba37cf47ee188c58887',1,'zen::XmlElement']]], + ['getchild',['getChild',['../classzen_1_1_xml_element.html#a3ab82b1720460487f4afabcd115d0c7e',1,'zen::XmlElement::getChild(const String &name) const '],['../classzen_1_1_xml_element.html#a5d672e8ccc7592442ab927bb267af658',1,'zen::XmlElement::getChild(const String &name)']]], + ['getchildren',['getChildren',['../classzen_1_1_xml_element.html#a2640b438c4984f5eeb8760d82d73c5b8',1,'zen::XmlElement::getChildren(const String &name) const '],['../classzen_1_1_xml_element.html#ae209dac9655bc36121abb87688ece41d',1,'zen::XmlElement::getChildren(const String &name)'],['../classzen_1_1_xml_element.html#a55a6d1849490d82ae900cd9b923908f2',1,'zen::XmlElement::getChildren() const '],['../classzen_1_1_xml_element.html#ac59268177d162931f937b6a7f235ad96',1,'zen::XmlElement::getChildren()']]], + ['getencodingas',['getEncodingAs',['../classzen_1_1_xml_doc.html#a64ece4a1f3f8c802192b8f31506535da',1,'zen::XmlDoc']]], + ['geterrorsas',['getErrorsAs',['../classzen_1_1_xml_in.html#a84bb497d3b3fc753d054e52c4823c05e',1,'zen::XmlIn']]], + ['getnameas',['getNameAs',['../classzen_1_1_xml_element.html#a7c911eb06a59c864197b1a4098728e50',1,'zen::XmlElement']]], + ['getstandaloneas',['getStandaloneAs',['../classzen_1_1_xml_doc.html#ac1bfb9776852dc8195b9ffb4f65452e4',1,'zen::XmlDoc']]], + ['getvalue',['getValue',['../classzen_1_1_xml_element.html#a5ac9d586a5668c2c64e3c06c6203b070',1,'zen::XmlElement']]], + ['getversionas',['getVersionAs',['../classzen_1_1_xml_doc.html#a7f93dcdc00cdc8d98926cf8e47161665',1,'zen::XmlDoc']]] +]; diff --git a/zenxml/doc/search/all_6c.html b/zenxml/doc/search/all_6c.html new file mode 100644 index 00000000..f6383cc2 --- /dev/null +++ b/zenxml/doc/search/all_6c.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/all_6c.js b/zenxml/doc/search/all_6c.js new file mode 100644 index 00000000..0e6b8f02 --- /dev/null +++ b/zenxml/doc/search/all_6c.js @@ -0,0 +1,6 @@ +var searchData= +[ + ['lasterror',['lastError',['../structzen_1_1_xml_file_error.html#a4a109e749675a3887af8cfc140303b8f',1,'zen::XmlFileError']]], + ['load',['load',['../namespacezen.html#a900c1fb290f0eedc24354c487145dbee',1,'zen']]], + ['loadstream',['loadStream',['../namespacezen.html#a04fe23c3bd9b7d03309620b5ea763607',1,'zen']]] +]; diff --git a/zenxml/doc/search/all_6e.html b/zenxml/doc/search/all_6e.html new file mode 100644 index 00000000..1f92ee5b --- /dev/null +++ b/zenxml/doc/search/all_6e.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/all_6e.js b/zenxml/doc/search/all_6e.js new file mode 100644 index 00000000..250e97da --- /dev/null +++ b/zenxml/doc/search/all_6e.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['next',['next',['../classzen_1_1_xml_in.html#a60cf2678c989621545d27745dcafa4a4',1,'zen::XmlIn']]] +]; diff --git a/zenxml/doc/search/all_6f.html b/zenxml/doc/search/all_6f.html new file mode 100644 index 00000000..61827e82 --- /dev/null +++ b/zenxml/doc/search/all_6f.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/all_6f.js b/zenxml/doc/search/all_6f.js new file mode 100644 index 00000000..4152ba50 --- /dev/null +++ b/zenxml/doc/search/all_6f.js @@ -0,0 +1,6 @@ +var searchData= +[ + ['_2a',['*',['../classzen_1_1_xml_in.html#a954950849b52557369932ab2a8b4ad72',1,'zen::XmlIn']]], + ['operator_28_29',['operator()',['../classzen_1_1_xml_out.html#a09ca9144515e3c109b36062b0475c8eb',1,'zen::XmlOut::operator()()'],['../classzen_1_1_xml_in.html#a98cc59f687c89549381e76105f8fb506',1,'zen::XmlIn::operator()()']]], + ['operator_5b_5d',['operator[]',['../classzen_1_1_xml_out.html#a00f883d3f9d60535b06b5ae609dc8831',1,'zen::XmlOut::operator[]()'],['../classzen_1_1_xml_in.html#a9b38167835a28eac9a2297f35f51e53d',1,'zen::XmlIn::operator[]()']]] +]; diff --git a/zenxml/doc/search/all_70.html b/zenxml/doc/search/all_70.html new file mode 100644 index 00000000..0340151b --- /dev/null +++ b/zenxml/doc/search/all_70.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/all_70.js b/zenxml/doc/search/all_70.js new file mode 100644 index 00000000..b293cd99 --- /dev/null +++ b/zenxml/doc/search/all_70.js @@ -0,0 +1,5 @@ +var searchData= +[ + ['parent',['parent',['../classzen_1_1_xml_element.html#a4af309f59ef09f46a559f1f0e1eac6c1',1,'zen::XmlElement::parent()'],['../classzen_1_1_xml_element.html#a7ba1f26be5629f89ba7648d658f7058c',1,'zen::XmlElement::parent() const ']]], + ['parse',['parse',['../namespacezen.html#a6cf1ec0b57fc1ae9aa95761800e67ec7',1,'zen']]] +]; diff --git a/zenxml/doc/search/all_72.html b/zenxml/doc/search/all_72.html new file mode 100644 index 00000000..0ab18d65 --- /dev/null +++ b/zenxml/doc/search/all_72.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/all_72.js b/zenxml/doc/search/all_72.js new file mode 100644 index 00000000..86fadf65 --- /dev/null +++ b/zenxml/doc/search/all_72.js @@ -0,0 +1,9 @@ +var searchData= +[ + ['readstruc',['readStruc',['../namespacezen.html#a2bdcecfe7435ef11cedbce47d4e72ee1',1,'zen']]], + ['readtext',['readText',['../namespacezen.html#acaf85ab94b61882f957afcd355386bff',1,'zen']]], + ['ref',['ref',['../classzen_1_1_xml_out.html#aec117344e8a534382e8d5e76711f97b2',1,'zen::XmlOut']]], + ['removeattribute',['removeAttribute',['../classzen_1_1_xml_element.html#ad9c2ce2e55294c8110825988595e3934',1,'zen::XmlElement']]], + ['root',['root',['../classzen_1_1_xml_doc.html#ad4a9594d93885fc1a12db28e8246648d',1,'zen::XmlDoc::root() const '],['../classzen_1_1_xml_doc.html#a094e156f9d265443e52a527638e88a1e',1,'zen::XmlDoc::root()']]], + ['row',['row',['../structzen_1_1_xml_parsing_error.html#a3ed4cd1b5599df9b52500f620421496e',1,'zen::XmlParsingError']]] +]; diff --git a/zenxml/doc/search/all_73.html b/zenxml/doc/search/all_73.html new file mode 100644 index 00000000..1ec8f174 --- /dev/null +++ b/zenxml/doc/search/all_73.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/all_73.js b/zenxml/doc/search/all_73.js new file mode 100644 index 00000000..a062befe --- /dev/null +++ b/zenxml/doc/search/all_73.js @@ -0,0 +1,11 @@ +var searchData= +[ + ['save',['save',['../namespacezen.html#adeeb6b2318097382ae47aa939fc15d4d',1,'zen']]], + ['savestream',['saveStream',['../namespacezen.html#a4ba7bbaa14a787b07fc13da9145aabe2',1,'zen']]], + ['serialize',['serialize',['../namespacezen.html#afaa4920e275078e6c8009fbdf58b57ee',1,'zen']]], + ['setattribute',['setAttribute',['../classzen_1_1_xml_element.html#a211a6f037c22a54d3facb7a8347a8421',1,'zen::XmlElement']]], + ['setencoding',['setEncoding',['../classzen_1_1_xml_doc.html#a2ae30bca2f490479f58c272148935a62',1,'zen::XmlDoc']]], + ['setstandalone',['setStandalone',['../classzen_1_1_xml_doc.html#a4c92f9b8c1bb47247b827d89794590d4',1,'zen::XmlDoc']]], + ['setvalue',['setValue',['../classzen_1_1_xml_element.html#aaf3a26f6199fc88cce7d9d911ba21b01',1,'zen::XmlElement']]], + ['setversion',['setVersion',['../classzen_1_1_xml_doc.html#ab45914339c476e1da35746f5e00dbc64',1,'zen::XmlDoc']]] +]; diff --git a/zenxml/doc/search/all_77.html b/zenxml/doc/search/all_77.html new file mode 100644 index 00000000..73323d31 --- /dev/null +++ b/zenxml/doc/search/all_77.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/all_77.js b/zenxml/doc/search/all_77.js new file mode 100644 index 00000000..5e349623 --- /dev/null +++ b/zenxml/doc/search/all_77.js @@ -0,0 +1,5 @@ +var searchData= +[ + ['writestruc',['writeStruc',['../namespacezen.html#a29ddb823fe0a195f19a64448881b8bf6',1,'zen']]], + ['writetext',['writeText',['../namespacezen.html#a2ce2998296871fc2f4718ceceb22a23f',1,'zen']]] +]; diff --git a/zenxml/doc/search/all_78.html b/zenxml/doc/search/all_78.html new file mode 100644 index 00000000..10780d66 --- /dev/null +++ b/zenxml/doc/search/all_78.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/all_78.js b/zenxml/doc/search/all_78.js new file mode 100644 index 00000000..371f5dde --- /dev/null +++ b/zenxml/doc/search/all_78.js @@ -0,0 +1,13 @@ +var searchData= +[ + ['xmldoc',['XmlDoc',['../classzen_1_1_xml_doc.html',1,'zen']]], + ['xmldoc',['XmlDoc',['../classzen_1_1_xml_doc.html#adbed9c31066d456a9cc8c610f15670ed',1,'zen::XmlDoc::XmlDoc()'],['../classzen_1_1_xml_doc.html#a74ff8434848672fe6483845d04c082df',1,'zen::XmlDoc::XmlDoc(String rootName)']]], + ['xmlelement',['XmlElement',['../classzen_1_1_xml_element.html',1,'zen']]], + ['xmlerror',['XmlError',['../structzen_1_1_xml_error.html',1,'zen']]], + ['xmlfileerror',['XmlFileError',['../structzen_1_1_xml_file_error.html',1,'zen']]], + ['xmlin',['XmlIn',['../classzen_1_1_xml_in.html#a5b48c9848e6c631a04cec2477ff85c0f',1,'zen::XmlIn::XmlIn(const XmlDoc &doc)'],['../classzen_1_1_xml_in.html#ae072660cde71fd4695c04d074098b430',1,'zen::XmlIn::XmlIn(const XmlElement *element)'],['../classzen_1_1_xml_in.html#a33de75412df69cb25e0fd8b3bc70c9f8',1,'zen::XmlIn::XmlIn(const XmlElement &element)']]], + ['xmlin',['XmlIn',['../classzen_1_1_xml_in.html',1,'zen']]], + ['xmlout',['XmlOut',['../classzen_1_1_xml_out.html',1,'zen']]], + ['xmlout',['XmlOut',['../classzen_1_1_xml_out.html#ad8b1ccb8f3d4e7b0ab2598597ea50bcc',1,'zen::XmlOut::XmlOut(XmlDoc &doc)'],['../classzen_1_1_xml_out.html#aa80be3a56f70a58d2730a763166088c0',1,'zen::XmlOut::XmlOut(XmlElement &element)']]], + ['xmlparsingerror',['XmlParsingError',['../structzen_1_1_xml_parsing_error.html',1,'zen']]] +]; diff --git a/zenxml/doc/search/all_7a.html b/zenxml/doc/search/all_7a.html new file mode 100644 index 00000000..0593a62d --- /dev/null +++ b/zenxml/doc/search/all_7a.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/all_7a.js b/zenxml/doc/search/all_7a.js new file mode 100644 index 00000000..14074229 --- /dev/null +++ b/zenxml/doc/search/all_7a.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['zen',['zen',['../namespacezen.html',1,'']]] +]; diff --git a/zenxml/doc/search/classes_78.html b/zenxml/doc/search/classes_78.html new file mode 100644 index 00000000..dfce8ecc --- /dev/null +++ b/zenxml/doc/search/classes_78.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/classes_78.js b/zenxml/doc/search/classes_78.js new file mode 100644 index 00000000..fa78dcd3 --- /dev/null +++ b/zenxml/doc/search/classes_78.js @@ -0,0 +1,10 @@ +var searchData= +[ + ['xmldoc',['XmlDoc',['../classzen_1_1_xml_doc.html',1,'zen']]], + ['xmlelement',['XmlElement',['../classzen_1_1_xml_element.html',1,'zen']]], + ['xmlerror',['XmlError',['../structzen_1_1_xml_error.html',1,'zen']]], + ['xmlfileerror',['XmlFileError',['../structzen_1_1_xml_file_error.html',1,'zen']]], + ['xmlin',['XmlIn',['../classzen_1_1_xml_in.html',1,'zen']]], + ['xmlout',['XmlOut',['../classzen_1_1_xml_out.html',1,'zen']]], + ['xmlparsingerror',['XmlParsingError',['../structzen_1_1_xml_parsing_error.html',1,'zen']]] +]; diff --git a/zenxml/doc/search/close.png b/zenxml/doc/search/close.png new file mode 100644 index 00000000..9342d3df Binary files /dev/null and b/zenxml/doc/search/close.png differ diff --git a/zenxml/doc/search/functions_61.html b/zenxml/doc/search/functions_61.html new file mode 100644 index 00000000..7f395337 --- /dev/null +++ b/zenxml/doc/search/functions_61.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/functions_61.js b/zenxml/doc/search/functions_61.js new file mode 100644 index 00000000..2b7cd296 --- /dev/null +++ b/zenxml/doc/search/functions_61.js @@ -0,0 +1,5 @@ +var searchData= +[ + ['addchild',['addChild',['../classzen_1_1_xml_element.html#a653caffa6fad89db7d14f67f987ad0f9',1,'zen::XmlElement']]], + ['attribute',['attribute',['../classzen_1_1_xml_out.html#acaf9b71fe1d907dd63dd4b91e2e03805',1,'zen::XmlOut::attribute()'],['../classzen_1_1_xml_in.html#a971cd7054c551c4460d5220f6ec5cf01',1,'zen::XmlIn::attribute()']]] +]; diff --git a/zenxml/doc/search/functions_65.html b/zenxml/doc/search/functions_65.html new file mode 100644 index 00000000..a77debae --- /dev/null +++ b/zenxml/doc/search/functions_65.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/functions_65.js b/zenxml/doc/search/functions_65.js new file mode 100644 index 00000000..46ba186a --- /dev/null +++ b/zenxml/doc/search/functions_65.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['errorsoccured',['errorsOccured',['../classzen_1_1_xml_in.html#a33b5dd504d3165aa3f923f6b33e9991a',1,'zen::XmlIn']]] +]; diff --git a/zenxml/doc/search/functions_67.html b/zenxml/doc/search/functions_67.html new file mode 100644 index 00000000..d0ab42a3 --- /dev/null +++ b/zenxml/doc/search/functions_67.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/functions_67.js b/zenxml/doc/search/functions_67.js new file mode 100644 index 00000000..029444b8 --- /dev/null +++ b/zenxml/doc/search/functions_67.js @@ -0,0 +1,13 @@ +var searchData= +[ + ['get',['get',['../classzen_1_1_xml_in.html#a647b468b7c6c95b25f2e43627184177f',1,'zen::XmlIn']]], + ['getattribute',['getAttribute',['../classzen_1_1_xml_element.html#af90ac6f435b03ba37cf47ee188c58887',1,'zen::XmlElement']]], + ['getchild',['getChild',['../classzen_1_1_xml_element.html#a3ab82b1720460487f4afabcd115d0c7e',1,'zen::XmlElement::getChild(const String &name) const '],['../classzen_1_1_xml_element.html#a5d672e8ccc7592442ab927bb267af658',1,'zen::XmlElement::getChild(const String &name)']]], + ['getchildren',['getChildren',['../classzen_1_1_xml_element.html#a2640b438c4984f5eeb8760d82d73c5b8',1,'zen::XmlElement::getChildren(const String &name) const '],['../classzen_1_1_xml_element.html#ae209dac9655bc36121abb87688ece41d',1,'zen::XmlElement::getChildren(const String &name)'],['../classzen_1_1_xml_element.html#a55a6d1849490d82ae900cd9b923908f2',1,'zen::XmlElement::getChildren() const '],['../classzen_1_1_xml_element.html#ac59268177d162931f937b6a7f235ad96',1,'zen::XmlElement::getChildren()']]], + ['getencodingas',['getEncodingAs',['../classzen_1_1_xml_doc.html#a64ece4a1f3f8c802192b8f31506535da',1,'zen::XmlDoc']]], + ['geterrorsas',['getErrorsAs',['../classzen_1_1_xml_in.html#a84bb497d3b3fc753d054e52c4823c05e',1,'zen::XmlIn']]], + ['getnameas',['getNameAs',['../classzen_1_1_xml_element.html#a7c911eb06a59c864197b1a4098728e50',1,'zen::XmlElement']]], + ['getstandaloneas',['getStandaloneAs',['../classzen_1_1_xml_doc.html#ac1bfb9776852dc8195b9ffb4f65452e4',1,'zen::XmlDoc']]], + ['getvalue',['getValue',['../classzen_1_1_xml_element.html#a5ac9d586a5668c2c64e3c06c6203b070',1,'zen::XmlElement']]], + ['getversionas',['getVersionAs',['../classzen_1_1_xml_doc.html#a7f93dcdc00cdc8d98926cf8e47161665',1,'zen::XmlDoc']]] +]; diff --git a/zenxml/doc/search/functions_6c.html b/zenxml/doc/search/functions_6c.html new file mode 100644 index 00000000..da371cfa --- /dev/null +++ b/zenxml/doc/search/functions_6c.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/functions_6c.js b/zenxml/doc/search/functions_6c.js new file mode 100644 index 00000000..93c6b2bd --- /dev/null +++ b/zenxml/doc/search/functions_6c.js @@ -0,0 +1,5 @@ +var searchData= +[ + ['load',['load',['../namespacezen.html#a900c1fb290f0eedc24354c487145dbee',1,'zen']]], + ['loadstream',['loadStream',['../namespacezen.html#a04fe23c3bd9b7d03309620b5ea763607',1,'zen']]] +]; diff --git a/zenxml/doc/search/functions_6e.html b/zenxml/doc/search/functions_6e.html new file mode 100644 index 00000000..d734dd07 --- /dev/null +++ b/zenxml/doc/search/functions_6e.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/functions_6e.js b/zenxml/doc/search/functions_6e.js new file mode 100644 index 00000000..250e97da --- /dev/null +++ b/zenxml/doc/search/functions_6e.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['next',['next',['../classzen_1_1_xml_in.html#a60cf2678c989621545d27745dcafa4a4',1,'zen::XmlIn']]] +]; diff --git a/zenxml/doc/search/functions_6f.html b/zenxml/doc/search/functions_6f.html new file mode 100644 index 00000000..222f0f83 --- /dev/null +++ b/zenxml/doc/search/functions_6f.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/functions_6f.js b/zenxml/doc/search/functions_6f.js new file mode 100644 index 00000000..4152ba50 --- /dev/null +++ b/zenxml/doc/search/functions_6f.js @@ -0,0 +1,6 @@ +var searchData= +[ + ['_2a',['*',['../classzen_1_1_xml_in.html#a954950849b52557369932ab2a8b4ad72',1,'zen::XmlIn']]], + ['operator_28_29',['operator()',['../classzen_1_1_xml_out.html#a09ca9144515e3c109b36062b0475c8eb',1,'zen::XmlOut::operator()()'],['../classzen_1_1_xml_in.html#a98cc59f687c89549381e76105f8fb506',1,'zen::XmlIn::operator()()']]], + ['operator_5b_5d',['operator[]',['../classzen_1_1_xml_out.html#a00f883d3f9d60535b06b5ae609dc8831',1,'zen::XmlOut::operator[]()'],['../classzen_1_1_xml_in.html#a9b38167835a28eac9a2297f35f51e53d',1,'zen::XmlIn::operator[]()']]] +]; diff --git a/zenxml/doc/search/functions_70.html b/zenxml/doc/search/functions_70.html new file mode 100644 index 00000000..c62125ed --- /dev/null +++ b/zenxml/doc/search/functions_70.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/functions_70.js b/zenxml/doc/search/functions_70.js new file mode 100644 index 00000000..b293cd99 --- /dev/null +++ b/zenxml/doc/search/functions_70.js @@ -0,0 +1,5 @@ +var searchData= +[ + ['parent',['parent',['../classzen_1_1_xml_element.html#a4af309f59ef09f46a559f1f0e1eac6c1',1,'zen::XmlElement::parent()'],['../classzen_1_1_xml_element.html#a7ba1f26be5629f89ba7648d658f7058c',1,'zen::XmlElement::parent() const ']]], + ['parse',['parse',['../namespacezen.html#a6cf1ec0b57fc1ae9aa95761800e67ec7',1,'zen']]] +]; diff --git a/zenxml/doc/search/functions_72.html b/zenxml/doc/search/functions_72.html new file mode 100644 index 00000000..a4336f7c --- /dev/null +++ b/zenxml/doc/search/functions_72.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/functions_72.js b/zenxml/doc/search/functions_72.js new file mode 100644 index 00000000..86b2b142 --- /dev/null +++ b/zenxml/doc/search/functions_72.js @@ -0,0 +1,8 @@ +var searchData= +[ + ['readstruc',['readStruc',['../namespacezen.html#a2bdcecfe7435ef11cedbce47d4e72ee1',1,'zen']]], + ['readtext',['readText',['../namespacezen.html#acaf85ab94b61882f957afcd355386bff',1,'zen']]], + ['ref',['ref',['../classzen_1_1_xml_out.html#aec117344e8a534382e8d5e76711f97b2',1,'zen::XmlOut']]], + ['removeattribute',['removeAttribute',['../classzen_1_1_xml_element.html#ad9c2ce2e55294c8110825988595e3934',1,'zen::XmlElement']]], + ['root',['root',['../classzen_1_1_xml_doc.html#ad4a9594d93885fc1a12db28e8246648d',1,'zen::XmlDoc::root() const '],['../classzen_1_1_xml_doc.html#a094e156f9d265443e52a527638e88a1e',1,'zen::XmlDoc::root()']]] +]; diff --git a/zenxml/doc/search/functions_73.html b/zenxml/doc/search/functions_73.html new file mode 100644 index 00000000..774d577f --- /dev/null +++ b/zenxml/doc/search/functions_73.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/functions_73.js b/zenxml/doc/search/functions_73.js new file mode 100644 index 00000000..a062befe --- /dev/null +++ b/zenxml/doc/search/functions_73.js @@ -0,0 +1,11 @@ +var searchData= +[ + ['save',['save',['../namespacezen.html#adeeb6b2318097382ae47aa939fc15d4d',1,'zen']]], + ['savestream',['saveStream',['../namespacezen.html#a4ba7bbaa14a787b07fc13da9145aabe2',1,'zen']]], + ['serialize',['serialize',['../namespacezen.html#afaa4920e275078e6c8009fbdf58b57ee',1,'zen']]], + ['setattribute',['setAttribute',['../classzen_1_1_xml_element.html#a211a6f037c22a54d3facb7a8347a8421',1,'zen::XmlElement']]], + ['setencoding',['setEncoding',['../classzen_1_1_xml_doc.html#a2ae30bca2f490479f58c272148935a62',1,'zen::XmlDoc']]], + ['setstandalone',['setStandalone',['../classzen_1_1_xml_doc.html#a4c92f9b8c1bb47247b827d89794590d4',1,'zen::XmlDoc']]], + ['setvalue',['setValue',['../classzen_1_1_xml_element.html#aaf3a26f6199fc88cce7d9d911ba21b01',1,'zen::XmlElement']]], + ['setversion',['setVersion',['../classzen_1_1_xml_doc.html#ab45914339c476e1da35746f5e00dbc64',1,'zen::XmlDoc']]] +]; diff --git a/zenxml/doc/search/functions_77.html b/zenxml/doc/search/functions_77.html new file mode 100644 index 00000000..8fe97554 --- /dev/null +++ b/zenxml/doc/search/functions_77.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/functions_77.js b/zenxml/doc/search/functions_77.js new file mode 100644 index 00000000..5e349623 --- /dev/null +++ b/zenxml/doc/search/functions_77.js @@ -0,0 +1,5 @@ +var searchData= +[ + ['writestruc',['writeStruc',['../namespacezen.html#a29ddb823fe0a195f19a64448881b8bf6',1,'zen']]], + ['writetext',['writeText',['../namespacezen.html#a2ce2998296871fc2f4718ceceb22a23f',1,'zen']]] +]; diff --git a/zenxml/doc/search/functions_78.html b/zenxml/doc/search/functions_78.html new file mode 100644 index 00000000..2b33d25e --- /dev/null +++ b/zenxml/doc/search/functions_78.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/functions_78.js b/zenxml/doc/search/functions_78.js new file mode 100644 index 00000000..b9817184 --- /dev/null +++ b/zenxml/doc/search/functions_78.js @@ -0,0 +1,6 @@ +var searchData= +[ + ['xmldoc',['XmlDoc',['../classzen_1_1_xml_doc.html#adbed9c31066d456a9cc8c610f15670ed',1,'zen::XmlDoc::XmlDoc()'],['../classzen_1_1_xml_doc.html#a74ff8434848672fe6483845d04c082df',1,'zen::XmlDoc::XmlDoc(String rootName)']]], + ['xmlin',['XmlIn',['../classzen_1_1_xml_in.html#a5b48c9848e6c631a04cec2477ff85c0f',1,'zen::XmlIn::XmlIn(const XmlDoc &doc)'],['../classzen_1_1_xml_in.html#ae072660cde71fd4695c04d074098b430',1,'zen::XmlIn::XmlIn(const XmlElement *element)'],['../classzen_1_1_xml_in.html#a33de75412df69cb25e0fd8b3bc70c9f8',1,'zen::XmlIn::XmlIn(const XmlElement &element)']]], + ['xmlout',['XmlOut',['../classzen_1_1_xml_out.html#ad8b1ccb8f3d4e7b0ab2598597ea50bcc',1,'zen::XmlOut::XmlOut(XmlDoc &doc)'],['../classzen_1_1_xml_out.html#aa80be3a56f70a58d2730a763166088c0',1,'zen::XmlOut::XmlOut(XmlElement &element)']]] +]; diff --git a/zenxml/doc/search/mag_sel.png b/zenxml/doc/search/mag_sel.png new file mode 100644 index 00000000..81f6040a Binary files /dev/null and b/zenxml/doc/search/mag_sel.png differ diff --git a/zenxml/doc/search/namespaces_7a.html b/zenxml/doc/search/namespaces_7a.html new file mode 100644 index 00000000..c8f3629d --- /dev/null +++ b/zenxml/doc/search/namespaces_7a.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/namespaces_7a.js b/zenxml/doc/search/namespaces_7a.js new file mode 100644 index 00000000..14074229 --- /dev/null +++ b/zenxml/doc/search/namespaces_7a.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['zen',['zen',['../namespacezen.html',1,'']]] +]; diff --git a/zenxml/doc/search/nomatches.html b/zenxml/doc/search/nomatches.html new file mode 100644 index 00000000..b1ded27e --- /dev/null +++ b/zenxml/doc/search/nomatches.html @@ -0,0 +1,12 @@ + + + + + + + +
    +
    No Matches
    +
    + + diff --git a/zenxml/doc/search/search.css b/zenxml/doc/search/search.css new file mode 100644 index 00000000..d18c1da8 --- /dev/null +++ b/zenxml/doc/search/search.css @@ -0,0 +1,238 @@ +/*---------------- Search Box */ + +#FSearchBox { + float: left; +} + +#MSearchBox { + white-space : nowrap; + position: absolute; + float: none; + display: inline; + margin-top: 8px; + right: 0px; + width: 170px; + z-index: 102; + background-color: white; +} + +#MSearchBox .left +{ + display:block; + position:absolute; + left:10px; + width:20px; + height:19px; + background:url('search_l.png') no-repeat; + background-position:right; +} + +#MSearchSelect { + display:block; + position:absolute; + width:20px; + height:19px; +} + +.left #MSearchSelect { + left:4px; +} + +.right #MSearchSelect { + right:5px; +} + +#MSearchField { + display:block; + position:absolute; + height:19px; + background:url('search_m.png') repeat-x; + border:none; + width:116px; + margin-left:20px; + padding-left:4px; + color: #909090; + outline: none; + font: 9pt Arial, Verdana, sans-serif; +} + +#FSearchBox #MSearchField { + margin-left:15px; +} + +#MSearchBox .right { + display:block; + position:absolute; + right:10px; + top:0px; + width:20px; + height:19px; + background:url('search_r.png') no-repeat; + background-position:left; +} + +#MSearchClose { + display: none; + position: absolute; + top: 4px; + background : none; + border: none; + margin: 0px 4px 0px 0px; + padding: 0px 0px; + outline: none; +} + +.left #MSearchClose { + left: 6px; +} + +.right #MSearchClose { + right: 2px; +} + +.MSearchBoxActive #MSearchField { + color: #000000; +} + +/*---------------- Search filter selection */ + +#MSearchSelectWindow { + display: none; + position: absolute; + left: 0; top: 0; + border: 1px solid #90A5CE; + background-color: #F9FAFC; + z-index: 1; + padding-top: 4px; + padding-bottom: 4px; + -moz-border-radius: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); +} + +.SelectItem { + font: 8pt Arial, Verdana, sans-serif; + padding-left: 2px; + padding-right: 12px; + border: 0px; +} + +span.SelectionMark { + margin-right: 4px; + font-family: monospace; + outline-style: none; + text-decoration: none; +} + +a.SelectItem { + display: block; + outline-style: none; + color: #000000; + text-decoration: none; + padding-left: 6px; + padding-right: 12px; +} + +a.SelectItem:focus, +a.SelectItem:active { + color: #000000; + outline-style: none; + text-decoration: none; +} + +a.SelectItem:hover { + color: #FFFFFF; + background-color: #3D578C; + outline-style: none; + text-decoration: none; + cursor: pointer; + display: block; +} + +/*---------------- Search results window */ + +iframe#MSearchResults { + width: 60ex; + height: 15em; +} + +#MSearchResultsWindow { + display: none; + position: absolute; + left: 0; top: 0; + border: 1px solid #000; + background-color: #EEF1F7; +} + +/* ----------------------------------- */ + + +#SRIndex { + clear:both; + padding-bottom: 15px; +} + +.SREntry { + font-size: 10pt; + padding-left: 1ex; +} + +.SRPage .SREntry { + font-size: 8pt; + padding: 1px 5px; +} + +body.SRPage { + margin: 5px 2px; +} + +.SRChildren { + padding-left: 3ex; padding-bottom: .5em +} + +.SRPage .SRChildren { + display: none; +} + +.SRSymbol { + font-weight: bold; + color: #425E97; + font-family: Arial, Verdana, sans-serif; + text-decoration: none; + outline: none; +} + +a.SRScope { + display: block; + color: #425E97; + font-family: Arial, Verdana, sans-serif; + text-decoration: none; + outline: none; +} + +a.SRSymbol:focus, a.SRSymbol:active, +a.SRScope:focus, a.SRScope:active { + text-decoration: underline; +} + +span.SRScope { + padding-left: 4px; +} + +.SRPage .SRStatus { + padding: 2px 5px; + font-size: 8pt; + font-style: italic; +} + +.SRResult { + display: none; +} + +DIV.searchresults { + margin-left: 10px; + margin-right: 10px; +} diff --git a/zenxml/doc/search/search.js b/zenxml/doc/search/search.js new file mode 100644 index 00000000..e2269bf9 --- /dev/null +++ b/zenxml/doc/search/search.js @@ -0,0 +1,803 @@ +// Search script generated by doxygen +// Copyright (C) 2009 by Dimitri van Heesch. + +// The code in this file is loosly based on main.js, part of Natural Docs, +// which is Copyright (C) 2003-2008 Greg Valure +// Natural Docs is licensed under the GPL. + +var indexSectionsWithContent = +{ + 0: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101010100001011101100011010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + 1: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + 2: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + 3: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010100001011101100011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + 4: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000001000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +}; + +var indexSectionNames = +{ + 0: "all", + 1: "classes", + 2: "namespaces", + 3: "functions", + 4: "variables" +}; + +function convertToId(search) +{ + var result = ''; + for (i=0;i do a search + { + this.Search(); + } + } + + this.OnSearchSelectKey = function(evt) + { + var e = (evt) ? evt : window.event; // for IE + if (e.keyCode==40 && this.searchIndex0) // Up + { + this.searchIndex--; + this.OnSelectItem(this.searchIndex); + } + else if (e.keyCode==13 || e.keyCode==27) + { + this.OnSelectItem(this.searchIndex); + this.CloseSelectionWindow(); + this.DOMSearchField().focus(); + } + return false; + } + + // --------- Actions + + // Closes the results window. + this.CloseResultsWindow = function() + { + this.DOMPopupSearchResultsWindow().style.display = 'none'; + this.DOMSearchClose().style.display = 'none'; + this.Activate(false); + } + + this.CloseSelectionWindow = function() + { + this.DOMSearchSelectWindow().style.display = 'none'; + } + + // Performs a search. + this.Search = function() + { + this.keyTimeout = 0; + + // strip leading whitespace + var searchValue = this.DOMSearchField().value.replace(/^ +/, ""); + + var code = searchValue.toLowerCase().charCodeAt(0); + var hexCode; + if (code<16) + { + hexCode="0"+code.toString(16); + } + else + { + hexCode=code.toString(16); + } + + var resultsPage; + var resultsPageWithSearch; + var hasResultsPage; + + if (indexSectionsWithContent[this.searchIndex].charAt(code) == '1') + { + resultsPage = this.resultsPath + '/' + indexSectionNames[this.searchIndex] + '_' + hexCode + '.html'; + resultsPageWithSearch = resultsPage+'?'+escape(searchValue); + hasResultsPage = true; + } + else // nothing available for this search term + { + resultsPage = this.resultsPath + '/nomatches.html'; + resultsPageWithSearch = resultsPage; + hasResultsPage = false; + } + + window.frames.MSearchResults.location = resultsPageWithSearch; + var domPopupSearchResultsWindow = this.DOMPopupSearchResultsWindow(); + + if (domPopupSearchResultsWindow.style.display!='block') + { + var domSearchBox = this.DOMSearchBox(); + this.DOMSearchClose().style.display = 'inline'; + if (this.insideFrame) + { + var domPopupSearchResults = this.DOMPopupSearchResults(); + domPopupSearchResultsWindow.style.position = 'relative'; + domPopupSearchResultsWindow.style.display = 'block'; + var width = document.body.clientWidth - 8; // the -8 is for IE :-( + domPopupSearchResultsWindow.style.width = width + 'px'; + domPopupSearchResults.style.width = width + 'px'; + } + else + { + var domPopupSearchResults = this.DOMPopupSearchResults(); + var left = getXPos(domSearchBox) + 150; // domSearchBox.offsetWidth; + var top = getYPos(domSearchBox) + 20; // domSearchBox.offsetHeight + 1; + domPopupSearchResultsWindow.style.display = 'block'; + left -= domPopupSearchResults.offsetWidth; + domPopupSearchResultsWindow.style.top = top + 'px'; + domPopupSearchResultsWindow.style.left = left + 'px'; + } + } + + this.lastSearchValue = searchValue; + this.lastResultsPage = resultsPage; + } + + // -------- Activation Functions + + // Activates or deactivates the search panel, resetting things to + // their default values if necessary. + this.Activate = function(isActive) + { + if (isActive || // open it + this.DOMPopupSearchResultsWindow().style.display == 'block' + ) + { + this.DOMSearchBox().className = 'MSearchBoxActive'; + + var searchField = this.DOMSearchField(); + + if (searchField.value == this.searchLabel) // clear "Search" term upon entry + { + searchField.value = ''; + this.searchActive = true; + } + } + else if (!isActive) // directly remove the panel + { + this.DOMSearchBox().className = 'MSearchBoxInactive'; + this.DOMSearchField().value = this.searchLabel; + this.searchActive = false; + this.lastSearchValue = '' + this.lastResultsPage = ''; + } + } +} + +// ----------------------------------------------------------------------- + +// The class that handles everything on the search results page. +function SearchResults(name) +{ + // The number of matches from the last run of . + this.lastMatchCount = 0; + this.lastKey = 0; + this.repeatOn = false; + + // Toggles the visibility of the passed element ID. + this.FindChildElement = function(id) + { + var parentElement = document.getElementById(id); + var element = parentElement.firstChild; + + while (element && element!=parentElement) + { + if (element.nodeName == 'DIV' && element.className == 'SRChildren') + { + return element; + } + + if (element.nodeName == 'DIV' && element.hasChildNodes()) + { + element = element.firstChild; + } + else if (element.nextSibling) + { + element = element.nextSibling; + } + else + { + do + { + element = element.parentNode; + } + while (element && element!=parentElement && !element.nextSibling); + + if (element && element!=parentElement) + { + element = element.nextSibling; + } + } + } + } + + this.Toggle = function(id) + { + var element = this.FindChildElement(id); + if (element) + { + if (element.style.display == 'block') + { + element.style.display = 'none'; + } + else + { + element.style.display = 'block'; + } + } + } + + // Searches for the passed string. If there is no parameter, + // it takes it from the URL query. + // + // Always returns true, since other documents may try to call it + // and that may or may not be possible. + this.Search = function(search) + { + if (!search) // get search word from URL + { + search = window.location.search; + search = search.substring(1); // Remove the leading '?' + search = unescape(search); + } + + search = search.replace(/^ +/, ""); // strip leading spaces + search = search.replace(/ +$/, ""); // strip trailing spaces + search = search.toLowerCase(); + search = convertToId(search); + + var resultRows = document.getElementsByTagName("div"); + var matches = 0; + + var i = 0; + while (i < resultRows.length) + { + var row = resultRows.item(i); + if (row.className == "SRResult") + { + var rowMatchName = row.id.toLowerCase(); + rowMatchName = rowMatchName.replace(/^sr\d*_/, ''); // strip 'sr123_' + + if (search.length<=rowMatchName.length && + rowMatchName.substr(0, search.length)==search) + { + row.style.display = 'block'; + matches++; + } + else + { + row.style.display = 'none'; + } + } + i++; + } + document.getElementById("Searching").style.display='none'; + if (matches == 0) // no results + { + document.getElementById("NoMatches").style.display='block'; + } + else // at least one result + { + document.getElementById("NoMatches").style.display='none'; + } + this.lastMatchCount = matches; + return true; + } + + // return the first item with index index or higher that is visible + this.NavNext = function(index) + { + var focusItem; + while (1) + { + var focusName = 'Item'+index; + focusItem = document.getElementById(focusName); + if (focusItem && focusItem.parentNode.parentNode.style.display=='block') + { + break; + } + else if (!focusItem) // last element + { + break; + } + focusItem=null; + index++; + } + return focusItem; + } + + this.NavPrev = function(index) + { + var focusItem; + while (1) + { + var focusName = 'Item'+index; + focusItem = document.getElementById(focusName); + if (focusItem && focusItem.parentNode.parentNode.style.display=='block') + { + break; + } + else if (!focusItem) // last element + { + break; + } + focusItem=null; + index--; + } + return focusItem; + } + + this.ProcessKeys = function(e) + { + if (e.type == "keydown") + { + this.repeatOn = false; + this.lastKey = e.keyCode; + } + else if (e.type == "keypress") + { + if (!this.repeatOn) + { + if (this.lastKey) this.repeatOn = true; + return false; // ignore first keypress after keydown + } + } + else if (e.type == "keyup") + { + this.lastKey = 0; + this.repeatOn = false; + } + return this.lastKey!=0; + } + + this.Nav = function(evt,itemIndex) + { + var e = (evt) ? evt : window.event; // for IE + if (e.keyCode==13) return true; + if (!this.ProcessKeys(e)) return false; + + if (this.lastKey==38) // Up + { + var newIndex = itemIndex-1; + var focusItem = this.NavPrev(newIndex); + if (focusItem) + { + var child = this.FindChildElement(focusItem.parentNode.parentNode.id); + if (child && child.style.display == 'block') // children visible + { + var n=0; + var tmpElem; + while (1) // search for last child + { + tmpElem = document.getElementById('Item'+newIndex+'_c'+n); + if (tmpElem) + { + focusItem = tmpElem; + } + else // found it! + { + break; + } + n++; + } + } + } + if (focusItem) + { + focusItem.focus(); + } + else // return focus to search field + { + parent.document.getElementById("MSearchField").focus(); + } + } + else if (this.lastKey==40) // Down + { + var newIndex = itemIndex+1; + var focusItem; + var item = document.getElementById('Item'+itemIndex); + var elem = this.FindChildElement(item.parentNode.parentNode.id); + if (elem && elem.style.display == 'block') // children visible + { + focusItem = document.getElementById('Item'+itemIndex+'_c0'); + } + if (!focusItem) focusItem = this.NavNext(newIndex); + if (focusItem) focusItem.focus(); + } + else if (this.lastKey==39) // Right + { + var item = document.getElementById('Item'+itemIndex); + var elem = this.FindChildElement(item.parentNode.parentNode.id); + if (elem) elem.style.display = 'block'; + } + else if (this.lastKey==37) // Left + { + var item = document.getElementById('Item'+itemIndex); + var elem = this.FindChildElement(item.parentNode.parentNode.id); + if (elem) elem.style.display = 'none'; + } + else if (this.lastKey==27) // Escape + { + parent.searchBox.CloseResultsWindow(); + parent.document.getElementById("MSearchField").focus(); + } + else if (this.lastKey==13) // Enter + { + return true; + } + return false; + } + + this.NavChild = function(evt,itemIndex,childIndex) + { + var e = (evt) ? evt : window.event; // for IE + if (e.keyCode==13) return true; + if (!this.ProcessKeys(e)) return false; + + if (this.lastKey==38) // Up + { + if (childIndex>0) + { + var newIndex = childIndex-1; + document.getElementById('Item'+itemIndex+'_c'+newIndex).focus(); + } + else // already at first child, jump to parent + { + document.getElementById('Item'+itemIndex).focus(); + } + } + else if (this.lastKey==40) // Down + { + var newIndex = childIndex+1; + var elem = document.getElementById('Item'+itemIndex+'_c'+newIndex); + if (!elem) // last child, jump to parent next parent + { + elem = this.NavNext(itemIndex+1); + } + if (elem) + { + elem.focus(); + } + } + else if (this.lastKey==27) // Escape + { + parent.searchBox.CloseResultsWindow(); + parent.document.getElementById("MSearchField").focus(); + } + else if (this.lastKey==13) // Enter + { + return true; + } + return false; + } +} + +function setKeyActions(elem,action) +{ + elem.setAttribute('onkeydown',action); + elem.setAttribute('onkeypress',action); + elem.setAttribute('onkeyup',action); +} + +function setClassAttr(elem,attr) +{ + elem.setAttribute('class',attr); + elem.setAttribute('className',attr); +} + +function createResults() +{ + var results = document.getElementById("SRResults"); + for (var e=0; e + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/variables_63.js b/zenxml/doc/search/variables_63.js new file mode 100644 index 00000000..3c8f3573 --- /dev/null +++ b/zenxml/doc/search/variables_63.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['col',['col',['../structzen_1_1_xml_parsing_error.html#a4a37dc48883337499804a9dc791669fd',1,'zen::XmlParsingError']]] +]; diff --git a/zenxml/doc/search/variables_6c.html b/zenxml/doc/search/variables_6c.html new file mode 100644 index 00000000..6016f5cc --- /dev/null +++ b/zenxml/doc/search/variables_6c.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/variables_6c.js b/zenxml/doc/search/variables_6c.js new file mode 100644 index 00000000..e2e327ad --- /dev/null +++ b/zenxml/doc/search/variables_6c.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['lasterror',['lastError',['../structzen_1_1_xml_file_error.html#a4a109e749675a3887af8cfc140303b8f',1,'zen::XmlFileError']]] +]; diff --git a/zenxml/doc/search/variables_72.html b/zenxml/doc/search/variables_72.html new file mode 100644 index 00000000..29b87f04 --- /dev/null +++ b/zenxml/doc/search/variables_72.html @@ -0,0 +1,25 @@ + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/zenxml/doc/search/variables_72.js b/zenxml/doc/search/variables_72.js new file mode 100644 index 00000000..f37305dd --- /dev/null +++ b/zenxml/doc/search/variables_72.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['row',['row',['../structzen_1_1_xml_parsing_error.html#a3ed4cd1b5599df9b52500f620421496e',1,'zen::XmlParsingError']]] +]; diff --git a/zenxml/doc/structzen_1_1_xml_error-members.html b/zenxml/doc/structzen_1_1_xml_error-members.html new file mode 100644 index 00000000..cc906ffc --- /dev/null +++ b/zenxml/doc/structzen_1_1_xml_error-members.html @@ -0,0 +1,118 @@ + + + + + +zen::Xml: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + + + + + +
    + +
    + + +
    +
    +
    +
    zen::XmlError Member List
    +
    +
    +This is the complete list of members for zen::XmlError, including all inherited members. +
    + + + + + + diff --git a/zenxml/doc/structzen_1_1_xml_error.html b/zenxml/doc/structzen_1_1_xml_error.html new file mode 100644 index 00000000..d089d6ee --- /dev/null +++ b/zenxml/doc/structzen_1_1_xml_error.html @@ -0,0 +1,136 @@ + + + + + +zen::Xml: zen::XmlError Struct Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + + + + + +
    + +
    + + +
    +
    +
    +
    zen::XmlError Struct Reference
    +
    +
    + +

    Exception base class for zen::Xml. + More...

    + +

    #include <error.h>

    +
    +Inheritance diagram for zen::XmlError:
    +
    +
    + + +zen::XmlFileError +zen::XmlParsingError + +
    + +

    List of all members.

    +

    Detailed Description

    +

    Exception base class for zen::Xml.

    +
    + + + + + + diff --git a/zenxml/doc/structzen_1_1_xml_error.png b/zenxml/doc/structzen_1_1_xml_error.png new file mode 100644 index 00000000..0fc21923 Binary files /dev/null and b/zenxml/doc/structzen_1_1_xml_error.png differ diff --git a/zenxml/doc/structzen_1_1_xml_file_error-members.html b/zenxml/doc/structzen_1_1_xml_file_error-members.html new file mode 100644 index 00000000..53a1115a --- /dev/null +++ b/zenxml/doc/structzen_1_1_xml_file_error-members.html @@ -0,0 +1,119 @@ + + + + + +zen::Xml: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + + + + + +
    + +
    + + +
    +
    +
    +
    zen::XmlFileError Member List
    +
    +
    +This is the complete list of members for zen::XmlFileError, including all inherited members. + +
    lastErrorzen::XmlFileError
    + + + + + + diff --git a/zenxml/doc/structzen_1_1_xml_file_error.html b/zenxml/doc/structzen_1_1_xml_file_error.html new file mode 100644 index 00000000..c7e54c33 --- /dev/null +++ b/zenxml/doc/structzen_1_1_xml_file_error.html @@ -0,0 +1,144 @@ + + + + + +zen::Xml: zen::XmlFileError Struct Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + + + + + +
    + +
    + + +
    +
    + +
    +
    zen::XmlFileError Struct Reference
    +
    +
    + +

    Exception thrown due to failed file I/O. + More...

    + +

    #include <io.h>

    +
    +Inheritance diagram for zen::XmlFileError:
    +
    +
    + + +zen::XmlError + +
    + +

    List of all members.

    + + + + +

    +Public Attributes

    +ErrorCode lastError
     Native error code: errno.
    +

    Detailed Description

    +

    Exception thrown due to failed file I/O.

    +
    + + + + + + diff --git a/zenxml/doc/structzen_1_1_xml_file_error.png b/zenxml/doc/structzen_1_1_xml_file_error.png new file mode 100644 index 00000000..6393414f Binary files /dev/null and b/zenxml/doc/structzen_1_1_xml_file_error.png differ diff --git a/zenxml/doc/structzen_1_1_xml_parsing_error-members.html b/zenxml/doc/structzen_1_1_xml_parsing_error-members.html new file mode 100644 index 00000000..acc507ae --- /dev/null +++ b/zenxml/doc/structzen_1_1_xml_parsing_error-members.html @@ -0,0 +1,120 @@ + + + + + +zen::Xml: Member List + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + + + + + +
    + +
    + + +
    +
    +
    +
    zen::XmlParsingError Member List
    +
    +
    +This is the complete list of members for zen::XmlParsingError, including all inherited members. + + +
    colzen::XmlParsingError
    rowzen::XmlParsingError
    + + + + + + diff --git a/zenxml/doc/structzen_1_1_xml_parsing_error.html b/zenxml/doc/structzen_1_1_xml_parsing_error.html new file mode 100644 index 00000000..bece27d7 --- /dev/null +++ b/zenxml/doc/structzen_1_1_xml_parsing_error.html @@ -0,0 +1,147 @@ + + + + + +zen::Xml: zen::XmlParsingError Struct Reference + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + + + + + +
    + +
    + + +
    +
    + +
    +
    zen::XmlParsingError Struct Reference
    +
    +
    + +

    Exception thrown due to an XML parsing error. + More...

    + +

    #include <parser.h>

    +
    +Inheritance diagram for zen::XmlParsingError:
    +
    +
    + + +zen::XmlError + +
    + +

    List of all members.

    + + + + + + +

    +Public Attributes

    +size_t row
     Input file row where the parsing error occured (zero-based)
    +size_t col
     Input file column where the parsing error occured (zero-based)
    +

    Detailed Description

    +

    Exception thrown due to an XML parsing error.

    +
    + + + + + + diff --git a/zenxml/doc/structzen_1_1_xml_parsing_error.png b/zenxml/doc/structzen_1_1_xml_parsing_error.png new file mode 100644 index 00000000..f0eb8f79 Binary files /dev/null and b/zenxml/doc/structzen_1_1_xml_parsing_error.png differ diff --git a/zenxml/doc/tab_a.png b/zenxml/doc/tab_a.png new file mode 100644 index 00000000..2d99ef23 Binary files /dev/null and b/zenxml/doc/tab_a.png differ diff --git a/zenxml/doc/tab_b.png b/zenxml/doc/tab_b.png new file mode 100644 index 00000000..b2c3d2be Binary files /dev/null and b/zenxml/doc/tab_b.png differ diff --git a/zenxml/doc/tab_h.png b/zenxml/doc/tab_h.png new file mode 100644 index 00000000..c11f48f1 Binary files /dev/null and b/zenxml/doc/tab_h.png differ diff --git a/zenxml/doc/tab_s.png b/zenxml/doc/tab_s.png new file mode 100644 index 00000000..978943ac Binary files /dev/null and b/zenxml/doc/tab_s.png differ diff --git a/zenxml/doc/tabs.css b/zenxml/doc/tabs.css new file mode 100644 index 00000000..21920562 --- /dev/null +++ b/zenxml/doc/tabs.css @@ -0,0 +1,59 @@ +.tabs, .tabs2, .tabs3 { + background-image: url('tab_b.png'); + width: 100%; + z-index: 101; + font-size: 13px; +} + +.tabs2 { + font-size: 10px; +} +.tabs3 { + font-size: 9px; +} + +.tablist { + margin: 0; + padding: 0; + display: table; +} + +.tablist li { + float: left; + display: table-cell; + background-image: url('tab_b.png'); + line-height: 36px; + list-style: none; +} + +.tablist a { + display: block; + padding: 0 20px; + font-weight: bold; + background-image:url('tab_s.png'); + background-repeat:no-repeat; + background-position:right; + color: #283A5D; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + text-decoration: none; + outline: none; +} + +.tabs3 .tablist a { + padding: 0 10px; +} + +.tablist a:hover { + background-image: url('tab_h.png'); + background-repeat:repeat-x; + color: #fff; + text-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); + text-decoration: none; +} + +.tablist li.current a { + background-image: url('tab_a.png'); + background-repeat:repeat-x; + color: #fff; + text-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); +} diff --git a/zenxml/doc/xml_8h_source.html b/zenxml/doc/xml_8h_source.html new file mode 100644 index 00000000..ecdce60a --- /dev/null +++ b/zenxml/doc/xml_8h_source.html @@ -0,0 +1,122 @@ + + + + + +zen::Xml: xml.h Source File + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + +
    +
    zen::Xml + +
    +
    Simple C++ XML Processing
    +
    +
    + + + + + +
    + + + + +
    + +
    + +
    +
    +
    xml.h
    +
    +
    +
    00001 // **************************************************************************
    +00002 // * This file is part of the zen::Xml project. It is distributed under the *
    +00003 // * Boost Software License: http://www.boost.org/LICENSE_1_0.txt           *
    +00004 // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved        *
    +00005 // **************************************************************************
    +00006 
    +00007 #ifndef ZEN_XML_HEADER_349578228034572457454554
    +00008 #define ZEN_XML_HEADER_349578228034572457454554
    +00009 
    +00010 #include "bind.h"
    +00011 
    +00013 namespace zen {}
    +00014 
    +00015 #endif //ZEN_XML_HEADER_349578228034572457454554
    +
    + + + + + + diff --git a/zenxml/dom.h b/zenxml/dom.h deleted file mode 100644 index a2ed78f7..00000000 --- a/zenxml/dom.h +++ /dev/null @@ -1,335 +0,0 @@ -// ************************************************************************** -// * This file is part of the zen::Xml project. It is distributed under the * -// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef ZEN_XML_DOM_HEADER_82085720723894567204564256 -#define ZEN_XML_DOM_HEADER_82085720723894567204564256 - -#include -#include -#include -#include -#include "cvrt_text.h" //"readText/writeText" - -namespace zen -{ -class XmlDoc; - -/// An XML element -class XmlElement -{ - struct PrivateConstruction {}; -public: - //Construct an empty XML element - //This constructor should be private, however std::make_shared() requires public access - //Therefore at least prevent users from calling it via private dummy type PrivateConstruction - template - XmlElement(const String& name, XmlElement* parentElement, PrivateConstruction) : name_(utfCvrtTo(name)), parent_(parentElement) {} - - ///Retrieve the name of this XML element. - /** - \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ... - \returns Name of the XML element. - */ - template - String getNameAs() const { return utfCvrtTo(name_); } - - ///Get the value of this element as a user type. - /** - \tparam T Arbitrary user data type: e.g. any string class, all built-in arithmetic numbers, STL container, ... - \returns "true" if Xml element was successfully converted to value, cannot fail for string-like types - */ - template - bool getValue(T& value) const { return readStruc(*this, value); } - - ///Set the value of this element. - /** - \tparam T Arbitrary user data type: e.g. any string-like type, all built-in arithmetic numbers, STL container, ... - */ - template - void setValue(const T& value) { writeStruc(value, *this); } - - ///Retrieve an attribute by name. - /** - \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... - \tparam T String-convertible user data type: e.g. any string class, all built-in arithmetic numbers - \param name The name of the attribute to retrieve. - \param value The value of the attribute converted to T. - \return "true" if value was retrieved successfully. - */ - template - bool getAttribute(const String& name, T& value) const - { - auto it = attributes.find(utfCvrtTo(name)); - return it == attributes.end() ? false : readText(it->second, value); - } - - ///Create or update an XML attribute. - /** - \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... - \tparam T String-convertible user data type: e.g. any string-like type, all built-in arithmetic numbers - \param name The name of the attribute to create or update. - \param value The value to set. - */ - template - void setAttribute(const String& name, const T& value) - { - std::string attrValue; - writeText(value, attrValue); - attributes[utfCvrtTo(name)] = attrValue; - } - - ///Remove the attribute with the given name. - /** - \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... - */ - template - void removeAttribute(const String& name) { attributes.erase(utfCvrtTo(name)); } - - ///Create a new child element and return a reference to it. - /** - \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... - \param name The name of the child element to be created. - */ - template - XmlElement& addChild(const String& name) - { - std::string utf8Name = utfCvrtTo(name); - auto newElement = std::make_shared(utf8Name, this, PrivateConstruction()); - childElements.push_back(newElement); - childElementsSorted.insert(std::make_pair(utf8Name, newElement)); - return *newElement; - } - - ///Retrieve a child element with the given name. - /** - \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... - \param name The name of the child element to be retrieved. - \return A pointer to the child element or nullptr if none was found. - */ - template - const XmlElement* getChild(const String& name) const - { - auto it = childElementsSorted.find(utfCvrtTo(name)); - return it == childElementsSorted.end() ? nullptr : &*(it->second); - } - - ///\sa getChild - template - XmlElement* getChild(const String& name) - { - return const_cast(static_cast(this)->getChild(name)); - } - - template < class IterTy, //underlying iterator type - class T, //target object type - class AccessPolicy > //access policy: see AccessPtrMap - class PtrIter : public std::iterator, private AccessPolicy //get rid of shared_ptr indirection - { - public: - PtrIter(IterTy it) : it_(it) {} - PtrIter(const PtrIter& other) : it_(other.it_) {} - PtrIter& operator++() { ++it_; return *this; } - PtrIter operator++(int) { PtrIter tmp(*this); operator++(); return tmp; } - inline friend bool operator==(const PtrIter& lhs, const PtrIter& rhs) { return lhs.it_ == rhs.it_; } - inline friend bool operator!=(const PtrIter& lhs, const PtrIter& rhs) { return !(lhs == rhs); } - T& operator* () { return AccessPolicy::template objectRef(it_); } - T* operator->() { return &AccessPolicy::template objectRef(it_); } - private: - IterTy it_; - }; - - struct AccessPtrMap - { - template - T& objectRef(const IterTy& it) { return *(it->second); } - }; - - typedef PtrIter>::iterator, XmlElement, AccessPtrMap> ChildIter2; - typedef PtrIter>::const_iterator, const XmlElement, AccessPtrMap> ChildIterConst2; - - ///Access all child elements with the given name via STL iterators. - /** - \code - auto iterPair = elem.getChildren("Item"); - std::for_each(iterPair.first, iterPair.second, - [](const XmlElement& child) { ... }); - \endcode - \param name The name of the child elements to be retrieved. - \return A pair of STL begin/end iterators to access the child elements sequentially. - */ - template - std::pair getChildren(const String& name) const { return childElementsSorted.equal_range(utfCvrtTo(name)); } - - ///\sa getChildren - template - std::pair getChildren(const String& name) { return childElementsSorted.equal_range(utfCvrtTo(name)); } - - struct AccessPtrVec - { - template - T& objectRef(const IterTy& it) { return **it; } - }; - - typedef PtrIter>::iterator, XmlElement, AccessPtrVec> ChildIter; - typedef PtrIter>::const_iterator, const XmlElement, AccessPtrVec> ChildIterConst; - - ///Access all child elements sequentially via STL iterators. - /** - \code - auto iterPair = elem.getChildren(); - std::for_each(iterPair.first, iterPair.second, - [](const XmlElement& child) { ... }); - \endcode - \return A pair of STL begin/end iterators to access all child elements sequentially. - */ - std::pair getChildren() const { return std::make_pair(childElements.begin(), childElements.end()); } - - ///\sa getChildren - std::pair getChildren() { return std::make_pair(childElements.begin(), childElements.end()); } - - ///Get parent XML element, may be nullptr for root element - XmlElement* parent() { return parent_; }; - ///Get parent XML element, may be nullptr for root element - const XmlElement* parent() const { return parent_; }; - - - typedef std::map::const_iterator AttrIter; - - /* -> disabled documentation extraction - \brief Get all attributes associated with the element. - \code - auto iterPair = elem.getAttributes(); - for (auto it = iterPair.first; it != iterPair.second; ++it) - std::cout << "name: " << it->first << " value: " << it->second << "\n"; - \endcode - \return A pair of STL begin/end iterators to access all attributes sequentially as a list of name/value pairs of std::string. - */ - std::pair getAttributes() const { return std::make_pair(attributes.begin(), attributes.end()); } - - //Transactionally swap two elements. -> disabled documentation extraction - void swap(XmlElement& other) - { - name_ .swap(other.name_); - value_ .swap(other.value_); - attributes.swap(other.attributes); - childElements.swap(other.childElements); - childElementsSorted.swap(other.childElementsSorted); - //std::swap(parent_, other.parent_); -> parent is physical location; update children's parent reference instead: - std::for_each( childElements.begin(), childElements.end(), [&](const std::shared_ptr& child) { child->parent_ = this; }); - std::for_each(other.childElements.begin(), other.childElements.end(), [&](const std::shared_ptr& child) { child->parent_ = &other; }); - } - -private: - friend class XmlDoc; - - XmlElement(const XmlElement&); //not implemented - XmlElement& operator=(const XmlElement&); // - - std::string name_; - std::string value_; - std::map attributes; - std::vector> childElements; //all child elements in order of creation - std::multimap> childElementsSorted; //alternate key: sorted by element name - XmlElement* parent_; -}; - - -//XmlElement::setValue() calls zen::writeStruc() which calls XmlElement::setValue() ... => these two specializations end the circle -template <> inline -void XmlElement::setValue(const std::string& value) { value_ = value; } - -template <> inline -bool XmlElement::getValue(std::string& value) const { value = value_; return true; } - - -///The complete XML document -class XmlDoc -{ -public: - ///Default constructor setting up an empty XML document with a standard declaration: - XmlDoc() : version_("1.0"), encoding_("UTF-8"), rootElement("Root", nullptr, XmlElement::PrivateConstruction()) {} - - XmlDoc(XmlDoc&& tmp) : rootElement("Root", nullptr, XmlElement::PrivateConstruction()) { swap(tmp); } - XmlDoc& operator=(XmlDoc&& tmp) { swap(tmp); return *this; } - - //Setup an empty XML document - /** - \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... - \param rootName The name of the XML document's root element. - */ - template - XmlDoc(String rootName) : version_("1.0"), encoding_("UTF-8"), rootElement(rootName, nullptr, XmlElement::PrivateConstruction()) {} - - ///Get a const reference to the document's root element. - const XmlElement& root() const { return rootElement; } - ///Get a reference to the document's root element. - XmlElement& root() { return rootElement; } - - ///Get the version used in the XML declaration. - /** - \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ... - */ - template - String getVersionAs() const { return utfCvrtTo(version_); } - - ///Set the version used in the XML declaration. - /** - \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... - */ - template - void setVersion(const String& version) { version_ = utfCvrtTo(version); } - - ///Get the encoding used in the XML declaration. - /** - \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ... - */ - template - String getEncodingAs() const { return utfCvrtTo(encoding_); } - - ///Set the encoding used in the XML declaration. - /** - \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... - */ - template - void setEncoding(const String& encoding) { encoding_ = utfCvrtTo(encoding); } - - ///Get the standalone string used in the XML declaration. - /** - \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ... - */ - template - String getStandaloneAs() const { return utfCvrtTo(standalone_); } - - ///Set the standalone string used in the XML declaration. - /** - \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... - */ - template - void setStandalone(const String& standalone) { standalone_ = utfCvrtTo(standalone); } - - //Transactionally swap two elements. -> disabled documentation extraction - void swap(XmlDoc& other) - { - version_ .swap(other.version_); - encoding_ .swap(other.encoding_); - standalone_.swap(other.standalone_); - rootElement.swap(other.rootElement); - } - -private: - XmlDoc(const XmlDoc&); //not implemented, thanks to XmlElement::parent_ - XmlDoc& operator=(const XmlDoc&); // - - std::string version_; - std::string encoding_; - std::string standalone_; - - XmlElement rootElement; -}; - -} - -#endif //ZEN_XML_DOM_HEADER_82085720723894567204564256 diff --git a/zenxml/error.h b/zenxml/error.h deleted file mode 100644 index bea04520..00000000 --- a/zenxml/error.h +++ /dev/null @@ -1,19 +0,0 @@ -// ************************************************************************** -// * This file is part of the zen::Xml project. It is distributed under the * -// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef ZEN_XML_ERROR_HEADER_018734618433021489473214873214 -#define ZEN_XML_ERROR_HEADER_018734618433021489473214873214 - -namespace zen -{ -///Exception base class for zen::Xml -struct XmlError -{ - virtual ~XmlError() {} -}; -} - -#endif //ZEN_XML_ERROR_HEADER_018734618433021489473214873214 diff --git a/zenxml/io.h b/zenxml/io.h deleted file mode 100644 index 596d7acf..00000000 --- a/zenxml/io.h +++ /dev/null @@ -1,125 +0,0 @@ -// ************************************************************************** -// * This file is part of the zen::Xml project. It is distributed under the * -// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef ZEN_XML_IO_HEADER_8917640501480763248343343 -#define ZEN_XML_IO_HEADER_8917640501480763248343343 - -#include -#include -#include -#include -#include "error.h" - -namespace zen -{ -/** -\file -\brief Save and load byte streams from files -*/ - -#if !defined(ZEN_WIN) && !defined(ZEN_LINUX) && !defined(ZEN_MAC) -#error Please specify your platform: #define ZEN_WIN, ZEN_LINUX or ZEN_MAC -#endif - - -///Exception thrown due to failed file I/O -struct XmlFileError : public XmlError -{ - typedef int ErrorCode; - - explicit XmlFileError(ErrorCode ec) : lastError(ec) {} - ///Native error code: errno - ErrorCode lastError; -}; - - -#ifdef ZEN_WIN -namespace implemenation //sad but true -{ -template inline -FILE* fopen(const String& filename, const wchar_t* mode) -{ -#ifdef _MSC_VER - FILE* handle = nullptr; - errno_t rv = ::_wfopen_s(&handle, utfCvrtTo(filename).c_str(), mode); //more secure? - (void)rv; - return handle; -#else - return ::_wfopen(utfCvrtTo(filename).c_str(), mode); -#endif -} -} -#endif - - -///Save byte stream to a file -/** -\tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... -\param stream Input byte stream -\param filename Output file name -\throw XmlFileError -*/ -template -void saveStream(const std::string& stream, const String& filename) //throw XmlFileError -{ -#ifdef ZEN_WIN - FILE* handle = implemenation::fopen(utfCvrtTo(filename).c_str(), L"wb"); -#else - FILE* handle = ::fopen(utfCvrtTo(filename).c_str(), "w"); -#endif - if (handle == nullptr) - throw XmlFileError(errno); - ZEN_ON_SCOPE_EXIT(::fclose(handle)); - - const size_t bytesWritten = ::fwrite(stream.c_str(), 1, stream.size(), handle); - if (::ferror(handle) != 0) - throw XmlFileError(errno); - - (void)bytesWritten; - assert(bytesWritten == stream.size()); -} - - -///Load byte stream from a file -/** -\tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... -\param filename Input file name -\return Output byte stream -\throw XmlFileError -*/ -template -std::string loadStream(const String& filename) //throw XmlFileError -{ -#ifdef ZEN_WIN - FILE* handle = implemenation::fopen(utfCvrtTo(filename).c_str(), L"rb"); -#else - FILE* handle = ::fopen(utfCvrtTo(filename).c_str(), "r"); -#endif - if (handle == nullptr) - throw XmlFileError(errno); - ZEN_ON_SCOPE_EXIT(::fclose(handle)); - - std::string stream; - const size_t blockSize = 64 * 1024; - do - { - stream.resize(stream.size() + blockSize); //let's pray std::string implements exponential growth! - - const size_t bytesRead = ::fread(&*(stream.begin() + stream.size() - blockSize), 1, blockSize, handle); - if (::ferror(handle)) - throw XmlFileError(errno); - if (bytesRead > blockSize) - throw XmlFileError(0); - if (bytesRead < blockSize) - stream.resize(stream.size() - (blockSize - bytesRead)); //caveat: unsigned arithmetics - } - while (!::feof(handle)); - - return stream; -} -} - -#endif //ZEN_XML_IO_HEADER_8917640501480763248343343 diff --git a/zenxml/parser.h b/zenxml/parser.h deleted file mode 100644 index 51779852..00000000 --- a/zenxml/parser.h +++ /dev/null @@ -1,618 +0,0 @@ -// ************************************************************************** -// * This file is part of the zen::Xml project. It is distributed under the * -// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef ZEN_XML_PARSER_HEADER_81248670213764583021432 -#define ZEN_XML_PARSER_HEADER_81248670213764583021432 - -#include -#include //ptrdiff_t; req. on Linux -#include -#include "dom.h" -#include "error.h" - -namespace zen -{ -/** -\file -\brief Convert an XML document object model (class XmlDoc) to and from a byte stream representation. -*/ - -///Save XML document as a byte stream -/** -\param doc Input XML document -\param lineBreak Line break, default: carriage return + new line -\param indent Indentation, default: four space characters -\return Output byte stream -*/ -std::string serialize(const XmlDoc& doc, - const std::string& lineBreak = "\r\n", - const std::string& indent = " "); //throw () - -///Exception thrown due to an XML parsing error -struct XmlParsingError : public XmlError -{ - XmlParsingError(size_t rowNo, size_t colNo) : row(rowNo), col(colNo) {} - ///Input file row where the parsing error occured (zero-based) - size_t row; //beginning with 0 - ///Input file column where the parsing error occured (zero-based) - size_t col; // -}; - - -///Load XML document from a byte stream -/** -\param stream Input byte stream -\returns Output XML document -\throw XmlParsingError -*/ -XmlDoc parse(const std::string& stream); //throw XmlParsingError - - - - - - - - - - - - - - - - - - - - -//---------------------------- implementation ---------------------------- -//see: http://www.w3.org/TR/xml/ - -namespace implementation -{ -inline -std::pair hexify(unsigned char c) -{ - auto hexifyDigit = [](int num) -> char //input [0, 15], output 0-9, A-F - { - assert(0 <= num&& num <= 15); //guaranteed by design below! - return static_cast(num <= 9 ? //no signed/unsigned char problem here! - '0' + num : - 'A' + (num - 10)); - }; - return std::make_pair(hexifyDigit(c / 16), hexifyDigit(c % 16)); -} - - -inline -char unhexify(char high, char low) -{ - auto unhexifyDigit = [](char hex) -> int //input 0-9, a-f, A-F; output range: [0, 15] - { - if ('0' <= hex && hex <= '9') //no signed/unsigned char problem here! - return hex - '0'; - else if ('A' <= hex && hex <= 'F') - return (hex - 'A') + 10; - else if ('a' <= hex && hex <= 'f') - return (hex - 'a') + 10; - assert(false); - return 0; - }; - return static_cast(16 * unhexifyDigit(high) + unhexifyDigit(low)); //[!] convert to unsigned char first, then to char (which may be signed) -}; - - -template inline -std::string normalize(const std::string& str, Predicate pred) //pred: unary function taking a char, return true if value shall be encoded as hex -{ - std::string output; - std::for_each(str.begin(), str.end(), - [&](char c) - { - if (c == '&') // - output += "&"; - else if (c == '<') //normalization mandatory: http://www.w3.org/TR/xml/#syntax - output += "<"; - else if (c == '>') // - output += ">"; - else if (pred(c)) - { - if (c == '\'') - output += "'"; - else if (c == '\"') - output += """; - else - { - output += "&#x"; - const auto hexDigits = hexify(c); //hexify beats "printNumber("&#x%02X;", c)" by a nice factor of 3! - output += hexDigits.first; - output += hexDigits.second; - output += ';'; - } - } - else - output += c; - }); - return output; -} - -inline -std::string normalizeName(const std::string& str) -{ - return normalize(str, [](char c) { return isWhiteSpace(c) || c == '=' || c == '/' || c == '\'' || c == '\"'; }); -} - -inline -std::string normalizeElementValue(const std::string& str) -{ - return normalize(str, [](char c) { return static_cast(c) < 32; }); -} - -inline -std::string normalizeAttribValue(const std::string& str) -{ - return normalize(str, [](char c) { return static_cast(c) < 32 || c == '\'' || c == '\"'; }); -} - - -template inline -bool checkEntity(CharIterator& first, CharIterator last, const char (&placeholder)[N]) -{ - assert(placeholder[N - 1] == 0); - const ptrdiff_t strLen = N - 1; //don't count null-terminator - if (last - first >= strLen && std::equal(first, first + strLen, placeholder)) - { - first += strLen - 1; - return true; - } - return false; -} - - -namespace -{ -std::string denormalize(const std::string& str) -{ - std::string output; - for (auto it = str.begin(); it != str.end(); ++it) - { - const char c = *it; - - if (c == '&') - { - if (checkEntity(it, str.end(), "&")) - output += '&'; - else if (checkEntity(it, str.end(), "<")) - output += '<'; - else if (checkEntity(it, str.end(), ">")) - output += '>'; - else if (checkEntity(it, str.end(), "'")) - output += '\''; - else if (checkEntity(it, str.end(), """)) - output += '\"'; - else if (str.end() - it >= 6 && - it[1] == '#' && - it[2] == 'x' && - it[5] == ';') - { - output += unhexify(it[3], it[4]); //unhexify beats "::sscanf(&it[3], "%02X", &tmp)" by a factor of 3000 for ~250000 calls!!! - it += 5; - } - else - output += c; //unexpected char! - } - else if (c == '\r') //map all end-of-line characters to \n http://www.w3.org/TR/xml/#sec-line-ends - { - auto itNext = it + 1; - if (itNext != str.end() && *itNext == '\n') - ++it; - output += '\n'; - } - else - output += c; - }; - return output; -} - - -void serialize(const XmlElement& element, std::string& stream, - const std::string& lineBreak, - const std::string& indent, - size_t indentLevel) -{ - const std::string& nameFmt = normalizeName(element.getNameAs()); - - for (size_t i = 0; i < indentLevel; ++i) - stream += indent; - - stream += '<' + nameFmt; - - auto attr = element.getAttributes(); - for (auto it = attr.first; it != attr.second; ++it) - stream += ' ' + normalizeName(it->first) + "=\"" + normalizeAttribValue(it->second) + '\"'; - - //no support for mixed-mode content - auto iterPair = element.getChildren(); - if (iterPair.first != iterPair.second) //structured element - { - stream += '>' + lineBreak; - - std::for_each(iterPair.first, iterPair.second, - [&](const XmlElement & el) { serialize(el, stream, lineBreak, indent, indentLevel + 1); }); - - for (size_t i = 0; i < indentLevel; ++i) - stream += indent; - stream += "' + lineBreak; - } - else - { - std::string value; - element.getValue(value); - - if (!value.empty()) //value element - stream += '>' + normalizeElementValue(value) + "' + lineBreak; - else //empty element - stream += "/>" + lineBreak; - } -} - -std::string serialize(const XmlDoc& doc, - const std::string& lineBreak, - const std::string& indent) -{ - std::string version = doc.getVersionAs(); - if (!version.empty()) - version = " version=\"" + normalizeAttribValue(version) + '\"'; - - std::string encoding = doc.getEncodingAs(); - if (!encoding.empty()) - encoding = " encoding=\"" + normalizeAttribValue(encoding) + '\"'; - - std::string standalone = doc.getStandaloneAs(); - if (!standalone.empty()) - standalone = " standalone=\"" + normalizeAttribValue(standalone) + '\"'; - - std::string output = "" + lineBreak; - serialize(doc.root(), output, lineBreak, indent, 0); - return output; -} -} -} - -inline -std::string serialize(const XmlDoc& doc, - const std::string& lineBreak, - const std::string& indent) { return implementation::serialize(doc, lineBreak, indent); } - -/* -Grammar for XML parser -------------------------------- -document-expression: - - element-expression: - -element-expression: - - pm-expression - -element-list-expression: - - element-expression element-list-expression - -attributes-expression: - - string="string" attributes-expression - -pm-expression: - string - element-list-expression -*/ - -namespace implementation -{ -struct Token -{ - enum Type - { - TK_LESS, - TK_GREATER, - TK_LESS_SLASH, - TK_SLASH_GREATER, - TK_EQUAL, - TK_QUOTE, - TK_DECL_BEGIN, - TK_DECL_END, - TK_NAME, - TK_END - }; - - Token(Type t) : type(t) {} - Token(const std::string& txt) : type(TK_NAME), name(txt) {} - - Type type; - std::string name; //filled if type == TK_NAME -}; - -class Scanner -{ -public: - Scanner(const std::string& stream) : - xmlCommentBegin(""), - stream_(stream), - pos(stream_.begin()) - { - if (zen::startsWith(stream_, BYTE_ORDER_MARK_UTF8)) - pos += strLength(BYTE_ORDER_MARK_UTF8); - - tokens.push_back(std::make_pair("", Token::TK_DECL_END)); - tokens.push_back(std::make_pair("", Token::TK_SLASH_GREATER)); - tokens.push_back(std::make_pair("<" , Token::TK_LESS)); //evaluate after TK_DECL_BEGIN! - tokens.push_back(std::make_pair(">" , Token::TK_GREATER)); - tokens.push_back(std::make_pair("=" , Token::TK_EQUAL)); - tokens.push_back(std::make_pair("\"", Token::TK_QUOTE)); - tokens.push_back(std::make_pair("\'", Token::TK_QUOTE)); - } - - Token nextToken() //throw XmlParsingError - { - //skip whitespace - pos = std::find_if(pos, stream_.end(), [](char c) { return !zen::isWhiteSpace(c); }); - - if (pos == stream_.end()) - return Token::TK_END; - - //skip XML comments - if (startsWith(xmlCommentBegin)) - { - auto it = std::search(pos + xmlCommentBegin.size(), stream_.end(), xmlCommentEnd.begin(), xmlCommentEnd.end()); - if (it != stream_.end()) - { - pos = it + xmlCommentEnd.size(); - return nextToken(); - } - } - - for (auto it = tokens.begin(); it != tokens.end(); ++it) - if (startsWith(it->first)) - { - pos += it->first.size(); - return it->second; - } - - auto nameEnd = std::find_if(pos, stream_.end(), [](char c) - { - return c == '<' || - c == '>' || - c == '=' || - c == '/' || - c == '\'' || - c == '\"' || - zen::isWhiteSpace(c); - }); - - if (nameEnd != pos) - { - std::string name(&*pos, nameEnd - pos); - pos = nameEnd; - return implementation::denormalize(name); - } - - //unknown token - throw XmlParsingError(posRow(), posCol()); - } - - std::string extractElementValue() - { - auto it = std::find_if(pos, stream_.end(), [](char c) - { - return c == '<' || - c == '>'; - }); - std::string output(pos, it); - pos = it; - return implementation::denormalize(output); - } - - std::string extractAttributeValue() - { - auto it = std::find_if(pos, stream_.end(), [](char c) - { - return c == '<' || - c == '>' || - c == '\'' || - c == '\"'; - }); - std::string output(pos, it); - pos = it; - return implementation::denormalize(output); - } - - size_t posRow() const //current row beginning with 0 - { - const size_t crSum = std::count(stream_.begin(), pos, '\r'); //carriage returns - const size_t nlSum = std::count(stream_.begin(), pos, '\n'); //new lines - assert(crSum == 0 || nlSum == 0 || crSum == nlSum); - return std::max(crSum, nlSum); //be compatible with Linux/Mac/Win - } - - size_t posCol() const //current col beginning with 0 - { - //seek beginning of line - for (auto it = pos; it != stream_.begin(); ) - { - --it; - if (*it == '\r' || *it == '\n') - return pos - it - 1; - } - return pos - stream_.begin(); - } - -private: - Scanner(const Scanner&); - Scanner& operator=(const Scanner&); - - bool startsWith(const std::string& prefix) const - { - if (stream_.end() - pos < static_cast(prefix.size())) - return false; - return std::equal(prefix.begin(), prefix.end(), pos); - } - - typedef std::vector > TokenList; - TokenList tokens; - - const std::string xmlCommentBegin; - const std::string xmlCommentEnd; - - const std::string stream_; - std::string::const_iterator pos; -}; - - -class XmlParser -{ -public: - XmlParser(const std::string& stream) : - scn(stream), - tk(scn.nextToken()) {} - - XmlDoc parse() //throw XmlParsingError - { - XmlDoc doc; - - //declaration (optional) - if (token().type == Token::TK_DECL_BEGIN) - { - nextToken(); - - while (token().type == Token::TK_NAME) - { - std::string attribName = token().name; - nextToken(); - - consumeToken(Token::TK_EQUAL); - expectToken(Token::TK_QUOTE); - std::string attribValue = scn.extractAttributeValue(); - nextToken(); - - consumeToken(Token::TK_QUOTE); - - if (attribName == "version") - doc.setVersion(attribValue); - else if (attribName == "encoding") - doc.setEncoding(attribValue); - else if (attribName == "standalone") - doc.setStandalone(attribValue); - } - consumeToken(Token::TK_DECL_END); - } - - XmlDoc dummy; - XmlElement& elemTmp = dummy.root(); - parseChildElements(elemTmp); - - auto iterPair = elemTmp.getChildren(); - if (iterPair.first != iterPair.second) - doc.root().swap(*iterPair.first); - - expectToken(Token::TK_END); - return doc; - }; - -private: - XmlParser(const XmlParser&); - XmlParser& operator=(const XmlParser&); - - void parseChildElements(XmlElement& parent) - { - while (token().type == Token::TK_LESS) - { - nextToken(); - - expectToken(Token::TK_NAME); - std::string elementName = token().name; - nextToken(); - - XmlElement& newElement = parent.addChild(elementName); - - parseAttributes(newElement); - - if (token().type == Token::TK_SLASH_GREATER) //empty element - { - nextToken(); - continue; - } - - expectToken(Token::TK_GREATER); - std::string elementValue = scn.extractElementValue(); - nextToken(); - - //no support for mixed-mode content - if (token().type == Token::TK_LESS) //structured element - parseChildElements(newElement); - else //value element - newElement.setValue(elementValue); - - consumeToken(Token::TK_LESS_SLASH); - - if (token().type != Token::TK_NAME || - elementName != token().name) - throw XmlParsingError(scn.posRow(), scn.posCol()); - nextToken(); - - consumeToken(Token::TK_GREATER); - } - }; - - void parseAttributes(XmlElement& element) - { - while (token().type == Token::TK_NAME) - { - std::string attribName = token().name; - nextToken(); - - consumeToken(Token::TK_EQUAL); - expectToken(Token::TK_QUOTE); - std::string attribValue = scn.extractAttributeValue(); - nextToken(); - - consumeToken(Token::TK_QUOTE); - element.setAttribute(attribName, attribValue); - } - } - - const Token& token() const { return tk; } - void nextToken() { tk = scn.nextToken(); } - - void consumeToken(Token::Type t) //throw XmlParsingError - { - expectToken(t); //throw XmlParsingError - nextToken(); - } - - void expectToken(Token::Type t) //throw XmlParsingError - { - if (token().type != t) - throw XmlParsingError(scn.posRow(), scn.posCol()); - } - - Scanner scn; - Token tk; -}; -} - -inline -XmlDoc parse(const std::string& stream) //throw XmlParsingError -{ - return implementation::XmlParser(stream).parse(); //throw XmlParsingError -} -} - -#endif //ZEN_XML_PARSER_HEADER_81248670213764583021432 diff --git a/zenxml/summary.dox b/zenxml/summary.dox deleted file mode 100644 index abe0537d..00000000 --- a/zenxml/summary.dox +++ /dev/null @@ -1,684 +0,0 @@ -// ************************************************************************** -// * This file is part of the zen::Xml project. It is distributed under the * -// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -/** -\mainpage Overview - -\li \ref sec_Rationale -\li \ref sec_Quick_Start -\li \ref sec_Supported_Platforms -\li \ref sec_Flexible_Programming_Model -\li \ref sec_Structured_XML_element_access -\li \ref sec_Access_XML_attributes -\li \ref sec_Automatic_conversion_built_in -\li \ref sec_Automatic_conversion_string -\li \ref sec_Automatic_conversion_STL -\li \ref sec_Support_user_defined -\li \ref sec_Structured_user_types -\li \ref sec_Type_Safety - -\section sec_Rationale Rationale - -zen::Xml is an XML library serializing structured user data in a convenient way. -Using compile-time information gathered by techniques of template metaprogramming it minimizes the manual overhead required and frees the user from implementing fundamental type conversions -by himself. Basic data types such as -- \b all built-in arithmetic numbers, -- \b all kinds of string classes and "string-like" types, -- \b all types defined as STL containers - -are handled automatically. Thereby a large number of recurring problems is solved by the library: -- generic number to string conversions -- generic char to wchar_t conversions (UTF) for custom string classes in a platform independent manner -- serialization of arbitrary STL container types -- simple integration: header-only, no extra dependencies, fully portable -- support arbitrary string classes everywhere: for file names, XML element names, attribute names, values, ... -- XML library built on C++11 with focus on elegance, minimal code size, flexibility and performance -- easily extensible API: allow for internationalization, fine-granular error handling, and custom file I/O - -The design follows the philosophy of the Loki library: \n -http://loki-lib.sourceforge.net/index.php?n=Main.Philosophy - -\section sec_Quick_Start Quick Start - -1. Download zen::Xml: http://sourceforge.net/projects/zenxml - -2. Setup one of the following preprocessor macros for your project to identify the platform (this is only required if you use C-stream-based file IO) -\code - ZEN_WIN - ZEN_LINUX - ZEN_MAC -\endcode - -3. For optimal performance define this global macro in release build: (following convention of the assert macro) -\code - NDEBUG -\endcode - -4. Include the main header: -\code -#include -\endcode - -5. Start serializing user data: - -\code -size_t a = 10; -double b = 2.0; -int c = -1; -\endcode - -\code -zen::XmlDoc doc; //empty XML document - -zen::XmlOut out(doc); //the simplest way to fill the document is to use a data output proxy -out["elem1"](a); // -out["elem2"](b); //map data types to XML elements -out["elem3"](c); // - -try -{ - save(doc, "file.xml"); //throw zen::XmlFileError -} -catch (const zen::XmlFileError& e) { /* handle error */ } -\endcode - -The following XML file will be created: -\verbatim - - - 10 - 2.000000 - -1 - -\endverbatim - -Load an XML file and map its content to user data: -\code -zen::XmlDoc doc; //empty XML document - -try -{ - load("file.xml", doc); //throw XmlFileError, XmlParsingError -} -catch (const zen::XmlError& e) { /* handle error */ } - -zen::XmlIn in(doc); //the simplest way to read the document is to use a data input proxy -in["elem1"](a); // -in["elem2"](b); //map XML elements into user data -in["elem3"](c); // - -//check for mapping errors, i.e. missing elements or conversion errors: you may consider these as warnings only -if (in.errorsOccured()) -{ - std::vector failedElements = in.getErrorsAs(); - /* generate error message showing the XML element names that failed to convert */ -} -\endcode - - -\section sec_Supported_Platforms Supported Platforms - -zen::Xml is written in a platform independent manner and runs on any rudimentary C++11 compliant compiler. -It has been tested successfully under: - -- Windows: - -# Visual C++ 2010 - 32 bit - -# Visual C++ 2010 - 64 bit - -# MinGW: GCC 4.5.2 - 32 bit - -- Linux: - -# GCC 4.5.2 - 32 bit - -# GCC 4.5.2 - 64 bit - -- Mac OS X: - -# Clang 3.2 - 64 bit - -Note: In order to enable C++11 features in GCC it is required to specify either of the following compiler options: -\verbatim --std=c++11 --std=c++0x --std=gnu++0x -\endverbatim - - -\section sec_Flexible_Programming_Model Flexible Programming Model - -Depending on what granularity of control is required in a particular application, zen::Xml allows the user to choose between full control or simplicity. -\n\n -The library is structured into the following parts, each of which can be used in isolation: -\n\n -\b \ \n -|\n -| io.h\n -|\n -\\n -|\n -| parser.h\n -|\n -\\n -|\n -| bind.h\n -|\n -\ -\n\n - -- Save an XML document to memory -\code -zen::XmlDoc doc; - ... //fill it -std::string stream = serialize(doc); //throw () -/* you now have a binary XML stream */ - -saveStream(stream, "file.xml"); //throw XmlFileError -//if all you need is to store XmlDoc in a file direcly you can use zen::save() instead -\endcode - -- Load XML document from memory -\code -//get XML byte stream: -std::string stream = loadStream("file.xml"); //throw XmlFileError - -zen::XmlDoc doc; -//parse byte stream into an XML document: -parse(stream, doc); //throw XmlParsingError -//if all you need is to load an XmlDoc from a file you can use zen::load() directly -\endcode - -- Fine-granular error checking with the data input proxy -\code -zen::XmlIn in(doc); -//map XML elements into user data -if (!in["elem1"](a)) - throw MyCustomException(); -if (!in["elem2"](b)) - throw MyCustomException(); -if (!in["elem3"](c)) - throw MyCustomException(); - -//if (in.errorsOccured()) ... <- not required here: contains the same conversion errors checked manually before -\endcode - -- Access the Document Object Model directly (without input/output proxy) -\n\n -The full power of type conversions which is available via the input/output proxy classes zen::XmlIn and zen::XmlOut is also available for the document object model! -\code -using namespace zen; - -XmlDoc doc; - -XmlElement& child = doc.root().addChild("elem1"); -child.setValue(1234); - -save(doc, "file.xml"); //throw XmlFileError -\endcode -\n -\code -using namespace zen; - -XmlDoc doc; -load("file.xml", doc); //throw XmlFileError, XmlParsingError - -XmlElement* child = doc.root().getChild("elem1"); -if (child) -{ - int value = -1; - if (!child->getValue(value)) - ... //handle conversion error -} -else - ... //XML element not found -\endcode - - -\section sec_Structured_XML_element_access Structured XML element access - -\code -//write a value into one deeply nested XML element - note the different types used seamlessly: char[], wchar_t[], char, wchar_t, int -zen::XmlOut out(doc); -out["elemento1"][L"элемент2"][L"要素3"][L"στοιχείο4"]["elem5"][L"元素6"][L'元']['z'](-1234); -\endcode - -The resulting XML: -\verbatim - - - - <элемент2> - <要素3> - <στοιχείο4> - - <元素6> - <元> - -1234 - - - - - - - - -\endverbatim - - -\section sec_Access_XML_attributes Access XML attributes - -\code -zen::XmlDoc doc; - -zen::XmlOut out(doc); -out["elem"].attribute("attr1", -1); // -out["elem"].attribute("attr2", 2.1); //write data into XML attributes -out["elem"].attribute("attr3", true); // - -save(doc, "file.xml"); //throw XmlFileError -\endcode - -The resulting XML: -\verbatim - - - - -\endverbatim - - -\section sec_Automatic_conversion_built_in Automatic conversion for built-in arithmetic types - -All built-in arithmetic types and bool are detected at compile time and a proper conversion is applied. -Common conversions for integer-like types such as int, long, long long, ect. as well as floating point types are optimized for maximum performance. - -\code -zen::XmlOut out(doc); - -out["int"] (-1234); -out["double"](1.23); -out["float"] (4.56f); -out["ulong"] (1234UL); -out["bool"] (false); -\endcode - -The resulting XML: -\verbatim - - - -1234 - 1.23 - 4.56 - 1234 - false - -\endverbatim - - -\section sec_Automatic_conversion_string Automatic conversion for string-like types - -The document object model of zen::Xml internally stores all names and values as a std::string. Consequently everything that is not a std::string but is "string-like" is UTF-converted -into a std::string representation. By default zen::Xml accepts all character arrays like char[], wchar_t[], char*, wchar_t*, single characters like -char, wchar_t, standard string classes like std::string, std::wstring and user-defined string classes. -If the input string is based on char, it will simply be copied and thereby preserves any local encodings. If the input string is based on wchar_t it will -be converted to an UTF-8 encoded std::string. The correct wchar_t encoding of the system will be detected at compile time, for example UTF-16 on Windows, -UTF-32 on most Linux distributions. - -Note: User-defined string classes are automatically supported if they fulfill the following string concept by defining: - -# A typedef named value_type for the underlying character type: must be \p char or \p wchar_t - -# A member function c_str() returning something that can be converted into a const value_type* - -# A member function length() returning the number of characters returned by c_str() - -\code -std::string elem1 = "elemento1"; -std::wstring elem2 = L"элемент2"; -wxString elem3 = L"要素3"; -MyString elem4 = L"στοιχείο4"; - -zen::XmlOut out(doc); - -out["string"] (elem1); -out["wstring"] (elem2); -out["wxString"] (elem3); -out["MyString"] (elem4); -out["char[6]"] ("elem5"); -out["wchar_t[4]"](L"元素6"); -out["wchar_t"] (L'元'); -out["char"] ('z'); -\endcode - -The resulting XML: -\verbatim - - - elemento1 - элемент2 - 要素3 - στοιχείο4 - elem5 - 元素6 - - z - -\endverbatim - - -\section sec_Automatic_conversion_STL Automatic conversion for STL container types - -- User-defined STL compatible types are automatically supported if they fulfill the following container concept by defining: - -# A typedef named value_type for the underlying element type of the container - -# A typedef named iterator for a non-const iterator into the container - -# A typedef named const_iterator for a const iterator into the container -\n\n - -# A member function begin() returning an iterator pointing to the first element in the container - -# A member function end() returning an iterator pointing just after the last element in the container - -# A member function insert() with the signature iterator insert(iterator position, const value_type& x) - -# A member function clear() removing all elements from the container - -- In order to support combinations of user types and STL containers such as std::vector or std::vector> it is sufficient to -only integrate MyType into zen::Xml. \n -See \ref sec_Support_user_defined - -\code -std::deque testDeque; -std::list testList; -std::map testMap; -std::multimap testMultiMap; -std::set testSet; -std::multiset testMultiSet; -std::vector testVector; -std::vector > testVectorList; -std::pair testPair; - -/* fill container */ - -zen::XmlOut out(doc); - -out["deque"] (testDeque); -out["list"] (testList); -out["map"] (testMap); -out["multimap"] (testMultiMap); -out["set"] (testSet); -out["multiset"] (testMultiSet); -out["vector"] (testVector); -out["vect_list"](testVectorList); -out["pair" ] (testPair); -\endcode - -The resulting XML: -\verbatim - - - - 1.234 - 5.678 - - - 1 - 2 - - - - 1.1 - a - - - 2.2 - b - - - - - 3 - 99 - - - 3 - 100 - - - 4 - 101 - - - - 1 - 2 - - - 1 - 1 - 2 - - - Ä - Ö - - - - ä - ö - ü - - - ä - ö - ü - - - - a - â - - -\endverbatim - - -\section sec_Support_user_defined Support for user-defined types - -User types can be integrated into zen::Xml by providing specializations of zen::readText() and zen::writeText() or zen::readStruc() and zen::writeStruc(). -The first pair should be used for all non-structured types that can be represented as a simple text string. This specialization is then used to convert the type to XML elements -and XML attributes. The second pair should be specialized for structured types that require an XML representation as a hierarchy of elements. This specialization is used when converting -the type to XML elements only. -\n\n -See section \ref sec_Type_Safety for a discussion of type categories. -\n\n -Example: Specialization for an enum type -\code -enum UnitTime -{ - UNIT_SECOND, - UNIT_MINUTE, - UNIT_HOUR -}; - -namespace zen -{ -template <> inline -void writeText(const UnitTime& value, std::string& output) -{ - switch (value) - { - case UNIT_SECOND: output = "second"; break; - case UNIT_MINUTE: output = "minute"; break; - case UNIT_HOUR: output = "hour" ; break; - } -} - -template <> inline -bool readText(const std::string& input, UnitTime& value) -{ - std::string tmp = input; - zen::trim(tmp); - if (tmp == "second") - value = UNIT_SECOND; - else if (tmp == "minute") - value = UNIT_MINUTE; - else if (tmp == "hour") - value = UNIT_HOUR; - else - return false; - return true; -} -} -\endcode - -Example: Brute-force specialization for an enum type -\code -namespace zen -{ -template <> inline -void writeText(const EnumType& value, std::string& output) -{ - output = zen::numberTo(static_cast(value)); //treat enum like an integer -} - -template <> inline -bool readText(const std::string& input, EnumType& value) -{ - value = static_cast(zen::stringTo(input)); //treat enum like an integer - return true; -} -} -\endcode - -Example: Specialization for a structured user type -\code -struct Config -{ - int a; - std::wstring b; -}; - -namespace zen -{ -template <> inline -void writeStruc(const Config& value, XmlElement& output) -{ - XmlOut out(output); - out["number" ](value.a); - out["address"](value.b); -} - -template <> inline -bool readStruc(const XmlElement& input, Config& value) -{ - XmlIn in(input); - bool rv1 = in["number" ](value.a); - bool rv2 = in["address"](value.b); - return rv1 && rv2; -} -} - -int main() -{ - Config cfg = { 2, L"Abc 3" }; - - std::vector cfgList; - cfgList.push_back(cfg); - - zen::XmlDoc doc; - zen::XmlOut out(doc); //write to Xml via output proxy - out["config"](cfgList); - save(doc, "file.xml"); //throw XmlFileError -} -\endcode - -The resulting XML: -\verbatim - - - - - 2 -
    Abc 3
    -
    -
    -
    -\endverbatim - - -\section sec_Structured_user_types Structured user types - -Although it is possible to enable conversion of structured user types by specializing zen::readStruc() and zen::writeStruc() (see \ref sec_Support_user_defined), -this approach has one drawback: If a mapping error occurs when converting an XML element to structured user data, for example a child-element is missing, -the input proxy class zen::XmlIn is only able to detect that the whole conversion failed. It cannot say which child-elements in particular failed to convert. -\n\n -Therefore it may be appropriate to convert structured types by calling subroutines in order to enable fine-granular logging: - -\code -void readConfig(const zen::XmlIn& in, Config& cfg) -{ - in["number" ](value.a); //failed conversions will now be logged for each single item by XmlIn - in["address"](value.b); //instead of only once for the complete Config type! -} - - -void loadConfig(const wxString& filename, Config& cfg) -{ - zen::XmlDoc doc; //empty XML document - - try - { - load(filename, doc); //throw XmlFileError, XmlParsingError - } - catch (const zen::XmlError& e) { /* handle error */ } - - zen::XmlIn in(doc); - - zen::XmlIn inConfig = in["config"]; //get input proxy for child element "config" - - readConfig(inConfig, cfg); //map child element to user data by calling subroutine - - //check for mapping errors: errors occuring in subroutines are considered, too! - if (in.errorsOccured()) - /* show mapping errors */ -} -\endcode - - -\section sec_Type_Safety Type Safety - -zen::Xml heavily uses methods of compile-time introspection in order to free the user from managing basic type conversions by himself. -Thereby it is important to find the right balance between automatic conversions and type safety so that program correctness is not compromised. -In the context of XML processing three fundamental type categories can be recognized: - -- string-like types: std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... -- to-string-convertible types: any string-like type, all built-in arithmetic numbers, bool -- structured types: any to-string-convertible type, STL containers, std::pair, structured user types - -These categories can be seen as a sequence of inclusive sets: -\verbatim ------------------------------ -| structured | Used as: XML element value -| ------------------------- | Conversion via: readStruc(), writeStruc() - may be specialized for user-defined types! -| | to-string-convertible | | Used as: XML element/attribute value -| | --------------- | | Conversion via: readText(), writeText() - may be specialized for user-defined types! -| | | string-like | | | Used as: XML element/attribute value or element name -| | --------------- | | Conversion via: utfCvrtTo<>() -| ------------------------- | ------------------------------ -\endverbatim - -A practical implication of this design is that conversions that do not make sense in a particular context simply lead to compile-time errors: -\code -zen::XmlOut out(doc); -out[L'Z'](someValue); //fine: a wchar_t is acceptable as an element name -out[1234](someValue); //compiler error: an integer is NOT "string-like"! -\endcode -\n -\code -int i = 0; -std::vector v; - -zen::XmlOut out(doc); -out["elem1"](i); //fine: both i and v can be converted to an XML element -out["elem2"](v); // - -out["elem"].attribute("attr1", i); //fine: an integer can be converted to an XML attribute -out["elem"].attribute("attr2", v); //compiler error: a std::vector is NOT "to-string-convertible"! -\endcode - - \author \b Zenju - \n\n - Email: zenju AT gmx DOT de -*/ \ No newline at end of file diff --git a/zenxml/unit_test.cpp b/zenxml/unit_test.cpp deleted file mode 100644 index 8f49de6f..00000000 --- a/zenxml/unit_test.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// ************************************************************************** -// * This file is part of the zen::Xml project. It is distributed under the * -// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include -#include -#include -#include -#include -#include -#include "xml.h" - -using namespace zen; - -namespace -{ -void unit_test() -{ - class Dummy {}; - - //compile time checks only - - assert_static(!IsStlContainer ::value); - assert_static(!IsStlContainer ::value); - assert_static(!IsStlContainer ::value); - assert_static(!IsStlContainer ::value); - assert_static(IsStlContainer> ::value); - assert_static(IsStlContainer> ::value); - assert_static(IsStlContainer> ::value); - assert_static((IsStlContainer> ::value)); - assert_static((IsStlContainer>::value)); - assert_static(IsStlContainer > ::value); - assert_static((IsStlPair > ::value)); - assert_static(!IsStlPair ::value); - - assert_static(!IsStringLike::value); - assert_static(!IsStringLike::value); - assert_static(!IsStringLike::value); - assert_static(!IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - assert_static(IsStringLike::value); - - assert_static(!(IsSameType::Type, char>::value)); - assert_static(!(IsSameType::Type, char>::value)); - assert_static(!(IsSameType::Type, char>::value)); - assert_static((IsSameType::Type, char>::value)); - assert_static((IsSameType::Type, wchar_t>::value)); - assert_static((IsSameType::Type, char>::value)); - assert_static((IsSameType::Type, wchar_t>::value)); - assert_static((IsSameType::Type, char>::value)); - assert_static((IsSameType::Type, wchar_t>::value)); - assert_static((IsSameType::Type, char>::value)); - assert_static((IsSameType::Type, wchar_t>::value)); - assert_static((IsSameType::Type, char>::value)); - assert_static((IsSameType::Type, char>::value)); - assert_static((IsSameType::Type, wchar_t>::value)); - assert_static((IsSameType::Type, wchar_t>::value)); - assert_static((IsSameType::Type, char>::value)); - assert_static((IsSameType::Type, wchar_t>::value)); - assert_static((IsSameType::Type, char>::value)); - assert_static((IsSameType::Type, wchar_t>::value)); - assert_static((IsSameType::Type, char>::value)); - assert_static((IsSameType::Type, wchar_t>::value)); - assert_static((IsSameType::Type, char>::value)); - assert_static((IsSameType::Type, wchar_t>::value)); - assert_static((IsSameType::Type, char>::value)); - assert_static((IsSameType::Type, wchar_t>::value)); - assert_static((IsSameType::Type, char>::value)); - assert_static((IsSameType::Type, wchar_t>::value)); -} -} diff --git a/zenxml/xml.h b/zenxml/xml.h deleted file mode 100644 index 3a01b1a1..00000000 --- a/zenxml/xml.h +++ /dev/null @@ -1,15 +0,0 @@ -// ************************************************************************** -// * This file is part of the zen::Xml project. It is distributed under the * -// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef ZEN_XML_HEADER_349578228034572457454554 -#define ZEN_XML_HEADER_349578228034572457454554 - -#include "bind.h" - -/// The zen::Xml namespace -namespace zen {} - -#endif //ZEN_XML_HEADER_349578228034572457454554 \ No newline at end of file diff --git a/zenxml/zenxml/bind.h b/zenxml/zenxml/bind.h new file mode 100644 index 00000000..abeff452 --- /dev/null +++ b/zenxml/zenxml/bind.h @@ -0,0 +1,390 @@ +// ************************************************************************** +// * This file is part of the zen::Xml project. It is distributed under the * +// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef ZEN_XML_BIND_HEADER_9081740816593478258435 +#define ZEN_XML_BIND_HEADER_9081740816593478258435 + +#include +#include "cvrt_struc.h" +#include "parser.h" +#include "io.h" + +namespace zen +{ +/** +\file +\brief Map user data types to XML +*/ + +///Load XML document from a file +/** +Convenience function that does nothing more than calling loadStream() and parse(). + +\tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... +\param filename Input file name +\returns The loaded XML document +\throw XmlFileError +\throw XmlParsingError +*/ +template inline +XmlDoc load(const String& filename) //throw XmlFileError, XmlParsingError +{ + std::string stream = loadStream(filename); //throw XmlFileError + return parse(stream); //throw XmlParsingError +} + + +///Save XML document to a file +/** +Convenience function that does nothing more than calling serialize() and saveStream(). + +\tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... +\param doc The XML document to save +\param filename Output file name +\param lineBreak Line break, default: carriage return + new line +\param indent Indentation, default: four space characters +\throw XmlFileError +*/ +template inline +void save(const XmlDoc& doc, + const String& filename, + const std::string& lineBreak = "\r\n", + const std::string& indent = " ") //throw XmlFileError +{ + std::string stream = serialize(doc, lineBreak, indent); //throw () + saveStream(stream, filename); //throw XmlFileError +} + + +///Proxy class to conveniently convert user data into XML structure +class XmlOut +{ +public: + ///Construct an output proxy for an XML document + /** + \code + zen::XmlDoc doc; + + zen::XmlOut out(doc); + out["elem1"]( 1); // + out["elem2"]( 2); //write data into XML elements + out["elem3"](-3); // + + save(doc, "out.xml"); //throw XmlFileError + \endcode + Output: + \verbatim + + + 1 + 2 + -3 + + \endverbatim + */ + XmlOut(XmlDoc& doc) : ref_(&doc.root()) {} + ///Construct an output proxy for a single XML element + /** + \sa XmlOut(XmlDoc& doc) + */ + XmlOut(XmlElement& element) : ref_(&element) {} + + ///Retrieve a handle to an XML child element for writing + /** + The child element will be created if it is not yet existing. + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + \param name The name of the child element + */ + template + XmlOut operator[](const String& name) const + { + const std::string utf8name = utfCvrtTo(name); + XmlElement* child = ref_->getChild(utf8name); + return child ? *child : ref_->addChild(utf8name); + } + + ///Write user data to the underlying XML element + /** + This conversion requires a specialization of zen::writeText() or zen::writeStruc() for type T. + \tparam T User type that is converted into an XML element value. + */ + template + void operator()(const T& value) { writeStruc(value, *ref_); } + + ///Write user data to an XML attribute + /** + This conversion requires a specialization of zen::writeText() for type T. + \code + zen::XmlDoc doc; + + zen::XmlOut out(doc); + out["elem"].attribute("attr1", 1); // + out["elem"].attribute("attr2", 2); //write data into XML attributes + out["elem"].attribute("attr3", -3); // + + save(doc, "out.xml"); //throw XmlFileError + \endcode + Output: + \verbatim + + + + + \endverbatim + + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + \tparam T String-convertible user data type: e.g. any string-like type, all built-in arithmetic numbers + \sa XmlElement::setAttribute() + */ + template + void attribute(const String& name, const T& value) { ref_->setAttribute(name, value); } + + ///Return a reference to the underlying Xml element + XmlElement& ref() { return *ref_; } + +private: + XmlElement* ref_; //always bound! +}; + + +///Proxy class to conveniently convert XML structure to user data +class XmlIn +{ + class ErrorLog; + struct ConversionToBool { int dummy; }; + +public: + ///Construct an input proxy for an XML document + /** + \code + zen::XmlDoc doc; + ... //load document + zen::XmlIn in(doc); + in["elem1"](value1); // + in["elem2"](value2); //read data from XML elements into variables "value1", "value2", "value3" + in["elem3"](value3); // + \endcode + */ + XmlIn(const XmlDoc& doc) : refIndex(0), log(std::make_shared()) { refList.push_back(&doc.root()); } + ///Construct an input proxy for a single XML element, may be nullptr + /** + \sa XmlIn(const XmlDoc& doc) + */ + XmlIn(const XmlElement* element) : refIndex(0), log(std::make_shared()) { refList.push_back(element); } + ///Construct an input proxy for a single XML element + /** + \sa XmlIn(const XmlDoc& doc) + */ + XmlIn(const XmlElement& element) : refIndex(0), log(std::make_shared()) { refList.push_back(&element); } + + ///Retrieve a handle to an XML child element for reading + /** + It is \b not an error if the child element does not exist, but only later if a conversion to user data is attempted. + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + \param name The name of the child element + */ + template + XmlIn operator[](const String& name) const + { + std::vector childList; + + if (refIndex < refList.size()) + { + auto iterPair = refList[refIndex]->getChildren(name); + std::for_each(iterPair.first, iterPair.second, + [&](const XmlElement& child) { childList.push_back(&child); }); + } + + return XmlIn(childList, childList.empty() ? getChildNameFormatted(name) : std::string(), log); + } + + ///Refer to next sibling element with the same name + /** + Example: Loop over all XML child elements named "Item" + \verbatim + + + 1 + 3 + 5 + + \endverbatim + + \code + zen::XmlIn in(doc); + ... + for (zen::XmlIn child = in["Item"]; child; child.next()) + { + ... + } + \endcode + */ + void next() { ++refIndex; } + + ///Read user data from the underlying XML element + /** + This conversion requires a specialization of zen::readText() or zen::readStruc() for type T. + \tparam T User type that receives the data + \return "true" if data was read successfully + */ + template + bool operator()(T& value) const + { + if (refIndex < refList.size()) + { + bool success = readStruc(*refList[refIndex], value); + if (!success) + log->notifyConversionError(getNameFormatted()); + return success; + } + else + { + log->notifyMissingElement(getNameFormatted()); + return false; + } + } + + ///Read user data from an XML attribute + /** + This conversion requires a specialization of zen::readText() for type T. + + \code + zen::XmlDoc doc; + ... //load document + zen::XmlIn in(doc); + in["elem"].attribute("attr1", value1); // + in["elem"].attribute("attr2", value2); //read data from XML attributes into variables "value1", "value2", "value3" + in["elem"].attribute("attr3", value3); // + \endcode + + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + \tparam T String-convertible user data type: e.g. any string-like type, all built-in arithmetic numbers + \returns "true" if the attribute was found and the conversion to the output value was successful. + \sa XmlElement::getAttribute() + */ + template + bool attribute(const String& name, T& value) const + { + if (refIndex < refList.size()) + { + bool success = refList[refIndex]->getAttribute(name, value); + if (!success) + log->notifyMissingAttribute(getNameFormatted(), utfCvrtTo(name)); + return success; + } + else + { + log->notifyMissingElement(getNameFormatted()); + return false; + } + } + + ///Return a pointer to the underlying Xml element, may be nullptr + const XmlElement* get() const { return refIndex < refList.size() ? refList[refIndex] : nullptr; } + + ///Test whether the underlying XML element exists + /** + \code + XmlIn in(doc); + XmlIn child = in["elem1"]; + if (child) + ... + \endcode + Use member pointer as implicit conversion to bool (C++ Templates - Vandevoorde/Josuttis; chapter 20) + */ + operator int ConversionToBool::* () const { return get() ? &ConversionToBool::dummy : nullptr; } + + ///Notifies errors while mapping the XML to user data + /** + Error logging is shared by each hiearchy of XmlIn proxy instances that are created from each other. Consequently it doesn't matter which instance you query for errors: + \code + XmlIn in(doc); + XmlIn inItem = in["item1"]; + + int value = 0; + inItem(value); //let's assume this conversion failed + + assert(in.errorsOccured() == inItem.errorsOccured()); + assert(in.getErrorsAs() == inItem.getErrorsAs()); + \endcode + + Note that error logging is \b NOT global, but owned by all instances of a hierarchy of XmlIn proxies. + Therefore it's safe to use unrelated XmlIn proxies in multiple threads. + \n\n + However be aware that the chain of connected proxy instances will be broken once you call XmlIn::get() to retrieve the underlying pointer. + Errors that occur when working with this pointer are not logged by the original set of related instances. + */ + bool errorsOccured() const { return !log->elementList().empty(); } + + ///Get a list of XML element and attribute names which failed to convert to user data. + /** + \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ... + \returns A list of XML element and attribute names, empty list if no errors occured. + */ + template + std::vector getErrorsAs() const + { + std::vector output; + const auto& elements = log->elementList(); + std::transform(elements.begin(), elements.end(), std::back_inserter(output), [](const std::string& str) { return utfCvrtTo(str); }); + return output; + } + +private: + XmlIn(const std::vector& siblingList, const std::string& elementNameFmt, const std::shared_ptr& sharedlog) : + refList(siblingList), refIndex(0), formattedName(elementNameFmt), log(sharedlog) + { assert((!siblingList.empty() && elementNameFmt.empty()) || (siblingList.empty() && !elementNameFmt.empty())); } + + static std::string getNameFormatted(const XmlElement& elem) //" " + { + return (elem.parent() ? getNameFormatted(*elem.parent()) + " " : std::string()) + "<" + elem.getNameAs() + ">"; + } + + std::string getNameFormatted() const + { + if (refIndex < refList.size()) + { + assert(formattedName.empty()); + return getNameFormatted(*refList[refIndex]); + } + else + return formattedName; + } + + std::string getChildNameFormatted(const std::string& childName) const + { + std::string parentName = getNameFormatted(); + return (parentName.empty() ? std::string() : (parentName + " ")) + "<" + childName + ">"; + } + + class ErrorLog + { + public: + void notifyConversionError (const std::string& formattedName) { insert(formattedName); } + void notifyMissingElement (const std::string& formattedName) { insert(formattedName); } + void notifyMissingAttribute(const std::string& formattedName, const std::string& attribName) { insert(formattedName + " @" + attribName); } + + const std::vector& elementList() const { return failedElements; } + + private: + void insert(const std::string& newVal) + { + if (usedElements.insert(newVal).second) + failedElements.push_back(newVal); + } + + std::vector failedElements; //unique list of failed elements + std::set usedElements; + }; + + std::vector refList; //all sibling elements with same name (all pointers bound!) + size_t refIndex; //this sibling's index in refList + std::string formattedName; //contains full and formatted element name if (and only if) refList is empty + std::shared_ptr log; //always bound +}; +} + +#endif //ZEN_XML_BIND_HEADER_9081740816593478258435 diff --git a/zenxml/zenxml/cvrt_struc.h b/zenxml/zenxml/cvrt_struc.h new file mode 100644 index 00000000..690edacb --- /dev/null +++ b/zenxml/zenxml/cvrt_struc.h @@ -0,0 +1,211 @@ +// ************************************************************************** +// * This file is part of the zen::Xml project. It is distributed under the * +// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef ZEN_XML_CONVERT_STRUC_HEADER_018727409908342709743 +#define ZEN_XML_CONVERT_STRUC_HEADER_018727409908342709743 + +#include "dom.h" + +namespace zen +{ +/** +\file +\brief Handle conversion of arbitrary types to and from XML elements. +See comments in cvrt_text.h +*/ + +///Convert XML element to structured user data +/** + \param input The input XML element. + \param value Conversion target value. + \return "true" if value was read successfully. +*/ +template bool readStruc(const XmlElement& input, T& value); +///Convert structured user data into an XML element +/** + \param value The value to be converted. + \param output The output XML element. +*/ +template void writeStruc(const T& value, XmlElement& output); + + + + + + + + + + + + + + + + + +//------------------------------ implementation ------------------------------------- +namespace impl_2384343 +{ +ZEN_INIT_DETECT_MEMBER_TYPE(value_type); +ZEN_INIT_DETECT_MEMBER_TYPE(iterator); +ZEN_INIT_DETECT_MEMBER_TYPE(const_iterator); + +ZEN_INIT_DETECT_MEMBER(begin) // +ZEN_INIT_DETECT_MEMBER(end) //we don't know the exact declaration of the member attribute: may be in a base class! +ZEN_INIT_DETECT_MEMBER(insert) // +} + +template +struct IsStlContainer : + StaticBool< + impl_2384343::HasMemberType_value_type ::value&& + impl_2384343::HasMemberType_iterator ::value&& + impl_2384343::HasMemberType_const_iterator::value&& + impl_2384343::HasMember_begin ::value&& + impl_2384343::HasMember_end ::value&& + impl_2384343::HasMember_insert ::value> {}; + + +namespace impl_2384343 +{ +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 +struct IsStlPair : + StaticBool< + impl_2384343::HasMemberType_first_type ::value&& + impl_2384343::HasMemberType_second_type::value&& + impl_2384343::HasMember_first ::value&& + impl_2384343::HasMember_second ::value> {}; + +//###################################################################################### + +//Conversion from arbitrary types to an XML element +enum ValueType +{ + VALUE_TYPE_STL_CONTAINER, + VALUE_TYPE_STL_PAIR, + VALUE_TYPE_OTHER, +}; + +template +struct GetValueType : StaticEnum::value != TEXT_TYPE_OTHER ? VALUE_TYPE_OTHER : //some string classes are also STL containers, so check this first + IsStlContainer::value ? VALUE_TYPE_STL_CONTAINER : + IsStlPair::value ? VALUE_TYPE_STL_PAIR : + VALUE_TYPE_OTHER> {}; + + +template +struct ConvertElement; +/* -> expected interface +{ + void writeStruc(const T& value, XmlElement& output) const; + bool readStruc(const XmlElement& input, T& value) const; +}; +*/ + + +//partial specialization: handle conversion for all STL-container types! +template +struct ConvertElement +{ + void writeStruc(const T& value, XmlElement& output) const + { + std::for_each(value.begin(), value.end(), + [&](const typename T::value_type & childVal) + { + XmlElement& newChild = output.addChild("Item"); + zen::writeStruc(childVal, newChild); + }); + } + bool readStruc(const XmlElement& input, T& value) const + { + bool success = true; + value.clear(); + + auto iterPair = input.getChildren("Item"); + for (auto iter = iterPair.first; iter != iterPair.second; ++iter) + { + typename T::value_type childVal; //MSVC 2010 bug: cannot put this into a lambda body + if (zen::readStruc(*iter, childVal)) + value.insert(value.end(), childVal); + else + success = false; + } + return success; + } +}; + + +//partial specialization: handle conversion for std::pair +template +struct ConvertElement +{ + void writeStruc(const T& value, XmlElement& output) const + { + XmlElement& child1 = output.addChild("one"); //don't use "1st/2nd", this will confuse a few pedantic XML parsers + zen::writeStruc(value.first, child1); + + XmlElement& child2 = output.addChild("two"); + zen::writeStruc(value.second, child2); + } + bool readStruc(const XmlElement& input, T& value) const + { + bool success = true; + const XmlElement* child1 = input.getChild("one"); + if (!child1 || !zen::readStruc(*child1, value.first)) + success = false; + + const XmlElement* child2 = input.getChild("two"); + if (!child2 || !zen::readStruc(*child2, value.second)) + success = false; + + return success; + } +}; + + +//partial specialization: not a pure structured type, try text conversion (thereby respect user specializations of writeText()/readText()) +template +struct ConvertElement +{ + void writeStruc(const T& value, XmlElement& output) const + { + std::string tmp; + writeText(value, tmp); + output.setValue(tmp); + } + bool readStruc(const XmlElement& input, T& value) const + { + std::string rawStr; + input.getValue(rawStr); + return readText(rawStr, value); + } +}; + + +template inline +void writeStruc(const T& value, XmlElement& output) +{ + ConvertElement::value>().writeStruc(value, output); +} + + +template inline +bool readStruc(const XmlElement& input, T& value) +{ + return ConvertElement::value>().readStruc(input, value); +} +} + +#endif //ZEN_XML_CONVERT_STRUC_HEADER_018727409908342709743 diff --git a/zenxml/zenxml/cvrt_text.h b/zenxml/zenxml/cvrt_text.h new file mode 100644 index 00000000..80664317 --- /dev/null +++ b/zenxml/zenxml/cvrt_text.h @@ -0,0 +1,222 @@ +// ************************************************************************** +// * This file is part of the zen::Xml project. It is distributed under the * +// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef ZEN_XML_CONVERT_TEXT_HEADER_018727339083427097434 +#define ZEN_XML_CONVERT_TEXT_HEADER_018727339083427097434 + +#include +#include + +namespace zen +{ +/** +\file +\brief Handle conversion of string-convertible types to and from std::string. + +It is \b not required to call these functions directly. They are implicitly used by zen::XmlElement::getValue(), +zen::XmlElement::setValue(), zen::XmlElement::getAttribute() and zen::XmlElement::setAttribute(). +\n\n +Conversions for the following user types are supported by default: + - strings - std::string, std::wstring, char*, wchar_t*, char, wchar_t, ect..., all STL-compatible-string-classes + - numbers - int, double, float, bool, long, ect..., all built-in numbers + - STL containers - std::map, std::set, std::vector, std::list, ect..., all STL-compatible-containers + - std::pair + +You can add support for additional types via template specialization. \n\n +Specialize zen::readStruc() and zen::writeStruc() to enable conversion from structured user types to XML elements. +Specialize zen::readText() and zen::writeText() to enable conversion from string-convertible user types to std::string. +Prefer latter if possible since it does not only enable conversions from XML elements to user data, but also from and to XML attributes. +\n\n + Example: type "bool" +\code +namespace zen +{ +template <> inline +void writeText(const bool& value, std::string& output) +{ + output = value ? "true" : "false"; +} + +template <> inline +bool readText(const std::string& input, bool& value) +{ + std::string tmp = input; + zen::trim(tmp); + if (tmp == "true") + value = true; + else if (tmp == "false") + value = false; + else + return false; + return true; +} +} +\endcode +*/ + + +///Convert text to user data - used by XML elements and attributes +/** + \param input Input text. + \param value Conversion target value. + \return "true" if value was read successfully. +*/ +template bool readText(const std::string& input, T& value); +///Convert user data into text - used by XML elements and attributes +/** + \param value The value to be converted. + \param output Output text. +*/ +template void writeText(const T& value, std::string& output); + + +/* Different classes of data types: + +----------------------------- +| structured | readStruc/writeStruc - e.g. string-convertible types, STL containers, std::pair, structured user types +| ------------------------- | +| | to-string-convertible | | readText/writeText - e.g. string-like types, all built-in arithmetic numbers, bool +| | --------------- | | +| | | string-like | | | utfCvrtTo - e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... +| | --------------- | | +| ------------------------- | +----------------------------- +*/ + + + + + + + + + + + + + + + +//------------------------------ implementation ------------------------------------- + +//Conversion from arbitrary types to text (for use with XML elements and attributes) +enum TextType +{ + TEXT_TYPE_BOOL, + TEXT_TYPE_NUMBER, + TEXT_TYPE_STRING, + TEXT_TYPE_OTHER, +}; + +template +struct GetTextType : StaticEnum::value ? TEXT_TYPE_BOOL : + IsStringLike::value ? TEXT_TYPE_STRING : //string before number to correctly handle char/wchar_t -> this was an issue with Loki only! + IsArithmetic::value ? TEXT_TYPE_NUMBER : // + TEXT_TYPE_OTHER> {}; + +//###################################################################################### + +template +struct ConvertText; +/* -> expected interface +{ + void writeText(const T& value, std::string& output) const; + bool readText(const std::string& input, T& value) const; +}; +*/ + +//partial specialization: type bool +template +struct ConvertText +{ + void writeText(bool value, std::string& output) const + { + output = value ? "true" : "false"; + } + bool readText(const std::string& input, bool& value) const + { + std::string tmp = input; + zen::trim(tmp); + if (tmp == "true") + value = true; + else if (tmp == "false") + value = false; + else + return false; + return true; + } +}; + +//partial specialization: handle conversion for all built-in arithmetic types! +template +struct ConvertText +{ + void writeText(const T& value, std::string& output) const + { + output = numberTo(value); + } + bool readText(const std::string& input, T& value) const + { + value = stringTo(input); + return true; + } +}; + +//partial specialization: handle conversion for all string-like types! +template +struct ConvertText +{ + void writeText(const T& value, std::string& output) const + { + output = utfCvrtTo(value); + } + bool readText(const std::string& input, T& value) const + { + value = utfCvrtTo(input); + return true; + } +}; + + +//partial specialization: unknown type +template +struct ConvertText +{ + //########################################################################################################################################### + assert_static(sizeof(T) == -1); + /* + ATTENTION: The data type T is yet unknown to the zen::Xml framework! + + Please provide a specialization for T of the following two functions in order to handle conversions to XML elements and attributes + + template <> void zen::writeText(const T& value, std::string& output) + template <> bool zen::readText(const std::string& input, T& value) + + If T is structured and cannot be converted to a text representation specialize these two functions to allow at least for conversions to XML elements: + + template <> void zen::writeStruc(const T& value, XmlElement& output) + template <> bool zen::readStruc(const XmlElement& input, T& value) + */ + //########################################################################################################################################### +}; + + +template inline +void writeText(const T& value, std::string& output) +{ + ConvertText::value>().writeText(value, output); +} + + +template inline +bool readText(const std::string& input, T& value) +{ + return ConvertText::value>().readText(input, value); +} +} + +#endif //ZEN_XML_CONVERT_TEXT_HEADER_018727339083427097434 diff --git a/zenxml/zenxml/dom.h b/zenxml/zenxml/dom.h new file mode 100644 index 00000000..a2ed78f7 --- /dev/null +++ b/zenxml/zenxml/dom.h @@ -0,0 +1,335 @@ +// ************************************************************************** +// * This file is part of the zen::Xml project. It is distributed under the * +// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef ZEN_XML_DOM_HEADER_82085720723894567204564256 +#define ZEN_XML_DOM_HEADER_82085720723894567204564256 + +#include +#include +#include +#include +#include "cvrt_text.h" //"readText/writeText" + +namespace zen +{ +class XmlDoc; + +/// An XML element +class XmlElement +{ + struct PrivateConstruction {}; +public: + //Construct an empty XML element + //This constructor should be private, however std::make_shared() requires public access + //Therefore at least prevent users from calling it via private dummy type PrivateConstruction + template + XmlElement(const String& name, XmlElement* parentElement, PrivateConstruction) : name_(utfCvrtTo(name)), parent_(parentElement) {} + + ///Retrieve the name of this XML element. + /** + \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ... + \returns Name of the XML element. + */ + template + String getNameAs() const { return utfCvrtTo(name_); } + + ///Get the value of this element as a user type. + /** + \tparam T Arbitrary user data type: e.g. any string class, all built-in arithmetic numbers, STL container, ... + \returns "true" if Xml element was successfully converted to value, cannot fail for string-like types + */ + template + bool getValue(T& value) const { return readStruc(*this, value); } + + ///Set the value of this element. + /** + \tparam T Arbitrary user data type: e.g. any string-like type, all built-in arithmetic numbers, STL container, ... + */ + template + void setValue(const T& value) { writeStruc(value, *this); } + + ///Retrieve an attribute by name. + /** + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + \tparam T String-convertible user data type: e.g. any string class, all built-in arithmetic numbers + \param name The name of the attribute to retrieve. + \param value The value of the attribute converted to T. + \return "true" if value was retrieved successfully. + */ + template + bool getAttribute(const String& name, T& value) const + { + auto it = attributes.find(utfCvrtTo(name)); + return it == attributes.end() ? false : readText(it->second, value); + } + + ///Create or update an XML attribute. + /** + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + \tparam T String-convertible user data type: e.g. any string-like type, all built-in arithmetic numbers + \param name The name of the attribute to create or update. + \param value The value to set. + */ + template + void setAttribute(const String& name, const T& value) + { + std::string attrValue; + writeText(value, attrValue); + attributes[utfCvrtTo(name)] = attrValue; + } + + ///Remove the attribute with the given name. + /** + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + */ + template + void removeAttribute(const String& name) { attributes.erase(utfCvrtTo(name)); } + + ///Create a new child element and return a reference to it. + /** + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + \param name The name of the child element to be created. + */ + template + XmlElement& addChild(const String& name) + { + std::string utf8Name = utfCvrtTo(name); + auto newElement = std::make_shared(utf8Name, this, PrivateConstruction()); + childElements.push_back(newElement); + childElementsSorted.insert(std::make_pair(utf8Name, newElement)); + return *newElement; + } + + ///Retrieve a child element with the given name. + /** + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + \param name The name of the child element to be retrieved. + \return A pointer to the child element or nullptr if none was found. + */ + template + const XmlElement* getChild(const String& name) const + { + auto it = childElementsSorted.find(utfCvrtTo(name)); + return it == childElementsSorted.end() ? nullptr : &*(it->second); + } + + ///\sa getChild + template + XmlElement* getChild(const String& name) + { + return const_cast(static_cast(this)->getChild(name)); + } + + template < class IterTy, //underlying iterator type + class T, //target object type + class AccessPolicy > //access policy: see AccessPtrMap + class PtrIter : public std::iterator, private AccessPolicy //get rid of shared_ptr indirection + { + public: + PtrIter(IterTy it) : it_(it) {} + PtrIter(const PtrIter& other) : it_(other.it_) {} + PtrIter& operator++() { ++it_; return *this; } + PtrIter operator++(int) { PtrIter tmp(*this); operator++(); return tmp; } + inline friend bool operator==(const PtrIter& lhs, const PtrIter& rhs) { return lhs.it_ == rhs.it_; } + inline friend bool operator!=(const PtrIter& lhs, const PtrIter& rhs) { return !(lhs == rhs); } + T& operator* () { return AccessPolicy::template objectRef(it_); } + T* operator->() { return &AccessPolicy::template objectRef(it_); } + private: + IterTy it_; + }; + + struct AccessPtrMap + { + template + T& objectRef(const IterTy& it) { return *(it->second); } + }; + + typedef PtrIter>::iterator, XmlElement, AccessPtrMap> ChildIter2; + typedef PtrIter>::const_iterator, const XmlElement, AccessPtrMap> ChildIterConst2; + + ///Access all child elements with the given name via STL iterators. + /** + \code + auto iterPair = elem.getChildren("Item"); + std::for_each(iterPair.first, iterPair.second, + [](const XmlElement& child) { ... }); + \endcode + \param name The name of the child elements to be retrieved. + \return A pair of STL begin/end iterators to access the child elements sequentially. + */ + template + std::pair getChildren(const String& name) const { return childElementsSorted.equal_range(utfCvrtTo(name)); } + + ///\sa getChildren + template + std::pair getChildren(const String& name) { return childElementsSorted.equal_range(utfCvrtTo(name)); } + + struct AccessPtrVec + { + template + T& objectRef(const IterTy& it) { return **it; } + }; + + typedef PtrIter>::iterator, XmlElement, AccessPtrVec> ChildIter; + typedef PtrIter>::const_iterator, const XmlElement, AccessPtrVec> ChildIterConst; + + ///Access all child elements sequentially via STL iterators. + /** + \code + auto iterPair = elem.getChildren(); + std::for_each(iterPair.first, iterPair.second, + [](const XmlElement& child) { ... }); + \endcode + \return A pair of STL begin/end iterators to access all child elements sequentially. + */ + std::pair getChildren() const { return std::make_pair(childElements.begin(), childElements.end()); } + + ///\sa getChildren + std::pair getChildren() { return std::make_pair(childElements.begin(), childElements.end()); } + + ///Get parent XML element, may be nullptr for root element + XmlElement* parent() { return parent_; }; + ///Get parent XML element, may be nullptr for root element + const XmlElement* parent() const { return parent_; }; + + + typedef std::map::const_iterator AttrIter; + + /* -> disabled documentation extraction + \brief Get all attributes associated with the element. + \code + auto iterPair = elem.getAttributes(); + for (auto it = iterPair.first; it != iterPair.second; ++it) + std::cout << "name: " << it->first << " value: " << it->second << "\n"; + \endcode + \return A pair of STL begin/end iterators to access all attributes sequentially as a list of name/value pairs of std::string. + */ + std::pair getAttributes() const { return std::make_pair(attributes.begin(), attributes.end()); } + + //Transactionally swap two elements. -> disabled documentation extraction + void swap(XmlElement& other) + { + name_ .swap(other.name_); + value_ .swap(other.value_); + attributes.swap(other.attributes); + childElements.swap(other.childElements); + childElementsSorted.swap(other.childElementsSorted); + //std::swap(parent_, other.parent_); -> parent is physical location; update children's parent reference instead: + std::for_each( childElements.begin(), childElements.end(), [&](const std::shared_ptr& child) { child->parent_ = this; }); + std::for_each(other.childElements.begin(), other.childElements.end(), [&](const std::shared_ptr& child) { child->parent_ = &other; }); + } + +private: + friend class XmlDoc; + + XmlElement(const XmlElement&); //not implemented + XmlElement& operator=(const XmlElement&); // + + std::string name_; + std::string value_; + std::map attributes; + std::vector> childElements; //all child elements in order of creation + std::multimap> childElementsSorted; //alternate key: sorted by element name + XmlElement* parent_; +}; + + +//XmlElement::setValue() calls zen::writeStruc() which calls XmlElement::setValue() ... => these two specializations end the circle +template <> inline +void XmlElement::setValue(const std::string& value) { value_ = value; } + +template <> inline +bool XmlElement::getValue(std::string& value) const { value = value_; return true; } + + +///The complete XML document +class XmlDoc +{ +public: + ///Default constructor setting up an empty XML document with a standard declaration: + XmlDoc() : version_("1.0"), encoding_("UTF-8"), rootElement("Root", nullptr, XmlElement::PrivateConstruction()) {} + + XmlDoc(XmlDoc&& tmp) : rootElement("Root", nullptr, XmlElement::PrivateConstruction()) { swap(tmp); } + XmlDoc& operator=(XmlDoc&& tmp) { swap(tmp); return *this; } + + //Setup an empty XML document + /** + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + \param rootName The name of the XML document's root element. + */ + template + XmlDoc(String rootName) : version_("1.0"), encoding_("UTF-8"), rootElement(rootName, nullptr, XmlElement::PrivateConstruction()) {} + + ///Get a const reference to the document's root element. + const XmlElement& root() const { return rootElement; } + ///Get a reference to the document's root element. + XmlElement& root() { return rootElement; } + + ///Get the version used in the XML declaration. + /** + \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ... + */ + template + String getVersionAs() const { return utfCvrtTo(version_); } + + ///Set the version used in the XML declaration. + /** + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + */ + template + void setVersion(const String& version) { version_ = utfCvrtTo(version); } + + ///Get the encoding used in the XML declaration. + /** + \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ... + */ + template + String getEncodingAs() const { return utfCvrtTo(encoding_); } + + ///Set the encoding used in the XML declaration. + /** + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + */ + template + void setEncoding(const String& encoding) { encoding_ = utfCvrtTo(encoding); } + + ///Get the standalone string used in the XML declaration. + /** + \tparam String Arbitrary string class: e.g. std::string, std::wstring, wxString, MyStringClass, ... + */ + template + String getStandaloneAs() const { return utfCvrtTo(standalone_); } + + ///Set the standalone string used in the XML declaration. + /** + \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... + */ + template + void setStandalone(const String& standalone) { standalone_ = utfCvrtTo(standalone); } + + //Transactionally swap two elements. -> disabled documentation extraction + void swap(XmlDoc& other) + { + version_ .swap(other.version_); + encoding_ .swap(other.encoding_); + standalone_.swap(other.standalone_); + rootElement.swap(other.rootElement); + } + +private: + XmlDoc(const XmlDoc&); //not implemented, thanks to XmlElement::parent_ + XmlDoc& operator=(const XmlDoc&); // + + std::string version_; + std::string encoding_; + std::string standalone_; + + XmlElement rootElement; +}; + +} + +#endif //ZEN_XML_DOM_HEADER_82085720723894567204564256 diff --git a/zenxml/zenxml/error.h b/zenxml/zenxml/error.h new file mode 100644 index 00000000..bea04520 --- /dev/null +++ b/zenxml/zenxml/error.h @@ -0,0 +1,19 @@ +// ************************************************************************** +// * This file is part of the zen::Xml project. It is distributed under the * +// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef ZEN_XML_ERROR_HEADER_018734618433021489473214873214 +#define ZEN_XML_ERROR_HEADER_018734618433021489473214873214 + +namespace zen +{ +///Exception base class for zen::Xml +struct XmlError +{ + virtual ~XmlError() {} +}; +} + +#endif //ZEN_XML_ERROR_HEADER_018734618433021489473214873214 diff --git a/zenxml/zenxml/io.h b/zenxml/zenxml/io.h new file mode 100644 index 00000000..596d7acf --- /dev/null +++ b/zenxml/zenxml/io.h @@ -0,0 +1,125 @@ +// ************************************************************************** +// * This file is part of the zen::Xml project. It is distributed under the * +// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef ZEN_XML_IO_HEADER_8917640501480763248343343 +#define ZEN_XML_IO_HEADER_8917640501480763248343343 + +#include +#include +#include +#include +#include "error.h" + +namespace zen +{ +/** +\file +\brief Save and load byte streams from files +*/ + +#if !defined(ZEN_WIN) && !defined(ZEN_LINUX) && !defined(ZEN_MAC) +#error Please specify your platform: #define ZEN_WIN, ZEN_LINUX or ZEN_MAC +#endif + + +///Exception thrown due to failed file I/O +struct XmlFileError : public XmlError +{ + typedef int ErrorCode; + + explicit XmlFileError(ErrorCode ec) : lastError(ec) {} + ///Native error code: errno + ErrorCode lastError; +}; + + +#ifdef ZEN_WIN +namespace implemenation //sad but true +{ +template inline +FILE* fopen(const String& filename, const wchar_t* mode) +{ +#ifdef _MSC_VER + FILE* handle = nullptr; + errno_t rv = ::_wfopen_s(&handle, utfCvrtTo(filename).c_str(), mode); //more secure? + (void)rv; + return handle; +#else + return ::_wfopen(utfCvrtTo(filename).c_str(), mode); +#endif +} +} +#endif + + +///Save byte stream to a file +/** +\tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... +\param stream Input byte stream +\param filename Output file name +\throw XmlFileError +*/ +template +void saveStream(const std::string& stream, const String& filename) //throw XmlFileError +{ +#ifdef ZEN_WIN + FILE* handle = implemenation::fopen(utfCvrtTo(filename).c_str(), L"wb"); +#else + FILE* handle = ::fopen(utfCvrtTo(filename).c_str(), "w"); +#endif + if (handle == nullptr) + throw XmlFileError(errno); + ZEN_ON_SCOPE_EXIT(::fclose(handle)); + + const size_t bytesWritten = ::fwrite(stream.c_str(), 1, stream.size(), handle); + if (::ferror(handle) != 0) + throw XmlFileError(errno); + + (void)bytesWritten; + assert(bytesWritten == stream.size()); +} + + +///Load byte stream from a file +/** +\tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... +\param filename Input file name +\return Output byte stream +\throw XmlFileError +*/ +template +std::string loadStream(const String& filename) //throw XmlFileError +{ +#ifdef ZEN_WIN + FILE* handle = implemenation::fopen(utfCvrtTo(filename).c_str(), L"rb"); +#else + FILE* handle = ::fopen(utfCvrtTo(filename).c_str(), "r"); +#endif + if (handle == nullptr) + throw XmlFileError(errno); + ZEN_ON_SCOPE_EXIT(::fclose(handle)); + + std::string stream; + const size_t blockSize = 64 * 1024; + do + { + stream.resize(stream.size() + blockSize); //let's pray std::string implements exponential growth! + + const size_t bytesRead = ::fread(&*(stream.begin() + stream.size() - blockSize), 1, blockSize, handle); + if (::ferror(handle)) + throw XmlFileError(errno); + if (bytesRead > blockSize) + throw XmlFileError(0); + if (bytesRead < blockSize) + stream.resize(stream.size() - (blockSize - bytesRead)); //caveat: unsigned arithmetics + } + while (!::feof(handle)); + + return stream; +} +} + +#endif //ZEN_XML_IO_HEADER_8917640501480763248343343 diff --git a/zenxml/zenxml/parser.h b/zenxml/zenxml/parser.h new file mode 100644 index 00000000..51779852 --- /dev/null +++ b/zenxml/zenxml/parser.h @@ -0,0 +1,618 @@ +// ************************************************************************** +// * This file is part of the zen::Xml project. It is distributed under the * +// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef ZEN_XML_PARSER_HEADER_81248670213764583021432 +#define ZEN_XML_PARSER_HEADER_81248670213764583021432 + +#include +#include //ptrdiff_t; req. on Linux +#include +#include "dom.h" +#include "error.h" + +namespace zen +{ +/** +\file +\brief Convert an XML document object model (class XmlDoc) to and from a byte stream representation. +*/ + +///Save XML document as a byte stream +/** +\param doc Input XML document +\param lineBreak Line break, default: carriage return + new line +\param indent Indentation, default: four space characters +\return Output byte stream +*/ +std::string serialize(const XmlDoc& doc, + const std::string& lineBreak = "\r\n", + const std::string& indent = " "); //throw () + +///Exception thrown due to an XML parsing error +struct XmlParsingError : public XmlError +{ + XmlParsingError(size_t rowNo, size_t colNo) : row(rowNo), col(colNo) {} + ///Input file row where the parsing error occured (zero-based) + size_t row; //beginning with 0 + ///Input file column where the parsing error occured (zero-based) + size_t col; // +}; + + +///Load XML document from a byte stream +/** +\param stream Input byte stream +\returns Output XML document +\throw XmlParsingError +*/ +XmlDoc parse(const std::string& stream); //throw XmlParsingError + + + + + + + + + + + + + + + + + + + + +//---------------------------- implementation ---------------------------- +//see: http://www.w3.org/TR/xml/ + +namespace implementation +{ +inline +std::pair hexify(unsigned char c) +{ + auto hexifyDigit = [](int num) -> char //input [0, 15], output 0-9, A-F + { + assert(0 <= num&& num <= 15); //guaranteed by design below! + return static_cast(num <= 9 ? //no signed/unsigned char problem here! + '0' + num : + 'A' + (num - 10)); + }; + return std::make_pair(hexifyDigit(c / 16), hexifyDigit(c % 16)); +} + + +inline +char unhexify(char high, char low) +{ + auto unhexifyDigit = [](char hex) -> int //input 0-9, a-f, A-F; output range: [0, 15] + { + if ('0' <= hex && hex <= '9') //no signed/unsigned char problem here! + return hex - '0'; + else if ('A' <= hex && hex <= 'F') + return (hex - 'A') + 10; + else if ('a' <= hex && hex <= 'f') + return (hex - 'a') + 10; + assert(false); + return 0; + }; + return static_cast(16 * unhexifyDigit(high) + unhexifyDigit(low)); //[!] convert to unsigned char first, then to char (which may be signed) +}; + + +template inline +std::string normalize(const std::string& str, Predicate pred) //pred: unary function taking a char, return true if value shall be encoded as hex +{ + std::string output; + std::for_each(str.begin(), str.end(), + [&](char c) + { + if (c == '&') // + output += "&"; + else if (c == '<') //normalization mandatory: http://www.w3.org/TR/xml/#syntax + output += "<"; + else if (c == '>') // + output += ">"; + else if (pred(c)) + { + if (c == '\'') + output += "'"; + else if (c == '\"') + output += """; + else + { + output += "&#x"; + const auto hexDigits = hexify(c); //hexify beats "printNumber("&#x%02X;", c)" by a nice factor of 3! + output += hexDigits.first; + output += hexDigits.second; + output += ';'; + } + } + else + output += c; + }); + return output; +} + +inline +std::string normalizeName(const std::string& str) +{ + return normalize(str, [](char c) { return isWhiteSpace(c) || c == '=' || c == '/' || c == '\'' || c == '\"'; }); +} + +inline +std::string normalizeElementValue(const std::string& str) +{ + return normalize(str, [](char c) { return static_cast(c) < 32; }); +} + +inline +std::string normalizeAttribValue(const std::string& str) +{ + return normalize(str, [](char c) { return static_cast(c) < 32 || c == '\'' || c == '\"'; }); +} + + +template inline +bool checkEntity(CharIterator& first, CharIterator last, const char (&placeholder)[N]) +{ + assert(placeholder[N - 1] == 0); + const ptrdiff_t strLen = N - 1; //don't count null-terminator + if (last - first >= strLen && std::equal(first, first + strLen, placeholder)) + { + first += strLen - 1; + return true; + } + return false; +} + + +namespace +{ +std::string denormalize(const std::string& str) +{ + std::string output; + for (auto it = str.begin(); it != str.end(); ++it) + { + const char c = *it; + + if (c == '&') + { + if (checkEntity(it, str.end(), "&")) + output += '&'; + else if (checkEntity(it, str.end(), "<")) + output += '<'; + else if (checkEntity(it, str.end(), ">")) + output += '>'; + else if (checkEntity(it, str.end(), "'")) + output += '\''; + else if (checkEntity(it, str.end(), """)) + output += '\"'; + else if (str.end() - it >= 6 && + it[1] == '#' && + it[2] == 'x' && + it[5] == ';') + { + output += unhexify(it[3], it[4]); //unhexify beats "::sscanf(&it[3], "%02X", &tmp)" by a factor of 3000 for ~250000 calls!!! + it += 5; + } + else + output += c; //unexpected char! + } + else if (c == '\r') //map all end-of-line characters to \n http://www.w3.org/TR/xml/#sec-line-ends + { + auto itNext = it + 1; + if (itNext != str.end() && *itNext == '\n') + ++it; + output += '\n'; + } + else + output += c; + }; + return output; +} + + +void serialize(const XmlElement& element, std::string& stream, + const std::string& lineBreak, + const std::string& indent, + size_t indentLevel) +{ + const std::string& nameFmt = normalizeName(element.getNameAs()); + + for (size_t i = 0; i < indentLevel; ++i) + stream += indent; + + stream += '<' + nameFmt; + + auto attr = element.getAttributes(); + for (auto it = attr.first; it != attr.second; ++it) + stream += ' ' + normalizeName(it->first) + "=\"" + normalizeAttribValue(it->second) + '\"'; + + //no support for mixed-mode content + auto iterPair = element.getChildren(); + if (iterPair.first != iterPair.second) //structured element + { + stream += '>' + lineBreak; + + std::for_each(iterPair.first, iterPair.second, + [&](const XmlElement & el) { serialize(el, stream, lineBreak, indent, indentLevel + 1); }); + + for (size_t i = 0; i < indentLevel; ++i) + stream += indent; + stream += "' + lineBreak; + } + else + { + std::string value; + element.getValue(value); + + if (!value.empty()) //value element + stream += '>' + normalizeElementValue(value) + "' + lineBreak; + else //empty element + stream += "/>" + lineBreak; + } +} + +std::string serialize(const XmlDoc& doc, + const std::string& lineBreak, + const std::string& indent) +{ + std::string version = doc.getVersionAs(); + if (!version.empty()) + version = " version=\"" + normalizeAttribValue(version) + '\"'; + + std::string encoding = doc.getEncodingAs(); + if (!encoding.empty()) + encoding = " encoding=\"" + normalizeAttribValue(encoding) + '\"'; + + std::string standalone = doc.getStandaloneAs(); + if (!standalone.empty()) + standalone = " standalone=\"" + normalizeAttribValue(standalone) + '\"'; + + std::string output = "" + lineBreak; + serialize(doc.root(), output, lineBreak, indent, 0); + return output; +} +} +} + +inline +std::string serialize(const XmlDoc& doc, + const std::string& lineBreak, + const std::string& indent) { return implementation::serialize(doc, lineBreak, indent); } + +/* +Grammar for XML parser +------------------------------- +document-expression: + + element-expression: + +element-expression: + + pm-expression + +element-list-expression: + + element-expression element-list-expression + +attributes-expression: + + string="string" attributes-expression + +pm-expression: + string + element-list-expression +*/ + +namespace implementation +{ +struct Token +{ + enum Type + { + TK_LESS, + TK_GREATER, + TK_LESS_SLASH, + TK_SLASH_GREATER, + TK_EQUAL, + TK_QUOTE, + TK_DECL_BEGIN, + TK_DECL_END, + TK_NAME, + TK_END + }; + + Token(Type t) : type(t) {} + Token(const std::string& txt) : type(TK_NAME), name(txt) {} + + Type type; + std::string name; //filled if type == TK_NAME +}; + +class Scanner +{ +public: + Scanner(const std::string& stream) : + xmlCommentBegin(""), + stream_(stream), + pos(stream_.begin()) + { + if (zen::startsWith(stream_, BYTE_ORDER_MARK_UTF8)) + pos += strLength(BYTE_ORDER_MARK_UTF8); + + tokens.push_back(std::make_pair("", Token::TK_DECL_END)); + tokens.push_back(std::make_pair("", Token::TK_SLASH_GREATER)); + tokens.push_back(std::make_pair("<" , Token::TK_LESS)); //evaluate after TK_DECL_BEGIN! + tokens.push_back(std::make_pair(">" , Token::TK_GREATER)); + tokens.push_back(std::make_pair("=" , Token::TK_EQUAL)); + tokens.push_back(std::make_pair("\"", Token::TK_QUOTE)); + tokens.push_back(std::make_pair("\'", Token::TK_QUOTE)); + } + + Token nextToken() //throw XmlParsingError + { + //skip whitespace + pos = std::find_if(pos, stream_.end(), [](char c) { return !zen::isWhiteSpace(c); }); + + if (pos == stream_.end()) + return Token::TK_END; + + //skip XML comments + if (startsWith(xmlCommentBegin)) + { + auto it = std::search(pos + xmlCommentBegin.size(), stream_.end(), xmlCommentEnd.begin(), xmlCommentEnd.end()); + if (it != stream_.end()) + { + pos = it + xmlCommentEnd.size(); + return nextToken(); + } + } + + for (auto it = tokens.begin(); it != tokens.end(); ++it) + if (startsWith(it->first)) + { + pos += it->first.size(); + return it->second; + } + + auto nameEnd = std::find_if(pos, stream_.end(), [](char c) + { + return c == '<' || + c == '>' || + c == '=' || + c == '/' || + c == '\'' || + c == '\"' || + zen::isWhiteSpace(c); + }); + + if (nameEnd != pos) + { + std::string name(&*pos, nameEnd - pos); + pos = nameEnd; + return implementation::denormalize(name); + } + + //unknown token + throw XmlParsingError(posRow(), posCol()); + } + + std::string extractElementValue() + { + auto it = std::find_if(pos, stream_.end(), [](char c) + { + return c == '<' || + c == '>'; + }); + std::string output(pos, it); + pos = it; + return implementation::denormalize(output); + } + + std::string extractAttributeValue() + { + auto it = std::find_if(pos, stream_.end(), [](char c) + { + return c == '<' || + c == '>' || + c == '\'' || + c == '\"'; + }); + std::string output(pos, it); + pos = it; + return implementation::denormalize(output); + } + + size_t posRow() const //current row beginning with 0 + { + const size_t crSum = std::count(stream_.begin(), pos, '\r'); //carriage returns + const size_t nlSum = std::count(stream_.begin(), pos, '\n'); //new lines + assert(crSum == 0 || nlSum == 0 || crSum == nlSum); + return std::max(crSum, nlSum); //be compatible with Linux/Mac/Win + } + + size_t posCol() const //current col beginning with 0 + { + //seek beginning of line + for (auto it = pos; it != stream_.begin(); ) + { + --it; + if (*it == '\r' || *it == '\n') + return pos - it - 1; + } + return pos - stream_.begin(); + } + +private: + Scanner(const Scanner&); + Scanner& operator=(const Scanner&); + + bool startsWith(const std::string& prefix) const + { + if (stream_.end() - pos < static_cast(prefix.size())) + return false; + return std::equal(prefix.begin(), prefix.end(), pos); + } + + typedef std::vector > TokenList; + TokenList tokens; + + const std::string xmlCommentBegin; + const std::string xmlCommentEnd; + + const std::string stream_; + std::string::const_iterator pos; +}; + + +class XmlParser +{ +public: + XmlParser(const std::string& stream) : + scn(stream), + tk(scn.nextToken()) {} + + XmlDoc parse() //throw XmlParsingError + { + XmlDoc doc; + + //declaration (optional) + if (token().type == Token::TK_DECL_BEGIN) + { + nextToken(); + + while (token().type == Token::TK_NAME) + { + std::string attribName = token().name; + nextToken(); + + consumeToken(Token::TK_EQUAL); + expectToken(Token::TK_QUOTE); + std::string attribValue = scn.extractAttributeValue(); + nextToken(); + + consumeToken(Token::TK_QUOTE); + + if (attribName == "version") + doc.setVersion(attribValue); + else if (attribName == "encoding") + doc.setEncoding(attribValue); + else if (attribName == "standalone") + doc.setStandalone(attribValue); + } + consumeToken(Token::TK_DECL_END); + } + + XmlDoc dummy; + XmlElement& elemTmp = dummy.root(); + parseChildElements(elemTmp); + + auto iterPair = elemTmp.getChildren(); + if (iterPair.first != iterPair.second) + doc.root().swap(*iterPair.first); + + expectToken(Token::TK_END); + return doc; + }; + +private: + XmlParser(const XmlParser&); + XmlParser& operator=(const XmlParser&); + + void parseChildElements(XmlElement& parent) + { + while (token().type == Token::TK_LESS) + { + nextToken(); + + expectToken(Token::TK_NAME); + std::string elementName = token().name; + nextToken(); + + XmlElement& newElement = parent.addChild(elementName); + + parseAttributes(newElement); + + if (token().type == Token::TK_SLASH_GREATER) //empty element + { + nextToken(); + continue; + } + + expectToken(Token::TK_GREATER); + std::string elementValue = scn.extractElementValue(); + nextToken(); + + //no support for mixed-mode content + if (token().type == Token::TK_LESS) //structured element + parseChildElements(newElement); + else //value element + newElement.setValue(elementValue); + + consumeToken(Token::TK_LESS_SLASH); + + if (token().type != Token::TK_NAME || + elementName != token().name) + throw XmlParsingError(scn.posRow(), scn.posCol()); + nextToken(); + + consumeToken(Token::TK_GREATER); + } + }; + + void parseAttributes(XmlElement& element) + { + while (token().type == Token::TK_NAME) + { + std::string attribName = token().name; + nextToken(); + + consumeToken(Token::TK_EQUAL); + expectToken(Token::TK_QUOTE); + std::string attribValue = scn.extractAttributeValue(); + nextToken(); + + consumeToken(Token::TK_QUOTE); + element.setAttribute(attribName, attribValue); + } + } + + const Token& token() const { return tk; } + void nextToken() { tk = scn.nextToken(); } + + void consumeToken(Token::Type t) //throw XmlParsingError + { + expectToken(t); //throw XmlParsingError + nextToken(); + } + + void expectToken(Token::Type t) //throw XmlParsingError + { + if (token().type != t) + throw XmlParsingError(scn.posRow(), scn.posCol()); + } + + Scanner scn; + Token tk; +}; +} + +inline +XmlDoc parse(const std::string& stream) //throw XmlParsingError +{ + return implementation::XmlParser(stream).parse(); //throw XmlParsingError +} +} + +#endif //ZEN_XML_PARSER_HEADER_81248670213764583021432 diff --git a/zenxml/zenxml/unit_test.cpp b/zenxml/zenxml/unit_test.cpp new file mode 100644 index 00000000..8f49de6f --- /dev/null +++ b/zenxml/zenxml/unit_test.cpp @@ -0,0 +1,95 @@ +// ************************************************************************** +// * This file is part of the zen::Xml project. It is distributed under the * +// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include +#include +#include +#include +#include +#include +#include "xml.h" + +using namespace zen; + +namespace +{ +void unit_test() +{ + class Dummy {}; + + //compile time checks only + + assert_static(!IsStlContainer ::value); + assert_static(!IsStlContainer ::value); + assert_static(!IsStlContainer ::value); + assert_static(!IsStlContainer ::value); + assert_static(IsStlContainer> ::value); + assert_static(IsStlContainer> ::value); + assert_static(IsStlContainer> ::value); + assert_static((IsStlContainer> ::value)); + assert_static((IsStlContainer>::value)); + assert_static(IsStlContainer > ::value); + assert_static((IsStlPair > ::value)); + assert_static(!IsStlPair ::value); + + assert_static(!IsStringLike::value); + assert_static(!IsStringLike::value); + assert_static(!IsStringLike::value); + assert_static(!IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + assert_static(IsStringLike::value); + + assert_static(!(IsSameType::Type, char>::value)); + assert_static(!(IsSameType::Type, char>::value)); + assert_static(!(IsSameType::Type, char>::value)); + assert_static((IsSameType::Type, char>::value)); + assert_static((IsSameType::Type, wchar_t>::value)); + assert_static((IsSameType::Type, char>::value)); + assert_static((IsSameType::Type, wchar_t>::value)); + assert_static((IsSameType::Type, char>::value)); + assert_static((IsSameType::Type, wchar_t>::value)); + assert_static((IsSameType::Type, char>::value)); + assert_static((IsSameType::Type, wchar_t>::value)); + assert_static((IsSameType::Type, char>::value)); + assert_static((IsSameType::Type, char>::value)); + assert_static((IsSameType::Type, wchar_t>::value)); + assert_static((IsSameType::Type, wchar_t>::value)); + assert_static((IsSameType::Type, char>::value)); + assert_static((IsSameType::Type, wchar_t>::value)); + assert_static((IsSameType::Type, char>::value)); + assert_static((IsSameType::Type, wchar_t>::value)); + assert_static((IsSameType::Type, char>::value)); + assert_static((IsSameType::Type, wchar_t>::value)); + assert_static((IsSameType::Type, char>::value)); + assert_static((IsSameType::Type, wchar_t>::value)); + assert_static((IsSameType::Type, char>::value)); + assert_static((IsSameType::Type, wchar_t>::value)); + assert_static((IsSameType::Type, char>::value)); + assert_static((IsSameType::Type, wchar_t>::value)); +} +} diff --git a/zenxml/zenxml/xml.h b/zenxml/zenxml/xml.h new file mode 100644 index 00000000..3a01b1a1 --- /dev/null +++ b/zenxml/zenxml/xml.h @@ -0,0 +1,15 @@ +// ************************************************************************** +// * This file is part of the zen::Xml project. It is distributed under the * +// * Boost Software License: http://www.boost.org/LICENSE_1_0.txt * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef ZEN_XML_HEADER_349578228034572457454554 +#define ZEN_XML_HEADER_349578228034572457454554 + +#include "bind.h" + +/// The zen::Xml namespace +namespace zen {} + +#endif //ZEN_XML_HEADER_349578228034572457454554 \ No newline at end of file -- cgit