diff options
129 files changed, 6425 insertions, 4627 deletions
diff --git a/Application.cpp b/Application.cpp index 0ae5e9f1..f1be7262 100644 --- a/Application.cpp +++ b/Application.cpp @@ -149,7 +149,7 @@ bool Application::OnInit() 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 + //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); @@ -160,6 +160,13 @@ bool Application::OnInit() } +int Application::OnExit() +{ + releaseWxLocale(); + return wxApp::OnExit(); +} + + void Application::onEnterEventLoop(wxEvent& event) { Disconnect(EVENT_ENTER_EVENT_LOOP, wxEventHandler(Application::onEnterEventLoop), nullptr, this); diff --git a/Application.h b/Application.h index b75ded34..11cdb494 100644 --- a/Application.h +++ b/Application.h @@ -8,10 +8,10 @@ #define FREEFILESYNCAPP_H #include <vector> +#include <zen/zstring.h> #include <wx/app.h> //#include "lib/process_xml.h" #include "lib/return_codes.h" -#include <zen/zstring.h> class Application : public wxApp @@ -21,8 +21,8 @@ public: private: virtual bool OnInit(); + virtual int OnExit(); virtual int OnRun(); - virtual int OnExit() { return 0; } virtual bool OnExceptionInMainLoop() { throw; } //just re-throw and avoid display of additional messagebox: it will be caught in OnRun() #ifdef FFS_MAC diff --git a/BUILD/Changelog.txt b/BUILD/Changelog.txt index b43688af..58bfe35b 100644 --- a/BUILD/Changelog.txt +++ b/BUILD/Changelog.txt @@ -2,15 +2,47 @@ |Changelog| =========== +FreeFileSync 5.16 +----------------- +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 +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 ----------------- New menu option to activate/deactivate automatic update checking Show status message while checking for program updates -Faster startup times through asynchronous config file checking +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 scrollbars for multiple folder pairs +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) @@ -20,12 +52,12 @@ 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 tooltip when dragging outside visible area +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 startup (ReactOS) +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 @@ -53,7 +85,7 @@ 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 popup on first start +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) @@ -134,7 +166,7 @@ FreeFileSync 5.9 ---------------- 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 dropdown list (Linux) +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) @@ -156,7 +188,7 @@ 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 "popup" +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 @@ -187,13 +219,13 @@ 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 logfile into volume root directory +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 tooltip describing items that differ in attributes only +More detailed tool tip describing items that differ in attributes only Added Scottish Gaelic translation @@ -257,11 +289,11 @@ 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 tooltip if multiple folder pairs are used +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 occurrs +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) @@ -285,11 +317,11 @@ 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 tooltip being shown if directory name changes +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 subdirectories of base directories +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 @@ -304,7 +336,7 @@ 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 tooltip occasionally going blank (Linux) +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) @@ -335,7 +367,7 @@ 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 logoff +Save settings before forced exit due to shutdown or log off Updated translation files @@ -383,10 +415,10 @@ FreeFileSync 4.1 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 startup +Removed busy cursor after program start up RealtimeSync: atomically detect missing directories Handle not existing reference by volume name as an invalid path -Improved startup responsiveness by checking dir/file existence asynchronously +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 @@ -445,16 +477,16 @@ RealtimeSync: Failure notification if command line is invalid (Linux) FreeFileSync 3.19 ----------------- -Exclude subdirectories from synchronization which cannot be accessed during comparison +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 dropdown menu on comparison and synchronization settings button +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 timespan +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 @@ -475,8 +507,8 @@ 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: zenXML -Addded Hebrew language -Addded Danish language +Added Hebrew language +Added Danish language Updated translation files @@ -484,7 +516,7 @@ FreeFileSync 3.17 ----------------- Filter files by size Filter latest files by time span -Launcher automatically selecting 32/64 bit executable on startup +Launcher automatically selecting 32/64 bit executable on start up More detailed systray progress indicator New database format for <Automatic> mode: a full sync is suggested before upgrading Update database at individual file level (support for partial and aborted syncs) @@ -501,8 +533,8 @@ 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 whitespace -Addded Ukrainian language +Allow base directory names having trailing white-space +Added Ukrainian language Updated translation files @@ -516,7 +548,7 @@ FreeFileSync 3.15 ----------------- 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: [<volume-name>]\<path> (usecase: variable drive letters, RealtimeSync) +Volumes can be specified by name: [<volume-name>]\<path> (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 @@ -525,7 +557,7 @@ 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 tooltip +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) @@ -535,15 +567,15 @@ Added macro %weekday% FreeFileSync 3.14 ----------------- New keyboard shortcuts: F5: compare F6: synchronize -Skip to next folder pair if fatal error occured (instead of abort) -Reload last selected configuration on startup +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 logfiles can be specified -Don't condense whitespace when loading XML configuration +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 @@ -567,8 +599,8 @@ 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 commandline -Show RealtimeSync job name as systray tooltip +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 @@ -582,7 +614,7 @@ FreeFileSync 3.12 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 tooltip foreground text color (Linux) +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 @@ -596,7 +628,7 @@ 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 tooltips correctly (Linux) +Handle empty tool tips correctly (Linux) Updated translation files @@ -610,7 +642,7 @@ FreeFileSync 3.10 ----------------- 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 tooltip and label text (resolves macros and relative paths) +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 @@ -619,7 +651,7 @@ 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 <FolderPairsMax>) -Added tooltips to display long filenames on main grid +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 @@ -640,9 +672,9 @@ Copy file and folder permissions (requires admin rights): 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 filesize: group symlinks before directories +Sort by file size: group symlinks before directories Added macros %week%, %month%, %year% for creating time-stamped directories -Touch database file when changes occured only +Touch database file when changes occurred only Moved settings "file time tolerance" and "verify copied files" to GlobalSettings.xml Updated translation files @@ -651,11 +683,11 @@ FreeFileSync 3.8 ---------------- New options handling Symlinks: ignore/direct/follow => warning: new database format for <Automatic> mode Fixed crash when starting sync for Windows XP SP2 -Prevent tooltip from stealing focus +Prevent tool tip from stealing focus Show associated file icons (Linux) Run folder existence checks in separate thread (faster network share access) Write <Automatic> mode database file even if both sides are already in sync -Don't raise status dialog to the top after synchronisation +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 @@ -665,7 +697,7 @@ Updated translation files FreeFileSync 3.7 ---------------- -RealtimeSync: Trigger commandline only if all directories are existing +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 @@ -714,8 +746,8 @@ 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 helpfile: Run RealtimeSync as Service -Prefix logfiles with name of batchjob +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 @@ -727,7 +759,7 @@ FreeFileSync 3.3 ---------------- New installer package for portable/local/32/64-bit versions Built-in support for very long filenames: apply \\?\-prefix automatically -New button for synchonization preview: show equal files +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 @@ -737,7 +769,7 @@ Significantly faster file icon loading Applied new IFileOperation interface for recycle bin (Windows >= Vista) Patched <Automatic> mode to handle FAT32 2-second file time precision Play optional sound after comparison: "Compare_Complete.wav" -Allow environment variables for logfile-directory +Allow environment variables for log file-directory Enhanced conflict reporting Added Swedish translation Updated translation files @@ -755,7 +787,7 @@ 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 dropdown list via DEL key +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 @@ -766,7 +798,7 @@ Updated translation files FreeFileSync 3.1 ---------------- -Support for multiple datasources in Automatic mode +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 @@ -807,7 +839,7 @@ 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 subdirectories when manually changing sync-direction +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 @@ -821,10 +853,10 @@ FreeFileSync 2.2 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 tooltip in middle grid showing detailed information (including conflicts) +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 tooltip showing progress for silent batchmode +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 @@ -832,7 +864,7 @@ 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 makros for double-click commandline: %name, %dir, %nameCo, %dirCo +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 @@ -880,7 +912,7 @@ New category type "conflict" New check for unresolved conflicts Improved overall GUI layout New check for erroneous file modification dates -Optional popup to notify on changed configuration +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 @@ -898,10 +930,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 scrollbar in middle grid for Linux build +Finally got rid of scroll bar in middle grid for Linux build Fixed issue with file icon display Fixed overlapping grid cells -Alternate logfile directory configurable via GUI +Alternate log file directory configurable via GUI Added drag & drop support for batch job assembly Simplyfied filter usage: - <dirname> matches "<dirname>\*" as well as "<dirname>\" - only distinct filter entries are considered @@ -935,7 +967,7 @@ 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 logfile directory configurable via *.ffs_batch Xml +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 @@ -957,11 +989,11 @@ 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: "popup, ignore errors, exit with returncode < 0" +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 recylcer option for manual deletion +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 @@ -1036,7 +1068,7 @@ 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 cancelling compare +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 @@ -1059,7 +1091,7 @@ FreeFileSync 1.10 ----------------- Transformed configuration file format to XML Exchanged batch files with shell links for full Unicode support (Windows-only) -Improved filter usage: ignore leading/trailing whitespace, upper/lower-case (Windows-only) chars +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 @@ -1081,8 +1113,8 @@ Delete elements in configuration history list via DELETE key FreeFileSync 1.8 ---------------- -Enhanced statusbar information -Enhanced logfile information +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 @@ -1109,7 +1141,7 @@ Flexible filter options depending on compare variant Improved synchronization statistics Fixed issue when trying to delete system folders Usability improvements -Recycle Bin usage as commandline parameter +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 @@ -1119,7 +1151,7 @@ Updated German translation FreeFileSync 1.5 ---------------- Improved speed of comparison by file content -Simplified and optimized calculation of accumulated filesizes +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 @@ -1129,7 +1161,7 @@ Updated German translation FreeFileSync 1.4 ---------------- -Implemented generic multithreading class to keep "compare by content" and "file synchronisation" responsive +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 @@ -1141,7 +1173,7 @@ Updated German translation FreeFileSync 1.3 ---------------- -Maintain and load different configurations by drag&drop, load-button or commandline +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 @@ -1157,20 +1189,20 @@ Updated German translation FreeFileSync 1.2 ---------------- New progress indicator and status information when synchronizing: - ->available for commandline mode and UI mode: Status update and final error report + ->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 commandline -New commandline option "-skiperrors" to continue synchronization despite errors -Enhanced logfile (-silent mode) to include all errors during compare and synchronization +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 commandline mode to report success or failure +Added different return values when used in command line mode to report success or failure Updated German translation diff --git a/BUILD/Compare_Complete.wav b/BUILD/Compare_Complete.wav Binary files differdeleted file mode 100644 index f304e6d7..00000000 --- a/BUILD/Compare_Complete.wav +++ /dev/null diff --git a/BUILD/FreeFileSync.chm b/BUILD/FreeFileSync.chm Binary files differindex 98150320..226e4af3 100644 --- a/BUILD/FreeFileSync.chm +++ b/BUILD/FreeFileSync.chm diff --git a/BUILD/Languages/arabic.lng b/BUILD/Languages/arabic.lng index 668ee713..b4301431 100644 --- a/BUILD/Languages/arabic.lng +++ b/BUILD/Languages/arabic.lng @@ -20,7 +20,7 @@ <target>مجموع الوقت:</target> <source>Cannot set directory lock for %x.</source> -<target></target> +<target>تعذر إنشاء Ù‚ÙÙ„ للمسار %x.</target> <source>Show in Explorer</source> <target>إظهار ÙÙŠ المستكشÙ</target> @@ -68,7 +68,7 @@ <target>نسخ</target> <source>Paste</source> -<target></target> +<target>لصق</target> <source>Save as batch job</source> <target>ØÙظ كمهمة دÙعية</target> @@ -115,28 +115,28 @@ <source>Cannot resolve symbolic link %x.</source> <target>لا يمكن ØÙ„ الارتباط الرمزي %x.</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source> <pluralform>1 Byte</pluralform> <pluralform>%x Bytes</pluralform> </source> <target> -<pluralform>%x Bytes</pluralform> +<pluralform>0 Bytes</pluralform> <pluralform>1 Byte</pluralform> -<pluralform>%x Bytes</pluralform> +<pluralform>2 Bytes</pluralform> <pluralform>%x Bytes</pluralform> <pluralform>%x Bytes</pluralform> <pluralform>%x Bytes</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>مل٠قاعدة البيانات %x غير متواÙÙ‚.</target> @@ -173,44 +173,44 @@ <source>Waiting while directory is locked (%x)...</source> <target>ÙÙŠ انتظار تأمين Ù‚ÙÙ„ للمسار (%x)...</target> -<source>Creating file %x</source> -<target>إنشاء المل٠%x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> </source> <target> -<pluralform>ولا ثانية</pluralform> -<pluralform>ثانية واØدة</pluralform> -<pluralform>ثانيتين</pluralform> +<pluralform>0 ثانية</pluralform> +<pluralform>1 ثانية واØدة</pluralform> +<pluralform>2 ثانيتين</pluralform> <pluralform>%x ثواني</pluralform> <pluralform>%x ثانية</pluralform> <pluralform>%x ثانية</pluralform> </target> +<source>Creating file %x</source> +<target>إنشاء المل٠%x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Øدث خطأ أثناء تØليل المل٠%xØŒ الص٠%yØŒ Ùˆ العمود %z.</target> <source>Scanning:</source> <target>الÙØص:</target> -<source>Encoding extended time information: %x</source> -<target>ترميز المعلومات الموسعة للوقت: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> </source> <target> -<pluralform>ولا بند</pluralform> -<pluralform>بند واØد</pluralform> -<pluralform>بندان</pluralform> -<pluralform>%x بنداً</pluralform> +<pluralform>0 بند</pluralform> +<pluralform>1 بند واØد</pluralform> +<pluralform>2 بندان</pluralform> +<pluralform>%x بنود</pluralform> <pluralform>%x بنداً</pluralform> <pluralform>%x بند</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>ترميز المعلومات الموسعة للوقت: %x</target> + <source>/sec</source> <target>\ثانية</target> @@ -424,7 +424,7 @@ The command is triggered if: <target>التاريخ:</target> <source>Action</source> -<target>نشاط</target> +<target>التصرÙ</target> <source>Category</source> <target>الÙئة</target> @@ -475,13 +475,13 @@ The command is triggered if: <target>&متقدم</target> <source>&Check now</source> -<target></target> +<target>&تØقق الآن</target> <source>Check &automatically once a week</source> -<target></target> +<target>تØقق &أوتوماتيكياً بشكل أسبوعي</target> <source>Check for new version</source> -<target></target> +<target>التØقق من وجود نسخة جديدة</target> <source>Compare</source> <target>قارن</target> @@ -602,7 +602,7 @@ are the same </target> <source>File time and size</source> -<target>وقت المل٠و Øجمه</target> +<target>تاريخ المل٠و Øجمه</target> <source> Files are found equal if @@ -622,19 +622,19 @@ is the same <target>التعامل مع الارتباط رمزي</target> <source>OK</source> -<target>Øسناً</target> +<target>مواÙÙ‚</target> <source><- Two way -></source> <target><- بالاتجاهين -></target> <source>Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database.</source> -<target></target> +<target>تØديد التغيرات Ùˆ مواكبتها على الجانبين. عمليات الØØ°Ù, النقل Ùˆ المشاكل المكتشÙØ© بواسطة قواعد البيانات</target> <source>Mirror ->></source> <target>انعكاس ->></target> <source>Mirror backup of left folder. Right folder is modified to exactly match left folder after synchronization.</source> -<target>نسخ اØتياطي انعكاس للمجلد الأيسر. يتم تعديل المجلد الأيمن ليطابق المجلد الأيسر بعد المزامنة.</target> +<target>إنشاء نسخة منعكسة عن المجلد الأيمن. يتم تعديل المجلد الأيسر ليطابق المجلد الأيمن.</target> <source>Update -></source> <target>تØديث -></target> @@ -667,22 +667,22 @@ is the same <target>الوسم برقم الإصدار</target> <source>Move files to user-defined folder</source> -<target></target> +<target>نقل الملÙات إلى مجلد ÙŠØدده المستخدم</target> <source>Naming convention:</source> <target>Ø§ØµØ·Ù„Ø§Ø Ø§Ù„ØªØ³Ù…ÙŠØ©:</target> <source>Item exists on left side only</source> -<target>العنصر موجود على الجانب الأيسر Ùقط</target> +<target>العنصر موجود على الجانب الأيمن Ùقط</target> <source>Item exists on right side only</source> -<target>العنصر موجود ÙÙŠ الجانب الأيمن Ùقط</target> +<target>العنصر موجود ÙÙŠ الجانب الأيسر Ùقط</target> <source>Left side is newer</source> -<target>الجانب الأيسر Ø£Øدث</target> +<target>الجانب الأيمن Ø£Øدث</target> <source>Right side is newer</source> -<target>الجانب الأيمن Ø£Øدث</target> +<target>الجانب الأيسر Ø£Øدث</target> <source>Items have different content</source> <target>العناصر مختلÙØ© بالمØتوى</target> @@ -751,7 +751,7 @@ Note: File names must be relative to base directories! <target>الØد الأقصى</target> <source>&Clear</source> -<target></target> +<target>&إزالة</target> <source>Fail-safe file copy</source> <target>نسخ ملÙات آمن من الÙشل</target> @@ -790,7 +790,7 @@ Note: File names must be relative to base directories! <target>المتغير</target> <source>Statistics</source> -<target>الإØصاءات</target> +<target>Ø¥Øصائيات</target> <source>Don't show this dialog again</source> <target>عدم إظهار ناÙذة الØوار هذه مرة أخرى</target> @@ -804,9 +804,6 @@ Note: File names must be relative to base directories! <source>&Find next</source> <target>&بØØ« عن التالي</target> -<source>Operation aborted!</source> -<target>تم Ø¥Øباط العملية!</target> - <source>Main bar</source> <target>الشريط الرئيسي</target> @@ -834,6 +831,45 @@ Note: File names must be relative to base directories! <source>Compare both sides</source> <target>مقارنة بين كلا الجانبين</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>0 مسار</pluralform> +<pluralform>1 مسار واØد</pluralform> +<pluralform>2 مساران</pluralform> +<pluralform>%x مسارات</pluralform> +<pluralform>%x مساراً</pluralform> +<pluralform>%x مسار</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>0 ملÙ</pluralform> +<pluralform>1 مل٠واØد</pluralform> +<pluralform>2 ملÙان</pluralform> +<pluralform>%x ملÙات</pluralform> +<pluralform>%x ملÙاً</pluralform> +<pluralform>%x ملÙ</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>الظاهر %y من أصل 0 سطر</pluralform> +<pluralform>الظاهر %y من أصل 1 (سطر ÙˆØيد)</pluralform> +<pluralform>الظاهر %y من أصل 2 (سطرين)</pluralform> +<pluralform>الظاهر %y من أصل %x سطور</pluralform> +<pluralform>الظاهر %y من أصل %x سطراً</pluralform> +<pluralform>الظاهر %y من أصل %x سطر</pluralform> +</target> + <source>Set direction:</source> <target>تØديد الاتجاه:</target> @@ -904,16 +940,16 @@ Note: File names must be relative to base directories! <target>تم ÙØªØ Ø§Ù„ØªÙƒÙˆÙŠÙ†!</target> <source>Show files that exist on left side only</source> -<target>إظهار الملÙات الموجودة ÙÙŠ الجانب الأيسر Ùقط</target> +<target>إظهار الملÙات الموجودة ÙÙŠ الجانب الأيمن Ùقط</target> <source>Show files that exist on right side only</source> -<target>إظهار الملÙات الموجودة ÙÙŠ الجانب الأيمن Ùقط</target> +<target>إظهار الملÙات الموجودة ÙÙŠ الجانب الأيسر Ùقط</target> <source>Show files that are newer on left</source> -<target>إظهار الملÙات الأØدث ÙÙŠ اليسار</target> +<target>إظهار الملÙات الأØدث ÙÙŠ اليمين</target> <source>Show files that are newer on right</source> -<target>إظهار الملÙات الأØدث ÙÙŠ اليمين</target> +<target>إظهار الملÙات الأØدث ÙÙŠ اليسار</target> <source>Show files that are equal</source> <target>إظهار الملÙات المتماثلة على الطرÙين</target> @@ -925,22 +961,22 @@ Note: File names must be relative to base directories! <target>إظهار الاختلاÙات</target> <source>Show files that will be created on the left side</source> -<target>إظهار الملÙات التي سيتم إنشاؤها ÙÙŠ الجانب الأيسر</target> +<target>إظهار الملÙات التي سيتم إنشاؤها ÙÙŠ الجانب الأيمن</target> <source>Show files that will be created on the right side</source> -<target>إظهار الملÙات التي سيتم إنشاؤها ÙÙŠ الجانب الأيمن</target> +<target>إظهار الملÙات التي سيتم إنشاؤها ÙÙŠ الجانب الأيسر</target> <source>Show files that will be deleted on the left side</source> -<target>إظهار الملÙات التي سيتم ØØ°Ùها من من الجانب الأيسر</target> +<target>إظهار الملÙات التي سيتم ØØ°Ùها من من الجانب الأيمن</target> <source>Show files that will be deleted on the right side</source> -<target>إظهار الملÙات التي سيتم ØØ°Ùها من الجانب الأيمن</target> +<target>إظهار الملÙات التي سيتم ØØ°Ùها من الجانب الأيسر</target> <source>Show files that will be overwritten on left side</source> -<target>إظهار الملÙات التي سيتم الكتابة Ùوقها ÙÙŠ الجانب الأيسر</target> +<target>إظهار الملÙات التي سيتم الكتابة Ùوقها ÙÙŠ الجانب الأيمن</target> <source>Show files that will be overwritten on right side</source> -<target>إظهار الملÙات التي سيتم الكتابة Ùوقها ÙÙŠ الجانب الأيمن</target> +<target>إظهار الملÙات التي سيتم الكتابة Ùوقها ÙÙŠ الجانب الأيسر</target> <source>Show files that won't be copied</source> <target>إظهار الملÙات التي لن يتم نسخها</target> @@ -948,6 +984,9 @@ Note: File names must be relative to base directories! <source>Set as default</source> <target>تØديد كوضع اÙتراضي</target> +<source>Operation aborted!</source> +<target>تم Ø¥Øباط العملية!</target> + <source>All folders are in sync!</source> <target>جميع المجلدات متزامنة!</target> @@ -961,46 +1000,7 @@ Note: File names must be relative to base directories! <target>تم تصدير قائمة الملÙات!</target> <source>Searching for program updates...</source> -<target></target> - -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>ولا مسار</pluralform> -<pluralform>مسار واØد</pluralform> -<pluralform>مساران</pluralform> -<pluralform>%x مسارات</pluralform> -<pluralform>%x مساراً</pluralform> -<pluralform>%x مسار</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>ولا ملÙ</pluralform> -<pluralform>مل٠واØد</pluralform> -<pluralform>ملÙان</pluralform> -<pluralform>%x ملÙات</pluralform> -<pluralform>%x ملÙاً</pluralform> -<pluralform>%x ملÙ</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>عرض %x من ولا صÙ</pluralform> -<pluralform>عرض %x من ص٠واØد</pluralform> -<pluralform>عرض %x من صÙين</pluralform> -<pluralform>عرض %x من %y صÙاً</pluralform> -<pluralform>عرض %x من %y صÙاً</pluralform> -<pluralform>عرض %x من %y صÙاً</pluralform> -</target> +<target>جاري البØØ« عن تØديثات للبرنامج...</target> <source>Ignore further errors</source> <target>تجاهل أي أخطاء أخرى</target> @@ -1009,7 +1009,7 @@ Note: File names must be relative to base directories! <target>&تجاهل</target> <source>Don't show this warning again</source> -<target></target> +<target>لا تظهر هذا التنبيه ثانية</target> <source>&Switch</source> <target>&تبديل</target> @@ -1083,6 +1083,32 @@ Note: File names must be relative to base directories! <source>Filter</source> <target>عامل التصÙية</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>هل تريد Øقاً نقل 0 مل٠التالي إلى سلة المهملات ØŸ</pluralform> +<pluralform>هل تريد Øقاً نقل المل٠1 التالي إلى سلة المهملات ØŸ</pluralform> +<pluralform>هل تريد Øقاً نقل الملÙين 2 التاليين إلى سلة المهملات ØŸ</pluralform> +<pluralform>هل تريد Øقاً نقل الـ %x ملÙات التالية إلى سلة المهملات ØŸ</pluralform> +<pluralform>هل تريد Øقاً نقل الـ %x ملÙاً التالية إلى سلة المهملات ØŸ</pluralform> +<pluralform>هل تريد Øقاً نقل الـ %x مل٠التالية إلى سلة المهملات ØŸ</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>هل تريد Øقاً ØØ°Ù 0 مل٠التالي ØŸ</pluralform> +<pluralform>هل تريد Øقاً Øذ٠المل٠1 التالي ØŸ</pluralform> +<pluralform>هل تريد Øقاً Øذ٠الملÙين 2 التاليين ØŸ</pluralform> +<pluralform>هل تريد Øقاً Øذ٠الـ %x ملÙات التالية ØŸ</pluralform> +<pluralform>هل تريد Øقاً Øذ٠الـ %x ملÙاً التالية ØŸ</pluralform> +<pluralform>هل تريد Øقاً Øذ٠الـ %x مل٠التالية ØŸ</pluralform> +</target> + <source>Direct</source> <target>مباشر</target> @@ -1110,23 +1136,11 @@ Note: File names must be relative to base directories! <source>Make hidden warnings and dialogs visible again?</source> <target>جعل التØذيرات Ùˆ نواÙØ° الØوارات المخÙية مرئية من جديد؟</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target></target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target></target> - <source>Leave as unresolved conflict</source> <target>ترك كاختلاÙات من دون ØÙ„</target> <source>Time stamp</source> -<target></target> +<target>البصمة الزمنية</target> <source>Append a timestamp to each file name</source> <target>إلØاق طابع زمني اسم كل ملÙ</target> @@ -1149,6 +1163,9 @@ Note: File names must be relative to base directories! <source>Files</source> <target>ملÙات</target> +<source>Items</source> +<target>عناصر</target> + <source>Percentage</source> <target>النسبة المئوية</target> @@ -1218,17 +1235,14 @@ Note: File names must be relative to base directories! <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> </source> <target> -<pluralform>ولا دقيقة</pluralform> -<pluralform>دقيقة واØدة</pluralform> -<pluralform>دقيقتان</pluralform> +<pluralform>0 دقيقة</pluralform> +<pluralform>1 دقيقة واØدة</pluralform> +<pluralform>2 دقيقتان</pluralform> <pluralform>%x دقائق</pluralform> <pluralform>%x دقيقة</pluralform> <pluralform>%x دقيقة</pluralform> @@ -1239,9 +1253,9 @@ Note: File names must be relative to base directories! <pluralform>%x hours</pluralform> </source> <target> -<pluralform>ولا ساعة</pluralform> -<pluralform>ساعة واØدة</pluralform> -<pluralform>ساعتين</pluralform> +<pluralform>0 ساعة</pluralform> +<pluralform>1 ساعة واØدة</pluralform> +<pluralform>2 ساعتين</pluralform> <pluralform>%x ساعات</pluralform> <pluralform>%x ساعة</pluralform> <pluralform>%x ساعة</pluralform> @@ -1252,9 +1266,9 @@ Note: File names must be relative to base directories! <pluralform>%x days</pluralform> </source> <target> -<pluralform>ولا يوم</pluralform> -<pluralform>يوم واØد</pluralform> -<pluralform>يومان</pluralform> +<pluralform>0 يوم</pluralform> +<pluralform>1 يوم واØد</pluralform> +<pluralform>2 يومان</pluralform> <pluralform>%x أيام</pluralform> <pluralform>%x يوماً</pluralform> <pluralform>%x يوم</pluralform> @@ -1312,13 +1326,13 @@ Note: File names must be relative to base directories! <target>سلة المهملات غير متوÙر للمسارات التالية! سيتم Øذ٠الملÙات بشكل دائم بدلاً من ذلك:</target> <source>The corresponding folder will be considered as empty.</source> -<target></target> +<target>سيتم اعتبار المجلد المواÙÙ‚ كمجلد Ùارغ</target> <source>Cannot find the following folders:</source> <target>تعذر العثور على المجلدات التالية:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>بإمكانك تجاهل هذا الخطأ باعتبالا كل مجلد Ùˆ كأنه Ùارغ</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>بإمكانك إهمال هذا الخطأ لاعتبار كل مجلد على أنه مجلد Ùارغ. سيتم إنشاء المجلدات تلقائياً خلال المزامنة</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>المسارات تلعية! كن Øذراً عند إعداد قواعد المزامنة:</target> @@ -1327,7 +1341,7 @@ Note: File names must be relative to base directories! <target>ابدأ المقارنة</target> <source>Calculating sync directions...</source> -<target></target> +<target>جاري Øساب اتجاهات المزامنة...</target> <source>Conflict detected:</source> <target>تم الكش٠عن تعارض:</target> @@ -1357,37 +1371,37 @@ Note: File names must be relative to base directories! <target>كلا الجانبين متماثلان</target> <source>Copy new item to left</source> -<target>نسخ عنصر جديد إلى اليسار</target> +<target>نسخ عنصر جديد إلى اليمين</target> <source>Copy new item to right</source> -<target>نسخ عنصر جديد إلى اليمين</target> +<target>نسخ عنصر جديد إلى اليسار</target> <source>Delete left item</source> -<target>Øذ٠العنصر الأيسر</target> +<target>Øذ٠العنصر الأيمن</target> <source>Delete right item</source> -<target>Øذ٠العنصر الأيمن</target> +<target>Øذ٠العنصر الأيسر</target> <source>Move file on left</source> -<target>نقل مل٠على اليسار</target> +<target>نقل مل٠على اليمين</target> <source>Move file on right</source> -<target>نقل مل٠على اليمين</target> +<target>نقل مل٠على اليسار</target> <source>Overwrite left item</source> -<target>الكتابة Ùوق العنصر الأيسر</target> +<target>الكتابة Ùوق العنصر الأيمن</target> <source>Overwrite right item</source> -<target>الكتابة Ùوق العنصر الأيمن</target> +<target>الكتابة Ùوق العنصر الأيسر</target> <source>Do nothing</source> <target>لا تÙعل شيئا</target> <source>Update attributes on left</source> -<target>تØديث السمات على اليسار</target> +<target>تØديث السمات على اليمين</target> <source>Update attributes on right</source> -<target>تØديث السمات على اليمين</target> +<target>تØديث السمات على اليسار</target> <source>Multiple...</source> <target>متعددة...</target> @@ -1458,12 +1472,6 @@ Note: File names must be relative to base directories! <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>سيتم تعديل مجلد الذي هو جزء من أزواج المجلدات المتعددة. الرجاء مراجعة إعدادات المزامنة.</target> -<source>Left</source> -<target>اليسار</target> - -<source>Right</source> -<target>ØÙ‚</target> - <source>Synchronizing folder pair:</source> <target>مزامنة زوج مجلدات:</target> @@ -1471,7 +1479,7 @@ Note: File names must be relative to base directories! <target>إنشاء قاعدة بيانات...</target> <source>Creating Volume Shadow Copy for %x...</source> -<target></target> +<target>جاري إنشاء نسخة ظلية لـ %x...</target> <source>Data verification error: Source and target file have different content!</source> <target>خطأ تØقق بيانات: للمل٠المصدر والمل٠الهد٠مØتويات مختلÙØ©!</target> diff --git a/BUILD/Languages/chinese_simple.lng b/BUILD/Languages/chinese_simple.lng index 3d0f0d25..0f87d315 100644 --- a/BUILD/Languages/chinese_simple.lng +++ b/BUILD/Languages/chinese_simple.lng @@ -20,7 +20,7 @@ <target>总共时间:</target> <source>Cannot set directory lock for %x.</source> -<target></target> +<target>æ— æ³•ä¸º %x 设置目录é”定.</target> <source>Show in Explorer</source> <target>在Explorerä¸æ˜¾ç¤º</target> @@ -44,7 +44,7 @@ <target>错误</target> <source>Selected variant:</source> -<target></target> +<target>选定的å˜åŒ–:</target> <source>Select alternate comparison settings</source> <target>选择替æ¢çš„比较设置</target> @@ -68,7 +68,7 @@ <target>å¤åˆ¶</target> <source>Paste</source> -<target></target> +<target>粘贴</target> <source>Save as batch job</source> <target>å¦å˜ä¸ºæ‰¹å¤„ç†ä½œä¸š</target> @@ -110,11 +110,19 @@ <target>致命错误</target> <source>Error Code %x:</source> -<target></target> +<target>错误代ç %x:</target> <source>Cannot resolve symbolic link %x.</source> <target>æ— æ³•è§£å†³ç¬¦å·è¿žæŽ¥ %x.</target> +<source> +<pluralform>1 Byte</pluralform> +<pluralform>%x Bytes</pluralform> +</source> +<target> +<pluralform>%x å—节</pluralform> +</target> + <source>%x MB</source> <target>%x MB</target> @@ -124,14 +132,6 @@ <source>%x GB</source> <target>%x GB</target> -<source> -<pluralform>1 Byte</pluralform> -<pluralform>%x Bytes</pluralform> -</source> -<target> -<pluralform>%x å—节</pluralform> -</target> - <source>Database file %x is incompatible.</source> <target>æ•°æ®åº“文件 %x ä¸å…¼å®¹.</target> @@ -168,9 +168,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>由于目录已é”定而æ£åœ¨ç‰å¾…(%x)...</target> -<source>Creating file %x</source> -<target>æ£åœ¨åˆ›å»ºæ–‡ä»¶ %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -179,15 +176,15 @@ <pluralform>%x 秒</pluralform> </target> +<source>Creating file %x</source> +<target>æ£åœ¨åˆ›å»ºæ–‡ä»¶ %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>当分æžæ–‡ä»¶ %x , è¡Œ %y, 列 %z 时出错.</target> <source>Scanning:</source> <target>扫æä¸:</target> -<source>Encoding extended time information: %x</source> -<target>æ£åœ¨ç¼–ç 扩展时间信æ¯:%x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -196,6 +193,9 @@ <pluralform>[%x 线程]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>æ£åœ¨ç¼–ç 扩展时间信æ¯:%x</target> + <source>/sec</source> <target>/秒</target> @@ -460,13 +460,13 @@ The command is triggered if: <target>高级(&A)</target> <source>&Check now</source> -<target></target> +<target>现在检查(&C)</target> <source>Check &automatically once a week</source> -<target></target> +<target>æ¯å‘¨è‡ªåŠ¨æ£€æŸ¥ä¸€æ¬¡(&A)</target> <source>Check for new version</source> -<target></target> +<target>检查新版本</target> <source>Compare</source> <target>比较</target> @@ -520,7 +520,7 @@ The command is triggered if: <target>在完æˆæ—¶</target> <source>Close</source> -<target></target> +<target>å…³é—</target> <source>&Pause</source> <target>æš‚åœ(&P)</target> @@ -613,7 +613,7 @@ is the same <target><-åŒå‘-></target> <source>Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database.</source> -<target></target> +<target>è¯†åˆ«å’Œä¼ æ’两侧的å˜åŒ–. åˆ é™¤, 移动和冲çªä¼šä½¿ç”¨ä¸€ä¸ªæ•°æ®åº“æ¥è‡ªåŠ¨æ£€æµ‹.</target> <source>Mirror ->></source> <target>é•œåƒ ->></target> @@ -652,7 +652,7 @@ is the same <target>ä¿ç•™åŽ†å²ç‰ˆæœ¬</target> <source>Move files to user-defined folder</source> -<target></target> +<target>移动文件至用户定义文件夹</target> <source>Naming convention:</source> <target>命å规则:</target> @@ -736,7 +736,7 @@ Note: File names must be relative to base directories! <target>最大</target> <source>&Clear</source> -<target></target> +<target>清除(&C)</target> <source>Fail-safe file copy</source> <target>æ— é£Žé™©çš„æ–‡ä»¶å¤åˆ¶</target> @@ -811,7 +811,7 @@ Note: File names must be relative to base directories! <target>选择视图</target> <source>Open...</source> -<target></target> +<target>打开...</target> <source>Save</source> <target>ä¿å˜</target> @@ -819,6 +819,30 @@ Note: File names must be relative to base directories! <source>Compare both sides</source> <target>比较两侧</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>%x 目录</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>%x 文件</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%x ä¸çš„ %y 行在视图ä¸</pluralform> +</target> + <source>Set direction:</source> <target>设置方å‘:</target> @@ -931,7 +955,7 @@ Note: File names must be relative to base directories! <target>显示将ä¸è¢«å¤åˆ¶çš„文件</target> <source>Set as default</source> -<target></target> +<target>设置为默认值</target> <source>All folders are in sync!</source> <target>所有文件夹都是åŒæ¥çš„!</target> @@ -946,31 +970,7 @@ Note: File names must be relative to base directories! <target>文件清å•å·²ç»å¯¼å‡º!</target> <source>Searching for program updates...</source> -<target></target> - -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>%x 目录</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>%x 文件</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%y ä¸çš„ %x è¡Œå¯è§</pluralform> -</target> +<target>æ£åœ¨æœç´¢ç¨‹åºçš„æ›´æ–°...</target> <source>Ignore further errors</source> <target>忽略之åŽå‡ºçŽ°çš„错误</target> @@ -979,7 +979,7 @@ Note: File names must be relative to base directories! <target>忽略(&I)</target> <source>Don't show this warning again</source> -<target></target> +<target>ä¸è¦å†æ˜¾ç¤ºè¿™ä¸ªè¦å‘Š</target> <source>&Switch</source> <target>切æ¢(&S)</target> @@ -1053,6 +1053,22 @@ Note: File names must be relative to base directories! <source>Filter</source> <target>过滤器</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>是å¦çœŸçš„è¦ç§»åŠ¨å¦‚下 %x 个项目到回收站?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>是å¦çœŸçš„è¦åˆ 除如下 %x 个项目?</pluralform> +</target> + <source>Direct</source> <target>直接</target> @@ -1080,23 +1096,11 @@ Note: File names must be relative to base directories! <source>Make hidden warnings and dialogs visible again?</source> <target>é‡æ–°è®©å·²ç»éšè—çš„è¦å‘Šå’Œå¯¹è¯æ¡†å˜ä¸ºå¯è§?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target></target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target></target> - <source>Leave as unresolved conflict</source> <target>é—留为未解决的冲çª</target> <source>Time stamp</source> -<target></target> +<target>时间戳</target> <source>Append a timestamp to each file name</source> <target>é™„åŠ æ—¶é—´æˆ³åˆ°æ¯ä¸€ä¸ªæ–‡ä»¶å</target> @@ -1119,6 +1123,9 @@ Note: File names must be relative to base directories! <source>Files</source> <target>文件</target> +<source>Items</source> +<target>项目</target> + <source>Percentage</source> <target>百分比</target> @@ -1188,9 +1195,6 @@ Note: File names must be relative to base directories! <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1219,10 +1223,10 @@ Note: File names must be relative to base directories! <target>æ— æ³•è®¾ç½® %x 的特æƒ.</target> <source>Failed to suspend system sleep mode.</source> -<target></target> +<target>ä¸æ¢ç³»ç»Ÿç¡çœ 模å¼å¤±è´¥.</target> <source>Cannot change process I/O priorities.</source> -<target></target> +<target>æ— æ³•æ›´æ”¹è¿›ç¨‹çš„I/O优先级.</target> <source>Unable to move %x to the Recycle Bin!</source> <target>æ— æ³•ç§»åŠ¨ %x 到回收站!</target> @@ -1243,7 +1247,7 @@ Note: File names must be relative to base directories! <target>设置默认的åŒæ¥æ–¹å‘:旧文件会被新文件覆盖.</target> <source>Checking recycle bin availability for folder %x...</source> -<target></target> +<target>æ£åœ¨ä¸ºæ–‡ä»¶å¤¹ %x 检测回收站å¯ç”¨æ€§...</target> <source>Moving file %x to recycle bin</source> <target>æ£åœ¨ç§»åŠ¨æ–‡ä»¶ %x 到回收站</target> @@ -1267,13 +1271,13 @@ Note: File names must be relative to base directories! <target>如下路径的回收站ä¸å¯ç”¨! æ–‡ä»¶ä¼šè¢«æ°¸ä¹…åˆ é™¤:</target> <source>The corresponding folder will be considered as empty.</source> -<target></target> +<target>相应的文件夹将被视为空.</target> <source>Cannot find the following folders:</source> -<target></target> +<target>æ— æ³•æ‰¾åˆ°å¦‚ä¸‹æ–‡ä»¶å¤¹:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target></target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>ä½ å¯å¿½ç•¥æ¤é”™è¯¯è€Œå°†æ¯ä¸ªæ–‡ä»¶å¤¹è§†ä¸ºç©º. 这些文件夹将会在åŒæ¥è¿‡ç¨‹ä¸è¢«è‡ªåŠ¨åˆ›å»º.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>目录有ä¾èµ–性ï¼åœ¨è®¾ç«‹åŒæ¥è§„则时请å°å¿ƒ:</target> @@ -1282,7 +1286,7 @@ Note: File names must be relative to base directories! <target>开始比较</target> <source>Calculating sync directions...</source> -<target></target> +<target>æ£åœ¨è®¡ç®—åŒæ¥æ–¹å‘...</target> <source>Conflict detected:</source> <target>检测到的冲çª:</target> @@ -1378,7 +1382,7 @@ Note: File names must be relative to base directories! <target>æ›´æ–° %x 的属性</target> <source>Cannot find %x.</source> -<target></target> +<target>æ— æ³•æ‰¾åˆ° %x.</target> <source>Target folder %x already existing.</source> <target>ç›®æ ‡æ–‡ä»¶å¤¹ %x å·²ç»å˜åœ¨.</target> @@ -1426,7 +1430,7 @@ Note: File names must be relative to base directories! <target>æ£åœ¨ç”Ÿæˆæ•°æ®åº“...</target> <source>Creating Volume Shadow Copy for %x...</source> -<target></target> +<target>æ£åœ¨ä¸º %x 创建å·å½±å“å¤åˆ¶...</target> <source>Data verification error: Source and target file have different content!</source> <target>æ•°æ®æ ¡éªŒé”™è¯¯:æºæ–‡ä»¶å’Œç›®æ ‡æ–‡ä»¶å†…容ä¸åŒ!</target> diff --git a/BUILD/Languages/chinese_traditional.lng b/BUILD/Languages/chinese_traditional.lng index 2a63530b..763359ae 100644 --- a/BUILD/Languages/chinese_traditional.lng +++ b/BUILD/Languages/chinese_traditional.lng @@ -113,7 +113,15 @@ <target>錯誤代碼 %x:</target> <source>Cannot resolve symbolic link %x.</source> -<target>無法解æžç¬¦è™Ÿé€£çµ %X。</target> +<target>無法解æžç¬¦è™Ÿé€£çµ %x。</target> + +<source> +<pluralform>1 Byte</pluralform> +<pluralform>%x Bytes</pluralform> +</source> +<target> +<pluralform>%x ä½å…ƒçµ„</pluralform> +</target> <source>%x MB</source> <target>%x MB</target> @@ -124,14 +132,6 @@ <source>%x GB</source> <target>%x GB</target> -<source> -<pluralform>1 Byte</pluralform> -<pluralform>%x Bytes</pluralform> -</source> -<target> -<pluralform>%x ä½å…ƒçµ„</pluralform> -</target> - <source>Database file %x is incompatible.</source> <target>資料庫檔案 %x 是ä¸ç›¸å®¹çš„。</target> @@ -148,10 +148,10 @@ <target>記憶體ä¸è¶³ï¼</target> <source>Cannot write file %x.</source> -<target>無法寫入檔案 %X。</target> +<target>無法寫入檔案 %x。</target> <source>Cannot read file %x.</source> -<target>無法讀å–檔案 %X。</target> +<target>無法讀å–檔案 %x。</target> <source>Database files do not share a common session.</source> <target>資料庫檔案ä¸æœƒå…±äº«ä¸€å€‹å…±åŒçš„連線。</target> @@ -168,9 +168,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>ç‰å¾…åŒæ™‚目錄被鎖定(%x)...</target> -<source>Creating file %x</source> -<target>æ£åœ¨æ–°å»ºæª”案 %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -179,15 +176,15 @@ <pluralform>%x 秒</pluralform> </target> +<source>Creating file %x</source> +<target>æ£åœ¨æ–°å»ºæª”案 %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>è§£æž %x 檔案,第 %y 列,第 %z 行出ç¾éŒ¯èª¤ã€‚</target> <source>Scanning:</source> <target>æ£åœ¨æŽƒçž„:</target> -<source>Encoding extended time information: %x</source> -<target>編碼延長時間資訊:%x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -196,6 +193,9 @@ <pluralform>[%x 個執行緒]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>編碼延長時間資訊:%x</target> + <source>/sec</source> <target>/秒</target> @@ -367,7 +367,7 @@ The command is triggered if: <target>無法連接到sourceforge.netï¼</target> <source>Current FreeFileSync version number was not found online! Do you want to check manually?</source> -<target>ç›®å‰é€£ç·šæœªæ‰¾åˆ°FreeFileSync版號ï¼æ˜¯å¦è¦æ‰‹å‹•æª¢æŸ¥ï¼Ÿ</target> +<target>ç›®å‰é€£ç·šæ‰¾ä¸åˆ°FreeFileSync版號ï¼æ˜¯å¦è¦æ‰‹å‹•æª¢æŸ¥ï¼Ÿ</target> <source>Do you want FreeFileSync to automatically check for updates every week?</source> <target>è¦æ¯é€±è‡ªå‹•æª¢æŸ¥æ›´æ–°FreeFileSync嗎?</target> @@ -439,7 +439,7 @@ The command is triggered if: <target>儲å˜(&S)</target> <source>Save as &batch job...</source> -<target>å¦å˜ç‚ºæ‰¹æ¬¡è™•ç†ä½œæ¥(&B)</target> +<target>å¦å˜ç‚ºæ‰¹æ¬¡è™•ç†ä½œæ¥(&b)</target> <source>1. &Compare</source> <target>1. 比å°(&C)</target> @@ -493,7 +493,7 @@ The command is triggered if: <target>將被新建的檔案和資料夾數é‡</target> <source>Number of files that will be overwritten</source> -<target>一些檔案和目錄將被覆蓋</target> +<target>一些檔案和目錄會被覆蓋</target> <source>Number of files and folders that will be deleted</source> <target>將被刪除的檔案和資料夾數é‡</target> @@ -780,9 +780,6 @@ Note: File names must be relative to base directories! <source>&Find next</source> <target>找下一個(&F)</target> -<source>Operation aborted!</source> -<target>æ“作已ä¸æ¢ï¼</target> - <source>Main bar</source> <target>主欄ä½</target> @@ -810,6 +807,30 @@ Note: File names must be relative to base directories! <source>Compare both sides</source> <target>比å°å…©é‚Š</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>%x 個目錄</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>%x 個檔案</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>視圖ä¸çš„第 %x 列ä¸çš„第 %y 列</pluralform> +</target> + <source>Set direction:</source> <target>è¨å®šæ–¹å‘:</target> @@ -924,6 +945,9 @@ Note: File names must be relative to base directories! <source>Set as default</source> <target>è¨ç‚ºé è¨å€¼</target> +<source>Operation aborted!</source> +<target>æ“作已ä¸æ¢ï¼</target> + <source>All folders are in sync!</source> <target>所有資料夾都是åŒæ¥çš„ï¼</target> @@ -939,30 +963,6 @@ Note: File names must be relative to base directories! <source>Searching for program updates...</source> <target>æ£åœ¨æœå°‹ç¨‹å¼æ›´æ–°...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>%x 個目錄</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>%x 個檔案</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>檢視ä¸çš„ %x 之 %y 列</pluralform> -</target> - <source>Ignore further errors</source> <target>忽略進一æ¥éŒ¯èª¤</target> @@ -1044,6 +1044,22 @@ Note: File names must be relative to base directories! <source>Filter</source> <target>篩é¸å™¨</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>您確定è¦å°‡ä¸‹åˆ— %x é …ç›®ç§»å‹•åˆ°è³‡æºå›žæ”¶ç’嗎?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>您確定è¦å°‡ä¸‹åˆ— %x é …ç›®åˆªé™¤å—Žï¼Ÿ</pluralform> +</target> + <source>Direct</source> <target>直接</target> @@ -1071,22 +1087,6 @@ Note: File names must be relative to base directories! <source>Make hidden warnings and dialogs visible again?</source> <target>使隱è—çš„è¦å‘Šå’Œå°è©±æ¡†å†æ¬¡å¯è¦‹ï¼Ÿ</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>您確定è¦å°‡ä¸‹åˆ— %x é …ç›®ç§»å‹•åˆ°è³‡æºå›žæ”¶ç’嗎?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>您確定è¦å°‡ä¸‹åˆ— %x é …ç›®åˆªé™¤å—Žï¼Ÿ</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>ä¿ç•™çµ¦æœªè§£æ±ºçš„è¡çª</target> @@ -1114,6 +1114,9 @@ Note: File names must be relative to base directories! <source>Files</source> <target>檔案</target> +<source>Items</source> +<target>é …ç›®</target> + <source>Percentage</source> <target>百分比</target> @@ -1183,9 +1186,6 @@ Note: File names must be relative to base directories! <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1217,7 +1217,7 @@ Note: File names must be relative to base directories! <target>ä¸æ¢ç³»çµ±ç¡çœ 模å¼å¤±æ•—。</target> <source>Cannot change process I/O priorities.</source> -<target>無法更改 I/O 處ç†å„ªå…ˆé †åºã€‚</target> +<target>無法更改I/O處ç†å„ªå…ˆé †åºã€‚</target> <source>Unable to move %x to the Recycle Bin!</source> <target>無法將 %x 移動到資æºå›žæ”¶ç’ï¼</target> @@ -1232,7 +1232,7 @@ Note: File names must be relative to base directories! <target>自上次åŒæ¥ä»¥ä¾†éƒ½æ²’有變更ï¼</target> <source>The corresponding database entries are not in sync considering current settings.</source> -<target>相應的資料庫æ¢ç›®åœ¨åŒæ¥æ™‚,ä¸è€ƒæ…®åˆ°ç›®å‰çš„è¨å®šã€‚</target> +<target>相應的資料庫æ¢ç›®åœ¨åŒæ¥æ™‚,ä¸æœƒè€ƒæ…®åˆ°ç›®å‰çš„è¨å®šã€‚</target> <source>Setting default synchronization directions: Old files will be overwritten with newer files.</source> <target>è¨å®šé è¨åŒæ¥æ–¹å‘:舊檔案會被較新的檔案覆蓋。</target> @@ -1267,8 +1267,8 @@ Note: File names must be relative to base directories! <source>Cannot find the following folders:</source> <target>找ä¸åˆ°ä¸‹åˆ—資料夾:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>您å¯ä»¥å¿½ç•¥å°‡æ¯å€‹è³‡æ–™å¤¾è¦–為空的錯誤。</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>您å¯ä»¥å¿½ç•¥æ¯å€‹è³‡æ–™å¤¾è¦–為空的錯誤。資料夾會在åŒæ¥éŽç¨‹ä¸è‡ªå‹•æ–°å»ºã€‚</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>目錄有ä¾é 性ï¼è«‹å°å¿ƒè¨å®šåŒæ¥è¦å‰‡ï¼š</target> @@ -1394,7 +1394,7 @@ Note: File names must be relative to base directories! <target>檢測到顯著的差異:</target> <source>More than 50% of the total number of files will be copied or deleted!</source> -<target>超éŽç¸½æ•¸ 50% 以上的檔案將被複製或刪除ï¼</target> +<target>超éŽç¸½æ•¸ 50% 以上的檔案會被複製或刪除ï¼</target> <source>Not enough free disk space available in:</source> <target>æ²’æœ‰è¶³å¤ çš„å¯ç”¨ç©ºé–“:</target> @@ -1408,12 +1408,6 @@ Note: File names must be relative to base directories! <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>一個將被修改的資料夾,是多個é…å°è³‡æ–™å¤¾çš„一部分。請檢查åŒæ¥è¨å®šã€‚</target> -<source>Left</source> -<target>左邊</target> - -<source>Right</source> -<target>å³é‚Š</target> - <source>Synchronizing folder pair:</source> <target>åŒæ¥é…å°è³‡æ–™å¤¾ï¼š</target> diff --git a/BUILD/Languages/croatian.lng b/BUILD/Languages/croatian.lng index 766107d6..9fefb3ed 100644 --- a/BUILD/Languages/croatian.lng +++ b/BUILD/Languages/croatian.lng @@ -113,16 +113,13 @@ <target>PogreÅ¡ka broj %x:</target> <source>Cannot resolve symbolic link %x.</source> -<target>Ne mogu rijeÅ¡iti simboliÄnu poveznicu</target> +<target>Ne mogu odrediti poveznicu %x.</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> +<source>Cannot determine final path for %x.</source> +<target></target> -<source>%x GB</source> -<target>%x GB</target> +<source>Cannot find system function %x.</source> +<target>Ne mogu pronaći sistemsku funkciju %x.</target> <source> <pluralform>1 Byte</pluralform> @@ -134,6 +131,15 @@ <pluralform>%x Bajtova</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>Datoteka baze %x je nekompatibilna</target> @@ -150,10 +156,10 @@ <target>Nedostatak memorije</target> <source>Cannot write file %x.</source> -<target>Ne mogu zapisati datoteku</target> +<target>Ne mogu zapisati datoteku %x.</target> <source>Cannot read file %x.</source> -<target>Ne mogu Äitati datoteku</target> +<target>Ne mogu Äitati datoteku %x.</target> <source>Database files do not share a common session.</source> <target>Datoteke baze ne dijele zajedniÄki protokol</target> @@ -170,9 +176,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>ÄŒekam dok se direktorij zakljuÄava (%x)...</target> -<source>Creating file %x</source> -<target>IzraÄ‘ujem datoteku %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -183,15 +186,15 @@ <pluralform>%x sek</pluralform> </target> +<source>Creating file %x</source> +<target>IzraÄ‘ujem datoteku %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>GreÅ¡ka u analizi datoteke %x, red %y, stupac %z.</target> <source>Scanning:</source> <target>Pretražujem:</target> -<source>Encoding extended time information: %x</source> -<target>:Spremam informacije o vremenu %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -199,9 +202,12 @@ <target> <pluralform>%x Stavka</pluralform> <pluralform>%x Stavke</pluralform> -<pluralform>% Stavki</pluralform> +<pluralform>%x Stavki</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>:Spremam informacije o vremenu %x</target> + <source>/sec</source> <target>/sek</target> @@ -414,12 +420,12 @@ Naredba će biti pokrenuta ako se: <source>Date:</source> <target>Datum:</target> -<source>Action</source> -<target>Radnja</target> - <source>Category</source> <target>Kategorija</target> +<source>Action</source> +<target>Radnja</target> + <source>Drag && drop</source> <target>Povuci && ispusti</target> @@ -795,12 +801,6 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama! <source>&Find next</source> <target>&NaÄ‘i slijedeće</target> -<source>Operation aborted!</source> -<target>Operacija otkazana!</target> - -<source>Main bar</source> -<target>Glavna traka</target> - <source>Folder pairs</source> <target>Par mape</target> @@ -810,6 +810,9 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama! <source>Configuration</source> <target>Postavke</target> +<source>Main bar</source> +<target>Glavna traka</target> + <source>Filter files</source> <target>Filtriraj datoteke</target> @@ -825,6 +828,32 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama! <source>Compare both sides</source> <target>Usporedi obje strane</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>%x direktorij</pluralform> +<pluralform>%x direktorija</pluralform> +<pluralform>%x direktorija</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>%x datoteka</pluralform> +<pluralform>%x datoteke</pluralform> +<pluralform>%x datoteka</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target></target> + <source>Set direction:</source> <target>Odaberi smijer:</target> @@ -939,51 +968,21 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama! <source>Set as default</source> <target>Postavi kao zadano</target> +<source>Operation aborted!</source> +<target>Operacija otkazana!</target> + <source>All folders are in sync!</source> <target>Sve mape su u sinkronizaciji!</target> <source>Comma separated list</source> <target>Zarezom odvojene liste</target> -<source>Legend</source> -<target>Legenda</target> - <source>File list exported!</source> <target>DatoteÄna lista izvezena!</target> <source>Searching for program updates...</source> <target>Pretražujem ažuriranja programa...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>%x direktorij</pluralform> -<pluralform>%x direktorija</pluralform> -<pluralform>%x direktorija</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>%x datoteka</pluralform> -<pluralform>%x datoteke</pluralform> -<pluralform>%x datoteka</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x od %y red u prikazu</pluralform> -<pluralform>%x od %y reda u prikazu</pluralform> -<pluralform>%x od %y redova u prikazu</pluralform> -</target> - <source>Ignore further errors</source> <target>Zanemari buduće greÅ¡ke</target> @@ -1065,6 +1064,18 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama! <source>Filter</source> <target>Filtriranje</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target></target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target></target> + <source>Direct</source> <target>Neposredno</target> @@ -1092,33 +1103,21 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama! <source>Make hidden warnings and dialogs visible again?</source> <target>Vrati skrivena upozorenja i dijaloÅ¡ki okvir ponovno vidljivim?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target></target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target></target> - <source>Leave as unresolved conflict</source> <target>Ostavi kao nerijeÅ¡eni sukob</target> -<source>Time stamp</source> -<target>Vremenska oznaka</target> - -<source>Append a timestamp to each file name</source> -<target>Dodaj vremensku oznaku svakom imenu datoteke</target> - <source>Replace</source> <target>Zamjeni</target> <source>Move files and replace if existing</source> <target>Premjesti datoteke i zamjeni ako već postoji</target> +<source>Time stamp</source> +<target>Vremenska oznaka</target> + +<source>Append a timestamp to each file name</source> +<target>Dodaj vremensku oznaku svakom imenu datoteke</target> + <source>Folder</source> <target>Mapa</target> @@ -1132,7 +1131,7 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama! <target>Datoteke</target> <source>Items</source> -<target></target> +<target>Stavke</target> <source>Percentage</source> <target>Postotak</target> @@ -1161,9 +1160,6 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama! <source>Cannot write modification time of %x.</source> <target>Ne mogu zapisati vrijeme izmjene %x.</target> -<source>Cannot find system function %x.</source> -<target>Ne mogu pronaći sistemsku funkciju %x.</target> - <source>Cannot read security context of %x.</source> <target>Ne mogu Äitati zaÅ¡tićeni sadržaj %x.</target> @@ -1179,8 +1175,8 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama! <source>Cannot create directory %x.</source> <target>Ne mogu izraditi mapu %x.</target> -<source>Cannot copy symbolic link %x to %y.</source> -<target>Ne mogu kopirati simboliÄnu poveznicu %x na %y.</target> +<source>Cannot create symbolic link %x.</source> +<target></target> <source>Cannot copy file %x to %y.</source> <target>Ne mogu kopirati datoteku %x na %y.</target> @@ -1203,9 +1199,6 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama! <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1246,7 +1239,7 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama! <target>Ne može se promjeniti proces I/O prioriteta</target> <source>Unable to move %x to the Recycle Bin!</source> -<target>Nije moguće prebaciti u KoÅ¡ za smeće</target> +<target>Nije moguće premjestiti %x u koÅ¡ za smeće!</target> <source>Both sides have changed since last synchronization!</source> <target>Obje su strane promjenjene od posljednje sinkronizacije!</target> @@ -1294,17 +1287,11 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama! <target>Ne mogu pronaći slijedeće mape:</target> <source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> -<target></target> +<target>Možete ignorirati ovu pogreÅ¡ku te uzeti u obzir svaku mapu praznom. Mape se tada stvaraju automatski tokom sinkronizacije.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Direktoriji su u ovisnosti! Budite oprezni pri postavljanju sinkronizacijskih pravila:</target> -<source>Start comparison</source> -<target>ZapoÄni usporedbu</target> - -<source>Calculating sync directions...</source> -<target>IzraÄunavam smjerove sinkronizacije...</target> - <source>Conflict detected:</source> <target>Sukob pronaÄ‘en:</target> @@ -1317,18 +1304,21 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama! <source>Items differ in attributes only</source> <target>Stavke se razlikuju samo u atributima</target> -<source>Symbolic links %x have the same date but a different target.</source> -<target>SimboliÄne poveznice %x imaju isti datum ali drugaÄiji cilj</target> +<source>Resolving symbolic link %x</source> +<target></target> <source>Comparing content of files %x</source> <target>UsporeÄ‘ujem sadržaj datoteka %x</target> -<source>Comparing files by content failed.</source> -<target>Usporedba datoteka prema sadržaju nije uspjela.</target> - <source>Generating file list...</source> <target>Generiram listu datoteka...</target> +<source>Start comparison</source> +<target>ZapoÄni usporedbu</target> + +<source>Calculating sync directions...</source> +<target>IzraÄunavam smjerove sinkronizacije...</target> + <source>Both sides are equal</source> <target>Obje strane su jednake</target> @@ -1434,12 +1424,6 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama! <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>Mapa će biti ureÄ‘ena koja je dio viÅ¡e pari mapa. Molimo provjerite sinkronizacijske postavke.</target> -<source>Left</source> -<target>Lijevo</target> - -<source>Right</source> -<target>Desno</target> - <source>Synchronizing folder pair:</source> <target>Par sinkronizirane mape:</target> diff --git a/BUILD/Languages/czech.lng b/BUILD/Languages/czech.lng index 092ed55d..33ea5144 100644 --- a/BUILD/Languages/czech.lng +++ b/BUILD/Languages/czech.lng @@ -115,15 +115,6 @@ <source>Cannot resolve symbolic link %x.</source> <target>Nelze najÃt odkaz zástupce %x.</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source> <pluralform>1 Byte</pluralform> <pluralform>%x Bytes</pluralform> @@ -134,6 +125,15 @@ <pluralform>%x B</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>Chybný formát databáze %x.</target> @@ -170,9 +170,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>ÄŒekánà na uzamÄenà adresáře (%x)</target> -<source>Creating file %x</source> -<target>Vytvářenà souboru %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -183,15 +180,15 @@ <pluralform>%x sekund</pluralform> </target> +<source>Creating file %x</source> +<target>Vytvářenà souboru %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Chyba zpracovánà souboru %x: na řádku %y ve sloupci %z</target> <source>Scanning:</source> <target>Zpracováváno:</target> -<source>Encoding extended time information: %x</source> -<target>Zpracovánà rozÅ¡ÃÅ™ené informace o Äase: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -202,6 +199,9 @@ <pluralform>[%x procesů]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>Zpracovánà rozÅ¡ÃÅ™ené informace o Äase: %x</target> + <source>/sec</source> <target>/s</target> @@ -266,7 +266,7 @@ <target>3. ZmáÄknÄ›te 'Start'</target> <source>To get started just import a .ffs_batch file.</source> -<target>Můžete naÄÃst také konfiguraÄnà soubor soubor .ffs_batch</target> +<target>Můžete naÄÃst také konfiguraÄnà soubor .ffs_batch</target> <source>Folders to watch</source> <target>Složka ke sledovánÃ</target> @@ -469,7 +469,7 @@ PÅ™Ãkaz je spuÅ¡tÄ›n když: <target>Zkontrolovat &nynÃ</target> <source>Check &automatically once a week</source> -<target>Kontrolovat &automaticky jendou týdnÄ›</target> +<target>Kontrolovat &automaticky jednou týdnÄ›</target> <source>Check for new version</source> <target>&Zkontrolovat aktualizace</target> @@ -550,7 +550,7 @@ PÅ™Ãkaz je spuÅ¡tÄ›n když: <target>Skrýt vÅ¡echny chyby a varovánÃ</target> <source>Pop-up</source> -<target>HlaÅ¡enÃ</target> +<target>HlášenÃ</target> <source>Show pop-up on errors or warnings</source> <target>Zobrazit hlášenà pÅ™i chybÄ› nebo varovánÃ</target> @@ -760,7 +760,7 @@ Poznámka: Filtr je aplikován relativnÄ› k základnÃm adresářům. <target>KopÃrovat pÅ™Ãstupová oprávnÄ›nà k souborům</target> <source>Transfer file and folder permissions (Requires Administrator rights)</source> -<target>PÅ™enést pÅ™Ãstupová oprávnÄ›nà souborů a složek (Vyžaduje administrátorké oprávnÄ›nÃ)</target> +<target>PÅ™enést pÅ™Ãstupová oprávnÄ›nà souborů a složek (Vyžaduje administrátorské oprávnÄ›nÃ)</target> <source>Restore hidden dialogs</source> <target>Obnovit skryté dialogy</target> @@ -795,9 +795,6 @@ Poznámka: Filtr je aplikován relativnÄ› k základnÃm adresářům. <source>&Find next</source> <target>&NajÃt dalÅ¡Ã</target> -<source>Operation aborted!</source> -<target>Operace zruÅ¡ena!</target> - <source>Main bar</source> <target>Hlavnà liÅ¡ta</target> @@ -825,6 +822,36 @@ Poznámka: Filtr je aplikován relativnÄ› k základnÃm adresářům. <source>Compare both sides</source> <target>Porovnat obÄ› strany</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>1 adresář</pluralform> +<pluralform>%x adresáře</pluralform> +<pluralform>%x adresářů</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>1 soubor</pluralform> +<pluralform>%x soubory</pluralform> +<pluralform>%x souborů</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%y z 1 řádku</pluralform> +<pluralform>%y z %x řádků</pluralform> +<pluralform>%y z %x řádků</pluralform> +</target> + <source>Set direction:</source> <target>Nastavit adresář:</target> @@ -939,6 +966,9 @@ Poznámka: Filtr je aplikován relativnÄ› k základnÃm adresářům. <source>Set as default</source> <target>Nastavit jako výchozÃ</target> +<source>Operation aborted!</source> +<target>Operace zruÅ¡ena!</target> + <source>All folders are in sync!</source> <target>VÅ¡echny složky jsou synchronizovány!</target> @@ -954,36 +984,6 @@ Poznámka: Filtr je aplikován relativnÄ› k základnÃm adresářům. <source>Searching for program updates...</source> <target>Hledánà aktualizacà programu ...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>1 adresář</pluralform> -<pluralform>%x adresáře</pluralform> -<pluralform>%x adresářů</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>1 soubor</pluralform> -<pluralform>%x soubory</pluralform> -<pluralform>%x souborů</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x z 1 řádku</pluralform> -<pluralform>%x z %y řádků</pluralform> -<pluralform>%x z %y řádků</pluralform> -</target> - <source>Ignore further errors</source> <target>PÅ™eskoÄit dalÅ¡Ã chyby</target> @@ -1065,6 +1065,26 @@ Poznámka: Filtr je aplikován relativnÄ› k základnÃm adresářům. <source>Filter</source> <target>Filtr</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Opravdu chcete pÅ™esunout následujÃcà položku do KoÅ¡e?</pluralform> +<pluralform>Opravdu chcete pÅ™esunout následujÃcà %x položky do KoÅ¡e?</pluralform> +<pluralform>Opravdu chcete pÅ™esunout následujÃcÃch %x položek do KoÅ¡e?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Opravdu chcete smazat následujÃcà položku?</pluralform> +<pluralform>Opravdu chcete smazat následujÃcà %x položky?</pluralform> +<pluralform>Opravdu chcete smazat následujÃcÃch %x položek?</pluralform> +</target> + <source>Direct</source> <target>Zachovat</target> @@ -1092,26 +1112,6 @@ Poznámka: Filtr je aplikován relativnÄ› k základnÃm adresářům. <source>Make hidden warnings and dialogs visible again?</source> <target>Povolit opÄ›t zobrazovánà skrytých dialogů a hlášenÃ?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>Opravdu chcete pÅ™esunout následujÃcà položku do KoÅ¡e?</pluralform> -<pluralform>Opravdu chcete pÅ™esunout následujÃcà %x položky do KoÅ¡e?</pluralform> -<pluralform>Opravdu chcete pÅ™esunout následujÃcÃch %x položek do KoÅ¡e?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>Opravdu chcete smazat následujÃcà položku?</pluralform> -<pluralform>Opravdu chcete smazat následujÃcà %x položky?</pluralform> -<pluralform>Opravdu chcete smazat následujÃcÃch %x položek?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>Ponechat jako nevyÅ™eÅ¡ený konflikt</target> @@ -1140,7 +1140,7 @@ Poznámka: Filtr je aplikován relativnÄ› k základnÃm adresářům. <target>Soubory</target> <source>Items</source> -<target></target> +<target>Položky</target> <source>Percentage</source> <target>Procentnà podÃl</target> @@ -1188,7 +1188,7 @@ Poznámka: Filtr je aplikován relativnÄ› k základnÃm adresářům. <target>Nelze vytvoÅ™it adresář %x.</target> <source>Cannot copy symbolic link %x to %y.</source> -<target>Nelze kopÃtovat zástupce %x do %y.</target> +<target>Nelze kopÃrovat zástupce %x do %y.</target> <source>Cannot copy file %x to %y.</source> <target>Nelze kopÃrovat soubor %x do %y.</target> @@ -1211,9 +1211,6 @@ Poznámka: Filtr je aplikován relativnÄ› k základnÃm adresářům. <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1278,7 +1275,7 @@ Poznámka: Filtr je aplikován relativnÄ› k základnÃm adresářům. <target>PÅ™esouvánà souboru %x do KoÅ¡e</target> <source>Moving folder %x to recycle bin</source> -<target>PÅ™esouvánà afresáře %x do KoÅ¡e</target> +<target>PÅ™esouvánà adresáře %x do KoÅ¡e</target> <source>Moving symbolic link %x to recycle bin</source> <target>PÅ™esouvánà symbolického odkazu %x do KoÅ¡e</target> @@ -1301,8 +1298,8 @@ Poznámka: Filtr je aplikován relativnÄ› k základnÃm adresářům. <source>Cannot find the following folders:</source> <target>Nelze najÃt následujÃcà složky:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>Tuto chybu můžete pÅ™eskoÄit a považovat neexistujÃcà složku jako prázdnou.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>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.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Adresáře jsou závislé! BuÄte opatrnà s definicà synchronizaÄnÃch pravidel:</target> @@ -1341,16 +1338,16 @@ Poznámka: Filtr je aplikován relativnÄ› k základnÃm adresářům. <target>ObÄ› strany jsou shodné</target> <source>Copy new item to left</source> -<target>KopÃrovat novou položku do leva</target> +<target>KopÃrovat novou položku doleva</target> <source>Copy new item to right</source> -<target>KopÃrovat novou položku do prava</target> +<target>KopÃrovat novou položku doprava</target> <source>Delete left item</source> -<target>Smazat položku z leva</target> +<target>Smazat položku zleva</target> <source>Delete right item</source> -<target>Smazat položku z prava</target> +<target>Smazat položku zprava</target> <source>Move file on left</source> <target>PÅ™esunout soubor nalevo</target> @@ -1359,19 +1356,19 @@ Poznámka: Filtr je aplikován relativnÄ› k základnÃm adresářům. <target>PÅ™esunout soubor napravo</target> <source>Overwrite left item</source> -<target>PÅ™epsat levou položku tou z prava</target> +<target>PÅ™epsat levou položku tou zprava</target> <source>Overwrite right item</source> -<target>PÅ™epsat pravou položku tou z leva</target> +<target>PÅ™epsat pravou položku tou zleva</target> <source>Do nothing</source> <target>Nic nedÄ›lat</target> <source>Update attributes on left</source> -<target>Nastavit vlastnosti v levo</target> +<target>Nastavit vlastnosti vlevo</target> <source>Update attributes on right</source> -<target>Nastavit vlastnoti v pravo</target> +<target>Nastavit vlastnosti vpravo</target> <source>Multiple...</source> <target>Různé...</target> @@ -1410,7 +1407,7 @@ Poznámka: Filtr je aplikován relativnÄ› k základnÃm adresářům. <target>Nelze najÃt %x.</target> <source>Target folder %x already existing.</source> -<target>CÃlova složka %x již existuje.</target> +<target>CÃlová složka %x již existuje.</target> <source>Target folder input field must not be empty.</source> <target>CÃlová složka nesmà být prázdná.</target> @@ -1440,7 +1437,7 @@ Poznámka: Filtr je aplikován relativnÄ› k základnÃm adresářům. <target>K dispozici</target> <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> -<target>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à synchronizacem.</target> +<target>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.</target> <source>Left</source> <target>Levý</target> diff --git a/BUILD/Languages/danish.lng b/BUILD/Languages/danish.lng index 43e5de24..aa57a5c9 100644 --- a/BUILD/Languages/danish.lng +++ b/BUILD/Languages/danish.lng @@ -115,15 +115,6 @@ <source>Cannot resolve symbolic link %x.</source> <target>Kan ikke følge symlinket %x.</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source> <pluralform>1 Byte</pluralform> <pluralform>%x Bytes</pluralform> @@ -133,6 +124,15 @@ <pluralform>%x byte</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>Databasefilen %x er inkompatibel.</target> @@ -169,9 +169,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>Venter mens mappe lÃ¥ses (%x)...</target> -<source>Creating file %x</source> -<target>Opretter filen %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -181,15 +178,15 @@ <pluralform>%x sek</pluralform> </target> +<source>Creating file %x</source> +<target>Opretter filen %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Behandlingsfejl i filen %x, række %y, kolonne %z.</target> <source>Scanning:</source> <target>Skanner:</target> -<source>Encoding extended time information: %x</source> -<target>Opretter udvidet tidsinformation: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -199,6 +196,9 @@ <pluralform>[%x trÃ¥de]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>Opretter udvidet tidsinformation: %x</target> + <source>/sec</source> <target>/sek</target> @@ -209,10 +209,10 @@ <target>Indstillingsfilen %x er kun delvist indlæst.</target> <source>Cannot access Volume Shadow Copy Service.</source> -<target>Kan ikke opnÃ¥ adgang til VVS.</target> +<target>Kan ikke opnÃ¥ adgang til VSS.</target> <source>Please use FreeFileSync 64-bit version to create shadow copies on this system.</source> -<target>Brug FreeFileSync 64-bit til at lave VVS kopier pÃ¥ dette system.</target> +<target>Brug FreeFileSync 64-bit til at lave VSS kopier pÃ¥ dette system.</target> <source>Cannot load file %x.</source> <target>Kan ikke indlæse filen %x.</target> @@ -328,7 +328,7 @@ Kommandoen udføres hvis: <target>Venter pÃ¥ manglende mapper...</target> <source>A folder input field is empty.</source> -<target>Der findes et udefineret mappevalg.</target> +<target>Der er ikke valgt nogen mapper.</target> <source>Synchronization aborted!</source> <target>Synkronisering afbrudt!</target> @@ -751,7 +751,7 @@ Bemærk: Filnavne skal relatere til grundmapperne! <target>Kopier lÃ¥ste filer</target> <source>Copy shared or locked files using Volume Shadow Copy Service (Requires Administrator rights)</source> -<target>Kopier delte eller lÃ¥ste filer ved hjælp af VVS (kræver administrative rettigheder)</target> +<target>Kopier delte eller lÃ¥ste filer ved hjælp af VSS (kræver administrative rettigheder)</target> <source>Copy file access permissions</source> <target>Kopier adgangstilladelser</target> @@ -792,9 +792,6 @@ Bemærk: Filnavne skal relatere til grundmapperne! <source>&Find next</source> <target>Find næste</target> -<source>Operation aborted!</source> -<target>Handling afbrudt!</target> - <source>Main bar</source> <target>Hovedlinie</target> @@ -822,6 +819,33 @@ Bemærk: Filnavne skal relatere til grundmapperne! <source>Compare both sides</source> <target>Analyser begge sider</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>1 mappe</pluralform> +<pluralform>%x mapper</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>1 fil</pluralform> +<pluralform>%x filer</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%y af 1 række i visning</pluralform> +<pluralform>%y af %x rækker i visning</pluralform> +</target> + <source>Set direction:</source> <target>Retning:</target> @@ -936,6 +960,9 @@ Bemærk: Filnavne skal relatere til grundmapperne! <source>Set as default</source> <target>Sæt som standard</target> +<source>Operation aborted!</source> +<target>Handling afbrudt!</target> + <source>All folders are in sync!</source> <target>Alle mapper er synkrone</target> @@ -951,33 +978,6 @@ Bemærk: Filnavne skal relatere til grundmapperne! <source>Searching for program updates...</source> <target>Søger efter opdatering...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>1 mappe</pluralform> -<pluralform>%x mapper</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>1 fil</pluralform> -<pluralform>%x filer</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x af 1 række</pluralform> -<pluralform>%x af %y rækker</pluralform> -</target> - <source>Ignore further errors</source> <target>Ignorer fremtidige fejl</target> @@ -1059,6 +1059,24 @@ Bemærk: Filnavne skal relatere til grundmapperne! <source>Filter</source> <target>Filter</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Vil du flytte emnet til papirkurven?</pluralform> +<pluralform>Vil du flytte følgende %x emner til papirkurven?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Vil du slette følgende emne?</pluralform> +<pluralform>Vil du slette følgende %x emner?</pluralform> +</target> + <source>Direct</source> <target>Direkte</target> @@ -1086,24 +1104,6 @@ Bemærk: Filnavne skal relatere til grundmapperne! <source>Make hidden warnings and dialogs visible again?</source> <target>Vis skjulte advarsler og dialoger igen?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>Vil du flytte emnet til papirkurven?</pluralform> -<pluralform>Vil du flytte følgende %x emner til papirkurven?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>Vil du slette følgende emne?</pluralform> -<pluralform>Vil du slette følgende %x emner?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>Efterlad som uløst konflikt</target> @@ -1131,6 +1131,9 @@ Bemærk: Filnavne skal relatere til grundmapperne! <source>Files</source> <target>Filer</target> +<source>Items</source> +<target>Emner</target> + <source>Percentage</source> <target>Procent</target> @@ -1200,9 +1203,6 @@ Bemærk: Filnavne skal relatere til grundmapperne! <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1287,8 +1287,8 @@ Bemærk: Filnavne skal relatere til grundmapperne! <source>Cannot find the following folders:</source> <target>Kan ikke finde følgende mapper:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>Ignorer denne fejl for at betragte hver mappe som tom.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>Ignorer denne fejl for at betragte hver mappe som tom. Mapperne bliver sÃ¥ automatisk oprettet ved synkronisering.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Afhængige mapper! Vær forsigtig nÃ¥r du laver synkroniseringsregler:</target> diff --git a/BUILD/Languages/dutch.lng b/BUILD/Languages/dutch.lng index 4bb61171..cec09b84 100644 --- a/BUILD/Languages/dutch.lng +++ b/BUILD/Languages/dutch.lng @@ -115,15 +115,6 @@ <source>Cannot resolve symbolic link %x.</source> <target>Kan snelkoppeling %x niet vinden.</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source> <pluralform>1 Byte</pluralform> <pluralform>%x Bytes</pluralform> @@ -133,6 +124,15 @@ <pluralform>%x Bytes</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>Databasebestand %x is niet compatibel.</target> @@ -169,9 +169,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>Wachten totdat map is vergrendeld (%x)...</target> -<source>Creating file %x</source> -<target>Bestand %x wordt aangemaakt</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -181,15 +178,15 @@ <pluralform>%x sec</pluralform> </target> +<source>Creating file %x</source> +<target>Bestand %x wordt aangemaakt</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Fout bij het parsen van bestand %x, rij %y, kolom %z.</target> <source>Scanning:</source> <target>Doorzoekt:</target> -<source>Encoding extended time information: %x</source> -<target>Coderen uitgebreide tijdinformatie: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -199,6 +196,9 @@ <pluralform>[%x Threads]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>Coderen uitgebreide tijdinformatie: %x</target> + <source>/sec</source> <target>/sec</target> @@ -822,6 +822,33 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! <source>Compare both sides</source> <target>Vergelijk beide zijdes</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>1 map</pluralform> +<pluralform>%x mappen</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>1 bestand</pluralform> +<pluralform>%x bestanden</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%y van 1 rij in weergave</pluralform> +<pluralform>%y van %x rijen in weergave</pluralform> +</target> + <source>Set direction:</source> <target>Stel richting in:</target> @@ -951,33 +978,6 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! <source>Searching for program updates...</source> <target>Bezig met zoeken naar programma updates...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>1 map</pluralform> -<pluralform>%x mappen</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>1 bestand</pluralform> -<pluralform>%x bestanden</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x van 1 rij in het overzicht</pluralform> -<pluralform>%x van %y rijen in het overzicht</pluralform> -</target> - <source>Ignore further errors</source> <target>Negeer verdere foutmeldingen</target> @@ -1059,6 +1059,24 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! <source>Filter</source> <target>Filter</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Weet u zeker dat u het volgende item naar de Prullebak wilt verplaatsen?</pluralform> +<pluralform>Weet u zeker dat u de volgende %x items naar de Prullebak wilt verplaatsen?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Wilt u het volgende item echt verwijderen?</pluralform> +<pluralform>Wilt u echt de volgende %x items verwijderen?</pluralform> +</target> + <source>Direct</source> <target>Direct</target> @@ -1086,24 +1104,6 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! <source>Make hidden warnings and dialogs visible again?</source> <target>Verborgen waarschuwingen en dialogen zichtbaar maken?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>Weet u zeker dat u het volgende item naar de Prullebak wilt verplaatsen?</pluralform> -<pluralform>Weet u zeker dat u de volgende %x items naar de Prullebak wilt verplaatsen?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>Wilt u het volgende item echt verwijderen?</pluralform> -<pluralform>Wilt u echt de volgende %x items verwijderen?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>Beschouw als onopgelost conflict</target> @@ -1131,6 +1131,9 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! <source>Files</source> <target>Bestanden</target> +<source>Items</source> +<target>Items</target> + <source>Percentage</source> <target>Percentage</target> @@ -1200,9 +1203,6 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1287,8 +1287,8 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! <source>Cannot find the following folders:</source> <target>Kan de volgende mappen niet vinden:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>U kunt deze foutmelding negeren om elke map als leeg te beschouwen.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>U kunt deze foutmelding negeren om elke map als leeg te markeren. De mappen worden dan automatisch aangemaakt tijdens de synchronisatie.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Mappen zijn afhankelijk van elkaar! Wees voorzichtig met het maken van synchronisatieregels:</target> diff --git a/BUILD/Languages/english_uk.lng b/BUILD/Languages/english_uk.lng index 93340c36..43cf6f51 100644 --- a/BUILD/Languages/english_uk.lng +++ b/BUILD/Languages/english_uk.lng @@ -115,14 +115,11 @@ <source>Cannot resolve symbolic link %x.</source> <target>Cannot resolve symbolic link %x.</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> +<source>Cannot determine final path for %x.</source> +<target>Cannot determine final path for %x.</target> -<source>%x GB</source> -<target>%x GB</target> +<source>Cannot find system function %x.</source> +<target>Cannot find system function %x.</target> <source> <pluralform>1 Byte</pluralform> @@ -133,6 +130,15 @@ <pluralform>%x Bytes</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>Database file %x is incompatible.</target> @@ -169,9 +175,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>Waiting while directory is locked (%x)...</target> -<source>Creating file %x</source> -<target>Creating file %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -181,15 +184,15 @@ <pluralform>%x sec</pluralform> </target> +<source>Creating file %x</source> +<target>Creating file %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Error parsing file %x, row %y, column %z.</target> <source>Scanning:</source> <target>Scanning:</target> -<source>Encoding extended time information: %x</source> -<target>Encoding extended time information: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -199,6 +202,9 @@ <pluralform>[%x Threads]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>Encoding extended time information: %x</target> + <source>/sec</source> <target>/sec</target> @@ -411,12 +417,12 @@ The command is triggered if: <source>Date:</source> <target>Date:</target> -<source>Action</source> -<target>Action</target> - <source>Category</source> <target>Category</target> +<source>Action</source> +<target>Action</target> + <source>Drag && drop</source> <target>Drag && drop</target> @@ -792,12 +798,6 @@ Note: File names must be relative to base directories! <source>&Find next</source> <target>&Find next</target> -<source>Operation aborted!</source> -<target>Operation aborted!</target> - -<source>Main bar</source> -<target>Main bar</target> - <source>Folder pairs</source> <target>Folder pairs</target> @@ -807,6 +807,9 @@ Note: File names must be relative to base directories! <source>Configuration</source> <target>Configuration</target> +<source>Main bar</source> +<target>Main bar</target> + <source>Filter files</source> <target>Filter files</target> @@ -822,6 +825,33 @@ Note: File names must be relative to base directories! <source>Compare both sides</source> <target>Compare both sides</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</target> + <source>Set direction:</source> <target>Set direction:</target> @@ -936,48 +966,21 @@ Note: File names must be relative to base directories! <source>Set as default</source> <target>Set as default</target> +<source>Operation aborted!</source> +<target>Operation aborted!</target> + <source>All folders are in sync!</source> <target>All folders are in sync!</target> <source>Comma separated list</source> <target>Comma separated list</target> -<source>Legend</source> -<target>Legend</target> - <source>File list exported!</source> <target>File list exported!</target> <source>Searching for program updates...</source> <target>Searching for program updates...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</target> - <source>Ignore further errors</source> <target>Ignore further errors</target> @@ -1059,6 +1062,24 @@ Note: File names must be relative to base directories! <source>Filter</source> <target>Filter</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</target> + <source>Direct</source> <target>Direct</target> @@ -1086,39 +1107,21 @@ Note: File names must be relative to base directories! <source>Make hidden warnings and dialogs visible again?</source> <target>Make hidden warnings and dialogues visible again?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>Leave as unresolved conflict</target> -<source>Time stamp</source> -<target>Time stamp</target> - -<source>Append a timestamp to each file name</source> -<target>Append a timestamp to each file name</target> - <source>Replace</source> <target>Replace</target> <source>Move files and replace if existing</source> <target>Move files and replace if existing</target> +<source>Time stamp</source> +<target>Time stamp</target> + +<source>Append a timestamp to each file name</source> +<target>Append a timestamp to each file name</target> + <source>Folder</source> <target>Folder</target> @@ -1131,6 +1134,9 @@ Note: File names must be relative to base directories! <source>Files</source> <target>Files</target> +<source>Items</source> +<target>Items</target> + <source>Percentage</source> <target>Percentage</target> @@ -1158,9 +1164,6 @@ Note: File names must be relative to base directories! <source>Cannot write modification time of %x.</source> <target>Cannot write modification time of %x.</target> -<source>Cannot find system function %x.</source> -<target>Cannot find system function %x.</target> - <source>Cannot read security context of %x.</source> <target>Cannot read security context of %x.</target> @@ -1176,8 +1179,8 @@ Note: File names must be relative to base directories! <source>Cannot create directory %x.</source> <target>Cannot create directory %x.</target> -<source>Cannot copy symbolic link %x to %y.</source> -<target>Cannot copy symbolic link %x to %y.</target> +<source>Cannot create symbolic link %x.</source> +<target>Cannot create symbolic link %x.</target> <source>Cannot copy file %x to %y.</source> <target>Cannot copy file %x to %y.</target> @@ -1191,18 +1194,12 @@ Note: File names must be relative to base directories! <source>Cannot enumerate directory %x.</source> <target>Cannot enumerate directory %x.</target> -<source>Detected endless directory recursion.</source> -<target>Detected endless directory recursion.</target> - <source>%x TB</source> <target>%x TB</target> <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1287,18 +1284,12 @@ Note: File names must be relative to base directories! <source>Cannot find the following folders:</source> <target>Cannot find the following folders:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>You can ignore this error to consider each folder as empty.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronisation.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Directories are dependent! Be careful when setting up synchronisation rules:</target> -<source>Start comparison</source> -<target>Start comparison</target> - -<source>Calculating sync directions...</source> -<target>Calculating sync directions...</target> - <source>Conflict detected:</source> <target>Conflict detected:</target> @@ -1311,18 +1302,21 @@ Note: File names must be relative to base directories! <source>Items differ in attributes only</source> <target>Items differ in attributes only</target> -<source>Symbolic links %x have the same date but a different target.</source> -<target>Symbolic links %x have the same date but a different target.</target> +<source>Resolving symbolic link %x</source> +<target>Resolving symbolic link %x</target> <source>Comparing content of files %x</source> <target>Comparing content of files %x</target> -<source>Comparing files by content failed.</source> -<target>Comparing files by content failed.</target> - <source>Generating file list...</source> <target>Generating file list...</target> +<source>Start comparison</source> +<target>Start comparison</target> + +<source>Calculating sync directions...</source> +<target>Calculating sync directions...</target> + <source>Both sides are equal</source> <target>Both sides are equal</target> @@ -1428,12 +1422,6 @@ Note: File names must be relative to base directories! <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>A folder will be modified which is part of multiple folder pairs. Please review synchronisation settings.</target> -<source>Left</source> -<target>Left</target> - -<source>Right</source> -<target>Right</target> - <source>Synchronizing folder pair:</source> <target>Synchronising folder pair:</target> diff --git a/BUILD/Languages/finnish.lng b/BUILD/Languages/finnish.lng index 5d82fab5..8c594d10 100644 --- a/BUILD/Languages/finnish.lng +++ b/BUILD/Languages/finnish.lng @@ -115,15 +115,6 @@ <source>Cannot resolve symbolic link %x.</source> <target>Tämä linkki on virheellinen %x.</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source> <pluralform>1 Byte</pluralform> <pluralform>%x Bytes</pluralform> @@ -133,6 +124,15 @@ <pluralform>%x tavua</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>Tietokanta %x vierasta muotoa.</target> @@ -161,7 +161,7 @@ <target>Virhe havaittu!</target> <source>Cannot read file attributes of %x.</source> -<target>x% -ominaisuudet ei tunnisteta.</target> +<target>Tiedoston %x määritteitä ei voitu lukea.</target> <source>Cannot get process information.</source> <target>Prosessin tietoja ei saada.</target> @@ -169,9 +169,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>Odotan hakemiston lukitusta (%x)...</target> -<source>Creating file %x</source> -<target>Luodaan tiedosto %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -181,15 +178,15 @@ <pluralform>%x s</pluralform> </target> +<source>Creating file %x</source> +<target>Luodaan tiedosto %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Virhe jäsennys tiedo %x, rivi %y, sarake %z.</target> <source>Scanning:</source> <target>Skannaus:</target> -<source>Encoding extended time information: %x</source> -<target>Tulkitaan laajennettua aikatietoa: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -199,6 +196,9 @@ <pluralform>[%x säijettä]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>Tulkitaan laajennettua aikatietoa: %x</target> + <source>/sec</source> <target>/s</target> @@ -792,9 +792,6 @@ Huom: Tiedostojen nimet täytyy olla suhteessa pää hakemistoihin! <source>&Find next</source> <target>&Etsi seuraava</target> -<source>Operation aborted!</source> -<target>Toiminto lopetettiin!</target> - <source>Main bar</source> <target>Pääpalkki</target> @@ -822,6 +819,33 @@ Huom: Tiedostojen nimet täytyy olla suhteessa pää hakemistoihin! <source>Compare both sides</source> <target>Vertaile molemmat puolet</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>1 hakemisto</pluralform> +<pluralform>%x hakemistoja</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>1 tiedosto</pluralform> +<pluralform>%x tiedostoja</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%y rivi 1:stä näytetään</pluralform> +<pluralform>%y riviä %x:stä näytetään</pluralform> +</target> + <source>Set direction:</source> <target>Aseta suunta:</target> @@ -936,6 +960,9 @@ Huom: Tiedostojen nimet täytyy olla suhteessa pää hakemistoihin! <source>Set as default</source> <target>Aseta oletukseksi</target> +<source>Operation aborted!</source> +<target>Toiminto lopetettiin!</target> + <source>All folders are in sync!</source> <target>Kaikki hakemistot täsmäytetty!</target> @@ -951,33 +978,6 @@ Huom: Tiedostojen nimet täytyy olla suhteessa pää hakemistoihin! <source>Searching for program updates...</source> <target>Ohjelmapäivytys haetaa...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>1 hakemisto</pluralform> -<pluralform>%x hakemistoja</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>1 tiedosto</pluralform> -<pluralform>%x tiedostoja</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x / 1:stä rivistä näytössä</pluralform> -<pluralform>%x / %y:tä rivistä näytössä</pluralform> -</target> - <source>Ignore further errors</source> <target>Hylkää toistuvat virheviestit</target> @@ -1059,6 +1059,24 @@ Huom: Tiedostojen nimet täytyy olla suhteessa pää hakemistoihin! <source>Filter</source> <target>Suodin</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Haluatko todella siirtää tätä Roskakoriin?</pluralform> +<pluralform>Haluatko todella siirtää nämä %x kohdetta Roskakoriin?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Haluatko todella poistta tämä?</pluralform> +<pluralform>Haluatko todella poistaa nämä %x kohdetta?</pluralform> +</target> + <source>Direct</source> <target>Suoraan</target> @@ -1086,24 +1104,6 @@ Huom: Tiedostojen nimet täytyy olla suhteessa pää hakemistoihin! <source>Make hidden warnings and dialogs visible again?</source> <target>Näytä piiloitetut varoitukset/ikkunnat uudestaan?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>Haluatko todella siirtää tätä Roskakoriin?</pluralform> -<pluralform>Haluatko todella siirtää nämä %x kohdetta Roskakoriin?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>Haluatko todella poistta tämä?</pluralform> -<pluralform>Haluatko todella poistaa nämä %x kohdetta?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>Jätä ratkaisemattomana virheenä</target> @@ -1132,7 +1132,7 @@ Huom: Tiedostojen nimet täytyy olla suhteessa pää hakemistoihin! <target>Tiedostot</target> <source>Items</source> -<target></target> +<target>Kappaletta</target> <source>Percentage</source> <target>Prosenttia</target> @@ -1192,7 +1192,7 @@ Huom: Tiedostojen nimet täytyy olla suhteessa pää hakemistoihin! <target>Hakemistoa %x ei voida avata.</target> <source>Cannot enumerate directory %x.</source> -<target>Hakemistoa ei voida luetella.</target> +<target>Hakemistoa %x ei voitu luetella.</target> <source>Detected endless directory recursion.</source> <target>Päättymätön hakemiston toisto.</target> @@ -1203,9 +1203,6 @@ Huom: Tiedostojen nimet täytyy olla suhteessa pää hakemistoihin! <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1290,8 +1287,8 @@ Huom: Tiedostojen nimet täytyy olla suhteessa pää hakemistoihin! <source>Cannot find the following folders:</source> <target>Hakemistot ei löydy:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>Ohita tämä virhe olettamalla hakemistot tyhjäksi.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>Voit ohita virhe olettamalla hakemistot tyhjiksi. Hakemistot luodaan täsmäytyksen aikana.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Hakemistot riippuvuussuhteessa! Aseta täsmäyssääntöjä varovasti:</target> @@ -1431,20 +1428,11 @@ Huom: Tiedostojen nimet täytyy olla suhteessa pää hakemistoihin! <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>Moniosaisen hakemistoparin hakemistoa muutetaan. Tarkista täsmäytyksen asetuksia.</target> -<source>Left</source> -<target>Vasen</target> - -<source>Right</source> -<target>Oikea</target> - <source>Synchronizing folder pair:</source> <target>Täsmäytetään hakemistoparia:</target> <source>Generating database...</source> -<target> -Tietokanta luodaan. -.. -</target> +<target>Tietokanta luodaan...</target> <source>Creating Volume Shadow Copy for %x...</source> <target>Luodaan Volume Shadow Copy %x:lle...</target> diff --git a/BUILD/Languages/french.lng b/BUILD/Languages/french.lng index 2bbb75d9..b6f7d90d 100644 --- a/BUILD/Languages/french.lng +++ b/BUILD/Languages/french.lng @@ -115,15 +115,6 @@ <source>Cannot resolve symbolic link %x.</source> <target>Impossible de résoudre le lien symbolique %x.</target> -<source>%x MB</source> -<target>%x Mo</target> - -<source>%x KB</source> -<target>%x Ko</target> - -<source>%x GB</source> -<target>%x Go</target> - <source> <pluralform>1 Byte</pluralform> <pluralform>%x Bytes</pluralform> @@ -133,6 +124,15 @@ <pluralform>%x octets</pluralform> </target> +<source>%x MB</source> +<target>%x Mo</target> + +<source>%x KB</source> +<target>%x Ko</target> + +<source>%x GB</source> +<target>%x Go</target> + <source>Database file %x is incompatible.</source> <target>La base de données %x n'est pas compatible.</target> @@ -169,9 +169,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>En attente tant que le répertoire est verrouillé (%x)...</target> -<source>Creating file %x</source> -<target>Création du fichier %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -181,15 +178,15 @@ <pluralform>%x sec</pluralform> </target> +<source>Creating file %x</source> +<target>Création du fichier %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Erreur lors de l'analyse du fichier %x, ligne %y, colonne %z.</target> <source>Scanning:</source> <target>Lecture en cours :</target> -<source>Encoding extended time information: %x</source> -<target>Codage de l'heure au format étendu : %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -199,6 +196,9 @@ <pluralform>[%x Tâches]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>Codage de l'heure au format étendu : %x</target> + <source>/sec</source> <target>/sec</target> @@ -792,9 +792,6 @@ Attention : les noms de fichiers doivent être relatifs aux répertoires de base <source>&Find next</source> <target>&Chercher le suivant</target> -<source>Operation aborted!</source> -<target>Opération abandonnée !</target> - <source>Main bar</source> <target>Barre principale</target> @@ -822,6 +819,33 @@ Attention : les noms de fichiers doivent être relatifs aux répertoires de base <source>Compare both sides</source> <target>Comparer les deux listes</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>%x répertoire</pluralform> +<pluralform>%x répertoires</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>%x fichier</pluralform> +<pluralform>%x fichiers</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>vue de %y ligne sur %x</pluralform> +<pluralform>vue de %y lignes sur %x</pluralform> +</target> + <source>Set direction:</source> <target>Choix de la direction :</target> @@ -936,6 +960,9 @@ Attention : les noms de fichiers doivent être relatifs aux répertoires de base <source>Set as default</source> <target>Définir par défaut</target> +<source>Operation aborted!</source> +<target>Opération abandonnée !</target> + <source>All folders are in sync!</source> <target>Tous les dossiers sont synchronisés !</target> @@ -951,33 +978,6 @@ Attention : les noms de fichiers doivent être relatifs aux répertoires de base <source>Searching for program updates...</source> <target>Recherche de mises à jour ...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>%x répertoire</pluralform> -<pluralform>%x répertoires</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>%x fichier</pluralform> -<pluralform>%x fichiers</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x of %y ligne affichée</pluralform> -<pluralform>%x of %y lignes affichées</pluralform> -</target> - <source>Ignore further errors</source> <target>Ignorer les erreurs suivantes</target> @@ -1059,6 +1059,24 @@ Attention : les noms de fichiers doivent être relatifs aux répertoires de base <source>Filter</source> <target>Filtre</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Voulez-vous vraiment mettre dans la corbeille %x élément ?</pluralform> +<pluralform>Voulez-vous vraiment mettre dans la corbeille ces %x éléments ?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Voulez-vous vraiment supprimer %x élément ?</pluralform> +<pluralform>Voulez-vous vraiment supprimer ces %x éléments ?</pluralform> +</target> + <source>Direct</source> <target>Direct</target> @@ -1081,29 +1099,11 @@ Attention : les noms de fichiers doivent être relatifs aux répertoires de base <target>- Duplication vers %item_path%</target> <source>- Other side's counterpart to %item_folder%</source> -<target>Duplication vers %item_folder%</target> +<target>- Duplication vers %item_folder%</target> <source>Make hidden warnings and dialogs visible again?</source> <target>Voulez-vous réafficher les avertissements et les boîtes de dialogue ?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>Voulez-vous vraiment mettre à la Corbeille l'élément suivant ?</pluralform> -<pluralform>Voulez-vous vraiment mettre à la Corbeille les %x éléments suivants ?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>Voulez-vous vraiment supprimer l'élément suivant ?</pluralform> -<pluralform>Voulez-vous vraiment supprimer les %x éléments suivants ?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>Abandonner en tant que conflit non résolu</target> @@ -1131,6 +1131,9 @@ Attention : les noms de fichiers doivent être relatifs aux répertoires de base <source>Files</source> <target>Fichiers</target> +<source>Items</source> +<target>Éléments</target> + <source>Percentage</source> <target>Pourcentage</target> @@ -1180,7 +1183,7 @@ Attention : les noms de fichiers doivent être relatifs aux répertoires de base <target>Impossible de copier le lien symbolique %x vers %y.</target> <source>Cannot copy file %x to %y.</source> -<target>Impossible de copier le fichier %X vers %y.</target> +<target>Impossible de copier le fichier %x vers %y.</target> <source>Type of item %x is not supported:</source> <target>Le type de l'élément %x n'est pas accepté :</target> @@ -1200,9 +1203,6 @@ Attention : les noms de fichiers doivent être relatifs aux répertoires de base <source>%x PB</source> <target>%x Po</target> -<source>%x%</source> -<target>%x %</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1287,8 +1287,8 @@ Attention : les noms de fichiers doivent être relatifs aux répertoires de base <source>Cannot find the following folders:</source> <target>Impossible de trouver les dossiers suivants :</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>Vous pouvez ignorer cette erreur et considérer chaque dossier comme vide.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>Vous pouvez ignorer cette erreur en considérant chaque dossier comme vide. Les dossiers seront automatiquement créés pendant la synchronisation.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Les répertoires sont imbriqués ! Attention à la mise à jour des règles de synchronisation :</target> @@ -1428,12 +1428,6 @@ Attention : les noms de fichiers doivent être relatifs aux répertoires de base <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>Un dossier faisant partie de paires multiples sera modifié. Veuillez vérifier les paramètres de synchronisation.</target> -<source>Left</source> -<target>Gauche</target> - -<source>Right</source> -<target>Droite</target> - <source>Synchronizing folder pair:</source> <target>Synchronisation de la paire de dossiers</target> diff --git a/BUILD/Languages/german.lng b/BUILD/Languages/german.lng index 706736c4..c068055f 100644 --- a/BUILD/Languages/german.lng +++ b/BUILD/Languages/german.lng @@ -115,14 +115,11 @@ <source>Cannot resolve symbolic link %x.</source> <target>Der Symbolische Link %x kann nicht aufgelöst werden.</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> +<source>Cannot determine final path for %x.</source> +<target>Der endgültige Pfad für %x kann nicht ermittelt werden.</target> -<source>%x GB</source> -<target>%x GB</target> +<source>Cannot find system function %x.</source> +<target>Die Systemfunktion %x wurde nicht gefunden.</target> <source> <pluralform>1 Byte</pluralform> @@ -133,6 +130,15 @@ <pluralform>%x Bytes</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>Die Datenbankdatei %x ist nicht kompatibel.</target> @@ -169,9 +175,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>Warte während Verzeichnis gesperrt ist (%x)...</target> -<source>Creating file %x</source> -<target>Erstelle Datei %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -181,15 +184,15 @@ <pluralform>%x Sek.</pluralform> </target> +<source>Creating file %x</source> +<target>Erstelle Datei %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Fehler beim Auswerten der Datei %x, Zeile %y, Spalte %z.</target> <source>Scanning:</source> <target>Suche Dateien:</target> -<source>Encoding extended time information: %x</source> -<target>Speichere erweiterte Zeitinformation: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -199,6 +202,9 @@ <pluralform>[%x Threads]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>Speichere erweiterte Zeitinformation: %x</target> + <source>/sec</source> <target>/s</target> @@ -411,12 +417,12 @@ Die Befehlszeile wird ausgelöst wenn: <source>Date:</source> <target>Datum:</target> -<source>Action</source> -<target>Aktion</target> - <source>Category</source> <target>Kategorie</target> +<source>Action</source> +<target>Aktion</target> + <source>Drag && drop</source> <target>Drag && Drop</target> @@ -792,12 +798,6 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <source>&Find next</source> <target>&Weitersuchen</target> -<source>Operation aborted!</source> -<target>Vorgang abgebrochen!</target> - -<source>Main bar</source> -<target>Hauptleiste</target> - <source>Folder pairs</source> <target>Ordnerpaare</target> @@ -807,6 +807,9 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <source>Configuration</source> <target>Konfiguration</target> +<source>Main bar</source> +<target>Hauptleiste</target> + <source>Filter files</source> <target>Dateien filtern</target> @@ -822,6 +825,33 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <source>Compare both sides</source> <target>Beide Seiten vergleichen</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>1 Verzeichnis</pluralform> +<pluralform>%x Verzeichnisse</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>1 Datei</pluralform> +<pluralform>%x Dateien</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>Zeige %y von 1 Zeile</pluralform> +<pluralform>Zeige %y von %x Zeilen</pluralform> +</target> + <source>Set direction:</source> <target>Setze Richtung:</target> @@ -886,7 +916,7 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <target>&Nicht speichern</target> <source>Never save changes</source> -<target>Änderungen niemals speichern</target> +<target>Änderungen nie speichern</target> <source>Configuration loaded!</source> <target>Konfiguration geladen!</target> @@ -936,48 +966,21 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <source>Set as default</source> <target>Als Standard festlegen</target> +<source>Operation aborted!</source> +<target>Vorgang abgebrochen!</target> + <source>All folders are in sync!</source> <target>Alle Ordner sind synchron!</target> <source>Comma separated list</source> <target>Kommagetrennte Liste</target> -<source>Legend</source> -<target>Legende</target> - <source>File list exported!</source> <target>Dateiliste exportiert!</target> <source>Searching for program updates...</source> <target>Suche nach aktualisierten Programmversionen...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>1 Verzeichnis</pluralform> -<pluralform>%x Verzeichnisse</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>1 Datei</pluralform> -<pluralform>%x Dateien</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>Zeige %x von 1 Zeile</pluralform> -<pluralform>Zeige %x von %y Zeilen</pluralform> -</target> - <source>Ignore further errors</source> <target>Weitere Fehler ignorieren</target> @@ -1059,6 +1062,24 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <source>Filter</source> <target>Filter</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Soll das folgende Element wirklich in den Papierkorb verschoben werden?</pluralform> +<pluralform>Sollen die folgenden %x Elemente wirklich in den Papierkorb verschoben werden?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Soll das folgende Element wirklich gelöscht werden?</pluralform> +<pluralform>Sollen die folgenden %x Elemente wirklich gelöscht werden?</pluralform> +</target> + <source>Direct</source> <target>Direkt</target> @@ -1086,39 +1107,21 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <source>Make hidden warnings and dialogs visible again?</source> <target>Versteckte Warnungen und Dialoge wieder sichtbar machen?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>Soll das folgende Element wirklich in den Papierkorb verschoben werden?</pluralform> -<pluralform>Sollen die folgenden %x Elemente wirklich in den Papierkorb verschoben werden?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>Soll das folgende Element wirklich gelöscht werden?</pluralform> -<pluralform>Sollen die folgenden %x Elemente wirklich gelöscht werden?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>Als unbehandelten Konflikt belassen</target> -<source>Time stamp</source> -<target>Zeitstempel</target> - -<source>Append a timestamp to each file name</source> -<target>Einen Zeitstempel an jeden Dateinamen anhängen</target> - <source>Replace</source> <target>Ersetzen</target> <source>Move files and replace if existing</source> <target>Dateien verschieben und vorhandene ersetzen</target> +<source>Time stamp</source> +<target>Zeitstempel</target> + +<source>Append a timestamp to each file name</source> +<target>Einen Zeitstempel an jeden Dateinamen anhängen</target> + <source>Folder</source> <target>Ordner</target> @@ -1161,9 +1164,6 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <source>Cannot write modification time of %x.</source> <target>Die Änderungszeit von %x kann nicht geschrieben werden.</target> -<source>Cannot find system function %x.</source> -<target>Die Systemfunktion %x wurde nicht gefunden.</target> - <source>Cannot read security context of %x.</source> <target>Der Sicherheitskontext von %x kann nicht gelesen werden.</target> @@ -1179,8 +1179,8 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <source>Cannot create directory %x.</source> <target>Das Verzeichnis %x kann nicht erstellt werden.</target> -<source>Cannot copy symbolic link %x to %y.</source> -<target>Der Symbolische Link %x kann nicht nach %y kopiert werden.</target> +<source>Cannot create symbolic link %x.</source> +<target>Der Symbolische Link %x kann nicht erstellt werden.</target> <source>Cannot copy file %x to %y.</source> <target>Die Datei %x kann nicht nach %y kopiert werden.</target> @@ -1194,18 +1194,12 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <source>Cannot enumerate directory %x.</source> <target>Das Verzeichnis %x kann nicht gelistet werden.</target> -<source>Detected endless directory recursion.</source> -<target>Eine endlose Verzeichnisrekursion wurde erkannt.</target> - <source>%x TB</source> <target>%x TB</target> <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1296,12 +1290,6 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Die Verzeichnisse sind voneinander abhängig! Achtung beim Festlegen der Synchronisationsregeln:</target> -<source>Start comparison</source> -<target>Starte Vergleich</target> - -<source>Calculating sync directions...</source> -<target>Berechne Synchronisationsrichtungen...</target> - <source>Conflict detected:</source> <target>Ein Konflikt wurde erkannt:</target> @@ -1314,18 +1302,21 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <source>Items differ in attributes only</source> <target>Die Elemente unterscheiden sich nur in Attributen</target> -<source>Symbolic links %x have the same date but a different target.</source> -<target>Die Symbolischen Links %x haben dasselbe Datum, aber ein unterschiedliches Ziel.</target> +<source>Resolving symbolic link %x</source> +<target>Folge dem Symbolischen Link %x</target> <source>Comparing content of files %x</source> <target>Vergleiche Inhalt der Dateien %x</target> -<source>Comparing files by content failed.</source> -<target>Vergleich nach Dateiinhalt ist fehlgeschlagen.</target> - <source>Generating file list...</source> <target>Erzeuge Dateiliste...</target> +<source>Start comparison</source> +<target>Starte Vergleich</target> + +<source>Calculating sync directions...</source> +<target>Berechne Synchronisationsrichtungen...</target> + <source>Both sides are equal</source> <target>Beide Seiten sind gleich</target> @@ -1431,12 +1422,6 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>Ein Ordner wird verändert werden, der Teil mehrerer Ordnerpaare ist. Bitte überprüfen Sie die Synchronisationseinstellungen.</target> -<source>Left</source> -<target>Links</target> - -<source>Right</source> -<target>Rechts</target> - <source>Synchronizing folder pair:</source> <target>Synchronisiere Ordnerpaar:</target> diff --git a/BUILD/Languages/greek.lng b/BUILD/Languages/greek.lng index 51650524..3dc272d9 100644 --- a/BUILD/Languages/greek.lng +++ b/BUILD/Languages/greek.lng @@ -115,15 +115,6 @@ <source>Cannot resolve symbolic link %x.</source> <target>Ο συμβολικός δεσμός %x δεν μποÏεί να επιλυθεί</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source> <pluralform>1 Byte</pluralform> <pluralform>%x Bytes</pluralform> @@ -133,6 +124,15 @@ <pluralform>%x Bytes</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>Το αÏχείο βάσης δεδομÎνων %x δεν είναι συμβατό</target> @@ -169,9 +169,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>Αναμονή μÎχÏι να κλειδωθεί ο υποκατάλογος (%x)...</target> -<source>Creating file %x</source> -<target>ΔημιουÏγία του αÏχείου %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -181,15 +178,15 @@ <pluralform>%x δ/λεπτα</pluralform> </target> +<source>Creating file %x</source> +<target>ΔημιουÏγία του αÏχείου %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Σφάλμα κατά την ανάλυση του αÏχείου %x, γÏαμμή %y, στήλη %z.</target> <source>Scanning:</source> <target>Ανίχνευση:</target> -<source>Encoding extended time information: %x</source> -<target>Κωδικοποίηση εκτεταμÎνων πληÏοφοÏιών για την ÏŽÏα: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -199,6 +196,9 @@ <pluralform>[%x Îήματα]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>Κωδικοποίηση εκτεταμÎνων πληÏοφοÏιών για την ÏŽÏα: %x</target> + <source>/sec</source> <target>/δευτεÏόλεπτο</target> @@ -822,6 +822,33 @@ Note: File names must be relative to base directories! <source>Compare both sides</source> <target>ΣÏγκÏιση των δÏο πλευÏών</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>1 υποκατάλογος</pluralform> +<pluralform>%x υποκατάλογοι</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>1 αÏχείο</pluralform> +<pluralform>%x αÏχεία</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%y από τη 1 φανεÏή γÏαμμή</pluralform> +<pluralform>%y από τις %x φανεÏÎÏ‚ γÏαμμÎÏ‚</pluralform> +</target> + <source>Set direction:</source> <target>Επιλογή κατεÏθυνσης:</target> @@ -951,33 +978,6 @@ Note: File names must be relative to base directories! <source>Searching for program updates...</source> <target>Αναζήτηση καινοÏÏιας Îκδοσης...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>1 υποκατάλογος</pluralform> -<pluralform>%x υποκατάλογοι</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>1 αÏχείο</pluralform> -<pluralform>%x αÏχεία</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x από τη 1 φανεÏή γÏαμμή</pluralform> -<pluralform>%x από τις %y φανεÏÎÏ‚ γÏαμμÎÏ‚</pluralform> -</target> - <source>Ignore further errors</source> <target>Αγνόηση επόμενων σφαλμάτων</target> @@ -1059,6 +1059,24 @@ Note: File names must be relative to base directories! <source>Filter</source> <target>ΦίλτÏο</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Είσαι σίγουÏος ότι θÎλεις να μεταφεÏθεί το ακόλουθο αντικείμενο στον Κάδο ΑνακÏκλωσης;</pluralform> +<pluralform>Είσαι σίγουÏος ότι θÎλεις να μεταφεÏθοÏν τα ακόλουθα %x αντικείμενα στον Κάδο ΑνακÏκλωσης;</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Είσαι σίγουÏος ότι θÎλεις να διαγÏαφεί το ακόλουθο αντικείμενο;</pluralform> +<pluralform>Είσαι σίγουÏος ότι θÎλεις να διαγÏαφοÏν τα ακόλουθα %x αντικείμενα;</pluralform> +</target> + <source>Direct</source> <target>Ως δεσμοÏ</target> @@ -1086,24 +1104,6 @@ Note: File names must be relative to base directories! <source>Make hidden warnings and dialogs visible again?</source> <target>Îα επανεμφανιστοÏν τα κÏυμμÎνα μηνÏματα και Ï€Ïοειδοποιήσεις;</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>Είσαι σίγουÏος ότι θÎλεις να μεταφεÏθεί το ακόλουθο αντικείμενο στον Κάδο ΑνακÏκλωσης;</pluralform> -<pluralform>Είσαι σίγουÏος ότι θÎλεις να μεταφεÏθοÏν τα ακόλουθα %x αντικείμενα στον Κάδο ΑνακÏκλωσης;</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>Είσαι σίγουÏος ότι θÎλεις να διαγÏαφεί το ακόλουθο αντικείμενο;</pluralform> -<pluralform>Είσαι σίγουÏος ότι θÎλεις να διαγÏαφοÏν τα ακόλουθα %x αντικείμενα;</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>ΠαÏάβλεψη ως ανεπίλυτη διÎνεξη</target> @@ -1131,6 +1131,9 @@ Note: File names must be relative to base directories! <source>Files</source> <target>ΑÏχεία</target> +<source>Items</source> +<target>Στοιχεία</target> + <source>Percentage</source> <target>Ποσοστό</target> @@ -1162,10 +1165,10 @@ Note: File names must be relative to base directories! <target>Δεν ανευÏίσκεται η λειτουÏγία συστήματος %x.</target> <source>Cannot read security context of %x.</source> -<target>Δεν μποÏεί να αναγνωσθεί το πεÏιβάλλον ασφαλείας του x%.</target> +<target>Δεν μποÏεί να αναγνωσθεί το πεÏιβάλλον ασφαλείας του %x.</target> <source>Cannot write security context of %x.</source> -<target>Δεν μποÏεί να γίνει εγγÏαφή του πεÏιβάλλοντος ασφαλείας του x%.</target> +<target>Δεν μποÏεί να γίνει εγγÏαφή του πεÏιβάλλοντος ασφαλείας του %x.</target> <source>Cannot read permissions of %x.</source> <target>Δεν μποÏοÏν να αναγνωσθοÏν οι άδειες Ï€ÏοσπÎλασης του %x.</target> @@ -1200,9 +1203,6 @@ Note: File names must be relative to base directories! <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1287,8 +1287,8 @@ Note: File names must be relative to base directories! <source>Cannot find the following folders:</source> <target>Οι ακόλουθοι υποκατάλογοι δεν ήταν δυνατό να βÏεθοÏν:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>ΜποÏείτε να αγνοήσετε αυτό το σφάλμα, για να αντιμετωπίσετε κάθε υποκατάλογο ως κενό.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>ΜποÏείτε να αγνοήσετε αυτό το σφάλμα, για να αντιμετωπίσετε κάθε υποκατάλογο ως κενό. Οι υποκατάλογοι θα δημιουÏγηθοÏν αυτόματα κατά τη διάÏκεια του συγχÏονισμοÏ.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Οι υποκατάλογοι είναι εξαÏτώμενοι. Î Ïοσοχή κατά την εισαγωγή των κανόνων συγχÏονισμοÏ:</target> diff --git a/BUILD/Languages/hebrew.lng b/BUILD/Languages/hebrew.lng index 89c5ab1c..cdaf1dea 100644 --- a/BUILD/Languages/hebrew.lng +++ b/BUILD/Languages/hebrew.lng @@ -115,15 +115,6 @@ <source>Cannot resolve symbolic link %x.</source> <target>×œ× ×™×›×•×œ ×œ×¤×¢× ×— ×ת הקישור הסימבולי %x.</target> -<source>%x MB</source> -<target>%x מגה בייט</target> - -<source>%x KB</source> -<target>%x קילו בייט</target> - -<source>%x GB</source> -<target>%x ×’×™×’×” בייט</target> - <source> <pluralform>1 Byte</pluralform> <pluralform>%x Bytes</pluralform> @@ -133,6 +124,15 @@ <pluralform>%x בייט</pluralform> </target> +<source>%x MB</source> +<target>%x מגה בייט</target> + +<source>%x KB</source> +<target>%x קילו בייט</target> + +<source>%x GB</source> +<target>%x ×’×™×’×” בייט</target> + <source>Database file %x is incompatible.</source> <target>קובץ מסד × ×ª×•× ×™× %x ××™× ×• ×‘×ž×‘× ×” מת××™×.</target> @@ -169,9 +169,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>ממתין ×›×שר מחיצה × ×¢×•×œ×” (%x)...</target> -<source>Creating file %x</source> -<target>יוצר קובץ %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -181,15 +178,15 @@ <pluralform>%x ×©× ×™×•×ª</pluralform> </target> +<source>Creating file %x</source> +<target>יוצר קובץ %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>שגי××” ×‘×¤×¢× ×•×— קובץ %x, שורה %y, טור %z.</target> <source>Scanning:</source> <target>סורק:</target> -<source>Encoding extended time information: %x</source> -<target>מקודד ××™× ×¤×•×¨×ž×¦×™×ª זמן מורחבת: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -199,6 +196,9 @@ <pluralform>[%x ×ª×”×œ×™×›×•× ×™×]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>מקודד ××™× ×¤×•×¨×ž×¦×™×ª זמן מורחבת: %x</target> + <source>/sec</source> <target>/×©× </target> @@ -411,12 +411,12 @@ The command is triggered if: <source>Date:</source> <target>ת×ריך:</target> -<source>Action</source> -<target>פעולה</target> - <source>Category</source> <target>קטגוריה</target> +<source>Action</source> +<target>פעולה</target> + <source>Drag && drop</source> <target>גרור והשלך</target> @@ -792,9 +792,6 @@ Note: File names must be relative to base directories! <source>&Find next</source> <target>&×ž×¦× ×”×‘×</target> -<source>Operation aborted!</source> -<target>הפעולה בוטלה!</target> - <source>Main bar</source> <target>סרגל ר×שי</target> @@ -822,6 +819,33 @@ Note: File names must be relative to base directories! <source>Compare both sides</source> <target>השווה בין ×©× ×™ הצדדי×</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>1 מחיצה</pluralform> +<pluralform>%x מחיצות</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>1 קובץ</pluralform> +<pluralform>%x קבצי×</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%y של שורה 1 במר××”</pluralform> +<pluralform>%y של %x שורות במר××”</pluralform> +</target> + <source>Set direction:</source> <target>בחר כוון:</target> @@ -936,6 +960,9 @@ Note: File names must be relative to base directories! <source>Set as default</source> <target>הגדר כברירת מחדל</target> +<source>Operation aborted!</source> +<target>הפעולה בוטלה!</target> + <source>All folders are in sync!</source> <target>כל התיקיות ×ž×¡×•× ×›×¨× ×•×ª</target> @@ -951,33 +978,6 @@ Note: File names must be relative to base directories! <source>Searching for program updates...</source> <target>מחפש ×¢×™×“×›×•× ×™ ×ª×•×›× ×”...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>1 מחיצה</pluralform> -<pluralform>%x מחיצות</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>1 קובץ</pluralform> -<pluralform>%x קבצי×</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x מתוך 1 שורה בתצוגה</pluralform> -<pluralform>%x מתוך %y שורות בתצוגה</pluralform> -</target> - <source>Ignore further errors</source> <target>×”×ª×¢×œ× ×ž×©×’×™×ות × ×•×¡×¤×•×ª</target> @@ -1059,6 +1059,24 @@ Note: File names must be relative to base directories! <source>Filter</source> <target>×ž×¡× ×Ÿ</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>×”×× ×‘×¨×¦×•× ×š להעביר ×ת הפריט ×”×‘× ×œ×¡×œ המיחזור?</pluralform> +<pluralform>×”×× ×‘×¨×¦×•× ×š להעביר ×ת ×”×¤×¨×™×˜×™× %x הב××™× ×œ×¡×œ המיחזור?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>×”×× ×‘×¨×¦×•× ×š למחוק ×ת הפריט הב×?</pluralform> +<pluralform>×”×× ×‘×¨×¦×•× ×š למחוק ×ת ×”×¤×¨×™×˜×™× %x הב××™×?</pluralform> +</target> + <source>Direct</source> <target>כוון</target> @@ -1086,39 +1104,21 @@ Note: File names must be relative to base directories! <source>Make hidden warnings and dialogs visible again?</source> <target>להפוך די××œ×•×’×™× ×ž×•×¡×ª×¨×™× ×•×זהרות ×œ×’×œ×•×™×™× ×©×•×‘?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>×”×× ×‘×¨×¦×•× ×š להעביר ×ת הפריט ×”×‘× ×œ×¡×œ המיחזור?</pluralform> -<pluralform>×”×× ×‘×¨×¦×•× ×š להעביר ×ת ×”×¤×¨×™×˜×™× %x הב××™× ×œ×¡×œ המיחזור?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>×”×× ×‘×¨×¦×•× ×š למחוק ×ת הפריט הב×?</pluralform> -<pluralform>×”×× ×‘×¨×¦×•× ×š למחוק ×ת ×”×¤×¨×™×˜×™× %x הב××™×?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>הש×ר ×›×§×•× ×¤×œ×™×§×˜ ×œ× ×ž×˜×•×¤×œ</target> -<source>Time stamp</source> -<target>תג זמן</target> - -<source>Append a timestamp to each file name</source> -<target>הצמד תג זמן לכל ×©× ×§×•×‘×¥</target> - <source>Replace</source> <target>החלף</target> <source>Move files and replace if existing</source> <target>העבר ×§×‘×¦×™× ×•×”×—×œ×£ במדה וקיימי×</target> +<source>Time stamp</source> +<target>תג זמן</target> + +<source>Append a timestamp to each file name</source> +<target>הצמד תג זמן לכל ×©× ×§×•×‘×¥</target> + <source>Folder</source> <target>תיקייה</target> @@ -1131,6 +1131,9 @@ Note: File names must be relative to base directories! <source>Files</source> <target>קבצי×</target> +<source>Items</source> +<target>פריטי×</target> + <source>Percentage</source> <target>×חוז</target> @@ -1200,9 +1203,6 @@ Note: File names must be relative to base directories! <source>%x PB</source> <target>%x פטה בייט</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1287,8 +1287,8 @@ Note: File names must be relative to base directories! <source>Cannot find the following folders:</source> <target>×œ× ×™×›×•×œ ×œ×ž×¦×•× ×ת המחיצות הב×ות:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>×פשר ×œ×”×ª×¢×œ× ×ž×©×’×™××” זו ולהחשיב כל מחיצה כריקה.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>×פשר ×œ×”×ª×¢×œ× ×ž×©×’×™××” זו ולהחשיב כל תיקיה כריקה. התיקיות יווצרו ×וטומטית בזמן ×”×¡×™× ×›×¨×•×Ÿ.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>מחיצות תלויות! זהירות בהגדרת כללי ×¡× ×›×¨×•×Ÿ:</target> @@ -1428,12 +1428,6 @@ Note: File names must be relative to base directories! <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>התיקייה ×©×ª×©×ª× ×” ×”×™× ×—×œ×§ מריבוי זוגות תיקיות. ×× × ×¡×§×•×¨ מחדש הגדרות ×¡×™× ×›×¨×•×Ÿ.</target> -<source>Left</source> -<target>ימין</target> - -<source>Right</source> -<target>שמ×ל</target> - <source>Synchronizing folder pair:</source> <target>×ž×¡× ×›×¨×Ÿ זוג תיקיות:</target> diff --git a/BUILD/Languages/hungarian.lng b/BUILD/Languages/hungarian.lng index 58600506..e407e030 100644 --- a/BUILD/Languages/hungarian.lng +++ b/BUILD/Languages/hungarian.lng @@ -115,14 +115,11 @@ <source>Cannot resolve symbolic link %x.</source> <target>Sikertelen a következÅ‘ symlink feloldása: %x.</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> +<source>Cannot determine final path for %x.</source> +<target></target> -<source>%x GB</source> -<target>%x GB</target> +<source>Cannot find system function %x.</source> +<target>Nem található a következÅ‘ rendszerfunkció: %x.</target> <source> <pluralform>1 Byte</pluralform> @@ -133,6 +130,15 @@ <pluralform>%x Bájt</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>Inkompatibilis adatbázisfájl: %x.</target> @@ -169,9 +175,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>Várakozás a mappa zárolásának a feloldására (%x)...</target> -<source>Creating file %x</source> -<target>%x fájl létrehozása</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -181,15 +184,15 @@ <pluralform>%x másodperc</pluralform> </target> +<source>Creating file %x</source> +<target>%x fájl létrehozása</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Hiba történt a fájl feldolgozása közben: %x fájl, %y sor, %z oszlop.</target> <source>Scanning:</source> <target>Vizsgálat:</target> -<source>Encoding extended time information: %x</source> -<target>KibÅ‘vÃtett idÅ‘információk kódolása: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -199,6 +202,9 @@ <pluralform>[%x szál]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>KibÅ‘vÃtett idÅ‘információk kódolása: %x</target> + <source>/sec</source> <target>/másodperc</target> @@ -411,12 +417,12 @@ A parancs végrehajtódik, ha: <source>Date:</source> <target>Dátum:</target> -<source>Action</source> -<target>Művelet</target> - <source>Category</source> <target>Kategória</target> +<source>Action</source> +<target>Művelet</target> + <source>Drag && drop</source> <target>Húzd && Ejtsd</target> @@ -790,12 +796,6 @@ Megjegyzés: A fájlneveknek relatÃvnak kell lenniük az alap mappához viszony <source>&Find next</source> <target>&KövetkezÅ‘ keresése</target> -<source>Operation aborted!</source> -<target>Művelet megszakÃtva!</target> - -<source>Main bar</source> -<target>FÅ‘ panel</target> - <source>Folder pairs</source> <target>Mappa párok</target> @@ -805,6 +805,9 @@ Megjegyzés: A fájlneveknek relatÃvnak kell lenniük az alap mappához viszony <source>Configuration</source> <target>BeállÃtás</target> +<source>Main bar</source> +<target>FÅ‘ panel</target> + <source>Filter files</source> <target>Fájlok szűrése</target> @@ -820,6 +823,33 @@ Megjegyzés: A fájlneveknek relatÃvnak kell lenniük az alap mappához viszony <source>Compare both sides</source> <target>Mindkét oldal összehasonlÃtása</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>1 mappát</pluralform> +<pluralform>%x mappát</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>1 fájlt</pluralform> +<pluralform>%x fájlt</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%y sor látható</pluralform> +<pluralform>%x sorból %y látható</pluralform> +</target> + <source>Set direction:</source> <target>Irány</target> @@ -934,48 +964,21 @@ Megjegyzés: A fájlneveknek relatÃvnak kell lenniük az alap mappához viszony <source>Set as default</source> <target>BeállÃtás alapértelmezettként</target> +<source>Operation aborted!</source> +<target>Művelet megszakÃtva!</target> + <source>All folders are in sync!</source> <target>Minden mappa szinkronban!</target> <source>Comma separated list</source> <target>Comma separated values</target> -<source>Legend</source> -<target>ElÅ‘zmény</target> - <source>File list exported!</source> <target>A fájllista exportálása befejezÅ‘dött!</target> <source>Searching for program updates...</source> <target>SzoftverfrissÃtés keresése...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>1 mappát</pluralform> -<pluralform>%x mappát</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>1 fájlt</pluralform> -<pluralform>%x fájlt</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x/1 sor a nézetben</pluralform> -<pluralform>%x/%y sor a nézetben</pluralform> -</target> - <source>Ignore further errors</source> <target>További hibák figyelmen kÃvül hagyása</target> @@ -1057,6 +1060,24 @@ Megjegyzés: A fájlneveknek relatÃvnak kell lenniük az alap mappához viszony <source>Filter</source> <target>SzűrÅ‘</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Valóban a Lomtárba akarod mozgatni a következÅ‘ elemet?</pluralform> +<pluralform>Valóban a Lomtárba akarod mozgatni a következÅ‘ %x elemet?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Valóban törölni akarod a következÅ‘ elemet?</pluralform> +<pluralform>Valóban törölni akarod a következÅ‘ %x elemet?</pluralform> +</target> + <source>Direct</source> <target>Közvetlen</target> @@ -1084,39 +1105,21 @@ Megjegyzés: A fájlneveknek relatÃvnak kell lenniük az alap mappához viszony <source>Make hidden warnings and dialogs visible again?</source> <target>Tegyük újra láthatóvá a rejtett figyelmeztetéseket és párbeszédablakokat?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>Valóban a Lomtárba akarod mozgatni a következÅ‘ elemet?</pluralform> -<pluralform>Valóban a Lomtárba akarod mozgatni a következÅ‘ %x elemet?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>Valóban törölni akarod a következÅ‘ elemet?</pluralform> -<pluralform>Valóban törölni akarod a következÅ‘ %x elemet?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>Feloldatlan ütközésként hagyni</target> -<source>Time stamp</source> -<target>IdÅ‘bélyeg</target> - -<source>Append a timestamp to each file name</source> -<target>IdÅ‘bélyeg hozzáadása a minden fájlnévhez</target> - <source>Replace</source> <target>FelülÃrás</target> <source>Move files and replace if existing</source> <target>Fájlok mozgatása és felülÃrása létezés esetén</target> +<source>Time stamp</source> +<target>IdÅ‘bélyeg</target> + +<source>Append a timestamp to each file name</source> +<target>IdÅ‘bélyeg hozzáadása a minden fájlnévhez</target> + <source>Folder</source> <target>Mappa</target> @@ -1130,7 +1133,7 @@ Megjegyzés: A fájlneveknek relatÃvnak kell lenniük az alap mappához viszony <target>Fájlok</target> <source>Items</source> -<target></target> +<target>Elemek</target> <source>Percentage</source> <target>Százalék</target> @@ -1159,9 +1162,6 @@ Megjegyzés: A fájlneveknek relatÃvnak kell lenniük az alap mappához viszony <source>Cannot write modification time of %x.</source> <target>Az utolsó módosÃtás dátumának a beállÃtása sikertelen a következÅ‘ fájlnál: %x.</target> -<source>Cannot find system function %x.</source> -<target>Nem található a következÅ‘ rendszerfunkció: %x.</target> - <source>Cannot read security context of %x.</source> <target>A következÅ‘ fájl biztonsági tulajdonságainak olvasása sikertelen: %x.</target> @@ -1177,8 +1177,8 @@ Megjegyzés: A fájlneveknek relatÃvnak kell lenniük az alap mappához viszony <source>Cannot create directory %x.</source> <target>A következÅ‘ mappa létrehozása sikertelen: %x.</target> -<source>Cannot copy symbolic link %x to %y.</source> -<target>%x symlink másolása a(z) %y symlinkbe sikertelen.</target> +<source>Cannot create symbolic link %x.</source> +<target></target> <source>Cannot copy file %x to %y.</source> <target>%x fájl másolása a(z) %y fájlba sikertelen.</target> @@ -1192,18 +1192,12 @@ Megjegyzés: A fájlneveknek relatÃvnak kell lenniük az alap mappához viszony <source>Cannot enumerate directory %x.</source> <target>A következÅ‘ mappa sorszámozása sikertelen: %x.</target> -<source>Detected endless directory recursion.</source> -<target>Végtelen mapparekurzió detektálva.</target> - <source>%x TB</source> <target>%x TB</target> <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1289,17 +1283,11 @@ Megjegyzés: A fájlneveknek relatÃvnak kell lenniük az alap mappához viszony <target>A következÅ‘ mappák nem találhatóak:</target> <source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> -<target></target> +<target>Figyelmen kÃvül hagyhatod ezt a hibát ezzel üresnek tekintve a mappákat. KésÅ‘bb a mappák létre lesznek hozva automatikusan a szinkronizálás közben.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>A mappák függenek egymástól! Legyen óvatos, amikor megadja a szinkronizálási szabályokat:</target> -<source>Start comparison</source> -<target>ÖsszehasonlÃtás megkezdése</target> - -<source>Calculating sync directions...</source> -<target>Szinkronizációs irányok számÃtása...</target> - <source>Conflict detected:</source> <target>Ãœtközés történt:</target> @@ -1312,18 +1300,21 @@ Megjegyzés: A fájlneveknek relatÃvnak kell lenniük az alap mappához viszony <source>Items differ in attributes only</source> <target>Az elemek csak attribútumaikban különböznek</target> -<source>Symbolic links %x have the same date but a different target.</source> -<target>A következÅ‘ symlinkeknek megegyezik a dátuma, de a célpontjuk különbözÅ‘: %x.</target> +<source>Resolving symbolic link %x</source> +<target></target> <source>Comparing content of files %x</source> <target>%x fájlok tartalmának összehasonlÃtása</target> -<source>Comparing files by content failed.</source> -<target>Sikertelen a fájlok összehasonlÃtása tartalom alapján.</target> - <source>Generating file list...</source> <target>Fájllista generálása...</target> +<source>Start comparison</source> +<target>ÖsszehasonlÃtás megkezdése</target> + +<source>Calculating sync directions...</source> +<target>Szinkronizációs irányok számÃtása...</target> + <source>Both sides are equal</source> <target>Mindkét oldal egyforma</target> @@ -1429,12 +1420,6 @@ Megjegyzés: A fájlneveknek relatÃvnak kell lenniük az alap mappához viszony <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>Egy olyan mappa kerül módosÃtásra, amely része több mappapárnak. Kérjük, ellenÅ‘rizze a szinkronizációs beállÃtásokat.</target> -<source>Left</source> -<target>Bal oldal</target> - -<source>Right</source> -<target>Jobb oldal</target> - <source>Synchronizing folder pair:</source> <target>Mappapár szinkronizálása:</target> diff --git a/BUILD/Languages/italian.lng b/BUILD/Languages/italian.lng index 0bd4d263..f0f8aa0d 100644 --- a/BUILD/Languages/italian.lng +++ b/BUILD/Languages/italian.lng @@ -115,14 +115,11 @@ <source>Cannot resolve symbolic link %x.</source> <target>Impossibile risolvere collegamento %x.</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> +<source>Cannot determine final path for %x.</source> +<target></target> -<source>%x GB</source> -<target>%x GB</target> +<source>Cannot find system function %x.</source> +<target>Impossibile trovare funzione di sistema %x.</target> <source> <pluralform>1 Byte</pluralform> @@ -133,6 +130,15 @@ <pluralform>%x Byte</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>Il file database %x non è compatibile.</target> @@ -169,9 +175,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>Attendi mentre la cartella è bloccata (%x)...</target> -<source>Creating file %x</source> -<target>Creazione file %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -181,15 +184,15 @@ <pluralform>%x sec</pluralform> </target> +<source>Creating file %x</source> +<target>Creazione file %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Errore nel parsing del file %x, riga %y, colonna %z.</target> <source>Scanning:</source> <target>Analisi di:</target> -<source>Encoding extended time information: %x</source> -<target>Codifica estesa informazioni orario: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -199,6 +202,9 @@ <pluralform>[%x threads]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>Codifica estesa informazioni orario: %x</target> + <source>/sec</source> <target>/sec</target> @@ -411,12 +417,12 @@ Il comando è attivato se: <source>Date:</source> <target>Data:</target> -<source>Action</source> -<target>Azioni</target> - <source>Category</source> <target>Categoria</target> +<source>Action</source> +<target>Azioni</target> + <source>Drag && drop</source> <target>Trascina</target> @@ -792,12 +798,6 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza! <source>&Find next</source> <target>&Trova successivo</target> -<source>Operation aborted!</source> -<target>Operazione abortita!</target> - -<source>Main bar</source> -<target>Barra principale</target> - <source>Folder pairs</source> <target>Coppia di cartelle</target> @@ -807,6 +807,9 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza! <source>Configuration</source> <target>Configurazione</target> +<source>Main bar</source> +<target>Barra principale</target> + <source>Filter files</source> <target>Filtro dei file</target> @@ -822,6 +825,30 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza! <source>Compare both sides</source> <target>Compara le due liste</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>1 cartella</pluralform> +<pluralform>%x cartelle</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>1 file</pluralform> +<pluralform>%x file</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target></target> + <source>Set direction:</source> <target>Imposta direzione:</target> @@ -936,48 +963,21 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza! <source>Set as default</source> <target>Imposta come predefinito</target> +<source>Operation aborted!</source> +<target>Operazione abortita!</target> + <source>All folders are in sync!</source> <target>Tutte le cartelle sono sincronizzate!</target> <source>Comma separated list</source> <target>Elenco elementi separati da virgola</target> -<source>Legend</source> -<target>Legenda</target> - <source>File list exported!</source> <target>Elenco file esportato!</target> <source>Searching for program updates...</source> <target>Ricerca di aggiornamenti al programma...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>1 cartella</pluralform> -<pluralform>%x cartelle</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>1 file</pluralform> -<pluralform>%x file</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x di 1 riga nella vista</pluralform> -<pluralform>%x di %y righe nella vista</pluralform> -</target> - <source>Ignore further errors</source> <target>Ignora i prossimi errori</target> @@ -1059,6 +1059,24 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza! <source>Filter</source> <target>Filtro</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Vuoi veramente spostare il seguente oggetto nel Cestino?</pluralform> +<pluralform>Vuoi veramente spostare i seguenti %x oggetti nel Cestino?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Vuoi veramente eliminare il seguente oggetto?</pluralform> +<pluralform>Vuoi veramente eliminare i seguenti %x oggetti?</pluralform> +</target> + <source>Direct</source> <target>Diretto</target> @@ -1086,39 +1104,21 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza! <source>Make hidden warnings and dialogs visible again?</source> <target>Vuoi visualizzare nuovamente i dialoghi e avvisi nascosti?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>Vuoi veramente spostare il seguente oggetto nel Cestino?</pluralform> -<pluralform>Vuoi veramente spostare i seguenti %x oggetti nel Cestino?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>Vuoi veramente eliminare il seguente oggetto?</pluralform> -<pluralform>Vuoi veramente eliminare i seguenti %x oggetti?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>Lascia come conflitti irrisolti</target> -<source>Time stamp</source> -<target>Data e ora</target> - -<source>Append a timestamp to each file name</source> -<target>Accoda data e ora ad ogni nome di file</target> - <source>Replace</source> <target>Sostituisci</target> <source>Move files and replace if existing</source> <target>Sposta file e sostituisci se esistenti</target> +<source>Time stamp</source> +<target>Data e ora</target> + +<source>Append a timestamp to each file name</source> +<target>Accoda data e ora ad ogni nome di file</target> + <source>Folder</source> <target>Cartella</target> @@ -1161,9 +1161,6 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza! <source>Cannot write modification time of %x.</source> <target>Impossibile scrivere data e ora di modifica di %x.</target> -<source>Cannot find system function %x.</source> -<target>Impossibile trovare funzione di sistema %x.</target> - <source>Cannot read security context of %x.</source> <target>Impossibile leggere il contesto di protezione di %x.</target> @@ -1179,8 +1176,8 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza! <source>Cannot create directory %x.</source> <target>Impossibile creare cartella %x.</target> -<source>Cannot copy symbolic link %x to %y.</source> -<target>Impossibile copiare collegamento %x su %y.</target> +<source>Cannot create symbolic link %x.</source> +<target></target> <source>Cannot copy file %x to %y.</source> <target>Impossibile copiare file %x in %y.</target> @@ -1203,9 +1200,6 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza! <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1296,12 +1290,6 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza! <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Le cartelle sono dipendenti! Fai attenzione quando configuri le regole di sincronizzazione:</target> -<source>Start comparison</source> -<target>Inizia comparazione</target> - -<source>Calculating sync directions...</source> -<target>Calcolo della direzione di sincronizzazione...</target> - <source>Conflict detected:</source> <target>Rilevato conflitto:</target> @@ -1314,18 +1302,21 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza! <source>Items differ in attributes only</source> <target>Gli oggetti differiscono solo negli attributi</target> -<source>Symbolic links %x have the same date but a different target.</source> -<target>Il collegamento %x ha stessa data ma un diverso puntamento.</target> +<source>Resolving symbolic link %x</source> +<target></target> <source>Comparing content of files %x</source> <target>Comparazione contenuto del file %x</target> -<source>Comparing files by content failed.</source> -<target>Comparazione file in base al contenuto fallita.</target> - <source>Generating file list...</source> <target>Generazione elenco file...</target> +<source>Start comparison</source> +<target>Inizia comparazione</target> + +<source>Calculating sync directions...</source> +<target>Calcolo della direzione di sincronizzazione...</target> + <source>Both sides are equal</source> <target>Entrambi i lati sono uguali</target> @@ -1431,12 +1422,6 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza! <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>Verrà modificata una cartella che è parte di molteplici coppie di cartelle. Controlla le impostazioni di sincronizzazione.</target> -<source>Left</source> -<target>Sinistra</target> - -<source>Right</source> -<target>Destra</target> - <source>Synchronizing folder pair:</source> <target>Sincronizzazione della coppia di cartelle:</target> diff --git a/BUILD/Languages/japanese.lng b/BUILD/Languages/japanese.lng index 01b596e0..3b855ea5 100644 --- a/BUILD/Languages/japanese.lng +++ b/BUILD/Languages/japanese.lng @@ -115,6 +115,14 @@ <source>Cannot resolve symbolic link %x.</source> <target>シンボリックリンク %x を解決ã§ãã¾ã›ã‚“.</target> +<source> +<pluralform>1 Byte</pluralform> +<pluralform>%x Bytes</pluralform> +</source> +<target> +<pluralform>%x ãƒã‚¤ãƒˆ</pluralform> +</target> + <source>%x MB</source> <target>%x MB</target> @@ -124,14 +132,6 @@ <source>%x GB</source> <target>%x GB</target> -<source> -<pluralform>1 Byte</pluralform> -<pluralform>%x Bytes</pluralform> -</source> -<target> -<pluralform>%x ãƒã‚¤ãƒˆ</pluralform> -</target> - <source>Database file %x is incompatible.</source> <target>データベース %x ã¨ã¯äº’æ›æ€§ãŒã‚ã‚Šã¾ã›ã‚“.</target> @@ -168,9 +168,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>待機時間ä¸ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã¯ãƒãƒƒã‚¯ã•ã‚Œã¾ã™(%x)...</target> -<source>Creating file %x</source> -<target>ファイル %x を作æˆä¸</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -179,15 +176,15 @@ <pluralform>%x 秒.</pluralform> </target> +<source>Creating file %x</source> +<target>ファイル %x を作æˆä¸</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>ファイル %x ã®æ§‹æ–‡è§£æžã‚¨ãƒ©ãƒ¼, è¡Œ %y, 列 %z.</target> <source>Scanning:</source> <target>スã‚ャン:</target> -<source>Encoding extended time information: %x</source> -<target>æ‹¡å¼µã•ã‚ŒãŸæ™‚é–“æƒ…å ±ã®ã‚¨ãƒ³ã‚³ãƒ¼ãƒ‡ã‚£ãƒ³ã‚°: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -196,6 +193,9 @@ <pluralform>[%x スレッド]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>æ‹¡å¼µã•ã‚ŒãŸæ™‚é–“æƒ…å ±ã®ã‚¨ãƒ³ã‚³ãƒ¼ãƒ‡ã‚£ãƒ³ã‚°: %x</target> + <source>/sec</source> <target>/秒</target> @@ -819,6 +819,30 @@ Note: File names must be relative to base directories! <source>Compare both sides</source> <target>両方を比較</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>%x ディレクトリ</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>%x ファイル</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%y ã® %x è¡Œ(表示内)</pluralform> +</target> + <source>Set direction:</source> <target>æ–¹å‘ã®è¨å®š:</target> @@ -948,30 +972,6 @@ Note: File names must be relative to base directories! <source>Searching for program updates...</source> <target>アップデートを検索ã—ã¦ã„ã¾ã™...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>%x ディレクトリ</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>%x ファイル</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x / %y 行を表示</pluralform> -</target> - <source>Ignore further errors</source> <target>以é™ã®ã‚¨ãƒ©ãƒ¼ã‚’無視</target> @@ -1053,6 +1053,22 @@ Note: File names must be relative to base directories! <source>Filter</source> <target>フィルター</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>本当ã«ä»¥ä¸‹ã® %x 個ã®é …目をゴミ箱ã«ç§»å‹•ã—ã¾ã™ã‹</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>本当ã«ä»¥ä¸‹ã® %x 個ã®é …目を削除ã—ã¾ã™ã‹</pluralform> +</target> + <source>Direct</source> <target>ダイレクト</target> @@ -1080,22 +1096,6 @@ Note: File names must be relative to base directories! <source>Make hidden warnings and dialogs visible again?</source> <target>éžè¡¨ç¤ºã«ã—ãŸè¦å‘Šãƒ€ã‚¤ã‚¢ãƒã‚°ã‚’å†ã³è¡¨ç¤ºã—ã¾ã™ã‹?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>本当ã«ä»¥ä¸‹ã® %x 個ã®é …目をゴミ箱ã«ç§»å‹•ã—ã¾ã™ã‹</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>本当ã«ä»¥ä¸‹ã® %x 個ã®é …目を削除ã—ã¾ã™ã‹</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>未解決ã®ç«¶åˆã¯ãã®ã¾ã¾æ®‹ã™</target> @@ -1123,6 +1123,9 @@ Note: File names must be relative to base directories! <source>Files</source> <target>ファイル</target> +<source>Items</source> +<target>é …ç›®</target> + <source>Percentage</source> <target>パーセント</target> @@ -1192,9 +1195,6 @@ Note: File names must be relative to base directories! <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1276,8 +1276,8 @@ Note: File names must be relative to base directories! <source>Cannot find the following folders:</source> <target>以下ã®ãƒ•ã‚©ãƒ«ãƒ€ãŒã¿ã¤ã‹ã‚Šã¾ã›ã‚“:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>å„フォルダを空ã¨ã¿ãªã™å ´åˆã¯ã€ã“ã®ã‚¨ãƒ©ãƒ¼ã¯ç„¡è¦–ã§ãã¾ã™</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>å„フォルダを空ã¨ã¿ãªã™å ´åˆã¯ã“ã®ã‚¨ãƒ©ãƒ¼ã‚’無視ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ã“ã®ãƒ•ã‚©ãƒ«ãƒ€ã¯ã€åŒæœŸå‡¦ç†ã‚’実行ã™ã‚‹éš›è‡ªå‹•çš„ã«ä½œæˆã•ã‚Œã‚‹ã‚‚ã®ã§ã™ã€‚</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>ディレクトリãŒä¾å˜é–¢ä¿‚ã«ã‚ã‚Šã¾ã™! åŒæœŸè¦å‰‡ã®è¨å®šæ™‚ã«ã¯æ³¨æ„ã—ã¦ãã ã•ã„:</target> @@ -1313,7 +1313,7 @@ Note: File names must be relative to base directories! <target>ファイル一覧を作æˆä¸...</target> <source>Both sides are equal</source> -<target>両å´ã¨ã‚‚ç‰ã—ã</target> +<target>両å´ã¨ã‚‚ç‰ã—ã„</target> <source>Copy new item to left</source> <target>æ–°ã—ã„é …ç›®ã‚’å·¦ã«ã‚³ãƒ”ー</target> diff --git a/BUILD/Languages/korean.lng b/BUILD/Languages/korean.lng index 07cc454e..e47f241d 100644 --- a/BUILD/Languages/korean.lng +++ b/BUILD/Languages/korean.lng @@ -115,6 +115,14 @@ <source>Cannot resolve symbolic link %x.</source> <target>ì‹¬ë³¼ë¦ ë§í¬ %x ì„(를) í•´ê²°í• ìˆ˜ 없습니다.</target> +<source> +<pluralform>1 Byte</pluralform> +<pluralform>%x Bytes</pluralform> +</source> +<target> +<pluralform>%x ë°”ì´íŠ¸</pluralform> +</target> + <source>%x MB</source> <target>%x MB</target> @@ -124,14 +132,6 @@ <source>%x GB</source> <target>%x GB</target> -<source> -<pluralform>1 Byte</pluralform> -<pluralform>%x Bytes</pluralform> -</source> -<target> -<pluralform>%x ë°”ì´íŠ¸</pluralform> -</target> - <source>Database file %x is incompatible.</source> <target>ë°ì´í„°ë² ì´ìŠ¤ íŒŒì¼ %x ì€(는) 호환 불가능합니다.</target> @@ -168,9 +168,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>ë””ë ‰í† ë¦¬ ìž ê¸ˆ 대기 중 (%x)...</target> -<source>Creating file %x</source> -<target>íŒŒì¼ %x ìƒì„± 중</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -179,15 +176,15 @@ <pluralform>%xì´ˆ</pluralform> </target> +<source>Creating file %x</source> +<target>íŒŒì¼ %x ìƒì„± 중</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>ë¶„ì„ ì˜¤ë¥˜ - 파ì¼: %x; í–‰: %y; ì—´: %z</target> <source>Scanning:</source> <target>스캔 :</target> -<source>Encoding extended time information: %x</source> -<target>ì¸ì½”딩 확장 시간 ì •ë³´ : %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -196,6 +193,9 @@ <pluralform>[%x ìŠ¤ë ˆë“œ]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>ì¸ì½”딩 확장 시간 ì •ë³´ : %x</target> + <source>/sec</source> <target>/ì´ˆ</target> @@ -819,6 +819,30 @@ Note: File names must be relative to base directories! <source>Compare both sides</source> <target>양측 비êµ</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>%xê°œ ë””ë ‰í† ë¦¬</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>%xê°œ 파ì¼</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>ë³´ê¸°ì— ë‚˜íƒ€ë‚œ %x í–‰ì˜ %y</pluralform> +</target> + <source>Set direction:</source> <target>ë°©í–¥ ì„¤ì • :</target> @@ -948,30 +972,6 @@ Note: File names must be relative to base directories! <source>Searching for program updates...</source> <target>프로그램 ì—…ë°ì´íŠ¸ 검색 중...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>%xê°œ ë””ë ‰í† ë¦¬</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>%xê°œ 파ì¼</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>ë³´ê¸°ì— ë‚˜íƒ€ë‚œ %yê°œ í–‰ì˜ %xê°œ 대ìƒ</pluralform> -</target> - <source>Ignore further errors</source> <target>ë” ì´ìƒì˜ 오류는 무시</target> @@ -1053,6 +1053,22 @@ Note: File names must be relative to base directories! <source>Filter</source> <target>í•„í„°</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>ì •ë§ë¡œ ë‹¤ìŒ %x í•ëª©(들)ì„ íœ´ì§€í†µìœ¼ë¡œ ì´ë™í•˜ì‹œê² 습니까?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>ì •ë§ë¡œ ë‹¤ìŒ %x í•ëª©(들)ì„ ì‚ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?</pluralform> +</target> + <source>Direct</source> <target>다ì´ë ‰íŠ¸</target> @@ -1080,22 +1096,6 @@ Note: File names must be relative to base directories! <source>Make hidden warnings and dialogs visible again?</source> <target>숨겨진 ê²½ê³ ì°½ ë˜ëŠ” 대화 ì°½ì„ ë‹¤ì‹œ ë³´ì´ê²Œ í•˜ì‹œê² ìŠµë‹ˆê¹Œ?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>ì •ë§ë¡œ ë‹¤ìŒ %x í•ëª©(들)ì„ íœ´ì§€í†µìœ¼ë¡œ ì´ë™í•˜ì‹œê² 습니까?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>ì •ë§ë¡œ ë‹¤ìŒ %x í•ëª©(들)ì„ ì‚ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>미해결 충ëŒë¡œ 놔ë‘기</target> @@ -1123,6 +1123,9 @@ Note: File names must be relative to base directories! <source>Files</source> <target>파ì¼</target> +<source>Items</source> +<target>í•ëª©</target> + <source>Percentage</source> <target>백분율(%)</target> @@ -1192,9 +1195,6 @@ Note: File names must be relative to base directories! <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1276,8 +1276,8 @@ Note: File names must be relative to base directories! <source>Cannot find the following folders:</source> <target>ë‹¤ìŒ í´ë”를 ì°¾ì„ ìˆ˜ 없습니다 :</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>ì´ ì˜¤ë¥˜ë¥¼ ë¬´ì‹œí•˜ê³ ê° í´ë”를 비어 있는 ìƒíƒœë¡œ ê°„ì£¼í•´ë„ ë¬´ë°©í•©ë‹ˆë‹¤.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>ê° í´ë”를 비어있는 ìƒíƒœë¡œ ê°„ì£¼í• ê²½ìš° ì´ ì˜¤ë¥˜ëŠ” 무시ë 수 있습니다. í´ë”ë“¤ì€ ë™ê¸°í™”ê°€ 진행ë˜ëŠ” ë™ì•ˆ ìžë™ ìƒì„±ë©ë‹ˆë‹¤.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>ë””ë ‰í† ë¦¬ê°€ ì˜ì¡´ ê´€ê³„ì— ìžˆìŠµë‹ˆë‹¤. ë™ê¸°í™” 규칙 ì„¤ì •ì‹œ 주ì˜í•˜ì‹ì‹œì˜¤.</target> diff --git a/BUILD/Languages/lithuanian.lng b/BUILD/Languages/lithuanian.lng index c652bf6f..6c4ca60a 100644 --- a/BUILD/Languages/lithuanian.lng +++ b/BUILD/Languages/lithuanian.lng @@ -113,16 +113,13 @@ <target>Klaidos kodas %x:</target> <source>Cannot resolve symbolic link %x.</source> -<target>Nepavyksta iÅ¡sprÄ™sti simbolinÄ—s nuorodos.</target> +<target></target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> +<source>Cannot determine final path for %x.</source> +<target></target> -<source>%x GB</source> -<target>%x GB</target> +<source>Cannot find system function %x.</source> +<target>Nepavyksta rasti sistemos funkcijos %x.</target> <source> <pluralform>1 Byte</pluralform> @@ -135,6 +132,15 @@ <pluralform>%x Baitas</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>Duomenų bazÄ— %x yra netinkama.</target> @@ -163,7 +169,7 @@ <target>Atsirado iÅ¡imtis!</target> <source>Cannot read file attributes of %x.</source> -<target>Nepavyksta nuskaityti failo atributų</target> +<target></target> <source>Cannot get process information.</source> <target>Nepavyksta gauti eigos informacijos.</target> @@ -171,9 +177,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>Laikiama kol katalogas bus užrakintas (%x)...</target> -<source>Creating file %x</source> -<target>Kuriamas failas %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -185,15 +188,15 @@ <pluralform>%x sek</pluralform> </target> +<source>Creating file %x</source> +<target>Kuriamas failas %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Klaida trinant failÄ… %x, eilÄ— %y, stulpelis %z.</target> <source>Scanning:</source> <target>Skenuojama:</target> -<source>Encoding extended time information: %x</source> -<target>Koduojama iÅ¡plÄ—stinÄ— laiko informacija: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -205,6 +208,9 @@ <pluralform>[%x GrÄ—smÄ—]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>Koduojama iÅ¡plÄ—stinÄ— laiko informacija: %x</target> + <source>/sec</source> <target>/sek.</target> @@ -417,12 +423,12 @@ Komanda inicijuojama jei: <source>Date:</source> <target>Data:</target> -<source>Action</source> -<target>Veiksmas</target> - <source>Category</source> <target>Kategorija</target> +<source>Action</source> +<target>Veiksmas</target> + <source>Drag && drop</source> <target>Vilkti ir numesti</target> @@ -798,12 +804,6 @@ Pastaba: Failų pavadinimai privalo atitikti bazinius katalogus! <source>&Find next</source> <target>&Rasti kitÄ…</target> -<source>Operation aborted!</source> -<target>Operacija nutraukta!</target> - -<source>Main bar</source> -<target>PagrindinÄ— juosta</target> - <source>Folder pairs</source> <target>Aplankų poros</target> @@ -813,6 +813,9 @@ Pastaba: Failų pavadinimai privalo atitikti bazinius katalogus! <source>Configuration</source> <target>Nustatymai</target> +<source>Main bar</source> +<target>PagrindinÄ— juosta</target> + <source>Filter files</source> <target>Filtruoti failus</target> @@ -828,6 +831,34 @@ Pastaba: Failų pavadinimai privalo atitikti bazinius katalogus! <source>Compare both sides</source> <target>Sulyginti abi puses</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>1 katalogas</pluralform> +<pluralform>%x katalogai</pluralform> +<pluralform>%x katalogų</pluralform> +<pluralform>%x katalogas</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>1 failas</pluralform> +<pluralform>%x failai</pluralform> +<pluralform>%x failų</pluralform> +<pluralform>%x failas</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target></target> + <source>Set direction:</source> <target>Nustatyti kryptį:</target> @@ -942,54 +973,21 @@ Pastaba: Failų pavadinimai privalo atitikti bazinius katalogus! <source>Set as default</source> <target>Nustatyti kaip numatytÄ…</target> +<source>Operation aborted!</source> +<target>Operacija nutraukta!</target> + <source>All folders are in sync!</source> <target>Visi aplankai susinchronizuoti!</target> <source>Comma separated list</source> <target>Kableliais atksirtas sÄ…raÅ¡as</target> -<source>Legend</source> -<target>Legenda</target> - <source>File list exported!</source> <target>Failų sÄ…raÅ¡as eksportuotas!</target> <source>Searching for program updates...</source> <target>IeÅ¡koma programos atnaujinimų...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>1 katalogas</pluralform> -<pluralform>%x katalogai</pluralform> -<pluralform>%x katalogų</pluralform> -<pluralform>%x katalogas</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>1 failas</pluralform> -<pluralform>%x failai</pluralform> -<pluralform>%x failų</pluralform> -<pluralform>%x failas</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x iÅ¡ 1 eilÄ—s rodmenyje</pluralform> -<pluralform>%x iÅ¡ %y eilių rodmenyje</pluralform> -<pluralform>%x iÅ¡ %y eilių rodmenyje</pluralform> -<pluralform>%x iÅ¡ %y eilÄ—s rodmenyje</pluralform> -</target> - <source>Ignore further errors</source> <target>Ignoruoti tolimesnes klaidas</target> @@ -1071,6 +1069,28 @@ Pastaba: Failų pavadinimai privalo atitikti bazinius katalogus! <source>Filter</source> <target>Filtras</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Ar tikrai norite perkelti šį elementÄ… į Å¡iukÅ¡liadėžę?</pluralform> +<pluralform>Ar tikrai norite perkelti Å¡iuos %x elementus į Å¡iukÅ¡liadėžę?</pluralform> +<pluralform>Ar tikrai norite perkelti Å¡iuos %x elementų į Å¡iukÅ¡liadėžę?</pluralform> +<pluralform>Ar tikrai norite perkelti šį %x elementÄ… į Å¡iukÅ¡liadėžę?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Ar tikrai norite iÅ¡trinti šį elementÄ…?</pluralform> +<pluralform>Ar tikrai norite iÅ¡trinti Å¡iuos %x elementus?</pluralform> +<pluralform>Ar tikrai norite iÅ¡trinti Å¡iuos %x elementų?</pluralform> +<pluralform>Ar tikrai norite iÅ¡trinti šį %x elementÄ…?</pluralform> +</target> + <source>Direct</source> <target>Tiesiogiai</target> @@ -1098,43 +1118,21 @@ Pastaba: Failų pavadinimai privalo atitikti bazinius katalogus! <source>Make hidden warnings and dialogs visible again?</source> <target>Ar vÄ—l rodyti paslÄ—ptus perspÄ—jimus ir langus?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>Ar tikrai norite perkelti šį elementÄ… į Å¡iukÅ¡liadėžę?</pluralform> -<pluralform>Ar tikrai norite perkelti Å¡iuos %x elementus į Å¡iukÅ¡liadėžę?</pluralform> -<pluralform>Ar tikrai norite perkelti Å¡iuos %x elementų į Å¡iukÅ¡liadėžę?</pluralform> -<pluralform>Ar tikrai norite perkelti šį %x elementÄ… į Å¡iukÅ¡liadėžę?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>Ar tikrai norite iÅ¡trinti šį elementÄ…?</pluralform> -<pluralform>Ar tikrai norite iÅ¡trinti Å¡iuos %x elementus?</pluralform> -<pluralform>Ar tikrai norite iÅ¡trinti Å¡iuos %x elementų?</pluralform> -<pluralform>Ar tikrai norite iÅ¡trinti šį %x elementÄ…?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>Palikti kaip neiÅ¡prÄ™stÄ… konfliktÄ…</target> -<source>Time stamp</source> -<target>Laiko žymÄ—</target> - -<source>Append a timestamp to each file name</source> -<target>PridÄ—ti laiko žymÄ™ kiekvieno failo pavadinime</target> - <source>Replace</source> <target>Pakeisti</target> <source>Move files and replace if existing</source> <target>Perkelti failus ir pakeisti jei egzistuoja</target> +<source>Time stamp</source> +<target>Laiko žymÄ—</target> + +<source>Append a timestamp to each file name</source> +<target>PridÄ—ti laiko žymÄ™ kiekvieno failo pavadinime</target> + <source>Folder</source> <target>Aplankas</target> @@ -1147,6 +1145,9 @@ Pastaba: Failų pavadinimai privalo atitikti bazinius katalogus! <source>Files</source> <target>Failai</target> +<source>Items</source> +<target></target> + <source>Percentage</source> <target>Procentai</target> @@ -1174,9 +1175,6 @@ Pastaba: Failų pavadinimai privalo atitikti bazinius katalogus! <source>Cannot write modification time of %x.</source> <target>Nepavyksta Å¡raÅ¡yti pakeitimo datos %x.</target> -<source>Cannot find system function %x.</source> -<target>Nepavyksta rasti sistemos funkcijos %x.</target> - <source>Cannot read security context of %x.</source> <target>Nepavyksta perskaityti %x saugumo konteksto.</target> @@ -1192,8 +1190,8 @@ Pastaba: Failų pavadinimai privalo atitikti bazinius katalogus! <source>Cannot create directory %x.</source> <target>Nepavyksta sukurti katalogo %x.</target> -<source>Cannot copy symbolic link %x to %y.</source> -<target>Nepavyksta nukopijuoti simbolinÄ—s nuorodos %x į %y.</target> +<source>Cannot create symbolic link %x.</source> +<target></target> <source>Cannot copy file %x to %y.</source> <target>Nepavyksta nukopijuoti failo %x į %y.</target> @@ -1216,9 +1214,6 @@ Pastaba: Failų pavadinimai privalo atitikti bazinius katalogus! <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1309,18 +1304,12 @@ Pastaba: Failų pavadinimai privalo atitikti bazinius katalogus! <source>Cannot find the following folders:</source> <target>Nepavyksta rasti Å¡ių aplankų:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>Galite ignoruoti Å¡iÄ… klaidÄ…, kad kiekvienas aplankas bÅ«tų laikomas tuÅ¡Äiu.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target></target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Katalogai yra priklausomi! BÅ«kite atsargÅ«s nustatydami sinchronizavimo taisykles:</target> -<source>Start comparison</source> -<target>PradÄ—ti palyginimÄ…</target> - -<source>Calculating sync directions...</source> -<target>ApskaiÄiuojamos sinchrinizacijos kryptys...</target> - <source>Conflict detected:</source> <target>Aptiktas konfliktas:</target> @@ -1333,18 +1322,21 @@ Pastaba: Failų pavadinimai privalo atitikti bazinius katalogus! <source>Items differ in attributes only</source> <target>Elementai skiriasi tik atributais</target> -<source>Symbolic links %x have the same date but a different target.</source> -<target>SimbolinÄ—s nuorodos %x turi tokiÄ… paÄiÄ… datÄ… bet skirtingus tikslus</target> +<source>Resolving symbolic link %x</source> +<target></target> <source>Comparing content of files %x</source> <target>Sulyginamas failų turinys %x</target> -<source>Comparing files by content failed.</source> -<target>Nepavyko failų sulyginimas pagal turinį.</target> - <source>Generating file list...</source> <target>Generuojamas failų sÄ…raÅ¡as...</target> +<source>Start comparison</source> +<target>PradÄ—ti palyginimÄ…</target> + +<source>Calculating sync directions...</source> +<target>ApskaiÄiuojamos sinchrinizacijos kryptys...</target> + <source>Both sides are equal</source> <target>Abi pusÄ—s yra lygios</target> @@ -1450,12 +1442,6 @@ Pastaba: Failų pavadinimai privalo atitikti bazinius katalogus! <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>Aplankas, kuris yra dalis keletos aplankų porų, bus pakeistas. PraÅ¡ome peržiÅ«rÄ—ti sinchronizavimo nustatymus.</target> -<source>Left</source> -<target>KairÄ—</target> - -<source>Right</source> -<target>DeÅ¡inÄ—</target> - <source>Synchronizing folder pair:</source> <target>Sinchrinizuojama aplankų pora:</target> diff --git a/BUILD/Languages/norwegian.lng b/BUILD/Languages/norwegian.lng index c85b7f9f..d453b110 100644 --- a/BUILD/Languages/norwegian.lng +++ b/BUILD/Languages/norwegian.lng @@ -115,14 +115,11 @@ <source>Cannot resolve symbolic link %x.</source> <target>Kan ikke følge symbolsk lenke %x.</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> +<source>Cannot determine final path for %x.</source> +<target></target> -<source>%x GB</source> -<target>%x GB</target> +<source>Cannot find system function %x.</source> +<target>Kan ikke finne systemfunksjonen %x.</target> <source> <pluralform>1 Byte</pluralform> @@ -133,6 +130,15 @@ <pluralform>%x Bytes</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>Databasefil %x er inkompatibel.</target> @@ -169,9 +175,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>Venter mens mappe er lÃ¥st (%x)...</target> -<source>Creating file %x</source> -<target>Oppretter fil %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -181,15 +184,15 @@ <pluralform>%x sekunder</pluralform> </target> +<source>Creating file %x</source> +<target>Oppretter fil %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Feil ved analysering av fil %x, rekke %y, kolonne %z.</target> <source>Scanning:</source> <target>Skanner:</target> -<source>Encoding extended time information: %x</source> -<target>Koder utvided tidsinformasjon: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -199,6 +202,9 @@ <pluralform>[%x TrÃ¥der]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>Koder utvided tidsinformasjon: %x</target> + <source>/sec</source> <target>/sekund</target> @@ -411,12 +417,12 @@ Kommandoen utløses hvis: <source>Date:</source> <target>Dato:</target> -<source>Action</source> -<target>Handling</target> - <source>Category</source> <target>Kategori</target> +<source>Action</source> +<target>Handling</target> + <source>Drag && drop</source> <target>Dra && slipp</target> @@ -792,12 +798,6 @@ Merk: Filnavn mÃ¥ være relative til basismapper! <source>&Find next</source> <target>&Søk neste</target> -<source>Operation aborted!</source> -<target>Handling avbrutt!</target> - -<source>Main bar</source> -<target>Hovedverktøylinje</target> - <source>Folder pairs</source> <target>Mappepar</target> @@ -807,6 +807,9 @@ Merk: Filnavn mÃ¥ være relative til basismapper! <source>Configuration</source> <target>Innstilling</target> +<source>Main bar</source> +<target>Hovedverktøylinje</target> + <source>Filter files</source> <target>Filtrer filer</target> @@ -822,6 +825,30 @@ Merk: Filnavn mÃ¥ være relative til basismapper! <source>Compare both sides</source> <target>Sammenlign begge sider</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>1 mappe</pluralform> +<pluralform>%x mapper</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>1 fil</pluralform> +<pluralform>%x filer</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target></target> + <source>Set direction:</source> <target>Still inn retningen:</target> @@ -936,48 +963,21 @@ Merk: Filnavn mÃ¥ være relative til basismapper! <source>Set as default</source> <target>Set som standard</target> +<source>Operation aborted!</source> +<target>Handling avbrutt!</target> + <source>All folders are in sync!</source> <target>Alle mapper er synkroniserte!</target> <source>Comma separated list</source> <target>Komma-separert liste</target> -<source>Legend</source> -<target>Forklaring</target> - <source>File list exported!</source> <target>Filliste eksportert!</target> <source>Searching for program updates...</source> <target></target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>1 mappe</pluralform> -<pluralform>%x mapper</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>1 fil</pluralform> -<pluralform>%x filer</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x av 1 rekke</pluralform> -<pluralform>%x av %y rekker</pluralform> -</target> - <source>Ignore further errors</source> <target>Ignorer ytterligere feil</target> @@ -1059,6 +1059,18 @@ Merk: Filnavn mÃ¥ være relative til basismapper! <source>Filter</source> <target>Filter</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target></target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target></target> + <source>Direct</source> <target>Direkte</target> @@ -1086,33 +1098,21 @@ Merk: Filnavn mÃ¥ være relative til basismapper! <source>Make hidden warnings and dialogs visible again?</source> <target>Gjør skjylte advarsler og dialoger synlige igjen?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target></target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target></target> - <source>Leave as unresolved conflict</source> <target>Etterlat som uløste konflikter</target> -<source>Time stamp</source> -<target></target> - -<source>Append a timestamp to each file name</source> -<target>Legg til et tidsstempel til hvert filnavn</target> - <source>Replace</source> <target>Erstatt</target> <source>Move files and replace if existing</source> <target>Flytt filer og erstatt hvis eksisterer</target> +<source>Time stamp</source> +<target></target> + +<source>Append a timestamp to each file name</source> +<target>Legg til et tidsstempel til hvert filnavn</target> + <source>Folder</source> <target>Mappe</target> @@ -1125,6 +1125,9 @@ Merk: Filnavn mÃ¥ være relative til basismapper! <source>Files</source> <target>Filer</target> +<source>Items</source> +<target></target> + <source>Percentage</source> <target>Prosent</target> @@ -1152,9 +1155,6 @@ Merk: Filnavn mÃ¥ være relative til basismapper! <source>Cannot write modification time of %x.</source> <target>Kan ikke skrive endringstid til %x.</target> -<source>Cannot find system function %x.</source> -<target>Kan ikke finne systemfunksjonen %x.</target> - <source>Cannot read security context of %x.</source> <target>Kan ikke lese sikkerhetskontekst til %x.</target> @@ -1170,8 +1170,8 @@ Merk: Filnavn mÃ¥ være relative til basismapper! <source>Cannot create directory %x.</source> <target>Kan ikke opprette mappen %x.</target> -<source>Cannot copy symbolic link %x to %y.</source> -<target>Kan ikke kopiere symbolsk lenke %x til %y.</target> +<source>Cannot create symbolic link %x.</source> +<target></target> <source>Cannot copy file %x to %y.</source> <target>Kan ikke kopiere filen %x til %y.</target> @@ -1194,9 +1194,6 @@ Merk: Filnavn mÃ¥ være relative til basismapper! <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1281,18 +1278,12 @@ Merk: Filnavn mÃ¥ være relative til basismapper! <source>Cannot find the following folders:</source> <target>Kan ikke finne følgende mapper:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>Denne feilen kan ignoreres dersom mappen er tom.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target></target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Mapper er avhengige av hverandre! Vær forsiktig nÃ¥r du setter opp synkroniseringsregler:</target> -<source>Start comparison</source> -<target>Start sammenligning</target> - -<source>Calculating sync directions...</source> -<target></target> - <source>Conflict detected:</source> <target>Konflikt oppdaget:</target> @@ -1305,18 +1296,21 @@ Merk: Filnavn mÃ¥ være relative til basismapper! <source>Items differ in attributes only</source> <target>Elementer har kun forskjellige attributter</target> -<source>Symbolic links %x have the same date but a different target.</source> -<target>Symbolske lenker %x har den samme datoen, men forskjellige mÃ¥l.</target> +<source>Resolving symbolic link %x</source> +<target></target> <source>Comparing content of files %x</source> <target>Sammenligner innhold til filer %x</target> -<source>Comparing files by content failed.</source> -<target>Sammenligning av filer etter innhold feilet.</target> - <source>Generating file list...</source> <target>Lager filliste...</target> +<source>Start comparison</source> +<target>Start sammenligning</target> + +<source>Calculating sync directions...</source> +<target></target> + <source>Both sides are equal</source> <target>Begge sider er like</target> @@ -1422,12 +1416,6 @@ Merk: Filnavn mÃ¥ være relative til basismapper! <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>En mappe vil endres som er en del av flere mappepar. Se over innstillingene for synkronisering.</target> -<source>Left</source> -<target>Venstre</target> - -<source>Right</source> -<target>Høyre</target> - <source>Synchronizing folder pair:</source> <target>Synkroniserer mappepar:</target> diff --git a/BUILD/Languages/polish.lng b/BUILD/Languages/polish.lng index d39124b3..0906761a 100644 --- a/BUILD/Languages/polish.lng +++ b/BUILD/Languages/polish.lng @@ -115,15 +115,6 @@ <source>Cannot resolve symbolic link %x.</source> <target>Nie można okreÅ›lić poÅ‚ożenia dowiÄ…zania symbolicznego %x.</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source> <pluralform>1 Byte</pluralform> <pluralform>%x Bytes</pluralform> @@ -134,6 +125,15 @@ <pluralform>%x Bajtów</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>Plik bazy danych %x nie jest kompatybilny.</target> @@ -170,9 +170,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>Blokada katalogu (%x), oczekiwanie...</target> -<source>Creating file %x</source> -<target>Tworzenie pliku %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -183,15 +180,15 @@ <pluralform>%x sekund</pluralform> </target> +<source>Creating file %x</source> +<target>Tworzenie pliku %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>BÅ‚Ä…d podczas parsowania pliku %x, rzÄ…d %y, kolumna %z.</target> <source>Scanning:</source> <target>Skanowanie:</target> -<source>Encoding extended time information: %x</source> -<target>Odkodowywanie rozszerzonych informacji o czasie: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -202,6 +199,9 @@ <pluralform>[%x WÄ…tków]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>Odkodowywanie rozszerzonych informacji o czasie: %x</target> + <source>/sec</source> <target>/sekundÄ™</target> @@ -795,9 +795,6 @@ Uwaga: Nazwy plików muszÄ… być podane jako relatywne wzglÄ™dem katalogu bazowe <source>&Find next</source> <target>&Znajdź nastÄ™pny</target> -<source>Operation aborted!</source> -<target>Operacja przerwana!</target> - <source>Main bar</source> <target>Główny pasek</target> @@ -825,6 +822,36 @@ Uwaga: Nazwy plików muszÄ… być podane jako relatywne wzglÄ™dem katalogu bazowe <source>Compare both sides</source> <target>Porównaj foldery</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>1 katalog</pluralform> +<pluralform>%x katalogi</pluralform> +<pluralform>%x katalogów</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>1 plik</pluralform> +<pluralform>%x pliki</pluralform> +<pluralform>%x plików</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>Widok %y z 1 lini</pluralform> +<pluralform>Widok %y z %x lini</pluralform> +<pluralform>Widok %y z %x lini</pluralform> +</target> + <source>Set direction:</source> <target>Kierunek synchronizacji:</target> @@ -939,6 +966,9 @@ Uwaga: Nazwy plików muszÄ… być podane jako relatywne wzglÄ™dem katalogu bazowe <source>Set as default</source> <target>Zapisz jako domyÅ›lne</target> +<source>Operation aborted!</source> +<target>Operacja przerwana!</target> + <source>All folders are in sync!</source> <target>Wszystkie katalogi sÄ… zsynchronizowane!</target> @@ -954,36 +984,6 @@ Uwaga: Nazwy plików muszÄ… być podane jako relatywne wzglÄ™dem katalogu bazowe <source>Searching for program updates...</source> <target>Wyszukiwanie aktualizacji...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>1 katalog</pluralform> -<pluralform>%x katalogi</pluralform> -<pluralform>%x katalogów</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>1 plik</pluralform> -<pluralform>%x pliki</pluralform> -<pluralform>%x plików</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x z 1 rzÄ™du w widoku</pluralform> -<pluralform>%x z %y rzÄ™dów w widoku</pluralform> -<pluralform>%x z %y rzÄ™dów w widoku</pluralform> -</target> - <source>Ignore further errors</source> <target>Ignoruj kolejne bÅ‚Ä™dy</target> @@ -1065,6 +1065,26 @@ Uwaga: Nazwy plików muszÄ… być podane jako relatywne wzglÄ™dem katalogu bazowe <source>Filter</source> <target>Filtr</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Czy na pewno chcesz przenieść do kosza nastÄ™pujÄ…cy element?</pluralform> +<pluralform>Czy na pewno chcesz przenieść do kosza %x nastÄ™pujÄ…ce elementy?</pluralform> +<pluralform>Czy na pewno chcesz przenieść do kosza %x nastÄ™pujÄ…cych elementów?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Czy na pewno chcesz usunąć nastÄ™pujÄ…cy element?</pluralform> +<pluralform>Czy na pewno chcesz usunąć %x nastÄ™pujÄ…ce elementy?</pluralform> +<pluralform>Czy na pewno chcesz usunąć %x nastÄ™pujÄ…cych elementów?</pluralform> +</target> + <source>Direct</source> <target>BezpoÅ›rednio</target> @@ -1092,26 +1112,6 @@ Uwaga: Nazwy plików muszÄ… być podane jako relatywne wzglÄ™dem katalogu bazowe <source>Make hidden warnings and dialogs visible again?</source> <target>Przywrócić ukryte powiadomienia i dialogi?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>Czy na pewno chcesz przenieść do kosza nastÄ™pujÄ…cy element?</pluralform> -<pluralform>Czy na pewno chcesz przenieść do kosza %x nastÄ™pujÄ…ce elementy?</pluralform> -<pluralform>Czy na pewno chcesz przenieść do kosza %x nastÄ™pujÄ…cych elementów?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>Czy na pewno chcesz usunąć nastÄ™pujÄ…cy element?</pluralform> -<pluralform>Czy na pewno chcesz usunąć %x nastÄ™pujÄ…ce elementy?</pluralform> -<pluralform>Czy na pewno chcesz usunąć %x nastÄ™pujÄ…cych elementów?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>Zostaw jako nierozwiÄ…zany konflikt</target> @@ -1140,7 +1140,7 @@ Uwaga: Nazwy plików muszÄ… być podane jako relatywne wzglÄ™dem katalogu bazowe <target>Pliki</target> <source>Items</source> -<target></target> +<target>Elementy</target> <source>Percentage</source> <target>Procentowo</target> @@ -1211,9 +1211,6 @@ Uwaga: Nazwy plików muszÄ… być podane jako relatywne wzglÄ™dem katalogu bazowe <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1301,8 +1298,8 @@ Uwaga: Nazwy plików muszÄ… być podane jako relatywne wzglÄ™dem katalogu bazowe <source>Cannot find the following folders:</source> <target>Nie można znaleźć nastÄ™pujÄ…cych katalogów:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>Możesz zignorować ten bÅ‚Ä…d aby uznajÄ…c katalogi jako puste.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>Możesz zignorować ten bÅ‚Ä…d aby uznajÄ…c katalogi jako puste. Katalog zostanie utworzony automatycznie podczas synchronizacji.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Katalogi sÄ… zależne! PamiÄ™taj o tym podczas ustawiania zasad synchronizacji:</target> @@ -1442,12 +1439,6 @@ Uwaga: Nazwy plików muszÄ… być podane jako relatywne wzglÄ™dem katalogu bazowe <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>Katalog zostanie zmodyfikowany w zwiÄ…zku z synchronizacjÄ… wielu katalogów. Zweryfikuj ustawienia synchronizacji.</target> -<source>Left</source> -<target>Lewy</target> - -<source>Right</source> -<target>Prawy</target> - <source>Synchronizing folder pair:</source> <target>Synchronizacja katalgów:</target> diff --git a/BUILD/Languages/portuguese.lng b/BUILD/Languages/portuguese.lng index 8fd451a1..c524a9db 100644 --- a/BUILD/Languages/portuguese.lng +++ b/BUILD/Languages/portuguese.lng @@ -115,15 +115,6 @@ <source>Cannot resolve symbolic link %x.</source> <target>Não é possÃvel resolver o link simbólico %x.</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source> <pluralform>1 Byte</pluralform> <pluralform>%x Bytes</pluralform> @@ -133,6 +124,15 @@ <pluralform>%x Bytes</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>Base de dados %x não é compatÃvel.</target> @@ -169,9 +169,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>Aguardar enquanto o directório é bloqueado (%x)...</target> -<source>Creating file %x</source> -<target>Criar ficheiro %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -181,15 +178,15 @@ <pluralform>%x segs</pluralform> </target> +<source>Creating file %x</source> +<target>Criar ficheiro %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Erro ao analisar ficheiro %x, linha %y, coluna %z.</target> <source>Scanning:</source> <target>A pesquisar:</target> -<source>Encoding extended time information: %x</source> -<target>A codificar dados temporais extendidos: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -199,6 +196,9 @@ <pluralform>[%x Threads]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>A codificar dados temporais extendidos: %x</target> + <source>/sec</source> <target>/seg</target> @@ -411,12 +411,12 @@ O comando é executado se: <source>Date:</source> <target>Data:</target> -<source>Action</source> -<target>Ação</target> - <source>Category</source> <target>Categoria</target> +<source>Action</source> +<target>Ação</target> + <source>Drag && drop</source> <target>Arrastar && Largar</target> @@ -791,12 +791,6 @@ Nota: Nome dos ficheiros tem que ser relativo aos diretórios base! <source>&Find next</source> <target>&Procurar seguinte</target> -<source>Operation aborted!</source> -<target>Operação abortada!</target> - -<source>Main bar</source> -<target>Barra principal</target> - <source>Folder pairs</source> <target>Par de directório</target> @@ -806,6 +800,9 @@ Nota: Nome dos ficheiros tem que ser relativo aos diretórios base! <source>Configuration</source> <target>Configuração</target> +<source>Main bar</source> +<target>Barra principal</target> + <source>Filter files</source> <target>Filtrar ficheiros</target> @@ -821,6 +818,33 @@ Nota: Nome dos ficheiros tem que ser relativo aos diretórios base! <source>Compare both sides</source> <target>Comparar listas</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>1 directório</pluralform> +<pluralform>%x directórios</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>1 ficheiro</pluralform> +<pluralform>%x ficheiros</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%y de 1 linha em vista</pluralform> +<pluralform>%y de %x linhas em vista</pluralform> +</target> + <source>Set direction:</source> <target>Escolher direcção:</target> @@ -935,6 +959,9 @@ Nota: Nome dos ficheiros tem que ser relativo aos diretórios base! <source>Set as default</source> <target>Definir como padrão</target> +<source>Operation aborted!</source> +<target>Operação abortada!</target> + <source>All folders are in sync!</source> <target>Todas as pastas estão sincronizadas!</target> @@ -950,33 +977,6 @@ Nota: Nome dos ficheiros tem que ser relativo aos diretórios base! <source>Searching for program updates...</source> <target>A procurar actualizações do programa...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>1 directório</pluralform> -<pluralform>%x directórios</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>1 ficheiro</pluralform> -<pluralform>%x ficheiros</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x de 1 linha em vista</pluralform> -<pluralform>%x de %y linhas em vista</pluralform> -</target> - <source>Ignore further errors</source> <target>Ignorar próximos erros</target> @@ -1058,6 +1058,24 @@ Nota: Nome dos ficheiros tem que ser relativo aos diretórios base! <source>Filter</source> <target>Filtro</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Deseja mesmo mover o seguinte item para a Reciclagem?</pluralform> +<pluralform>Deseja mesmo mover os seguintes %x itens para a Reciclagem?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Deseja mesmo eliminar o seguinte item?</pluralform> +<pluralform>Deseja mesmo eliminar os seguintes %x itens?</pluralform> +</target> + <source>Direct</source> <target>Direto</target> @@ -1085,39 +1103,21 @@ Nota: Nome dos ficheiros tem que ser relativo aos diretórios base! <source>Make hidden warnings and dialogs visible again?</source> <target>Tornar visiveis os diálogos e avisos escondidos novamente?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>Deseja mesmo mover o seguinte item para a Reciclagem?</pluralform> -<pluralform>Deseja mesmo mover os seguintes %x itens para a Reciclagem?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>Deseja mesmo eliminar o seguinte item?</pluralform> -<pluralform>Deseja mesmo eliminar os seguintes %x itens?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>Deixar como conflito</target> -<source>Time stamp</source> -<target>Selo temporal</target> - -<source>Append a timestamp to each file name</source> -<target>Juntar selo temporal a cada nome de ficheiro</target> - <source>Replace</source> <target>Substituir</target> <source>Move files and replace if existing</source> <target>Mover ficheiros e substituir se existirem</target> +<source>Time stamp</source> +<target>Selo temporal</target> + +<source>Append a timestamp to each file name</source> +<target>Juntar selo temporal a cada nome de ficheiro</target> + <source>Folder</source> <target>Pasta</target> @@ -1130,6 +1130,9 @@ Nota: Nome dos ficheiros tem que ser relativo aos diretórios base! <source>Files</source> <target>Ficheiros</target> +<source>Items</source> +<target>Itens</target> + <source>Percentage</source> <target>Percentagem</target> @@ -1199,9 +1202,6 @@ Nota: Nome dos ficheiros tem que ser relativo aos diretórios base! <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1286,8 +1286,8 @@ Nota: Nome dos ficheiros tem que ser relativo aos diretórios base! <source>Cannot find the following folders:</source> <target>Não é possÃvel encontrar as seguintes pastas:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>Pode ignorar este erro para considerar cada pasta como vazia.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>Pode ignorar este erro para considerar cada pasta como vazia. As pastas necessárias serão criadas automaticamente durante a sincronização.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Directórios são dependentes! Cuidado ao definir as regras de sincronização:</target> @@ -1427,12 +1427,6 @@ Nota: Nome dos ficheiros tem que ser relativo aos diretórios base! <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>Uma pasta que faz parte de vários pares vai ser modificada. Por favor, reveja os parametros de sincronização.</target> -<source>Left</source> -<target>Esquerda</target> - -<source>Right</source> -<target>Direita</target> - <source>Synchronizing folder pair:</source> <target>Sincronizar o par de pastas:</target> diff --git a/BUILD/Languages/portuguese_br.lng b/BUILD/Languages/portuguese_br.lng index 8615e134..e952271b 100644 --- a/BUILD/Languages/portuguese_br.lng +++ b/BUILD/Languages/portuguese_br.lng @@ -4,7 +4,7 @@ <locale>pt_BR</locale> <flag file>brazil.png</flag file> <plural forms>2</plural forms> - <plural definition>n <= 1 ? 0 : 1</plural definition> + <plural definition>n == 1 ? 0 : 1</plural definition> </header> <source>Searching for folder %x...</source> @@ -115,6 +115,15 @@ <source>Cannot resolve symbolic link %x.</source> <target>Não foi possÃvel resolver o link simbólico %x.</target> +<source> +<pluralform>1 Byte</pluralform> +<pluralform>%x Bytes</pluralform> +</source> +<target> +<pluralform>1 Byte</pluralform> +<pluralform>%x Bytes</pluralform> +</target> + <source>%x MB</source> <target>%x MB</target> @@ -124,15 +133,6 @@ <source>%x GB</source> <target>%x GB</target> -<source> -<pluralform>1 Byte</pluralform> -<pluralform>%x Bytes</pluralform> -</source> -<target> -<pluralform>%x Byte</pluralform> -<pluralform>%x Bytes</pluralform> -</target> - <source>Database file %x is incompatible.</source> <target>O arquivo de banco de dados %x é incompatÃvel.</target> @@ -169,36 +169,36 @@ <source>Waiting while directory is locked (%x)...</source> <target>Esperando enquanto o diretório é bloqueado (%x)...</target> -<source>Creating file %x</source> -<target>Criando arquivo %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> </source> <target> -<pluralform>%x seg</pluralform> +<pluralform>1 seg</pluralform> <pluralform>%x segs</pluralform> </target> +<source>Creating file %x</source> +<target>Criando arquivo %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Erro analisando o arquivo %x, linha %y, coluna %z.</target> <source>Scanning:</source> <target>Pesquisando:</target> -<source>Encoding extended time information: %x</source> -<target>Codificando informações adicionais de tempo: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> </source> <target> -<pluralform>[%x Thread]</pluralform> +<pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>Codificando informações adicionais de tempo: %x</target> + <source>/sec</source> <target>/seg</target> @@ -792,9 +792,6 @@ Nota: Os nomes dos arquivos devem ser relativos aos diretórios base! <source>&Find next</source> <target>&Localizar próxima</target> -<source>Operation aborted!</source> -<target>Operação cancelada!</target> - <source>Main bar</source> <target>Barra principal</target> @@ -822,6 +819,33 @@ Nota: Os nomes dos arquivos devem ser relativos aos diretórios base! <source>Compare both sides</source> <target>Comparar os dois lados</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>1 diretório</pluralform> +<pluralform>%x diretórios</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>1 arquivo</pluralform> +<pluralform>%x arquivos</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%y de 1 linha</pluralform> +<pluralform>%y de %x linhas</pluralform> +</target> + <source>Set direction:</source> <target>Configurar direção</target> @@ -936,6 +960,9 @@ Nota: Os nomes dos arquivos devem ser relativos aos diretórios base! <source>Set as default</source> <target>Definir como padrão</target> +<source>Operation aborted!</source> +<target>Operação cancelada!</target> + <source>All folders are in sync!</source> <target>Todas as pastas estão sincronizadas!</target> @@ -951,33 +978,6 @@ Nota: Os nomes dos arquivos devem ser relativos aos diretórios base! <source>Searching for program updates...</source> <target>Procurando atualizações do programa...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>%x diretório</pluralform> -<pluralform>%x diretórios</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>%x arquivo</pluralform> -<pluralform>%x arquivos</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x de %y linha</pluralform> -<pluralform>%x de %y linhas</pluralform> -</target> - <source>Ignore further errors</source> <target>Ignorar erros subsequentes</target> @@ -1059,6 +1059,24 @@ Nota: Os nomes dos arquivos devem ser relativos aos diretórios base! <source>Filter</source> <target>Filtro</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Você realmente deseja mover o seguinte item para a Lixeira?</pluralform> +<pluralform>Você realmente deseja mover os seguintes %x itens para a Lixeira?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Você realmente deseja apagar o seguinte item?</pluralform> +<pluralform>Você realmente deseja apagar os seguintes %x itens?</pluralform> +</target> + <source>Direct</source> <target>Direto</target> @@ -1086,24 +1104,6 @@ Nota: Os nomes dos arquivos devem ser relativos aos diretórios base! <source>Make hidden warnings and dialogs visible again?</source> <target>Tornar avisar e diálogos ocultados visÃveis outra vez?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>Você realmente quer mover o seguinte item para a Lixeira?</pluralform> -<pluralform>Você realmente quer mover os seguintes %x itens para a Lixeira?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>Você realmente quer apagar o seguinte item?</pluralform> -<pluralform>Você realmente quer apagar os seguintes %x itens?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>Deixar como conflito não resolvido</target> @@ -1132,7 +1132,7 @@ Nota: Os nomes dos arquivos devem ser relativos aos diretórios base! <target>Arquivos</target> <source>Items</source> -<target></target> +<target>Itens</target> <source>Percentage</source> <target>Percentual</target> @@ -1203,15 +1203,12 @@ Nota: Os nomes dos arquivos devem ser relativos aos diretórios base! <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> </source> <target> -<pluralform>%x min</pluralform> +<pluralform>1 min</pluralform> <pluralform>%x min</pluralform> </target> @@ -1220,7 +1217,7 @@ Nota: Os nomes dos arquivos devem ser relativos aos diretórios base! <pluralform>%x hours</pluralform> </source> <target> -<pluralform>%x hora</pluralform> +<pluralform>1 hora</pluralform> <pluralform>%x horas</pluralform> </target> @@ -1229,7 +1226,7 @@ Nota: Os nomes dos arquivos devem ser relativos aos diretórios base! <pluralform>%x days</pluralform> </source> <target> -<pluralform>%x dia</pluralform> +<pluralform>1 dia</pluralform> <pluralform>%x dias</pluralform> </target> @@ -1290,8 +1287,8 @@ Nota: Os nomes dos arquivos devem ser relativos aos diretórios base! <source>Cannot find the following folders:</source> <target>Não foi possÃvel localizar as seguintes pastas:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>Você pode ignorar este erro para considerar cada pasta como vazia.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>Você pode ignorar este erro para considerar cada pasta como vazia. As pastas serão criadas automaticamentes durante a sincronização.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Os diretórios são dependentes! Cuidado ao definir as regras de sincronização:</target> @@ -1431,12 +1428,6 @@ Nota: Os nomes dos arquivos devem ser relativos aos diretórios base! <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>Uma pasta que é parte de múltiplos pares de pasta será modificada. Por favor, revise as configurações de sincronização.</target> -<source>Left</source> -<target>Esquerda</target> - -<source>Right</source> -<target>Direita</target> - <source>Synchronizing folder pair:</source> <target>Sincronizando par de pastas:</target> diff --git a/BUILD/Languages/romanian.lng b/BUILD/Languages/romanian.lng index e34b40d0..e34a4b9c 100644 --- a/BUILD/Languages/romanian.lng +++ b/BUILD/Languages/romanian.lng @@ -115,15 +115,6 @@ <source>Cannot resolve symbolic link %x.</source> <target>Nu pot rezolva legătura simbolică %x.</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source> <pluralform>1 Byte</pluralform> <pluralform>%x Bytes</pluralform> @@ -134,6 +125,15 @@ <pluralform>%x de BaiÈ›i</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>Fila cu baza de date %x este incompatibilă.</target> @@ -170,9 +170,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>AÈ™tept ca dosarul să fie zăvorît (%x)...</target> -<source>Creating file %x</source> -<target>Creez fila %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -183,15 +180,15 @@ <pluralform>%x de sec</pluralform> </target> +<source>Creating file %x</source> +<target>Creez fila %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Eroare la parsarea filei %x, rîndul %y, coloana %z.</target> <source>Scanning:</source> <target>Scanez:</target> -<source>Encoding extended time information: %x</source> -<target>Codez informaÈ›iile extinse despre timp: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -202,6 +199,9 @@ <pluralform>[%x de Fire]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>Codez informaÈ›iile extinse despre timp: %x</target> + <source>/sec</source> <target>/sec</target> @@ -795,9 +795,6 @@ Notă: Numele filelor trebuie să fie relative la dosarele bază (rădăcină)! <source>&Find next</source> <target>&GăseÈ™te Următorul</target> -<source>Operation aborted!</source> -<target>OperaÈ›ie abandonată!</target> - <source>Main bar</source> <target>Bara Principală</target> @@ -825,6 +822,36 @@ Notă: Numele filelor trebuie să fie relative la dosarele bază (rădăcină)! <source>Compare both sides</source> <target>Compară PărÈ›ile Stîngă È™i Dreaptă</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>1 dosar</pluralform> +<pluralform>%x dosare</pluralform> +<pluralform>%x de dosare</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>1 filă</pluralform> +<pluralform>%x file</pluralform> +<pluralform>%x de file</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%y din 1 rînd afiÈ™at</pluralform> +<pluralform>%y din %x rînduri afiÈ™ate</pluralform> +<pluralform>%y din %x de rînduri afiÈ™ate</pluralform> +</target> + <source>Set direction:</source> <target>Setează AcÈ›iunea ca în Icoana Alăturată:</target> @@ -939,6 +966,9 @@ Notă: Numele filelor trebuie să fie relative la dosarele bază (rădăcină)! <source>Set as default</source> <target>Setează ca implicit</target> +<source>Operation aborted!</source> +<target>OperaÈ›ie abandonată!</target> + <source>All folders are in sync!</source> <target>Toate dosarele sînt sincronizate!</target> @@ -954,36 +984,6 @@ Notă: Numele filelor trebuie să fie relative la dosarele bază (rădăcină)! <source>Searching for program updates...</source> <target>Caut actualizări ale programului...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>1 dosar</pluralform> -<pluralform>%x dosare</pluralform> -<pluralform>%x de dosare</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>1 filă</pluralform> -<pluralform>%x file</pluralform> -<pluralform>%x de file</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x din 1 rînduri afiÈ™ate</pluralform> -<pluralform>%x din %y rînduri afiÈ™ate</pluralform> -<pluralform>%x din %y de rînduri afiÈ™ate</pluralform> -</target> - <source>Ignore further errors</source> <target>Ignoră erorile ulterioare</target> @@ -1065,6 +1065,26 @@ Notă: Numele filelor trebuie să fie relative la dosarele bază (rădăcină)! <source>Filter</source> <target>Filtru</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Sigur vrei să muÈ›i în Reciclator elementul următor?</pluralform> +<pluralform>Sigur vrei să muÈ›i în Reciclator următoarele %x elemente?</pluralform> +<pluralform>Sigur vrei să muÈ›i în Reciclator următoarele %x de elemente?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Sigur vrei să È™tergi definitiv elementul următor?</pluralform> +<pluralform>Sigur vrei să È™tergi definitiv următoarele %x elemente?</pluralform> +<pluralform>Sigur vrei să È™tergi definitiv următoarele %x de elemente?</pluralform> +</target> + <source>Direct</source> <target>DirecÈ›ionează</target> @@ -1092,26 +1112,6 @@ Notă: Numele filelor trebuie să fie relative la dosarele bază (rădăcină)! <source>Make hidden warnings and dialogs visible again?</source> <target>Vrei ca dialogurile ascunse È™i mesajele de avertizare să fie vizibile din nou?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>Sigur vrei să muÈ›i în Reciclator elementul următor?</pluralform> -<pluralform>Sigur vrei să muÈ›i în Reciclator următoarele %x elemente?</pluralform> -<pluralform>Sigur vrei să muÈ›i în Reciclator următoarele %x de elemente?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>Sigur vrei să È™tergi definitiv elementul următor?</pluralform> -<pluralform>Sigur vrei să È™tergi definitiv următoarele %x elemente?</pluralform> -<pluralform>Sigur vrei să È™tergi definitiv următoarele %x de elemente?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>Lasă ca Conflict Nerezolvat</target> @@ -1139,6 +1139,9 @@ Notă: Numele filelor trebuie să fie relative la dosarele bază (rădăcină)! <source>Files</source> <target>File</target> +<source>Items</source> +<target>Elemente</target> + <source>Percentage</source> <target>Procent</target> @@ -1208,9 +1211,6 @@ Notă: Numele filelor trebuie să fie relative la dosarele bază (rădăcină)! <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1290,7 +1290,7 @@ Notă: Numele filelor trebuie să fie relative la dosarele bază (rădăcină)! <target>Șterg legătura simbolică %x</target> <source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> -<target>Reciclatorul nu este disponibil pentru căile următoare! Filele de acolo vor fi deci È™terse definitiv:</target> +<target>Reciclatorul nu este disponibil pentru căile următoare! Filele de acolo care trebuie È™terse ca urmare a sincronizării, vor fi deci È™terse definitiv:</target> <source>The corresponding folder will be considered as empty.</source> <target>Dosarul corespondent va fi considerat ca fiind gol.</target> @@ -1298,8 +1298,8 @@ Notă: Numele filelor trebuie să fie relative la dosarele bază (rădăcină)! <source>Cannot find the following folders:</source> <target>Nu pot găsi dosarele următoare:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>PoÈ›i ignora această eroare dacă vrei ca dosarele din ambele părÈ›i să fie considerate goale.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>PoÈ›i ignora această eroare dacă vrei ca ambele dosare să fie considerate goale. Dosarele vor fi apoi create automat în timpul sincronizării.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Dosarele sînt interdependente! AtenÈ›ie la setarea regulilor de sincronizare:</target> @@ -1439,12 +1439,6 @@ Notă: Numele filelor trebuie să fie relative la dosarele bază (rădăcină)! <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>Va fi modificat un dosar care face parte din mai multe perechi de dosare! Reverifică setările de sincronizare!</target> -<source>Left</source> -<target>Stînga</target> - -<source>Right</source> -<target>Dreapta</target> - <source>Synchronizing folder pair:</source> <target>Sincronizez perechea de dosare:</target> diff --git a/BUILD/Languages/russian.lng b/BUILD/Languages/russian.lng index 2837a169..8a9ef592 100644 --- a/BUILD/Languages/russian.lng +++ b/BUILD/Languages/russian.lng @@ -20,13 +20,13 @@ <target>Общее времÑ:</target> <source>Cannot set directory lock for %x.</source> -<target></target> +<target>Ðевозможно уÑтановить блокировку папки Ð´Ð»Ñ %x.</target> <source>Show in Explorer</source> <target>Показать в Проводнике</target> <source>Open with default application</source> -<target>Открыть Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¿Ð¾-умолчанию</target> +<target>Открыть Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¿Ð¾ умолчанию</target> <source>Browse directory</source> <target>Обзор папок</target> @@ -44,7 +44,7 @@ <target>Ошибка</target> <source>Selected variant:</source> -<target></target> +<target>Выбранный вариант:</target> <source>Select alternate comparison settings</source> <target>Выбрать альтернативные наÑтройки ÑравнениÑ</target> @@ -68,7 +68,7 @@ <target>Копировать</target> <source>Paste</source> -<target></target> +<target>Ð’Ñтавить</target> <source>Save as batch job</source> <target>Сохранить как пакетное задание</target> @@ -110,19 +110,10 @@ <target>КритичеÑÐºÐ°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°</target> <source>Error Code %x:</source> -<target></target> +<target>Код ошибки %x:</target> <source>Cannot resolve symbolic link %x.</source> -<target>Ðе удаетÑÑ Ñ€Ð°Ð·Ñ€ÐµÑˆÐ¸Ñ‚ÑŒ ÑимволичеÑкую ÑÑылку %x.</target> - -<source>%x MB</source> -<target>%x МБ</target> - -<source>%x KB</source> -<target>%x КБ</target> - -<source>%x GB</source> -<target>%x ГБ</target> +<target>Ðевозможно разрешить Ñимвольную ÑÑылку %x.</target> <source> <pluralform>1 Byte</pluralform> @@ -134,6 +125,15 @@ <pluralform>%x Байт</pluralform> </target> +<source>%x MB</source> +<target>%x МБ</target> + +<source>%x KB</source> +<target>%x КБ</target> + +<source>%x GB</source> +<target>%x ГБ</target> + <source>Database file %x is incompatible.</source> <target>Файл базы данных %x неÑовмеÑтим.</target> @@ -150,10 +150,10 @@ <target>ÐедоÑтаточно памÑти!</target> <source>Cannot write file %x.</source> -<target>Ðе удаетÑÑ Ð·Ð°Ð¿Ð¸Ñать файл %x.</target> +<target>Ðевозможно запиÑать файл %x.</target> <source>Cannot read file %x.</source> -<target>Ðе удаетÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚ÑŒ файл %x.</target> +<target>Ðевозможно прочитать файл %x.</target> <source>Database files do not share a common session.</source> <target>Файлы баз данных не имеют общей ÑеÑÑии.</target> @@ -162,17 +162,14 @@ <target>ИÑключение произошло!</target> <source>Cannot read file attributes of %x.</source> -<target>Ðе удаетÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚ÑŒ атрибуты файла %x.</target> +<target>Ðевозможно прочитать атрибуты файла %x.</target> <source>Cannot get process information.</source> -<target>Ðе удаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡Ð¸Ñ‚ÑŒ информацию о процеÑÑе.</target> +<target>Ðевозможно получить информацию о процеÑÑе.</target> <source>Waiting while directory is locked (%x)...</source> <target>Ожидание ÑнÑÑ‚Ð¸Ñ Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²ÐºÐ¸ Ñ Ð¿Ð°Ð¿ÐºÐ¸ %x...</target> -<source>Creating file %x</source> -<target>Создание файла %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -183,15 +180,15 @@ <pluralform>%x Ñекунд</pluralform> </target> +<source>Creating file %x</source> +<target>Создание файла %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Ошибка при разборе файла %x, Ñтрока %y, колонка %z.</target> <source>Scanning:</source> <target>Сканирую:</target> -<source>Encoding extended time information: %x</source> -<target>Кодирование раÑширенной информации о времени: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -202,6 +199,9 @@ <pluralform>[%x потоков]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>Кодирование раÑширенной информации о времени: %x</target> + <source>/sec</source> <target>/Ñ</target> @@ -212,13 +212,13 @@ <target>Файл конфигурации %x загрузилÑÑ Ñ‡Ð°Ñтично.</target> <source>Cannot access Volume Shadow Copy Service.</source> -<target>Ðе удаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡Ð¸Ñ‚ÑŒ доÑтуп к Ñлужбе Теневого ÐšÐ¾Ð¿Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¢Ð¾Ð¼Ð°.</target> +<target>Ðевозможно получить доÑтуп к Ñлужбе Теневого ÐšÐ¾Ð¿Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¢Ð¾Ð¼Ð°.</target> <source>Please use FreeFileSync 64-bit version to create shadow copies on this system.</source> <target>ПожалуйÑта, иÑпользуйте 64-разрÑдную верÑию FreeFileSync Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ñ‚ÐµÐ½ÐµÐ²Ñ‹Ñ… копий на Ñтой ÑиÑтеме.</target> <source>Cannot load file %x.</source> -<target>Ðе удаетÑÑ Ð·Ð°Ð³Ñ€ÑƒÐ·Ð¸Ñ‚ÑŒ файл %x.</target> +<target>Ðевозможно загрузить файл %x.</target> <source>Path %x does not contain a volume name.</source> <target>Путь %x не Ñодержит Ð¸Ð¼Ñ Ñ‚Ð¾Ð¼Ð°.</target> @@ -230,7 +230,7 @@ <target>Ðевозможно прочитать Ñледующие XML Ñлементы:</target> <source>Cannot find file %x.</source> -<target>Ðе удаетÑÑ Ð½Ð°Ð¹Ñ‚Ð¸ файл %x.</target> +<target>Ðевозможно найти файл %x.</target> <source>&Open...</source> <target>&Открыть...</target> @@ -466,13 +466,13 @@ The command is triggered if: <target>&Дополнительно</target> <source>&Check now</source> -<target></target> +<target>&Проверить ÑейчаÑ</target> <source>Check &automatically once a week</source> -<target></target> +<target>ПроверÑÑ‚ÑŒ &автоматичеÑки раз в неделю</target> <source>Check for new version</source> -<target></target> +<target>Проверка обновлений</target> <source>Compare</source> <target>Сравнить</target> @@ -526,7 +526,7 @@ The command is triggered if: <target>По завершению</target> <source>Close</source> -<target></target> +<target>Закрыть</target> <source>&Pause</source> <target>&Пауза</target> @@ -614,13 +614,13 @@ is the same <target><= Ð’ обе Ñтороны =></target> <source>Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database.</source> -<target></target> +<target>Ð’Ñ‹Ñвление и раÑпроÑтранение изменений на обе Ñтороны. Удаленные, перемещенные и конфликтующие файлы определÑÑŽÑ‚ÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки Ñ Ð¸Ñпользованием базы данных.</target> <source>Mirror ->></source> <target>Зеркало =>></target> <source>Mirror backup of left folder. Right folder is modified to exactly match left folder after synchronization.</source> -<target>Ð—ÐµÑ€ÐºÐ°Ð»ÑŒÐ½Ð°Ñ (резервнаÑ) ÐºÐ¾Ð¿Ð¸Ñ Ð»ÐµÐ²Ð¾Ð¹ чаÑти. Ð’ результате Ñинхронизации Ð¿Ñ€Ð°Ð²Ð°Ñ Ð¿Ð°Ð¿ÐºÐ° будет изменена до полного ÑоответÑÑ‚Ð²Ð¸Ñ Ð»ÐµÐ²Ð¾Ð¹.</target> +<target>Ð—ÐµÑ€ÐºÐ°Ð»ÑŒÐ½Ð°Ñ (резервнаÑ) ÐºÐ¾Ð¿Ð¸Ñ Ð»ÐµÐ²Ð¾Ð¹ чаÑти. Ð’ результате Ñинхронизации Ð¿Ñ€Ð°Ð²Ð°Ñ Ñторона будет изменена до полного ÑоответÑÑ‚Ð²Ð¸Ñ Ñ Ð»ÐµÐ²Ð¾Ð¹.</target> <source>Update -></source> <target>Обновить =></target> @@ -653,10 +653,10 @@ is the same <target>Ðрхивировать</target> <source>Move files to user-defined folder</source> -<target></target> +<target>ПеремеÑтить файлы в пользовательÑкую папку</target> <source>Naming convention:</source> -<target>УÑловие:</target> +<target>УÑловие переименованиÑ:</target> <source>Item exists on left side only</source> <target>Ðлемент ÑущеÑтвует только на левой Ñтороне</target> @@ -674,7 +674,7 @@ is the same <target>Ðлемент имеют различное Ñодержание</target> <source>Conflict/item cannot be categorized</source> -<target>Конфликт/Ñлемент не могут быть отнеÑены к какой-либо категории</target> +<target>Конфликт/Ñлемент невозможно отнеÑти к какой-либо категории</target> <source>Source code written in C++ using:</source> <target>ИÑходный код напиÑан на C++ Ñ Ð¸Ñпользованием:</target> @@ -737,7 +737,7 @@ Note: File names must be relative to base directories! <target>макÑимум</target> <source>&Clear</source> -<target></target> +<target>&ОчиÑтить</target> <source>Fail-safe file copy</source> <target>ОтказоуÑтойчивое копирование файла</target> @@ -767,7 +767,7 @@ Note: File names must be relative to base directories! <target>ОпиÑание</target> <source>&Default</source> -<target>&По-умолчанию</target> +<target>&По умолчанию</target> <source>Start synchronization</source> <target>Ðачать Ñинхронизацию</target> @@ -790,9 +790,6 @@ Note: File names must be relative to base directories! <source>&Find next</source> <target>&Ðайти далее</target> -<source>Operation aborted!</source> -<target>ÐžÐ¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð¾Ñ‚Ð¼ÐµÐ½ÐµÐ½Ð°!</target> - <source>Main bar</source> <target>Ð“Ð»Ð°Ð²Ð½Ð°Ñ Ð¿Ð°Ð½ÐµÐ»ÑŒ</target> @@ -812,7 +809,7 @@ Note: File names must be relative to base directories! <target>Вид ÑпиÑка файлов</target> <source>Open...</source> -<target></target> +<target>Открыть...</target> <source>Save</source> <target>Сохранить</target> @@ -820,6 +817,36 @@ Note: File names must be relative to base directories! <source>Compare both sides</source> <target>Сравнить обе Ñтороны</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>%x папка</pluralform> +<pluralform>%x папки</pluralform> +<pluralform>%x папок</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>%x файл</pluralform> +<pluralform>%x файла</pluralform> +<pluralform>%x файлов</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%y из %x Ñтроки показано</pluralform> +<pluralform>%y из %x Ñтрок показано</pluralform> +<pluralform>%y из %x Ñтрок показано</pluralform> +</target> + <source>Set direction:</source> <target>Выберите направление:</target> @@ -932,7 +959,10 @@ Note: File names must be relative to base directories! <target>Показать файлы, которые не будут Ñкопированы</target> <source>Set as default</source> -<target></target> +<target>УÑтановить по умолчанию</target> + +<source>Operation aborted!</source> +<target>ÐžÐ¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð¾Ñ‚Ð¼ÐµÐ½ÐµÐ½Ð°!</target> <source>All folders are in sync!</source> <target>Ð’Ñе папки Ñинхронизированы!</target> @@ -947,37 +977,7 @@ Note: File names must be relative to base directories! <target>СпиÑок файлов ÑкÑпортирован!</target> <source>Searching for program updates...</source> -<target></target> - -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>%x папка</pluralform> -<pluralform>%x папки</pluralform> -<pluralform>%x папок</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>%x файл</pluralform> -<pluralform>%x файла</pluralform> -<pluralform>%x файлов</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x из %y Ñтроки показано</pluralform> -<pluralform>%x из %y Ñтрок показано</pluralform> -<pluralform>%x из %y Ñтрок показано</pluralform> -</target> +<target>Проверка обновлений программы...</target> <source>Ignore further errors</source> <target>Игнорировать поÑледующие ошибки</target> @@ -986,7 +986,7 @@ Note: File names must be relative to base directories! <target>&Игнорировать</target> <source>Don't show this warning again</source> -<target></target> +<target>Больше не показывать Ñто предупреждение</target> <source>&Switch</source> <target>&Переключить</target> @@ -1040,13 +1040,13 @@ Note: File names must be relative to base directories! <target>на Ñтой неделе</target> <source>This month</source> -<target>в Ñтом меÑÑце</target> +<target>поÑледний меÑÑц</target> <source>This year</source> -<target>в Ñтом году</target> +<target>поÑледний год</target> <source>Last x days</source> -<target>поÑледние Ñ… днÑ(ей)</target> +<target>поÑледние X днÑ(ей)</target> <source>Byte</source> <target>Байт</target> @@ -1060,6 +1060,26 @@ Note: File names must be relative to base directories! <source>Filter</source> <target>Фильтр</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Ð’Ñ‹ точно хотите перемеÑтить Ñледующий %x Ñлемент в "Корзину"?</pluralform> +<pluralform>Ð’Ñ‹ точно хотите перемеÑтить Ñледующие %x Ñлемента в "Корзину"?</pluralform> +<pluralform>Ð’Ñ‹ точно хотите перемеÑтить Ñледующие %x Ñлементов в "Корзину"?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Ð’Ñ‹ точно хотите удалить Ñледующий %x Ñлемент?</pluralform> +<pluralform>Ð’Ñ‹ точно хотите удалить Ñледующие %x Ñлемента?</pluralform> +<pluralform>Ð’Ñ‹ точно хотите удалить Ñледующие %x Ñлементов?</pluralform> +</target> + <source>Direct</source> <target>ПрÑмое</target> @@ -1090,29 +1110,17 @@ Note: File names must be relative to base directories! <source>Make hidden warnings and dialogs visible again?</source> <target>Сделать Ñкрытые Ð¿Ñ€ÐµÐ´ÑƒÐ¿Ñ€ÐµÐ¶Ð´ÐµÐ½Ð¸Ñ Ð¸ диалоги видимыми Ñнова?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target></target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target></target> - <source>Leave as unresolved conflict</source> <target>ОÑтавить как нерешенный конфликт</target> <source>Time stamp</source> -<target></target> +<target>Добавить отметку времени</target> <source>Append a timestamp to each file name</source> <target>Добавить отметку времени Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ имени файла</target> <source>Replace</source> -<target>ПеремеÑтить</target> +<target>Без переименованиÑ</target> <source>Move files and replace if existing</source> <target>ПеремеÑтить файлы и заменить, еÑли ÑущеÑтвуют</target> @@ -1129,56 +1137,59 @@ Note: File names must be relative to base directories! <source>Files</source> <target>Файлы</target> +<source>Items</source> +<target>Ðлементы</target> + <source>Percentage</source> <target>Проценты</target> <source>Cannot monitor directory %x.</source> -<target>Ðе удаетÑÑ Ð½Ð°Ð±Ð»ÑŽÐ´Ð°Ñ‚ÑŒ папку %x.</target> +<target>Ðевозможно наблюдать папку %x.</target> <source>Conversion error:</source> <target>Ошибка преобразованиÑ:</target> <source>Cannot delete file %x.</source> -<target>Ðе удаетÑÑ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ файл %x.</target> +<target>Ðевозможно удалить файл %x.</target> <source>The file is locked by another process:</source> <target>Файл заблокирован другим процеÑÑом:</target> <source>Cannot move file %x to %y.</source> -<target>Ðе удаетÑÑ Ð¿ÐµÑ€ÐµÐ½ÐµÑти файл %x в %y.</target> +<target>Ðевозможно перенеÑти файл %x в %y.</target> <source>Cannot delete directory %x.</source> -<target>Ðе удаетÑÑ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ папку %x.</target> +<target>Ðевозможно удалить папку %x.</target> <source>Cannot write file attributes of %x.</source> -<target>Ðе удаетÑÑ Ð·Ð°Ð¿Ð¸Ñать атрибуты файла %x.</target> +<target>Ðевозможно запиÑать атрибуты файла %x.</target> <source>Cannot write modification time of %x.</source> -<target>Ðе удаетÑÑ Ð·Ð°Ð¿Ð¸Ñать Ð²Ñ€ÐµÐ¼Ñ Ð¼Ð¾Ð´Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸ файла %x.</target> +<target>Ðевозможно запиÑать Ð²Ñ€ÐµÐ¼Ñ Ð¼Ð¾Ð´Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸ файла %x.</target> <source>Cannot find system function %x.</source> -<target>Ðе удаетÑÑ Ð½Ð°Ð¹Ñ‚Ð¸ ÑиÑтемную функцию %x.</target> +<target>Ðевозможно найти ÑиÑтемную функцию %x.</target> <source>Cannot read security context of %x.</source> -<target>Ðе удаетÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚ÑŒ контекÑÑ‚ безобаÑноÑти %x.</target> +<target>Ðевозможно прочитать контекÑÑ‚ безобаÑноÑти %x.</target> <source>Cannot write security context of %x.</source> -<target>Ðе удаетÑÑ Ð·Ð°Ð¿Ð¸Ñать контекÑÑ‚ безобаÑноÑти %x.</target> +<target>Ðевозможно запиÑать контекÑÑ‚ безобаÑноÑти %x.</target> <source>Cannot read permissions of %x.</source> -<target>Ðе удаетÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚ÑŒ права доÑтупа %x.</target> +<target>Ðевозможно прочитать права доÑтупа %x.</target> <source>Cannot write permissions of %x.</source> -<target>Ðе удаетÑÑ Ð·Ð°Ð¿Ð¸Ñать права доÑтупа %x.</target> +<target>Ðевозможно запиÑать права доÑтупа %x.</target> <source>Cannot create directory %x.</source> -<target>Ðе удаетÑÑ Ñоздать папку %x.</target> +<target>Ðевозможно Ñоздать папку %x.</target> <source>Cannot copy symbolic link %x to %y.</source> -<target>Ðе удаетÑÑ Ñкопировать Ñимвольную ÑÑылку %x в %y.</target> +<target>Ðевозможно Ñкопировать Ñимвольную ÑÑылку %x в %y.</target> <source>Cannot copy file %x to %y.</source> -<target>Ðе удаетÑÑ Ñкопировать файл %x в %y.</target> +<target>Ðевозможно Ñкопировать файл %x в %y.</target> <source>Type of item %x is not supported:</source> <target>Тип Ñлемента %x не поддерживаетÑÑ:</target> @@ -1198,9 +1209,6 @@ Note: File names must be relative to base directories! <source>%x PB</source> <target>%x ПБ</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1232,13 +1240,13 @@ Note: File names must be relative to base directories! </target> <source>Cannot set privilege %x.</source> -<target>Ðе удаетÑÑ ÑƒÑтановить привелегии %x.</target> +<target>Ðевозможно уÑтановить привелегии %x.</target> <source>Failed to suspend system sleep mode.</source> -<target></target> +<target>Ðе удалоÑÑŒ приоÑтановить режим Ñна ÑиÑтемы.</target> <source>Cannot change process I/O priorities.</source> -<target></target> +<target>Ðевозможно изменить приоритет процеÑÑа.</target> <source>Unable to move %x to the Recycle Bin!</source> <target>Ðевозможно перемеÑтить %x в "Корзину"!</target> @@ -1262,7 +1270,7 @@ Note: File names must be relative to base directories! </target> <source>Checking recycle bin availability for folder %x...</source> -<target></target> +<target>Проверка доÑтупноÑти "Корзины" Ð´Ð»Ñ Ð¿Ð°Ð¿ÐºÐ¸ %x...</target> <source>Moving file %x to recycle bin</source> <target>Перемещение файла %x в "Корзину"</target> @@ -1286,13 +1294,13 @@ Note: File names must be relative to base directories! <target>Ð”Ð»Ñ Ñтого пути "Корзина" не доÑтупна! Файлы будут удалены безвозвратно:</target> <source>The corresponding folder will be considered as empty.</source> -<target></target> +<target>СоответÑÑ‚Ð²ÑƒÑŽÑ‰Ð°Ñ Ð¿Ð°Ð¿ÐºÐ° будет ÑчитатьÑÑ Ð¿ÑƒÑтой.</target> <source>Cannot find the following folders:</source> -<target></target> +<target>Ðевозможно найти Ñледующие папки:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target></target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>Ð’Ñ‹ можете проигнорировать Ñту ошибку, принÑв каждую папку за пуÑтую. При Ñтом папки будут Ñозданы автоматичеÑки во Ð²Ñ€ÐµÐ¼Ñ Ñинхронизации.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>ЗавиÑимые папки! Будьте внимательны при наÑтройке правил Ñинхронизации:</target> @@ -1301,7 +1309,7 @@ Note: File names must be relative to base directories! <target>Ðачать Ñравнение</target> <source>Calculating sync directions...</source> -<target></target> +<target>РаÑчет направлений Ñинхронизации...</target> <source>Conflict detected:</source> <target>Обнаружен конфликт:</target> @@ -1397,7 +1405,7 @@ Note: File names must be relative to base directories! <target>Обновление атрибутов %x</target> <source>Cannot find %x.</source> -<target></target> +<target>Ðевозможно найти %x.</target> <source>Target folder %x already existing.</source> <target>Ð¦ÐµÐ»ÐµÐ²Ð°Ñ Ð¿Ð°Ð¿ÐºÐ° %x уже ÑущеÑтвует.</target> @@ -1432,12 +1440,6 @@ Note: File names must be relative to base directories! <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>Папка, входÑÑ‰Ð°Ñ Ð² неÑколько пар папок, будет изменена. ПожалуйÑта, проверьте наÑтройки Ñинхронизации.</target> -<source>Left</source> -<target>Слева</target> - -<source>Right</source> -<target>Справа</target> - <source>Synchronizing folder pair:</source> <target>Ð¡Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð¿Ð°Ñ€Ñ‹ папок:</target> @@ -1445,7 +1447,7 @@ Note: File names must be relative to base directories! <target>Создание базы данных...</target> <source>Creating Volume Shadow Copy for %x...</source> -<target></target> +<target>Создание Тома Теневого ÐšÐ¾Ð¿Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð´Ð»Ñ %x...</target> <source>Data verification error: Source and target file have different content!</source> <target>Ошибка проверки данных: иÑходный и конечный файлы имеют разное Ñодержание!</target> diff --git a/BUILD/Languages/scottish_gaelic.lng b/BUILD/Languages/scottish_gaelic.lng index 0bfe3b64..3a0e3064 100644 --- a/BUILD/Languages/scottish_gaelic.lng +++ b/BUILD/Languages/scottish_gaelic.lng @@ -115,15 +115,6 @@ <source>Cannot resolve symbolic link %x.</source> <target>Cha ghabh an symbolic link %x fhuasgladh.</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source> <pluralform>1 Byte</pluralform> <pluralform>%x Bytes</pluralform> @@ -135,6 +126,15 @@ <pluralform>%x baidht</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>Chan eil am faidhle stòir-dhà ta %x co-chòrdail.</target> @@ -171,9 +171,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>A' feitheamh fhad 's a tha am pasgan glaiste (%x)...</target> -<source>Creating file %x</source> -<target>A' cruthachadh an fhaidhle %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -185,15 +182,15 @@ <pluralform>%x diog</pluralform> </target> +<source>Creating file %x</source> +<target>A' cruthachadh an fhaidhle %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Mearachd le parsadh an fhaidhle %x, loidhne %y, colbh %z.</target> <source>Scanning:</source> <target>'Ga sganadh:</target> -<source>Encoding extended time information: %x</source> -<target>A' còdachadh fiosrachadh leudaichte an ama: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -205,6 +202,9 @@ <pluralform>[%x snà ithlean]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>A' còdachadh fiosrachadh leudaichte an ama: %x</target> + <source>/sec</source> <target>/diog</target> @@ -417,12 +417,12 @@ Thèid an loidhne-à ithne a chur gu dol: <source>Date:</source> <target>Ceann-là :</target> -<source>Action</source> -<target>Gnìomh</target> - <source>Category</source> <target>Roinn seòrsa</target> +<source>Action</source> +<target>Gnìomh</target> + <source>Drag && drop</source> <target>Slaod â” leig à s</target> @@ -797,9 +797,6 @@ An aire: Feumaidh ainmean nam faidhlichean a bhi dà imheach ris na bun-phasgain <source>&Find next</source> <target>&Lorg an ath-fhear</target> -<source>Operation aborted!</source> -<target>Sguireadh dhen ghnìomh!</target> - <source>Main bar</source> <target>Am prìomh-bhà r</target> @@ -827,6 +824,39 @@ An aire: Feumaidh ainmean nam faidhlichean a bhi dà imheach ris na bun-phasgain <source>Compare both sides</source> <target>Dèan coimeas air an dà thaobh</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>%x phasgan</pluralform> +<pluralform>%x phasgan</pluralform> +<pluralform>%x pasgain</pluralform> +<pluralform>%x pasgan</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>%x fhaidhle</pluralform> +<pluralform>%x fhaidhle</pluralform> +<pluralform>%x faidhlichean</pluralform> +<pluralform>%x faidhle</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%y de %x rà gh san t-sealladh</pluralform> +<pluralform>%y de %x rà gh san t-sealladh</pluralform> +<pluralform>%y de %x rà ghan san t-sealladh</pluralform> +<pluralform>%y de %x rà gh san t-sealladh</pluralform> +</target> + <source>Set direction:</source> <target>Suidhich a' chomhair:</target> @@ -941,6 +971,9 @@ An aire: Feumaidh ainmean nam faidhlichean a bhi dà imheach ris na bun-phasgain <source>Set as default</source> <target>Suidhich mar a' bhun-roghainn</target> +<source>Operation aborted!</source> +<target>Sguireadh dhen ghnìomh!</target> + <source>All folders are in sync!</source> <target>Tha gach pasgan air a shioncronachadh!</target> @@ -956,39 +989,6 @@ An aire: Feumaidh ainmean nam faidhlichean a bhi dà imheach ris na bun-phasgain <source>Searching for program updates...</source> <target>A' lorg ùrachaidhean a' phrògraim...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>%x phasgan</pluralform> -<pluralform>%x phasgan</pluralform> -<pluralform>%x pasgain</pluralform> -<pluralform>%x pasgan</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>%x fhaidhle</pluralform> -<pluralform>%x fhaidhle</pluralform> -<pluralform>%x faidhlichean</pluralform> -<pluralform>%x faidhle</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>A' sealltainn %x rà gh</pluralform> -<pluralform>A' sealltainn %x à %y rà gh</pluralform> -<pluralform>A' sealltainn %x à %y rà ghan</pluralform> -<pluralform>A' sealltainn %x à %y rà gh</pluralform> -</target> - <source>Ignore further errors</source> <target>Leig seachad mearachd sam bith eile</target> @@ -1070,6 +1070,28 @@ An aire: Feumaidh ainmean nam faidhlichean a bhi dà imheach ris na bun-phasgain <source>Filter</source> <target>Criathrag</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>A bheil thu cinnteach gu bheil thu airson an %x nì seo a ghluasad dhan bhiona?</pluralform> +<pluralform>A bheil thu cinnteach gu bheil thu airson na %x nì seo a ghluasad dhan bhiona?</pluralform> +<pluralform>A bheil thu cinnteach gu bheil thu airson na %x nithean seo a ghluasad dhan bhiona?</pluralform> +<pluralform>A bheil thu cinnteach gu bheil thu airson na %x nì seo a ghluasad dhan bhiona?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>A bheil thu cinnteach gu bheil thu airson an %x nì seo a sguabadh à s?</pluralform> +<pluralform>A bheil thu cinnteach gu bheil thu airson na %x nì seo a sguabadh à s?</pluralform> +<pluralform>A bheil thu cinnteach gu bheil thu airson na %x nithean seo a sguabadh à s?</pluralform> +<pluralform>A bheil thu cinnteach gu bheil thu airson na %x nì seo a sguabadh à s?</pluralform> +</target> + <source>Direct</source> <target>Dìreach</target> @@ -1097,43 +1119,21 @@ An aire: Feumaidh ainmean nam faidhlichean a bhi dà imheach ris na bun-phasgain <source>Make hidden warnings and dialogs visible again?</source> <target>A bheil thus airson na rabhaidhean 's còmraidhean falaichte fhaicinn a-rithist?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>A bheil thu cinnteach gu bheil thu airson an nì a leanas a chur dhan bhiona?</pluralform> -<pluralform>A bheil thu cinnteach gu bheil thu airson an %x nì a leanas a chur dhan bhiona?</pluralform> -<pluralform>A bheil thu cinnteach gu bheil thu airson na %x nithean a leanas a chur dhan bhiona?</pluralform> -<pluralform>A bheil thu cinnteach gu bheil thu airson na %x nì a leanas a chur dhan bhiona?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>A bheil thu cinnteach gu bheil thu airso an nì a leanas a sguabadh à s?</pluralform> -<pluralform>A bheil thu cinnteach gu bheil thu airso an %x nì a leanas a sguabadh à s?</pluralform> -<pluralform>A bheil thu cinnteach gu bheil thu airso na %x nithean a leanas a sguabadh à s?</pluralform> -<pluralform>A bheil thu cinnteach gu bheil thu airso na %x nì a leanas a sguabadh à s?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>Fà g mar còmhstri gun rèiteachadh</target> -<source>Time stamp</source> -<target>Stampa ama</target> - -<source>Append a timestamp to each file name</source> -<target>Cuir stampa-ama ris ainm gach faidhle</target> - <source>Replace</source> <target>Cuir 'na à ite</target> <source>Move files and replace if existing</source> <target>Gluais na faidhlichean 's cuir iad an à ite na feadhainn là ithreach ma tha gin ann</target> +<source>Time stamp</source> +<target>Stampa ama</target> + +<source>Append a timestamp to each file name</source> +<target>Cuir stampa-ama ris ainm gach faidhle</target> + <source>Folder</source> <target>Pasgan</target> @@ -1146,6 +1146,9 @@ An aire: Feumaidh ainmean nam faidhlichean a bhi dà imheach ris na bun-phasgain <source>Files</source> <target>Faidhlichean</target> +<source>Items</source> +<target>Nithean</target> + <source>Percentage</source> <target>Ceudad</target> @@ -1215,9 +1218,6 @@ An aire: Feumaidh ainmean nam faidhlichean a bhi dà imheach ris na bun-phasgain <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1308,8 +1308,8 @@ An aire: Feumaidh ainmean nam faidhlichean a bhi dà imheach ris na bun-phasgain <source>Cannot find the following folders:</source> <target>Chan urrainn dhuinn na pasgain a leanas a lorg:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>'S urrainn dhut a' mhearachd seo a leigeil seachad airson 's gun dèid gach pasgan a là imhseachadh mar phasgan falamh.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>'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</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Tha na pasgain an eisimeil a chèile! Bi faiceallach nuair a shuidhicheas tu na riaghailtean sioncronachaidh:</target> @@ -1449,12 +1449,6 @@ An aire: Feumaidh ainmean nam faidhlichean a bhi dà imheach ris na bun-phasgain <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>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?</target> -<source>Left</source> -<target>Clì</target> - -<source>Right</source> -<target>Deas</target> - <source>Synchronizing folder pair:</source> <target>A' sioncronachadh paidhir nam pasgan:</target> diff --git a/BUILD/Languages/serbian.lng b/BUILD/Languages/serbian.lng new file mode 100644 index 00000000..a3fe8a9a --- /dev/null +++ b/BUILD/Languages/serbian.lng @@ -0,0 +1,1450 @@ +<header> + <language name>CрпÑки</language name> + <translator>БалканÑки Шпијун</translator> + <locale>sr_RS</locale> + <flag file>serbia.png</flag file> + <plural forms>3</plural forms> + <plural definition>n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2</plural definition> +</header> + +<source>Searching for folder %x...</source> +<target>Тражим фолдер %x...</target> + +<source>Items processed:</source> +<target>Обрађени елементи:</target> + +<source>Items remaining:</source> +<target>ПреоÑтали елементи:</target> + +<source>Total time:</source> +<target>Укупно време:</target> + +<source>Cannot set directory lock for %x.</source> +<target>Ðе могу закључати фолдер %x.</target> + +<source>Show in Explorer</source> +<target>Прикажи у ЕкÑплореру</target> + +<source>Open with default application</source> +<target>Отвори Ñа подразумеваном апликацијом</target> + +<source>Browse directory</source> +<target>Одабери фолдер</target> + +<source>Abort requested: Waiting for current operation to finish...</source> +<target>Прекид захтеван: Чека Ñе да Ñе тренутна акција заврши...</target> + +<source>Failure to create timestamp for versioning:</source> +<target>ÐеуÑпело Ñтварање временÑке ознаке за верзију:</target> + +<source>RealtimeSync - Automated Synchronization</source> +<target>RealtimeSync - ÐутоматÑка Синхронизација</target> + +<source>Error</source> +<target>Грешка</target> + +<source>Selected variant:</source> +<target>Одабрани начин:</target> + +<source>Select alternate comparison settings</source> +<target>Изаберите алтернативна подешавања упоређивања</target> + +<source>Select alternate synchronization settings</source> +<target>Изаберите алтернативна подешавања Ñинхронизације</target> + +<source>Filter is active</source> +<target>Филтер је активан</target> + +<source>No filter selected</source> +<target>Ðиједан филтер није одабран</target> + +<source>Remove alternate settings</source> +<target>Уклони алтернативна подешавања</target> + +<source>Clear filter settings</source> +<target>Уклони подешавања филтера</target> + +<source>Copy</source> +<target>Копирај</target> + +<source>Paste</source> +<target>Залепи</target> + +<source>Save as batch job</source> +<target>Сачувај као беч задатак</target> + +<source>Comparison settings</source> +<target>Подешавања упоређивања</target> + +<source>Synchronization settings</source> +<target>Подешавања Ñинхронизације</target> + +<source>About</source> +<target>О програму</target> + +<source>Confirm</source> +<target>Потврди</target> + +<source>Configure filter</source> +<target>Конфигуриши филтер</target> + +<source>Global settings</source> +<target>Глобална подешавања</target> + +<source>Find</source> +<target>Пронађи</target> + +<source>Select time span</source> +<target>Изаберите временÑки раÑпон</target> + +<source>Invalid command line:</source> +<target>Ðетачна командна линија:</target> + +<source>Info</source> +<target>Инфо</target> + +<source>Warning</source> +<target>Упозорење</target> + +<source>Fatal Error</source> +<target>Критична грешка</target> + +<source>Error Code %x:</source> +<target>Грешка број %x:</target> + +<source>Cannot resolve symbolic link %x.</source> +<target>Ðе могу разрешити Ñимболичну везу %x.</target> + +<source>Cannot determine final path for %x.</source> +<target></target> + +<source>Cannot find system function %x.</source> +<target>Ðе могу пронаћи ÑиÑтемÑку функцију %x.</target> + +<source> +<pluralform>1 Byte</pluralform> +<pluralform>%x Bytes</pluralform> +</source> +<target> +<pluralform>%x Бајт</pluralform> +<pluralform>%x Бајта</pluralform> +<pluralform>%x Бајтова</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + +<source>Database file %x is incompatible.</source> +<target>Датотека базе %x је некомпатибилна.</target> + +<source>Initial synchronization:</source> +<target>Почетна Ñинхронизација:</target> + +<source>Database file %x does not yet exist.</source> +<target>Датотека базе %x још не поÑтоји.</target> + +<source>Database file is corrupt:</source> +<target>Датотека базе је оштећена:</target> + +<source>Out of memory!</source> +<target>ÐедоÑтатак меморије!</target> + +<source>Cannot write file %x.</source> +<target>Ðе могу упиÑати датотеку %x.</target> + +<source>Cannot read file %x.</source> +<target>Ðе могу читати датотеку %x.</target> + +<source>Database files do not share a common session.</source> +<target>Датотеке базе не деле заједничку ÑеÑију.</target> + +<source>An exception occurred!</source> +<target>Догодило Ñе изузеће!</target> + +<source>Cannot read file attributes of %x.</source> +<target>Ðе могу прочитати атрибуте од %x.</target> + +<source>Cannot get process information.</source> +<target>Ðе могу добити информације о процеÑу.</target> + +<source>Waiting while directory is locked (%x)...</source> +<target>Чека Ñе док Ñе фолдер не закључа (%x)...</target> + +<source> +<pluralform>1 sec</pluralform> +<pluralform>%x sec</pluralform> +</source> +<target> +<pluralform>%x Ñек</pluralform> +<pluralform>%x Ñек</pluralform> +<pluralform>%x Ñек</pluralform> +</target> + +<source>Creating file %x</source> +<target>Правим датотеку %x</target> + +<source>Error parsing file %x, row %y, column %z.</source> +<target>Грешка у анализи датотеке %x, ред %y, колона %z.</target> + +<source>Scanning:</source> +<target>Претражујем:</target> + +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target> +<pluralform>[%x Ðит]</pluralform> +<pluralform>[%x Ðити]</pluralform> +<pluralform>[%x Ðити]</pluralform> +</target> + +<source>Encoding extended time information: %x</source> +<target>Кодирам проширене информације о времену: %x</target> + +<source>/sec</source> +<target>/Ñек</target> + +<source>File %x does not contain a valid configuration.</source> +<target>Датотека %x не Ñадржи валидну конфигурацију.</target> + +<source>Configuration file %x loaded partially only.</source> +<target>Датотека подешавања %x учитана Ñамо делимично.</target> + +<source>Cannot access Volume Shadow Copy Service.</source> +<target>Ðе могу приÑтупити Volume Shadow Copy СервиÑу.</target> + +<source>Please use FreeFileSync 64-bit version to create shadow copies on this system.</source> +<target>Молимо кориÑтите FreeFileSync 64-битну верзију за израду shadow копија на овом ÑиÑтему.</target> + +<source>Cannot load file %x.</source> +<target>Ðе могу учитати датотеку %x.</target> + +<source>Path %x does not contain a volume name.</source> +<target>Путања %x не Ñадржи назив партиције.</target> + +<source>Volume name %x not part of file name %y!</source> +<target>Ðазив партиције %x није део имена датотеке %y!</target> + +<source>Cannot read the following XML elements:</source> +<target>Ðе могу прочитати Ñледеће XML елементе:</target> + +<source>Cannot find file %x.</source> +<target>Ðе могу пронаћи датотеку %x.</target> + +<source>&Open...</source> +<target>&Отвори...</target> + +<source>Save &as...</source> +<target>Сачувај &као...</target> + +<source>&Quit</source> +<target>&Излаз</target> + +<source>&Program</source> +<target>&Програм</target> + +<source>&Content</source> +<target>&Садржај</target> + +<source>&About</source> +<target>&О програму</target> + +<source>&Help</source> +<target>&Помоћ</target> + +<source>Usage:</source> +<target>Употреба:</target> + +<source>1. Select folders to watch.</source> +<target>1. Одаберите фолдере за надгледање.</target> + +<source>2. Enter a command line.</source> +<target>2. УнеÑите командну линију.</target> + +<source>3. Press 'Start'.</source> +<target>3. ПритиÑните 'Старт'.</target> + +<source>To get started just import a .ffs_batch file.</source> +<target>Да би почели увезите .ffs_batch датотеку.</target> + +<source>Folders to watch</source> +<target>Фолдери за надгледање</target> + +<source>Add folder</source> +<target>Додај фолдер</target> + +<source>Remove folder</source> +<target>Уклони фолдер</target> + +<source>Browse</source> +<target>Одабери</target> + +<source>Select a folder</source> +<target>Одаберите фолдер</target> + +<source>Idle time [seconds]</source> +<target>Време мировања [Ñекунди]</target> + +<source>Idle time between last detected change and execution of command</source> +<target>Време мировања између задње препознате промене и извршења наредбе</target> + +<source>Command line</source> +<target>Командна линија</target> + +<source> +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) +</source> +<target> +Ðаредба ће бити покренута ако Ñе: +- датотеке или подфолдери промене +- нови фолдери појаве (нпр. укључење USB Ñтика) +</target> + +<source>Start</source> +<target>Старт</target> + +<source>&Retry</source> +<target>&Понови</target> + +<source>Cancel</source> +<target>ОдуÑтани</target> + +<source>Build: %x</source> +<target>Подверзија: %x</target> + +<source>All files</source> +<target>Све датотеке</target> + +<source>&Restore</source> +<target>&Врати</target> + +<source>&Exit</source> +<target>&Излаз</target> + +<source>Monitoring active...</source> +<target>Ðадгледање активно...</target> + +<source>Waiting for missing directories...</source> +<target>Чека Ñе недоÑтајући фолдери...</target> + +<source>A folder input field is empty.</source> +<target>Поље за одабир фолдера је празно.</target> + +<source>Synchronization aborted!</source> +<target>Синхронизација прекинута!</target> + +<source>Synchronization completed with errors!</source> +<target>Синхронизација завршена Ñ Ð³Ñ€ÐµÑˆÐºÐ°Ð¼Ð°!</target> + +<source>Synchronization completed with warnings.</source> +<target>Синхронизација завршена Ñ ÑƒÐ¿Ð¾Ð·Ð¾Ñ€ÐµÑšÐ¸Ð¼Ð°.</target> + +<source>Nothing to synchronize!</source> +<target>Ðема ничега за Ñинхронизацију!</target> + +<source>Synchronization completed successfully.</source> +<target>Синхронизација уÑпешно завршена.</target> + +<source>Saving log file %x...</source> +<target>УпиÑујем лог датотеку %x...</target> + +<source>Press "Switch" to resolve issues in FreeFileSync main dialog.</source> +<target>ПритиÑни "Замени" за решавање проблема у FreeFileSync главном оквиру дијалога.</target> + +<source>Switching to FreeFileSync main dialog...</source> +<target>Пребацујем у FreeFileSync главни оквир дијалога...</target> + +<source>A new version of FreeFileSync is available:</source> +<target>Ðова верзија FreeFileSync је доÑтупна:</target> + +<source>Download now?</source> +<target>Преузети Ñада?</target> + +<source>FreeFileSync is up to date!</source> +<target>FreeFileSync је ажуриран!</target> + +<source>Information</source> +<target>Информација</target> + +<source>Unable to connect to sourceforge.net!</source> +<target>Ðе могу Ñе повезати на sourceforge.net!</target> + +<source>Current FreeFileSync version number was not found online! Do you want to check manually?</source> +<target>Тренутна FreeFileSync верзија није пронађена online! Да ли желите да проверите ручно?</target> + +<source>Do you want FreeFileSync to automatically check for updates every week?</source> +<target>Да ли желите да FreeFileSync аутоматÑки провери апдејт Ñваке недеље?</target> + +<source>(Requires an Internet connection!)</source> +<target>(Потребна интернет конекција!)</target> + +<source><Symlink></source> +<target><Сим-веза></target> + +<source><Folder></source> +<target><Фолдер></target> + +<source>Full path</source> +<target>Пуна путања</target> + +<source>Name</source> +<target>Име</target> + +<source>Relative path</source> +<target>Релативна путања</target> + +<source>Base folder</source> +<target>ОÑновни фолдер</target> + +<source>Size</source> +<target>Величина</target> + +<source>Date</source> +<target>Датум</target> + +<source>Extension</source> +<target>ЕкÑтензија</target> + +<source>Size:</source> +<target>Величина:</target> + +<source>Date:</source> +<target>Датум:</target> + +<source>Category</source> +<target>Категорија</target> + +<source>Action</source> +<target>Ðкција</target> + +<source>Drag && drop</source> +<target>Вуци && пуÑти</target> + +<source>Close progress dialog</source> +<target>Затвори дијалог прогреÑа</target> + +<source>Standby</source> +<target>Пређи у Ñтање приправноÑти</target> + +<source>Log off</source> +<target>Одјави Ñе</target> + +<source>Shut down</source> +<target>ИÑкључи рачунар</target> + +<source>Hibernate</source> +<target>Хибернација</target> + +<source>&New</source> +<target>&Ðово</target> + +<source>&Save</source> +<target>&Сачувај</target> + +<source>Save as &batch job...</source> +<target>Сачувај као &беч задатак...</target> + +<source>1. &Compare</source> +<target>1. &Упореди</target> + +<source>2. &Synchronize</source> +<target>2. &Синхронизуј</target> + +<source>&Language</source> +<target>&Језик</target> + +<source>&Global settings...</source> +<target>&Глобална подешавања...</target> + +<source>&Export file list...</source> +<target>&Извоз лиÑте датотека...</target> + +<source>&Advanced</source> +<target>&Ðапредно</target> + +<source>&Check now</source> +<target>&Провери Ñада</target> + +<source>Check &automatically once a week</source> +<target>Провери &аутоматÑки једном недељно</target> + +<source>Check for new version</source> +<target>Потражи новију верзију</target> + +<source>Compare</source> +<target>Упореди</target> + +<source>Synchronize</source> +<target>Синхронизуј</target> + +<source>Add folder pair</source> +<target>Додај фолдер пар</target> + +<source>Remove folder pair</source> +<target>Уклони фолдер пар</target> + +<source>Swap sides</source> +<target>Замени Ñтране</target> + +<source>Hide excluded items</source> +<target>Сакриј иÑкључене Ñтавке</target> + +<source>Show filtered or temporarily excluded files</source> +<target>Прикажи филтриране или привремено иÑкључене датотеке</target> + +<source>Number of files and folders that will be created</source> +<target>Број датотека и фолдера који ће бити креирани</target> + +<source>Number of files that will be overwritten</source> +<target>Број датотека које ће бити замењене у Ñадржају</target> + +<source>Number of files and folders that will be deleted</source> +<target>Број датотека и фолдера који ће бити избриÑани</target> + +<source>Total bytes to copy</source> +<target>Укупно бајтова за копирање</target> + +<source>Items found:</source> +<target>Пронађене Ñтавке:</target> + +<source>Speed:</source> +<target>Брзина:</target> + +<source>Time remaining:</source> +<target>ПреоÑтало време:</target> + +<source>Time elapsed:</source> +<target>Протекло време:</target> + +<source>Synchronizing...</source> +<target>Синхронизујем...</target> + +<source>On completion</source> +<target>При завршетку</target> + +<source>Close</source> +<target>Затвори</target> + +<source>&Pause</source> +<target>&Пауза</target> + +<source>Batch job</source> +<target>Беч задатак</target> + +<source>Create a batch file to automate synchronization. Double-click this file or schedule in your system's task planner: FreeFileSync.exe <job name>.ffs_batch</source> +<target>Креирајте беч датотеку за аутоматÑку Ñинхронизацију. Дупли клик на ову датотеку или додајте задатак у вашем таÑк менаџеру: FreeFileSync.exe <назив задатка>.ffs_batch</target> + +<source>Help</source> +<target>Помоћ</target> + +<source>Error handling</source> +<target>Грешка при обради</target> + +<source>Ignore</source> +<target>Игнориши</target> + +<source>Hide all error and warning messages</source> +<target>Сакриј Ñве грешке и упозорења</target> + +<source>Pop-up</source> +<target>ИÑкачући прозор</target> + +<source>Show pop-up on errors or warnings</source> +<target>Прикажи иÑкачући прозор при грешкама и упозорењима</target> + +<source>Exit</source> +<target>Изађи</target> + +<source>Abort synchronization on first error</source> +<target>Прекини Ñинхронизацију при првој грешци</target> + +<source>Show progress dialog</source> +<target>Прикажи дијалог прогреÑа</target> + +<source>Save log</source> +<target>Сачувај лог</target> + +<source>Select folder to save log files</source> +<target>Одабери фолдер за чување лог датотека</target> + +<source>Limit</source> +<target>Ограничи</target> + +<source>Limit maximum number of log files</source> +<target>Ограничи макÑималан број лог датотека</target> + +<source>Select variant</source> +<target>Одабери варијанту</target> + +<source> +Files are found equal if + - last write time and date + - file size +are the same +</source> +<target> +Датотеке Ñе Ñматрају једнаке ако Ñу им + - време задње промене и датум + - величине датотека +једнаки +</target> + +<source>File time and size</source> +<target>Време и величина датотеке</target> + +<source> +Files are found equal if + - file content +is the same +</source> +<target> +Датотеке Ñе Ñматрају једнаке ако им је + - Ñадржај датотеке +једнак +</target> + +<source>File content</source> +<target>Садржај датотеке</target> + +<source>Symbolic Link handling</source> +<target>Управљање Ñимболичним везама</target> + +<source>OK</source> +<target>У реду</target> + +<source><- Two way -></source> +<target><- ДвоÑмерно -></target> + +<source>Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database.</source> +<target>Пронађи и изврши промене на обе Ñтране. БриÑања, премештања и конфликти Ñе откривају аутоматÑки употребом базе података.</target> + +<source>Mirror ->></source> +<target>Огледално ->></target> + +<source>Mirror backup of left folder. Right folder is modified to exactly match left folder after synchronization.</source> +<target>Огледални бекап левог фолдера. ДеÑни фолдер Ñе модификује да би био једнак левом фолдеру након Ñинхронизације.</target> + +<source>Update -></source> +<target>Ðжурирарно -></target> + +<source>Copy new or updated files to right folder.</source> +<target>Копирај нове или ажуриране датотеке у деÑни фолдер.</target> + +<source>Custom</source> +<target>Специфично</target> + +<source>Configure your own synchronization rules.</source> +<target>Конфигуришите ваша влаÑтита Ñинхронизациона правила.</target> + +<source>Deletion handling</source> +<target>Управљање при бриÑању</target> + +<source>Permanent</source> +<target>Трајно</target> + +<source>Delete or overwrite files permanently</source> +<target>Трајно избриши или замени датотеке</target> + +<source>Recycle Bin</source> +<target>Корпа за Ñмеће</target> + +<source>Use Recycle Bin for deleted and overwritten files</source> +<target>КориÑти Корпу за Ñмеће за избриÑане и замењене датотеке</target> + +<source>Versioning</source> +<target>Верзионирање</target> + +<source>Move files to user-defined folder</source> +<target>ПремеÑти датотеке у кориÑнички одабран фолдер</target> + +<source>Naming convention:</source> +<target>Правило именовања:</target> + +<source>Item exists on left side only</source> +<target>Ставка поÑтоји Ñамо на левој Ñтрани</target> + +<source>Item exists on right side only</source> +<target>Ставка поÑтоји Ñамо на деÑној Ñтрани</target> + +<source>Left side is newer</source> +<target>Лева Ñтрана је новија</target> + +<source>Right side is newer</source> +<target>ДеÑна Ñтрана је новија</target> + +<source>Items have different content</source> +<target>Ставке имају различит Ñадржај</target> + +<source>Conflict/item cannot be categorized</source> +<target>Конфликт/Ñтавка не може бити разврÑтана</target> + +<source>Source code written in C++ using:</source> +<target>Изворни код напиÑан у C++ уз коришћење:</target> + +<source>If you like FreeFileSync</source> +<target>Ðко вам Ñе Ñвиђа FreeFileSync</target> + +<source>Donate with PayPal</source> +<target>Донација Ñа PayPal-ом</target> + +<source>Many thanks for localization:</source> +<target>Велике похвале за локализацију:</target> + +<source>Feedback and suggestions are welcome</source> +<target>Повратне информације и предлози Ñу добродошли</target> + +<source>Homepage</source> +<target>Веб Ñтраница</target> + +<source>FreeFileSync at Sourceforge</source> +<target>FreeFileSync на Sourceforge-у</target> + +<source>Email</source> +<target>И-меил</target> + +<source>Published under the GNU General Public License</source> +<target>Објављено под ГÐУ Општом јавном лиценцом</target> + +<source>Delete on both sides</source> +<target>Избриши на обе Ñтране</target> + +<source>Delete on both sides even if the file is selected on one side only</source> +<target>Избриши на обе Ñтране чак иако је датотека Ñелектована Ñамо на једној Ñтрани</target> + +<source> +Only files that match all filter settings will be synchronized. +Note: File names must be relative to base directories! +</source> +<target> +Само датотеке које одговарају Ñвим филтерÑким подешавањима биће Ñинхронизоване. +Ðапомена: Имена датотека морају бити релативна према оÑновним фолдерима! +</target> + +<source>Include</source> +<target>Укључи</target> + +<source>Exclude</source> +<target>ИÑкључи</target> + +<source>Time span</source> +<target>ВременÑко ограничење</target> + +<source>File size</source> +<target>Величина датотеке</target> + +<source>Minimum</source> +<target>Минимум</target> + +<source>Maximum</source> +<target>МакÑимум</target> + +<source>&Clear</source> +<target>&Уклони</target> + +<source>Fail-safe file copy</source> +<target>Копирање заштићено од грешака</target> + +<source>Write to a temporary file (*.ffs_tmp) first then rename it. This guarantees a consistent state even in case of fatal error.</source> +<target>Запиши у привремену датотеку (*.ffs_tmp) прво, а онда је преименуј. Ово гарантује непромењивоÑÑ‚ чак и у Ñлучају критичне грешке.</target> + +<source>Copy locked files</source> +<target>Копирај закључане датотеке</target> + +<source>Copy shared or locked files using Volume Shadow Copy Service (Requires Administrator rights)</source> +<target>Копирај дељене или закључане датотеке кориÑтећи Volume Shadow Copy Ð¡ÐµÑ€Ð²Ð¸Ñ (Потребна админиÑтраторÑка права)</target> + +<source>Copy file access permissions</source> +<target>Копирај овлашћења приÑтупа датотекама</target> + +<source>Transfer file and folder permissions (Requires Administrator rights)</source> +<target>ПремеÑти овлашћења приÑтупа датотека (Потребна админиÑтраторÑка права)</target> + +<source>Restore hidden dialogs</source> +<target>Прикажи Ñкривене диалоге</target> + +<source>External applications</source> +<target>Спољне апликације</target> + +<source>Description</source> +<target>ОпиÑ</target> + +<source>&Default</source> +<target>&Подразумевано</target> + +<source>Start synchronization</source> +<target>Почни Ñинхронизацију</target> + +<source>Variant</source> +<target>Варијанта</target> + +<source>Statistics</source> +<target>СтатиÑтика</target> + +<source>Don't show this dialog again</source> +<target>Ðе приказуј овај диалог поновно</target> + +<source>Find what:</source> +<target>Ðађи шта:</target> + +<source>Match case</source> +<target>По величини Ñлова</target> + +<source>&Find next</source> +<target>&Ðађи Ñледеће</target> + +<source>Folder pairs</source> +<target>ФолдерÑки парови</target> + +<source>Overview</source> +<target>Преглед</target> + +<source>Configuration</source> +<target>Подешавања</target> + +<source>Main bar</source> +<target>Главна трака</target> + +<source>Filter files</source> +<target>Филтрирај датотеке</target> + +<source>Select view</source> +<target>Изаберите приказ</target> + +<source>Open...</source> +<target>Отвори...</target> + +<source>Save</source> +<target>Сачувај</target> + +<source>Compare both sides</source> +<target>Упореди обе Ñтране</target> + +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>%x фолдер</pluralform> +<pluralform>%x фолдера</pluralform> +<pluralform>%x фолдера</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>%x датотека</pluralform> +<pluralform>%x датотеке</pluralform> +<pluralform>%x датотека</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%y од %x реда у приказу</pluralform> +<pluralform>%y од %x реда у приказу</pluralform> +<pluralform>%y од %x редова у приказу</pluralform> +</target> + +<source>Set direction:</source> +<target>Одабери Ñмер:</target> + +<source>Exclude temporarily</source> +<target>Тренутно иÑкључи</target> + +<source>Include temporarily</source> +<target>Тренутно укључи</target> + +<source>Exclude via filter:</source> +<target>ИÑкључи преко филтера:</target> + +<source><multiple selection></source> +<target><вишеÑтруки одабир></target> + +<source>Delete</source> +<target>Избриши</target> + +<source>Include all</source> +<target>Укључи Ñве</target> + +<source>Exclude all</source> +<target>ИÑкључи Ñве</target> + +<source>Show icons:</source> +<target>Прикажи иконе:</target> + +<source>Small</source> +<target>Мале</target> + +<source>Medium</source> +<target>Средње</target> + +<source>Large</source> +<target>Велике</target> + +<source>Select time span...</source> +<target>Одаберите временÑки раÑпон...</target> + +<source>Default view</source> +<target>Подразумевани приказ</target> + +<source>Show "%x"</source> +<target>Прикажи "%x"</target> + +<source><Last session></source> +<target><Задња ÑеÑија></target> + +<source>Folder Comparison and Synchronization</source> +<target>Упоређивање и Ñинхронизација фолдера</target> + +<source>Configuration saved!</source> +<target>Подешавања Ñачувана!</target> + +<source>FreeFileSync batch</source> +<target>FreeFileSync беч задатак</target> + +<source>Do you want to save changes to %x?</source> +<target>Да ли желите Ñачувати промене за %x?</target> + +<source>Do&n't save</source> +<target>Ðе&мој Ñачувати</target> + +<source>Never save changes</source> +<target>Ðемој никад Ñачувати промене</target> + +<source>Configuration loaded!</source> +<target>Подешавања учитана!</target> + +<source>Show files that exist on left side only</source> +<target>Прикажи датотеке које поÑтоје Ñамо на левој Ñтрани</target> + +<source>Show files that exist on right side only</source> +<target>Прикажи датотеке које поÑтоје Ñамо на деÑној Ñтрани</target> + +<source>Show files that are newer on left</source> +<target>Прикажи датотеке које Ñу новије лево</target> + +<source>Show files that are newer on right</source> +<target>Прикажи датотеке које Ñу новије деÑно</target> + +<source>Show files that are equal</source> +<target>Прикажи једнаке датотеке</target> + +<source>Show files that are different</source> +<target>Прикажи датотеке које Ñу различите</target> + +<source>Show conflicts</source> +<target>Прикажи конфликте</target> + +<source>Show files that will be created on the left side</source> +<target>Прикажи датотеке које ће бити креиране на левој Ñтрани</target> + +<source>Show files that will be created on the right side</source> +<target>Прикажи датотеке које ће бити креиране на деÑној Ñтрани</target> + +<source>Show files that will be deleted on the left side</source> +<target>Прикажи датотеке које ће бити избриÑане на левој Ñтрани</target> + +<source>Show files that will be deleted on the right side</source> +<target>Прикажи датотеке које ће бити избриÑане на деÑној Ñтрани</target> + +<source>Show files that will be overwritten on left side</source> +<target>Прикажи датотеке које ће бити замењене на левој Ñтрани</target> + +<source>Show files that will be overwritten on right side</source> +<target>Прикажи датотеке које ће бити замењене на деÑној Ñтрани</target> + +<source>Show files that won't be copied</source> +<target>Прикажи датотеке које неће бити копиране</target> + +<source>Set as default</source> +<target>ПоÑтави као подразумевано</target> + +<source>Operation aborted!</source> +<target>Операција отказана!</target> + +<source>All folders are in sync!</source> +<target>Сви фолдери Ñу Ñинхронизовани!</target> + +<source>Comma separated list</source> +<target>Зарезом одвојене лиÑте</target> + +<source>File list exported!</source> +<target>ЛиÑта датотека екÑпортована!</target> + +<source>Searching for program updates...</source> +<target>Претражујем ажурирање за програм...</target> + +<source>Ignore further errors</source> +<target>Занемари даље грешке</target> + +<source>&Ignore</source> +<target>&Игнориши</target> + +<source>Don't show this warning again</source> +<target>Ðе приказуј ово упозорење поновно</target> + +<source>&Switch</source> +<target>&Замени</target> + +<source>Question</source> +<target>Питање</target> + +<source>&Yes</source> +<target>&Да</target> + +<source>&No</source> +<target>&Ðе</target> + +<source>Scanning...</source> +<target>Прегледавање...</target> + +<source>Comparing content...</source> +<target>Упоређујем Ñадржај...</target> + +<source>Paused</source> +<target>Паузирано</target> + +<source>Initializing...</source> +<target>Покретање...</target> + +<source>Aborted</source> +<target>Прекинуто</target> + +<source>Completed</source> +<target>Завршено</target> + +<source>Continue</source> +<target>ÐаÑтави</target> + +<source>Pause</source> +<target>Пауза</target> + +<source>Logging</source> +<target>Лог запиÑивање</target> + +<source>Cannot find %x</source> +<target>Ðемогу пронаћи %x</target> + +<source>Inactive</source> +<target>Ðеактивно</target> + +<source>Today</source> +<target>ДанаÑ</target> + +<source>This week</source> +<target>Ове недеље</target> + +<source>This month</source> +<target>Овог меÑеца</target> + +<source>This year</source> +<target>Ове године</target> + +<source>Last x days</source> +<target>Задњих x дана</target> + +<source>Byte</source> +<target>Бајт</target> + +<source>KB</source> +<target>KB</target> + +<source>MB</source> +<target>MB</target> + +<source>Filter</source> +<target>Филтрирање</target> + +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Да ли Ñтварно желите да премеÑтите Ñледећу %x Ñтавку у Корпи за Ñмеће?</pluralform> +<pluralform>Да ли Ñтварно желите да премеÑтите Ñледеће %x Ñтавке у Корпи за Ñмеће?</pluralform> +<pluralform>Да ли Ñтварно желите да премеÑтите Ñледећих %x Ñтавки у Корпи за Ñмеће?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Да ли Ñтварно желите да обришете Ñледећу %x Ñтавку?</pluralform> +<pluralform>Да ли Ñтварно желите да обришете Ñледеће %x Ñтавке?</pluralform> +<pluralform>Да ли Ñтварно желите да обришете Ñледећих %x Ñтавки?</pluralform> +</target> + +<source>Direct</source> +<target>ÐепоÑредно</target> + +<source>Follow</source> +<target>Следи</target> + +<source>Copy NTFS permissions</source> +<target>Копирај NTFS овлашћења</target> + +<source>Integrate external applications into context menu. The following macros are available:</source> +<target>Интегриши Ñпољне апликације у контекÑтни мени. Следећи макрои Ñу доÑтупни:</target> + +<source>- full file or folder name</source> +<target>- цело име датотеке или фолдера</target> + +<source>- folder part only</source> +<target>- Ñамо фолдерÑки део</target> + +<source>- Other side's counterpart to %item_path%</source> +<target>- Дупликат Ñ Ð´Ñ€ÑƒÐ³Ðµ Ñтране у %item_path%</target> + +<source>- Other side's counterpart to %item_folder%</source> +<target>- Дупликат Ñ Ð´Ñ€ÑƒÐ³Ðµ Ñтране у %item_folder%</target> + +<source>Make hidden warnings and dialogs visible again?</source> +<target>Врати Ñкривена упозорења и диалоге поновно видљивим?</target> + +<source>Leave as unresolved conflict</source> +<target>ОÑтави као неразрешени конфликт</target> + +<source>Replace</source> +<target>Замени</target> + +<source>Move files and replace if existing</source> +<target>ПремеÑти датотеке и замени их ако већ поÑтоје</target> + +<source>Time stamp</source> +<target>ВременÑка ознака</target> + +<source>Append a timestamp to each file name</source> +<target>Додај временÑку ознаку Ñваком имену датотеке</target> + +<source>Folder</source> +<target>Фолдер</target> + +<source>File</source> +<target>Датотека</target> + +<source>YYYY-MM-DD hhmmss</source> +<target>ГГГГ-ММ-ДД ччммÑÑ</target> + +<source>Files</source> +<target>Датотеке</target> + +<source>Items</source> +<target>Ставке</target> + +<source>Percentage</source> +<target>Проценат</target> + +<source>Cannot monitor directory %x.</source> +<target>Ðе могу надгледати фолдер %x.</target> + +<source>Conversion error:</source> +<target>Грешка при претварању:</target> + +<source>Cannot delete file %x.</source> +<target>Ðе могу избриÑати датотеку %x.</target> + +<source>The file is locked by another process:</source> +<target>Датотека је блокирана другим процеÑом:</target> + +<source>Cannot move file %x to %y.</source> +<target>Ðе могу премеÑтити датотеку %x у %y.</target> + +<source>Cannot delete directory %x.</source> +<target>Ðе могу избриÑати фолдер %x.</target> + +<source>Cannot write file attributes of %x.</source> +<target>Ðе могу упиÑати ÑвојÑтва од %x.</target> + +<source>Cannot write modification time of %x.</source> +<target>Ðе могу упиÑати време промене %x.</target> + +<source>Cannot read security context of %x.</source> +<target>Ðе могу читати безбедоноÑни Ñадржај %x.</target> + +<source>Cannot write security context of %x.</source> +<target>Ðе могу упиÑати безбедоноÑни Ñадржај %x.</target> + +<source>Cannot read permissions of %x.</source> +<target>Ðе могу читати овлашћења од %x.</target> + +<source>Cannot write permissions of %x.</source> +<target>Ðе могу упиÑати овлашћења за %x.</target> + +<source>Cannot create directory %x.</source> +<target>Ðе могу креирати фолдер %x.</target> + +<source>Cannot create symbolic link %x.</source> +<target></target> + +<source>Cannot copy file %x to %y.</source> +<target>Ðе могу копирати датотеку %x на %y.</target> + +<source>Type of item %x is not supported:</source> +<target>Тип Ñтавке %x није подржан:</target> + +<source>Cannot open directory %x.</source> +<target>Ðе могу отворити фолдер %x.</target> + +<source>Cannot enumerate directory %x.</source> +<target>Ðе могу излиÑтати фолдер %x.</target> + +<source>Detected endless directory recursion.</source> +<target>Детектована неограничена фолдер рекурзија</target> + +<source>%x TB</source> +<target>%x TB</target> + +<source>%x PB</source> +<target>%x PB</target> + +<source> +<pluralform>1 min</pluralform> +<pluralform>%x min</pluralform> +</source> +<target> +<pluralform>%x мин</pluralform> +<pluralform>%x мин</pluralform> +<pluralform>%x мин</pluralform> +</target> + +<source> +<pluralform>1 hour</pluralform> +<pluralform>%x hours</pluralform> +</source> +<target> +<pluralform>%x Ñат</pluralform> +<pluralform>%x Ñата</pluralform> +<pluralform>%x Ñати</pluralform> +</target> + +<source> +<pluralform>1 day</pluralform> +<pluralform>%x days</pluralform> +</source> +<target> +<pluralform>%x дан</pluralform> +<pluralform>%x дана</pluralform> +<pluralform>%x дана</pluralform> +</target> + +<source>Cannot set privilege %x.</source> +<target>Ðе могу поÑтавити права за %x.</target> + +<source>Failed to suspend system sleep mode.</source> +<target>ÐеуÑпео покушај ÑуÑпендовања мода Ñпавања рачунара</target> + +<source>Cannot change process I/O priorities.</source> +<target>Ðе може Ñе променити Ð¿Ñ€Ð¾Ñ†ÐµÑ I/O приоритета</target> + +<source>Unable to move %x to the Recycle Bin!</source> +<target>Ðије могуће пребацити %x у Корпи за Ñмеће</target> + +<source>Both sides have changed since last synchronization!</source> +<target>Обе Ñу Ñтране промењене од поÑледње Ñинхронизације!</target> + +<source>Cannot determine sync-direction:</source> +<target>Ðе могу одредити Ñмер Ñинхронизације:</target> + +<source>No change since last synchronization!</source> +<target>Ðема промена од задње Ñинхронизације!</target> + +<source>The corresponding database entries are not in sync considering current settings.</source> +<target>Споменути уноÑи базе података ниÑу у Ñинхронизацији у одноÑу на тренутна подешавања.</target> + +<source>Setting default synchronization directions: Old files will be overwritten with newer files.</source> +<target>ПоÑтављам подразумеване ÑинхронизацијÑке Ñмерове: Старе датотеке биће замењене новијим датотекама.</target> + +<source>Checking recycle bin availability for folder %x...</source> +<target>Проверавам доÑтупноÑÑ‚ Корпе за Ñмеће за фолдер %x...</target> + +<source>Moving file %x to recycle bin</source> +<target>Премештам датотеку %x у Корпи за Ñмеће</target> + +<source>Moving folder %x to recycle bin</source> +<target>Премештам фолдер %x у Корпи за Ñмеће</target> + +<source>Moving symbolic link %x to recycle bin</source> +<target>Премештам Ñимболичну везу %x у Корпи за Ñмеће</target> + +<source>Deleting file %x</source> +<target>БриÑање датотеке %x</target> + +<source>Deleting folder %x</source> +<target>БриÑање фолдера %x</target> + +<source>Deleting symbolic link %x</source> +<target>БриÑање Ñимболичне везе %x</target> + +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target>Корпа за Ñмеће није доÑтупна за наведене путање! УмеÑто тога датотеке ће бити трајно обриÑане:</target> + +<source>The corresponding folder will be considered as empty.</source> +<target>Одговарајући фолдер Ñматраће Ñе празним.</target> + +<source>Cannot find the following folders:</source> +<target>Ðе могу пронаћи Ñледеће фолдере:</target> + +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>Можете игнориÑати ову грешку да би Ñматрали Ñваки фолдер празним. Фолдери ће бити креирани аутоматÑки током Ñинхронизације.</target> + +<source>Directories are dependent! Be careful when setting up synchronization rules:</source> +<target>Фолдери Ñу завиÑни! Будите опрезни при поÑтављању Ñинхронизационих правила:</target> + +<source>Conflict detected:</source> +<target>Конфликт уочен:</target> + +<source>File %x has an invalid date!</source> +<target>Датотека %x има невалидни датум!</target> + +<source>Files %x have the same date but a different size!</source> +<target>Датотеке %x имају иÑти датум али другачију величину!</target> + +<source>Items differ in attributes only</source> +<target>Ставке Ñе разликују Ñамо у атрибутима</target> + +<source>Resolving symbolic link %x</source> +<target></target> + +<source>Comparing content of files %x</source> +<target>Упоређујем Ñадржај датотека %x</target> + +<source>Generating file list...</source> +<target>Стварам лиÑту датотека...</target> + +<source>Start comparison</source> +<target>Почни упоређивање</target> + +<source>Calculating sync directions...</source> +<target>Одређујем Ñмерове Ñинхронизације...</target> + +<source>Both sides are equal</source> +<target>Обе Ñтране Ñу једнаке</target> + +<source>Copy new item to left</source> +<target>Копирај нову Ñтавку лево</target> + +<source>Copy new item to right</source> +<target>Копирај нову Ñтавку деÑно</target> + +<source>Delete left item</source> +<target>Избриши леву Ñтавку</target> + +<source>Delete right item</source> +<target>Избриши деÑну Ñтавку</target> + +<source>Move file on left</source> +<target>ПремеÑти датотеку лево</target> + +<source>Move file on right</source> +<target>ПремеÑти датотеку деÑно</target> + +<source>Overwrite left item</source> +<target>Замени леву Ñтавку</target> + +<source>Overwrite right item</source> +<target>Замени деÑну Ñтавку</target> + +<source>Do nothing</source> +<target>Ðе ради ништа</target> + +<source>Update attributes on left</source> +<target>ОÑвежи атрибуте лево</target> + +<source>Update attributes on right</source> +<target>ОÑвежи атрибуте деÑно</target> + +<source>Multiple...</source> +<target>МногоÑтруко...</target> + +<source>Moving file %x to %y</source> +<target>Премештам датотеку %x у %y</target> + +<source>Moving folder %x to %y</source> +<target>Премештам фолдер %x у %y</target> + +<source>Moving symbolic link %x to %y</source> +<target>Премештам Ñимболичну везу %x у %y</target> + +<source>Removing old versions...</source> +<target>Уклањам Ñтарије верзије...</target> + +<source>Creating symbolic link %x</source> +<target>Креирам Ñимболичну везу %x</target> + +<source>Creating folder %x</source> +<target>Креирам фолдер %x</target> + +<source>Overwriting file %x</source> +<target>Замењујем датотеку %x</target> + +<source>Overwriting symbolic link %x</source> +<target>Замењујем Ñимболичну везу %x</target> + +<source>Verifying file %x</source> +<target>Проверавам датотеку %x</target> + +<source>Updating attributes of %x</source> +<target>Обнављам атрибуте од %x</target> + +<source>Cannot find %x.</source> +<target>Ðе могу пронаћи %x.</target> + +<source>Target folder %x already existing.</source> +<target>Одредишни фолдер %x већ поÑтоји.</target> + +<source>Target folder input field must not be empty.</source> +<target>Поље за одабир одредишног фолдера не може бити празно.</target> + +<source>Folder input field for versioning must not be empty.</source> +<target>Поље за одабир фолдера за верзионирање не може бити празно.</target> + +<source>Source folder %x not found.</source> +<target>Изворни фолдер %x није пронађен.</target> + +<source>The following items have unresolved conflicts and will not be synchronized:</source> +<target>Следеће Ñтавке имају неразрешених конфликата и неће бити Ñинхронизоване:</target> + +<source>Significant difference detected:</source> +<target>Значајна разлика опажена:</target> + +<source>More than 50% of the total number of files will be copied or deleted!</source> +<target>Више од 50% од укупног броја датотека ће бити копирано или избриÑано!</target> + +<source>Not enough free disk space available in:</source> +<target>Ðедовољно проÑтора на диÑку у:</target> + +<source>Required:</source> +<target>Потребно:</target> + +<source>Available:</source> +<target>ДоÑтупно:</target> + +<source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> +<target>Фолдер ће бити промењен који је део више парова фолдера. Молимо проверите ÑинхронизацијÑка подешавања.</target> + +<source>Synchronizing folder pair:</source> +<target>Синхронизовање фолдерÑког пара:</target> + +<source>Generating database...</source> +<target>ГенериÑање базе података...</target> + +<source>Creating Volume Shadow Copy for %x...</source> +<target>Креирање Volume Shadow Copy за %x...</target> + +<source>Data verification error: Source and target file have different content!</source> +<target>Грешка при проверавању података: Изворна и циљна датотека имају различите Ñадржаје!</target> + diff --git a/BUILD/Languages/slovenian.lng b/BUILD/Languages/slovenian.lng index 9a6ce1cf..a5973b7c 100644 --- a/BUILD/Languages/slovenian.lng +++ b/BUILD/Languages/slovenian.lng @@ -20,7 +20,7 @@ <target>Celoten Äas:</target> <source>Cannot set directory lock for %x.</source> -<target></target> +<target>Ne morem nastaviti zaklepanja imenikov za %x.</target> <source>Show in Explorer</source> <target>Prikaži v Raziskovalcu</target> @@ -44,7 +44,7 @@ <target>Napaka</target> <source>Selected variant:</source> -<target></target> +<target>Izbrana varianta:</target> <source>Select alternate comparison settings</source> <target>Izberite alternativne nastavitve primerjanja</target> @@ -68,7 +68,7 @@ <target>Kopiraj</target> <source>Paste</source> -<target></target> +<target>Prilepi</target> <source>Save as batch job</source> <target>Shrani kot paketno opravilo</target> @@ -110,20 +110,11 @@ <target>Usodna napaka</target> <source>Error Code %x:</source> -<target></target> +<target>Koda napake %x:</target> <source>Cannot resolve symbolic link %x.</source> <target>Ne morem razreÅ¡iti simboliÄne povezave %x.</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source> <pluralform>1 Byte</pluralform> <pluralform>%x Bytes</pluralform> @@ -135,6 +126,15 @@ <pluralform>%x Bajtov</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>Datoteka podatkovne baze %x je nekompatibilna.</target> @@ -171,9 +171,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>ÄŒakam, medtem ko se zaklepa imenik (%x)...</target> -<source>Creating file %x</source> -<target>Ustvarjam datoteko %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -185,15 +182,15 @@ <pluralform>%x sek</pluralform> </target> +<source>Creating file %x</source> +<target>Ustvarjam datoteko %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Napaka pri razÄlenjevanju datoteke %x, vrstica %y, stolpec %z.</target> <source>Scanning:</source> <target>Pregledujem:</target> -<source>Encoding extended time information: %x</source> -<target>Podrobne informacije o Äasu enkodiranja: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -205,6 +202,9 @@ <pluralform>[%x niti]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>Podrobne informacije o Äasu enkodiranja: %x</target> + <source>/sec</source> <target>/sek</target> @@ -469,13 +469,13 @@ Ukaz se sproži Äe: <target>&Napredno</target> <source>&Check now</source> -<target></target> +<target>P&reveri zdaj</target> <source>Check &automatically once a week</source> -<target></target> +<target>S&amodejno preveri enkrat tedensko</target> <source>Check for new version</source> -<target></target> +<target>Preveri za novo razliÄico</target> <source>Compare</source> <target>Primerjaj</target> @@ -493,7 +493,7 @@ Ukaz se sproži Äe: <target>Zamenjaj strani</target> <source>Hide excluded items</source> -<target>Skrij izkljuÄene predmete</target> +<target>Skrij izkljuÄene elemente</target> <source>Show filtered or temporarily excluded files</source> <target>Pokaži filtrirane ali zaÄasno izkljuÄene datoteke</target> @@ -529,7 +529,7 @@ Ukaz se sproži Äe: <target>Ob zakljuÄku</target> <source>Close</source> -<target></target> +<target>Zapri</target> <source>&Pause</source> <target>&Premor</target> @@ -622,7 +622,7 @@ enaka <target><- Obojesmerno -></target> <source>Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database.</source> -<target></target> +<target>Identificiraj in razÅ¡irjaj spremembe na obeh straneh. Izbrisi, premiki in spori so samodejno zaznani z uporabo podatkovne baze.</target> <source>Mirror ->></source> <target>Zrcalno ->></target> @@ -661,7 +661,7 @@ enaka <target>Ustvarjanje razliÄiÄ</target> <source>Move files to user-defined folder</source> -<target></target> +<target>Premakni datoteke v uporabniÅ¡ko-definirano mapo</target> <source>Naming convention:</source> <target>Konvencija poimenovanja:</target> @@ -745,7 +745,7 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <target>Maksimum</target> <source>&Clear</source> -<target></target> +<target>P&oÄisti</target> <source>Fail-safe file copy</source> <target>Kopiranje datotek varno pred odpovedjo</target> @@ -798,9 +798,6 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <source>&Find next</source> <target>&Najdi naslednje</target> -<source>Operation aborted!</source> -<target>Operacija prekinjena!</target> - <source>Main bar</source> <target>Glavna vrstica</target> @@ -820,7 +817,7 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <target>Izberite pogled</target> <source>Open...</source> -<target></target> +<target>Odpri...</target> <source>Save</source> <target>Shrani</target> @@ -828,6 +825,39 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <source>Compare both sides</source> <target>Primerjaj obe strani</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>%x imenik</pluralform> +<pluralform>%x imenika</pluralform> +<pluralform>%x imeniki</pluralform> +<pluralform>%x imenikov</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>%x datoteka</pluralform> +<pluralform>%x datoteki</pluralform> +<pluralform>%x datoteke</pluralform> +<pluralform>%x datotek</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%y od %x vrstice prikazana</pluralform> +<pluralform>%y od %x vrstic prikazanih</pluralform> +<pluralform>%y od %x vrstic prikazanih</pluralform> +<pluralform>%y od %x vrstic prikazanih</pluralform> +</target> + <source>Set direction:</source> <target>Nastavi smer:</target> @@ -940,7 +970,10 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <target>Prikaži datoteke, ki ne bodo kopirane</target> <source>Set as default</source> -<target></target> +<target>Nastavi kot privzeto</target> + +<source>Operation aborted!</source> +<target>Operacija prekinjena!</target> <source>All folders are in sync!</source> <target>Vse mape so sinhronizirane!</target> @@ -955,40 +988,7 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <target>Seznam datotek je bil izvožen!</target> <source>Searching for program updates...</source> -<target></target> - -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>%x imenik</pluralform> -<pluralform>%x imenika</pluralform> -<pluralform>%x imeniki</pluralform> -<pluralform>%x imenikov</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>%x datoteka</pluralform> -<pluralform>%x datoteki</pluralform> -<pluralform>%x datoteke</pluralform> -<pluralform>%x datotek</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x od %y vrstice v prikazu</pluralform> -<pluralform>%x od %y vrstic v prikazu</pluralform> -<pluralform>%x od %y vrstic v prikazu</pluralform> -<pluralform>%x od %y vrstic v prikazu</pluralform> -</target> +<target>IÅ¡Äem posodobitve programa...</target> <source>Ignore further errors</source> <target>Ignoriraj nadaljnje napake</target> @@ -997,7 +997,7 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <target>&Ignoriraj</target> <source>Don't show this warning again</source> -<target></target> +<target>Ne prikaži veÄ tega opozorila</target> <source>&Switch</source> <target>&Preklopi</target> @@ -1071,6 +1071,28 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <source>Filter</source> <target>Filter</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Ali resniÄno želite premakniti sledeÄi %xelement v KoÅ¡?</pluralform> +<pluralform>Ali resniÄno želite premakniti naslednja %x elementa v KoÅ¡?</pluralform> +<pluralform>Ali resniÄno želite premakniti naslednje %x elemente v KoÅ¡?</pluralform> +<pluralform>Ali resniÄno želite premakniti naslednjih %x elementov v KoÅ¡?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Ali resniÄno želite izbrisati sledeÄi %x element?</pluralform> +<pluralform>Ali resniÄno želite izbrisati naslednja %x elementa?</pluralform> +<pluralform>Ali resniÄno želite izbrisati naslednje %x elemente?</pluralform> +<pluralform>Ali resniÄno želite izbrisati naslednjih %x elementov?</pluralform> +</target> + <source>Direct</source> <target>Neposredno</target> @@ -1098,23 +1120,11 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <source>Make hidden warnings and dialogs visible again?</source> <target>Ponovno prikažem vsa skrita obvestila in pogovore?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target></target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target></target> - <source>Leave as unresolved conflict</source> <target>Pusti kot nereÅ¡eni spor</target> <source>Time stamp</source> -<target></target> +<target>ÄŒasovna oznaka</target> <source>Append a timestamp to each file name</source> <target>Dodaj Äasovno oznako k vsakemu imenu datoteke</target> @@ -1137,6 +1147,9 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <source>Files</source> <target>Datoteke</target> +<source>Items</source> +<target>Elementi</target> + <source>Percentage</source> <target>Odstotek</target> @@ -1189,7 +1202,7 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <target>Ne morem kopirati datoteke %x v %y.</target> <source>Type of item %x is not supported:</source> -<target>Predmet tipa %x ni podprt:</target> +<target>Element tipa %x ni podprt:</target> <source>Cannot open directory %x.</source> <target>Ne morem odpreti imenika %x.</target> @@ -1206,9 +1219,6 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1246,10 +1256,10 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <target>Ne morem nastaviti privilegija %x.</target> <source>Failed to suspend system sleep mode.</source> -<target></target> +<target>NaÄin spanja ni uspelo prekiniti.</target> <source>Cannot change process I/O priorities.</source> -<target></target> +<target>Ne morem spremeniti V/I prioritet procesa.</target> <source>Unable to move %x to the Recycle Bin!</source> <target>Ne morem premakniti %x v KoÅ¡</target> @@ -1270,7 +1280,7 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <target>Nastavljanje privzetih smeri sinhronizacije: Stare datoteke bodo prepisane z novimi datotekami.</target> <source>Checking recycle bin availability for folder %x...</source> -<target></target> +<target>Preverjam razpoložljivost koÅ¡a za mapo %x...</target> <source>Moving file %x to recycle bin</source> <target>Premikam datoteko %x v koÅ¡</target> @@ -1294,13 +1304,13 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <target>KoÅ¡ ni na voljo za naslednje poti! Namesto tega bodo datoteke trajno izbrisane:</target> <source>The corresponding folder will be considered as empty.</source> -<target></target> +<target>UstrezajoÄa mapa bo smatrana kot prazna.</target> <source>Cannot find the following folders:</source> -<target></target> +<target>Ne morem najti naslednjih map:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target></target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>To napako, ki smatra vsako mapo kot prazno, lahko ignorirate. Mape bodo potem samodejno ustvarjene med sinhronizacijo.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Imeniki so v odvisnosti! Bodite pozorni, ko nastavljate sinhronizacijska pravila:</target> @@ -1309,7 +1319,7 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <target>ZaÄni primerjavo</target> <source>Calculating sync directions...</source> -<target></target> +<target>PreraÄunavam sinhronizacijske smeri...</target> <source>Conflict detected:</source> <target>Zaznan spor:</target> @@ -1405,7 +1415,7 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <target>Posodabljam atribute od %x</target> <source>Cannot find %x.</source> -<target></target> +<target>Ne morem najti %x.</target> <source>Target folder %x already existing.</source> <target>Ciljna mapa %x že obstaja.</target> @@ -1420,7 +1430,7 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <target>Izvorna mapa %x se ne najde.</target> <source>The following items have unresolved conflicts and will not be synchronized:</source> -<target>Naslednji objekti imajo nereÅ¡ene konflikte in ne bodo sinhronizirani:</target> +<target>Naslednji elementi imajo nereÅ¡ene konflikte in ne bodo sinhronizirani:</target> <source>Significant difference detected:</source> <target>Zaznana je pomembna razlika:</target> @@ -1440,12 +1450,6 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>Spremenjena bo mapa, ki je del veÄkratnih parov map. Prosim preglejte nastavitve sinhronizacije.</target> -<source>Left</source> -<target>Levo</target> - -<source>Right</source> -<target>Desno</target> - <source>Synchronizing folder pair:</source> <target>Sinhroniziram par map:</target> @@ -1453,7 +1457,7 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom! <target>Ustvarjam podatkovno bazo...</target> <source>Creating Volume Shadow Copy for %x...</source> -<target></target> +<target>Ustvarjam SenÄno kopijo nosilca za %x...</target> <source>Data verification error: Source and target file have different content!</source> <target>Napaka pri preverjanju podatkov: izvorna in ciljna datoteka imata razliÄno vsebino!</target> diff --git a/BUILD/Languages/spanish.lng b/BUILD/Languages/spanish.lng index 084477df..3a3df962 100644 --- a/BUILD/Languages/spanish.lng +++ b/BUILD/Languages/spanish.lng @@ -115,14 +115,11 @@ <source>Cannot resolve symbolic link %x.</source> <target>No se puede resolver el enlace simbólico %x.</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> +<source>Cannot determine final path for %x.</source> +<target></target> -<source>%x GB</source> -<target>%x GB</target> +<source>Cannot find system function %x.</source> +<target>No se puede encontrar la función del sistema %x.</target> <source> <pluralform>1 Byte</pluralform> @@ -133,6 +130,15 @@ <pluralform>%x Bytes</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>El archivo de base de datos %x es incompatible</target> @@ -169,9 +175,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>Esperando mientras el directorio se encuentre bloqueado (%x)...</target> -<source>Creating file %x</source> -<target>Creando archivo %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -181,15 +184,15 @@ <pluralform>%x segs</pluralform> </target> +<source>Creating file %x</source> +<target>Creando archivo %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Error analizando archivo %x, fila %y, columna %z.</target> <source>Scanning:</source> <target>Escanear:</target> -<source>Encoding extended time information: %x</source> -<target>Información temporal extendida de la codificación: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -199,6 +202,9 @@ <pluralform>[%x Hilos]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>Información temporal extendida de la codificación: %x</target> + <source>/sec</source> <target>/seg</target> @@ -411,12 +417,12 @@ El comando es disparado si:: <source>Date:</source> <target>Fecha:</target> -<source>Action</source> -<target>Acción</target> - <source>Category</source> <target>CategorÃa</target> +<source>Action</source> +<target>Acción</target> + <source>Drag && drop</source> <target>Arrastrar y soltar</target> @@ -792,12 +798,6 @@ Aviso: ¡Los nombres de archivos deben ser relativos a los directorios base! <source>&Find next</source> <target>&Buscar siguiente</target> -<source>Operation aborted!</source> -<target>¡Operación abortada!</target> - -<source>Main bar</source> -<target>Barra principal</target> - <source>Folder pairs</source> <target>Pares de carpetas</target> @@ -807,6 +807,9 @@ Aviso: ¡Los nombres de archivos deben ser relativos a los directorios base! <source>Configuration</source> <target>Configuración</target> +<source>Main bar</source> +<target>Barra principal</target> + <source>Filter files</source> <target>Filtrar archivos</target> @@ -822,6 +825,30 @@ Aviso: ¡Los nombres de archivos deben ser relativos a los directorios base! <source>Compare both sides</source> <target>Comparar ambos lados</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>1 directorio</pluralform> +<pluralform>%x directorios</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>1 archivo</pluralform> +<pluralform>%x archivos</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target></target> + <source>Set direction:</source> <target>Indicar dirección:</target> @@ -936,48 +963,21 @@ Aviso: ¡Los nombres de archivos deben ser relativos a los directorios base! <source>Set as default</source> <target>Establecer por defecto</target> +<source>Operation aborted!</source> +<target>¡Operación abortada!</target> + <source>All folders are in sync!</source> <target>¡Todas las carpetas están sincronizadas!</target> <source>Comma separated list</source> <target>Lista separada por comas</target> -<source>Legend</source> -<target>Leyenda</target> - <source>File list exported!</source> <target>¡Lista de archivos exportada!</target> <source>Searching for program updates...</source> <target>Buscando actualizaciones del programa...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>1 directorio</pluralform> -<pluralform>%x directorios</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>1 archivo</pluralform> -<pluralform>%x archivos</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x de una fila en vista</pluralform> -<pluralform>%x de %y filas en vista</pluralform> -</target> - <source>Ignore further errors</source> <target>Ignorar más errores</target> @@ -1059,6 +1059,21 @@ Aviso: ¡Los nombres de archivos deben ser relativos a los directorios base! <source>Filter</source> <target>Filtro</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>¿De verdad quiere mover el siguiente objecto a la papelera de reciclaje?</pluralform> +<pluralform>¿De verdad quiere mover los siguientes objectos %x a la papelera de reciclaje?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target></target> + <source>Direct</source> <target>Enviar</target> @@ -1086,39 +1101,21 @@ Aviso: ¡Los nombres de archivos deben ser relativos a los directorios base! <source>Make hidden warnings and dialogs visible again?</source> <target>¿Hacer visibles de nuevo los mensajes de alerta y diálogo?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>¿De verdad quiere mover el siguiente objecto a la papelera de reciclaje?</pluralform> -<pluralform>¿De verdad quiere mover los siguientes objectos %x a la papelera de reciclaje?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>¿De verdad quiere eliminar el siguiente objeto?</pluralform> -<pluralform>¿De verdad quiere eliminar los siguientes objetos?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>Dejar como conflicto sin resolver</target> -<source>Time stamp</source> -<target>Intervalo de tiempo</target> - -<source>Append a timestamp to each file name</source> -<target>Incluir fecha y hora a cada nombre de archivo</target> - <source>Replace</source> <target>Reemplazar</target> <source>Move files and replace if existing</source> <target>Mover archivos y reemplazar si ya existen</target> +<source>Time stamp</source> +<target>Intervalo de tiempo</target> + +<source>Append a timestamp to each file name</source> +<target>Incluir fecha y hora a cada nombre de archivo</target> + <source>Folder</source> <target>Carpeta</target> @@ -1161,9 +1158,6 @@ Aviso: ¡Los nombres de archivos deben ser relativos a los directorios base! <source>Cannot write modification time of %x.</source> <target>No se puede escribir el tiempo de modificación de %x.</target> -<source>Cannot find system function %x.</source> -<target>No se puede encontrar la función del sistema %x.</target> - <source>Cannot read security context of %x.</source> <target>No se puede leer el contexto de seguridad de %x.</target> @@ -1179,8 +1173,8 @@ Aviso: ¡Los nombres de archivos deben ser relativos a los directorios base! <source>Cannot create directory %x.</source> <target>No se puede crear el directorio %x.</target> -<source>Cannot copy symbolic link %x to %y.</source> -<target>No se puede copiar el enlance simbólico %x a %y.</target> +<source>Cannot create symbolic link %x.</source> +<target></target> <source>Cannot copy file %x to %y.</source> <target>No se puede copiar el archivo %x a %y.</target> @@ -1203,9 +1197,6 @@ Aviso: ¡Los nombres de archivos deben ser relativos a los directorios base! <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1290,18 +1281,12 @@ Aviso: ¡Los nombres de archivos deben ser relativos a los directorios base! <source>Cannot find the following folders:</source> <target>No se pudieron encontrar las siguiente carpetas:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>Puede ignorar este error si considera la carpeta como vacia.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target></target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>¡Los directorios son dependientes! Tenga cuidado al establecer las reglas de sincronización:</target> -<source>Start comparison</source> -<target>Empezar comparación</target> - -<source>Calculating sync directions...</source> -<target>Calculando direcciones de sincronización...</target> - <source>Conflict detected:</source> <target>Conflicto detectado:</target> @@ -1314,18 +1299,21 @@ Aviso: ¡Los nombres de archivos deben ser relativos a los directorios base! <source>Items differ in attributes only</source> <target>Los elementos sólo se diferencian en los atributos</target> -<source>Symbolic links %x have the same date but a different target.</source> -<target>Los enlaces simbólicos %x tienen la misma fecha pero un destino diferente.</target> +<source>Resolving symbolic link %x</source> +<target></target> <source>Comparing content of files %x</source> <target>Comparación del contenido de los archivos %x</target> -<source>Comparing files by content failed.</source> -<target>La comparación de archivos por el contenido ha fallado.</target> - <source>Generating file list...</source> <target>Generando lista de archivos...</target> +<source>Start comparison</source> +<target>Empezar comparación</target> + +<source>Calculating sync directions...</source> +<target>Calculando direcciones de sincronización...</target> + <source>Both sides are equal</source> <target>Ambos lados son iguales</target> @@ -1431,12 +1419,6 @@ Aviso: ¡Los nombres de archivos deben ser relativos a los directorios base! <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>Una carpeta, que es parte de múltiple pares de carpetas, será modificada. Por favor, revise las opciones de sincronización.</target> -<source>Left</source> -<target>Izquierda</target> - -<source>Right</source> -<target>Derecha</target> - <source>Synchronizing folder pair:</source> <target>Sincronizando par de carpetas:</target> diff --git a/BUILD/Languages/swedish.lng b/BUILD/Languages/swedish.lng index 8e3a32a7..0b1e2fe3 100644 --- a/BUILD/Languages/swedish.lng +++ b/BUILD/Languages/swedish.lng @@ -115,15 +115,6 @@ <source>Cannot resolve symbolic link %x.</source> <target>Den symboliska länken %x kan inte matchas.</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source> <pluralform>1 Byte</pluralform> <pluralform>%x Bytes</pluralform> @@ -133,6 +124,15 @@ <pluralform>%x Byte</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>Databasfilen %x är inkompatibel</target> @@ -169,9 +169,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>Väntar medan mappen lÃ¥ses (%x)...</target> -<source>Creating file %x</source> -<target>Skapar fil %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -181,15 +178,15 @@ <pluralform>%x sek</pluralform> </target> +<source>Creating file %x</source> +<target>Skapar fil %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Tolkningsfel pÃ¥ filen %x, rad %y, kolumn %z.</target> <source>Scanning:</source> <target>Skannar:</target> -<source>Encoding extended time information: %x</source> -<target>Kodar utökad tidsinformation: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -199,6 +196,9 @@ <pluralform>[%x trÃ¥dar]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>Kodar utökad tidsinformation: %x</target> + <source>/sec</source> <target>/s</target> @@ -822,6 +822,33 @@ OBS! Filnamnen mÃ¥ste anges relativt till basmappar <source>Compare both sides</source> <target>Jämför bÃ¥da sidor</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>1 mapp</pluralform> +<pluralform>%x mappar</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>1 fil</pluralform> +<pluralform>%x filer</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%y av 1 rad i vyn</pluralform> +<pluralform>%y av %x rader i vyn</pluralform> +</target> + <source>Set direction:</source> <target>Ange riktning:</target> @@ -951,33 +978,6 @@ OBS! Filnamnen mÃ¥ste anges relativt till basmappar <source>Searching for program updates...</source> <target>Söker efter programuppdateringar...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>1 mapp</pluralform> -<pluralform>%x mappar</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>1 fil</pluralform> -<pluralform>%x filer</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x av 1 rad i vyn</pluralform> -<pluralform>%x av %y rader i vyn</pluralform> -</target> - <source>Ignore further errors</source> <target>Ignorera ytterligare fel</target> @@ -1059,6 +1059,24 @@ OBS! Filnamnen mÃ¥ste anges relativt till basmappar <source>Filter</source> <target>Filter</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Vill du verkligen flytta följande objekt till papperskorgen?</pluralform> +<pluralform>Vill du verkligen flytta följande %x objekt till papperskorgen?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Vill du verkligen ta bort följande objekt?</pluralform> +<pluralform>Vill du verkligen ta bort följande %x objekt?</pluralform> +</target> + <source>Direct</source> <target>Direkt</target> @@ -1086,24 +1104,6 @@ OBS! Filnamnen mÃ¥ste anges relativt till basmappar <source>Make hidden warnings and dialogs visible again?</source> <target>Vill du göra dolda varningar och dialoger, synliga igen?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>Vill du verkligen flytta följande objekt till papperskorgen?</pluralform> -<pluralform>Vill du verkligen flytta följande %x objekt till papperskorgen?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>Vill du verkligen ta bort följande objekt?</pluralform> -<pluralform>Vill du verkligen ta bort följande %x objekt?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>Ignorera konflikt</target> @@ -1131,6 +1131,9 @@ OBS! Filnamnen mÃ¥ste anges relativt till basmappar <source>Files</source> <target>Filer</target> +<source>Items</source> +<target>Objekt</target> + <source>Percentage</source> <target>Procent</target> @@ -1200,9 +1203,6 @@ OBS! Filnamnen mÃ¥ste anges relativt till basmappar <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1287,8 +1287,8 @@ OBS! Filnamnen mÃ¥ste anges relativt till basmappar <source>Cannot find the following folders:</source> <target>Kan inte hitta följande mappar:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>Du kan ignorera det här felet och betrakta varje mappa som tom.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>Du kan bortse frÃ¥n detta fel, och betrakta varje mapp som tom. Mapparna kommer dÃ¥ att skapas automatiskt, under synkroniseringen</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Mappar är beroende! Var försiktig när du sätter upp synkroniseringsregler:</target> diff --git a/BUILD/Languages/turkish.lng b/BUILD/Languages/turkish.lng index f3c87e86..52f44188 100644 --- a/BUILD/Languages/turkish.lng +++ b/BUILD/Languages/turkish.lng @@ -8,7 +8,7 @@ </header> <source>Searching for folder %x...</source> -<target>%s klasörü aranıyor...</target> +<target>%x klasörü aranıyor...</target> <source>Items processed:</source> <target>Ä°ÅŸlenen öge:</target> @@ -115,15 +115,6 @@ <source>Cannot resolve symbolic link %x.</source> <target>%x simge baÄŸlantısı çözümlenemedi</target> -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source> <pluralform>1 Byte</pluralform> <pluralform>%x Bytes</pluralform> @@ -133,6 +124,15 @@ <pluralform>%x Bayt</pluralform> </target> +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Database file %x is incompatible.</source> <target>%x veritabanı dosyası uyumsuz</target> @@ -169,9 +169,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>Klasör kilitli olduÄŸundan bekleniyor (%x)...</target> -<source>Creating file %x</source> -<target>%x dosyası ekleniyor</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -181,15 +178,15 @@ <pluralform>%x saniye</pluralform> </target> +<source>Creating file %x</source> +<target>%x dosyası ekleniyor</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>%x dosyası iÅŸlenirken hata, satır %y, sütun %z.</target> <source>Scanning:</source> <target>Taranıyor:</target> -<source>Encoding extended time information: %x</source> -<target>Uzatılmış zaman bilgisi kodlanıyor: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -199,6 +196,9 @@ <pluralform>[%x Ä°ÅŸ]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>Uzatılmış zaman bilgisi kodlanıyor: %x</target> + <source>/sec</source> <target>/saniye</target> @@ -822,6 +822,33 @@ Not: Dosya adları kök klasörlere göre bağıl olmalıdır! <source>Compare both sides</source> <target>Ä°ki tarafı karşılaÅŸtırır</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>1 klasör</pluralform> +<pluralform>%x klasör</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>1 dosya</pluralform> +<pluralform>%x dosya</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%y / 1 satır görüntüleniyor</pluralform> +<pluralform>%y / %x satır görüntüleniyor</pluralform> +</target> + <source>Set direction:</source> <target>Yönü seçin:</target> @@ -951,33 +978,6 @@ Not: Dosya adları kök klasörlere göre bağıl olmalıdır! <source>Searching for program updates...</source> <target>Yazılım güncellemesine bakılıyor...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>1 klasör</pluralform> -<pluralform>%x klasör</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>1 dosya</pluralform> -<pluralform>%x dosya</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x / 1 satır görüntüleniyor</pluralform> -<pluralform>%x / %y satır görüntüleniyor</pluralform> -</target> - <source>Ignore further errors</source> <target>Sonraki hatalar yoksayılsın</target> @@ -1059,6 +1059,24 @@ Not: Dosya adları kök klasörlere göre bağıl olmalıdır! <source>Filter</source> <target>Süzgeç</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>AÅŸağıdaki 1 ögeyi Geri Dönüşüm Kutusuna göndermek istediÄŸinize emin misiniz?</pluralform> +<pluralform>AÅŸağıdaki %x ögeyi Geri Dönüşüm Kutusuna göndermek istediÄŸinize emin misiniz?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>AÅŸağıdaki 1 ögeyi silmek istediÄŸinize emin misiniz?</pluralform> +<pluralform>AÅŸağıdaki %x ögeyi silmek istediÄŸinize emin misiniz?</pluralform> +</target> + <source>Direct</source> <target>DoÄŸrudan katılsın</target> @@ -1086,24 +1104,6 @@ Not: Dosya adları kök klasörlere göre bağıl olmalıdır! <source>Make hidden warnings and dialogs visible again?</source> <target>GizlenmiÅŸ iletiler yeniden görüntülensin mi?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>AÅŸağıdaki ögeyi Geri Dönüşüm Kutusuna göndermek istediÄŸinize emin misiniz?</pluralform> -<pluralform>AÅŸağıdaki ögeleri Geri Dönüşüm Kutusuna göndermek istediÄŸinize emin misiniz?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>AÅŸağıdaki ögeyi silmek istediÄŸinize emin misiniz?</pluralform> -<pluralform>AÅŸağıdaki ögeleri silmek istediÄŸinize emin misiniz?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>UyuÅŸmazlık çözülmeden bırakılsın</target> @@ -1131,6 +1131,9 @@ Not: Dosya adları kök klasörlere göre bağıl olmalıdır! <source>Files</source> <target>Dosyalar</target> +<source>Items</source> +<target>Ögeler</target> + <source>Percentage</source> <target>Yüzde</target> @@ -1200,9 +1203,6 @@ Not: Dosya adları kök klasörlere göre bağıl olmalıdır! <source>%x PB</source> <target>%x PB</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1287,8 +1287,8 @@ Not: Dosya adları kök klasörlere göre bağıl olmalıdır! <source>Cannot find the following folders:</source> <target>AÅŸağıdaki klasörler bulunamadı:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>Bu hatayı yok sayarak karşıdaki klasörleri boÅŸ kabul edebilirsiniz.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>Bu hatayı yok sayarak karşıdaki klasörleri boÅŸ kabul edebilirsiniz. Bu klasörler eÅŸleÅŸtirme sırasında kendiliÄŸinden oluÅŸturulur.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Klasörler bağımlı! EÅŸleÅŸtirme kurallarını koyarken dikkatli olun:</target> diff --git a/BUILD/Languages/ukrainian.lng b/BUILD/Languages/ukrainian.lng index 374e212e..49997d5c 100644 --- a/BUILD/Languages/ukrainian.lng +++ b/BUILD/Languages/ukrainian.lng @@ -115,15 +115,6 @@ <source>Cannot resolve symbolic link %x.</source> <target>Ðе вдаєтьÑÑ Ð²Ð¸Ñ€Ñ–ÑˆÐ¸Ñ‚Ð¸ Ñимвольне поÑÐ¸Ð»Ð°Ð½Ð½Ñ %x.</target> -<source>%x MB</source> -<target>%x МБ</target> - -<source>%x KB</source> -<target>%x КБ</target> - -<source>%x GB</source> -<target>%x ГБ</target> - <source> <pluralform>1 Byte</pluralform> <pluralform>%x Bytes</pluralform> @@ -134,6 +125,15 @@ <pluralform>%x байтів</pluralform> </target> +<source>%x MB</source> +<target>%x МБ</target> + +<source>%x KB</source> +<target>%x КБ</target> + +<source>%x GB</source> +<target>%x ГБ</target> + <source>Database file %x is incompatible.</source> <target>ÐеÑуміÑний файл бази даних %x.</target> @@ -162,7 +162,7 @@ <target>ВідбулоÑÑŒ виключеннÑ!</target> <source>Cannot read file attributes of %x.</source> -<target>Ðе вдаєтьÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ атрибути файла %Ñ….</target> +<target>Ðе вдаєтьÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ атрибути файла %x.</target> <source>Cannot get process information.</source> <target>Ðе вдаєтьÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ інформацію процеÑу</target> @@ -170,9 +170,6 @@ <source>Waiting while directory is locked (%x)...</source> <target>ÐžÑ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð½ÑÑ‚Ñ‚Ñ Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð· каталогу (%x)...</target> -<source>Creating file %x</source> -<target>Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ %x</target> - <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -183,15 +180,15 @@ <pluralform>%x Ñек</pluralform> </target> +<source>Creating file %x</source> +<target>Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ %x</target> + <source>Error parsing file %x, row %y, column %z.</source> <target>Помилка розбору файла %x, Ñ€Ñдок %y, колонка %z.</target> <source>Scanning:</source> <target>Сканую:</target> -<source>Encoding extended time information: %x</source> -<target>ÐšÐ¾Ð´ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð¾Ñ— інформації про чаÑ: %x</target> - <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> @@ -202,6 +199,9 @@ <pluralform>[%x Ðитей виконаннÑ]</pluralform> </target> +<source>Encoding extended time information: %x</source> +<target>ÐšÐ¾Ð´ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð¾Ñ— інформації про чаÑ: %x</target> + <source>/sec</source> <target>/Ñек</target> @@ -791,9 +791,6 @@ Note: File names must be relative to base directories! <source>&Find next</source> <target>&Знайти наÑтупний</target> -<source>Operation aborted!</source> -<target>ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð²Ñ–Ð´Ð¼Ñ–Ð½ÐµÐ½Ð°!</target> - <source>Main bar</source> <target>Головна панель</target> @@ -821,6 +818,36 @@ Note: File names must be relative to base directories! <source>Compare both sides</source> <target>ПорівнÑти обидві Ñторони</target> +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target> +<pluralform>%x каталог</pluralform> +<pluralform>%x каталоги</pluralform> +<pluralform>%x каталогів</pluralform> +</target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target> +<pluralform>%x файл</pluralform> +<pluralform>%x файли</pluralform> +<pluralform>%x файлів</pluralform> +</target> + +<source> +<pluralform>%y of 1 row in view</pluralform> +<pluralform>%y of %x rows in view</pluralform> +</source> +<target> +<pluralform>%y of %x Ñ€Ñдка</pluralform> +<pluralform>%y of %x Ñ€Ñдків</pluralform> +<pluralform>%y of %x Ñ€Ñдків</pluralform> +</target> + <source>Set direction:</source> <target>Виберіть напрÑм:</target> @@ -935,6 +962,9 @@ Note: File names must be relative to base directories! <source>Set as default</source> <target>Ð’Ñтановити за замовчуваннÑм</target> +<source>Operation aborted!</source> +<target>ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð²Ñ–Ð´Ð¼Ñ–Ð½ÐµÐ½Ð°!</target> + <source>All folders are in sync!</source> <target>Ð’ÑÑ– папки Ñинхронізовано!</target> @@ -950,36 +980,6 @@ Note: File names must be relative to base directories! <source>Searching for program updates...</source> <target>Пошук оновлень програми ...</target> -<source> -<pluralform>1 directory</pluralform> -<pluralform>%x directories</pluralform> -</source> -<target> -<pluralform>%x каталог</pluralform> -<pluralform>%x каталоги</pluralform> -<pluralform>%x каталогів</pluralform> -</target> - -<source> -<pluralform>1 file</pluralform> -<pluralform>%x files</pluralform> -</source> -<target> -<pluralform>%x файл</pluralform> -<pluralform>%x файли</pluralform> -<pluralform>%x файлів</pluralform> -</target> - -<source> -<pluralform>%x of 1 row in view</pluralform> -<pluralform>%x of %y rows in view</pluralform> -</source> -<target> -<pluralform>%x з %y Ñ€Ñдка</pluralform> -<pluralform>%x з %y Ñ€Ñдків</pluralform> -<pluralform>%x з %y Ñ€Ñдків</pluralform> -</target> - <source>Ignore further errors</source> <target>Ігнорувати майбутні помилки</target> @@ -1061,6 +1061,26 @@ Note: File names must be relative to base directories! <source>Filter</source> <target>Фільтр</target> +<source> +<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> +</source> +<target> +<pluralform>Ви дійÑно хочете переміÑтити цей %x елемент до Корзини?</pluralform> +<pluralform>Ви дійÑно хочете переміÑтити ці %x елементи до Корзини?</pluralform> +<pluralform>Ви дійÑно хочете переміÑтити ці %x елементів до Корзини?</pluralform> +</target> + +<source> +<pluralform>Do you really want to delete the following item?</pluralform> +<pluralform>Do you really want to delete the following %x items?</pluralform> +</source> +<target> +<pluralform>Ви дійÑно хочете вилучити цей %x елемент?</pluralform> +<pluralform>Ви дійÑно хочете вилучити ці %x елементи?</pluralform> +<pluralform>Ви дійÑно хочете вилучити ці %x елементів?</pluralform> +</target> + <source>Direct</source> <target>ПрÑмо</target> @@ -1088,26 +1108,6 @@ Note: File names must be relative to base directories! <source>Make hidden warnings and dialogs visible again?</source> <target>Зробити приховані Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Ñ‚Ð° діалоги ​​знову видимими?</target> -<source> -<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform> -<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform> -</source> -<target> -<pluralform>Ви дійÑно хочете переміÑтити цей елемент до Корзини?</pluralform> -<pluralform>Ви дійÑно хочете переміÑтити ці %x елементи до Корзини?</pluralform> -<pluralform>Ви дійÑно хочете переміÑтити ці %x елементів до Корзини?</pluralform> -</target> - -<source> -<pluralform>Do you really want to delete the following item?</pluralform> -<pluralform>Do you really want to delete the following %x items?</pluralform> -</source> -<target> -<pluralform>Ви дійÑно хочете вилучити цей елемент?</pluralform> -<pluralform>Ви дійÑно хочете вилучити ці %x елементи?</pluralform> -<pluralform>Ви дійÑно хочете вилучити ці %x елементів?</pluralform> -</target> - <source>Leave as unresolved conflict</source> <target>Залишити Ñк невирішений конфлікт</target> @@ -1136,7 +1136,7 @@ Note: File names must be relative to base directories! <target>Файли</target> <source>Items</source> -<target></target> +<target>Елементи</target> <source>Percentage</source> <target>Проценти</target> @@ -1148,7 +1148,7 @@ Note: File names must be relative to base directories! <target>Помилка перетвореннÑ:</target> <source>Cannot delete file %x.</source> -<target>Ðе вдаєтьÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ файл %Ñ….</target> +<target>Ðе вдаєтьÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ файл %x.</target> <source>The file is locked by another process:</source> <target>Файл заблоковано іншим процеÑом:</target> @@ -1157,40 +1157,40 @@ Note: File names must be relative to base directories! <target>Ðе вдаєтьÑÑ Ð¿ÐµÑ€ÐµÐ¼Ñ–Ñтити файл %x до %y.</target> <source>Cannot delete directory %x.</source> -<target>Ðе вдаєтьÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ каталог Ñ…%.</target> +<target>Ðе вдаєтьÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ каталог %x.</target> <source>Cannot write file attributes of %x.</source> <target>Ðе вдаєтьÑÑ Ð·Ð°Ð¿Ð¸Ñати атрибути файла %x.</target> <source>Cannot write modification time of %x.</source> -<target>Ðе вдаєтьÑÑ Ð·Ð°Ð¿Ð¸Ñати чаÑу модифікації %Ñ….</target> +<target>Ðе вдаєтьÑÑ Ð·Ð°Ð¿Ð¸Ñати чаÑу модифікації %x.</target> <source>Cannot find system function %x.</source> <target>Ðе вдаєтьÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ ÑиÑтемної функції %x.</target> <source>Cannot read security context of %x.</source> -<target>Ðе вдаєтьÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ контекÑту безпеки %Ñ….</target> +<target>Ðе вдаєтьÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ контекÑту безпеки %x.</target> <source>Cannot write security context of %x.</source> -<target>Ðе вдаєтьÑÑ Ð·Ð°Ð¿Ð¸Ñати контекÑту безпеки %Ñ….</target> +<target>Ðе вдаєтьÑÑ Ð·Ð°Ð¿Ð¸Ñати контекÑту безпеки %x.</target> <source>Cannot read permissions of %x.</source> -<target>Ðе вдаєтьÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ дозволів %Ñ….</target> +<target>Ðе вдаєтьÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ дозволів %x.</target> <source>Cannot write permissions of %x.</source> -<target>Ðе вдаєтьÑÑ Ð·Ð°Ð¿Ð¸Ñати дозволів %Ñ….</target> +<target>Ðе вдаєтьÑÑ Ð·Ð°Ð¿Ð¸Ñати дозволів %x.</target> <source>Cannot create directory %x.</source> <target>Ðе вдаєтьÑÑ Ñтворити каталогу %x.</target> <source>Cannot copy symbolic link %x to %y.</source> -<target>Ðе вдаєтьÑÑ Ð·ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ñ‚Ð¸ Ñимвольне поÑÐ¸Ð»Ð°Ð½Ð½Ñ %Ñ… в %y.</target> +<target>Ðе вдаєтьÑÑ Ð·ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ñ‚Ð¸ Ñимвольне поÑÐ¸Ð»Ð°Ð½Ð½Ñ %x в %y.</target> <source>Cannot copy file %x to %y.</source> -<target>Ðе вдаєтьÑÑ Ð·ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ñ‚Ð¸ файл %Ñ… до %y.</target> +<target>Ðе вдаєтьÑÑ Ð·ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ñ‚Ð¸ файл %x до %y.</target> <source>Type of item %x is not supported:</source> -<target>Тип елемента %Ñ… не підтримуєтьÑÑ:</target> +<target>Тип елемента %x не підтримуєтьÑÑ:</target> <source>Cannot open directory %x.</source> <target>Ðе вдаєтьÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ каталогу %x.</target> @@ -1207,9 +1207,6 @@ Note: File names must be relative to base directories! <source>%x PB</source> <target>%x ПБ</target> -<source>%x%</source> -<target>%x%</target> - <source> <pluralform>1 min</pluralform> <pluralform>%x min</pluralform> @@ -1241,7 +1238,7 @@ Note: File names must be relative to base directories! </target> <source>Cannot set privilege %x.</source> -<target>Ðе вдаєтьÑÑ Ð²Ñтановити привілеї %Ñ….</target> +<target>Ðе вдаєтьÑÑ Ð²Ñтановити привілеї %x.</target> <source>Failed to suspend system sleep mode.</source> <target>Ðе вдалоÑÑ Ð¿Ñ€Ð¸Ð·ÑƒÐ¿Ð¸Ð½Ð¸Ñ‚Ð¸ режим Ñну ÑиÑтеми.</target> @@ -1300,8 +1297,8 @@ Note: File names must be relative to base directories! <source>Cannot find the following folders:</source> <target>Ðе вдаєтьÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ такі папки:</target> -<source>You can ignore this error to consider each folder as empty.</source> -<target>Ви можете ігнорувати цю помилку, вважаючи кожну папку порожньою.</target> +<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source> +<target>Ви можете ігнорувати цю помилку, вважаючи кожну папку порожньою. Папки будуть автоматично Ñтворені під Ñ‡Ð°Ñ Ñинхронізації.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> <target>Залежні каталоги! Будьте уважні при налаштуванні правил Ñинхронізації:</target> @@ -1325,7 +1322,7 @@ Note: File names must be relative to base directories! <target>Елементи відрізнÑÑŽÑ‚ÑŒÑÑ Ñ‚Ñ–Ð»ÑŒÐºÐ¸ атрибутами</target> <source>Symbolic links %x have the same date but a different target.</source> -<target>Символьні поÑÐ¸Ð»Ð°Ð½Ð½Ñ %Ñ… мають таку ж дату, але різні цілі.</target> +<target>Символьні поÑÐ¸Ð»Ð°Ð½Ð½Ñ %x мають таку ж дату, але різні цілі.</target> <source>Comparing content of files %x</source> <target>ÐŸÐ¾Ñ€Ñ–Ð²Ð½ÑŽÐ²Ð°Ð½Ð½Ñ Ð²Ð¼Ñ–Ñту файлів %x</target> @@ -1441,12 +1438,6 @@ Note: File names must be relative to base directories! <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> <target>Буде змінена папка, Ñка Ñ” чаÑтиною кількох пар папок. Будь лаÑка, переглÑньте Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñинхронізації.</target> -<source>Left</source> -<target>Ліворуч</target> - -<source>Right</source> -<target>Праворуч</target> - <source>Synchronizing folder pair:</source> <target>Ð¡Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ Ð¿Ð°Ñ€Ð¸ папок:</target> diff --git a/BUILD/LastError.log b/BUILD/LastError.log deleted file mode 100644 index 55a1d2e2..00000000 --- a/BUILD/LastError.log +++ /dev/null @@ -1 +0,0 @@ -[30.04.2013 19:55:13] Unknown error.
\ No newline at end of file diff --git a/BUILD/Resources.zip b/BUILD/Resources.zip Binary files differindex c0d59262..00df0a4f 100644 --- a/BUILD/Resources.zip +++ b/BUILD/Resources.zip diff --git a/FreeFileSync.vcxproj b/FreeFileSync.vcxproj index 9645adb9..c46f8749 100644 --- a/FreeFileSync.vcxproj +++ b/FreeFileSync.vcxproj @@ -70,28 +70,28 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <LinkIncremental>true</LinkIncremental> <OutDir>BUILD\Bin\</OutDir> - <IntDir>OBJ\$(ProjectName)_$(Configuration)_$(PlatformName)_VCPP\</IntDir> + <IntDir>OBJ\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\</IntDir> <GenerateManifest>false</GenerateManifest> <TargetName>$(ProjectName)_$(PlatformName)</TargetName> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <LinkIncremental>true</LinkIncremental> <OutDir>BUILD\Bin\</OutDir> - <IntDir>OBJ\$(ProjectName)_$(Configuration)_$(PlatformName)_VCPP\</IntDir> + <IntDir>OBJ\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\</IntDir> <GenerateManifest>false</GenerateManifest> <TargetName>$(ProjectName)_$(PlatformName)</TargetName> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <LinkIncremental>false</LinkIncremental> <OutDir>BUILD\Bin\</OutDir> - <IntDir>OBJ\$(ProjectName)_$(Configuration)_$(PlatformName)_VCPP\</IntDir> + <IntDir>OBJ\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\</IntDir> <GenerateManifest>false</GenerateManifest> <TargetName>$(ProjectName)_$(PlatformName)</TargetName> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <LinkIncremental>false</LinkIncremental> <OutDir>BUILD\Bin\</OutDir> - <IntDir>OBJ\$(ProjectName)_$(Configuration)_$(PlatformName)_VCPP\</IntDir> + <IntDir>OBJ\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\</IntDir> <GenerateManifest>false</GenerateManifest> <TargetName>$(ProjectName)_$(PlatformName)</TargetName> </PropertyGroup> @@ -172,6 +172,8 @@ <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <MultiProcessorCompilation>true</MultiProcessorCompilation> <ForcedIncludeFiles>zen/warn_static.h</ForcedIncludeFiles> + <AdditionalOptions> + </AdditionalOptions> </ClCompile> <Link> <SubSystem>Windows</SubSystem> @@ -205,6 +207,8 @@ <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <MultiProcessorCompilation>true</MultiProcessorCompilation> <ForcedIncludeFiles>zen/warn_static.h</ForcedIncludeFiles> + <AdditionalOptions> + </AdditionalOptions> </ClCompile> <Link> <SubSystem>Windows</SubSystem> @@ -134,16 +134,16 @@ MM_LIST+=lib/osx_file_icon.mm endif #list of all *.o files -OBJECT_LIST = $(CPP_LIST:%.cpp=OBJ/FFS_Release_GCC_Make/%.o) -OBJECT_LIST += $(MM_LIST:%.mm=OBJ/FFS_Release_GCC_Make/%.mm.o) +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_Release_GCC_Make/%.mm.o : %.mm +OBJ/FFS_GCC_Make_Release/%.mm.o : %.mm mkdir -p $(dir $@) $(COMPILER_BIN) $(CXXFLAGS) -c $< -o $@ -OBJ/FFS_Release_GCC_Make/%.o : %.cpp +OBJ/FFS_GCC_Make_Release/%.o : %.cpp mkdir -p $(dir $@) $(COMPILER_BIN) $(CXXFLAGS) -c $< -o $@ @@ -151,7 +151,7 @@ FreeFileSync: $(OBJECT_LIST) $(COMPILER_BIN) -o ./BUILD/$(APPNAME) $(OBJECT_LIST) $(LINKFLAGS) clean: - rm -rf OBJ/FFS_Release_GCC_Make + rm -rf OBJ/FFS_GCC_Make_Release rm -f BUILD/$(APPNAME) rm -f wx+/pch.h.gch @@ -162,7 +162,6 @@ install: mkdir -p $(APPSHAREDIR) cp -R BUILD/Languages/ \ BUILD/Help/ \ - BUILD/Compare_Complete.wav \ BUILD/Sync_Complete.wav \ BUILD/Resources.zip \ BUILD/styles.gtk_rc \ diff --git a/RealtimeSync/RealtimeSync.vcxproj b/RealtimeSync/RealtimeSync.vcxproj index 73674abc..097f9965 100644 --- a/RealtimeSync/RealtimeSync.vcxproj +++ b/RealtimeSync/RealtimeSync.vcxproj @@ -70,28 +70,28 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <LinkIncremental>true</LinkIncremental> <OutDir>..\BUILD\Bin\</OutDir> - <IntDir>..\OBJ\$(ProjectName)_$(Configuration)_$(PlatformName)_VCPP\</IntDir> + <IntDir>..\OBJ\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\</IntDir> <GenerateManifest>false</GenerateManifest> <TargetName>$(ProjectName)_$(PlatformName)</TargetName> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <LinkIncremental>true</LinkIncremental> <OutDir>..\BUILD\Bin\</OutDir> - <IntDir>..\OBJ\$(ProjectName)_$(Configuration)_$(PlatformName)_VCPP\</IntDir> + <IntDir>..\OBJ\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\</IntDir> <GenerateManifest>false</GenerateManifest> <TargetName>$(ProjectName)_$(PlatformName)</TargetName> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <LinkIncremental>false</LinkIncremental> <OutDir>..\BUILD\Bin\</OutDir> - <IntDir>..\OBJ\$(ProjectName)_$(Configuration)_$(PlatformName)_VCPP\</IntDir> + <IntDir>..\OBJ\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\</IntDir> <GenerateManifest>false</GenerateManifest> <TargetName>$(ProjectName)_$(PlatformName)</TargetName> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <LinkIncremental>false</LinkIncremental> <OutDir>..\BUILD\Bin\</OutDir> - <IntDir>..\OBJ\$(ProjectName)_$(Configuration)_$(PlatformName)_VCPP\</IntDir> + <IntDir>..\OBJ\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\</IntDir> <GenerateManifest>false</GenerateManifest> <TargetName>$(ProjectName)_$(PlatformName)</TargetName> </PropertyGroup> diff --git a/RealtimeSync/application.cpp b/RealtimeSync/application.cpp index 1efed1a1..61c9ca96 100644 --- a/RealtimeSync/application.cpp +++ b/RealtimeSync/application.cpp @@ -77,8 +77,7 @@ bool Application::OnInit() ::TransformProcessType(&psn, kProcessTransformToForegroundApplication); //behave like an application bundle, even when the app is not packaged (yet) #endif - warn_static("fix") - SetAppName(L"FreeFileSync"); //abuse FFS's name, to have "GetUserDataDir()" return the same directory + SetAppName(L"FreeFileSync"); //reuse FFS's name, to have "GetUserDataDir()/GetResourcesDir()" return the same directory in ffs_paths.cpp //do not call wxApp::OnInit() to avoid using default commandline parser @@ -91,6 +90,13 @@ bool Application::OnInit() } +int Application::OnExit() +{ + releaseWxLocale(); + return wxApp::OnExit(); +} + + void Application::onEnterEventLoop(wxEvent& event) { Disconnect(EVENT_ENTER_EVENT_LOOP, wxEventHandler(Application::onEnterEventLoop), nullptr, this); @@ -162,4 +168,3 @@ int Application::OnRun() return FFS_RC_SUCCESS; //program's return code } - diff --git a/RealtimeSync/application.h b/RealtimeSync/application.h index b3c302d7..2edef90c 100644 --- a/RealtimeSync/application.h +++ b/RealtimeSync/application.h @@ -13,6 +13,7 @@ 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() diff --git a/RealtimeSync/main_dlg.cpp b/RealtimeSync/main_dlg.cpp index 55d33fa1..f0d4d4f7 100644 --- a/RealtimeSync/main_dlg.cpp +++ b/RealtimeSync/main_dlg.cpp @@ -52,7 +52,7 @@ MainDialog::MainDialog(wxDialog* dlg, const wxString& cfgFileName) #endif wxWindowUpdateLocker dummy(this); //avoid display distortion - SetIcon(GlobalResources::instance().programIcon); //set application icon + SetIcon(GlobalResources::instance().programIconRTS); //set application icon setRelativeFontSize(*m_buttonStart, 1.5); m_buttonStart->setInnerBorderSize(8); @@ -115,6 +115,8 @@ MainDialog::MainDialog(wxDialog* dlg, const wxString& cfgFileName) //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); } @@ -134,6 +136,15 @@ MainDialog::~MainDialog() } +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 wxString& MainDialog::lastConfigFileName() { static wxString instance = toWx(zen::getConfigDir()) + L"LastRun.ffs_real"; @@ -423,20 +434,23 @@ void MainDialog::addFolder(const std::vector<wxString>& newFolders, bool addFron } -void MainDialog::removeAddFolder(int pos) +void MainDialog::removeAddFolder(size_t pos) { wxWindowUpdateLocker dummy(this); //avoid display distortion - if (0 <= pos && pos < static_cast<int>(dirNamesExtra.size())) + if (pos < dirNamesExtra.size()) { //remove folder pairs from window - DirectoryPanel* dirToDelete = dirNamesExtra[pos]; - const int folderHeight = dirToDelete->GetSize().GetHeight(); + DirectoryPanel* pairToDelete = dirNamesExtra[pos]; + const int folderHeight = pairToDelete->GetSize().GetHeight(); - bSizerFolders->Detach(dirToDelete); //Remove() does not work on Window*, so do it manually - dirToDelete->Destroy(); // + 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 diff --git a/RealtimeSync/main_dlg.h b/RealtimeSync/main_dlg.h index f5fed970..5a028f3a 100644 --- a/RealtimeSync/main_dlg.h +++ b/RealtimeSync/main_dlg.h @@ -12,6 +12,7 @@ #include <memory> #include <wx+/file_drop.h> #include "../ui/dir_name.h" +#include <zen/async_task.h> namespace xmlAccess { @@ -50,7 +51,7 @@ private: void addFolder(const wxString& dirname, bool addFront = false); void addFolder(const std::vector<wxString>& newFolders, bool addFront = false); - void removeAddFolder(int pos); //keep it an int, allow negative values! + void removeAddFolder(size_t pos); void clearAddFolders(); static const wxString& lastConfigFileName(); @@ -59,6 +60,17 @@ private: std::vector<DirectoryPanel*> dirNamesExtra; //additional pairs to the standard pair wxString currentConfigFileName; + + void onProcessAsyncTasks(wxEvent& event); + + template <class Fun, class Fun2> + void processAsync(Fun doAsync, Fun2 evalOnGui) { asyncTasks.add(doAsync, evalOnGui); timerForAsyncTasks.Start(50); /*timer interval in [ms] */ } + template <class Fun, class Fun2> + 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 index 82909526..325578a1 100644 --- a/RealtimeSync/makefile +++ b/RealtimeSync/makefile @@ -77,11 +77,11 @@ CPP_LIST+=../zen/zstring.cpp CPP_LIST+=../wx+/button.cpp #list of all *.o files (we need the "RTS" subdirectory to handle "../*.cpp" files -OBJECT_LIST=$(CPP_LIST:%.cpp=../OBJ/RTS_Release_GCC_Make/RTS/%.o) +OBJECT_LIST=$(CPP_LIST:%.cpp=../OBJ/RTS_GCC_Make_Release/RTS/%.o) all: RealtimeSync -../OBJ/RTS_Release_GCC_Make/RTS/%.o : %.cpp +../OBJ/RTS_GCC_Make_Release/RTS/%.o : %.cpp mkdir -p $(dir $@) $(COMPILER_BIN) $(CXXFLAGS) -c $< -o $@ @@ -89,7 +89,7 @@ RealtimeSync: $(OBJECT_LIST) $(COMPILER_BIN) -o ../BUILD/$(APPNAME) $(OBJECT_LIST) $(LINKFLAGS) clean: - rm -rf ../OBJ/RTS_Release_GCC_Make + rm -rf ../OBJ/RTS_GCC_Make_Release rm -f ../BUILD/$(APPNAME) rm -f ../wx+/pch.h.gch diff --git a/RealtimeSync/resources.cpp b/RealtimeSync/resources.cpp index 2b31ad12..f55887ee 100644 --- a/RealtimeSync/resources.cpp +++ b/RealtimeSync/resources.cpp @@ -49,16 +49,16 @@ GlobalResources::GlobalResources() #ifdef FFS_WIN //for compatibility it seems we need to stick with a "real" icon - programIcon = wxIcon(L"A_PROGRAM_ICON"); + programIconRTS = wxIcon(L"A_RTS_ICON"); #elif defined FFS_LINUX - programIcon.CopyFromBitmap(getImage(L"RealtimeSync")); + programIconRTS.CopyFromBitmap(getImage(L"RealtimeSync")); #elif defined FFS_MAC assert(getImage(L"RealtimeSync").GetWidth () == getImage(L"RealtimeSync").GetHeight() && getImage(L"RealtimeSync").GetWidth() % 128 == 0); //wxWidgets' bitmap to icon conversion on OS X can only deal with very specific sizes - programIcon.CopyFromBitmap(getImage(L"RealtimeSync").ConvertToImage().Scale(128, 128, wxIMAGE_QUALITY_HIGH)); + programIconRTS.CopyFromBitmap(getImage(L"RealtimeSync").ConvertToImage().Scale(128, 128, wxIMAGE_QUALITY_HIGH)); #endif } diff --git a/RealtimeSync/resources.h b/RealtimeSync/resources.h index 623265f3..7cec3a73 100644 --- a/RealtimeSync/resources.h +++ b/RealtimeSync/resources.h @@ -20,7 +20,7 @@ public: const wxBitmap& getImage(const wxString& name) const; //image resource objects - wxIcon programIcon; + wxIcon programIconRTS; private: GlobalResources(); diff --git a/algorithm.cpp b/algorithm.cpp index 46239369..0d591206 100644 --- a/algorithm.cpp +++ b/algorithm.cpp @@ -7,7 +7,6 @@ #include "algorithm.h" #include <set> #include <stdexcept> -#include <tuple> #include <zen/file_handling.h> #include <zen/recycler.h> #include <zen/stl_tools.h> @@ -225,14 +224,15 @@ bool stillInSync(const InSyncFile& dbFile, CompareVariant compareVar, size_t fil switch (compareVar) { case CMP_BY_TIME_SIZE: - return dbFile.inSyncType == InSyncFile::IN_SYNC_BINARY_EQUAL || //special rule: this is already "good enough" for CMP_BY_TIME_SIZE! - //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); + if (dbFile.inSyncType == IN_SYNC_BINARY_EQUAL) 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.inSyncType == InSyncFile::IN_SYNC_BINARY_EQUAL; + return dbFile.inSyncType == IN_SYNC_BINARY_EQUAL; //in contrast to comparison, we don't care about modification time here! } assert(false); @@ -262,26 +262,29 @@ bool isEqual(const SymLinkMapping& linkObj, const InSyncDir::LinkList::value_typ return linkObj.getShortName<side>() == 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<side>(), descrDb.lastWriteTimeRaw, 2) && -#ifdef FFS_WIN //comparison of symbolic link type is relevant for Windows only - linkObj.getLinkType<side>() == descrDb.type && -#endif - linkObj.getTargetPath<side>() == descrDb.targetPath; + sameFileTime(linkObj.getLastWriteTime<side>(), descrDb.lastWriteTimeRaw, 2); } //check whether database entry is in sync considering *current* comparison settings inline -bool stillInSync(const InSyncSymlink& dbLink, CompareVariant compareVar) +bool stillInSync(const InSyncSymlink& dbLink, CompareVariant compareVar, size_t fileTimeTolerance) { - return !dbLink.left .targetPath.empty() && //if one of these is empty, we can't make a statement whether both sides are in sync - !dbLink.right.targetPath.empty() && // -#ifdef FFS_WIN //type of symbolic link is relevant for Windows only - dbLink.left.type == dbLink.right.type && -#endif - dbLink.left.targetPath == dbLink.right.targetPath; - //case-sensitive short name match is a database invariant! - //in contrast to comparison, we don't care about modification time! + switch (compareVar) + { + case CMP_BY_TIME_SIZE: + if (dbLink.inSyncType == IN_SYNC_BINARY_EQUAL) 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.inSyncType == IN_SYNC_BINARY_EQUAL; + //in contrast to comparison, we don't care about modification time here! + } + assert(false); + return false; } //-------------------------------------------------------------------- @@ -349,10 +352,7 @@ private: //----------- detect renamed files ----------------- if (!exLeftOnly.empty() && !exRightOnly.empty()) - { - collectEqualDbEntries(*lastSyncState); //fill map "onceEqual" - detectRenamedFiles(); - } + detectRenamedFiles(*lastSyncState); } std::shared_ptr<InSyncDir> loadDBFile(const BaseDirMapping& baseMap) //return nullptr on failure @@ -387,12 +387,22 @@ private: if (cat == FILE_LEFT_SIDE_ONLY) { if (fileObj.getFileId<LEFT_SIDE>() != FileId()) - exLeftOnly.push_back(&fileObj); + { + auto rv = exLeftOnly.insert(std::make_pair(fileObj.getFileId<LEFT_SIDE>(), &fileObj)); + assert(rv.second); + if (!rv.second) //duplicate file ID! + rv.first->second = nullptr; + } } else if (cat == FILE_RIGHT_SIDE_ONLY) { if (fileObj.getFileId<RIGHT_SIDE>() != FileId()) - exRightOnly.insert(std::make_pair(getFileIdKey<RIGHT_SIDE>(fileObj), &fileObj)); + { + auto rv = exRightOnly.insert(std::make_pair(fileObj.getFileId<RIGHT_SIDE>(), &fileObj)); + assert(rv.second); + if (!rv.second) //duplicate file ID! + rv.first->second = nullptr; + } } //---------------------------------------------------------------------- @@ -455,7 +465,7 @@ private: 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)) + if (dbEntry && !stillInSync(dbEntry->second, cmpVar, fileTimeTolerance)) linkObj.setSyncDirConflict(txtDbNotInSync); else linkObj.setSyncDir(changeOnLeft ? SYNC_DIR_RIGHT : SYNC_DIR_LEFT); @@ -515,63 +525,51 @@ private: recurse(dirObj, dbEntry ? &dbEntry->second : nullptr); //recursion } - void collectEqualDbEntries(const InSyncDir& container) + //note: - we cannot integrate this traversal into "recurse()" since it may take a *slightly* different path: e.g. file renamed on both sides + void detectRenamedFiles(InSyncDir& container) { - //note: we cannot integrate this traversal into "recurse()" since it may take a *slightly* different path: e.g. file renamed on both sides - std::for_each(container.files.begin(), container.files.end(), - [&](const std::pair<Zstring, InSyncFile>& filePair) - { - if (getFileId(filePair.second.left ) != FileId() && - getFileId(filePair.second.right) != FileId() && - stillInSync(filePair.second, cmpVar, fileTimeTolerance)) - onceEqual.insert(std::make_pair(getFileIdKey(filePair.second.left), getFileIdKey(filePair.second.right))); - }); + [&](std::pair<const Zstring, InSyncFile>& filePair) { findAndSetMovePair(filePair.second); }); std::for_each(container.dirs.begin(), container.dirs.end(), - [&](const std::pair<Zstring, InSyncDir>& dirPair) { collectEqualDbEntries(dirPair.second); }); + [&](std::pair<const Zstring, InSyncDir>& dirPair) { detectRenamedFiles(dirPair.second); }); } - typedef std::tuple<Int64, UInt64, FileId> FileIdKey; //(date, size, file ID) - - - //modification date is *not* considered as part of container key, so check here! - template <class Container> - static typename Container::const_iterator findValue(const Container& cnt, const FileIdKey& key) + template <SelectedSide side> + static bool sameSizeAndDate(const FileMapping& fsObj, const FileDescriptor& fileDescr) { - auto iterPair = cnt.equal_range(key); //since file id is already unique, we expect a single-element range at most - auto it = std::find_if(iterPair.first, iterPair.second, - [&](const typename Container::value_type& item) - { - return sameFileTime(std::get<0>(item.first), std::get<0>(key), 2); //respect 2 second FAT/FAT32 precision! - //the file time could be inferred from the source side after a file copy while a slightly different time is stored on a FAT32 disk! - }); - return it == iterPair.second ? cnt.end() : it; + return fsObj.getFileSize<side>() == fileDescr.fileSize && + sameFileTime(fsObj.getLastWriteTime<side>(), fileDescr.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! } - void detectRenamedFiles() const + void findAndSetMovePair(const InSyncFile& dbEntry) const { - std::for_each(exLeftOnly.begin(), exLeftOnly.end(), - [&](FileMapping* fileLeftOnly) - { - const FileIdKey& keyLeft = RedetermineAuto::getFileIdKey<LEFT_SIDE>(*fileLeftOnly); - - auto it = findValue(onceEqual, keyLeft); - if (it != onceEqual.end()) - { - const FileIdKey& keyRight = it->second; - - auto iter2 = findValue(exRightOnly, keyRight); - if (iter2 != exRightOnly.end()) - { - FileMapping* fileRightOnly = iter2->second; + const FileId idLeft = getFileId(dbEntry.left); + const FileId idRight = getFileId(dbEntry.right); - //found a pair, mark it! - fileLeftOnly ->setMoveRef(fileRightOnly->getId()); - fileRightOnly->setMoveRef(fileLeftOnly ->getId()); - } - } - }); + if (idLeft != FileId() && + idRight != FileId() && + stillInSync(dbEntry, cmpVar, fileTimeTolerance)) + { + auto itL = exLeftOnly.find(idLeft); + if (itL != exLeftOnly.end()) + if (FileMapping* fileLeftOnly = itL->second) //= nullptr, if duplicate ID! + if (sameSizeAndDate<LEFT_SIDE>(*fileLeftOnly, dbEntry.left)) + { + auto itR = exRightOnly.find(idRight); + if (itR != exRightOnly.end()) + if (FileMapping* fileRightOnly = itR->second) //= nullptr, if duplicate ID! + if (sameSizeAndDate<RIGHT_SIDE>(*fileRightOnly, dbEntry.right)) + 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 std::wstring txtBothSidesChanged; @@ -580,36 +578,10 @@ private: const CompareVariant cmpVar; const size_t fileTimeTolerance; - std::function<void(const std::wstring&)> reportWarning_; - //detection of renamed files - template <SelectedSide side> - static FileIdKey getFileIdKey(const FileMapping& fsObj) { return std::make_tuple(fsObj.getLastWriteTime<side>(), fsObj.getFileSize<side>(), fsObj.getFileId<side>()); } - static FileIdKey getFileIdKey(const FileDescriptor& fileDescr) { return std::make_tuple(fileDescr.lastWriteTimeRaw, fileDescr.fileSize, getFileId(fileDescr)); } - - struct LessFileIdKey - { - bool operator()(const FileIdKey& lhs, const FileIdKey& rhs) const - { - //caveat: *don't* allow 2 sec tolerance as container predicate!! - // => no strict weak ordering relation! reason: no transitivity of equivalence! - - //-> bad: if (!sameFileTime(std::get<0>(lhs), std::get<0>(rhs), 2)) - // return std::get<0>(lhs) < std::get<0>(rhs); - - if (std::get<1>(lhs) != std::get<1>(rhs)) //file size - return std::get<1>(lhs) < std::get<1>(rhs); - - return std::get<2>(lhs) < std::get<2>(rhs); //file id - } - }; - - std::vector<FileMapping*> exLeftOnly; - - std::multimap<FileIdKey, FileIdKey, LessFileIdKey> onceEqual; //associates left and right database entries which are considered "equal" := "same name, size, date" - - std::multimap<FileIdKey, FileMapping*, LessFileIdKey> exRightOnly; + std::map<FileId, FileMapping*> exLeftOnly; //FileMapping* == nullptr for duplicate ids! => consider aliasing through symlinks! + std::map<FileId, FileMapping*> exRightOnly; //=> avoid ambiguity for mixtures of files/symlinks on one side and allow 1-1 mapping only! /* detect renamed files @@ -623,10 +595,10 @@ private: Algorithm: ---------- - DB-file left --- (name, size, date) ---> DB-file right - /|\ | + 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! @@ -1289,12 +1261,14 @@ struct ItemDeleter : public FSObjectVisitor //throw FileError, but nothrow cons if (useRecycleBin_) zen::recycleOrDelete(linkObj.getFullName<side>()); //throw FileError else - switch (linkObj.getLinkType<side>()) + switch (getSymlinkType(linkObj.getFullName<side>())) { - case LinkDescriptor::TYPE_DIR: + case SYMLINK_TYPE_DIR: zen::removeDirectory(linkObj.getFullName<side>()); //throw FileError break; - case LinkDescriptor::TYPE_FILE: + + case SYMLINK_TYPE_FILE: + case SYMLINK_TYPE_UNKNOWN: zen::removeFile(linkObj.getFullName<side>()); //throw FileError break; } diff --git a/algorithm.h b/algorithm.h index 1c29a5bd..91881d6e 100644 --- a/algorithm.h +++ b/algorithm.h @@ -65,4 +65,4 @@ void deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDeleteOnLef bool& warningRecyclerMissing); } -#endif // ALGORITHM_H_INCLUDED +#endif //ALGORITHM_H_INCLUDED diff --git a/comparison.cpp b/comparison.cpp index 3cb20df0..6db6cf03 100644 --- a/comparison.cpp +++ b/comparison.cpp @@ -10,6 +10,7 @@ #include <zen/perf.h> #include <zen/scope_guard.h> #include <zen/process_priority.h> +#include <zen/symlink_target.h> #include <zen/format_unit.h> #include "algorithm.h" #include "lib/parallel_scan.h" @@ -74,19 +75,19 @@ void checkForIncompleteInput(const std::vector<FolderPairCfg>& folderPairsForm, } -std::set<Zstring, LessFilename> determineExistentDirs(const std::vector<Zstring>& dirnames, +std::set<Zstring, LessFilename> determineExistentDirs(const std::set<Zstring, LessFilename>& dirnames, bool allowUserInteraction, ProcessCallback& callback) { std::set<Zstring, LessFilename> dirsEx; - tryReportingError([&] + tryReportingError2([&] { dirsEx = getExistingDirsUpdating(dirnames, allowUserInteraction, callback); //check *all* directories on each try! //get list of not existing directories - std::vector<Zstring> dirsMissing = dirnames; - vector_remove_if(dirsMissing, [&](const Zstring& dirname) { return dirname.empty() || dirsEx.find(dirname) != dirsEx.end(); }); + std::set<Zstring, LessFilename> dirsMissing = dirnames; + set_remove_if(dirsMissing, [&](const Zstring& dirname) { return dirname.empty() || dirsEx.find(dirname) != dirsEx.end(); }); if (!dirsMissing.empty()) { @@ -175,7 +176,6 @@ bool filesHaveSameContentUpdating(const Zstring& filename1, const Zstring& filen return sameContent; } -} //############################################################################################################################# @@ -254,156 +254,8 @@ ComparisonBuffer::ComparisonBuffer(const std::set<DirectoryKey>& keysToRead, } -void zen::compare(size_t fileTimeTolerance, - xmlAccess::OptionalDialogs& warnings, - bool allowUserInteraction, - bool runWithBackgroundPriority, - bool createDirLocks, - std::unique_ptr<LockHolder>& dirLocks, - const std::vector<FolderPairCfg>& cfgList, - FolderComparison& output, - ProcessCallback& callback) -{ - //specify process and resource handling priorities - std::unique_ptr<ScheduleForBackgroundProcessing> backgroundPrio; - if (runWithBackgroundPriority) - try - { - backgroundPrio = make_unique<ScheduleForBackgroundProcessing>(); //throw FileError - } - catch (const FileError& e) - { - //not an error in this context - callback.reportInfo(e.toString()); //may throw! - } - - //prevent operating system going into sleep state - std::unique_ptr<PreventStandby> noStandby; - try - { - noStandby = make_unique<PreventStandby>(); //throw FileError - } - catch (const FileError& e) - { - //not an error in this context - callback.reportInfo(e.toString()); //may throw! - } - - //PERF_START; - - callback.reportInfo(_("Start comparison")); //we want some indicator at the very beginning 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<Zstring, LessFilename> 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::vector<Zstring> dirnames; - std::for_each(cfgList.begin(), cfgList.end(), - [&](const FolderPairCfg& fpCfg) - { - dirnames.push_back(fpCfg.leftDirectoryFmt); - dirnames.push_back(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<LockHolder>(dirnamesExisting, warnings.warningDirectoryLockFailed, callback); - - //------------------- fill directory buffer --------------------------------------------------- - std::set<DirectoryKey> keysToRead; - - 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! - keysToRead.insert(DirectoryKey(fpCfg.leftDirectoryFmt, fpCfg.filter.nameFilter, fpCfg.handleSymlinks)); - if (dirAvailable(fpCfg.rightDirectoryFmt)) - keysToRead.insert(DirectoryKey(fpCfg.rightDirectoryFmt, fpCfg.filter.nameFilter, fpCfg.handleSymlinks)); - }); - - ComparisonBuffer cmpBuff(keysToRead, - fileTimeTolerance, - callback); - - //------------ traverse/read folders ----------------------------------------------------- - - FolderComparison output_tmp; //write to output not before END of process! - - //buffer "config"/"result of binary comparison" for latter processing as a single block - std::vector<std::pair<FolderPairCfg, BaseDirMapping*>> workLoadBinary; - - std::for_each(cfgList.begin(), cfgList.end(), - [&](const FolderPairCfg& fpCfg) - { - //a pity VC11 screws up on std::make_shared with 7 arguments... - output_tmp.push_back(std::shared_ptr<BaseDirMapping>(new BaseDirMapping(fpCfg.leftDirectoryFmt, - dirAvailable(fpCfg.leftDirectoryFmt), - fpCfg.rightDirectoryFmt, - dirAvailable(fpCfg.rightDirectoryFmt), - fpCfg.filter.nameFilter, - fpCfg.compareVar, - fileTimeTolerance))); - switch (fpCfg.compareVar) - { - case CMP_BY_TIME_SIZE: - cmpBuff.compareByTimeSize(fpCfg, *output_tmp.back()); - break; - case CMP_BY_CONTENT: - workLoadBinary.push_back(std::make_pair(fpCfg, &*output_tmp.back())); - break; - } - }); - //process binary comparison in one block - cmpBuff.compareByContent(workLoadBinary); - - assert(output_tmp.size() == cfgList.size()); - - //--------- set initial sync-direction -------------------------------------------------- - - for (auto j = begin(output_tmp); j != end(output_tmp); ++j) - { - const FolderPairCfg& fpCfg = cfgList[j - output_tmp.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!!! - output_tmp.swap(output); - } - catch (const std::bad_alloc& e) - { - callback.reportFatalError(_("Out of memory!") + L" " + utfCvrtTo<std::wstring>(e.what())); - } - catch (const std::exception& e) - { - callback.reportFatalError(utfCvrtTo<std::wstring>(e.what())); - } -} - //--------------------assemble conflict descriptions--------------------------- -namespace -{ //const wchar_t arrowLeft [] = L"\u2190"; //const wchar_t arrowRight[] = L"\u2192"; unicode arrows -> too small const wchar_t arrowLeft [] = L"<--"; @@ -413,8 +265,7 @@ const wchar_t arrowRight[] = L"-->"; //check for very old dates or date2s in the future std::wstring getConflictInvalidDate(const Zstring& fileNameFull, Int64 utcTime) { - return _("Conflict detected:") + L"\n" + - replaceCpy(_("File %x has an invalid date!"), L"%x", fmtFileName(fileNameFull)) + L"\n" + + return _("Conflict detected:") + L" " + replaceCpy(_("File %x has an invalid date!"), L"%x", fmtFileName(fileNameFull)) + L"\n" + _("Date:") + L" " + utcToLocalTimeString(utcTime); } @@ -422,8 +273,7 @@ std::wstring getConflictInvalidDate(const Zstring& fileNameFull, Int64 utcTime) //check for changed files with same modification date std::wstring getConflictSameDateDiffSize(const FileMapping& fileObj) { - return _("Conflict detected:") + L"\n" + - replaceCpy(_("Files %x have the same date but a different size!"), L"%x", fmtFileName(fileObj.getObjRelativeName())) + L"\n" + + return _("Conflict detected:") + L" " + replaceCpy(_("Files %x have the same date but a different size!"), L"%x", fmtFileName(fileObj.getObjRelativeName())) + L"\n" + arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.getLastWriteTime<LEFT_SIDE >()) + L" " + _("Size:") + L" " + toGuiString(fileObj.getFileSize<LEFT_SIDE>()) + L"\n" + arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.getLastWriteTime<RIGHT_SIDE>()) + L" " + _("Size:") + L" " + toGuiString(fileObj.getFileSize<RIGHT_SIDE>()); } @@ -445,12 +295,9 @@ std::wstring getDescrDiffMetaDate(const FileOrLinkMapping& fileObj) arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.template getLastWriteTime<LEFT_SIDE >()) + L"\n" + arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.template getLastWriteTime<RIGHT_SIDE>()); } -} //----------------------------------------------------------------------------- -namespace -{ void categorizeSymlinkByTime(SymLinkMapping& linkObj, size_t fileTimeTolerance) { //categorize symlinks that exist on both sides @@ -458,29 +305,14 @@ void categorizeSymlinkByTime(SymLinkMapping& linkObj, size_t fileTimeTolerance) linkObj.getLastWriteTime<RIGHT_SIDE>(), fileTimeTolerance)) { case CmpFileTime::TIME_EQUAL: - if (linkObj.getTargetPath<LEFT_SIDE>().empty()) - linkObj.setCategoryConflict(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkObj.getFullName<LEFT_SIDE>()))); - else if (linkObj.getTargetPath<RIGHT_SIDE>().empty()) - linkObj.setCategoryConflict(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkObj.getFullName<RIGHT_SIDE>()))); + //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 - else if ( -#ifdef FFS_WIN //type of symbolic link is relevant for Windows only - linkObj.getLinkType<LEFT_SIDE>() == linkObj.getLinkType<RIGHT_SIDE>() && -#endif - linkObj.getTargetPath<LEFT_SIDE>() == linkObj.getTargetPath<RIGHT_SIDE>()) //may both be empty if reading link content failed - { - //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<LEFT_SIDE>() == linkObj.getShortName<RIGHT_SIDE>()) - linkObj.setCategory<FILE_EQUAL>(); - else - linkObj.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(linkObj)); - } + if (linkObj.getShortName<LEFT_SIDE>() == linkObj.getShortName<RIGHT_SIDE>()) + linkObj.setCategory<FILE_EQUAL>(); else - linkObj.setCategoryConflict(_("Conflict detected:") + L"\n" + - replaceCpy(_("Symbolic links %x have the same date but a different target."), L"%x", fmtFileName(linkObj.getObjRelativeName()))); + linkObj.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(linkObj)); break; case CmpFileTime::TIME_LEFT_NEWER: @@ -500,7 +332,7 @@ void categorizeSymlinkByTime(SymLinkMapping& linkObj, size_t fileTimeTolerance) break; } } -} + void ComparisonBuffer::compareByTimeSize(const FolderPairCfg& fpConfig, BaseDirMapping& output) { @@ -555,40 +387,52 @@ void ComparisonBuffer::compareByTimeSize(const FolderPairCfg& fpConfig, BaseDirM }); } -namespace -{ -void categorizeSymlinkByContent(SymLinkMapping& linkObj, size_t fileTimeTolerance) + +void categorizeSymlinkByContent(SymLinkMapping& linkObj, size_t fileTimeTolerance, ProcessCallback& callback) { //categorize symlinks that exist on both sides - if (linkObj.getTargetPath<LEFT_SIDE>().empty()) - linkObj.setCategoryConflict(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkObj.getFullName<LEFT_SIDE>()))); - else if (linkObj.getTargetPath<RIGHT_SIDE>().empty()) - linkObj.setCategoryConflict(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkObj.getFullName<RIGHT_SIDE>()))); - //if one of these is empty it's handled like reading "file content" failed + Zstring targetPathRawL; + Zstring targetPathRawR; + Opt<std::wstring> errMsg = tryReportingError2([&] + { + callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtFileName(linkObj.getFullName<LEFT_SIDE>()))); + targetPathRawL = getSymlinkTargetRaw(linkObj.getFullName<LEFT_SIDE>()); //throw FileError - else if ( + callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtFileName(linkObj.getFullName<RIGHT_SIDE>()))); + targetPathRawR = getSymlinkTargetRaw(linkObj.getFullName<RIGHT_SIDE>()); //throw FileError + }, callback); + + if (errMsg) + linkObj.setCategoryConflict(_("Conflict detected:") + L" " + *errMsg); + else + { + if (targetPathRawL == targetPathRawR #ifdef FFS_WIN //type of symbolic link is relevant for Windows only - linkObj.getLinkType<LEFT_SIDE>() == linkObj.getLinkType<RIGHT_SIDE>() && + && + getSymlinkType(linkObj.getFullName<LEFT_SIDE >()) == + getSymlinkType(linkObj.getFullName<RIGHT_SIDE>()) #endif - linkObj.getTargetPath<LEFT_SIDE>() == linkObj.getTargetPath<RIGHT_SIDE>()) //may both be empty if determination failed!!! - { - //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 - - //symlinks have same "content" - if (linkObj.getShortName<LEFT_SIDE>() != linkObj.getShortName<RIGHT_SIDE>()) - linkObj.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(linkObj)); - else if (CmpFileTime::getResult(linkObj.getLastWriteTime<LEFT_SIDE>(), linkObj.getLastWriteTime<RIGHT_SIDE>(), fileTimeTolerance) != CmpFileTime::TIME_EQUAL) - linkObj.setCategoryDiffMetadata(getDescrDiffMetaDate(linkObj)); + ) + { + //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 + + //symlinks have same "content" + if (linkObj.getShortName<LEFT_SIDE>() != linkObj.getShortName<RIGHT_SIDE>()) + linkObj.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(linkObj)); + else if (CmpFileTime::getResult(linkObj.getLastWriteTime<LEFT_SIDE>(), + linkObj.getLastWriteTime<RIGHT_SIDE>(), fileTimeTolerance) != CmpFileTime::TIME_EQUAL) + linkObj.setCategoryDiffMetadata(getDescrDiffMetaDate(linkObj)); + else + linkObj.setCategory<FILE_EQUAL>(); + } else - linkObj.setCategory<FILE_EQUAL>(); + linkObj.setCategory<FILE_DIFFERENT>(); } - else - linkObj.setCategory<FILE_DIFFERENT>(); -} } + void ComparisonBuffer::compareByContent(std::vector<std::pair<FolderPairCfg, BaseDirMapping*>>& workLoad) { if (workLoad.empty()) return; @@ -606,7 +450,7 @@ void ComparisonBuffer::compareByContent(std::vector<std::pair<FolderPairCfg, Bas //finish symlink categorization std::for_each(uncategorizedLinks.begin(), uncategorizedLinks.end(), - [&](SymLinkMapping* linkMap) { categorizeSymlinkByContent(*linkMap, fileTimeTolerance); }); + [&](SymLinkMapping* linkMap) { categorizeSymlinkByContent(*linkMap, fileTimeTolerance, callback_); }); } //finish categorization... @@ -634,7 +478,7 @@ void ComparisonBuffer::compareByContent(std::vector<std::pair<FolderPairCfg, Bas to<Int64>(bytesTotal), ProcessCallback::PHASE_COMPARING_CONTENT); - const std::wstring txtComparingContentOfFiles = replaceCpy(_("Comparing content of files %x"), L"%x", L"\n%x", false); + const std::wstring txtComparingContentOfFiles = _("Comparing content of files %x"); //compare files (that have same size) bytewise... std::for_each(filesToCompareBytewise.begin(), filesToCompareBytewise.end(), @@ -644,12 +488,23 @@ void ComparisonBuffer::compareByContent(std::vector<std::pair<FolderPairCfg, Bas //check files that exist in left and right model but have different content - if (!tryReportingError([&] - { - if (filesHaveSameContentUpdating(fileObj->getFullName<LEFT_SIDE>(), //throw FileError + bool haveSameContent = false; + Opt<std::wstring> errMsg = tryReportingError2([&] + { + haveSameContent = filesHaveSameContentUpdating(fileObj->getFullName<LEFT_SIDE>(), //throw FileError fileObj->getFullName<RIGHT_SIDE>(), to<Int64>(fileObj->getFileSize<LEFT_SIDE>()), - callback_)) + callback_); + + callback_.updateProcessedData(1, 0); //processed bytes are reported in subfunctions! + callback_.requestUiRefresh(); //may throw + }, callback_); + + if (errMsg) + fileObj->setCategoryConflict(_("Conflict detected:") + L" " + *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 @@ -663,19 +518,11 @@ void ComparisonBuffer::compareByContent(std::vector<std::pair<FolderPairCfg, Bas } else fileObj->setCategory<FILE_DIFFERENT>(); - - callback_.updateProcessedData(1, 0); //processed bytes are reported in subfunctions! - - }, callback_)) - fileObj->setCategoryConflict(_("Conflict detected:") + L"\n" + _("Comparing files by content failed.")); - - callback_.requestUiRefresh(); //may throw + } }); } -namespace -{ class MergeSides { public: @@ -837,7 +684,6 @@ void processFilteredDirs(HierarchyObject& hierObj, const HardFilter& filterProc) dirObj.refSubFiles().empty(); }); } -} //create comparison result table and fill category except for files existing on both sides: undefinedFiles and undefinedLinks are appended! @@ -879,19 +725,171 @@ void ComparisonBuffer::performComparison(const FolderPairCfg& fpCfg, addSoftFiltering(output, fpCfg.filter.timeSizeFilter); //properly handle (user-ignored) traversing errors: just uncheck them, no need to physically delete them (<automatic> mode will be grateful) - std::set<Zstring> failedReads; - if (bufValueLeft ) failedReads.insert(bufValueLeft ->failedReads.begin(), bufValueLeft ->failedReads.end()); - if (bufValueRight) failedReads.insert(bufValueRight->failedReads.begin(), bufValueRight->failedReads.end()); + auto addToSet = [](std::set<Zstring>& s, const std::set<Zstring>& v) { s.insert(v.begin(), v.end()); }; + std::set<Zstring> failedDirReads; + if (bufValueLeft ) addToSet(failedDirReads, bufValueLeft ->failedDirReads); + if (bufValueRight) addToSet(failedDirReads, bufValueRight->failedDirReads); - if (!failedReads.empty()) + std::set<Zstring> failedItemReads; + if (bufValueLeft ) addToSet(failedItemReads, bufValueLeft ->failedItemReads); + if (bufValueRight) addToSet(failedItemReads, bufValueRight->failedItemReads); + + if (!failedDirReads.empty() || !failedItemReads.empty()) { Zstring filterFailedRead; - //exclude subfolders only - std::for_each(failedReads.begin(), failedReads.end(), - [&](const Zstring& relDirPf) { filterFailedRead += relDirPf + Zstr("?*\n"); }); //note: relDirPf is empty for base dir, otherwise postfixed! e.g. "subdir\" + std::for_each(failedDirReads .begin(), failedDirReads .end(), [&](const Zstring& relDirPf) { filterFailedRead += relDirPf + Zstr("?*\n"); }); //exclude child items only! + std::for_each(failedItemReads.begin(), failedItemReads.end(), [&](const Zstring& relItem ) { filterFailedRead += relItem + Zstr("\n"); }); //exclude item AND child items! addHardFiltering(output, filterFailedRead); } //################################################################################## } +} + + +void zen::compare(size_t fileTimeTolerance, + xmlAccess::OptionalDialogs& warnings, + bool allowUserInteraction, + bool runWithBackgroundPriority, + bool createDirLocks, + std::unique_ptr<LockHolder>& dirLocks, + const std::vector<FolderPairCfg>& cfgList, + FolderComparison& output, + ProcessCallback& callback) +{ + //specify process and resource handling priorities + std::unique_ptr<ScheduleForBackgroundProcessing> backgroundPrio; + if (runWithBackgroundPriority) + try + { + backgroundPrio = make_unique<ScheduleForBackgroundProcessing>(); //throw FileError + } + catch (const FileError& e) + { + //not an error in this context + callback.reportInfo(e.toString()); //may throw! + } + + //prevent operating system going into sleep state + std::unique_ptr<PreventStandby> noStandby; + try + { + noStandby = make_unique<PreventStandby>(); //throw FileError + } + catch (const FileError& e) + { + //not an error in this context + callback.reportInfo(e.toString()); //may throw! + } + + //PERF_START; + + callback.reportInfo(_("Start comparison")); //we want some indicator at the very beginning 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<Zstring, LessFilename> 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<Zstring, LessFilename> 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<LockHolder>(dirnamesExisting, warnings.warningDirectoryLockFailed, callback); + + //------------------- fill directory buffer --------------------------------------------------- + std::set<DirectoryKey> keysToRead; + + 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! + keysToRead.insert(DirectoryKey(fpCfg.leftDirectoryFmt, fpCfg.filter.nameFilter, fpCfg.handleSymlinks)); + if (dirAvailable(fpCfg.rightDirectoryFmt)) + keysToRead.insert(DirectoryKey(fpCfg.rightDirectoryFmt, fpCfg.filter.nameFilter, fpCfg.handleSymlinks)); + }); + + ComparisonBuffer cmpBuff(keysToRead, + fileTimeTolerance, + callback); + + //------------ traverse/read folders ----------------------------------------------------- + + FolderComparison output_tmp; //write to output not before END of process! + + //buffer "config"/"result of binary comparison" for latter processing as a single block + std::vector<std::pair<FolderPairCfg, BaseDirMapping*>> workLoadBinary; + + std::for_each(cfgList.begin(), cfgList.end(), + [&](const FolderPairCfg& fpCfg) + { + //a pity VC11 screws up on std::make_shared with 7 arguments... + output_tmp.push_back(std::shared_ptr<BaseDirMapping>(new BaseDirMapping(fpCfg.leftDirectoryFmt, + dirAvailable(fpCfg.leftDirectoryFmt), + fpCfg.rightDirectoryFmt, + dirAvailable(fpCfg.rightDirectoryFmt), + fpCfg.filter.nameFilter, + fpCfg.compareVar, + fileTimeTolerance))); + switch (fpCfg.compareVar) + { + case CMP_BY_TIME_SIZE: + cmpBuff.compareByTimeSize(fpCfg, *output_tmp.back()); + break; + case CMP_BY_CONTENT: + workLoadBinary.push_back(std::make_pair(fpCfg, &*output_tmp.back())); + break; + } + }); + //process binary comparison in one block + cmpBuff.compareByContent(workLoadBinary); + + assert(output_tmp.size() == cfgList.size()); + + //--------- set initial sync-direction -------------------------------------------------- + + for (auto j = begin(output_tmp); j != end(output_tmp); ++j) + { + const FolderPairCfg& fpCfg = cfgList[j - output_tmp.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!!! + output_tmp.swap(output); + } + catch (const std::bad_alloc& e) + { + callback.reportFatalError(_("Out of memory!") + L" " + utfCvrtTo<std::wstring>(e.what())); + } + catch (const std::exception& e) + { + callback.reportFatalError(utfCvrtTo<std::wstring>(e.what())); + } +} diff --git a/file_hierarchy.h b/file_hierarchy.h index d003e9ef..eb5cc91e 100644 --- a/file_hierarchy.h +++ b/file_hierarchy.h @@ -44,23 +44,10 @@ FileId getFileId(const FileDescriptor& fd) { return FileId(fd.devId, fd.fileIdx) struct LinkDescriptor { - enum LinkType - { - TYPE_DIR, //Windows: dir symlink; Linux: dir symlink - TYPE_FILE //Windows: file symlink; Linux: file symlink or broken link (or other symlink, pathological) - }; - - LinkDescriptor() : type(TYPE_FILE) {} - LinkDescriptor(Int64 lastWriteTimeRawIn, - const Zstring& targetPathIn, - LinkType lt) : - lastWriteTimeRaw(lastWriteTimeRawIn), - targetPath(targetPathIn), - type(lt) {} + LinkDescriptor() {} + explicit LinkDescriptor(Int64 lastWriteTimeRawIn) : lastWriteTimeRaw(lastWriteTimeRawIn) {} - Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC, same semantics like time_t (== signed long) - Zstring targetPath; //optional: symlink "content", may be empty if determination failed - LinkType type; //type is required for Windows only! On Linux there is no such thing => consider this when comparing Symbolic Links! + Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC, same semantics like time_t (== signed long) }; @@ -424,10 +411,11 @@ private: virtual void removeObjectR() = 0; //categorization - CompareFilesResult cmpResult; std::unique_ptr<std::wstring> cmpResultDescr; //only filled if getCategory() == FILE_CONFLICT or FILE_DIFFERENT_METADATA + CompareFilesResult cmpResult; bool selectedForSynchronization; + SyncDirection syncDir; std::unique_ptr<std::wstring> 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!) @@ -530,8 +518,6 @@ public: virtual void accept(FSObjectVisitor& visitor) const; template <SelectedSide side> zen::Int64 getLastWriteTime() const; //write time of the link, NOT target! - template <SelectedSide side> LinkDescriptor::LinkType getLinkType() const; - template <SelectedSide side> const Zstring& getTargetPath() const; CompareSymlinkResult getLinkCategory() const; //returns actually used subset of CompareFilesResult @@ -1145,34 +1131,6 @@ zen::Int64 SymLinkMapping::getLastWriteTime<RIGHT_SIDE>() const } -template <> inline -LinkDescriptor::LinkType SymLinkMapping::getLinkType<LEFT_SIDE>() const -{ - return dataLeft.type; -} - - -template <> inline -LinkDescriptor::LinkType SymLinkMapping::getLinkType<RIGHT_SIDE>() const -{ - return dataRight.type; -} - - -template <> inline -const Zstring& SymLinkMapping::getTargetPath<LEFT_SIDE>() const -{ - return dataLeft.targetPath; -} - - -template <> inline -const Zstring& SymLinkMapping::getTargetPath<RIGHT_SIDE>() const -{ - return dataRight.targetPath; -} - - inline CompareSymlinkResult SymLinkMapping::getLinkCategory() const { diff --git a/lib/cmp_filetime.h b/lib/cmp_filetime.h index eb595ace..4e75675b 100644 --- a/lib/cmp_filetime.h +++ b/lib/cmp_filetime.h @@ -11,9 +11,9 @@ inline bool sameFileTime(const Int64& a, const Int64& b, size_t tolerance) { if (a < b) - return b <= a + static_cast<int>(tolerance); + return b <= a + static_cast<ptrdiff_t>(tolerance); else - return a <= b + static_cast<int>(tolerance); + return a <= b + static_cast<ptrdiff_t>(tolerance); } //--------------------------------------------------------------------------------------------------------------- diff --git a/lib/db_file.cpp b/lib/db_file.cpp index aa893711..e4e3d748 100644 --- a/lib/db_file.cpp +++ b/lib/db_file.cpp @@ -38,8 +38,8 @@ typedef std::map<UniqueId, BinaryStream> StreamMapping; //list of streams ordere template <SelectedSide side> inline Zstring getDBFilename(const BaseDirMapping& baseMap, bool tempfile = false) { - //Linux and Windows builds are binary incompatible: different file id?, problem with case sensitivity? - //what about endianess!? + //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 @@ -49,13 +49,11 @@ Zstring getDBFilename(const BaseDirMapping& baseMap, bool tempfile = false) //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 baseMap.getBaseDirPf<side>() + dbname; } //####################################################################################################################################### -//save/load streams void saveStreams(const StreamMapping& streamList, const Zstring& filename) //throw FileError { BinStreamOut streamOut; @@ -75,9 +73,11 @@ void saveStreams(const StreamMapping& streamList, const Zstring& filename) //thr writeContainer<BinaryStream>(streamOut, it->second); } + assert(!somethingExists(filename)); //orphan tmp files should be cleaned up already at this point! saveBinStream(filename, streamOut.get()); //throw FileError #ifdef FFS_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 } @@ -97,7 +97,7 @@ StreamMapping loadStreams(const Zstring& filename) //throw FileError, FileErrorD throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filename))); const int version = readNumber<std::int32_t>(streamIn); //throw UnexpectedEndOfStreamError - if (version != DB_FILE_FORMAT_VER) //read file format version# + if (version != DB_FILE_FORMAT_VER) //read file format version number throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtFileName(filename))); //read stream lists @@ -228,8 +228,12 @@ private: static void write(BinStreamOut& output, const LinkDescriptor& descr) { writeNumber<std::int64_t>(output, to<std:: int64_t>(descr.lastWriteTimeRaw)); - writeUtf8(output, descr.targetPath); - writeNumber<std::int32_t>(output, descr.type); + + warn_static("implement proper migration!") + //writeUtf8(output, descr.targetPath); + writeUtf8(output, Zstring()); + //writeNumber<std::int32_t>(output, descr.type); + writeNumber<std::int32_t>(output, 0); } static void write(BinStreamOut& output, const InSyncDir::InSyncStatus& status) @@ -249,6 +253,10 @@ private: void process(const std::pair<Zstring, InSyncSymlink>& symlinkPair) { writeUtf8(outputBoth, symlinkPair.first); + + warn_static("new parameter: imp proper migration!") + //writeNumber<std::int32_t>(outputBoth, symlinkPair.second.inSyncType); + write(outputLeft, symlinkPair.second.left); write(outputRight, symlinkPair.second.right); } @@ -352,8 +360,12 @@ private: static void read(BinStreamIn& input, LinkDescriptor& descr) { descr.lastWriteTimeRaw = readNumber<std::int64_t>(input); - descr.targetPath = readUtf8(input); //file name - descr.type = static_cast<LinkDescriptor::LinkType>(readNumber<std::int32_t>(input)); + + warn_static("implement proper migration!") + //descr.targetPath = readUtf8(input); + readUtf8(input); + //descr.type = static_cast<LinkDescriptor::LinkType>(readNumber<std::int32_t>(input)); + readNumber<std::int32_t>(input); } static void read(BinStreamIn& input, InSyncDir::InSyncStatus& status) @@ -364,10 +376,10 @@ private: void recurse(InSyncDir& container) { size_t fileCount = readNumber<std::uint32_t>(inputBoth); - while (fileCount-- != 0) + while (fileCount-- != 0) { const Zstring shortName = readUtf8(inputBoth); - const auto inSyncType = static_cast<InSyncFile::InSyncType>(readNumber<std::int32_t>(inputBoth)); + const auto inSyncType = static_cast<InSyncType>(readNumber<std::int32_t>(inputBoth)); FileDescriptor dataL; FileDescriptor dataR; @@ -378,20 +390,24 @@ private: } size_t linkCount = readNumber<std::uint32_t>(inputBoth); - while (linkCount-- != 0) + while (linkCount-- != 0) { const Zstring shortName = readUtf8(inputBoth); + warn_static("new parameter: imp proper migration!") + const auto inSyncType = IN_SYNC_BINARY_EQUAL; + //const auto inSyncType = static_cast<InSyncType>(readNumber<std::int32_t>(inputBoth)); + LinkDescriptor dataL; LinkDescriptor dataR; read(inputLeft, dataL); read(inputRight, dataR); - container.addSymlink(shortName, dataL, dataR); + container.addSymlink(shortName, dataL, dataR, inSyncType); } size_t dirCount = readNumber<std::uint32_t>(inputBoth); - while (dirCount-- != 0) + while (dirCount-- != 0) { const Zstring shortName = readUtf8(inputBoth); @@ -448,25 +464,44 @@ private: } template <class M, class V> - static V& updateItem(M& map, const Zstring& key, const V& value) //efficient create or update without "default-constructible" requirement (Effective STL, item 24) + 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 FFS_WIN || defined FFS_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 FFS_WIN || defined FFS_MAC //caveat: key might need to be updated, too, if there is a change in short name case!!! + #if defined FFS_WIN || defined FFS_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; } - else -#endif - { - it->second = value; - return it->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) @@ -478,6 +513,10 @@ private: { if (fileMap.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(fileMap.getShortName<LEFT_SIDE>() == fileMap.getShortName<RIGHT_SIDE>()); + //create or update new "in-sync" state InSyncFile& file = updateItem(dbFiles, fileMap.getObjShortName(), InSyncFile(FileDescriptor(fileMap.getLastWriteTime<LEFT_SIDE>(), @@ -487,10 +526,8 @@ private: fileMap.getFileSize <RIGHT_SIDE>(), fileMap.getFileId <RIGHT_SIDE>()), binaryComparison_ ? - InSyncFile::IN_SYNC_BINARY_EQUAL : - InSyncFile::IN_SYNC_ATTRIBUTES_EQUAL)); //efficient add or update (Effective STL, item 24) - //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!!! + IN_SYNC_BINARY_EQUAL : + IN_SYNC_ATTRIBUTES_EQUAL)); toPreserve.insert(&file); } else //not in sync: preserve last synchronous state @@ -522,14 +559,15 @@ private: { if (linkMap.getLinkCategory() == SYMLINK_EQUAL) //data in sync: write current state { + assert(linkMap.getShortName<LEFT_SIDE>() == linkMap.getShortName<RIGHT_SIDE>()); + //create or update new "in-sync" state InSyncSymlink& link = updateItem(dbLinks, linkMap.getObjShortName(), - InSyncSymlink(LinkDescriptor(linkMap.getLastWriteTime<LEFT_SIDE>(), - linkMap.getTargetPath <LEFT_SIDE>(), - linkMap.getLinkType <LEFT_SIDE>()), - LinkDescriptor(linkMap.getLastWriteTime<RIGHT_SIDE>(), - linkMap.getTargetPath <RIGHT_SIDE>(), - linkMap.getLinkType <RIGHT_SIDE>()))); //efficient add or update (Effective STL, item 24) + InSyncSymlink(LinkDescriptor(linkMap.getLastWriteTime<LEFT_SIDE>()), + LinkDescriptor(linkMap.getLastWriteTime<RIGHT_SIDE>()), + binaryComparison_ ? + IN_SYNC_BINARY_EQUAL : + IN_SYNC_ATTRIBUTES_EQUAL)); toPreserve.insert(&link); } else //not in sync: preserve last synchronous state @@ -563,6 +601,8 @@ private: { case DIR_EQUAL: { + assert(dirMap.getShortName<LEFT_SIDE>() == dirMap.getShortName<RIGHT_SIDE>()); + //update directory entry only (shallow), but do *not touch* exising child elements!!! const Zstring& key = dirMap.getObjShortName(); auto insertResult = dbDirs.insert(std::make_pair(key, InSyncDir(InSyncDir::STATUS_IN_SYNC))); //get or create diff --git a/lib/db_file.h b/lib/db_file.h index c080081c..181a433e 100644 --- a/lib/db_file.h +++ b/lib/db_file.h @@ -14,15 +14,15 @@ namespace zen { const Zstring SYNC_DB_FILE_ENDING = Zstr(".ffs_db"); +enum InSyncType +{ + IN_SYNC_BINARY_EQUAL, //checked file content + IN_SYNC_ATTRIBUTES_EQUAL, //only "looks" like they're equal +}; + //artificial hierarchy of last synchronous state: struct InSyncFile { - enum InSyncType - { - IN_SYNC_BINARY_EQUAL, //checked file content - IN_SYNC_ATTRIBUTES_EQUAL, //only "looks" like they're equal - }; - InSyncFile(const FileDescriptor& l, const FileDescriptor& r, InSyncType type) : left(l), right(r), inSyncType(type) {} FileDescriptor left; FileDescriptor right; @@ -31,9 +31,10 @@ struct InSyncFile struct InSyncSymlink { - InSyncSymlink(const LinkDescriptor& l, const LinkDescriptor& r) : left(l), right(r) {} + InSyncSymlink(const LinkDescriptor& l, const LinkDescriptor& r, InSyncType type) : left(l), right(r), inSyncType(type) {} LinkDescriptor left; LinkDescriptor right; + InSyncType inSyncType; }; struct InSyncDir @@ -66,14 +67,14 @@ struct InSyncDir return dirs.insert(std::make_pair(shortName, InSyncDir(statusIn))).first->second; } - void addFile(const Zstring& shortName, const FileDescriptor& dataL, const FileDescriptor& dataR, InSyncFile::InSyncType type) + void addFile(const Zstring& shortName, const FileDescriptor& dataL, const FileDescriptor& dataR, InSyncType type) { files.insert(std::make_pair(shortName, InSyncFile(dataL, dataR, type))); } - void addSymlink(const Zstring& shortName, const LinkDescriptor& dataL, const LinkDescriptor& dataR) + void addSymlink(const Zstring& shortName, const LinkDescriptor& dataL, const LinkDescriptor& dataR, InSyncType type) { - symlinks.insert(std::make_pair(shortName, InSyncSymlink(dataL, dataR))); + symlinks.insert(std::make_pair(shortName, InSyncSymlink(dataL, dataR, type))); } }; @@ -85,4 +86,4 @@ std::shared_ptr<InSyncDir> loadLastSynchronousState(const BaseDirMapping& baseMa void saveLastSynchronousState(const BaseDirMapping& baseMapping); //throw FileError } -#endif // DBFILE_H_INCLUDED +#endif //DBFILE_H_INCLUDED diff --git a/lib/dir_exist_async.h b/lib/dir_exist_async.h index 678a0235..b96dc7e1 100644 --- a/lib/dir_exist_async.h +++ b/lib/dir_exist_async.h @@ -20,7 +20,8 @@ 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! -std::set<Zstring, LessFilename> getExistingDirsUpdating(const std::vector<Zstring>& dirnames, bool allowUserInteraction, ProcessCallback& procCallback) +//- avoid checking duplicate entries by design: set<Zstring, LessFilename> +std::set<Zstring, LessFilename> getExistingDirsUpdating(const std::set<Zstring, LessFilename>& dirnames, bool allowUserInteraction, ProcessCallback& procCallback) { using namespace zen; @@ -64,8 +65,8 @@ std::set<Zstring, LessFilename> getExistingDirsUpdating(const std::vector<Zstrin inline //also silences Clang "unused function" for compilation units depending from getExistingDirsUpdating() only bool dirExistsUpdating(const Zstring& dirname, bool allowUserInteraction, ProcessCallback& procCallback) { - std::vector<Zstring> dirnames; - dirnames.push_back(dirname); + std::set<Zstring, LessFilename> dirnames; + dirnames.insert(dirname); std::set<Zstring, LessFilename> dirsEx = getExistingDirsUpdating(dirnames, allowUserInteraction, procCallback); return dirsEx.find(dirname) != dirsEx.end(); } diff --git a/lib/dir_lock.cpp b/lib/dir_lock.cpp index 328b87d3..f3a16677 100644 --- a/lib/dir_lock.cpp +++ b/lib/dir_lock.cpp @@ -75,7 +75,6 @@ public: void emitLifeSign() const //try to append one byte...; throw() { const char buffer[1] = {' '}; - #ifdef FFS_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!!! @@ -99,16 +98,16 @@ public: return; DWORD bytesWritten = 0; //this parameter is NOT optional: http://blogs.msdn.com/b/oldnewthing/archive/2013/04/04/10407417.aspx - /*bool rv = */ - ::WriteFile(fileHandle, //__in HANDLE hFile, - buffer, //__out LPVOID lpBuffer, - 1, //__in DWORD nNumberOfBytesToWrite, - &bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten, - nullptr); //__inout_opt LPOVERLAPPED lpOverlapped + 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 FFS_LINUX || defined FFS_MAC const int fileHandle = ::open(lockfilename_.c_str(), O_WRONLY | O_APPEND); - if (fileHandle < 0) + if (fileHandle == -1) return; ZEN_ON_SCOPE_EXIT(::close(fileHandle)); @@ -537,13 +536,15 @@ bool tryLock(const Zstring& lockfilename) //throw FileError else throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(lockfilename)) + L"\n\n" + zen::getLastErrorFormatted()); } - ::CloseHandle(fileHandle); + ScopeGuard guardLockFile = zen::makeGuard([&] { removeFile(lockfilename); }); + FileOutput fileOut(fileHandle, lockfilename); //pass handle ownership - ::SetFileAttributes(applyLongPathPrefix(lockfilename).c_str(), FILE_ATTRIBUTE_HIDDEN); //(try to) hide it + //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 FFS_LINUX || defined FFS_MAC - //O_EXCL contains a race condition on NFS file systems: http://linux.die.net/man/2/open ::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) { @@ -552,13 +553,19 @@ bool tryLock(const Zstring& lockfilename) //throw FileError else throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(lockfilename)) + L"\n\n" + zen::getLastErrorFormatted()); } - ::close(fileHandle); -#endif - ScopeGuard guardLockFile = zen::makeGuard([&] { removeFile(lockfilename); }); + FileOutputUnbuffered fileOut(fileHandle, lockfilename); //pass handle ownership +#endif //write housekeeping info: user, process info, lock GUID - writeLockInfo(lockfilename); //throw FileError + 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; @@ -639,6 +646,8 @@ public: private: LockAdmin() {} + LockAdmin(const LockAdmin&); //=delete + LockAdmin& operator=(const LockAdmin&); //=delete typedef std::string UniqueId; typedef std::map<Zstring, UniqueId, LessFilename> FileToGuidMap; //n:1 handle uppper/lower case correctly diff --git a/lib/ffs_paths.cpp b/lib/ffs_paths.cpp index 82232b5c..5ee4a3eb 100644 --- a/lib/ffs_paths.cpp +++ b/lib/ffs_paths.cpp @@ -69,7 +69,7 @@ Zstring zen::getResourceDir() else //use OS' standard paths return appendSeparator(toZ(wxStandardPathsBase::Get().GetResourcesDir())); #elif defined FFS_MAC - return appendSeparator(toZ(wxStandardPathsBase::Get().GetResourcesDir())); + return appendSeparator(toZ(wxStandardPathsBase::Get().GetResourcesDir())); //if packaged, used "Contents/Resources", else the executable directory #endif } diff --git a/lib/ffs_paths.h b/lib/ffs_paths.h index cb0b9c3c..28516a3f 100644 --- a/lib/ffs_paths.h +++ b/lib/ffs_paths.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef STANDARDPATHS_H_INCLUDED -#define STANDARDPATHS_H_INCLUDED +#ifndef STANDARDPATHS_H_84275908342534253425 +#define STANDARDPATHS_H_84275908342534253425 #include <zen/zstring.h> @@ -22,4 +22,4 @@ Zstring getFreeFileSyncLauncher(); //full path to application launcher C:\...\Fr bool manualProgramUpdateRequired(); } -#endif // STANDARDPATHS_H_INCLUDED +#endif //STANDARDPATHS_H_84275908342534253425 diff --git a/lib/generate_logfile.h b/lib/generate_logfile.h index c441de66..31f7bd43 100644 --- a/lib/generate_logfile.h +++ b/lib/generate_logfile.h @@ -76,16 +76,17 @@ std::wstring generateLogHeader(const SummaryInfo& s) results.push_back(tabSpace + _("Total time:") + L" " + copyStringTo<std::wstring>(wxTimeSpan::Seconds(s.totalTime).Format())); - //calculate max width, this considers UTF-16 only, not true Unicode... + //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()); }); - for (size_t i = 0; i < sepLineLen; ++i) output += L'_'; //this considers UTF-16 only, not true Unicode!!! + output.resize(output.size() + sepLineLen + 1, L'_'); output += L'\n'; - std::for_each(results.begin(), results.end(), [&](const std::wstring& str) { output += str; output += L'\n'; }); + std::for_each(results.begin(), results.end(), [&](const std::wstring& str) { output += L'|'; output += str; output += L'\n'; }); - for (size_t i = 0; i < sepLineLen; ++i) output += L'_'; + output += L'|'; + output.resize(output.size() + sepLineLen, L'_'); output += L'\n'; return output; diff --git a/lib/help_provider.h b/lib/help_provider.h index 8227efb5..040eb33c 100644 --- a/lib/help_provider.h +++ b/lib/help_provider.h @@ -95,4 +95,4 @@ void displayHelpEntry(wxWindow* parent) } } -#endif // HELPPROVIDER_H_INCLUDED +#endif //HELPPROVIDER_H_INCLUDED diff --git a/lib/icon_buffer.cpp b/lib/icon_buffer.cpp index 3912849e..04364b32 100644 --- a/lib/icon_buffer.cpp +++ b/lib/icon_buffer.cpp @@ -35,8 +35,9 @@ boost::thread::id mainThreadId = boost::this_thread::get_id(); #endif #ifdef FFS_WIN -#define DEF_DLL_FUN(name) DllFun<thumb::FunType_##name> name(thumb::getDllName(), thumb::funName_##name); +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::FunType_##name>(thumb::getDllName(), thumb::funName_##name) : DllFun<thumb::FunType_##name>(); DEF_DLL_FUN(getIconByIndex); // DEF_DLL_FUN(getThumbnail); //let's spare the boost::call_once hustle and allocate statically DEF_DLL_FUN(releaseImageData); // @@ -67,7 +68,7 @@ public: { if (handle_ != nullptr) #ifdef FFS_WIN - releaseImageData(handle_); + releaseImageData(handle_); //should be checked already before creating IconHolder! #elif defined FFS_LINUX ::g_object_unref(handle_); //superseedes "::gdk_pixbuf_unref"! #elif defined FFS_MAC @@ -203,10 +204,10 @@ IconHolder getIconByAttribute(LPCWSTR pszPath, DWORD dwFileAttributes, IconBuffe if (!imgList) //no need to IUnknown::Release() imgList! return IconHolder(); - if (!getIconByIndex) - return IconHolder(); + if (getIconByIndex && releaseImageData) + return IconHolder(getIconByIndex(fileInfo.iIcon, getThumbSizeType(sz))); - return IconHolder(getIconByIndex(fileInfo.iIcon, getThumbSizeType(sz))); + return IconHolder(); } @@ -237,7 +238,7 @@ IconHolder iconHolderFromGicon(GIcon* gicon, IconBuffer::IconSize sz) IconHolder getThumbnailIcon(const Zstring& filename, int requestedSize) //return 0 on failure { #ifdef FFS_WIN - if (getThumbnail) + if (getThumbnail && releaseImageData) return IconHolder(getThumbnail(filename.c_str(), requestedSize)); #elif defined FFS_LINUX @@ -375,13 +376,13 @@ IconHolder getAssociatedIcon(const Zstring& filename, IconBuffer::IconSize sz) const bool isLink = (fileInfo.dwAttributes & SFGAO_LINK) != 0; - if (getIconByIndex) + if (getIconByIndex && releaseImageData) if (const thumb::ImageData* imgData = getIconByIndex(fileInfo.iIcon, getThumbSizeType(sz))) return IconHolder(imgData); } #elif defined FFS_LINUX - GFile* file = ::g_file_new_for_path(filename.c_str()); //never fails + 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)) @@ -462,7 +463,7 @@ public: //must be called by main thread only! => wxBitmap is NOT thread-safe like an int (non-atomic ref-count!!!) Opt<wxBitmap> retrieveFileIcon(const Zstring& fileName) { - assert(boost::this_thread::get_id() == mainThreadId ); + assert(boost::this_thread::get_id() == mainThreadId); boost::lock_guard<boost::mutex> dummy(lockIconList); auto it = iconList.find(fileName); if (it == iconList.end()) @@ -481,7 +482,7 @@ public: //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 ); + assert(boost::this_thread::get_id() == mainThreadId); boost::lock_guard<boost::mutex> dummy(lockIconList); while (iconList.size() > BUFFER_SIZE_MAX) { diff --git a/lib/localization.cpp b/lib/localization.cpp index c29860f7..f050e255 100644 --- a/lib/localization.cpp +++ b/lib/localization.cpp @@ -18,7 +18,10 @@ #include "parse_lng.h" #include "ffs_paths.h" -#ifdef FFS_MAC +#ifdef FFS_LINUX +#include <wchar.h> //wcscasecmp + +#elif defined FFS_MAC #include <CoreServices/CoreServices.h> #endif @@ -27,10 +30,10 @@ using namespace zen; namespace { -class FFSLocale : public TranslationHandler +class FFSTranslation : public TranslationHandler { public: - FFSLocale(const wxString& filename, wxLanguage languageId); //throw lngfile::ParsingError, parse_plural::ParsingError + FFSTranslation(const std::wstring& filename, wxLanguage languageId); //throw lngfile::ParsingError, parse_plural::ParsingError wxLanguage langId() const { return langId_; } @@ -48,8 +51,8 @@ public: auto it = transMappingPl.find(std::make_pair(singular, plural)); if (it != transMappingPl.end()) { - const int formNo = pluralParser->getForm(n); - if (0 <= formNo && formNo < static_cast<int>(it->second.size())) + const size_t formNo = pluralParser->getForm(n); + if (formNo < it->second.size()) return it->second[formNo]; } return n == 1 ? singular : plural; //fallback @@ -57,7 +60,7 @@ public: private: typedef hash_map<std::wstring, std::wstring> Translation; //hash_map is 15% faster than std::map on GCC - typedef std::map<std::pair<std::wstring, std::wstring>, std::vector<std::wstring> > TranslationPlural; + typedef std::map<std::pair<std::wstring, std::wstring>, std::vector<std::wstring>> TranslationPlural; Translation transMapping; //map original text |-> translation TranslationPlural transMappingPl; @@ -66,7 +69,7 @@ private: }; -FFSLocale::FFSLocale(const wxString& filename, wxLanguage languageId) : langId_(languageId) //throw lngfile::ParsingError, parse_plural::ParsingError +FFSTranslation::FFSTranslation(const std::wstring& filename, wxLanguage languageId) : langId_(languageId) //throw lngfile::ParsingError, parse_plural::ParsingError { std::string inputStream; try @@ -120,7 +123,8 @@ public: virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) { return LINK_SKIP; } virtual std::shared_ptr<TraverseCallback> onDir(const Zchar* shortName, const Zstring& fullName) { return nullptr; } - virtual HandleError onError(const std::wstring& msg) { assert(false); return ON_ERROR_IGNORE; } //errors are not really critical in this context + virtual HandleError reportDirError (const std::wstring& msg) { assert(false); return ON_ERROR_IGNORE; } //errors are not really critical in this context + virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) { assert(false); return ON_ERROR_IGNORE; } // private: std::vector<Zstring>& lngFiles_; @@ -145,10 +149,11 @@ struct LessTranslation : public std::binary_function<ExistingTranslations::Entry return rv == CSTR_LESS_THAN; //convert to C-style string compare result #elif defined FFS_LINUX - return lhs.languageName.CmpNoCase(rhs.languageName) < 0; + return ::wcscasecmp(lhs.languageName.c_str(), rhs.languageName.c_str()) < 0; //ignores case; locale-dependent! + //return lhs.languageName.CmpNoCase(rhs.languageName) < 0; #elif defined FFS_MAC - auto allocCFStringRef = [](const wxString& str) -> CFStringRef //output not owned! + auto allocCFStringRef = [](const std::wstring& str) -> CFStringRef //output not owned! { return ::CFStringCreateWithCString(nullptr, //CFAllocatorRef alloc, utfCvrtTo<std::string>(str).c_str(), //const char *cStr, @@ -205,10 +210,10 @@ ExistingTranslations::ExistingTranslations() { ExistingTranslations::Entry newEntry; newEntry.languageID = locInfo->Language; - newEntry.languageName = utfCvrtTo<wxString>(lngHeader.languageName); - newEntry.languageFile = utfCvrtTo<wxString>(*it); - newEntry.translatorName = utfCvrtTo<wxString>(lngHeader.translatorName); - newEntry.languageFlag = utfCvrtTo<wxString>(lngHeader.flagFile); + newEntry.languageName = utfCvrtTo<std::wstring>(lngHeader.languageName); + newEntry.languageFile = utfCvrtTo<std::wstring>(*it); + newEntry.translatorName = utfCvrtTo<std::wstring>(lngHeader.translatorName); + newEntry.languageFlag = utfCvrtTo<std::wstring>(lngHeader.flagFile); locMapping.push_back(newEntry); } } @@ -253,6 +258,21 @@ wxLanguage mapLanguageDialect(wxLanguage language) 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: @@ -273,14 +293,6 @@ wxLanguage mapLanguageDialect(wxLanguage language) case wxLANGUAGE_ENGLISH_PHILIPPINES: return wxLANGUAGE_ENGLISH_US; - //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_FRENCH case wxLANGUAGE_FRENCH_BELGIAN: case wxLANGUAGE_FRENCH_CANADIAN: @@ -289,29 +301,36 @@ wxLanguage mapLanguageDialect(wxLanguage language) case wxLANGUAGE_FRENCH_SWISS: return wxLANGUAGE_FRENCH; - //variants of wxLANGUAGE_DUTCH - case wxLANGUAGE_DUTCH_BELGIAN: - return wxLANGUAGE_DUTCH; + //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_CHINESE_SIMPLIFIED - case wxLANGUAGE_CHINESE: - case wxLANGUAGE_CHINESE_SINGAPORE: - return wxLANGUAGE_CHINESE_SIMPLIFIED; + //variants of wxLANGUAGE_NORWEGIAN_BOKMAL + case wxLANGUAGE_NORWEGIAN_NYNORSK: + return wxLANGUAGE_NORWEGIAN_BOKMAL; - //variants of wxLANGUAGE_CHINESE_TRADITIONAL - case wxLANGUAGE_CHINESE_TAIWAN: - case wxLANGUAGE_CHINESE_HONGKONG: - case wxLANGUAGE_CHINESE_MACAU: - return wxLANGUAGE_CHINESE_TRADITIONAL; + //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: @@ -339,26 +358,24 @@ wxLanguage mapLanguageDialect(wxLanguage language) case wxLANGUAGE_SWEDISH_FINLAND: return wxLANGUAGE_SWEDISH; - //variants of wxLANGUAGE_NORWEGIAN_BOKMAL - case wxLANGUAGE_NORWEGIAN_NYNORSK: - return wxLANGUAGE_NORWEGIAN_BOKMAL; - //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_SLOVENIAN: - //case wxLANGUAGE_HUNGARIAN: //case wxLANGUAGE_PORTUGUESE: //case wxLANGUAGE_PORTUGUESE_BRAZILIAN: //case wxLANGUAGE_SCOTS_GAELIC: - //case wxLANGUAGE_KOREAN: + //case wxLANGUAGE_SLOVENIAN: + //case wxLANGUAGE_TURKISH: //case wxLANGUAGE_UKRAINIAN: - //case wxLANGUAGE_CROATIAN: default: return language; } @@ -387,6 +404,8 @@ public: locLng = lng; } + static void release() { locale.reset(); locLng = wxLANGUAGE_UNKNOWN; } + static wxLanguage getLanguage() { return locLng; } private: @@ -394,7 +413,13 @@ private: static wxLanguage locLng; }; std::unique_ptr<wxLocale> wxWidgetsLocale::locale; -wxLanguage wxWidgetsLocale::locLng = wxLANGUAGE_UNKNOWN; +wxLanguage wxWidgetsLocale::locLng = wxLANGUAGE_UNKNOWN; +} + + +void zen::releaseWxLocale() +{ + wxWidgetsLocale::release(); } @@ -404,7 +429,7 @@ void zen::setLanguage(int language) //throw FileError return; //support polling //(try to) retrieve language file - wxString languageFile; + std::wstring languageFile; for (auto it = ExistingTranslations::get().begin(); it != ExistingTranslations::get().end(); ++it) if (it->languageID == language) @@ -419,7 +444,7 @@ void zen::setLanguage(int language) //throw FileError else try { - zen::setTranslator(new FFSLocale(languageFile, static_cast<wxLanguage>(language))); //throw lngfile::ParsingError, parse_plural::ParsingError + zen::setTranslator(new FFSTranslation(languageFile, static_cast<wxLanguage>(language))); //throw lngfile::ParsingError, parse_plural::ParsingError } catch (lngfile::ParsingError& e) { @@ -438,10 +463,9 @@ void zen::setLanguage(int language) //throw FileError } - int zen::getLanguage() { - const FFSLocale* loc = dynamic_cast<const FFSLocale*>(zen::getTranslator()); + const FFSTranslation* loc = dynamic_cast<const FFSTranslation*>(zen::getTranslator()); return loc ? loc->langId() : wxLANGUAGE_ENGLISH_US; } diff --git a/lib/localization.h b/lib/localization.h index 125be0fd..2d871dd7 100644 --- a/lib/localization.h +++ b/lib/localization.h @@ -4,12 +4,12 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef MISC_H_INCLUDED -#define MISC_H_INCLUDED +#ifndef LOCALIZATION_H_8917342083178321534 +#define LOCALIZATION_H_8917342083178321534 #include <vector> #include <zen/file_error.h> -#include <wx/string.h> +//#include <wx/string.h> namespace zen { @@ -19,10 +19,10 @@ public: struct Entry { int languageID; - wxString languageName; - wxString languageFile; - wxString translatorName; - wxString languageFlag; + std::wstring languageName; + std::wstring languageFile; + std::wstring translatorName; + std::wstring languageFlag; }; static const std::vector<Entry>& get(); @@ -34,9 +34,13 @@ private: std::vector<Entry> 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 // MISC_H_INCLUDED +#endif //LOCALIZATION_H_8917342083178321534 diff --git a/lib/parallel_scan.cpp b/lib/parallel_scan.cpp index 37dd350e..33d8174f 100644 --- a/lib/parallel_scan.cpp +++ b/lib/parallel_scan.cpp @@ -5,15 +5,14 @@ // ************************************************************************** #include "parallel_scan.h" -#include <boost/detail/atomic_count.hpp> -#include "db_file.h" -#include "lock_holder.h" #include <zen/file_traverser.h> #include <zen/file_error.h> #include <zen/thread.h> //includes <boost/thread.hpp> #include <zen/scope_guard.h> #include <zen/fixed_list.h> #include <boost/detail/atomic_count.hpp> +#include "db_file.h" +#include "lock_holder.h" using namespace zen; @@ -283,21 +282,24 @@ public: TraverserShared(long threadID, SymLinkHandling handleSymlinks, const HardFilter::FilterRef& filter, - std::set<Zstring>& failedReads, + std::set<Zstring>& failedDirReads, + std::set<Zstring>& failedItemReads, AsyncCallback& acb) : handleSymlinks_(handleSymlinks), filterInstance(filter), - failedReads_(failedReads), + failedDirReads_(failedDirReads), + failedItemReads_(failedItemReads), acb_(acb), threadID_(threadID) {} const SymLinkHandling handleSymlinks_; const HardFilter::FilterRef filterInstance; //always bound! - std::set<Zstring>& failedReads_; //relative postfixed names of directories that could not be read (empty for root) + std::set<Zstring>& failedDirReads_; + std::set<Zstring>& failedItemReads_; AsyncCallback& acb_; - long threadID_; + const long threadID_; }; @@ -315,7 +317,8 @@ public: onDir (const Zchar* shortName, const Zstring& fullName); 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 HandleError onError (const std::wstring& msg); + virtual HandleError reportDirError (const std::wstring& msg); + virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName); private: TraverserShared& cfg; @@ -343,9 +346,7 @@ void DirCallback::onFile(const Zchar* shortName, const Zstring& fullName, const if (!cfg.filterInstance->passFileFilter(relNameParentPf_ + fileNameShort)) return; - // std::string fileId = details.fileSize >= 1024 * 1024U ? - // util::retrieveFileID(fullName) : - // std::string(); + // 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 @@ -356,7 +357,7 @@ void DirCallback::onFile(const Zchar* shortName, const Zstring& fullName, const Linux: retrieveFileID takes about 50% longer in VM! (avoidable because of redundant stat() call!) */ - output_.addSubFile(fileNameShort, FileDescriptor(details.lastWriteTimeRaw, details.fileSize, details.id)); + output_.addSubFile(fileNameShort, FileDescriptor(details.lastWriteTime, details.fileSize, details.id)); cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator } @@ -382,7 +383,7 @@ DirCallback::HandleLink DirCallback::onSymlink(const Zchar* shortName, const Zst //apply filter before processing (use relative name!) if (cfg.filterInstance->passFileFilter(relName)) //always use file filter: Link type may not be "stable" on Linux! { - output_.addSubLink(shortName, LinkDescriptor(details.lastWriteTimeRaw, details.targetPath, details.dirLink ? LinkDescriptor::TYPE_DIR : LinkDescriptor::TYPE_FILE)); + output_.addSubLink(shortName, LinkDescriptor(details.lastWriteTime)); cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator } } @@ -422,18 +423,33 @@ std::shared_ptr<TraverseCallback> DirCallback::onDir(const Zchar* shortName, con } -DirCallback::HandleError DirCallback::onError(const std::wstring& msg) +DirCallback::HandleError DirCallback::reportDirError(const std::wstring& msg) { switch (cfg.acb_.reportError(msg)) { case FillBufferCallback::ON_ERROR_IGNORE: - cfg.failedReads_.insert(relNameParentPf_); + 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, const Zchar* shortName) +{ + switch (cfg.acb_.reportError(msg)) + { + 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; } @@ -484,7 +500,8 @@ public: TraverserShared travCfg(threadID_, dirKey_.handleSymlinks_, //shared by all(!) instances of DirCallback while traversing a folder hierarchy dirKey_.filter_, - dirOutput_.failedReads, + dirOutput_.failedDirReads, + dirOutput_.failedItemReads, *acb_); DirCallback traverser(travCfg, diff --git a/lib/parallel_scan.h b/lib/parallel_scan.h index 5a52e44e..b7518428 100644 --- a/lib/parallel_scan.h +++ b/lib/parallel_scan.h @@ -46,7 +46,8 @@ bool operator<(const DirectoryKey& lhs, const DirectoryKey& rhs) struct DirectoryValue { DirContainer dirCont; - std::set<Zstring> failedReads; //relative postfixed names of directories that could not be read completely (empty string for root), e.g. access denied, or temporal network drop + std::set<Zstring> 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<Zstring> failedItemReads; //relative postfixed names (never empty) for failure to read single file/dir/symlink }; diff --git a/lib/parse_lng.h b/lib/parse_lng.h index b6af9b18..48c7044b 100644 --- a/lib/parse_lng.h +++ b/lib/parse_lng.h @@ -10,7 +10,8 @@ #include <algorithm> #include <cctype> #include <functional> -#include <list> +//#include <list> +#include <memory> #include <map> #include <set> #include <sstream> @@ -19,6 +20,8 @@ #include <vector> #include <zen/utf.h> #include <zen/string_tools.h> +#include "parse_plural.h" +//#include <zen/perf.h> namespace lngfile { @@ -28,7 +31,7 @@ typedef std::map <std::string, std::string> TranslationMap; //orig |-> translat //plural forms typedef std::pair<std::string, std::string> SingularPluralPair; //1 house| n houses typedef std::vector<std::string> PluralForms; //1 dom | 2 domy | 5 domów -typedef std::map <SingularPluralPair, PluralForms> TranslationPluralMap; //(sing/plu) |-> pluralforms +typedef std::map<SingularPluralPair, PluralForms> TranslationPluralMap; //(sing/plu) |-> pluralforms struct TransHeader { @@ -79,39 +82,36 @@ public: void addItem(const std::string& orig, const std::string& trans) { if (!transUnique.insert(orig).second) return; - - dump.push_back(RegularItem(std::make_pair(orig, trans))); - sequence.push_back(&dump.back()); + sequence.push_back(std::make_shared<RegularItem>(std::make_pair(orig, trans))); } void addPluralItem(const SingularPluralPair& orig, const PluralForms& trans) { if (!pluralUnique.insert(orig).second) return; - - dumpPlural.push_back(PluralItem(std::make_pair(orig, trans))); - sequence.push_back(&dumpPlural.back()); + sequence.push_back(std::make_shared<PluralItem>(std::make_pair(orig, trans))); } bool untranslatedTextExists() const { - for (auto it = dump.begin(); it != dump.end(); ++it) - if (it->value.second.empty()) - return true; - for (auto it = dumpPlural.begin(); it != dumpPlural.end(); ++it) - if (it->value.second.empty()) - return true; + for (auto it = sequence.begin(); it != sequence.end(); ++it) + if (const TranslationList::RegularItem* regular = dynamic_cast<const TranslationList::RegularItem*>(it->get())) + { + if (regular->value.second.empty()) + return true; + } + else if (const TranslationList::PluralItem* plural = dynamic_cast<const TranslationList::PluralItem* >(it->get())) + if (plural->value.second.empty()) + return true; return false; } private: friend std::string generateLng(const TranslationList& in, const TransHeader& header); - struct Item {virtual ~Item() {} }; + struct Item { virtual ~Item() {} }; struct RegularItem : public Item { RegularItem(const TranslationMap ::value_type& val) : value(val) {} TranslationMap ::value_type value; }; struct PluralItem : public Item { PluralItem (const TranslationPluralMap::value_type& val) : value(val) {} TranslationPluralMap::value_type value; }; - std::vector<Item*> sequence; //dynamic list of translation elements - std::list<RegularItem> dump; //manage memory - std::list<PluralItem> dumpPlural; //manage memory + std::vector<std::shared_ptr<Item>> sequence; //ordered list of translation elements std::set<TranslationMap ::key_type> transUnique; //check uniqueness std::set<TranslationPluralMap::key_type> pluralUnique; // @@ -307,9 +307,18 @@ public: { parseHeader(header); - //items - while (token().type != Token::TK_END) - parseRegular(out, pluralOut, header.pluralCount); + 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(scn.posRow(), scn.posCol()); + } } void parseHeader(TransHeader& header) @@ -350,12 +359,12 @@ public: } private: - void parseRegular(TranslationMap& out, TranslationPluralMap& pluralOut, int formCount) + 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, formCount); + return parsePlural(pluralOut, pluralInfo); std::string original = tk.text; consumeToken(Token::TK_TEXT); @@ -369,10 +378,12 @@ private: nextToken(); } consumeToken(Token::TK_TRG_END); + + validateTranslation(original, translation); //throw throw ParsingError out.insert(std::make_pair(original, translation)); } - void parsePlural(TranslationPluralMap& pluralOut, int formCount) + void parsePlural(TranslationPluralMap& pluralOut, const parse_plural::PluralFormInfo& pluralInfo) { //Token::TK_SRC_BEGIN already consumed @@ -398,16 +409,73 @@ private: consumeToken(Token::TK_TEXT); consumeToken(Token::TK_PLURAL_END); pluralList.push_back(pluralForm); - } - if (!pluralList.empty() && static_cast<int>(pluralList.size()) != formCount) //invalid number of plural forms + 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(scn.posRow(), scn.posCol()); - consumeToken(Token::TK_TRG_END); - pluralOut.insert(std::make_pair(SingularPluralPair(engSingular, engPlural), pluralList)); + 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(scn.posRow(), scn.posCol()); + }; + checkPlaceholder("%x"); + checkPlaceholder("%y"); + checkPlaceholder("%z"); + } } + void validateTranslation(const SingularPluralPair& original, const PluralForms& translation, const parse_plural::PluralFormInfo& pluralInfo) //throw ParsingError + { + //check the primary placeholder is existing at least for the second english text + if (!zen::contains(original.second, "%x")) + throw ParsingError(scn.posRow(), scn.posCol()); + + if (!translation.empty()) + { + //check for invalid number of plural forms + if (pluralInfo.getCount() != static_cast<int>(translation.size())) + throw ParsingError(scn.posRow(), scn.posCol()); + + //ensure the placeholder is used when needed + int pos = 0; + for (auto it = translation.begin(); it != translation.end(); ++it, ++pos) + if (!pluralInfo.isSingleNumberForm(pos) && !zen::contains(*it, "%x")) + throw ParsingError(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(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(scn.posRow(), scn.posCol()); + } + }; + + checkSecondaryPlaceholder("%y"); + checkSecondaryPlaceholder("%z"); + } + } void nextToken() { tk = scn.nextToken(); } const Token& token() const { return tk; } @@ -499,8 +567,8 @@ std::string generateLng(const TranslationList& in, const TransHeader& header) //items for (auto it = in.sequence.begin(); it != in.sequence.end(); ++it) { - const TranslationList::RegularItem* regular = dynamic_cast<const TranslationList::RegularItem*>(*it); - const TranslationList::PluralItem* plural = dynamic_cast<const TranslationList::PluralItem* >(*it); + const TranslationList::RegularItem* regular = dynamic_cast<const TranslationList::RegularItem*>(it->get()); + const TranslationList::PluralItem* plural = dynamic_cast<const TranslationList::PluralItem* >(it->get()); if (regular) { diff --git a/lib/parse_plural.h b/lib/parse_plural.h index c3591881..bb32f81f 100644 --- a/lib/parse_plural.h +++ b/lib/parse_plural.h @@ -38,7 +38,20 @@ private: }; +//validate plural form +class InvalidPluralForm {}; +class PluralFormInfo +{ +public: + PluralFormInfo(const std::string& definition, int pluralCount); //throw InvalidPluralForm + + int getCount() const { return static_cast<int>(formCount.size()); } + bool isSingleNumberForm(int n) const { return 0 <= n && n < static_cast<int>(formCount.size()) ? formCount[n] == 1 : false; } + +private: + std::vector<int> formCount; +}; @@ -413,6 +426,40 @@ private: inline +PluralFormInfo::PluralFormInfo(const std::string& definition, int pluralCount) //throw InvalidPluralForm +{ + if (pluralCount < 1) + throw InvalidPluralForm(); + + formCount.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<int>(formCount.size())) + ++formCount[form]; + else + throw InvalidPluralForm(); + } + } + catch (const parse_plural::ParsingError&) + { + throw InvalidPluralForm(); + } + + //ensure each form is used at least once: + if (!std::all_of(formCount.begin(), formCount.end(), [](int count) { return count >= 1; })) + throw InvalidPluralForm(); +} + + +inline PluralForm::PluralForm(const std::string& stream) : expr(implementation::Parser(stream, n_).parse()) {} //throw ParsingError } diff --git a/lib/process_xml.cpp b/lib/process_xml.cpp index 4974dcbc..78a40159 100644 --- a/lib/process_xml.cpp +++ b/lib/process_xml.cpp @@ -980,12 +980,12 @@ void readConfig(const XmlIn& in, xmlAccess::XmlGuiConfig& config) warn_static("remove after migration?") if (inGuiCfg["SyncPreviewActive"]) //obsolete name - inGuiCfg["SyncPreviewActive"](config.showSyncAction); + inGuiCfg["SyncPreviewActive"](config.highlightSyncAction); else { std::string val; if (inGuiCfg["MiddleGridView"](val)) //refactor into enum!? - config.showSyncAction = val == "Action"; + config.highlightSyncAction = val == "Action"; } } @@ -1124,7 +1124,7 @@ void readConfig(const Zstring& filename, XmlType type, ConfigType& cfg, int curr { XmlDoc doc; loadXmlDocument(filename, doc); //throw FfsXmlError - + if (getXmlType(doc) != type) //throw() throw FfsXmlError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename))); @@ -1376,7 +1376,7 @@ void writeConfig(const XmlGuiConfig& config, XmlOut& out) outGuiCfg["HideExcluded" ](config.hideExcludedItems); outGuiCfg["HandleError" ](config.handleError); - outGuiCfg["MiddleGridView"](config.showSyncAction ? "Action" : "Category"); //refactor into enum!? + outGuiCfg["MiddleGridView"](config.highlightSyncAction ? "Action" : "Category"); //refactor into enum!? } void writeConfig(const XmlBatchConfig& config, XmlOut& out) diff --git a/lib/process_xml.h b/lib/process_xml.h index 8a65d67a..626fafe0 100644 --- a/lib/process_xml.h +++ b/lib/process_xml.h @@ -50,23 +50,23 @@ struct XmlGuiConfig XmlGuiConfig() : hideExcludedItems(false), handleError(ON_GUIERROR_POPUP), - showSyncAction(true) {} //initialize values + highlightSyncAction(true) {} //initialize values zen::MainConfiguration mainCfg; bool hideExcludedItems; OnGuiError handleError; //reaction on error situation during synchronization - bool showSyncAction; + 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.showSyncAction == rhs.showSyncAction; + return lhs.mainCfg == rhs.mainCfg && + lhs.hideExcludedItems == rhs.hideExcludedItems && + lhs.handleError == rhs.handleError && + lhs.highlightSyncAction == rhs.highlightSyncAction; } diff --git a/lib/resolve_path.cpp b/lib/resolve_path.cpp index bea62da3..bf6f99a2 100644 --- a/lib/resolve_path.cpp +++ b/lib/resolve_path.cpp @@ -31,14 +31,15 @@ Zstring resolveRelativePath(const Zstring& relativeName) //note: ::GetFullPathNa const DWORD bufferSize = 10000; std::vector<wchar_t> buffer(bufferSize); - const DWORD charsWritten = ::GetFullPathName(applyLongPathPrefix(relativeName).c_str(), //__in LPCTSTR lpFileName, + //don't use long path prefix! does not work with relative paths "." and ".." + const DWORD charsWritten = ::GetFullPathName(relativeName.c_str(), //__in LPCTSTR lpFileName, bufferSize, //__in DWORD nBufferLength, &buffer[0], //__out LPTSTR lpBuffer, nullptr); //__out LPTSTR *lpFilePart if (charsWritten == 0 || charsWritten >= bufferSize) //theoretically, charsWritten cannot be == "bufferSize" return relativeName; //ERROR! Don't do anything - return removeLongPathPrefix(Zstring(&buffer[0], charsWritten)); //GetFullPathName() preserves long path prefix -> a low-level detail we don't want to leak out! + return Zstring(&buffer[0], charsWritten); } #elif defined FFS_LINUX || defined FFS_MAC @@ -545,11 +546,9 @@ 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 = expandMacros(dirString); - - dirname = expandVolumeName(dirname); //should not block + Zstring dirname = dirString; - //remove leading/trailing whitespace + //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); @@ -557,6 +556,9 @@ Zstring zen::getFormattedDirectoryName(const Zstring& dirString) // throw() if (dirname.empty()) //an empty string would later be resolved as "\"; this is not desired return Zstring(); + dirname = expandMacros(dirname); + dirname = expandVolumeName(dirname); //should not block + /* need to resolve relative paths: WINDOWS: diff --git a/lib/resolve_path.h b/lib/resolve_path.h index 975ed304..4e85c8ee 100644 --- a/lib/resolve_path.h +++ b/lib/resolve_path.h @@ -19,7 +19,7 @@ FULL directory format: - convert relative paths into absolute - trim whitespace and append file name separator */ -Zstring getFormattedDirectoryName(const Zstring& dirString); //throw() - non-blocking! no I/O! +Zstring getFormattedDirectoryName(const Zstring& dirString); //throw() - non-blocking! no I/O! not thread-safe!!!(see ::GetFullPathName()) //macro substitution only Zstring expandMacros(const Zstring& text); diff --git a/lib/resources.cpp b/lib/resources.cpp index e6691458..6f48e2e1 100644 --- a/lib/resources.cpp +++ b/lib/resources.cpp @@ -75,17 +75,17 @@ GlobalResources::GlobalResources() #ifdef FFS_WIN //for compatibility it seems we need to stick with a "real" icon - programIcon = wxIcon(L"A_PROGRAM_ICON"); + programIconFFS = wxIcon(L"A_FFS_ICON"); #elif defined FFS_LINUX //attention: make sure to not implicitly call "instance()" again => deadlock on Linux - programIcon.CopyFromBitmap(getImage(L"FreeFileSync")); //use big logo bitmap for better quality + programIconFFS.CopyFromBitmap(getImage(L"FreeFileSync")); //use big logo bitmap for better quality #elif defined FFS_MAC assert(getImage(L"FreeFileSync").GetWidth () == getImage(L"FreeFileSync").GetHeight() && getImage(L"FreeFileSync").GetWidth() % 128 == 0); //wxWidgets' bitmap to icon conversion on OS X can only deal with very specific sizes - programIcon.CopyFromBitmap(getImage(L"FreeFileSync").ConvertToImage().Scale(128, 128, wxIMAGE_QUALITY_HIGH)); //"von hinten durch die Brust ins Auge" + programIconFFS.CopyFromBitmap(getImage(L"FreeFileSync").ConvertToImage().Scale(128, 128, wxIMAGE_QUALITY_HIGH)); //"von hinten durch die Brust ins Auge" #endif } diff --git a/lib/resources.h b/lib/resources.h index a8d9469c..df651eaa 100644 --- a/lib/resources.h +++ b/lib/resources.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef RESOURCES_H_INCLUDED -#define RESOURCES_H_INCLUDED +#ifndef RESOURCES_H_8740257825342532457 +#define RESOURCES_H_8740257825342532457 #include <map> #include <wx/bitmap.h> @@ -23,7 +23,7 @@ public: //global image resource objects wxAnimation aniWink; wxAnimation aniSync; - wxIcon programIcon; + wxIcon programIconFFS; private: GlobalResources(); @@ -37,4 +37,4 @@ private: inline const wxBitmap& getResourceImage(const wxString& name) { return GlobalResources::instance().getImage(name); } -#endif // RESOURCES_H_INCLUDED +#endif //RESOURCES_H_8740257825342532457 diff --git a/lib/return_codes.h b/lib/return_codes.h index 6742c975..a37e11f2 100644 --- a/lib/return_codes.h +++ b/lib/return_codes.h @@ -25,8 +25,6 @@ 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 index d3106168..4bb299ac 100644 --- a/lib/shadow.cpp +++ b/lib/shadow.cpp @@ -8,7 +8,10 @@ #include <stdexcept> #include <zen/win.h> //includes "windows.h" #include <zen/dll.h> +#include <zen/win_ver.h> #include <zen/assert_static.h> +#include <zen/long_path_prefix.h> +#include <zen/symlink_target.h> #include "ShadowCopy/shadow.h" #include <zen/scope_guard.h> @@ -31,6 +34,8 @@ bool runningWOW64() //test if process is running under WOW64 (reference http://m } return false; } + +const bool wereVistaOrLater = vistaOrLater(); //thread-safety: init at startup } //############################################################################################################# @@ -51,14 +56,13 @@ public: throw FileError(_("Cannot access Volume Shadow Copy Service.") + L"\n" + _("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 Volume Shadow Copy Service.") + L"\n" + replaceCpy(_("Cannot load file %x."), L"%x", fmtFileName(getDllName()))); //--------------------------------------------------------------------------------------------------------- - //start shadow volume copy service: + //start volume shadow copy service: backupHandle = createShadowCopy(volumeNamePf.c_str()); if (!backupHandle) throw FileError(_("Cannot access Volume Shadow Copy Service.") + L"\n" + @@ -88,22 +92,31 @@ private: Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile, const std::function<void(const Zstring&)>& 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\<username> is a junction that may link to e.g. D:\Users\<username>, 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<wchar_t> volBuffer(bufferSize); - if (!::GetVolumePathName(inputFile.c_str(), //__in LPCTSTR lpszFileName, - &volBuffer[0], //__out LPTSTR lpszVolumePathName, - bufferSize)) //__in DWORD cchBufferLength - throw FileError(replaceCpy(_("Path %x does not contain a volume name."), L"%x", fmtFileName(inputFile))); + if (!::GetVolumePathName(filenameFinal.c_str(), //__in LPCTSTR lpszFileName, + &volBuffer[0], //__out LPTSTR lpszVolumePathName, + bufferSize)) //__in DWORD cchBufferLength + throw FileError(replaceCpy(_("Path %x does not contain a volume name."), L"%x", fmtFileName(filenameFinal))); - const Zstring volumeNamePf = appendSeparator(&volBuffer[0]); + 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 = inputFile.find(volumeNamePf); //inputFile needs NOT to begin with volumeNamePf: consider for example \\?\ prefix! + const size_t pos = filenameFinal.find(volumeNamePf); //filenameFinal needs NOT to begin with volumeNamePf: consider for example \\?\ prefix! if (pos == Zstring::npos) { std::wstring msg = _("Volume name %x not part of file name %y!"); replace(msg, L"%x", fmtFileName(volumeNamePf), false); - replace(msg, L"%y", fmtFileName(inputFile), false); + replace(msg, L"%y", fmtFileName(filenameFinal), false); throw FileError(msg); } @@ -117,5 +130,5 @@ Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile, const std::function } //return filename alias on shadow copy volume - return it->second->geNamePf() + Zstring(inputFile.c_str() + pos + volumeNamePf.length()); + return it->second->geNamePf() + Zstring(filenameFinal.c_str() + pos + volumeNamePf.length()); } diff --git a/lib/shadow.h b/lib/shadow.h index 4a05c860..f59d7753 100644 --- a/lib/shadow.h +++ b/lib/shadow.h @@ -21,7 +21,8 @@ class ShadowCopy //take and buffer Windows Volume Shadow Copy snapshots as neede public: ShadowCopy() {} - Zstring makeShadowCopy(const Zstring& inputFile, const std::function<void(const Zstring&)>& onBeforeMakeVolumeCopy); //throw FileError; returns filename on shadow copy + //return filename on shadow copy volume - follows symlinks! + Zstring makeShadowCopy(const Zstring& inputFile, const std::function<void(const Zstring&)>& onBeforeMakeVolumeCopy); //throw FileError private: ShadowCopy(const ShadowCopy&); @@ -33,4 +34,4 @@ private: }; } -#endif // SHADOW_H_INCLUDED +#endif //SHADOW_H_INCLUDED diff --git a/lib/status_handler_impl.h b/lib/status_handler_impl.h index 615288d2..e1212b65 100644 --- a/lib/status_handler_impl.h +++ b/lib/status_handler_impl.h @@ -7,28 +7,52 @@ #ifndef STATUSHANDLER_IMPL_H_INCLUDED #define STATUSHANDLER_IMPL_H_INCLUDED +#include <zen/optional.h> #include <zen/file_error.h> #include "process_callback.h" +//template <typename Function> inline +//bool tryReportingError(Function cmd, ProcessCallback& handler) //return "true" on success, "false" if error was ignored +//{ +// for (;;) +// try +// { +// cmd(); //throw FileError +// return true; +// } +// catch (zen::FileError& error) +// { +// switch (handler.reportError(error.toString())) //may throw! +// { +// case ProcessCallback::IGNORE_ERROR: +// return false; +// case ProcessCallback::RETRY: +// break; //continue with loop +// } +// } +//} + + template <typename Function> inline -bool tryReportingError(Function cmd, ProcessCallback& handler) //return "true" on success, "false" if error was ignored +zen::Opt<std::wstring> tryReportingError2(Function cmd, ProcessCallback& handler) //return ignored error message if available { for (;;) try { cmd(); //throw FileError - return true; + return zen::NoValue(); } catch (zen::FileError& error) { switch (handler.reportError(error.toString())) //may throw! { case ProcessCallback::IGNORE_ERROR: - return false; + 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 index a72433cc..a5bd17be 100644 --- a/lib/versioning.cpp +++ b/lib/versioning.cpp @@ -205,10 +205,17 @@ private: virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) { - if (details.dirLink) - dirs_.push_back(shortName); - else - files_.push_back(shortName); + switch (getSymlinkType(fullName)) + { + case SYMLINK_TYPE_DIR: + dirs_.push_back(shortName); + break; + + case SYMLINK_TYPE_FILE: + case SYMLINK_TYPE_UNKNOWN: + files_.push_back(shortName); + break; + } return LINK_SKIP; } @@ -218,7 +225,8 @@ private: return nullptr; //DON'T traverse into subdirs; moveDirectory works recursively! } - virtual HandleError onError(const std::wstring& msg) { throw FileError(msg); } + 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<Zstring>& files_; std::vector<Zstring>& dirs_; @@ -286,7 +294,7 @@ void FileVersioner::revisionDirImpl(const Zstring& sourceDir, const Zstring& rel assert(somethingExists(sourceDir)); //[!] //create target - if (symlinkExists(sourceDir)) //on Linux there is just one type of symlinks, and since we do revision file symlinks, we should revision dir symlinks as well! + if (symlinkExists(sourceDir)) //on Linux there is just one type of symlink, and since we do revision file symlinks, we should revision dir symlinks as well! { moveItemToVersioning(sourceDir, //throw FileError relativeName, @@ -355,7 +363,8 @@ 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<TraverseCallback> onDir(const Zchar* shortName, const Zstring& fullName) { updateUI_(); return nullptr; } //DON'T traverse into subdirs - virtual HandleError onError(const std::wstring& msg) { throw FileError(msg); } + 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<Zstring>& files_; std::function<void()> updateUI_; @@ -428,4 +437,4 @@ void FileVersioner::limitVersions(std::function<void()> updateUI) //throw FileEr }); }); } -*/
\ No newline at end of file +*/ diff --git a/structures.cpp b/structures.cpp index 76dec74e..e94793d6 100644 --- a/structures.cpp +++ b/structures.cpp @@ -173,11 +173,13 @@ std::wstring zen::getSymbol(SyncOperation op) case SO_DELETE_RIGHT: return L"delete ->"; case SO_MOVE_LEFT_SOURCE: + return L"move from <-"; case SO_MOVE_LEFT_TARGET: - return L"move <-"; + return L"move to <-"; case SO_MOVE_RIGHT_SOURCE: + return L"move from ->"; case SO_MOVE_RIGHT_TARGET: - return L"move ->"; + return L"move to ->"; case SO_OVERWRITE_LEFT: case SO_COPY_METADATA_TO_LEFT: return L"update <-"; diff --git a/synchronization.cpp b/synchronization.cpp index 91d11c04..f03da9f0 100644 --- a/synchronization.cpp +++ b/synchronization.cpp @@ -326,7 +326,7 @@ public: template <class Function> void removeFileUpdating(const Zstring& relativeName, Int64 bytesExpected, Function notifyItemDeletion); //throw FileError template <class Function> void removeDirUpdating (const Zstring& relativeName, Int64 bytesExpected, Function notifyItemDeletion); //reports ONLY data delta via updateProcessedData()! - template <class Function> void removeLinkUpdating(const Zstring& relativeName, Int64 bytesExpected, Function notifyItemDeletion, LinkDescriptor::LinkType lt); // + template <class Function> void removeLinkUpdating(const Zstring& relativeName, Int64 bytesExpected, Function notifyItemDeletion); // const std::wstring& getTxtRemovingFile () const { return txtRemovingFile; } // const std::wstring& getTxtRemovingSymLink() const { return txtRemovingSymlink; } //buffered status texts @@ -795,14 +795,16 @@ void DeletionHandling::removeFileUpdating(const Zstring& relativeName, Int64 byt } template <class Function> inline -void DeletionHandling::removeLinkUpdating(const Zstring& relativeName, Int64 bytesExpected, Function notifyItemDeletion, LinkDescriptor::LinkType lt) //throw FileError +void DeletionHandling::removeLinkUpdating(const Zstring& relativeName, Int64 bytesExpected, Function notifyItemDeletion) //throw FileError { - switch (lt) + const Zstring fullName = baseDirPf_ + relativeName; + switch (getSymlinkType(fullName)) { - case LinkDescriptor::TYPE_DIR: + case SYMLINK_TYPE_DIR: return removeDirUpdating(relativeName, bytesExpected, notifyItemDeletion); //throw FileError - case LinkDescriptor::TYPE_FILE: //Windows: true file symlink; Linux: file-link or broken link + case SYMLINK_TYPE_FILE: + case SYMLINK_TYPE_UNKNOWN: return removeFileUpdating(relativeName, bytesExpected, notifyItemDeletion); //throw FileError } } @@ -1101,6 +1103,8 @@ void SynchronizeFolderPair::prepare2StepMove(FileMapping& sourceObj, reportInfo(txtMovingFile, source, tmpTarget); + warn_static("was wenn diff volume: symlink aliasing!") //throw FileError, ErrorDifferentVolume, ErrorTargetExisting + renameFile(source, tmpTarget); //throw FileError //update file hierarchy @@ -1143,7 +1147,7 @@ bool SynchronizeFolderPair::createParentDir(FileSystemObject& fsObj) //throw Fil assert(parentDir->getSyncOperation() != SO_DELETE_LEFT && parentDir->getSyncOperation() != SO_DELETE_RIGHT); - synchronizeFolder(*parentDir); //throw FileError + synchronizeFolder(*parentDir); //throw FileError } return true; } @@ -1205,10 +1209,9 @@ void SynchronizeFolderPair::manageFileMove(FileMapping& sourceObj, } + //search for file move-operations void SynchronizeFolderPair::runZeroPass(HierarchyObject& hierObj) { - //search for file move-operations - for (auto it = hierObj.refSubFiles().begin(); it != hierObj.refSubFiles().end(); ++it) //VS 2010 crashes if we use for_each + lambda here... { FileMapping& fileObj = *it; @@ -1222,13 +1225,15 @@ void SynchronizeFolderPair::runZeroPass(HierarchyObject& hierObj) FileMapping* sourceObj = &fileObj; if (FileMapping* targetObj = dynamic_cast<FileMapping*>(FileSystemObject::retrieve(fileObj.getMoveRef()))) { - if (!tryReportingError([&] - { - if (syncOp == SO_MOVE_LEFT_SOURCE) - this->manageFileMove<LEFT_SIDE>(*sourceObj, *targetObj); + zen::Opt<std::wstring> errMsg = tryReportingError2([&] + { + if (syncOp == SO_MOVE_LEFT_SOURCE) + this->manageFileMove<LEFT_SIDE>(*sourceObj, *targetObj); //throw FileError else - this->manageFileMove<RIGHT_SIDE>(*sourceObj, *targetObj); - }, procCallback_)) + this->manageFileMove<RIGHT_SIDE>(*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" @@ -1383,7 +1388,7 @@ void SynchronizeFolderPair::runPass(HierarchyObject& hierObj) [&](FileMapping& fileObj) { if (pass == this->getPass(fileObj)) //"this->" required by two-pass lookup as enforced by GCC 4.7 - tryReportingError([&] { synchronizeFile(fileObj); }, procCallback_); + tryReportingError2([&] { synchronizeFile(fileObj); }, procCallback_); }); //synchronize symbolic links: @@ -1391,7 +1396,7 @@ void SynchronizeFolderPair::runPass(HierarchyObject& hierObj) [&](SymLinkMapping& linkObj) { if (pass == this->getPass(linkObj)) - tryReportingError([&] { synchronizeLink(linkObj); }, procCallback_); + tryReportingError2([&] { synchronizeLink(linkObj); }, procCallback_); }); //synchronize folders: @@ -1399,7 +1404,7 @@ void SynchronizeFolderPair::runPass(HierarchyObject& hierObj) [&](DirMapping& dirObj) { if (pass == this->getPass(dirObj)) - tryReportingError([&] { synchronizeFolder(dirObj); }, procCallback_); + tryReportingError2([&] { synchronizeFolder(dirObj); }, procCallback_); this->runPass<pass>(dirObj); //recurse }); @@ -1534,7 +1539,7 @@ void SynchronizeFolderPair::synchronizeFileInt(FileMapping& fileObj, SyncOperati const Zstring& target = targetObj->getBaseDirPf<sideTrg>() + targetObj->getRelativeName<sideSrc>(); reportInfo(txtMovingFile, source, target); - + warn_static("was wenn diff volume: symlink aliasing!") //throw FileError, ErrorDifferentVolume, ErrorTargetExisting renameFile(source, target); //throw FileError const FileDescriptor descrTarget(sourceObj->getLastWriteTime<sideTrg>(), @@ -1669,7 +1674,7 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymLinkMapping& linkObj, SyncOper { procCallback_.updateProcessedData(1, 0); //noexcept ++objectsReported; - }, linkObj.getLinkType<sideTrg>()); + }); guardStatistics.dismiss(); //update statistics to consider the real amount of data if (objectsReported != objectsExpected) @@ -1686,7 +1691,7 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymLinkMapping& linkObj, SyncOper reportInfo(txtOverwritingLink, target); reportStatus(getDelHandling<sideTrg>().getTxtRemovingSymLink(), linkObj.getFullName<sideTrg>()); - getDelHandling<sideTrg>().removeLinkUpdating(linkObj.getObjRelativeName(), 0, [] {}, linkObj.getLinkType<sideTrg>()); //throw FileError + getDelHandling<sideTrg>().removeLinkUpdating(linkObj.getObjRelativeName(), 0, [] {}); //throw FileError linkObj.removeObject<sideTrg>(); //remove file from FileMapping, to keep in sync (if subsequent copying fails!!) reportStatus(txtOverwritingLink, target); //restore status text @@ -1840,14 +1845,6 @@ void SynchronizeFolderPair::synchronizeFolderInt(DirMapping& dirObj, SyncOperati namespace { -void makeSameLength(std::wstring& first, std::wstring& second) -{ - const size_t maxPref = std::max(first.length(), second.length()); - first .resize(maxPref, L' '); - second.resize(maxPref, L' '); -} - - /* struct LessDependentDirectory : public std::binary_function<Zstring, Zstring, bool> { @@ -1871,16 +1868,18 @@ bool createBaseDirectory(BaseDirMapping& baseMap, ProcessCallback& callback) //n if (baseMap.isExisting<side>()) //atomicity: do NOT check directory existence again! { //just convenience: exit sync right here instead of showing tons of error messages during file copy - return tryReportingError([&] + zen::Opt<std::wstring> errMsg = tryReportingError2([&] { if (!dirExistsUpdating(dirname, false, callback)) throw FileError(replaceCpy(_("Cannot find %x."), L"%x", fmtFileName(dirname))); //this should really be a "fatal error" if not recoverable }, 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; - bool rv = tryReportingError([&] + zen::Opt<std::wstring> errMsg = tryReportingError2([&] { try { @@ -1901,7 +1900,7 @@ bool createBaseDirectory(BaseDirMapping& baseMap, ProcessCallback& callback) //n // 3. log file creates containing folder -> no, log only created in batch mode, and only *before* comparison } }, callback); //may throw in error-callback! - return rv && !temporaryNetworkDrop; + return !errMsg && !temporaryNetworkDrop; } } } @@ -2105,7 +2104,7 @@ void zen::synchronize(const TimeComp& timeStamp, } } - //the following scenario is covered by base directory creation below in case source directory exists (accessible or not), but latter doesn't cover not-yet-created source!!! + //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()) @@ -2269,13 +2268,9 @@ void zen::synchronize(const TimeComp& timeStamp, //------------------------------------------------------------------------------------------ //always report folder pairs for log file, even if there is no work to do - std::wstring left = _("Left") + L": "; - std::wstring right = _("Right") + L": "; - makeSameLength(left, right); - callback.reportInfo(_("Synchronizing folder pair:") + L"\n" + - L" " + left + j->getBaseDirPf<LEFT_SIDE >() + L"\n" + - L" " + right + j->getBaseDirPf<RIGHT_SIDE>()); + L" " + j->getBaseDirPf<LEFT_SIDE >() + L"\n" + + L" " + j->getBaseDirPf<RIGHT_SIDE>()); //------------------------------------------------------------------------------------------ const size_t folderIndex = j - begin(folderCmp); @@ -2304,7 +2299,7 @@ void zen::synchronize(const TimeComp& timeStamp, ZEN_ON_SCOPE_EXIT(BaseDirMapping::removeEmpty(*j);); bool copyPermissionsFp = false; - tryReportingError([&] + tryReportingError2([&] { copyPermissionsFp = copyFilePermissions && //copy permissions only if asked for and supported by *both* sides! !j->getBaseDirPf<LEFT_SIDE >().empty() && //scenario: directory selected on one side only @@ -2321,8 +2316,8 @@ void zen::synchronize(const TimeComp& timeStamp, syncFP.startSync(*j); //(try to gracefully) cleanup temporary Recycle bin folders and versioning -> will be done in ~DeletionHandling anyway... - tryReportingError([&] { iterDelHandlerL->tryCleanup(); }, callback); //show error dialog if necessary - tryReportingError([&] { iterDelHandlerR->tryCleanup(); }, callback); // + tryReportingError2([&] { iterDelHandlerL->tryCleanup(); }, callback); //show error dialog if necessary + tryReportingError2([&] { iterDelHandlerR->tryCleanup(); }, callback); // //(try to gracefully) write database file (will be done in ~EnforceUpdateDatabase anyway...) if (folderPairCfg.inAutomaticMode) @@ -2330,7 +2325,7 @@ void zen::synchronize(const TimeComp& timeStamp, callback.reportStatus(_("Generating database...")); callback.forceUiRefresh(); - tryReportingError([&] { zen::saveLastSynchronousState(*j); }, callback); //throw FileError + tryReportingError2([&] { zen::saveLastSynchronousState(*j); }, callback); //throw FileError guardUpdateDb.dismiss(); } } @@ -2521,6 +2516,5 @@ void SynchronizeFolderPair::verifyFileCopy(const Zstring& source, const Zstring& procCallback_.reportInfo(replaceCpy(txtVerifying, L"%x", fmtFileName(target))); VerifyStatusUpdater callback(procCallback_); - - tryReportingError([&] { ::verifyFiles(source, target, callback); }, procCallback_); + tryReportingError2([&] { ::verifyFiles(source, target, callback); }, procCallback_); } diff --git a/ui/batch_status_handler.cpp b/ui/batch_status_handler.cpp index 0edd289f..e06a0000 100644 --- a/ui/batch_status_handler.cpp +++ b/ui/batch_status_handler.cpp @@ -38,7 +38,8 @@ private: virtual std::shared_ptr<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 onError (const std::wstring& msg) { assert(false); return ON_ERROR_IGNORE; } //errors are not really critical in this context + virtual HandleError reportDirError (const std::wstring& msg) { assert(false); return ON_ERROR_IGNORE; } //errors are not really critical in this context + virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) { assert(false); return ON_ERROR_IGNORE; } // const Zstring prefix_; std::vector<Zstring>& logfiles_; @@ -112,13 +113,14 @@ BatchStatusHandler::BatchStatusHandler(bool showProgress, lastSyncsLogFileSizeMax_(lastSyncsLogFileSizeMax), handleError_(handleError), returnCode_(returnCode), - syncStatusFrame(*this, *this, nullptr, showProgress, jobName, execWhenFinished, execFinishedHistory), - jobName_(jobName) + progressDlg(createProgressDialog(*this, [this] { this->onProgressDialogTerminate(); }, *this, nullptr, showProgress, jobName, execWhenFinished, execFinishedHistory)), + jobName_(jobName) { if (logfilesCountLimit != 0) //init log file: starts internal timer! { - if (!tryReportingError([&] { logFile = prepareNewLogfile(logfileDirectory, jobName, timeStamp); }, //throw FileError; return value always bound! - *this)) + zen::Opt<std::wstring> errMsg = tryReportingError2([&] { logFile = prepareNewLogfile(logfileDirectory, jobName, timeStamp); }, //throw FileError; return value always bound! + *this); + if (errMsg) { raiseReturnCode(returnCode_, FFS_RC_ABORTED); throw BatchAbortProcess(); @@ -202,59 +204,73 @@ BatchStatusHandler::~BatchStatusHandler() } catch (FileError&) {} - //decide whether to stay on status screen or exit immediately... - if (switchToGuiRequested) //-> avoid recursive yield() calls, thous switch not before ending batch mode + if (progressDlg) { - try + //decide whether to stay on status screen or exit immediately... + if (switchToGuiRequested) //-> avoid recursive yield() calls, thous switch not before ending batch mode { - switchBatchToGui_.execute(); //open FreeFileSync GUI + try + { + switchBatchToGui_.execute(); //open FreeFileSync GUI + } + catch (...) {} + progressDlg->closeWindowDirectly(); //progressDlg is not main window anymore } - catch (...) {} - syncStatusFrame.closeWindowDirectly(); //syncStatusFrame is not main window anymore - } - else - { - if (syncStatusFrame.getAsWindow()->IsShown()) - showFinalResults = true; - - //execute "on completion" command (even in case of ignored errors) - if (!abortIsRequested()) //if aborted (manually), we don't execute the command + else { - const std::wstring finalCommand = syncStatusFrame.getExecWhenFinishedCommand(); //final value (after possible user modification) - if (isCloseProgressDlgCommand(finalCommand)) - showFinalResults = false; //take precedence over current visibility status - else if (!finalCommand.empty()) + if (progressDlg->getAsWindow()->IsShown()) + showFinalResults = true; + + //execute "on completion" command (even in case of ignored errors) + if (!abortIsRequested()) //if aborted (manually), we don't execute the command { - auto cmdexp = expandMacros(utfCvrtTo<Zstring>(finalCommand)); - shellExecute(cmdexp); + const std::wstring finalCommand = progressDlg->getExecWhenFinishedCommand(); //final value (after possible user modification) + if (isCloseProgressDlgCommand(finalCommand)) + showFinalResults = false; //take precedence over current visibility status + else if (!finalCommand.empty()) + { + auto cmdexp = expandMacros(utfCvrtTo<Zstring>(finalCommand)); + shellExecute(cmdexp); + } } + + 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()); + + //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 } - if (showFinalResults) //warning: wxWindow::Show() is called within processHasFinished()! + //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) { - //notify about (logical) application main window => program won't quit, but stay on this dialog - setMainWindow(syncStatusFrame.getAsWindow()); - - //notify to syncStatusFrame that current process has ended - if (abortIsRequested()) - syncStatusFrame.processHasFinished(SyncProgressDialog::RESULT_ABORTED, errorLog); //enable okay and close events - else if (totalErrors > 0) - syncStatusFrame.processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_ERROR, errorLog); - else if (totalWarnings > 0) - syncStatusFrame.processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS, errorLog); - else - syncStatusFrame.processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS, errorLog); + boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL)); + updateUiNow(); } - else - syncStatusFrame.closeWindowDirectly(); //syncStatusFrame is main window => program will quit directly } } + void BatchStatusHandler::initNewPhase(int objectsTotal, Int64 dataTotal, ProcessCallback::Phase phaseID) { StatusHandler::initNewPhase(objectsTotal, dataTotal, phaseID); - syncStatusFrame.initNewPhase(); //call after "StatusHandler::initNewPhase" + if (progressDlg) + progressDlg->initNewPhase(); //call after "StatusHandler::initNewPhase" } @@ -262,7 +278,8 @@ void BatchStatusHandler::updateProcessedData(int objectsDelta, Int64 dataDelta) { StatusHandler::updateProcessedData(objectsDelta, dataDelta); - syncStatusFrame.notifyProgressChange(); //noexcept + if (progressDlg) + progressDlg->notifyProgressChange(); //noexcept //note: this method should NOT throw in order to properly allow undoing setting of statistics! } @@ -285,11 +302,12 @@ void BatchStatusHandler::reportWarning(const std::wstring& warningMessage, bool& { case xmlAccess::ON_ERROR_POPUP: { - PauseTimers dummy(syncStatusFrame); + if (!progressDlg) abortThisProcess(); + PauseTimers dummy(*progressDlg); forceUiRefresh(); bool dontWarnAgain = false; - switch (showWarningDlg(syncStatusFrame.getAsWindow(), + switch (showWarningDlg(progressDlg->getAsWindow(), ReturnWarningDlg::BUTTON_IGNORE | ReturnWarningDlg::BUTTON_SWITCH | ReturnWarningDlg::BUTTON_CANCEL, warningMessage + L"\n\n" + _("Press \"Switch\" to resolve issues in FreeFileSync main dialog."), dontWarnAgain)) @@ -329,11 +347,12 @@ ProcessCallback::Response BatchStatusHandler::reportError(const std::wstring& er { case xmlAccess::ON_ERROR_POPUP: { - PauseTimers dummy(syncStatusFrame); + if (!progressDlg) abortThisProcess(); + PauseTimers dummy(*progressDlg); forceUiRefresh(); bool ignoreNextErrors = false; - switch (showErrorDlg(syncStatusFrame.getAsWindow(), + switch (showErrorDlg(progressDlg->getAsWindow(), ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_RETRY | ReturnErrorDlg::BUTTON_CANCEL, errorMessage, &ignoreNextErrors)) { @@ -373,11 +392,12 @@ void BatchStatusHandler::reportFatalError(const std::wstring& errorMessage) { case xmlAccess::ON_ERROR_POPUP: { - PauseTimers dummy(syncStatusFrame); + if (!progressDlg) abortThisProcess(); + PauseTimers dummy(*progressDlg); forceUiRefresh(); bool ignoreNextErrors = false; - switch (showFatalErrorDlg(syncStatusFrame.getAsWindow(), + switch (showFatalErrorDlg(progressDlg->getAsWindow(), ReturnFatalErrorDlg::BUTTON_IGNORE | ReturnFatalErrorDlg::BUTTON_CANCEL, errorMessage, &ignoreNextErrors)) { @@ -405,12 +425,20 @@ void BatchStatusHandler::reportFatalError(const std::wstring& errorMessage) void BatchStatusHandler::forceUiRefresh() { - syncStatusFrame.updateGui(); + if (progressDlg) + progressDlg->updateGui(); } void BatchStatusHandler::abortThisProcess() { requestAbortion(); //just make sure... - throw BatchAbortProcess(); //abort can be triggered by syncStatusFrame + 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 index dff2f7b3..7a77785b 100644 --- a/ui/batch_status_handler.h +++ b/ui/batch_status_handler.h @@ -21,6 +21,7 @@ class BatchAbortProcess {}; +//BatchStatusHandler(SyncProgressDialog) will internally process Window messages! disable GUI controls to avoid unexpected callbacks! class BatchStatusHandler : public zen::StatusHandler //throw BatchAbortProcess { public: @@ -37,7 +38,7 @@ public: std::vector<std::wstring>& execFinishedHistory); ~BatchStatusHandler(); - virtual void initNewPhase (int objectsTotal, zen::Int64 dataTotal, Phase phaseID); + 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(); @@ -48,6 +49,7 @@ public: private: virtual void abortThisProcess(); //throw BatchAbortProcess + void onProgressDialogTerminate(); const zen::SwitchToGui& switchBatchToGui_; //functionality to change from batch mode to GUI mode bool showFinalResults; @@ -57,7 +59,7 @@ private: zen::ErrorLog errorLog; //list of non-resolved errors and warnings zen::FfsReturnCode& returnCode_; - SyncProgressDialog syncStatusFrame; //the window managed by SyncStatus has longer lifetime than this handler! + SyncProgressDialog* progressDlg; //managed to have shorter lifetime than this handler! std::unique_ptr<zen::FileOutput> logFile; //optional! const std::wstring jobName_; diff --git a/ui/column_attr.h b/ui/column_attr.h index 5f0d79aa..399f553b 100644 --- a/ui/column_attr.h +++ b/ui/column_attr.h @@ -67,8 +67,9 @@ std::vector<ColumnAttributeRim> getDefaultColumnAttributesRight() enum ColumnTypeMiddle { - COL_TYPE_MIDDLE_VALUE, - COL_TYPE_BORDER + COL_TYPE_CHECKBOX, + COL_TYPE_CMP_CATEGORY, + COL_TYPE_SYNC_ACTION, }; //------------------------------------------------------------------ diff --git a/ui/custom_grid.cpp b/ui/custom_grid.cpp index 35d3248d..c1a39a0c 100644 --- a/ui/custom_grid.cpp +++ b/ui/custom_grid.cpp @@ -39,11 +39,6 @@ const wxColour COLOR_NOT_ACTIVE (228, 228, 228); //light grey const Zstring ICON_FILE_FOLDER = Zstr("folder"); - -const int CHECK_BOX_IMAGE = 12; //width of checkbox image -const int CHECK_BOX_SPACE_LEFT = 2; -const int CHECK_BOX_WIDTH = CHECK_BOX_SPACE_LEFT + CHECK_BOX_IMAGE; //width of first block - const size_t ROW_COUNT_NO_DATA = 10; /* @@ -95,6 +90,51 @@ std::pair<ptrdiff_t, ptrdiff_t> getVisibleRows(const Grid& grid) //returns range } +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<unsigned char>(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<int>(lhs.Red ()) - static_cast<int>(rhs.Red ())) + + numeric::power<2>(static_cast<int>(lhs.Green()) - static_cast<int>(rhs.Green())) + + numeric::power<2>(static_cast<int>(lhs.Blue ()) - static_cast<int>(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(); @@ -235,43 +275,8 @@ protected: else { //alternate background color to improve readability (while lacking cell borders) - if (getRowDisplayType(row) == DISP_TYPE_NORMAL && row % 2 == 1) - { - //accessibility, support high-contrast schemes => work with user-defined background color! - const auto backCol = getBackGroundColor(row); - - auto incChannel = [](unsigned char c, int diff) { return static_cast<unsigned char>(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<int>(lhs.Red ()) - static_cast<int>(rhs.Red ())) + - numeric::power<2>(static_cast<int>(lhs.Green()) - static_cast<int>(rhs.Green())) + - numeric::power<2>(static_cast<int>(lhs.Blue ()) - static_cast<int>(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); - } + if (getRowDisplayType(row) == DISP_TYPE_NORMAL) + fillBackgroundDefaultColorAlternating(dc, rect, row % 2 == 0); else clearArea(dc, rect, getBackGroundColor(row)); } @@ -295,7 +300,6 @@ protected: return COLOR_ORANGE; case DISP_TYPE_INACTIVE: return COLOR_NOT_ACTIVE; - } return wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); } @@ -630,9 +634,7 @@ private: } virtual void visit(const SymLinkMapping& linkObj) { - iconName = linkObj.getLinkType<side>() == LinkDescriptor::TYPE_DIR ? - ICON_FILE_FOLDER : - linkObj.getFullName<side>(); + iconName = linkObj.getFullName<side>(); } virtual void visit(const DirMapping& dirObj) { @@ -770,16 +772,16 @@ class GridDataMiddle : public GridDataBase public: GridDataMiddle(const std::shared_ptr<const zen::GridView>& gridDataView, Grid& grid) : GridDataBase(grid, gridDataView), - showSyncAction_(true), - toolTip(grid) {} //tool tip must not live longer than grid! + 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 (static_cast<ColumnTypeMiddle>(colType) == COL_TYPE_MIDDLE_VALUE && - row < refGrid().getRowCount()) + if (row < refGrid().getRowCount()) { refGrid().clearSelection(false); //don't emit event, prevent recursion! - dragSelection = make_unique<std::pair<size_t, BlockPosition>>(row, mousePosToBlock(clientPos, row)); + dragSelection = make_unique<std::pair<size_t, BlockPosition>>(row, mousePosToBlock(clientPos, row, static_cast<ColumnTypeMiddle>(colType))); toolTip.hide(); //handle custom tooltip } } @@ -839,22 +841,27 @@ public: //manage block highlighting and custom tooltip if (!dragSelection) { + auto refreshHighlight = [&](size_t row) + { + refreshCell(refGrid(), row, static_cast<ColumnType>(COL_TYPE_CHECKBOX)); + refreshCell(refGrid(), row, static_cast<ColumnType>(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 && static_cast<ColumnTypeMiddle>(colInfo->first) == COL_TYPE_MIDDLE_VALUE) + if (row < refGrid().getRowCount() && colInfo) { - if (highlight) //refresh old highlight - refreshCell(refGrid(), highlight->row_, static_cast<ColumnType>(COL_TYPE_MIDDLE_VALUE)); + if (highlight) refreshHighlight(highlight->row_); //refresh old highlight + + highlight = make_unique<MouseHighlight>(row, mousePosToBlock(clientPos, row, static_cast<ColumnTypeMiddle>(colInfo->first))); - highlight = make_unique<MouseHighlight>(row, mousePosToBlock(clientPos, row)); - refreshCell(refGrid(), highlight->row_, static_cast<ColumnType>(COL_TYPE_MIDDLE_VALUE)); + refreshHighlight(highlight->row_); //show custom tooltip if (refGrid().getMainWin().GetClientRect().Contains(clientPos)) //cursor might have moved outside visible client area - showToolTip(row, refGrid().getMainWin().ClientToScreen(clientPos)); + showToolTip(row, static_cast<ColumnTypeMiddle>(colInfo->first), refGrid().getMainWin().ClientToScreen(clientPos)); } else onMouseLeave(); @@ -867,184 +874,231 @@ public: { if (highlight) { - refreshCell(refGrid(), highlight->row_, static_cast<ColumnType>(COL_TYPE_MIDDLE_VALUE)); + refreshCell(refGrid(), highlight->row_, static_cast<ColumnType>(COL_TYPE_CHECKBOX)); + refreshCell(refGrid(), highlight->row_, static_cast<ColumnType>(COL_TYPE_SYNC_ACTION)); highlight.reset(); } toolTip.hide(); //handle custom tooltip } } - void showSyncAction(bool value) { showSyncAction_ = value; } + void highlightSyncAction(bool value) { highlightSyncAction_ = value; } private: virtual wxString getValue(size_t row, ColumnType colType) const { - if (static_cast<ColumnTypeMiddle>(colType) == COL_TYPE_MIDDLE_VALUE) - { - if (const FileSystemObject* fsObj = getRawData(row)) - return showSyncAction_ ? getSymbol(fsObj->getSyncOperation()) : getSymbol(fsObj->getCategory()); - } - return wxEmptyString; + if (const FileSystemObject* fsObj = getRawData(row)) + switch (static_cast<ColumnTypeMiddle>(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, bool hasFocus) { - drawCellBackground(dc, rect, enabled, selected, hasFocus, getBackGroundColor(row)); + const FileSystemObject* fsObj = getRawData(row); + GridData::drawCellBackground(dc, rect, enabled, selected, hasFocus, highlightSyncAction_ ? + getBackGroundColorSyncAction(fsObj) : + getBackGroundColorCmpCategory(fsObj)); } virtual void renderCell(Grid& grid, wxDC& dc, const wxRect& rect, size_t row, ColumnType colType) { switch (static_cast<ColumnTypeMiddle>(colType)) { - case COL_TYPE_MIDDLE_VALUE: - { + case COL_TYPE_CHECKBOX: if (const FileSystemObject* fsObj = getRawData(row)) { - //wxRect rectInside = drawCellBorder(dc, rect); wxRect rectInside = rect; - rectInside.width -= CHECK_BOX_SPACE_LEFT; - rectInside.x += CHECK_BOX_SPACE_LEFT; - - //draw checkbox - wxRect checkBoxArea = rectInside; - checkBoxArea.SetWidth(CHECK_BOX_IMAGE); + //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"), checkBoxArea, wxALIGN_CENTER, buffer); + drawBitmapRtlMirror(dc, getResourceImage(fsObj->isActive() ? L"checkboxTrueFocus" : L"checkboxFalseFocus"), rectInside, wxALIGN_CENTER, buffer); else //default - drawBitmapRtlMirror(dc, getResourceImage(fsObj->isActive() ? L"checkboxTrue" : L"checkboxFalse" ), checkBoxArea, wxALIGN_CENTER, buffer); + drawBitmapRtlMirror(dc, getResourceImage(fsObj->isActive() ? L"checkboxTrue" : L"checkboxFalse" ), rectInside, wxALIGN_CENTER, buffer); + } + break; - rectInside.width -= CHECK_BOX_IMAGE; - rectInside.x += CHECK_BOX_IMAGE; + case COL_TYPE_CMP_CATEGORY: + if (const FileSystemObject* fsObj = getRawData(row)) + { + if (highlightSyncAction_) + fillBackgroundDefaultColorAlternating(dc, rect, row % 2 == 0); + + const wxBitmap& cmpImg = getCmpResultImage(fsObj->getCategory()); + drawBitmapRtlMirror(dc, highlightSyncAction_ ? greyScale(cmpImg) : cmpImg, rect, wxALIGN_CENTER, buffer); + } + break; + + case COL_TYPE_SYNC_ACTION: + if (const FileSystemObject* fsObj = getRawData(row)) + { + if (!highlightSyncAction_) + fillBackgroundDefaultColorAlternating(dc, rect, row % 2 == 0); + + 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 (showSyncAction_) + if (rowHighlighted && highlightBlock != BLOCKPOS_CHECK_BOX) + switch (highlightBlock) + { + case BLOCKPOS_CHECK_BOX: + break; + case BLOCKPOS_LEFT: + drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SYNC_DIR_LEFT)), rect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, buffer); + break; + case BLOCKPOS_MIDDLE: + drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SYNC_DIR_NONE)), rect, wxALIGN_CENTER, buffer); + break; + case BLOCKPOS_RIGHT: + drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SYNC_DIR_RIGHT)), rect, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, buffer); + break; + } + else //default { - if (rowHighlighted && highlightBlock != BLOCKPOS_CHECK_BOX) - switch (highlightBlock) - { - case BLOCKPOS_CHECK_BOX: - break; - case BLOCKPOS_LEFT: - drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SYNC_DIR_LEFT)), rectInside, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, buffer); - break; - case BLOCKPOS_MIDDLE: - drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SYNC_DIR_NONE)), rectInside, wxALIGN_CENTER, buffer); - break; - case BLOCKPOS_RIGHT: - drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SYNC_DIR_RIGHT)), rectInside, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, buffer); - break; - } - else //default - drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->getSyncOperation()), rectInside, wxALIGN_CENTER, buffer); + const wxBitmap& opImg = getSyncOpImage(fsObj->getSyncOperation()); + drawBitmapRtlMirror(dc, highlightSyncAction_ ? opImg : greyScale(opImg), rect, wxALIGN_CENTER, buffer); } - else //comparison results view - drawBitmapRtlMirror(dc, getCmpResultImage(fsObj->getCategory()), rectInside, wxALIGN_CENTER, buffer); } - } - break; - case COL_TYPE_BORDER: - clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW)); break; } } - virtual wxString getColumnLabel(ColumnType colType) const { return wxEmptyString; } + virtual wxString getColumnLabel(ColumnType colType) const + { + switch (static_cast<ColumnTypeMiddle>(colType)) + { + case COL_TYPE_CHECKBOX: + break; + case COL_TYPE_CMP_CATEGORY: + return _("Category"); + case COL_TYPE_SYNC_ACTION: + return _("Action"); + } + return wxEmptyString; + } + + virtual wxString getToolTip(ColumnType colType) const { return getColumnLabel(colType); } virtual void renderColumnLabel(Grid& tree, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted) { switch (static_cast<ColumnTypeMiddle>(colType)) { - case COL_TYPE_MIDDLE_VALUE: + 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"compareSmall"); - const wxBitmap& syncIcon = getResourceImage(L"syncSmall"); - - const int space = 8; - const int imageWidthTotal = cmpIcon.GetWidth() + space + syncIcon.GetWidth(); + const wxBitmap& cmpIcon = getResourceImage(L"compareSmall"); + drawBitmapRtlNoMirror(dc, highlightSyncAction_ ? greyScale(cmpIcon) : cmpIcon, rectInside, wxALIGN_CENTER, buffer); + } + break; - const int posX = (rectInside.GetWidth () - imageWidthTotal) / 2; - const int posY = (rectInside.GetHeight() - cmpIcon.GetHeight()) / 2; - assert(cmpIcon.GetHeight() == syncIcon.GetHeight()); + case COL_TYPE_SYNC_ACTION: + { + wxRect rectInside = drawColumnLabelBorder(dc, rect); + drawColumnLabelBackground(dc, rectInside, highlighted); - dc.DrawBitmap(showSyncAction_ ? greyScale(cmpIcon) : cmpIcon, posX, posY, true); - dc.DrawBitmap(showSyncAction_ ? syncIcon : greyScale(syncIcon), posX + cmpIcon.GetWidth() + space, posY, true); + const wxBitmap& syncIcon = getResourceImage(L"syncSmall"); + drawBitmapRtlNoMirror(dc, highlightSyncAction_ ? syncIcon : greyScale(syncIcon), rectInside, wxALIGN_CENTER, buffer); } break; - - case COL_TYPE_BORDER: - drawCellBackground(dc, rect, true, false, true, wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW)); - break; } } - wxColor getBackGroundColor(size_t row) const + static wxColor getBackGroundColorSyncAction(const FileSystemObject* fsObj) { - if (const FileSystemObject* fsObj = getRawData(row)) + if (fsObj) { if (!fsObj->isActive()) return COLOR_NOT_ACTIVE; - else - { - if (showSyncAction_) //synchronization preview - { - 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; - } - } - else //comparison results view - { - switch (fsObj->getCategory()) - { - case FILE_LEFT_SIDE_ONLY: - case FILE_LEFT_NEWER: - return COLOR_SYNC_BLUE; //COLOR_CMP_BLUE; + 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); + } - case FILE_RIGHT_SIDE_ONLY: - case FILE_RIGHT_NEWER: - return COLOR_SYNC_GREEN; //COLOR_CMP_GREEN; + static wxColor getBackGroundColorCmpCategory(const FileSystemObject* fsObj) + { + if (fsObj) + { + if (!fsObj->isActive()) + return COLOR_NOT_ACTIVE; - 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; - } - } + 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); @@ -1058,44 +1112,60 @@ private: BLOCKPOS_RIGHT }; - //determine blockposition within cell - BlockPosition mousePosToBlock(const wxPoint& clientPos, size_t row) const + //determine block position within cell + BlockPosition mousePosToBlock(const wxPoint& clientPos, size_t row, ColumnTypeMiddle colType) const { - const int absX = refGrid().CalcUnscrolledPosition(clientPos).x; - - const wxRect rect = refGrid().getCellArea(row, static_cast<ColumnType>(COL_TYPE_MIDDLE_VALUE)); //returns empty rect if column not found; absolute coordinates! - if (rect.width > CHECK_BOX_WIDTH && rect.height > 0) + switch (static_cast<ColumnTypeMiddle>(colType)) { - const FileSystemObject* const fsObj = getRawData(row); - if (fsObj && showSyncAction_ && - fsObj->getSyncOperation() != SO_EQUAL) //in sync-preview equal files shall be treated as in cmp result, i.e. as full checkbox - { - // cell: - // ---------------------------------- - // | checkbox | left | middle | right| - // ---------------------------------- + case COL_TYPE_CHECKBOX: + case COL_TYPE_CMP_CATEGORY: + break; - const double blockWidth = (rect.GetWidth() - CHECK_BOX_WIDTH) / 3.0; - if (rect.GetX() + CHECK_BOX_WIDTH <= absX && absX < rect.GetX() + rect.GetWidth()) - { - if (absX < rect.GetX() + CHECK_BOX_WIDTH + blockWidth) - return BLOCKPOS_LEFT; - else if (absX < rect.GetX() + CHECK_BOX_WIDTH + 2 * blockWidth) - return BLOCKPOS_MIDDLE; - else - return BLOCKPOS_RIGHT; - } + case COL_TYPE_SYNC_ACTION: + { + const int absX = refGrid().CalcUnscrolledPosition(clientPos).x; + + const wxRect rect = refGrid().getCellArea(row, static_cast<ColumnType>(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, wxPoint posScreen) + void showToolTip(size_t row, ColumnTypeMiddle colType, wxPoint posScreen) { if (const FileSystemObject* fsObj = getRawData(row)) { - if (showSyncAction_) //synchronization preview + 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* { @@ -1173,9 +1243,7 @@ private: toolTip.hide(); //if invalid row... } - virtual wxString getToolTip(ColumnType colType) const { return showSyncAction_ ? _("Action") + L" (F8)" : _("Category") + L" (F8)"; } - - bool showSyncAction_; + bool highlightSyncAction_; struct MouseHighlight { @@ -1184,9 +1252,10 @@ private: const BlockPosition blockPos_; }; std::unique_ptr<MouseHighlight> highlight; //current mouse highlight - std::unique_ptr<std::pair<size_t, BlockPosition>> dragSelection; //(row, block) + std::unique_ptr<std::pair<size_t, BlockPosition>> dragSelection; //(row, block); area clicked when beginning selection std::unique_ptr<wxBitmap> buffer; //avoid costs of recreating this temporal variable Tooltip toolTip; + wxImage notch; }; //######################################################################################################## @@ -1478,11 +1547,15 @@ void gridview::init(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, const std //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); - gridCenter.SetSize(60 /*+ 2 * 5*/, -1); + 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<Grid::ColumnAttribute> attribMiddle; - //attribMiddle.push_back(Grid::ColumnAttribute(static_cast<ColumnType>(COL_TYPE_BORDER), 5, 0, true)); - attribMiddle.push_back(Grid::ColumnAttribute(static_cast<ColumnType>(COL_TYPE_MIDDLE_VALUE), 60, 0, true)); - //attribMiddle.push_back(Grid::ColumnAttribute(static_cast<ColumnType>(COL_TYPE_BORDER), 5, 0, true)); + attribMiddle.push_back(Grid::ColumnAttribute(static_cast<ColumnType>(COL_TYPE_CMP_CATEGORY), widthCategory, 0, true)); + attribMiddle.push_back(Grid::ColumnAttribute(static_cast<ColumnType>(COL_TYPE_CHECKBOX ), widthCheckbox, 0, true)); + attribMiddle.push_back(Grid::ColumnAttribute(static_cast<ColumnType>(COL_TYPE_SYNC_ACTION ), widthAction, 0, true)); gridCenter.setColumnConfig(attribMiddle); } @@ -1629,10 +1702,10 @@ void gridview::setNavigationMarker(Grid& gridLeft, } -void gridview::showSyncAction(Grid& gridCenter, bool value) +void gridview::highlightSyncAction(Grid& gridCenter, bool value) { if (auto* provMiddle = dynamic_cast<GridDataMiddle*>(gridCenter.getDataProvider())) - provMiddle->showSyncAction(value); + provMiddle->highlightSyncAction(value); else assert(false); } diff --git a/ui/custom_grid.h b/ui/custom_grid.h index c2e653ef..da6387f3 100644 --- a/ui/custom_grid.h +++ b/ui/custom_grid.h @@ -22,7 +22,7 @@ void init(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, const std::shared_p std::vector<Grid::ColumnAttribute> convertConfig(const std::vector<ColumnAttributeRim>& attribs); //+ make consistent std::vector<ColumnAttributeRim> convertConfig(const std::vector<Grid::ColumnAttribute>& attribs); // -void showSyncAction(Grid& gridCenter, bool value); +void highlightSyncAction(Grid& gridCenter, bool value); void setupIcons(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, bool show, IconBuffer::IconSize sz); diff --git a/ui/dir_name.cpp b/ui/dir_name.cpp index a35de5fc..2cfc8a3d 100644 --- a/ui/dir_name.cpp +++ b/ui/dir_name.cpp @@ -44,8 +44,9 @@ void setDirectoryNameImpl(const wxString& dirname, wxWindow& tooltipWnd, wxStati //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() && !endsWith(dirNormalized, FILE_NAME_SEPARATOR)) - dirNormalized += FILE_NAME_SEPARATOR; + if (!dirNormalized.empty()) + if (!endsWith(dirNormalized, FILE_NAME_SEPARATOR)) + dirNormalized += FILE_NAME_SEPARATOR; staticText->SetLabel(dirNormalized == dirFormatted ? wxString(_("Drag && drop")) : dirFormatted); } diff --git a/ui/grid_view.cpp b/ui/grid_view.cpp index 9a5143c1..a28a7ea4 100644 --- a/ui/grid_view.cpp +++ b/ui/grid_view.cpp @@ -554,4 +554,3 @@ void GridView::sortView(ColumnTypeRim type, bool onLeft, bool ascending) break; } } - diff --git a/ui/gui_generated.cpp b/ui/gui_generated.cpp index db1f03da..d2c886d8 100644 --- a/ui/gui_generated.cpp +++ b/ui/gui_generated.cpp @@ -790,6 +790,9 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const 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, 5 ); + bSizerViewFilter->Add( 0, 0, 1, wxEXPAND, 5 ); @@ -883,6 +886,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const 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 ); @@ -950,6 +954,7 @@ MainDialogGenerated::~MainDialogGenerated() m_bpButtonFilter->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigureFilter ), NULL, this ); m_bpButtonFilter->Disconnect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnGlobalFilterContext ), NULL, this ); m_checkBoxHideExcluded->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnShowExcluded ), NULL, this ); + m_bpButtonViewTypeSyncAction->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewType ), NULL, this ); m_bpButtonShowCreateLeft->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); m_bpButtonShowCreateLeft->Disconnect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); m_bpButtonShowUpdateLeft->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); @@ -1355,7 +1360,7 @@ SyncProgressDlgGenerated::SyncProgressDlgGenerated( wxWindow* parent, wxWindowID bSizerRoot->Add( m_panelProgress, 1, wxEXPAND, 5 ); m_listbookResult = new wxListbook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLB_TOP ); - wxSize m_listbookResultImageSize = wxSize( 180,1 ); + wxSize m_listbookResultImageSize = wxSize( 170,1 ); int m_listbookResultIndex = 0; wxImageList* m_listbookResultImages = new wxImageList( m_listbookResultImageSize.GetWidth(), m_listbookResultImageSize.GetHeight() ); m_listbookResult->AssignImageList( m_listbookResultImages ); @@ -1406,7 +1411,7 @@ SyncProgressDlgGenerated::SyncProgressDlgGenerated( wxWindow* parent, wxWindowID this->Centre( wxBOTH ); // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SyncProgressDlgGenerated::OnCloseBtn ) ); + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SyncProgressDlgGenerated::OnClose ) ); this->Connect( wxEVT_ICONIZE, wxIconizeEventHandler( SyncProgressDlgGenerated::OnIconize ) ); m_buttonClose->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncProgressDlgGenerated::OnOkay ), NULL, this ); m_buttonPause->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncProgressDlgGenerated::OnPause ), NULL, this ); @@ -1416,7 +1421,7 @@ SyncProgressDlgGenerated::SyncProgressDlgGenerated( wxWindow* parent, wxWindowID SyncProgressDlgGenerated::~SyncProgressDlgGenerated() { // Disconnect Events - this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SyncProgressDlgGenerated::OnCloseBtn ) ); + this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SyncProgressDlgGenerated::OnClose ) ); this->Disconnect( wxEVT_ICONIZE, wxIconizeEventHandler( SyncProgressDlgGenerated::OnIconize ) ); m_buttonClose->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncProgressDlgGenerated::OnOkay ), NULL, this ); m_buttonPause->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncProgressDlgGenerated::OnPause ), NULL, this ); @@ -1998,20 +2003,20 @@ SyncCfgDlgGenerated::SyncCfgDlgGenerated( wxWindow* parent, wxWindowID id, const 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* sbSizerKeepWidthStable; - sbSizerKeepWidthStable = new wxBoxSizer( wxHORIZONTAL ); + wxBoxSizer* sbSizerKeepWidthStableIfSyncDirsNotShown; + sbSizerKeepWidthStableIfSyncDirsNotShown = new wxBoxSizer( wxHORIZONTAL ); - sbSizerKeepWidthStable->Add( 45, 0, 0, 0, 5 ); + sbSizerKeepWidthStableIfSyncDirsNotShown->Add( 45, 0, 0, 0, 5 ); - sbSizerKeepWidthStable->Add( 5, 0, 0, 0, 5 ); + sbSizerKeepWidthStableIfSyncDirsNotShown->Add( 5, 0, 0, 0, 5 ); - sbSizerKeepWidthStable->Add( 46, 0, 0, 0, 5 ); + sbSizerKeepWidthStableIfSyncDirsNotShown->Add( 46, 0, 0, 0, 5 ); - bSizerConfig->Add( sbSizerKeepWidthStable, 0, 0, 5 ); + bSizerConfig->Add( sbSizerKeepWidthStableIfSyncDirsNotShown, 0, 0, 5 ); sbSizerSyncDirections = new wxBoxSizer( wxVERTICAL ); @@ -2626,6 +2631,7 @@ MessageDlgGenerated::MessageDlgGenerated( wxWindow* parent, wxWindowID id, const // Connect Events this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( MessageDlgGenerated::OnClose ) ); + m_checkBoxCustom->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( MessageDlgGenerated::OnCheckBoxClick ), NULL, this ); m_buttonCustom1->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MessageDlgGenerated::OnButton1 ), NULL, this ); m_buttonCustom2->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MessageDlgGenerated::OnButton2 ), NULL, this ); m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MessageDlgGenerated::OnCancel ), NULL, this ); @@ -2635,6 +2641,7 @@ MessageDlgGenerated::~MessageDlgGenerated() { // Disconnect Events this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( MessageDlgGenerated::OnClose ) ); + m_checkBoxCustom->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( MessageDlgGenerated::OnCheckBoxClick ), NULL, this ); m_buttonCustom1->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MessageDlgGenerated::OnButton1 ), NULL, this ); m_buttonCustom2->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MessageDlgGenerated::OnButton2 ), NULL, this ); m_buttonCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MessageDlgGenerated::OnCancel ), NULL, this ); @@ -2653,14 +2660,14 @@ DeleteDlgGenerated::DeleteDlgGenerated( wxWindow* parent, wxWindowID id, const w bSizer41 = new wxBoxSizer( wxHORIZONTAL ); m_bitmapDeleteType = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizer41->Add( m_bitmapDeleteType, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + bSizer41->Add( m_bitmapDeleteType, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); m_staticTextHeader = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextHeader->Wrap( -1 ); bSizer41->Add( m_staticTextHeader, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - bSizer24->Add( bSizer41, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); + bSizer24->Add( bSizer41, 0, 0, 5 ); m_staticline91 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); bSizer24->Add( m_staticline91, 0, wxEXPAND, 5 ); @@ -2682,6 +2689,7 @@ DeleteDlgGenerated::DeleteDlgGenerated( wxWindow* parent, wxWindowID id, const w bSizer99->Add( m_checkBoxUseRecycler, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxALL, 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|wxRIGHT|wxLEFT, 5 ); diff --git a/ui/gui_generated.h b/ui/gui_generated.h index 1f034dc2..4a7857fa 100644 --- a/ui/gui_generated.h +++ b/ui/gui_generated.h @@ -161,6 +161,7 @@ protected: wxStaticText* m_staticTextCreateRight; wxPanel* m_panelViewFilter; wxBoxSizer* bSizerViewFilter; + ToggleButton* m_bpButtonViewTypeSyncAction; ToggleButton* m_bpButtonShowCreateLeft; ToggleButton* m_bpButtonShowUpdateLeft; ToggleButton* m_bpButtonShowDeleteLeft; @@ -206,6 +207,7 @@ protected: 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(); } @@ -331,7 +333,7 @@ protected: wxButton* m_buttonAbort; // Virtual event handlers, overide them in your derived class - virtual void OnCloseBtn( wxCloseEvent& event ) { event.Skip(); } + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } virtual void OnIconize( wxIconizeEvent& event ) { event.Skip(); } virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); } virtual void OnPause( wxCommandEvent& event ) { event.Skip(); } @@ -651,6 +653,7 @@ protected: // 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 OnButton1( wxCommandEvent& event ) { event.Skip(); } virtual void OnButton2( wxCommandEvent& event ) { event.Skip(); } virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } diff --git a/ui/gui_status_handler.cpp b/ui/gui_status_handler.cpp index fa327136..c740bd09 100644 --- a/ui/gui_status_handler.cpp +++ b/ui/gui_status_handler.cpp @@ -25,39 +25,29 @@ CompareStatusHandler::CompareStatusHandler(MainDialog& dlg) : { wxWindowUpdateLocker dummy(&mainDlg); //avoid display distortion - //prevent user input during "compare", do not disable maindialog since abort-button would also be disabled - mainDlg.disableAllElements(true); - //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(); - - //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); } + 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() { - updateUiNow(); //ui update before enabling buttons again: prevent strange behaviour of delayed button clicks - - //reenable complete main dialog - mainDlg.enableAllElements(); - - mainDlg.compareStatus->finalize(); - mainDlg.auiMgr.GetPane(mainDlg.compareStatus->getAsWindow()).Hide(); - mainDlg.auiMgr.Update(); - //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); - if (abortIsRequested()) - mainDlg.flashStatusInformation(_("Operation aborted!")); + mainDlg.compareStatus->finalize(); + mainDlg.auiMgr.GetPane(mainDlg.compareStatus->getAsWindow()).Hide(); + mainDlg.auiMgr.Update(); } @@ -179,17 +169,17 @@ void CompareStatusHandler::abortThisProcess() //######################################################################################################## -SyncStatusHandler::SyncStatusHandler(MainDialog* parentDlg, +SyncStatusHandler::SyncStatusHandler(wxTopLevelWindow* parentDlg, size_t lastSyncsLogFileSizeMax, OnGuiError handleError, const std::wstring& jobName, const std::wstring& execWhenFinished, std::vector<std::wstring>& execFinishedHistory) : parentDlg_(parentDlg), - syncStatusFrame(*this, *this, parentDlg, true, jobName, execWhenFinished, execFinishedHistory), - lastSyncsLogFileSizeMax_(lastSyncsLogFileSizeMax), - handleError_(handleError), - jobName_(jobName) + progressDlg(createProgressDialog(*this, [this] { this->onProgressDialogTerminate(); }, *this, parentDlg, true, jobName, execWhenFinished, execFinishedHistory)), + lastSyncsLogFileSizeMax_(lastSyncsLogFileSizeMax), + handleError_(handleError), + jobName_(jobName) { totalTime.Start(); //measure total time } @@ -241,35 +231,46 @@ SyncStatusHandler::~SyncStatusHandler() } catch (FileError&) {} - bool showFinalResults = true; - - //execute "on completion" command (even in case of ignored errors) - if (!abortIsRequested()) //if aborted (manually), we don't execute the command + if (progressDlg) { - const std::wstring finalCommand = syncStatusFrame.getExecWhenFinishedCommand(); //final value (after possible user modification) - if (isCloseProgressDlgCommand(finalCommand)) - showFinalResults = false; //take precedence over current visibility status - else if (!finalCommand.empty()) + bool showFinalResults = true; + //execute "on completion" command (even in case of ignored errors) + if (!abortIsRequested()) //if aborted (manually), we don't execute the command { - auto cmdexp = expandMacros(utfCvrtTo<Zstring>(finalCommand)); - shellExecute(cmdexp); + const std::wstring finalCommand = progressDlg->getExecWhenFinishedCommand(); //final value (after possible user modification) + if (isCloseProgressDlgCommand(finalCommand)) + showFinalResults = false; //take precedence over current visibility status + else if (!finalCommand.empty()) + { + auto cmdexp = expandMacros(utfCvrtTo<Zstring>(finalCommand)); + shellExecute(cmdexp); + } } - } - //notify to syncStatusFrame that current process has ended - if (showFinalResults) - { - if (abortIsRequested()) - syncStatusFrame.processHasFinished(SyncProgressDialog::RESULT_ABORTED, errorLog); //enable okay and close events - else if (totalErrors > 0) - syncStatusFrame.processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_ERROR, errorLog); - else if (totalWarnings > 0) - syncStatusFrame.processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS, errorLog); + //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 - syncStatusFrame.processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS, errorLog); + 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) + { + boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL)); + updateUiNow(); + } } - else - syncStatusFrame.closeWindowDirectly(); //syncStatusFrame is main window => program will quit directly } @@ -277,14 +278,16 @@ void SyncStatusHandler::initNewPhase(int objectsTotal, Int64 dataTotal, Phase ph { assert(phaseID == PHASE_SYNCHRONIZING); StatusHandler::initNewPhase(objectsTotal, dataTotal, phaseID); - syncStatusFrame.initNewPhase(); //call after "StatusHandler::initNewPhase" + if (progressDlg) + progressDlg->initNewPhase(); //call after "StatusHandler::initNewPhase" } void SyncStatusHandler::updateProcessedData(int objectsDelta, Int64 dataDelta) { StatusHandler::updateProcessedData(objectsDelta, dataDelta); - syncStatusFrame.notifyProgressChange(); //noexcept + if (progressDlg) + progressDlg->notifyProgressChange(); //noexcept //note: this method should NOT throw in order to properly allow undoing setting of statistics! } @@ -304,7 +307,8 @@ ProcessCallback::Response SyncStatusHandler::reportError(const std::wstring& err { case ON_GUIERROR_POPUP: { - PauseTimers dummy(syncStatusFrame); + if (!progressDlg) abortThisProcess(); + PauseTimers dummy(*progressDlg); forceUiRefresh(); bool ignoreNextErrors = false; @@ -345,7 +349,8 @@ void SyncStatusHandler::reportFatalError(const std::wstring& errorMessage) { case ON_GUIERROR_POPUP: { - PauseTimers dummy(syncStatusFrame); + if (!progressDlg) abortThisProcess(); + PauseTimers dummy(*progressDlg); forceUiRefresh(); bool ignoreNextErrors = false; @@ -382,7 +387,8 @@ void SyncStatusHandler::reportWarning(const std::wstring& warningMessage, bool& { case ON_GUIERROR_POPUP: { - PauseTimers dummy(syncStatusFrame); + if (!progressDlg) abortThisProcess(); + PauseTimers dummy(*progressDlg); forceUiRefresh(); bool dontWarnAgain = false; @@ -411,12 +417,20 @@ void SyncStatusHandler::reportWarning(const std::wstring& warningMessage, bool& void SyncStatusHandler::forceUiRefresh() { - syncStatusFrame.updateGui(); + if (progressDlg) + progressDlg->updateGui(); } void SyncStatusHandler::abortThisProcess() { requestAbortion(); //just make sure... - throw GuiAbortProcess(); //abort can be triggered by syncStatusFrame + 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 index 312206b6..f7b58f05 100644 --- a/ui/gui_status_handler.h +++ b/ui/gui_status_handler.h @@ -20,13 +20,15 @@ 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 initNewPhase(int objectsTotal, zen::Int64 dataTotal, Phase phaseID); virtual void forceUiRefresh(); virtual Response reportError(const std::wstring& text); @@ -43,10 +45,11 @@ private: }; +//SyncStatusHandler(SyncProgressDialog) will internally process Window messages! disable GUI controls to avoid unexpected callbacks! class SyncStatusHandler : public zen::StatusHandler { public: - SyncStatusHandler(MainDialog* parentDlg, + SyncStatusHandler(wxTopLevelWindow* parentDlg, size_t lastSyncsLogFileSizeMax, xmlAccess::OnGuiError handleError, const std::wstring& jobName, @@ -54,7 +57,7 @@ public: std::vector<std::wstring>& execFinishedHistory); ~SyncStatusHandler(); - virtual void initNewPhase (int objectsTotal, zen::Int64 dataTotal, Phase phaseID); + 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(); @@ -65,9 +68,10 @@ public: private: virtual void abortThisProcess(); //throw GuiAbortProcess + void onProgressDialogTerminate(); - MainDialog* parentDlg_; - SyncProgressDialog syncStatusFrame; //the window managed by SyncStatus has longer lifetime than this handler! + wxTopLevelWindow* parentDlg_; + SyncProgressDialog* progressDlg; //managed to have shorter lifetime than this handler! const size_t lastSyncsLogFileSizeMax_; xmlAccess::OnGuiError handleError_; zen::ErrorLog errorLog; diff --git a/ui/main_dlg.cpp b/ui/main_dlg.cpp index cb05e086..bb1e0c86 100644 --- a/ui/main_dlg.cpp +++ b/ui/main_dlg.cpp @@ -5,16 +5,18 @@ // ************************************************************************** #include "main_dlg.h" -#include <wx/clipbrd.h> -#include <wx/wupdlock.h> -#include <wx/msgdlg.h> -#include <wx/sound.h> -#include <wx/filedlg.h> #include <zen/format_unit.h> #include <zen/file_handling.h> #include <zen/serialize.h> //#include <zen/file_id.h> +//#include <zen/perf.h> #include <zen/thread.h> +#include <wx/clipbrd.h> +#include <wx/wupdlock.h> +#include <wx/msgdlg.h> +#include <wx/sound.h> +#include <wx/filedlg.h> +#include <wx/display.h> #include <wx+/context_menu.h> #include <wx+/string_conv.h> #include <wx+/button.h> @@ -44,7 +46,6 @@ #include "../lib/help_provider.h" #include "../lib/lock_holder.h" #include "../lib/localization.h" -#include <zen/perf.h> using namespace zen; using namespace std::rel_ops; @@ -52,10 +53,15 @@ using namespace std::rel_ops; namespace { -struct wxClientHistoryData: public wxClientData //we need a wxClientData derived class to tell wxWidgets to take object ownership! +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) {} + //~wxClientHistoryData() + //{ + // std::cerr << cfgFile_.c_str() << "\n"; + //} + Zstring cfgFile_; int lastUseIndex_; //support sorting history by last usage, the higher the index the more recent the usage }; @@ -434,7 +440,6 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg, const xmlAccess::XmlGlobalSettings& globalSettings, bool startComparison) : MainDialogGenerated(nullptr), - showSyncAction_(false), folderHistoryLeft (std::make_shared<FolderHistory>()), //make sure it is always bound folderHistoryRight(std::make_shared<FolderHistory>()) // { @@ -465,36 +470,36 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg, auiMgr.SetManagedWindow(this); auiMgr.SetFlags(wxAUI_MGR_DEFAULT | wxAUI_MGR_LIVE_RESIZE); - //caption required for all panes that can be manipulated by the users => used by context menu - auiMgr.AddPane(m_panelTopButtons, - wxAuiPaneInfo().Name(L"Panel1").Layer(4).Top().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 - compareStatus = make_unique<CompareProgressDialog>(*this); //integrate the compare status panel (in hidden state) - auiMgr.AddPane(compareStatus->getAsWindow(), - wxAuiPaneInfo().Name(L"Panel9").Layer(4).Top().Row(1).CaptionVisible(false).PaneBorder(false).Hide()); //name "CmpStatus" used by context menu - - auiMgr.AddPane(m_panelDirectoryPairs, - wxAuiPaneInfo().Name(L"Panel2").Layer(2).Top().Row(2).Caption(_("Folder pairs")).CaptionVisible(false).PaneBorder(false).Gripper()); - + //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_gridNavi, - wxAuiPaneInfo().Name(L"Panel10").Left().Layer(3).Caption(_("Overview")).MinSize(300, m_gridNavi->GetSize().GetHeight())); //MinSize(): just default size, see comment below + wxAuiPaneInfo().Name(L"Panel10").Layer(3).Left().Caption(_("Overview")).MinSize(300, m_gridNavi->GetSize().GetHeight())); //MinSize(): just default size, see comment below auiMgr.AddPane(m_panelConfig, - wxAuiPaneInfo().Name(L"Panel4").Layer(4).Bottom().Row(1).Position(0).Caption(_("Configuration")).MinSize(m_listBoxHistory->GetSize().GetWidth(), m_panelConfig->GetSize().GetHeight())); + wxAuiPaneInfo().Name(L"Panel4").Layer(3).Left().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().Row(1).Position(1).Caption(_("Filter files")).MinSize(m_bpButtonFilter->GetSize().GetWidth(), m_panelFilter->GetSize().GetHeight())); + 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().Row(1).Position(2).Caption(_("Select view")).MinSize(m_bpButtonShowDoNothing->GetSize().GetWidth(), m_panelViewFilter->GetSize().GetHeight())); + 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().Row(1).Position(3).Caption(_("Statistics")).MinSize(m_bitmapData->GetSize().GetWidth() + m_staticTextData->GetSize().GetWidth(), m_panelStatistics->GetSize().GetHeight())); + 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(); @@ -560,7 +565,7 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg, new PanelMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere... //ownership passed to "this" #endif - SetIcon(GlobalResources::instance().programIcon); //set application icon + SetIcon(GlobalResources::instance().programIconFFS); //set application icon //notify about (logical) application main window => program won't quit, but stay on this dialog zen::setMainWindow(this); @@ -766,6 +771,14 @@ MainDialog::~MainDialog() wxTheApp->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::OnGlobalKeyEvent), nullptr, this); wxTheApp->Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::OnGlobalKeyEvent), nullptr, this); +#ifdef FFS_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 @@ -790,14 +803,31 @@ void MainDialog::setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSe //caveat set/get language asymmmetry! setLanguage(globalSettings.programLanguage); //throw FileError //we need to set langugabe before creating this class! - //set dialog size and position: test ALL parameters at once, since width/height are invalid if the window is minimized (eg x,y == -32000; height = 28, width = 160) - //note: negative values for x and y are possible when using multiple monitors! + //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 && - globalSettings.gui.dlgPos.x >= -3360 && - globalSettings.gui.dlgPos.y >= -200) - //wxDisplay::GetFromPoint(globalSettings.gui.dlgPos) != wxNOT_FOUND) //make sure upper left corner is in visible view -> not required - SetSize(wxRect(globalSettings.gui.dlgPos, globalSettings.gui.dlgSize)); + 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(); @@ -1333,9 +1363,9 @@ void MainDialog::setStatusBarFileStatistics(size_t filesOnLeftView, wxString statusMiddleNew; if (gridDataView->rowsTotal() > 0) { - statusMiddleNew = _P("%x of 1 row in view", "%x of %y rows in view", gridDataView->rowsTotal()); - replace(statusMiddleNew, L"%x", toGuiString(gridDataView->rowsOnView()), false); - replace(statusMiddleNew, L"%y", toGuiString(gridDataView->rowsTotal ()), false); + statusMiddleNew = _P("%y of 1 row in view", "%y of %x rows in view", gridDataView->rowsTotal()); + replace(statusMiddleNew, L"%x", toGuiString(gridDataView->rowsTotal ()), false); //%x is required to be the plural form placeholder! + replace(statusMiddleNew, L"%y", toGuiString(gridDataView->rowsOnView()), false); //%y is secondary placeholder } bSizerStatusRightDirectories->Show(foldersOnRightView > 0); @@ -1380,9 +1410,8 @@ void MainDialog::flashStatusInformation(const wxString& text) m_panelStatusBar->Layout(); //if (needLayoutUpdate) auiMgr.Update(); -> not needed here, this is called anyway in updateGui() - asyncTasks.add2([] { boost::this_thread::sleep(boost::posix_time::millisec(2500)); }, - [this] { this->restoreStatusInformation(); }); - startProcessingAsyncTasks(); + processAsync2([] { boost::this_thread::sleep(boost::posix_time::millisec(2500)); }, + [this] { this->restoreStatusInformation(); }); } @@ -1416,7 +1445,7 @@ void MainDialog::disableAllElements(bool enableAbort) { //when changing consider: comparison, synchronization, manual deletion - EnableCloseButton(false); //not allowed for synchronization! progress indicator is top window! + EnableCloseButton(false); //not allowed for synchronization! progress indicator is top window! -> not honored on OS X! //disables all elements (except abort button) that might receive user input during long-running processes: comparison, deletion m_panelViewFilter ->Disable(); @@ -1764,7 +1793,7 @@ void MainDialog::OnGlobalKeyEvent(wxKeyEvent& event) //process key events withou return; //-> swallow event! case WXK_F8: //F8 - showSyncAction(!showSyncAction_); + setViewTypeSyncAction(!m_bpButtonViewTypeSyncAction->isActive()); return; //-> swallow event! //redirect certain (unhandled) keys directly to grid! @@ -1878,7 +1907,7 @@ void MainDialog::onNaviGridContext(GridClickEvent& event) ContextMenu menu; //---------------------------------------------------------------------------------------------------- - if (showSyncAction_ && !selection.empty()) + 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) @@ -1971,7 +2000,7 @@ void MainDialog::onMainGridContextRim(bool leftSide) const auto& selection = getGridSelection(); //referenced by lambdas! ContextMenu menu; - if (showSyncAction_ && !selection.empty()) + if (!selection.empty()) { auto getImage = [&](SyncDirection dir, SyncOperation soDefault) { @@ -2158,8 +2187,13 @@ void MainDialog::excludeItems(const std::vector<FileSystemObject*>& selection) void MainDialog::onGridLabelContextC(GridClickEvent& event) { ContextMenu menu; - menu.addItem(_("Category") + L"\tF8", [&] { showSyncAction(false); }, showSyncAction_ ? nullptr : &getResourceImage(L"compareSmall")); - menu.addItem(_("Action"), [&] { showSyncAction(true ); }, showSyncAction_ ? &getResourceImage(L"syncSmall") : nullptr); + + const bool actionView = m_bpButtonViewTypeSyncAction->isActive(); + menu.addRadio(_("Category") + (actionView ? L"\tF8" : L""), [&] { setViewTypeSyncAction(false); }, !actionView); + menu.addRadio(_("Action") + (!actionView ? L"\tF8" : L""), [&] { setViewTypeSyncAction(true ); }, actionView); + + //menu.addItem(_("Category") + L"\tF8", [&] { setViewTypeSyncAction(false); }, m_bpButtonViewTypeSyncAction->isActive() ? nullptr : &getResourceImage(L"compareSmall")); + //menu.addItem(_("Action"), [&] { setViewTypeSyncAction(true ); }, m_bpButtonViewTypeSyncAction->isActive() ? &getResourceImage(L"syncSmall") : nullptr); menu.popup(*this); } @@ -2450,9 +2484,7 @@ void MainDialog::removeObsoleteCfgHistoryItems(const std::vector<Zstring>& filen return missingFiles; }; - asyncTasks.add(getMissingFilesAsync, - [this](const std::vector<Zstring>& files) { removeCfgHistoryItems(files); }); - startProcessingAsyncTasks(); + processAsync(getMissingFilesAsync, [this](const std::vector<Zstring>& files) { removeCfgHistoryItems(files); }); } @@ -2605,7 +2637,7 @@ bool MainDialog::trySaveConfig(const Zstring* fileNameGui) //return true if save bool MainDialog::trySaveBatchConfig(const Zstring* fileNameBatch) { - //essentially behave like trySaveConfig(): the collateral damage of not saving GUI-only settings "hideExcludedItems, showSyncAction" is negliable + //essentially behave like trySaveConfig(): the collateral damage of not saving GUI-only settings "hideExcludedItems, m_bpButtonViewTypeSyncAction" is negliable const xmlAccess::XmlGuiConfig guiCfg = getConfig(); @@ -2681,7 +2713,7 @@ bool MainDialog::saveOldConfig() //return false on user abort QuestConfig().setCaption(toWx(activeCfgFilename)). setLabelYes(_("&Save")). setLabelNo(_("Do&n't save")). - showCheckBox(neverSave, _("Never save changes")))) + showCheckBox(neverSave, _("Never save changes"), ReturnQuestionDlg::BUTTON_YES))) { case ReturnQuestionDlg::BUTTON_YES: @@ -2983,7 +3015,7 @@ void MainDialog::setConfig(const xmlAccess::XmlGuiConfig& newGuiCfg, const std:: //read GUI layout m_checkBoxHideExcluded->SetValue(currentCfg.hideExcludedItems); - showSyncAction(currentCfg.showSyncAction); + setViewTypeSyncAction(currentCfg.highlightSyncAction); //########################################################### //update compare variant name @@ -3029,7 +3061,7 @@ xmlAccess::XmlGuiConfig MainDialog::getConfig() const std::back_inserter(guiCfg.mainCfg.additionalPairs), getEnhancedPair); //sync preview - guiCfg.showSyncAction = showSyncAction_; + guiCfg.highlightSyncAction = m_bpButtonViewTypeSyncAction->isActive(); return guiCfg; } @@ -3113,6 +3145,12 @@ void MainDialog::OnGlobalFilterContext(wxMouseEvent& event) } +void MainDialog::OnToggleViewType(wxCommandEvent& event) +{ + setViewTypeSyncAction(!m_bpButtonViewTypeSyncAction->isActive()); //toggle view +} + + void MainDialog::OnToggleViewButton(wxCommandEvent& event) { if (auto button = dynamic_cast<ToggleButton*>(event.GetEventObject())) @@ -3147,63 +3185,28 @@ wxBitmap buttonReleased(const std::string& name) void MainDialog::initViewFilterButtons() { - //compare result buttons - m_bpButtonShowLeftOnly->init(buttonPressed("leftOnly"), - buttonReleased("leftOnly"), - _("Show files that exist on left side only")); - - m_bpButtonShowRightOnly->init(buttonPressed("rightOnly"), - buttonReleased("rightOnly"), - _("Show files that exist on right side only")); - - m_bpButtonShowLeftNewer->init(buttonPressed("leftNewer"), - buttonReleased("leftNewer"), - _("Show files that are newer on left")); - - m_bpButtonShowRightNewer->init(buttonPressed("rightNewer"), - buttonReleased("rightNewer"), - _("Show files that are newer on right")); + m_bpButtonViewTypeSyncAction->init(getResourceImage(L"viewTypeSyncAction"), getResourceImage(L"viewTypeCmpResult")); + //tooltip is updated dynamically in setViewTypeSyncAction() - m_bpButtonShowEqual->init(buttonPressed("equal"), - buttonReleased("equal"), - _("Show files that are equal")); + auto initButton = [](ToggleButton& btn, const char* imgName, const wxString& tooltip) { btn.init(buttonPressed(imgName), buttonReleased(imgName)); btn.SetToolTip(tooltip); }; - m_bpButtonShowDifferent->init(buttonPressed("different"), - buttonReleased("different"), - _("Show files that are different")); - - m_bpButtonShowConflict->init(buttonPressed("conflict"), - buttonReleased("conflict"), - _("Show conflicts")); + //compare result buttons + initButton(*m_bpButtonShowLeftOnly, "leftOnly", _("Show files that exist on left side only")); + initButton(*m_bpButtonShowRightOnly, "rightOnly", _("Show files that exist on right side only")); + initButton(*m_bpButtonShowLeftNewer, "leftNewer", _("Show files that are newer on left")); + initButton(*m_bpButtonShowRightNewer, "rightNewer", _("Show files that are newer on right")); + initButton(*m_bpButtonShowEqual, "equal", _("Show files that are equal")); + initButton(*m_bpButtonShowDifferent, "different", _("Show files that are different")); + initButton(*m_bpButtonShowConflict, "conflict", _("Show conflicts")); //sync preview buttons - m_bpButtonShowCreateLeft->init(buttonPressed("createLeft"), - buttonReleased("createLeft"), - _("Show files that will be created on the left side")); - - m_bpButtonShowCreateRight->init(buttonPressed("createRight"), - buttonReleased("createRight"), - _("Show files that will be created on the right side")); - - m_bpButtonShowDeleteLeft->init(buttonPressed("deleteLeft"), - buttonReleased("deleteLeft"), - _("Show files that will be deleted on the left side")); - - m_bpButtonShowDeleteRight->init(buttonPressed("deleteRight"), - buttonReleased("deleteRight"), - _("Show files that will be deleted on the right side")); - - m_bpButtonShowUpdateLeft->init(buttonPressed("updateLeft"), - buttonReleased("updateLeft"), - _("Show files that will be overwritten on left side")); - - m_bpButtonShowUpdateRight->init(buttonPressed("updateRight"), - buttonReleased("updateRight"), - _("Show files that will be overwritten on right side")); - - m_bpButtonShowDoNothing->init(buttonPressed("none"), - buttonReleased("none"), - _("Show files that won't be copied")); + initButton(*m_bpButtonShowCreateLeft, "createLeft", _("Show files that will be created on the left side")); + initButton(*m_bpButtonShowCreateRight, "createRight", _("Show files that will be created on the right side")); + initButton(*m_bpButtonShowDeleteLeft, "deleteLeft", _("Show files that will be deleted on the left side")); + initButton(*m_bpButtonShowDeleteRight, "deleteRight", _("Show files that will be deleted on the right side")); + initButton(*m_bpButtonShowUpdateLeft, "updateLeft", _("Show files that will be overwritten on left side")); + initButton(*m_bpButtonShowUpdateRight, "updateRight", _("Show files that will be overwritten on right side")); + initButton(*m_bpButtonShowDoNothing, "none", _("Show files that won't be copied")); } @@ -3299,6 +3302,9 @@ void MainDialog::OnCompare(wxCommandEvent& event) clearGrid(); //avoid memory peak by clearing old data first + disableAllElements(true); //CompareStatusHandler will internally process Window messages, so avoid unexpected callbacks! + ZEN_ON_SCOPE_EXIT(updateUiNow(); enableAllElements()); //ui update before enabling buttons again: prevent strange behaviour of delayed button clicks + try { //class handling status display and error messages @@ -3322,6 +3328,7 @@ void MainDialog::OnCompare(wxCommandEvent& event) } catch (GuiAbortProcess&) { + flashStatusInformation(_("Operation aborted!")); // if (m_buttonCompare->IsShownOnScreen()) m_buttonCompare->SetFocus(); updateGui(); //refresh grid in ANY case! (also on abort) return; @@ -3337,9 +3344,9 @@ void MainDialog::OnCompare(wxCommandEvent& event) 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); + //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())); @@ -3451,10 +3458,10 @@ void MainDialog::applyCompareConfig(bool switchMiddleGrid) switch (currentCfg.mainCfg.cmpConfig.compareVar) { case CMP_BY_TIME_SIZE: - showSyncAction(true); + setViewTypeSyncAction(true); break; case CMP_BY_CONTENT: - showSyncAction(false); + setViewTypeSyncAction(false); break; } } @@ -3515,6 +3522,9 @@ void MainDialog::OnStartSync(wxCommandEvent& event) 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, @@ -3560,7 +3570,7 @@ void MainDialog::OnStartSync(wxCommandEvent& event) //do NOT disable the sync button: user might want to try to sync the REMAINING rows } //enableSynchronization(false); - //remove rows that empty: just a beautification, invalid rows shouldn't cause issues + //remove empty rows: just a beautification, invalid rows shouldn't cause issues gridDataView->removeInvalidRows(); updateGui(); @@ -3616,7 +3626,7 @@ void MainDialog::onGridLabelLeftClickR(GridClickEvent& event) void MainDialog::onGridLabelLeftClickC(GridClickEvent& event) { //sorting middle grid is more or less useless: therefore let's toggle view instead! - showSyncAction(!showSyncAction_); //toggle view + setViewTypeSyncAction(!m_bpButtonViewTypeSyncAction->isActive()); //toggle view } @@ -3696,7 +3706,7 @@ void MainDialog::updateGridViewData() m_bpButtonShowUpdateRight->Show(false); m_bpButtonShowDoNothing ->Show(false); - if (showSyncAction_) + if (m_bpButtonViewTypeSyncAction->isActive()) { const GridView::StatusSyncPreview result = gridDataView->updateSyncPreview(currentCfg.hideExcludedItems, m_bpButtonShowCreateLeft ->isActive(), @@ -3727,22 +3737,24 @@ void MainDialog::updateGridViewData() m_bpButtonShowEqual ->Show(result.existsSyncEqual); m_bpButtonShowConflict ->Show(result.existsConflict); - if (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()) + 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 { @@ -3770,13 +3782,16 @@ void MainDialog::updateGridViewData() m_bpButtonShowEqual ->Show(result.existsEqual); m_bpButtonShowConflict ->Show(result.existsConflict); - if (m_bpButtonShowLeftOnly ->IsShown() || - m_bpButtonShowRightOnly ->IsShown() || - m_bpButtonShowLeftNewer ->IsShown() || - m_bpButtonShowRightNewer->IsShown() || - m_bpButtonShowDifferent ->IsShown() || - m_bpButtonShowEqual ->IsShown() || - m_bpButtonShowConflict ->IsShown()) + 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(); @@ -3788,7 +3803,7 @@ void MainDialog::updateGridViewData() gridview::refresh(*m_gridMainL, *m_gridMainC, *m_gridMainR); //navigation tree - if (showSyncAction_) + if (m_bpButtonViewTypeSyncAction->isActive()) treeDataView->updateSyncPreview(currentCfg.hideExcludedItems, m_bpButtonShowCreateLeft ->isActive(), m_bpButtonShowCreateRight->isActive(), @@ -3894,11 +3909,11 @@ void MainDialog::OnRemoveFolderPair(wxCommandEvent& event) wxWindowUpdateLocker dummy(this); //avoid display distortion const wxObject* const eventObj = event.GetEventObject(); //find folder pair originating the event - for (std::vector<DirectoryPair*>::const_iterator it = additionalFolderPairs.begin(); it != additionalFolderPairs.end(); ++it) + for (auto it = additionalFolderPairs.begin(); it != additionalFolderPairs.end(); ++it) if (eventObj == (*it)->m_bpButtonRemovePair) { removeAddFolderPair(it - additionalFolderPairs.begin()); - return; + break; } } @@ -3998,10 +4013,10 @@ void MainDialog::addFolderPair(const std::vector<FolderPairEnh>& newPairs, bool newPair->m_bpButtonRemovePair->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MainDialog::OnRemoveFolderPair), nullptr, this); }); - //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 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, @@ -4024,8 +4039,12 @@ void MainDialog::removeAddFolderPair(size_t pos) //const int pairHeight = pairToDelete->GetSize().GetHeight(); bSizerAddFolderPairs->Detach(pairToDelete); //Remove() does not work on Window*, so do it manually - pairToDelete->Destroy(); // additionalFolderPairs.erase(additionalFolderPairs.begin() + pos); //remove element from 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 = additionalFolderPairs.size(); @@ -4083,7 +4102,7 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event) //http://en.wikipedia.org/wiki/Comma-separated_values const lconv* localInfo = ::localeconv(); //always bound according to doc - const bool haveCommaAsDecimalSep = strcmp(localInfo->decimal_point, ",") == 0; + const bool haveCommaAsDecimalSep = std::string(localInfo->decimal_point) == ","; const char CSV_SEP = haveCommaAsDecimalSep ? ';' : ','; @@ -4100,32 +4119,6 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event) 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; - //write legend - header += fmtValue(_("Legend")) + '\n'; - if (showSyncAction_) - { - header += fmtValue(getSyncOpDescription(SO_EQUAL)) + CSV_SEP + fmtValue(getSymbol(SO_EQUAL)) + '\n'; - header += fmtValue(getSyncOpDescription(SO_CREATE_NEW_LEFT)) + CSV_SEP + fmtValue(getSymbol(SO_CREATE_NEW_LEFT)) + '\n'; - header += fmtValue(getSyncOpDescription(SO_CREATE_NEW_RIGHT)) + CSV_SEP + fmtValue(getSymbol(SO_CREATE_NEW_RIGHT)) + '\n'; - header += fmtValue(getSyncOpDescription(SO_OVERWRITE_LEFT)) + CSV_SEP + fmtValue(getSymbol(SO_OVERWRITE_LEFT)) + '\n'; - header += fmtValue(getSyncOpDescription(SO_OVERWRITE_RIGHT)) + CSV_SEP + fmtValue(getSymbol(SO_OVERWRITE_RIGHT)) + '\n'; - header += fmtValue(getSyncOpDescription(SO_DELETE_LEFT)) + CSV_SEP + fmtValue(getSymbol(SO_DELETE_LEFT)) + '\n'; - header += fmtValue(getSyncOpDescription(SO_DELETE_RIGHT)) + CSV_SEP + fmtValue(getSymbol(SO_DELETE_RIGHT)) + '\n'; - header += fmtValue(getSyncOpDescription(SO_DO_NOTHING)) + CSV_SEP + fmtValue(getSymbol(SO_DO_NOTHING)) + '\n'; - header += fmtValue(getSyncOpDescription(SO_UNRESOLVED_CONFLICT)) + CSV_SEP + fmtValue(getSymbol(SO_UNRESOLVED_CONFLICT)) + '\n'; - } - else - { - header += fmtValue(getCategoryDescription(FILE_EQUAL)) + CSV_SEP + fmtValue(getSymbol(FILE_EQUAL)) + '\n'; - header += fmtValue(getCategoryDescription(FILE_DIFFERENT)) + CSV_SEP + fmtValue(getSymbol(FILE_DIFFERENT)) + '\n'; - header += fmtValue(getCategoryDescription(FILE_LEFT_SIDE_ONLY)) + CSV_SEP + fmtValue(getSymbol(FILE_LEFT_SIDE_ONLY)) + '\n'; - header += fmtValue(getCategoryDescription(FILE_RIGHT_SIDE_ONLY)) + CSV_SEP + fmtValue(getSymbol(FILE_RIGHT_SIDE_ONLY)) + '\n'; - header += fmtValue(getCategoryDescription(FILE_LEFT_NEWER)) + CSV_SEP + fmtValue(getSymbol(FILE_LEFT_NEWER)) + '\n'; - header += fmtValue(getCategoryDescription(FILE_RIGHT_NEWER)) + CSV_SEP + fmtValue(getSymbol(FILE_RIGHT_NEWER)) + '\n'; - header += fmtValue(getCategoryDescription(FILE_CONFLICT)) + CSV_SEP + fmtValue(getSymbol(FILE_CONFLICT)) + '\n'; - } - header += '\n'; - //base folders header += fmtValue(_("Folder pairs")) + '\n' ; std::for_each(begin(folderCmp), end(folderCmp), @@ -4146,7 +4139,7 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event) 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_; }); + vector_remove_if(colAttrMiddle, [](const Grid::ColumnAttribute& ca) { return !ca.visible_ || static_cast<ColumnTypeMiddle>(ca.type_) == COL_TYPE_CHECKBOX; }); vector_remove_if(colAttrRight , [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); if (provLeft && provMiddle && provRight) @@ -4206,16 +4199,12 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event) tmp += fmtValue(provMiddle->getValue(row, ca.type_)); tmp += CSV_SEP; }); - if (!colAttrRight.empty()) + std::for_each(colAttrRight.begin(), colAttrRight.end(), + [&](const Grid::ColumnAttribute& ca) { - std::for_each(colAttrRight.begin(), colAttrRight.end() - 1, - [&](const Grid::ColumnAttribute& ca) - { - tmp += fmtValue(provRight->getValue(row, ca.type_)); - tmp += CSV_SEP; - }); - tmp += fmtValue(provRight->getValue(row, colAttrRight.back().type_)); - } + tmp += fmtValue(provRight->getValue(row, ca.type_)); + tmp += CSV_SEP; + }); tmp += '\n'; replace(tmp, '\n', LINE_BREAK); @@ -4295,6 +4284,10 @@ void MainDialog::switchProgramLanguage(int 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(); } @@ -4308,12 +4301,15 @@ void MainDialog::OnMenuLanguageSwitch(wxCommandEvent& event) //######################################################################################################### -void MainDialog::showSyncAction(bool value) +void MainDialog::setViewTypeSyncAction(bool value) { - showSyncAction_ = value; + //if (m_bpButtonViewTypeSyncAction->isActive() == value) return; support polling -> what about initialization? + + m_bpButtonViewTypeSyncAction->setActive(value); + m_bpButtonViewTypeSyncAction->SetToolTip((value ? _("Action") : _("Category")) + L" (F8)"); //toggle display of sync preview in middle grid - gridview::showSyncAction(*m_gridMainC, value); + gridview::highlightSyncAction(*m_gridMainC, value); updateGui(); } diff --git a/ui/main_dlg.h b/ui/main_dlg.h index 70f78e93..2d132814 100644 --- a/ui/main_dlg.h +++ b/ui/main_dlg.h @@ -127,9 +127,13 @@ private: void openExternalApplication(const wxString& commandline, const std::vector<zen::FileSystemObject*>& selection, bool leftSide); //selection may be empty //don't use wxWidgets idle handling => repeated idle requests/consumption hogs 100% cpu! - void startProcessingAsyncTasks() { timerForAsyncTasks.Start(50); } //timer interval in [ms] void onProcessAsyncTasks(wxEvent& event); + template <class Fun, class Fun2> + void processAsync(Fun doAsync, Fun2 evalOnGui) { asyncTasks.add(doAsync, evalOnGui); timerForAsyncTasks.Start(50); /*timer interval in [ms] */ } + template <class Fun, class Fun2> + 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); @@ -186,6 +190,7 @@ private: void onGridLabelContextR(zen::GridClickEvent& event); void onGridLabelContext(zen::Grid& grid, zen::ColumnTypeRim type, const std::vector<zen::ColumnAttributeRim>& defaultColumnAttributes); + void OnToggleViewType (wxCommandEvent& event); void OnToggleViewButton(wxCommandEvent& event); void OnConfigNew (wxCommandEvent& event); @@ -279,9 +284,10 @@ private: bool processingGlobalKeyEvent; //indicator to notify recursion in OnGlobalKeyEvent() - bool showSyncAction_; //toggle to display configuration preview instead of comparison result - //use this methods when changing values! - void showSyncAction(bool value); + //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 diff --git a/ui/msg_popup.cpp b/ui/msg_popup.cpp index 940163af..59908cf9 100644 --- a/ui/msg_popup.cpp +++ b/ui/msg_popup.cpp @@ -35,6 +35,8 @@ private: void OnCancel(wxCommandEvent& event) { EndModal(ReturnErrorDlg::BUTTON_CANCEL); } void OnButton1(wxCommandEvent& event); void OnButton2(wxCommandEvent& event); + void OnCheckBoxClick(wxCommandEvent& event) { updateGui(); event.Skip(); } + void updateGui(); bool* ignoreErrors; wxButton& buttonRetry; // @@ -87,9 +89,16 @@ ErrorDlg::ErrorDlg(wxWindow* parent, int activeButtons, const wxString& messageT else if (activeButtons & ReturnErrorDlg::BUTTON_CANCEL) setAsStandard(*m_buttonCancel); + updateGui(); Fit(); //child-element widths have changed: image was set } +void ErrorDlg::updateGui() +{ + //button doesn't make sense when checkbox is set! + buttonRetry.Enable(!checkBoxIgnoreErrors.GetValue()); +} + void ErrorDlg::OnButton1(wxCommandEvent& event) //retry { @@ -135,6 +144,8 @@ private: void OnCancel(wxCommandEvent& event) { EndModal(ReturnWarningDlg::BUTTON_CANCEL); } void OnButton1(wxCommandEvent& event); void OnButton2(wxCommandEvent& event); + void OnCheckBoxClick(wxCommandEvent& event) { updateGui(); event.Skip(); } + void updateGui(); bool& dontShowAgain; wxButton& buttonIgnore; // @@ -182,10 +193,18 @@ WarningDlg::WarningDlg(wxWindow* parent, int activeButtons, const wxString& mes else if (activeButtons & ReturnWarningDlg::BUTTON_CANCEL) setAsStandard(*m_buttonCancel); + updateGui(); Fit(); //child-element widths have changed: image was set } +void WarningDlg::updateGui() +{ + //button doesn't make sense when checkbox is set! + buttonSwitch.Enable(!checkBoxDontShowAgain.GetValue()); +} + + void WarningDlg::OnButton1(wxCommandEvent& event) //ignore { dontShowAgain = checkBoxDontShowAgain.GetValue(); @@ -212,49 +231,49 @@ ReturnWarningDlg::ButtonPressed zen::showWarningDlg(wxWindow* parent, int active class QuestionDlg : public MessageDlgGenerated { public: - QuestionDlg(wxWindow* parent, int activeButtons, const wxString& messageText, const wxString& caption, const wxString& labelYes, const wxString& labelNo, bool* checkBoxValue, const wxString& checkBoxLabel); + QuestionDlg(wxWindow* parent, int activeButtons, const wxString& messageText, const QuestConfig& cfg); private: void OnClose (wxCloseEvent& event) { EndModal(ReturnQuestionDlg::BUTTON_CANCEL); } void OnCancel(wxCommandEvent& event) { EndModal(ReturnQuestionDlg::BUTTON_CANCEL); } void OnButton1(wxCommandEvent& event); void OnButton2(wxCommandEvent& event); + void OnCheckBoxClick(wxCommandEvent& event) { updateGui(); event.Skip(); } + void updateGui(); - bool* checkBoxValue_; //optional wxButton& buttonYes; // map generic controls wxButton& buttonNo; // + + bool* const checkBoxValue_; //optional + const int disabledButtonsWhenChecked_; }; QuestionDlg::QuestionDlg(wxWindow* parent, int activeButtons, const wxString& messageText, - const wxString& caption, //optional - const wxString& labelYes, - const wxString& labelNo, - //optional checkbox: - bool* checkBoxValue, - const wxString& checkBoxLabel) : + const QuestConfig& cfg) : MessageDlgGenerated(parent), - checkBoxValue_(checkBoxValue), buttonYes(*m_buttonCustom1), - buttonNo (*m_buttonCustom2) + buttonNo (*m_buttonCustom2), + checkBoxValue_(cfg.checkBoxValue), + disabledButtonsWhenChecked_(cfg.disabledButtonsWhenChecked_) { #ifdef FFS_WIN new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" #endif - SetTitle(!caption.empty()? caption : _("Question")); + SetTitle(!cfg.caption.empty()? cfg.caption : _("Question")); m_bitmapMsgType->SetBitmap(getResourceImage(L"msg_question")); m_textCtrlMessage->SetValue(messageText); - buttonYes.SetLabel(!labelYes.empty() ? labelYes : _("&Yes")); - buttonNo .SetLabel(!labelNo .empty() ? labelNo : _("&No")); + buttonYes.SetLabel(!cfg.labelYes.empty() ? cfg.labelYes : _("&Yes")); + buttonNo .SetLabel(!cfg.labelNo .empty() ? cfg.labelNo : _("&No")); //buttonYes.SetId(wxID_YES); -> see comment in ErrorDlg //buttonNo .SetId(wxID_NO); - if (checkBoxValue) + if (cfg.checkBoxValue) { - m_checkBoxCustom->SetValue(*checkBoxValue); - m_checkBoxCustom->SetLabel(checkBoxLabel); + m_checkBoxCustom->SetValue(*cfg.checkBoxValue); + m_checkBoxCustom->SetLabel(cfg.checkBoxLabel); } else m_checkBoxCustom->Hide(); @@ -276,9 +295,24 @@ QuestionDlg::QuestionDlg(wxWindow* parent, else if (activeButtons & ReturnQuestionDlg::BUTTON_CANCEL) setAsStandard(*m_buttonCancel); + updateGui(); Fit(); //child-element widths have changed: image was set } + +void QuestionDlg::updateGui() +{ + auto updateEnabledStatus = [&](wxButton& btn, ReturnQuestionDlg::ButtonPressed btnId) + { + if (disabledButtonsWhenChecked_ & btnId) + btn.Enable(!m_checkBoxCustom->GetValue()); + }; + updateEnabledStatus(buttonYes, ReturnQuestionDlg::BUTTON_YES); + updateEnabledStatus(buttonNo, ReturnQuestionDlg::BUTTON_NO); + updateEnabledStatus(*m_buttonCancel, ReturnQuestionDlg::BUTTON_CANCEL); +} + + void QuestionDlg::OnButton1(wxCommandEvent& event) //yes { if (checkBoxValue_) @@ -299,7 +333,7 @@ ReturnQuestionDlg::ButtonPressed zen::showQuestionDlg(wxWindow* parent, const wxString& messageText, const QuestConfig& cfg) { - QuestionDlg qtnDlg(parent, activeButtons, messageText, cfg.caption, cfg.labelYes, cfg.labelNo, cfg.checkBoxValue, cfg.checkBoxLabel); + QuestionDlg qtnDlg(parent, activeButtons, messageText, cfg); qtnDlg.Raise(); return static_cast<ReturnQuestionDlg::ButtonPressed>(qtnDlg.ShowModal()); } diff --git a/ui/msg_popup.h b/ui/msg_popup.h index 5e59eb6b..5e7ea97f 100644 --- a/ui/msg_popup.h +++ b/ui/msg_popup.h @@ -4,12 +4,13 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef MESSAGEPOPUP_H_INCLUDED -#define MESSAGEPOPUP_H_INCLUDED +#ifndef MESSAGEPOPUP_H_820780154723456 +#define MESSAGEPOPUP_H_820780154723456 #include <wx/window.h> #include <wx/string.h> +class QuestionDlg; namespace zen { @@ -60,20 +61,17 @@ struct ReturnQuestionDlg }; }; -class QuestConfig; -ReturnQuestionDlg::ButtonPressed showQuestionDlg(wxWindow* parent, int activeButtons, const wxString& messageText, const QuestConfig& cfg); - class QuestConfig { public: - QuestConfig() : checkBoxValue() {} + QuestConfig() : checkBoxValue(), disabledButtonsWhenChecked_() {} QuestConfig& setCaption (const wxString& label) { caption = label; return *this; } QuestConfig& setLabelYes(const wxString& label) { labelYes = label; return *this; } QuestConfig& setLabelNo (const wxString& label) { labelNo = label; return *this; } - QuestConfig& showCheckBox(bool& value, const wxString& label) { checkBoxLabel = label; checkBoxValue = &value; return *this; } + QuestConfig& showCheckBox(bool& value, const wxString& label, int disabledButtonsWhenChecked) { checkBoxValue = &value; checkBoxLabel = label; disabledButtonsWhenChecked_ = disabledButtonsWhenChecked; return *this; } private: - friend ReturnQuestionDlg::ButtonPressed showQuestionDlg(wxWindow* parent, int activeButtons, const wxString& messageText, const QuestConfig& cfg); + friend class ::QuestionDlg; wxString caption; wxString labelYes; //overwrite default "Yes, No" labels @@ -81,9 +79,10 @@ private: //optional checkbox: bool* checkBoxValue; //in/out wxString checkBoxLabel; //in + int disabledButtonsWhenChecked_; }; ReturnQuestionDlg::ButtonPressed showQuestionDlg(wxWindow* parent, int activeButtons, const wxString& messageText, const QuestConfig& cfg = QuestConfig()); } -#endif // MESSAGEPOPUP_H_INCLUDED +#endif //MESSAGEPOPUP_H_820780154723456 diff --git a/ui/progress_indicator.cpp b/ui/progress_indicator.cpp index d23de61a..37237861 100644 --- a/ui/progress_indicator.cpp +++ b/ui/progress_indicator.cpp @@ -12,8 +12,11 @@ #include <wx/sound.h> #include <wx/clipbrd.h> #include <wx/msgdlg.h> +#include <wx/dataobj.h> //wxTextDataObject #include <zen/basic_math.h> #include <zen/format_unit.h> +#include <zen/scope_guard.h> +#include <wx+/grid.h> #include <wx+/mouse_move_dlg.h> #include <wx+/toggle_button.h> #include <wx+/image_tools.h> @@ -38,7 +41,7 @@ namespace const int GAUGE_FULL_RANGE = 50000; //window size used for statistics in milliseconds -const int WINDOW_REMAINING_TIME = 60000; //some use cases have dropouts of 40 seconds -> 60 sec. window size handles them well +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; // } @@ -59,6 +62,7 @@ private: wxString titleTextBackup; wxStopWatch timeElapsed; + long binCompStartMs; //begin of binary comparison phase in [ms] const Statistics* syncStat_; //only bound while sync is running @@ -72,8 +76,9 @@ private: CompareProgressDialog::Pimpl::Pimpl(wxTopLevelWindow& parentWindow) : CompareProgressDlgGenerated(&parentWindow), parentWindow_(parentWindow), + binCompStartMs(0), syncStat_(nullptr), - lastStatCallSpeed (-1000000) //some big number + lastStatCallSpeed(-1000000) //some big number { //init(); -> needed? } @@ -125,6 +130,8 @@ void CompareProgressDialog::Pimpl::switchToCompareBytewise() perf = make_unique<PerfCheck>(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); @@ -153,6 +160,7 @@ void CompareProgressDialog::Pimpl::updateStatusPanelNow() }; 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! @@ -194,22 +202,22 @@ void CompareProgressDialog::Pimpl::updateStatusPanelNow() setText(*m_staticTextDataRemaining, L"(" + filesizeToShortString(dataTotal - dataCurrent) + L")", &layoutChanged); //remaining time and speed: only visible during binary comparison + assert(perf); if (perf) - { - const long timeNow = timeElapsed.Time(); if (numeric::dist(lastStatCallSpeed, timeNow) >= 500) { lastStatCallSpeed = timeNow; - perf->addSample(objectsCurrent, to<double>(dataCurrent), timeNow); + if (numeric::dist(binCompStartMs, timeNow) >= 1000) //discard stats for first second: probably messy + perf->addSample(objectsCurrent, to<double>(dataCurrent), timeNow); - //current speed -> Win 7 copy uses 1 sec update interval + //current speed -> Win 7 copy uses 1 sec update interval instead setText(*m_staticTextSpeed, perf->getBytesPerSecond(), &layoutChanged); - } - //remaining time - setText(*m_staticTextRemTime, perf->getRemainingTime(to<double>(dataTotal - dataCurrent)), &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 + setText(*m_staticTextRemTime, perf->getRemainingTime(to<double>(dataTotal - dataCurrent)), &layoutChanged); + } } break; @@ -222,7 +230,7 @@ void CompareProgressDialog::Pimpl::updateStatusPanelNow() setText(*m_staticTextScanned, scannedObjects, &layoutChanged); //time elapsed - const long timeElapSec = timeElapsed.Time() / 1000; + const long timeElapSec = timeNow / 1000; setText(*m_staticTextTimeElapsed, timeElapSec < 3600 ? wxTimeSpan::Seconds(timeElapSec).Format( L"%M:%S") : @@ -565,9 +573,11 @@ public: const int warningCount = log.getItemCount(TYPE_WARNING); const int infoCount = log.getItemCount(TYPE_INFO); - m_bpButtonErrors ->init(buttonPressed ("msg_error" ), buttonReleased("msg_error" ), _("Error" ) + wxString::Format(L" (%d)", errorCount )); - m_bpButtonWarnings->init(buttonPressed ("msg_warning"), buttonReleased("msg_warning"), _("Warning") + wxString::Format(L" (%d)", warningCount)); - m_bpButtonInfo ->init(buttonPressed ("msg_info" ), buttonReleased("msg_info" ), _("Info" ) + wxString::Format(L" (%d)", infoCount )); + 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); @@ -832,41 +842,45 @@ struct LabelFormatterTimeElapsed : public LabelFormatter } -class SyncProgressDialog::Pimpl : public SyncProgressDlgGenerated +class SyncProgressDialogImpl : private SyncProgressDlgGenerated, public SyncProgressDialog { public: - Pimpl(AbortCallback& abortCb, - const Statistics& syncStat, - MainDialog* parentWindow, - const wxString& jobName, - const std::wstring& execWhenFinished, - std::vector<std::wstring>& execFinishedHistory); - ~Pimpl(); - - void initNewPhase(); - void notifyProgressChange(); - void updateGui(bool allowYield = true); + SyncProgressDialogImpl(AbortCallback& abortCb, + const std::function<void()>& notifyWindowTerminate, + const Statistics& syncStat, + wxTopLevelWindow* parentWindow, + bool showProgress, + const wxString& jobName, + const std::wstring& execWhenFinished, + std::vector<std::wstring>& execFinishedHistory); + ~SyncProgressDialogImpl(); //call this in StatusUpdater derived class destructor at the LATEST(!) to prevent access to currentStatusUpdater - void processHasFinished(SyncResult resultId, const ErrorLog& log); - void closeWindowDirectly(); + virtual void processHasFinished(SyncResult resultId, const ErrorLog& log); + virtual void closeWindowDirectly(); + + virtual wxWindow* getAsWindow() { return this; } + virtual void initNewPhase(); + virtual void notifyProgressChange(); + virtual void updateGui() { updateGuiInt(true); } - std::wstring getExecWhenFinishedCommand() const; + virtual std::wstring getExecWhenFinishedCommand() const; - void stopTimer() //halt all internal counters! + virtual void stopTimer() //halt all internal counters! { m_animationControl1->Stop(); timeElapsed.Pause (); } - void resumeTimer() + virtual void resumeTimer() { m_animationControl1->Play(); timeElapsed.Resume(); } +private: + void updateGuiInt(bool allowYield); void minimizeToTray(); -private: void OnKeyPressed(wxKeyEvent& event); virtual void OnOkay (wxCommandEvent& event); virtual void OnPause (wxCommandEvent& event); @@ -884,11 +898,13 @@ private: const wxString jobName_; wxStopWatch timeElapsed; - MainDialog* mainDialog; //optional + wxTopLevelWindow* mainDialog; //optional + + std::function<void()> notifyWindowTerminate_; //call once in OnClose(), NOT in destructor which is called far too late somewhere in wxWidgets main loop! //status variables - AbortCallback* abortCb_; //temporarily bound while sync is running - const Statistics* syncStat_; // + const Statistics* syncStat_; // + AbortCallback* abortCb_; //valid only while sync is running bool paused_; //valid only while sync is running SyncResult finalResult; //set after sync @@ -909,12 +925,14 @@ private: }; -SyncProgressDialog::Pimpl::Pimpl(AbortCallback& abortCb, - const Statistics& syncStat, - MainDialog* parentWindow, - const wxString& jobName, - const std::wstring& execWhenFinished, - std::vector<std::wstring>& execFinishedHistory) : +SyncProgressDialogImpl::SyncProgressDialogImpl(AbortCallback& abortCb, + const std::function<void()>& notifyWindowTerminate, + const Statistics& syncStat, + wxTopLevelWindow* parentWindow, + bool showProgress, + const wxString& jobName, + const std::wstring& execWhenFinished, + std::vector<std::wstring>& execFinishedHistory) : SyncProgressDlgGenerated(parentWindow, wxID_ANY, parentWindow ? wxString() : (wxString(L"FreeFileSync - ") + _("Folder Comparison and Synchronization")), @@ -924,8 +942,9 @@ SyncProgressDialog::Pimpl::Pimpl(AbortCallback& abortCb, wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL), jobName_ (jobName), mainDialog(parentWindow), - abortCb_ (&abortCb), + notifyWindowTerminate_(notifyWindowTerminate), syncStat_ (&syncStat), + abortCb_ (&abortCb), paused_ (false), finalResult(RESULT_ABORTED), //dummy value isZombie(false), @@ -941,23 +960,18 @@ SyncProgressDialog::Pimpl::Pimpl(AbortCallback& abortCb, setRelativeFontSize(*m_staticTextPhase, 1.5); if (mainDialog) - { titelTextBackup = mainDialog->GetTitle(); //save old title (will be used as progress indicator) - mainDialog->disableAllElements(false); //disable all child elements - } m_animationControl1->SetAnimation(GlobalResources::instance().aniSync); m_animationControl1->Play(); - SetIcon(GlobalResources::instance().programIcon); + SetIcon(GlobalResources::instance().programIconFFS); //initialize gauge m_gauge1->SetRange(GAUGE_FULL_RANGE); m_gauge1->SetValue(0); - warn_static("not honored on osx") - - EnableCloseButton(false); + EnableCloseButton(false); //this is NOT honored on OS X or during system shutdown on Windows! if (IsShown()) //don't steal focus when starting in sys-tray! m_buttonAbort->SetFocus(); @@ -978,7 +992,7 @@ SyncProgressDialog::Pimpl::Pimpl(AbortCallback& abortCb, Layout(); //register key event - Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(Pimpl::OnKeyPressed), nullptr, this); + Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(SyncProgressDialogImpl::OnKeyPressed), nullptr, this); //init graph graphDataBytes = std::make_shared<GraphDataBytes>(); @@ -1003,14 +1017,22 @@ SyncProgressDialog::Pimpl::Pimpl(AbortCallback& abortCb, updateDialogStatus(); //null-status will be shown while waiting for dir locks (if at all) Fit(); + + if (showProgress) + { + Show(); + //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(); } -SyncProgressDialog::Pimpl::~Pimpl() +SyncProgressDialogImpl::~SyncProgressDialogImpl() { if (mainDialog) { - mainDialog->enableAllElements(); mainDialog->SetTitle(titelTextBackup); //restore title text //make sure main dialog is shown again if still "minimized to systray"! see SyncProgressDialog::closeWindowDirectly() @@ -1019,10 +1041,12 @@ SyncProgressDialog::Pimpl::~Pimpl() mainDialog->Show(); } + + //our client is NOT expecting a second call vianotifyWindowTerminate_()! } -void SyncProgressDialog::Pimpl::OnKeyPressed(wxKeyEvent& event) +void SyncProgressDialogImpl::OnKeyPressed(wxKeyEvent& event) { const int keyCode = event.GetKeyCode(); if (keyCode == WXK_ESCAPE) @@ -1048,7 +1072,7 @@ void SyncProgressDialog::Pimpl::OnKeyPressed(wxKeyEvent& event) } -void SyncProgressDialog::Pimpl::initNewPhase() +void SyncProgressDialogImpl::initNewPhase() { updateDialogStatus(); //evaluates "syncStat_->currentPhase()" @@ -1066,14 +1090,13 @@ void SyncProgressDialog::Pimpl::initNewPhase() //so give updateGui() a chance to set a different value m_gauge1->SetValue(0); - updateGui(false); + updateGuiInt(false); } -void SyncProgressDialog::Pimpl::notifyProgressChange() //noexcept! +void SyncProgressDialogImpl::notifyProgressChange() //noexcept! { - if (syncStat_) - { + if (syncStat_) //sync running switch (syncStat_->currentPhase()) { case ProcessCallback::PHASE_NONE: @@ -1090,7 +1113,6 @@ void SyncProgressDialog::Pimpl::notifyProgressChange() //noexcept! } break; } - } } @@ -1157,7 +1179,7 @@ std::wstring getDialogPhaseText(const Statistics* syncStat, bool paused, SyncPro } -void SyncProgressDialog::Pimpl::setExternalStatus(const wxString& status, const wxString& progress) //progress may be empty! +void SyncProgressDialogImpl::setExternalStatus(const wxString& status, const wxString& progress) //progress may be empty! { //sys tray: order "top-down": jobname, status, progress wxString newTrayInfo = jobName_.empty() ? status : L"\"" + jobName_ + L"\"\n" + status; @@ -1186,15 +1208,15 @@ void SyncProgressDialog::Pimpl::setExternalStatus(const wxString& status, const } -void SyncProgressDialog::Pimpl::updateGui(bool allowYield) +void SyncProgressDialogImpl::updateGuiInt(bool allowYield) { - assert(syncStat_); - if (!syncStat_) //no sync running!! + if (!syncStat_) //sync not running return; //wxWindowUpdateLocker dummy(this); -> not needed bool layoutChanged = false; //avoid screen flicker by calling layout() only if necessary + const long timeNow = timeElapsed.Time(); //sync status text setText(*m_staticTextStatus, replaceCpy(syncStat_->currentStatusText(), L'\n', L' ')); //no layout update for status texts! @@ -1256,20 +1278,20 @@ void SyncProgressDialog::Pimpl::updateGui(bool allowYield) //remaining time and speed assert(perf); if (perf) - { - const long timeNow = timeElapsed.Time(); if (numeric::dist(lastStatCallSpeed, timeNow) >= 500) { lastStatCallSpeed = timeNow; - perf->addSample(objectsCurrent, to<double>(dataCurrent), timeNow); + if (numeric::dist(phaseStartMs, timeNow) >= 1000) //discard stats for first second: probably messy + perf->addSample(objectsCurrent, to<double>(dataCurrent), timeNow); - //current speed -> Win 7 copy uses 1 sec update interval + //current speed -> Win 7 copy uses 1 sec update interval instead setText(*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 + setText(*m_staticTextRemTime, perf->getRemainingTime(to<double>(dataTotal - dataCurrent)), &layoutChanged); } - //remaining time: display with relative error of 10% - based on samples taken every 0.5 sec only - accuracy of prediction grows with time - setText(*m_staticTextRemTime, perf->getRemainingTime(to<double>(dataTotal - dataCurrent)), &layoutChanged); - } } break; } @@ -1279,7 +1301,7 @@ void SyncProgressDialog::Pimpl::updateGui(bool allowYield) m_panelGraph->Refresh(); //time elapsed - const long timeElapSec = timeElapsed.Time() / 1000; + const long timeElapSec = timeNow / 1000; setText(*m_staticTextTimeElapsed, timeElapSec < 3600 ? wxTimeSpan::Seconds(timeElapSec).Format( L"%M:%S") : @@ -1336,13 +1358,13 @@ void SyncProgressDialog::Pimpl::updateGui(bool allowYield) } -std::wstring SyncProgressDialog::Pimpl::getExecWhenFinishedCommand() const +std::wstring SyncProgressDialogImpl::getExecWhenFinishedCommand() const { return m_comboBoxExecFinished->getValue(); } -void SyncProgressDialog::Pimpl::updateDialogStatus() //depends on "syncStat_, paused_, finalResult" +void SyncProgressDialogImpl::updateDialogStatus() //depends on "syncStat_, paused_, finalResult" { const wxString dlgStatusTxt = getDialogPhaseText(syncStat_, paused_, finalResult); @@ -1449,25 +1471,20 @@ void SyncProgressDialog::Pimpl::updateDialogStatus() //depends on "syncStat_, pa warn_static("osx: minimize to systray?") -void SyncProgressDialog::Pimpl::closeWindowDirectly() //this should really be called: do not call back + schedule deletion +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 cut callbacks! + //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! - //------- change class state ------- - abortCb_ = nullptr; //avoid callback to (maybe) deleted parent process - syncStat_ = nullptr; //set *after* last call to "updateGui" - //---------------------------------- - - //resumeFromSystray(); -> NO, instead ~Pimpl() makes sure that main dialog is shown again! - - Close(); + Close(); //generate close event: do NOT destroy window unconditionally! } -void SyncProgressDialog::Pimpl::processHasFinished(SyncResult resultId, const ErrorLog& log) //essential to call this in StatusHandler derived class destructor +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 @@ -1478,7 +1495,7 @@ void SyncProgressDialog::Pimpl::processHasFinished(SyncResult resultId, const Er //update numbers one last time (as if sync were still running) notifyProgressChange(); //make one last graph entry at the *current* time - updateGui(false); + updateGuiInt(false); switch (syncStat_->currentPhase()) //no matter if paused or not { @@ -1526,9 +1543,10 @@ void SyncProgressDialog::Pimpl::processHasFinished(SyncResult resultId, const Er } //------- change class state ------- - abortCb_ = nullptr; //avoid callback to (maybe) deleted parent process - syncStat_ = nullptr; //set *after* last call to "updateGui" finalResult = resultId; + + syncStat_ = nullptr; + abortCb_ = nullptr; //---------------------------------- updateDialogStatus(); @@ -1573,7 +1591,7 @@ void SyncProgressDialog::Pimpl::processHasFinished(SyncResult resultId, const Er //workaround wxListBox bug on Windows XP: labels are drawn on top of each other assert(m_listbookResult->GetImageList()); //make sure listbook keeps *any* image list //due to some crazy reasons that aren't worth debugging, this needs to be done directly in wxFormBuilder, - //the following call is *not* sufficient: m_listbookResult->AssignImageList(new wxImageList(180, 1)); + //the following call is *not* sufficient: m_listbookResult->AssignImageList(new wxImageList(170, 1)); //note: alternative solutions involving wxLC_LIST, wxLC_REPORT and SetWindowStyleFlag() do not work portably! wxListBook using wxLC_ICON is obviously a class invariant! //1. re-arrange graph into results listbook @@ -1595,7 +1613,6 @@ void SyncProgressDialog::Pimpl::processHasFinished(SyncResult resultId, const Er m_listbookResult->ChangeSelection(posLog); Layout(); - //play (optional) sound notification after sync has completed -> only play when waiting on results dialog, seems to be pointless otherwise! switch (finalResult) { @@ -1616,14 +1633,14 @@ void SyncProgressDialog::Pimpl::processHasFinished(SyncResult resultId, const Er } -void SyncProgressDialog::Pimpl::OnOkay(wxCommandEvent& event) +void SyncProgressDialogImpl::OnOkay(wxCommandEvent& event) { - isZombie = true; //on Fedora the iconize event is executed *before* entering SyncProgressDialog::Pimpl::OnClose()!!! + isZombie = true; //on Fedora an iconize event is issued *before* entering SyncProgressDialogImpl::OnClose()!!! Close(); //generate close event: do NOT destroy window unconditionally! } -void SyncProgressDialog::Pimpl::OnAbort(wxCommandEvent& event) +void SyncProgressDialogImpl::OnAbort(wxCommandEvent& event) { paused_ = false; updateDialogStatus(); //update status + pause button @@ -1634,27 +1651,36 @@ void SyncProgressDialog::Pimpl::OnAbort(wxCommandEvent& event) } -void SyncProgressDialog::Pimpl::OnPause(wxCommandEvent& event) +void SyncProgressDialogImpl::OnPause(wxCommandEvent& event) { paused_ = !paused_; updateDialogStatus(); //update status + pause button } -void SyncProgressDialog::Pimpl::OnClose(wxCloseEvent& event) +void SyncProgressDialogImpl::OnClose(wxCloseEvent& event) { - //this event handler may be called due to a system shutdown DURING synchronization! + //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 (as in batch mode) + //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; isZombie = true; //it "lives" until cleanup in next idle event Destroy(); } -void SyncProgressDialog::Pimpl::OnIconize(wxIconizeEvent& event) +void SyncProgressDialogImpl::OnIconize(wxIconizeEvent& event) { if (isZombie) return; //wxGTK sends iconize event *after* wxWindow::Destroy, sigh... @@ -1665,23 +1691,22 @@ void SyncProgressDialog::Pimpl::OnIconize(wxIconizeEvent& event) } -void SyncProgressDialog::Pimpl::OnResumeFromTray(wxCommandEvent& event) +void SyncProgressDialogImpl::OnResumeFromTray(wxCommandEvent& event) { resumeFromSystray(); } -void SyncProgressDialog::Pimpl::minimizeToTray() +void SyncProgressDialogImpl::minimizeToTray() { if (!trayIcon.get()) { trayIcon = make_unique<FfsTrayIcon>(); - trayIcon->Connect(FFS_REQUEST_RESUME_TRAY_EVENT, wxCommandEventHandler(SyncProgressDialog::Pimpl::OnResumeFromTray), nullptr, this); + trayIcon->Connect(FFS_REQUEST_RESUME_TRAY_EVENT, wxCommandEventHandler(SyncProgressDialogImpl::OnResumeFromTray), nullptr, this); //tray icon has shorter lifetime than this => no need to disconnect event later } - if (syncStat_) - updateGui(false); //set tray tooltip + progress: e.g. no updates while paused + updateGuiInt(false); //set tray tooltip + progress: e.g. no updates while paused Hide(); if (mainDialog) @@ -1689,7 +1714,7 @@ void SyncProgressDialog::Pimpl::minimizeToTray() } -void SyncProgressDialog::Pimpl::resumeFromSystray() +void SyncProgressDialogImpl::resumeFromSystray() { trayIcon.reset(); @@ -1708,80 +1733,19 @@ void SyncProgressDialog::Pimpl::resumeFromSystray() SetFocus(); updateDialogStatus(); //restore Windows 7 task bar status (e.g. required in pause mode) - if (syncStat_) - updateGui(false); //restore Windows 7 task bar progress (e.g. required in pause mode) + updateGuiInt(false); //restore Windows 7 task bar progress (e.g. required in pause mode) } - //######################################################################################## -//redirect to implementation -SyncProgressDialog::SyncProgressDialog(AbortCallback& abortCb, - const Statistics& syncStat, - MainDialog* parentWindow, - bool showProgress, - const wxString& jobName, - const std::wstring& execWhenFinished, - std::vector<std::wstring>& execFinishedHistory) : - pimpl(new Pimpl(abortCb, syncStat, parentWindow, jobName, execWhenFinished, execFinishedHistory)) -{ - if (showProgress) - { - pimpl->Show(); - warn_static("problem??") - //clear gui flicker, remove dummy texts: window must be visible to make this work! - pimpl->updateGui(true); //at least on OS X a real Yield() is required to flush pending GUI updates; Update() is not enough - } - else - pimpl->minimizeToTray(); -} - -SyncProgressDialog::~SyncProgressDialog() -{ - //DON'T delete pimpl! it will be deleted by the user clicking "OK/Cancel" -> (wxWindow::Destroy()) -} - -wxWindow* SyncProgressDialog::getAsWindow() -{ - return pimpl; -} - -void SyncProgressDialog::initNewPhase() -{ - pimpl->initNewPhase(); -} - -void SyncProgressDialog::notifyProgressChange() -{ - pimpl->notifyProgressChange(); -} - -void SyncProgressDialog::updateGui() -{ - pimpl->updateGui(); -} - -std::wstring SyncProgressDialog::getExecWhenFinishedCommand() const -{ - return pimpl->getExecWhenFinishedCommand(); -} - -void SyncProgressDialog::stopTimer() -{ - return pimpl->stopTimer(); -} - -void SyncProgressDialog::resumeTimer() -{ - return pimpl->resumeTimer(); -} - -void SyncProgressDialog::processHasFinished(SyncResult resultId, const ErrorLog& log) -{ - pimpl->processHasFinished(resultId, log); -} - -void SyncProgressDialog::closeWindowDirectly() //don't wait for user (silent mode) +SyncProgressDialog* createProgressDialog(zen::AbortCallback& abortCb, + const std::function<void()>& notifyWindowTerminate, //note: user closing window cannot be prevented on OS X! (And neither on Windows during system shutdown!) + const zen::Statistics& syncStat, + wxTopLevelWindow* parentWindow, //may be nullptr + bool showProgress, + const wxString& jobName, + const std::wstring& execWhenFinished, + std::vector<std::wstring>& execFinishedHistory) { - pimpl->closeWindowDirectly(); + return new SyncProgressDialogImpl(abortCb, notifyWindowTerminate, syncStat, parentWindow, showProgress, jobName, execWhenFinished, execFinishedHistory); } diff --git a/ui/progress_indicator.h b/ui/progress_indicator.h index d659af6e..0789ca80 100644 --- a/ui/progress_indicator.h +++ b/ui/progress_indicator.h @@ -7,11 +7,12 @@ #ifndef PROGRESSINDICATOR_H_INCLUDED #define PROGRESSINDICATOR_H_INCLUDED +#include <functional> #include <zen/error_log.h> -#include <zen/zstring.h> +//#include <zen/zstring.h> #include <wx/toplevel.h> #include "../lib/status_handler.h" -#include "main_dlg.h" +//#include "main_dlg.h" class CompareProgressDialog @@ -33,30 +34,10 @@ private: }; -class SyncProgressDialog -{ -public: - SyncProgressDialog(zen::AbortCallback& abortCb, - const zen::Statistics& syncStat, - MainDialog* parentWindow, //may be nullptr - bool showProgress, - const wxString& jobName, - const std::wstring& execWhenFinished, - std::vector<std::wstring>& execFinishedHistory); //changing parameter! - ~SyncProgressDialog(); - - wxWindow* getAsWindow(); //convenience! don't abuse! - - void initNewPhase(); //call after "StatusHandler::initNewPhase" - - void notifyProgressChange(); //throw (), required by graph! - void updateGui(); - - std::wstring getExecWhenFinishedCommand() const; //final value (after possible user modification) - - void stopTimer(); //halt all internal counters! - void resumeTimer(); // +//SyncStatusHandler will internally process Window messages => disable GUI controls to avoid unexpected callbacks! +struct SyncProgressDialog +{ enum SyncResult { RESULT_ABORTED, @@ -64,17 +45,40 @@ public: RESULT_FINISHED_WITH_WARNINGS, RESULT_FINISHED_WITH_SUCCESS }; - //essential to call one of these two methods in StatusUpdater derived class destructor at the LATEST(!) + //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) - void processHasFinished(SyncResult resultId, const zen::ErrorLog& log); //sync finished, still dialog may live on - void closeWindowDirectly(); //don't wait for user + 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 -private: - class Pimpl; - Pimpl* const pimpl; + //--------------------------------------------------------------------------- + + virtual wxWindow* getAsWindow() = 0; //convenience! don't abuse! + + 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<void()>& notifyWindowTerminate, //note: user closing window cannot be prevented on OS X! (And neither on Windows during system shutdown!) + const zen::Statistics& syncStat, + wxTopLevelWindow* parentWindow, //may be nullptr + bool showProgress, + const wxString& jobName, + const std::wstring& execWhenFinished, + std::vector<std::wstring>& 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: diff --git a/ui/small_dlgs.cpp b/ui/small_dlgs.cpp index 3827c837..32bb3e85 100644 --- a/ui/small_dlgs.cpp +++ b/ui/small_dlgs.cpp @@ -8,6 +8,7 @@ #include <wx/wupdlock.h> #include <zen/format_unit.h> #include <zen/build_info.h> +#include <zen/tick_count.h> #include <zen/stl_tools.h> #include <wx+/choice_enum.h> #include <wx+/button.h> @@ -306,8 +307,8 @@ ReturnSmallDlg::ButtonPressed zen::showFilterDialog(wxWindow* parent, bool isGlo filter); return static_cast<ReturnSmallDlg::ButtonPressed>(filterDlg.ShowModal()); } -//######################################################################################## +//######################################################################################## class DeleteDialog : public DeleteDlgGenerated { @@ -331,6 +332,8 @@ private: const std::vector<zen::FileSystemObject*>& rowsToDeleteOnRight; bool& outRefdeleteOnBothSides; bool& outRefuseRecycleBin; + const TickVal tickCountStartup; + const std::int64_t ticksPerSec_; }; @@ -343,7 +346,9 @@ DeleteDialog::DeleteDialog(wxWindow* parent, rowsToDeleteOnLeft(rowsOnLeft), rowsToDeleteOnRight(rowsOnRight), outRefdeleteOnBothSides(deleteOnBothSides), - outRefuseRecycleBin(useRecycleBin) + outRefuseRecycleBin(useRecycleBin), + tickCountStartup(getTicks()), + ticksPerSec_(ticksPerSec()) { #ifdef FFS_WIN new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" @@ -403,6 +408,12 @@ void DeleteDialog::updateGui() 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 + if (now.isValid() && tickCountStartup.isValid() && ticksPerSec_ != 0) + if (dist(tickCountStartup, now) * 1000 / ticksPerSec_ < 50) + return; + outRefuseRecycleBin = m_checkBoxUseRecycler->GetValue(); if (rowsToDeleteOnLeft != rowsToDeleteOnRight) outRefdeleteOnBothSides = m_checkBoxDeleteBothSides->GetValue(); @@ -746,7 +757,7 @@ void GlobalSettingsDlg::OnOkay(wxCommandEvent& event) void GlobalSettingsDlg::OnResetDialogs(wxCommandEvent& event) { if (showQuestionDlg(this, ReturnQuestionDlg::BUTTON_YES | ReturnQuestionDlg::BUTTON_CANCEL, - _("Make hidden warnings and dialogs visible again?")) == ReturnQuestionDlg::BUTTON_YES) + _("Make hidden warnings and dialogs visible again?"), QuestConfig().setLabelYes(_("&Restore"))) == ReturnQuestionDlg::BUTTON_YES) settings.optDialogs.resetDialogs(); } diff --git a/ui/sorting.h b/ui/sorting.h index 75b2593a..28d2f931 100644 --- a/ui/sorting.h +++ b/ui/sorting.h @@ -20,7 +20,7 @@ struct CompileTimeReminder : public FSObjectVisitor virtual void visit(const FileMapping& fileObj) {} virtual void visit(const SymLinkMapping& linkObj) {} virtual void visit(const DirMapping& dirObj ) {} -} checkDymanicCasts; //just a compile-time reminder to check dynamic casts in this file +} checkDymanicCasts; //just a compile-time reminder to manually check dynamic casts in this file when needed } inline diff --git a/ui/sync_cfg.cpp b/ui/sync_cfg.cpp index dbef3619..dc62b04a 100644 --- a/ui/sync_cfg.cpp +++ b/ui/sync_cfg.cpp @@ -235,8 +235,8 @@ SyncCfgDialog::SyncCfgDialog(wxWindow* parent, setRelativeFontSize(*m_toggleBtnCustom, 1.25); enumVersioningStyle. - add(VER_STYLE_ADD_TIMESTAMP, _("Time stamp"), _("Append a timestamp to each file name")). - add(VER_STYLE_REPLACE, _("Replace"), _("Move files and replace if existing")); + 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! diff --git a/ui/tree_view.cpp b/ui/tree_view.cpp index 07f7a942..cf866a71 100644 --- a/ui/tree_view.cpp +++ b/ui/tree_view.cpp @@ -664,6 +664,70 @@ std::unique_ptr<TreeView::Node> TreeView::getLine(size_t row) const namespace { +#ifdef _MSC_VER +#pragma warning(disable:4428) // VC wrongly issues warning C4428: universal-character-name encountered in source +#endif + +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<wxString>(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<wxString>(commonPostfix); +} + + const wxColour COLOR_LEVEL0(0xcc, 0xcc, 0xff); const wxColour COLOR_LEVEL1(0xcc, 0xff, 0xcc); const wxColour COLOR_LEVEL2(0xff, 0xff, 0x99); @@ -716,6 +780,32 @@ public: private: virtual size_t getRowCount() const { return treeDataView_ ? treeDataView_->linesTotal() : 0; } + virtual wxString getToolTip(size_t row, ColumnType colType) const + { + switch (static_cast<ColumnTypeNavi>(colType)) + { + case COL_TYPE_NAVI_BYTES: + case COL_TYPE_NAVI_ITEM_COUNT: + break; + + case COL_TYPE_NAVI_DIRECTORY: + if (treeDataView_) + if (std::unique_ptr<TreeView::Node> node = treeDataView_->getLine(row)) + if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get())) + { + const wxString& dirLeft = utfCvrtTo<wxString>(root->baseMap_.getBaseDirPf<LEFT_SIDE >()); + const wxString& dirRight = utfCvrtTo<wxString>(root->baseMap_.getBaseDirPf<RIGHT_SIDE>()); + 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_) @@ -728,17 +818,8 @@ private: case COL_TYPE_NAVI_DIRECTORY: if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get())) - { - const wxString dirLeft = utfCvrtTo<wxString>(beforeLast(root->baseMap_.getBaseDirPf<LEFT_SIDE >(), FILE_NAME_SEPARATOR)); - const wxString dirRight = utfCvrtTo<wxString>(beforeLast(root->baseMap_.getBaseDirPf<RIGHT_SIDE>(), FILE_NAME_SEPARATOR)); - - if (dirLeft.empty()) - return dirRight; - else if (dirRight.empty()) - return dirLeft; - else - return dirLeft + L" \x2212 " + dirRight; //\x2212 = unicode minus - } + return getShortDisplayNameForFolderPair(root->baseMap_.getBaseDirPf<LEFT_SIDE >(), + root->baseMap_.getBaseDirPf<RIGHT_SIDE>()); else if (const TreeView::DirNode* dir = dynamic_cast<const TreeView::DirNode*>(node.get())) return utfCvrtTo<wxString>(dir->dirObj_.getObjShortName()); else if (dynamic_cast<const TreeView::FilesNode*>(node.get())) @@ -749,7 +830,7 @@ private: return toGuiString(node->itemCount_); } } - return wxEmptyString; + return wxString(); } virtual void renderColumnLabel(Grid& tree, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted) diff --git a/version/version.h b/version/version.h index 3aae7d4c..37cdffc2 100644 --- a/version/version.h +++ b/version/version.h @@ -3,7 +3,7 @@ namespace zen { -const wchar_t currentVersion[] = L"5.15"; //internal linkage! +const wchar_t currentVersion[] = L"5.16"; //internal linkage! } #endif diff --git a/wx+/create_pch.cpp b/wx+/create_pch.cpp index ad4cabd9..be54115f 100644 --- a/wx+/create_pch.cpp +++ b/wx+/create_pch.cpp @@ -5,4 +5,4 @@ // ************************************************************************** //dummy file for Visual Studio to compile precompiled header: -//attaching pch to any other cpp file triggers a full rebuild each time code is changed!
\ No newline at end of file +//associating pch with any other cpp file will trigger a full pch rebuild each time code is changed!
\ No newline at end of file diff --git a/wx+/grid.cpp b/wx+/grid.cpp index 67d01fef..a0af0671 100644 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -1332,7 +1332,7 @@ private: double deltaTime = 0; if (ticksPerSec_ > 0) { - const TickVal now = getTicks(); //0 on error + const TickVal now = getTicks(); //isValid() on error deltaTime = static_cast<double>(dist(tickCountLast, now)) / ticksPerSec_; //unit: [sec] tickCountLast = now; } diff --git a/wx+/image_tools.h b/wx+/image_tools.h index 9de93c26..ec9e34d4 100644 --- a/wx+/image_tools.h +++ b/wx+/image_tools.h @@ -54,6 +54,8 @@ void move(wxImage& img, int up, int left) inline wxBitmap greyScale(const wxBitmap& bmp) { + assert(!bmp.GetMask()); //wxWidgets screws up for the gazillionth time applying a mask instead of alpha channel if the .png image has only 0 and 0xff opacity values!!! + wxImage output = bmp.ConvertToImage().ConvertToGreyscale(1.0 / 3, 1.0 / 3, 1.0 / 3); //treat all channels equally! //wxImage output = bmp.ConvertToImage().ConvertToGreyscale(); adjustBrightness(output, 160); diff --git a/wx+/toggle_button.h b/wx+/toggle_button.h index 548def1d..858082d3 100644 --- a/wx+/toggle_button.h +++ b/wx+/toggle_button.h @@ -27,8 +27,7 @@ public: } void init(const wxBitmap& activeBmp, - const wxBitmap& inactiveBmp, - const wxString& tooltip); + const wxBitmap& inactiveBmp); void setActive(bool value); bool isActive() const { return active; } @@ -56,11 +55,8 @@ private: //######################## implementation ######################## inline void ToggleButton::init(const wxBitmap& activeBmp, - const wxBitmap& inactiveBmp, - const wxString& tooltip) + const wxBitmap& inactiveBmp) { - SetToolTip(tooltip); - activeBmp_ = activeBmp; inactiveBmp_ = inactiveBmp; diff --git a/zen/async_task.h b/zen/async_task.h index b4123b5d..c5e5857a 100644 --- a/zen/async_task.h +++ b/zen/async_task.h @@ -11,6 +11,7 @@ #include <functional> #include <zen/thread.h> #include <zen/scope_guard.h> +//#include "type_tools.h" namespace zen { @@ -34,7 +35,7 @@ public: } template <class Fun, class Fun2> - void add2(Fun doAsync, Fun2 evalOnGui) //for doAsync returning void + void add2(Fun doAsync, Fun2 evalOnGui) //for evalOnGui taking no parameters { tasks.push_back(zen::async([=]() -> std::function<void()> { doAsync(); return [=]{ evalOnGui(); }; })); } @@ -64,7 +65,6 @@ private: bool inRecursion; std::list<boost::unique_future<std::function<void()>>> tasks; }; - } #endif //ASYNC_JOB_839147839170432143214321 diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index 6c6c0929..6402dce7 100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -386,7 +386,8 @@ public: dirs_.push_back(fullName); return otherMe_; } - virtual HandleError onError(const std::wstring& msg) { throw FileError(msg); } + virtual HandleError reportDirError (const std::wstring& msg) { throw FileError(msg); } + virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) { throw FileError(msg); } private: const std::shared_ptr<TraverseCallback>& otherMe_; //lifetime management, two options: 1. use std::weak_ptr 2. ref to shared_ptr diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp index b529720f..8dc3e72d 100644 --- a/zen/file_handling.cpp +++ b/zen/file_handling.cpp @@ -50,11 +50,11 @@ bool zen::fileExists(const Zstring& filename) { //symbolic links (broken or not) are also treated as existing files! #ifdef FFS_WIN - const DWORD ret = ::GetFileAttributes(applyLongPathPrefix(filename).c_str()); - return ret != INVALID_FILE_ATTRIBUTES && (ret & FILE_ATTRIBUTE_DIRECTORY) == 0; //returns true for (file-)symlinks also + const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(filename).c_str()); + return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0; //returns true for (file-)symlinks also #elif defined FFS_LINUX || defined FFS_MAC - struct stat fileInfo = {}; + struct ::stat fileInfo = {}; return ::lstat(filename.c_str(), &fileInfo) == 0 && (S_ISLNK(fileInfo.st_mode) || S_ISREG(fileInfo.st_mode)); //in Linux a symbolic link is neither file nor directory #endif @@ -65,11 +65,11 @@ bool zen::dirExists(const Zstring& dirname) { //symbolic links (broken or not) are also treated as existing directories! #ifdef FFS_WIN - const DWORD ret = ::GetFileAttributes(applyLongPathPrefix(dirname).c_str()); - return ret != INVALID_FILE_ATTRIBUTES && (ret & FILE_ATTRIBUTE_DIRECTORY) != 0; //returns true for (dir-)symlinks also + const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(dirname).c_str()); + return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; //returns true for (dir-)symlinks also #elif defined FFS_LINUX || defined FFS_MAC - struct stat dirInfo = {}; + struct ::stat dirInfo = {}; return ::lstat(dirname.c_str(), &dirInfo) == 0 && (S_ISLNK(dirInfo.st_mode) || S_ISDIR(dirInfo.st_mode)); //in Linux a symbolic link is neither file nor directory #endif @@ -89,7 +89,7 @@ bool zen::symlinkExists(const Zstring& linkname) return isSymlink(fileInfo); #elif defined FFS_LINUX || defined FFS_MAC - struct stat fileInfo = {}; + struct ::stat fileInfo = {}; return ::lstat(linkname.c_str(), &fileInfo) == 0 && S_ISLNK(fileInfo.st_mode); //symbolic link #endif @@ -99,16 +99,35 @@ bool zen::symlinkExists(const Zstring& linkname) bool zen::somethingExists(const Zstring& objname) { #ifdef FFS_WIN - const DWORD rv = ::GetFileAttributes(applyLongPathPrefix(objname).c_str()); - return rv != INVALID_FILE_ATTRIBUTES || ::GetLastError() == ERROR_SHARING_VIOLATION; //"C:\pagefile.sys" + const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(objname).c_str()); + return attr != INVALID_FILE_ATTRIBUTES || ::GetLastError() == ERROR_SHARING_VIOLATION; //"C:\pagefile.sys" #elif defined FFS_LINUX || defined FFS_MAC - struct stat fileInfo = {}; + struct ::stat fileInfo = {}; return ::lstat(objname.c_str(), &fileInfo) == 0; #endif } +SymLinkType zen::getSymlinkType(const Zstring& linkname) //throw() +{ + assert(symlinkExists(linkname)); +#ifdef FFS_WIN + const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(linkname).c_str()); + if (attr == INVALID_FILE_ATTRIBUTES) + return SYMLINK_TYPE_UNKNOWN; + return (attr & FILE_ATTRIBUTE_DIRECTORY) ? SYMLINK_TYPE_DIR : SYMLINK_TYPE_FILE; + +#elif defined FFS_LINUX || defined FFS_MAC + //S_ISDIR and S_ISLNK are mutually exclusive on Linux => explicitly need to follow link + struct ::stat fileInfo = {}; + if (::stat(linkname.c_str(), &fileInfo) != 0) + return SYMLINK_TYPE_UNKNOWN; + return S_ISDIR(fileInfo.st_mode) ? SYMLINK_TYPE_DIR : SYMLINK_TYPE_FILE; +#endif +} + + namespace { void getFileAttrib(const Zstring& filename, FileAttrib& attr, ProcSymlink procSl) //throw FileError @@ -155,7 +174,6 @@ void getFileAttrib(const Zstring& filename, FileAttrib& attr, ProcSymlink procSl nullptr); if (hFile == INVALID_HANDLE_VALUE) throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted()); - ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); BY_HANDLE_FILE_INFORMATION fileInfoHnd = {}; @@ -167,7 +185,7 @@ void getFileAttrib(const Zstring& filename, FileAttrib& attr, ProcSymlink procSl } #elif defined FFS_LINUX || defined FFS_MAC - struct stat fileInfo = {}; + struct ::stat fileInfo = {}; const int rv = procSl == SYMLINK_FOLLOW ? :: stat(filename.c_str(), &fileInfo) : @@ -575,34 +593,42 @@ class CollectFilesFlat : public zen::TraverseCallback { public: CollectFilesFlat(std::vector<Zstring>& files, std::vector<Zstring>& dirs) : - m_files(files), - m_dirs(dirs) {} + files_(files), + dirs_(dirs) {} virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) { - m_files.push_back(fullName); + files_.push_back(fullName); } virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) { - if (details.dirLink) - m_dirs.push_back(fullName); - else - m_files.push_back(fullName); + switch (getSymlinkType(fullName)) + { + case SYMLINK_TYPE_DIR: + dirs_.push_back(shortName); + break; + + case SYMLINK_TYPE_FILE: + case SYMLINK_TYPE_UNKNOWN: + files_.push_back(shortName); + break; + } return LINK_SKIP; } virtual std::shared_ptr<TraverseCallback> onDir(const Zchar* shortName, const Zstring& fullName) { - m_dirs.push_back(fullName); + dirs_.push_back(fullName); return nullptr; //DON'T traverse into subdirs; removeDirectory works recursively! } - virtual HandleError onError(const std::wstring& msg) { throw FileError(msg); } + virtual HandleError reportDirError (const std::wstring& msg) { throw FileError(msg); } + virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) { throw FileError(msg); } private: CollectFilesFlat(const CollectFilesFlat&); CollectFilesFlat& operator=(const CollectFilesFlat&); - std::vector<Zstring>& m_files; - std::vector<Zstring>& m_dirs; + std::vector<Zstring>& files_; + std::vector<Zstring>& dirs_; }; @@ -989,49 +1015,6 @@ bool zen::supportsPermissions(const Zstring& dirname) //throw FileError namespace { -#ifdef FFS_WIN -Zstring getSymlinkTargetPath(const Zstring& symlink) //throw FileError -{ - //open handle to target of symbolic link - const HANDLE hDir = ::CreateFile(applyLongPathPrefix(symlink).c_str(), - 0, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - nullptr, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory - nullptr); - if (hDir == INVALID_HANDLE_VALUE) - throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(symlink)) + L"\n\n" + getLastErrorFormatted()); - ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir)); - - //dynamically load windows API function - typedef DWORD (WINAPI* GetFinalPathNameByHandleWFunc)(HANDLE hFile, - LPTSTR lpszFilePath, - DWORD cchFilePath, - DWORD dwFlags); - const SysDllFun<GetFinalPathNameByHandleWFunc> getFinalPathNameByHandle(L"kernel32.dll", "GetFinalPathNameByHandleW"); - if (!getFinalPathNameByHandle) - throw FileError(replaceCpy(_("Cannot find system function %x."), L"%x", L"\"GetFinalPathNameByHandleW\"")); - - const DWORD BUFFER_SIZE = 10000; - std::vector<wchar_t> targetPath(BUFFER_SIZE); - const DWORD charsWritten = getFinalPathNameByHandle(hDir, //__in HANDLE hFile, - &targetPath[0], //__out LPTSTR lpszFilePath, - BUFFER_SIZE, //__in DWORD cchFilePath, - FILE_NAME_NORMALIZED); //__in DWORD dwFlags - if (charsWritten >= BUFFER_SIZE || charsWritten == 0) - { - std::wstring errorMessage = replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(symlink)); - if (charsWritten == 0) - errorMessage += L"\n\n" + getLastErrorFormatted(); - throw FileError(errorMessage); - } - - return Zstring(&targetPath[0], charsWritten); -} -#endif - - #ifdef HAVE_SELINUX //copy SELinux security context void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymlink procSl) //throw FileError @@ -1086,9 +1069,9 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym //in contrast to ::SetSecurityInfo(), ::SetFileSecurity() seems to honor the "inherit DACL/SACL" flags //CAVEAT: if a file system does not support ACLs, GetFileSecurity() will return successfully with a *valid* security descriptor containing *no* ACL entries! - //NOTE: ::GetFileSecurity()/::SetFileSecurity() do NOT follow Symlinks! - const Zstring sourceResolved = procSl == SYMLINK_FOLLOW && symlinkExists(source) ? getSymlinkTargetPath(source) : source; - const Zstring targetResolved = procSl == SYMLINK_FOLLOW && symlinkExists(target) ? getSymlinkTargetPath(target) : target; + //NOTE: ::GetFileSecurity()/::SetFileSecurity() do NOT follow Symlinks! getResolvedFilePath() requires Vista or later! + const Zstring sourceResolved = procSl == SYMLINK_FOLLOW && symlinkExists(source) ? getResolvedFilePath(source) : source; //throw FileError + const Zstring targetResolved = procSl == SYMLINK_FOLLOW && symlinkExists(target) ? getResolvedFilePath(target) : target; // //setting privileges requires admin rights! try @@ -1400,50 +1383,46 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT if (!templateDir.empty()) { #ifdef FFS_WIN - //try to copy file attributes - Zstring sourcePath; - - if (symlinkExists(templateDir)) - try - { - //get target directory of symbolic link - sourcePath = getSymlinkTargetPath(templateDir); //throw FileError - } - catch (FileError&) {} //dereferencing a symbolic link usually fails if it is located on network drive or client is XP: NOT really an error... - else - sourcePath = templateDir; - - //*try* to copy file attributes - if (!sourcePath.empty()) + //try to copy file attributes (dereference symlinks and junctions) + const HANDLE hDirSrc = ::CreateFile(zen::applyLongPathPrefix(templateDir).c_str(), + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS /*needed to open a directory*/ /*| FILE_FLAG_OPEN_REPARSE_POINT -> no, we follow symlinks!*/ , + nullptr); + if (hDirSrc != INVALID_HANDLE_VALUE) //dereferencing a symbolic link usually fails if it is located on network drive or client is XP: NOT really an error... { - const DWORD sourceAttr = ::GetFileAttributes(applyLongPathPrefix(sourcePath).c_str()); - if (sourceAttr != INVALID_FILE_ATTRIBUTES) + ZEN_ON_SCOPE_EXIT(::CloseHandle(hDirSrc)); + + BY_HANDLE_FILE_INFORMATION dirInfo = {}; + if (::GetFileInformationByHandle(hDirSrc, &dirInfo)) { - ::SetFileAttributes(applyLongPathPrefix(directory).c_str(), sourceAttr); + ::SetFileAttributes(applyLongPathPrefix(directory).c_str(), dirInfo.dwFileAttributes); //copy "read-only and system attributes": http://blogs.msdn.com/b/oldnewthing/archive/2003/09/30/55100.aspx - const bool isCompressed = (sourceAttr & FILE_ATTRIBUTE_COMPRESSED) != 0; - const bool isEncrypted = (sourceAttr & FILE_ATTRIBUTE_ENCRYPTED) != 0; + const bool isCompressed = (dirInfo.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; + const bool isEncrypted = (dirInfo.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0; if (isEncrypted) ::EncryptFile(directory.c_str()); //seems no long path is required (check passed!) if (isCompressed) { - HANDLE hDir = ::CreateFile(applyLongPathPrefix(directory).c_str(), - GENERIC_READ | GENERIC_WRITE, //read access required for FSCTL_SET_COMPRESSION - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - nullptr, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, - nullptr); - if (hDir != INVALID_HANDLE_VALUE) + HANDLE hDirTrg = ::CreateFile(applyLongPathPrefix(directory).c_str(), + GENERIC_READ | GENERIC_WRITE, //read access required for FSCTL_SET_COMPRESSION + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + nullptr); + if (hDirTrg != INVALID_HANDLE_VALUE) { - ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir)); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hDirTrg)); USHORT cmpState = COMPRESSION_FORMAT_DEFAULT; DWORD bytesReturned = 0; - ::DeviceIoControl(hDir, //handle to file or directory + ::DeviceIoControl(hDirTrg, //handle to file or directory FSCTL_SET_COMPRESSION, //dwIoControlCode &cmpState, //input buffer sizeof(cmpState), //size of input buffer @@ -1470,19 +1449,19 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions) //throw FileError { - const Zstring linkPath = getSymlinkRawTargetString(sourceLink); //accept broken symlinks; throw FileError + const Zstring linkPath = getSymlinkTargetRaw(sourceLink); //throw FileError; accept broken symlinks #ifdef FFS_WIN const bool isDirLink = [&]() -> bool { const DWORD ret = ::GetFileAttributes(applyLongPathPrefix(sourceLink).c_str()); - return ret != INVALID_FILE_ATTRIBUTES && (ret& FILE_ATTRIBUTE_DIRECTORY); + return ret != INVALID_FILE_ATTRIBUTES && (ret & FILE_ATTRIBUTE_DIRECTORY); }(); //dynamically load windows API function typedef BOOLEAN (WINAPI* CreateSymbolicLinkFunc)(LPCTSTR lpSymlinkFileName, LPCTSTR lpTargetFileName, DWORD dwFlags); - const SysDllFun<CreateSymbolicLinkFunc> createSymbolicLink(L"kernel32.dll", "CreateSymbolicLinkW"); + if (!createSymbolicLink) throw FileError(replaceCpy(_("Cannot find system function %x."), L"%x", L"\"CreateSymbolicLinkW\"")); @@ -1492,8 +1471,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool #elif defined FFS_LINUX || defined FFS_MAC if (::symlink(linkPath.c_str(), targetLink.c_str()) != 0) #endif - throw FileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtFileName(sourceLink)), L"%y", L"\n" + fmtFileName(targetLink)) + - L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot create symbolic link %x."), L"%x", fmtFileName(targetLink)) + L"\n\n" + getLastErrorFormatted()); //allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist zen::ScopeGuard guardNewDir = zen::makeGuard([&] diff --git a/zen/file_handling.h b/zen/file_handling.h index a0bd9b5b..c18a68ac 100644 --- a/zen/file_handling.h +++ b/zen/file_handling.h @@ -33,6 +33,14 @@ enum ResponseSame }; ResponseSame onSameVolume(const Zstring& folderLeft, const Zstring& folderRight); //throw() +enum SymLinkType +{ + SYMLINK_TYPE_DIR, //Windows: may be broken + SYMLINK_TYPE_FILE, //Windows: may be broken + SYMLINK_TYPE_UNKNOWN, //Windows: unable to determine type; Linux: broken Symlink +}; +SymLinkType getSymlinkType(const Zstring& linkname); //throw() + enum ProcSymlink { SYMLINK_DIRECT, diff --git a/zen/file_io.cpp b/zen/file_io.cpp index 4961cac9..cda48e36 100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -168,7 +168,7 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number const size_t bytesRead = ::fread(buffer, 1, bytesToRead, fileHandle); if (::ferror(fileHandle) != 0) //checks status of stream, not fread()! #endif - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (read)"); + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (ReadFile)"); #ifdef FFS_WIN if (bytesRead < bytesToRead) //verify only! @@ -227,7 +227,7 @@ FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw Fil const DWORD attrib = ::GetFileAttributes(applyLongPathPrefix(filename).c_str()); if (attrib != INVALID_FILE_ATTRIBUTES) { - fileHandle = getHandle(attrib); //retry + fileHandle = getHandle(attrib); //retry: alas this may still fail for hidden file, e.g. accessing shared folder in XP as Virtual Box guest! lastError = ::GetLastError(); } } @@ -301,7 +301,7 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro const size_t bytesWritten = ::fwrite(buffer, 1, bytesToWrite, fileHandle); if (::ferror(fileHandle) != 0) //checks status of stream, not fwrite()! #endif - throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (w)"); //w -> distinguish from fopen error message! + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (WriteFile)"); if (bytesWritten != bytesToWrite) //must be fulfilled for synchronous writes! throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + L"Incomplete write!"); @@ -316,7 +316,7 @@ FileInputUnbuffered::FileInputUnbuffered(const Zstring& filename) : FileInputBas checkForUnsupportedType(filename); //throw FileError; reading a named pipe would block forever! fdFile = ::open(filename.c_str(), O_RDONLY); - if (fdFile < 0) + if (fdFile == -1) { const ErrorCode lastError = getLastError(); @@ -361,7 +361,7 @@ FileOutputUnbuffered::FileOutputUnbuffered(const Zstring& filename, mode_t mode) //overwrite is: O_CREAT | O_WRONLY | O_TRUNC fdFile = ::open(filename.c_str(), O_CREAT | O_WRONLY | O_EXCL, mode & (S_IRWXU | S_IRWXG | S_IRWXO)); - if (fdFile < 0) + if (fdFile == -1) { const int lastError = errno; const std::wstring errorMessage = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename)) + L"\n\n" + zen::getLastErrorFormatted(lastError); @@ -375,6 +375,7 @@ FileOutputUnbuffered::FileOutputUnbuffered(const Zstring& filename, mode_t mode) } } +FileOutputUnbuffered::FileOutputUnbuffered(int fd, const Zstring& filename) : FileOutputBase(filename), fdFile(fd) {} FileOutputUnbuffered::~FileOutputUnbuffered() { ::close(fdFile); } @@ -395,7 +396,7 @@ void FileOutputUnbuffered::write(const void* buffer, size_t bytesToWrite) //thro if (bytesWritten == 0) //comment in safe-read.c suggests to treat this as an error due to buggy drivers errno = ENOSPC; - throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (w)"); + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (write)"); } if (bytesWritten > static_cast<ssize_t>(bytesToWrite)) //better safe than sorry throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + L"buffer overflow"); diff --git a/zen/file_io.h b/zen/file_io.h index 8e501172..407109f7 100644 --- a/zen/file_io.h +++ b/zen/file_io.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FILEIO_H_INCLUDED -#define FILEIO_H_INCLUDED +#ifndef FILEIO_89578342758342572345 +#define FILEIO_89578342758342572345 #include "file_io_base.h" #include "file_error.h" @@ -73,7 +73,7 @@ public: //considering safe-read.c it seems buffer size should be a multiple of 8192 virtual size_t read(void* buffer, size_t bytesToRead); //throw FileError; returns actual number of bytes read - //we should not rely on buffer being filled completely! + //do NOT rely on partially filled buffer meaning EOF! int getDescriptor() { return fdFile;} @@ -86,6 +86,7 @@ class FileOutputUnbuffered : public FileOutputBase public: //creates a new file (no overwrite allowed!) FileOutputUnbuffered(const Zstring& filename, mode_t mode); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting + FileOutputUnbuffered(int fd, const Zstring& filename); //takes ownership! ~FileOutputUnbuffered(); virtual void write(const void* buffer, size_t bytesToWrite); //throw FileError @@ -97,4 +98,4 @@ private: #endif } -#endif // FILEIO_H_INCLUDED +#endif //FILEIO_89578342758342572345 diff --git a/zen/file_io_base.h b/zen/file_io_base.h index f26cd8c2..8e9bf12e 100644 --- a/zen/file_io_base.h +++ b/zen/file_io_base.h @@ -21,8 +21,8 @@ protected: ~FileBase() {} private: - FileBase(const FileBase&); - FileBase& operator=(const FileBase&); + FileBase(const FileBase&); //=delete + FileBase& operator=(const FileBase&); // const Zstring filename_; }; diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index 84bd6c64..53049c21 100644 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -34,7 +34,7 @@ namespace //implement "retry" in a generic way: template <class Command> inline //function object expecting to throw FileError if operation fails -bool tryReportingError(Command cmd, zen::TraverseCallback& callback) //return "true" on success, "false" if error was ignored +bool tryReportingDirError(Command cmd, zen::TraverseCallback& callback) //return "true" on success, "false" if error was ignored { for (;;) try @@ -44,23 +44,40 @@ bool tryReportingError(Command cmd, zen::TraverseCallback& callback) //return "t } catch (const FileError& e) { - switch (callback.onError(e.toString())) + switch (callback.reportDirError(e.toString())) + { + case TraverseCallback::ON_ERROR_RETRY: + break; + case TraverseCallback::ON_ERROR_IGNORE: + return false; + } + } +} + +template <class Command> inline //function object expecting to throw FileError if operation fails +bool tryReportingItemError(Command cmd, zen::TraverseCallback& callback, const Zchar* shortName) //return "true" on success, "false" if error was ignored +{ + for (;;) + try + { + cmd(); //throw FileError + return true; + } + catch (const FileError& e) + { + switch (callback.reportItemError(e.toString(), shortName)) { case TraverseCallback::ON_ERROR_RETRY: break; case TraverseCallback::ON_ERROR_IGNORE: return false; - //default: - // assert(false); - //break; } } } #ifdef FFS_WIN -inline -bool getTargetInfoFromSymlink(const Zstring& linkName, zen::TraverseCallback::FileInfo& output) +void getInfoFromFileSymlink(const Zstring& linkName, zen::TraverseCallback::FileInfo& output) //throw FileError { //open handle to target of symbolic link HANDLE hFile = ::CreateFile(zen::applyLongPathPrefix(linkName).c_str(), @@ -68,21 +85,27 @@ bool getTargetInfoFromSymlink(const Zstring& linkName, zen::TraverseCallback::Fi FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory + FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory -> keep it even if we expect to open a file! See comment below nullptr); if (hFile == INVALID_HANDLE_VALUE) - return false; + throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)) + L"\n\n" + getLastErrorFormatted() + L" (CreateFile)"); ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); - BY_HANDLE_FILE_INFORMATION fileInfoByHandle = {}; - if (!::GetFileInformationByHandle(hFile, &fileInfoByHandle)) - return false; + BY_HANDLE_FILE_INFORMATION fileInfo = {}; + if (!::GetFileInformationByHandle(hFile, &fileInfo)) + throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)) + L"\n\n" + getLastErrorFormatted() + L" (GetFileInformationByHandle)"); + + //a file symlink may incorrectly point to a directory, but both CreateFile() and GetFileInformationByHandle() will succeed and return garbage! + //- if we did not use FILE_FLAG_BACKUP_SEMANTICS above, CreateFile() would error out with an even less helpful ERROR_ACCESS_DENIED! + //- reinterpreting the link as a directory symlink would still fail during traversal, so just show an error here + //- OTOH a directory symlink that points to a file fails immediately in ::FindFirstFile() with ERROR_DIRECTORY! -> nothing to do in this case + if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)) + L"\n\n" + getLastErrorFormatted(ERROR_FILE_INVALID) + L" (GetFileInformationByHandle)"); //write output - output.fileSize = UInt64(fileInfoByHandle.nFileSizeLow, fileInfoByHandle.nFileSizeHigh); - output.lastWriteTimeRaw = toTimeT(fileInfoByHandle.ftLastWriteTime); - output.id = FileId(); //= extractFileID(fileInfoByHandle); -> id from dereferenced symlink is problematic, since renaming will consider the link, not the target! - return true; + output.fileSize = UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); + output.lastWriteTime = toTimeT(fileInfo.ftLastWriteTime); + output.id = extractFileID(fileInfo); //consider detection of moved files: allow for duplicate file ids, renaming affects symlink, not target, ... } @@ -92,8 +115,7 @@ DWORD retrieveVolumeSerial(const Zstring& pathName) //returns 0 on error or if s //- root paths "C:\", "D:\" //- network shares: \\share\dirname //- indirection: subst S: %USERPROFILE% - // -> GetVolumePathName() on the other hand resolves "S:\Desktop\somedir" to "S:\Desktop\" - nice try... - + // -> GetVolumePathName()+GetVolumeInformation() OTOH incorrectly resolves "S:\Desktop\somedir" to "S:\Desktop\" - nice try... const HANDLE hDir = ::CreateFile(zen::applyLongPathPrefix(pathName).c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, @@ -113,43 +135,12 @@ DWORD retrieveVolumeSerial(const Zstring& pathName) //returns 0 on error or if s } -/* -DWORD retrieveVolumeSerial(const Zstring& pathName) //returns 0 on error! -{ - //note: this works for network shares: \\share\dirname, but not "subst"! - - const DWORD BUFFER_SIZE = 10000; - std::vector<wchar_t> buffer(BUFFER_SIZE); - - //full pathName need not yet exist! - if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName, - &buffer[0], //__out LPTSTR lpszVolumePathName, - BUFFER_SIZE)) //__in DWORD cchBufferLength - return 0; - - Zstring volumePath = appendSeparator(&buffer[0]); - - DWORD volumeSerial = 0; - if (!::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName, - nullptr, //__out LPTSTR lpVolumeNameBuffer, - 0, //__in DWORD nVolumeNameSize, - &volumeSerial, //__out_opt LPDWORD lpVolumeSerialNumber, - nullptr, //__out_opt LPDWORD lpMaximumComponentLength, - nullptr, //__out_opt LPDWORD lpFileSystemFlags, - nullptr, //__out LPTSTR lpFileSystemNameBuffer, - 0)) //__in DWORD nFileSystemNameSize - return 0; - - return volumeSerial; -} -*/ - - const bool isXpOrLater = winXpOrLater(); //VS2010 compiled DLLs are not supported on Win 2000: Popup dialog "DecodePointer not found" -const auto openDir = isXpOrLater ? DllFun<findplus::FunType_openDir >(findplus::getDllName(), findplus::funName_openDir ) : DllFun<findplus::FunType_openDir >(); // -const auto readDir = isXpOrLater ? DllFun<findplus::FunType_readDir >(findplus::getDllName(), findplus::funName_readDir ) : DllFun<findplus::FunType_readDir >(); //load at startup: avoid pre C++11 static initialization MT issues -const auto closeDir= isXpOrLater ? DllFun<findplus::FunType_closeDir>(findplus::getDllName(), findplus::funName_closeDir) : DllFun<findplus::FunType_closeDir>(); // +#define DEF_DLL_FUN(name) const auto name = isXpOrLater ? DllFun<findplus::FunType_##name>(findplus::getDllName(), findplus::funName_##name) : DllFun<findplus::FunType_##name>(); +DEF_DLL_FUN(openDir); // +DEF_DLL_FUN(readDir); //load at startup: avoid pre C++11 static initialization MT issues +DEF_DLL_FUN(closeDir); // /* Common C-style interface for Win32 FindFirstFile(), FindNextFile() and FileFilePlus openDir(), closeDir(): @@ -192,11 +183,11 @@ struct Win32Traverser typedef WIN32_FIND_DATA FindData; - static void create(const Zstring& directory, DirHandle& hnd) //throw FileError + static void create(const Zstring& dirname, DirHandle& hnd) //throw FileError { - const Zstring& directoryPf = appendSeparator(directory); + const Zstring& dirnamePf = appendSeparator(dirname); - hnd.searchHandle = ::FindFirstFile(applyLongPathPrefix(directoryPf + L'*').c_str(), &hnd.data); + hnd.searchHandle = ::FindFirstFile(applyLongPathPrefix(dirnamePf + L'*').c_str(), &hnd.data); //no noticable performance difference compared to FindFirstFileEx with FindExInfoBasic, FIND_FIRST_EX_CASE_SENSITIVE and/or FIND_FIRST_EX_LARGE_FETCH if (hnd.searchHandle == INVALID_HANDLE_VALUE) { @@ -205,17 +196,17 @@ struct Win32Traverser { //1. directory may not exist *or* 2. it is completely empty: not all directories contain "., .." entries, e.g. a drive's root directory; NetDrive // -> FindFirstFile() is a nice example of violation of API design principle of single responsibility - if (dirExists(directory)) //yes, a race-condition, still the best we can do + if (dirExists(dirname)) //yes, a race-condition, still the best we can do return; } - throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted()); } } static void destroy(const DirHandle& hnd) { ::FindClose(hnd.searchHandle); } //throw() template <class FallbackFun> - static bool getEntry(DirHandle& hnd, const Zstring& directory, FindData& fileInfo, FallbackFun) //throw FileError + static bool getEntry(DirHandle& hnd, const Zstring& dirname, FindData& fileInfo, FallbackFun) //throw FileError { if (hnd.searchHandle == INVALID_HANDLE_VALUE) //handle special case of "truly empty directories" return false; @@ -232,15 +223,15 @@ struct Win32Traverser if (::GetLastError() == ERROR_NO_MORE_FILES) //not an error situation return false; //else we have a problem... report it: - throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted()); } return true; } static void extractFileInfo(const FindData& fileInfo, DWORD volumeSerial, TraverseCallback::FileInfo& output) { - output.fileSize = UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); - output.lastWriteTimeRaw = getModTime(fileInfo); + output.fileSize = UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); + output.lastWriteTime = getModTime(fileInfo); output.id = FileId(); } @@ -264,17 +255,17 @@ struct FilePlusTraverser typedef findplus::FileInformation FindData; - static void create(const Zstring& directory, DirHandle& hnd) //throw FileError + static void create(const Zstring& dirname, DirHandle& hnd) //throw FileError { - hnd.searchHandle = ::openDir(applyLongPathPrefix(directory).c_str()); + hnd.searchHandle = ::openDir(applyLongPathPrefix(dirname).c_str()); if (hnd.searchHandle == nullptr) - throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted() + L" (+)"); + throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted() + L" (+)"); } static void destroy(DirHandle hnd) { ::closeDir(hnd.searchHandle); } //throw() template <class FallbackFun> - static bool getEntry(DirHandle hnd, const Zstring& directory, FindData& fileInfo, FallbackFun fb) //throw FileError + static bool getEntry(DirHandle hnd, const Zstring& dirname, FindData& fileInfo, FallbackFun fb) //throw FileError { if (!::readDir(hnd.searchHandle, fileInfo)) { @@ -293,7 +284,7 @@ struct FilePlusTraverser } //else we have a problem... report it: - throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted(lastError) + L" (+)"); + throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted(lastError) + L" (+)"); } return true; @@ -301,9 +292,9 @@ struct FilePlusTraverser static void extractFileInfo(const FindData& fileInfo, DWORD volumeSerial, TraverseCallback::FileInfo& output) { - output.fileSize = fileInfo.fileSize.QuadPart; - output.lastWriteTimeRaw = getModTime(fileInfo); - output.id = extractFileID(volumeSerial, fileInfo.fileId); + output.fileSize = fileInfo.fileSize.QuadPart; + output.lastWriteTime = getModTime(fileInfo); + output.id = extractFileID(volumeSerial, fileInfo.fileId); } static Int64 getModTime (const FindData& fileInfo) { return toTimeT(fileInfo.lastWriteTime); } @@ -319,9 +310,10 @@ class DirTraverser { public: DirTraverser(const Zstring& baseDirectory, TraverseCallback& sink, DstHackCallback* dstCallback) : - isFatFileSystem(dst::isFatDrive(baseDirectory)), - volumeSerial(retrieveVolumeSerial(baseDirectory)) //return 0 on error + isFatFileSystem(dst::isFatDrive(baseDirectory)) { + warn_static("ineffizient, wenn kein dst hack/file ids gebraucht werden???") + try //traversing certain folders with restricted permissions requires this privilege! (but copying these files may still fail) { activatePrivilege(SE_BACKUP_NAME); //throw FileError @@ -329,7 +321,7 @@ public: catch (FileError&) {} //don't cause issues in user mode if (::openDir && ::readDir && ::closeDir) - traverse<FilePlusTraverser>(baseDirectory, sink, 0); + traverse<FilePlusTraverser>(baseDirectory, sink, retrieveVolumeSerial(baseDirectory)); //retrieveVolumeSerial returns 0 on error else //fallback traverse<Win32Traverser>(baseDirectory, sink, 0); @@ -343,32 +335,28 @@ private: DirTraverser& operator=(const DirTraverser&); template <class Trav> - void traverse(const Zstring& directory, zen::TraverseCallback& sink, int level) + void traverse(const Zstring& dirname, zen::TraverseCallback& sink, DWORD volumeSerial /*may be 0!*/) { - tryReportingError([&] - { - if (level == 100) //notify endless recursion - throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + _("Detected endless directory recursion.")); - }, sink); + //no need to check for endless recursion: Windows seems to have an internal path limit of about 700 chars typename Trav::DirHandle searchHandle; - if (!tryReportingError([&] + if (!tryReportingDirError([&] { typedef Trav Trav; //f u VS! - Trav::create(directory, searchHandle); //throw FileError + Trav::create(dirname, searchHandle); //throw FileError }, sink)) return; //ignored error ZEN_ON_SCOPE_EXIT(typedef Trav Trav; Trav::destroy(searchHandle)); typename Trav::FindData findData = {}; - auto fallback = [&] { this->traverse<Win32Traverser>(directory, sink, level); }; //help VS2010 a little by avoiding too deeply nested lambdas + auto fallback = [&] { this->traverse<Win32Traverser>(dirname, sink, volumeSerial); }; //help VS2010 a little by avoiding too deeply nested lambdas for (;;) { bool gotEntry = false; - tryReportingError([&] { typedef Trav Trav; /*VS 2010 bug*/ gotEntry = Trav::getEntry(searchHandle, directory, findData, fallback); }, sink); //throw FileError + tryReportingDirError([&] { typedef Trav Trav; /*VS 2010 bug*/ gotEntry = Trav::getEntry(searchHandle, dirname, findData, fallback); }, sink); //throw FileError if (!gotEntry) //no more items or ignored error return; @@ -378,18 +366,12 @@ private: (shortName[1] == 0 || (shortName[1] == L'.' && shortName[2] == 0))) continue; - const Zstring& fullName = appendSeparator(directory) + shortName; + const Zstring& fullName = appendSeparator(dirname) + shortName; if (Trav::isSymlink(findData)) //check first! { TraverseCallback::SymlinkInfo linkInfo; - try - { - linkInfo.targetPath = getSymlinkRawTargetString(fullName); //throw FileError - } - catch (FileError&) { assert(false); } - linkInfo.lastWriteTimeRaw = Trav::getModTime (findData); - linkInfo.dirLink = Trav::isDirectory(findData); //directory symlinks have this flag on Windows + linkInfo.lastWriteTime = Trav::getModTime (findData); switch (sink.onSymlink(shortName, fullName, linkInfo)) { @@ -397,21 +379,21 @@ private: if (Trav::isDirectory(findData)) { if (const std::shared_ptr<TraverseCallback>& rv = sink.onDir(shortName, fullName)) - traverse<Trav>(fullName, *rv, level + 1); + traverse<Trav>(fullName, *rv, retrieveVolumeSerial(fullName)); //symlink may link to different volume => redetermine volume serial! } else //a file { TraverseCallback::FileInfo targetInfo; - const bool validLink = tryReportingError([&] //try to resolve symlink (and report error on failure!!!) + const bool validLink = tryReportingItemError([&] //try to resolve symlink (and report error on failure!!!) { - if (!getTargetInfoFromSymlink(fullName, targetInfo)) - throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(fullName)) + L"\n\n" + getLastErrorFormatted()); - }, sink); + getInfoFromFileSymlink(fullName, targetInfo); //throw FileError + }, sink, shortName); if (validLink) sink.onFile(shortName, fullName, targetInfo); - else //broken symlink - sink.onFile(shortName, fullName, TraverseCallback::FileInfo()); + // else //broken symlink + // sink.onFile(shortName, fullName, TraverseCallback::FileInfo()); + warn_static("impact of ignored broken file/incomplete dir read on two-way variant!?") } break; @@ -422,7 +404,7 @@ private: else if (Trav::isDirectory(findData)) { if (const std::shared_ptr<TraverseCallback>& rv = sink.onDir(shortName, fullName)) - traverse<Trav>(fullName, *rv, level + 1); + traverse<Trav>(fullName, *rv, volumeSerial); } else //a file { @@ -435,7 +417,7 @@ private: const dst::RawTime rawTime(Trav::getCreateTimeRaw(findData), Trav::getModTimeRaw(findData)); if (dst::fatHasUtcEncoded(rawTime)) //throw std::runtime_error - fileInfo.lastWriteTimeRaw = toTimeT(dst::fatDecodeUtcTime(rawTime)); //return real UTC time; throw (std::runtime_error) + fileInfo.lastWriteTime = toTimeT(dst::fatDecodeUtcTime(rawTime)); //return real UTC time; throw (std::runtime_error) else markForDstHack.push_back(std::make_pair(fullName, toTimeT(rawTime.writeTimeRaw))); } @@ -498,8 +480,6 @@ private: typedef std::vector<std::pair<Zstring, Int64> > FilenameTimeList; FilenameTimeList markForDstHack; //####################################### DST hack ########################################### - - const DWORD volumeSerial; //may be 0! }; #elif defined FFS_LINUX || defined FFS_MAC @@ -521,28 +501,23 @@ public: const size_t maxPath = std::max<long>(::pathconf(directoryFormatted.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return long(-1) buffer.resize(offsetof(struct ::dirent, d_name) + maxPath + 1); - traverse(directoryFormatted, sink, 0); + traverse(directoryFormatted, sink); } private: DirTraverser(const DirTraverser&); DirTraverser& operator=(const DirTraverser&); - void traverse(const Zstring& directory, zen::TraverseCallback& sink, int level) + void traverse(const Zstring& dirname, zen::TraverseCallback& sink) { - tryReportingError([&] - { - if (level == 100) //notify endless recursion - throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + _("Detected endless directory recursion.")); - }, sink); - + //no need to check for endless recursion: Linux has a fixed limit on the number of symbolic links in a path DIR* dirObj = nullptr; - if (!tryReportingError([&] + if (!tryReportingDirError([&] { - dirObj = ::opendir(directory.c_str()); //directory must NOT end with path separator, except "/" + dirObj = ::opendir(dirname.c_str()); //directory must NOT end with path separator, except "/" if (!dirObj) - throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted()); }, sink)) return; //ignored error ZEN_ON_SCOPE_EXIT(::closedir(dirObj)); //never close nullptr handles! -> crash @@ -550,10 +525,10 @@ private: for (;;) { struct ::dirent* dirEntry = nullptr; - tryReportingError([&] + tryReportingDirError([&] { if (::readdir_r(dirObj, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0) - throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted()); + throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted()); }, sink); if (!dirEntry) //no more items or ignored error return; @@ -583,61 +558,53 @@ private: //const char* sampleDecomposed = "\x6f\xcc\x81.txt"; //const char* samplePrecomposed = "\xc3\xb3.txt"; #endif - const Zstring& fullName = appendSeparator(directory) + shortName; + const Zstring& fullName = appendSeparator(dirname) + shortName; struct ::stat statData = {}; - if (!tryReportingError([&] + if (!tryReportingItemError([&] { if (::lstat(fullName.c_str(), &statData) != 0) //lstat() does not resolve symlinks throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(fullName)) + L"\n\n" + getLastErrorFormatted()); - }, sink)) + }, sink, shortName)) continue; //ignore error: skip file if (S_ISLNK(statData.st_mode)) //on Linux there is no distinction between file and directory symlinks! { - struct ::stat statDataTrg = {}; - bool validLink = ::stat(fullName.c_str(), &statDataTrg) == 0; //if "LINK_SKIP", a broken link is no error! - TraverseCallback::SymlinkInfo linkInfo; - try - { - linkInfo.targetPath = getSymlinkRawTargetString(fullName); //throw FileError - } - catch (FileError&) { assert(false); } - linkInfo.lastWriteTimeRaw = statData.st_mtime; //UTC time (ANSI C format); unit: 1 second - linkInfo.dirLink = validLink && S_ISDIR(statDataTrg.st_mode); - //S_ISDIR and S_ISLNK are mutually exclusive on Linux => explicitly need to follow link + linkInfo.lastWriteTime = statData.st_mtime; //UTC time (ANSI C format); unit: 1 second switch (sink.onSymlink(shortName, fullName, linkInfo)) { case TraverseCallback::LINK_FOLLOW: + { //try to resolve symlink (and report error on failure!!!) - validLink = tryReportingError([&] + struct ::stat statDataTrg = {}; + bool validLink = tryReportingItemError([&] { - if (validLink) return; //no need to check twice if (::stat(fullName.c_str(), &statDataTrg) != 0) throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(fullName)) + L"\n\n" + getLastErrorFormatted()); - }, sink); + }, sink, shortName); if (validLink) { if (S_ISDIR(statDataTrg.st_mode)) //a directory { if (const std::shared_ptr<TraverseCallback>& rv = sink.onDir(shortName, fullName)) - traverse(fullName, *rv, level + 1); + traverse(fullName, *rv); } else //a file or named pipe, ect. { TraverseCallback::FileInfo fileInfo; - fileInfo.fileSize = zen::UInt64(statDataTrg.st_size); - fileInfo.lastWriteTimeRaw = statDataTrg.st_mtime; //UTC time (time_t format); unit: 1 second - //fileInfo.id = extractFileID(statDataTrg); -> id from dereferenced symlink is problematic, since renaming would consider the link, not the target! + fileInfo.fileSize = zen::UInt64(statDataTrg.st_size); + fileInfo.lastWriteTime = statDataTrg.st_mtime; //UTC time (time_t format); unit: 1 second + fileInfo.id = extractFileID(statDataTrg); sink.onFile(shortName, fullName, fileInfo); } } - else //report broken symlink as file! - sink.onFile(shortName, fullName, TraverseCallback::FileInfo()); - break; + // else //report broken symlink as file! + // sink.onFile(shortName, fullName, TraverseCallback::FileInfo()); + } + break; case TraverseCallback::LINK_SKIP: break; @@ -646,14 +613,14 @@ private: else if (S_ISDIR(statData.st_mode)) //a directory { if (const std::shared_ptr<TraverseCallback>& rv = sink.onDir(shortName, fullName)) - traverse(fullName, *rv, level + 1); + traverse(fullName, *rv); } else //a file or named pipe, ect. { TraverseCallback::FileInfo fileInfo; - fileInfo.fileSize = zen::UInt64(statData.st_size); - fileInfo.lastWriteTimeRaw = statData.st_mtime; //UTC time (time_t format); unit: 1 second - fileInfo.id = extractFileID(statData); + fileInfo.fileSize = zen::UInt64(statData.st_size); + fileInfo.lastWriteTime = statData.st_mtime; //UTC time (time_t format); unit: 1 second + fileInfo.id = extractFileID(statData); sink.onFile(shortName, fullName, fileInfo); } @@ -676,7 +643,7 @@ private: } -void zen::traverseFolder(const Zstring& directory, TraverseCallback& sink, DstHackCallback* dstCallback) +void zen::traverseFolder(const Zstring& dirname, TraverseCallback& sink, DstHackCallback* dstCallback) { - DirTraverser(directory, sink, dstCallback); + DirTraverser(dirname, sink, dstCallback); } diff --git a/zen/file_traverser.h b/zen/file_traverser.h index 97fb0e9f..13b76966 100644 --- a/zen/file_traverser.h +++ b/zen/file_traverser.h @@ -22,17 +22,15 @@ struct TraverseCallback struct FileInfo { - UInt64 fileSize; //unit: bytes! - Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC - FileId id; //optional: initial if not supported! + UInt64 fileSize; //unit: bytes! + Int64 lastWriteTime; //number of seconds since Jan. 1st 1970 UTC + FileId id; //optional: initial if not supported! //std::unique_ptr<SymlinkInfo> symlinkInfo; //only filled if file is dereferenced symlink }; struct SymlinkInfo { - Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC - Zstring targetPath; //optional: empty if something goes wrong - bool dirLink; //"true": point to dir; "false": point to file (or broken Link on Linux) + Int64 lastWriteTime; //number of seconds since Jan. 1st 1970 UTC }; enum HandleLink @@ -51,7 +49,8 @@ struct TraverseCallback /**/ onDir (const Zchar* shortName, const Zstring& fullName) = 0; virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details) = 0; virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) = 0; - virtual HandleError onError (const std::wstring& msg) = 0; + virtual HandleError reportDirError (const std::wstring& msg) = 0; //failed directory traversal -> consider directory data as incomplete! + virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) = 0; //failed to get single file/dir/symlink only! }; @@ -68,7 +67,7 @@ struct DstHackCallback; //DST hack not required on Unix //custom traverser with detail information about files //Win: client needs to handle duplicate file notifications! (FilePlusTraverser fallback) //directory may end with PATH_SEPARATOR -void traverseFolder(const Zstring& directory, //throw() +void traverseFolder(const Zstring& dirname, //throw() TraverseCallback& sink, DstHackCallback* dstCallback = nullptr); //apply DST hack if callback is supplied } diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp index 7cc43e6b..60eb6869 100644 --- a/zen/format_unit.cpp +++ b/zen/format_unit.cpp @@ -39,7 +39,7 @@ std::wstring zen::filesizeToShortString(Int64 size) wchar_t buffer[50]; #ifdef __MINGW32__ - int charsWritten = ::_snwprintf(buffer, 50, L"%.*f", precisionDigits, sizeInUnit); //MinGW does not comply to the C standard here + int charsWritten = ::_snwprintf(buffer, 50, L"%.*f", precisionDigits, sizeInUnit); //MinGW does not comply to the C standard here #else int charsWritten = std::swprintf(buffer, 50, L"%.*f", precisionDigits, sizeInUnit); #endif @@ -148,7 +148,6 @@ std::wstring zen::remainingTimeToString(double timeInSec) std::wstring zen::fractionToString(double fraction) { - //return replaceCpy(_("%x%"), L"%x", printNumber<std::wstring>(L"%3.2f", fraction * 100.0), false); return printNumber<std::wstring>(L"%3.2f", fraction * 100.0) + L'%'; //no need to internationalize fraction!? } diff --git a/zen/int64.h b/zen/int64.h index a760d5ab..4c63a24f 100644 --- a/zen/int64.h +++ b/zen/int64.h @@ -14,7 +14,6 @@ #include <ostream> #include "assert_static.h" #include "type_tools.h" -#include "type_traits.h" #ifdef FFS_WIN #include "win.h" diff --git a/zen/long_path_prefix.h b/zen/long_path_prefix.h index c3d705e2..db1fbdca 100644 --- a/zen/long_path_prefix.h +++ b/zen/long_path_prefix.h @@ -58,8 +58,8 @@ Zstring applyLongPathPrefixImpl(const Zstring& path) assert(!zen::isWhiteSpace(path[0])); if (path.length() >= maxPath || //maximum allowed path length without prefix is (MAX_PATH - 1) - endsWith(path, L' ') || //by default all Win32 APIs trim trailing spaces and period, unless long path prefix is supplied! - endsWith(path, L'.')) // + endsWith(path, L' ') || //by default all Win32 APIs trim trailing spaces and period, unless long path prefix is supplied! + endsWith(path, L'.')) //note: adding long path prefix might screw up relative paths "." and ".."! if (!startsWith(path, LONG_PATH_PREFIX)) { if (startsWith(path, L"\\\\")) //UNC-name, e.g. \\zenju-pc\Users diff --git a/zen/serialize.h b/zen/serialize.h index a9238359..982165f9 100644 --- a/zen/serialize.h +++ b/zen/serialize.h @@ -86,6 +86,7 @@ struct BinStreamIn //throw UnexpectedEndOfStreamError const void* requestRead(size_t len) //throw UnexpectedEndOfStreamError { + if (len == 0) return nullptr; //don't allow for possibility to access empty buffer if (pos + len > buffer.size()) throw UnexpectedEndOfStreamError(); size_t oldPos = pos; @@ -102,8 +103,9 @@ struct BinStreamOut { void* requestWrite(size_t len) { - size_t oldSize = buffer.size(); - buffer.resize(buffer.size() + len); + if (len == 0) return nullptr; //don't allow for possibility to access empty buffer + const size_t oldSize = buffer.size(); + buffer.resize(oldSize + len); return &*buffer.begin() + oldSize; } diff --git a/zen/stl_tools.h b/zen/stl_tools.h index 07fc31d7..b394b128 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -18,7 +18,6 @@ #endif - //enhancements for <algorithm> namespace zen { @@ -47,6 +46,10 @@ template <class BidirectionalIterator1, class BidirectionalIterator2> BidirectionalIterator1 search_last(BidirectionalIterator1 first1, BidirectionalIterator1 last1, BidirectionalIterator2 first2, BidirectionalIterator2 last2); +template <class InputIterator1, class InputIterator2> +bool equal(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2); + //hash container: proper name + mitigate MSVC performance bug template <class T> class hash_set; template <class K, class V> class hash_map; @@ -166,6 +169,14 @@ BidirectionalIterator1 search_last(const BidirectionalIterator1 first1, Bidirect } +template <class InputIterator1, class InputIterator2> inline +bool equal(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2) +{ + return last1 - first1 == last2 - first2 && std::equal(first1, last1, first2); +} + + #if defined _MSC_VER && _MSC_VER <= 1600 //VS2010 performance bug in std::unordered_set<>: http://drdobbs.com/blogs/cpp/232200410 -> should be fixed in VS11 template <class T> class hash_set : public std::set<T> {}; template <class K, class V> class hash_map : public std::map<K, V> {}; diff --git a/zen/string_base.h b/zen/string_base.h index e38fab94..591ed62b 100644 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -13,7 +13,7 @@ #include "string_tools.h" #include <boost/detail/atomic_count.hpp> -//Zbase - a policy based string class optimizing performance and genericity +//Zbase - a policy based string class optimizing performance and flexibility namespace zen { @@ -183,7 +183,7 @@ private: //################################################################################################################################################################ -//perf note: interstingly StorageDeepCopy and StorageRefCountThreadSafe show same performance in FFS comparison +//perf note: interestingly StorageDeepCopy and StorageRefCountThreadSafe show same performance in FFS comparison template <class Char, //Character Type template <class, class> class SP = StorageRefCountThreadSafe, //Storage Policy @@ -231,7 +231,7 @@ public: size_t find (Char ch, size_t pos = 0) const; //returns "npos" if not found size_t rfind(Char ch, size_t pos = npos) const; // size_t rfind(const Char* str, size_t pos = npos) const; // - Zbase& replace(size_t pos1, size_t n1, const Zbase& str); + //Zbase& replace(size_t pos1, size_t n1, const Zbase& str); void reserve(size_t minCapacity); Zbase& assign(const Char* source, size_t len); Zbase& append(const Char* source, size_t len); @@ -361,11 +361,11 @@ Zbase<Char, SP, AP>::Zbase(const Zbase<Char, SP, AP>& source) template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP>::Zbase(Zbase<Char, SP, AP>&& tmp) { - //rawStr = this->clone(tmp.rawStr); NO! do not increment ref-count of a potentially unshared string! We'd lose optimization opportunity of reusing it! - //instead create a dummy string and swap: - if (this->canWrite(tmp.rawStr, 0)) //perf: this check saves about 4% + if (this->canWrite(tmp.rawStr, 0)) //perf: following optimization saves about 4% { - rawStr = this->create(0); //no perf issue! see comment in default constructor + //do not increment ref-count of an unshared string! We'd lose optimization opportunity of reusing its memory! + //instead create a dummy string and swap: + rawStr = this->create(0); //no perf issue! see comment in default constructor rawStr[0] = 0; swap(tmp); } @@ -450,6 +450,7 @@ size_t Zbase<Char, SP, AP>::rfind(const Char* str, size_t pos) const } +/* -> dead code ahead: better use zen::replace template instead! template <class Char, template <class, class> class SP, class AP> Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::replace(size_t pos1, size_t n1, const Zbase& str) { @@ -493,7 +494,7 @@ Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::replace(size_t pos1, size_t n1, const } return *this; } - +*/ template <class Char, template <class, class> class SP, class AP> inline void Zbase<Char, SP, AP>::resize(size_t newSize, Char fillChar) diff --git a/zen/string_traits.h b/zen/string_traits.h index 93e8c510..22aa2ffc 100644 --- a/zen/string_traits.h +++ b/zen/string_traits.h @@ -23,31 +23,31 @@ GetCharType<>::Type: GetCharType<std::wstring>::Type //equals wchar_t GetCharType<wchar_t[5]> ::Type //equals wchar_t -strBegin(): +strLength(): + strLength(str); //equals str.length() + strLength(array); //equals cStringLength(array) + +strBegin(): -> not null-terminated! -> may be nullptr if length is 0! std::wstring str(L"dummy"); char array[] = "dummy"; strBegin(str); //returns str.c_str() strBegin(array); //returns array - -strLength(): - strLength(str); //equals str.length() - strLength(array); //equals cStringLength(array) */ -//reference a sub-string or a char* as an intermediate string class when the length is already known +//reference a sub-string for consumption by zen string_tools template <class Char> -class StringProxy +class StringRef { public: - StringProxy(const Char* cstr, size_t len ) : cstr_(cstr), length_(len) {} - StringProxy(const Char* cstrBegin, const Char* cstrEnd) : cstr_(cstrBegin), length_(cstrEnd - cstrBegin) {} + template <class Iterator> + StringRef(Iterator first, Iterator last) : length_(last - first), data_(first != last ? &*first : nullptr) {} - const Char* c_str() const { return cstr_; } size_t length() const { return length_; } + const Char* data() const { return data_; } //1. no null-termination! 2. may be nullptr! private: - const Char* cstr_; size_t length_; + const Char* data_; }; @@ -61,7 +61,6 @@ private: - //---------------------- implementation ---------------------- namespace implementation { @@ -129,6 +128,28 @@ public: IsSameType<CharType, wchar_t>::value }; }; + + +template <> class StringTraits<StringRef<char>> +{ +public: + enum + { + isStringClass = false, + isStringLike = true + }; + typedef char CharType; +}; +template <> class StringTraits<StringRef<wchar_t>> +{ +public: + enum + { + isStringClass = false, + isStringLike = true + }; + typedef wchar_t CharType; +}; } template <class T> @@ -162,6 +183,8 @@ inline const char* strBegin(const char* str) { return str; } inline const wchar_t* strBegin(const wchar_t* str) { return str; } inline const char* strBegin(const char& ch) { return &ch; } inline const wchar_t* strBegin(const wchar_t& ch) { return &ch; } +inline const char* strBegin(const StringRef<char >& ref) { return ref.data(); } +inline const wchar_t* strBegin(const StringRef<wchar_t>& ref) { return ref.data(); } template <class S> inline @@ -174,6 +197,8 @@ inline size_t strLength(const char* str) { return implementation::cStringLeng inline size_t strLength(const wchar_t* str) { return implementation::cStringLength(str); } inline size_t strLength(char) { return 1; } inline size_t strLength(wchar_t) { return 1; } +inline size_t strLength(const StringRef<char >& ref) { return ref.length(); } +inline size_t strLength(const StringRef<wchar_t>& ref) { return ref.length(); } } #endif //STRING_TRAITS_HEADER_813274321443234 diff --git a/zen/symlink_target.h b/zen/symlink_target.h index 95aa84fb..bbced0fa 100644 --- a/zen/symlink_target.h +++ b/zen/symlink_target.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef SYMLINK_WIN_H_INCLUDED -#define SYMLINK_WIN_H_INCLUDED +#ifndef SYMLINK_80178347019835748321473214 +#define SYMLINK_80178347019835748321473214 #include "scope_guard.h" #include "file_error.h" @@ -15,6 +15,7 @@ #include "WinIoCtl.h" #include "privilege.h" #include "long_path_prefix.h" +#include "dll.h" #elif defined FFS_LINUX || defined FFS_MAC #include <unistd.h> @@ -26,9 +27,11 @@ namespace zen #ifdef FFS_WIN bool isSymlink(const WIN32_FIND_DATA& data); //*not* a simple FILE_ATTRIBUTE_REPARSE_POINT check! bool isSymlink(DWORD fileAttributes, DWORD reparseTag); + +Zstring getResolvedFilePath(const Zstring& filename); //throw FileError; requires Vista or later! #endif -Zstring getSymlinkRawTargetString(const Zstring& linkPath); //throw FileError +Zstring getSymlinkTargetRaw(const Zstring& linkPath); //throw FileError } @@ -90,7 +93,7 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro catch (FileError&) {} //This shall not cause an error in user mode! const HANDLE hLink = ::CreateFile(applyLongPathPrefix(linkPath).c_str(), - GENERIC_READ, + 0, //it seems we do not even need GENERIC_READ! FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, @@ -138,32 +141,78 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro return output; #elif defined FFS_LINUX || defined FFS_MAC - const int BUFFER_SIZE = 10000; + const size_t BUFFER_SIZE = 10000; std::vector<char> buffer(BUFFER_SIZE); - const int bytesWritten = ::readlink(linkPath.c_str(), &buffer[0], BUFFER_SIZE); - if (bytesWritten < 0 || bytesWritten >= BUFFER_SIZE) + const ssize_t bytesWritten = ::readlink(linkPath.c_str(), &buffer[0], BUFFER_SIZE); + if (bytesWritten < 0 || bytesWritten >= static_cast<ssize_t>(BUFFER_SIZE)) //detect truncation! { std::wstring errorMessage = replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)); if (bytesWritten < 0) errorMessage += L"\n\n" + getLastErrorFormatted(); throw FileError(errorMessage); } - buffer[bytesWritten] = 0; //set null-terminating char - - return Zstring(&buffer[0], bytesWritten); + return Zstring(&buffer[0], bytesWritten); //readlink does not append 0-termination! #endif } + + +#ifdef FFS_WIN +Zstring getResolvedFilePath_impl(const Zstring& filename) //throw FileError +{ + using namespace zen; + + const HANDLE hDir = ::CreateFile(applyLongPathPrefix(filename).c_str(), + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory + nullptr); + if (hDir == INVALID_HANDLE_VALUE) + throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted() + L" (CreateFile)"); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir)); + + //GetFinalPathNameByHandle() is not available before Vista! + typedef DWORD (WINAPI* GetFinalPathNameByHandleWFunc)(HANDLE hFile, LPTSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags); + const SysDllFun<GetFinalPathNameByHandleWFunc> getFinalPathNameByHandle(L"kernel32.dll", "GetFinalPathNameByHandleW"); + + if (!getFinalPathNameByHandle) + throw FileError(replaceCpy(_("Cannot find system function %x."), L"%x", L"\"GetFinalPathNameByHandleW\"")); + + const DWORD bufferSize = getFinalPathNameByHandle(hDir, nullptr, 0, 0); + if (bufferSize == 0) + throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted()); + + std::vector<wchar_t> targetPath(bufferSize); + const DWORD charsWritten = getFinalPathNameByHandle(hDir, //__in HANDLE hFile, + &targetPath[0], //__out LPTSTR lpszFilePath, + bufferSize, //__in DWORD cchFilePath, + 0); //__in DWORD dwFlags + if (charsWritten == 0 || charsWritten >= bufferSize) + { + std::wstring errorMessage = replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(filename)); + if (charsWritten == 0) + errorMessage += L"\n\n" + getLastErrorFormatted(); + throw FileError(errorMessage); + } + + return Zstring(&targetPath[0], charsWritten); +} +#endif } namespace zen { inline -Zstring getSymlinkRawTargetString(const Zstring& linkPath) { return getSymlinkRawTargetString_impl(linkPath); } +Zstring getSymlinkTargetRaw(const Zstring& linkPath) { return getSymlinkRawTargetString_impl(linkPath); } #ifdef FFS_WIN +inline +Zstring getResolvedFilePath(const Zstring& filename) { return getResolvedFilePath_impl(filename); } + /* Reparse Point Tags http://msdn.microsoft.com/en-us/library/windows/desktop/aa365511(v=vs.85).aspx @@ -190,4 +239,4 @@ bool isSymlink(const WIN32_FIND_DATA& data) #endif } -#endif // SYMLINK_WIN_H_INCLUDED +#endif //SYMLINK_80178347019835748321473214 diff --git a/zen/thread.h b/zen/thread.h index db9cf3a3..638d9474 100644 --- a/zen/thread.h +++ b/zen/thread.h @@ -10,11 +10,12 @@ //temporary solution until C++11 thread becomes fully available (considering std::thread's non-interruptibility and std::async craziness, this may be NEVER) #include <memory> -//fix this pathetic boost thread warning mess +//workaround this pathetic boost thread warning mess #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch-enum" #pragma GCC diagnostic ignored "-Wstrict-aliasing" +#pragma GCC diagnostic ignored "-Wredundant-decls" #pragma GCC diagnostic ignored "-Wshadow" #endif #ifdef _MSC_VER diff --git a/zen/tick_count.h b/zen/tick_count.h index be4839ca..d005a828 100644 --- a/zen/tick_count.h +++ b/zen/tick_count.h @@ -129,7 +129,7 @@ std::int64_t ticksPerSec() //return 0 on error inline -TickVal getTicks() //return 0 on error +TickVal getTicks() //return !isValid() on error { #ifdef FFS_WIN LARGE_INTEGER now = {}; @@ -291,7 +291,7 @@ bool parseTime(const String& format, const String& str, TimeComp& comp) //return if (std::any_of(iterStr, iterStr + digitCount, [](CharType c) { return !isDigit(c); })) return false; - result = zen::stringTo<int>(StringProxy<CharType>(iterStr, digitCount)); + result = zen::stringTo<int>(StringRef<CharType>(iterStr, iterStr + digitCount)); iterStr += digitCount; return true; }; @@ -50,18 +50,6 @@ size_t findUnicodePos(const UtfString& str, size_t unicodePos); //return positio - - - - - - - - - - - - //----------------------- implementation ---------------------------------- namespace implementation { @@ -69,45 +57,47 @@ typedef std::uint_fast32_t CodePoint; //must be at least four bytes typedef std::uint_fast16_t Char16; //we need an unsigned type typedef unsigned char Char8; -const CodePoint CODE_POINT_MAX = 0x10ffff; +const CodePoint LEAD_SURROGATE = 0xd800; +const CodePoint TRAIL_SURROGATE = 0xdc00; //== LEAD_SURROGATE_MAX + 1 +const CodePoint TRAIL_SURROGATE_MAX = 0xdfff; -const CodePoint HIGH_SURROGATE = 0xd800; -const CodePoint HIGH_SURROGATE_MAX = 0xdbff; - -const CodePoint LOW_SURROGATE = 0xdc00; -const CodePoint LOW_SURROGATE_MAX = 0xdfff; +const CodePoint REPLACEMENT_CHAR = 0xfffd; +const CodePoint CODE_POINT_MAX = 0x10ffff; template <class Function> inline void codePointToUtf16(CodePoint cp, Function writeOutput) //"writeOutput" is a unary function taking a Char16 { //http://en.wikipedia.org/wiki/UTF-16 - assert(cp < HIGH_SURROGATE || LOW_SURROGATE_MAX < cp); //code points [0xd800, 0xdfff] are not allowed for UTF-16 - assert(cp <= CODE_POINT_MAX); - if (cp < 0x10000) + if (cp < LEAD_SURROGATE) writeOutput(static_cast<Char16>(cp)); - else + else if (cp <= TRAIL_SURROGATE_MAX) //invalid code point + codePointToUtf16(REPLACEMENT_CHAR, writeOutput); //resolves to 1-character utf16 + else if (cp < 0x10000) + writeOutput(static_cast<Char16>(cp)); + else if (cp <= CODE_POINT_MAX) { cp -= 0x10000; - writeOutput(static_cast<Char16>((cp >> 10) + HIGH_SURROGATE)); - writeOutput(static_cast<Char16>((cp & 0x3ff) + LOW_SURROGATE)); + writeOutput(LEAD_SURROGATE + static_cast<Char16>(cp >> 10)); + writeOutput(TRAIL_SURROGATE + static_cast<Char16>(cp & 0x3ff)); } + else //invalid code point + codePointToUtf16(REPLACEMENT_CHAR, writeOutput); //resolves to 1-character utf16 } inline -size_t getUtf16Len(Char16 ch) //ch must be first code unit! +size_t getUtf16Len(Char16 ch) //ch must be first code unit! returns 0 on error! { - const CodePoint cp = ch; - - if (HIGH_SURROGATE <= cp && cp <= HIGH_SURROGATE_MAX) + if (ch < LEAD_SURROGATE) + return 1; + else if (ch < TRAIL_SURROGATE) return 2; + else if (ch <= TRAIL_SURROGATE_MAX) + return 0; //unexpected trail surrogate! else - { - assert(cp < LOW_SURROGATE || LOW_SURROGATE_MAX < cp); //NO low surrogate expected return 1; - } } @@ -119,19 +109,27 @@ void utf16ToCodePoint(CharIterator first, CharIterator last, Function writeOutpu for ( ; first != last; ++first) { CodePoint cp = static_cast<Char16>(*first); - if (HIGH_SURROGATE <= cp && cp <= HIGH_SURROGATE_MAX) + switch (getUtf16Len(static_cast<Char16>(cp))) { - if (++first == last) - { - assert(false); //low surrogate expected - return; - } - assert(LOW_SURROGATE <= static_cast<Char16>(*first) && static_cast<Char16>(*first) <= LOW_SURROGATE_MAX); //low surrogate expected - cp = ((cp - HIGH_SURROGATE) << 10) + static_cast<Char16>(*first) - LOW_SURROGATE + 0x10000; + case 0: //invalid utf16 character + cp = REPLACEMENT_CHAR; + break; + case 1: + break; + case 2: + if (++first != last) //trail surrogate expected! + { + const Char16 ch = static_cast<Char16>(*first); + if (TRAIL_SURROGATE <= ch && ch <= TRAIL_SURROGATE_MAX) //trail surrogate expected! + { + cp = ((cp - LEAD_SURROGATE) << 10) + (ch - TRAIL_SURROGATE) + 0x10000; + break; + } + } + --first; + cp = REPLACEMENT_CHAR; + break; } - else - assert(cp < LOW_SURROGATE || LOW_SURROGATE_MAX < cp); //NO low surrogate expected - writeOutput(cp); } } @@ -141,6 +139,7 @@ template <class Function> inline void codePointToUtf8(CodePoint cp, Function writeOutput) //"writeOutput" is a unary function taking a Char8 { //http://en.wikipedia.org/wiki/UTF-8 + //assert(cp < LEAD_SURROGATE || TRAIL_SURROGATE_MAX < cp); //code points [0xd800, 0xdfff] are reserved for UTF-16 and *should* not be encoded in UTF-8 if (cp < 0x80) writeOutput(static_cast<Char8>(cp)); @@ -151,23 +150,24 @@ void codePointToUtf8(CodePoint cp, Function writeOutput) //"writeOutput" is a un } else if (cp < 0x10000) { - writeOutput(static_cast<Char8>((cp >> 12 ) | 0xe0)); + writeOutput(static_cast<Char8>( (cp >> 12 ) | 0xe0)); writeOutput(static_cast<Char8>(((cp >> 6) & 0x3f) | 0x80)); - writeOutput(static_cast<Char8>((cp & 0x3f ) | 0x80)); + writeOutput(static_cast<Char8>( (cp & 0x3f ) | 0x80)); } - else + else if (cp <= CODE_POINT_MAX) { - assert(cp <= CODE_POINT_MAX); - writeOutput(static_cast<Char8>((cp >> 18 ) | 0xf0)); + writeOutput(static_cast<Char8>( (cp >> 18 ) | 0xf0)); writeOutput(static_cast<Char8>(((cp >> 12) & 0x3f) | 0x80)); writeOutput(static_cast<Char8>(((cp >> 6) & 0x3f) | 0x80)); - writeOutput(static_cast<Char8>((cp & 0x3f ) | 0x80)); + writeOutput(static_cast<Char8>( (cp & 0x3f ) | 0x80)); } + else //invalid code point + codePointToUtf8(REPLACEMENT_CHAR, writeOutput); //resolves to 3-byte utf8 } inline -size_t getUtf8Len(unsigned char ch) //ch must be first code unit! +size_t getUtf8Len(unsigned char ch) //ch must be first code unit! returns 0 on error! { if (ch < 0x80) return 1; @@ -177,12 +177,27 @@ size_t getUtf8Len(unsigned char ch) //ch must be first code unit! return 3; if (ch >> 3 == 0x1e) return 4; - - assert(false); //no valid begin of UTF8 encoding - return 1; + return 0; //innvalid begin of UTF8 encoding } +template <class CharIterator> inline +bool decodeTrail(CharIterator& first, CharIterator last, CodePoint& cp) //decode trailing surrogate byte +{ + if (++first != last) //trail surrogate expected! + { + const Char8 ch = static_cast<Char8>(*first); + if (ch >> 6 == 0x2) //trail surrogate expected! + { + cp = (cp << 6) + (ch & 0x3f); + return true; + } + } + --first; + cp = REPLACEMENT_CHAR; + return false; +} + template <class CharIterator, class Function> inline void utf8ToCodePoint(CharIterator first, CharIterator last, Function writeOutput) //"writeOutput" is a unary function taking a CodePoint { @@ -190,57 +205,32 @@ void utf8ToCodePoint(CharIterator first, CharIterator last, Function writeOutput for ( ; first != last; ++first) { - auto getChar = [&](Char8& ch) -> bool - { - if (++first == last) - { - assert(false); //low surrogate expected - return false; - } - ch = static_cast<Char8>(*first); - assert(ch >> 6 == 0x2); - return true; - }; - - Char8 ch = static_cast<Char8>(*first); - switch (getUtf8Len(ch)) + CodePoint cp = static_cast<Char8>(*first); + switch (getUtf8Len(static_cast<Char8>(cp))) { + case 0: //invalid utf8 character + cp = REPLACEMENT_CHAR; + break; case 1: - writeOutput(ch); break; case 2: - { - CodePoint cp = (ch & 0x1f) << 6; - if (!getChar(ch)) return; - cp += ch & 0x3f; - writeOutput(cp); - } - break; + cp &= 0x1f; + decodeTrail(first, last, cp); + break; case 3: - { - CodePoint cp = (ch & 0xf) << 12; - if (!getChar(ch)) return; - cp += (ch & 0x3f) << 6; - if (!getChar(ch)) return; - cp += ch & 0x3f; - writeOutput(cp); - } - break; + cp &= 0xf; + if (decodeTrail(first, last, cp)) + decodeTrail(first, last, cp); + break; case 4: - { - CodePoint cp = (ch & 0x7) << 18; - if (!getChar(ch)) return; - cp += (ch & 0x3f) << 12; - if (!getChar(ch)) return; - cp += (ch & 0x3f) << 6; - if (!getChar(ch)) return; - cp += ch & 0x3f; - writeOutput(cp); - } - break; - default: - assert(false); + cp &= 0x7; + if (decodeTrail(first, last, cp)) + if (decodeTrail(first, last, cp)) + decodeTrail(first, last, cp); + if (cp > CODE_POINT_MAX) cp = REPLACEMENT_CHAR; + break; } + writeOutput(cp); } } @@ -257,7 +247,10 @@ size_t unicodeLength(const CharString& str, char) //utf8 while (strFirst < strLast) //[!] { ++len; - strFirst += getUtf8Len(*strFirst); //[!] + + size_t utf8len = getUtf8Len(*strFirst); + if (utf8len == 0) ++utf8len; //invalid utf8 character + strFirst += utf8len; } return len; } @@ -275,7 +268,9 @@ size_t unicodeLengthWide(const WideString& str, Int2Type<2>) //windows: utf16-wc while (strFirst < strLast) //[!] { ++len; - strFirst += getUtf16Len(*strFirst); //[!] + size_t utf16len = getUtf16Len(*strFirst); + if (utf16len == 0) ++utf16len; //invalid utf16 character + strFirst += utf16len; } return len; } @@ -316,7 +311,9 @@ size_t findUnicodePos(const CharString& str, size_t unicodePos, char) //utf8-cha size_t utfPos = 0; while (unicodePos-- > 0) { - utfPos += getUtf8Len(strFirst[utfPos]); + size_t utf8len = getUtf8Len(strFirst[utfPos]); + if (utf8len == 0) ++utf8len; //invalid utf8 character + utfPos += utf8len; if (utfPos >= strLen) return strLen; @@ -336,7 +333,9 @@ size_t findUnicodePosWide(const WideString& str, size_t unicodePos, Int2Type<2>) size_t utfPos = 0; while (unicodePos-- > 0) { - utfPos += getUtf16Len(strFirst[utfPos]); + size_t utf16len = getUtf16Len(strFirst[utfPos]); + if (utf16len == 0) ++utf16len; //invalid utf16 character + utfPos += utf16len; if (utfPos >= strLen) return strLen; diff --git a/zen/zstring.cpp b/zen/zstring.cpp index 262df49e..085e6f72 100644 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -78,10 +78,8 @@ private: static std::string rawMemToString(const void* ptr, size_t size) { - std::string output(reinterpret_cast<const char*>(ptr), size); - vector_remove_if(output, [](char& c) { return c == 0; }); //remove intermediate 0-termination - if (output.size() > 100) - output.resize(100); + std::string output(reinterpret_cast<const char*>(ptr), std::min<size_t>(size, 100)); + std::replace(output.begin(), output.end(), '\0', ' '); //don't stop at 0-termination return output; } |