From c4182431ac7d9d306fdd2267e082fa4cec6fec2c Mon Sep 17 00:00:00 2001 From: Daniel Wilhelm Date: Fri, 18 Apr 2014 17:22:55 +0200 Subject: 5.12 --- Application.cpp | 2 +- BUILD/Changelog.txt | 18 +- BUILD/FreeFileSync.chm | Bin 437982 -> 440642 bytes BUILD/Help/html/Comparison Settings.html | 34 +- BUILD/Help/html/Daylight Saving Time.html | 12 +- BUILD/Help/html/Exclude Items.html | 8 +- BUILD/Help/html/External Applications.html | 30 +- BUILD/Help/html/RealtimeSync.html | 2 +- BUILD/Help/html/Run as Service.html | 12 +- BUILD/Help/html/Schedule a Batch Job.html | 2 +- BUILD/Help/img/ScheduleBatch.gif | Bin 21107 -> 0 bytes BUILD/Help/img/ScheduleBatch.png | Bin 0 -> 23029 bytes BUILD/Help/img/create_shortcut.png | Bin 15610 -> 16086 bytes BUILD/Help/img/schedule_realtimesync.png | Bin 28856 -> 29072 bytes BUILD/Help/img/shortcut_properties.png | Bin 29975 -> 29921 bytes BUILD/Help/img/win7scheduler.png | Bin 28693 -> 28955 bytes BUILD/Languages/arabic.lng | 1421 ++++++++++++++++++++++++++++ BUILD/Languages/chinese_simple.lng | 64 +- BUILD/Languages/chinese_traditional.lng | 4 +- BUILD/Languages/croatian.lng | 4 +- BUILD/Languages/czech.lng | 4 +- BUILD/Languages/danish.lng | 638 ++++++------- BUILD/Languages/dutch.lng | 67 +- BUILD/Languages/english_uk.lng | 90 +- BUILD/Languages/finnish.lng | 4 +- BUILD/Languages/french.lng | 4 +- BUILD/Languages/german.lng | 90 +- BUILD/Languages/greek.lng | 154 ++- BUILD/Languages/hebrew.lng | 4 +- BUILD/Languages/hungarian.lng | 119 ++- BUILD/Languages/italian.lng | 4 +- BUILD/Languages/japanese.lng | 4 +- BUILD/Languages/korean.lng | 4 +- BUILD/Languages/lithuanian.lng | 4 +- BUILD/Languages/norwegian.lng | 182 ++-- BUILD/Languages/polish.lng | 4 +- BUILD/Languages/portuguese.lng | 4 +- BUILD/Languages/portuguese_br.lng | 4 +- BUILD/Languages/romanian.lng | 4 +- BUILD/Languages/russian.lng | 4 +- BUILD/Languages/scottish_gaelic.lng | 4 +- BUILD/Languages/slovenian.lng | 4 +- BUILD/Languages/spanish.lng | 4 +- BUILD/Languages/swedish.lng | 4 +- BUILD/Languages/turkish.lng | 4 +- BUILD/Languages/ukrainian.lng | 89 +- BUILD/Resources.zip | Bin 226776 -> 225731 bytes FreeFileSync.cbp | 538 ----------- FreeFileSync.vcxproj | 1 + RealtimeSync/RealtimeSync.cbp | 158 ---- RealtimeSync/resources.cpp | 4 +- RealtimeSync/tray_menu.cpp | 2 +- RealtimeSync/watcher.cpp | 13 +- algorithm.cpp | 249 +++-- algorithm.h | 10 +- comparison.cpp | 45 +- file_hierarchy.h | 6 +- lib/binary.cpp | 18 +- lib/binary.h | 2 +- lib/dir_exist_async.h | 2 +- lib/localization.cpp | 124 +-- lib/lock_holder.h | 2 +- lib/parse_lng.h | 191 ++-- lib/parse_plural.h | 524 +++++----- lib/perf_check.cpp | 23 +- lib/perf_check.h | 1 - lib/process_xml.cpp | 95 +- lib/process_xml.h | 17 +- lib/resources.cpp | 4 +- lib/status_handler.cpp | 2 +- lib/status_handler.h | 2 +- lib/versioning.cpp | 82 +- lib/versioning.h | 32 +- process_callback.h | 22 +- synchronization.cpp | 716 +++++++------- synchronization.h | 2 - ui/batch_status_handler.cpp | 16 +- ui/check_version.cpp | 7 +- ui/custom_grid.cpp | 2 +- ui/grid_view.cpp | 2 +- ui/gui_generated.cpp | 255 +++-- ui/gui_generated.h | 65 +- ui/gui_status_handler.cpp | 6 +- ui/main_dlg.cpp | 1088 ++++++++++----------- ui/main_dlg.h | 43 +- ui/progress_indicator.cpp | 95 +- ui/progress_indicator.h | 4 +- ui/sync_cfg.cpp | 4 +- ui/tree_view.cpp | 8 + ui/triple_splitter.cpp | 8 +- version/version.h | 2 +- version/version.rc | 4 +- wx+/graph.cpp | 158 ++-- wx+/graph.h | 20 +- wx+/grid.cpp | 10 +- wx+/pch.h | 9 +- wx+/shell_execute.h | 12 +- zen/IFileOperation/file_op.cpp | 20 +- zen/basic_math.h | 2 +- zen/debug_memory_leaks.cpp | 4 +- zen/file_handling.cpp | 120 ++- zen/file_handling.h | 11 +- zen/file_traverser.h | 6 +- zen/format_unit.cpp | 4 +- zen/guid.h | 3 +- zen/perf.h | 6 +- zen/process_priority.h | 6 +- zen/recycler.h | 3 +- zen/string_base.h | 19 +- zen/thread.h | 14 +- 110 files changed, 4406 insertions(+), 3631 deletions(-) delete mode 100644 BUILD/Help/img/ScheduleBatch.gif create mode 100644 BUILD/Help/img/ScheduleBatch.png create mode 100644 BUILD/Languages/arabic.lng delete mode 100644 FreeFileSync.cbp delete mode 100644 RealtimeSync/RealtimeSync.cbp diff --git a/Application.cpp b/Application.cpp index 85606613..733311b7 100644 --- a/Application.cpp +++ b/Application.cpp @@ -164,7 +164,7 @@ void Application::OnStartApplication(wxIdleEvent&) #ifdef FFS_WIN wxToolTip::SetMaxWidth(-1); //disable tooltip wrapping -> Windows only (as of 2.9.2) #endif - wxToolTip::SetAutoPop(7000); //tooltip visibilty in ms, 5s seems to be default for Windows + wxToolTip::SetAutoPop(7000); //tooltip visibilty in ms, 5s is default for Windows: http://msdn.microsoft.com/en-us/library/windows/desktop/aa511495.aspx #endif try diff --git a/BUILD/Changelog.txt b/BUILD/Changelog.txt index 7c82a459..fa82072f 100644 --- a/BUILD/Changelog.txt +++ b/BUILD/Changelog.txt @@ -2,6 +2,22 @@ |FreeFileSync| -------------- +Changelog v5.12 +--------------- +Dynamic statistics adjustment during synchronization +Allow to save active view filter settings as default (context menu) +Stay responsive while checking recycle bin existence on slow disks +Reset option "Delete on both sides" upon each manual deletion +Added context menu to allow deletion of last used configurations +Support numpad add/subtract keys for overview tree +Revised external application integration +Call external applications for multiple selected items +Automatically schedule abandoned recycle bin temp directories (.ffs_tmp) for deletion +Binary comparison speed estimate considers errors and short-circuit evaluation +Use full time window of sync phase when calculating overall speed +Added Arabic language + + Changelog v5.11 --------------- New file versioning scheme: move to folder replacing existing files @@ -208,7 +224,7 @@ Support Ctrl + A in filter dialog Support large filter lists > 32 kByte Allow to hide file icons Avoid switching monitor when main dialog is maximized on multiple monitor systems -Improved huge XML files loading times by a factor of 3000, saving by a factor of 3 +Improved huge XML file loading times by a factor of 3000, saving by a factor of 3 Restore grid scroll position after repeated comparisons Show log after sync when non-fatal errors occurred Fixed crash in UTF8 conversion when processing a corrupted ffs_db file diff --git a/BUILD/FreeFileSync.chm b/BUILD/FreeFileSync.chm index e511c8e0..deb1bb4f 100644 Binary files a/BUILD/FreeFileSync.chm and b/BUILD/FreeFileSync.chm differ diff --git a/BUILD/Help/html/Comparison Settings.html b/BUILD/Help/html/Comparison Settings.html index e30b33f5..783b0728 100644 --- a/BUILD/Help/html/Comparison Settings.html +++ b/BUILD/Help/html/Comparison Settings.html @@ -3,9 +3,9 @@ - + - + @@ -27,7 +27,7 @@

Comparison Settings

-

+

I. Compare by "File time and size"

This @@ -65,21 +65,6 @@ distinguished:

-

This -results into the following categories:

-


II. @@ -110,17 +95,6 @@ modification time is not taken into account at all.

-

As a -result the files are separated into the following categories:

-


Symbolic link @@ -151,7 +125,7 @@ called symlinks or soft links):

Note

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

  • Copying symbolic links requires diff --git a/BUILD/Help/html/Daylight Saving Time.html b/BUILD/Help/html/Daylight Saving Time.html index d088d807..31c5f8e2 100644 --- a/BUILD/Help/html/Daylight Saving Time.html +++ b/BUILD/Help/html/Daylight Saving Time.html @@ -3,9 +3,9 @@ - + - + @@ -29,10 +29,10 @@ saving time The reason for this strange behavior lies in the way NTFS and FAT drives store file times: NTFS stores time in UTC format, while FAT uses diff --git a/BUILD/Help/html/Exclude Items.html b/BUILD/Help/html/Exclude Items.html index 239b639a..8d4c5979 100644 --- a/BUILD/Help/html/Exclude Items.html +++ b/BUILD/Help/html/Exclude Items.html @@ -3,9 +3,9 @@ - + - + @@ -44,7 +44,7 @@ Exclude items for mirror-sync from to D:\Target

    Single - file C:\Source\file.txt:
         Exclude: + file C:\Source\file.txt:
        Exclude: \file.txt

    Single folder C:\Source\subfolder:
        Exclude: @@ -64,7 +64,7 @@ Exclude items for mirror-sync from in their path:
        Exclude: *temp*

    Multiple - entries separated by semicolon:
        Exclude: + entries separated by semicolon:
        Exclude: *.tmp; *.doc; *.bak



    diff --git a/BUILD/Help/html/External Applications.html b/BUILD/Help/html/External Applications.html index f9d939dc..bdf08cb5 100644 --- a/BUILD/Help/html/External Applications.html +++ b/BUILD/Help/html/External Applications.html @@ -3,9 +3,9 @@ - + - + @@ -26,28 +26,28 @@ applications

  • By -default FreeFileSync opens the operating system's standard file -browser on each mouse double-click by invoking "explorer +default FreeFileSync opens the operating system's file browser on +each mouse double-click by invoking "explorer /select, "%item_path%"" on Windows and "xdg-open "%item_folder%"" on Linux.

    However -you are free to integrate other external applications into -FreeFileSync: navigate to "Menu --> Advanced -> Global settings: External Applications" -and add or replace a command string. The first entry is executed when -double-clicking a row on main grid while all other entries are only -available via right-click context menu. The following internal macros -may be used:

    +you may integrate other external applications into FreeFileSync: +navigate to "
    Menu -> +Advanced -> Global settings: External Applications" +and add or replace a command. The first entry is executed when +double-clicking a row or pressing ENTER on main grid while all other +entries can only be accessed via the context menu shown after a +right-click. The following internal macros are available:

    %item_path%     - full file or folder name
    %item_folder%   - folder - part only
    %item2_path%    - Other - side's counterpart to %item_path%
    %item2_folder%  - - Other side's counterpart to - %item_folder%

    + part only
    %item2_path%    - + Counterpart of %item_path% on the + opposite grid
    %item2_folder%  - Counterpart + of %item_folder% on the opposite grid




    diff --git a/BUILD/Help/html/RealtimeSync.html b/BUILD/Help/html/RealtimeSync.html index f7740c09..b8a94c62 100644 --- a/BUILD/Help/html/RealtimeSync.html +++ b/BUILD/Help/html/RealtimeSync.html @@ -71,7 +71,7 @@ to begin monitoring.

    integrated into the operating system's auto start facility:
          "C:\Program Files\FreeFileSync\RealtimeSync.exe" - "C:\MyConfig.ffs_real"
          "C:\Program + "C:\SyncJob.ffs_real"
          "C:\Program Files\FreeFileSync\RealtimeSync.exe" "C:\SyncJob.ffs_batch"
     

  • Using RealtimeSync is not restricted to starting FreeFileSync. It can diff --git a/BUILD/Help/html/Run as Service.html b/BUILD/Help/html/Run as Service.html index f6329a33..ae8f57c3 100644 --- a/BUILD/Help/html/Run as Service.html +++ b/BUILD/Help/html/Run as Service.html @@ -3,9 +3,9 @@ - + - + @@ -41,7 +41,7 @@ file>

    "C:\Program Files\FreeFileSync\RealtimeSync.exe" "C:\some - folder\SyncJob.ffs_batch"

    + folder\SyncJob.ffs_real"


    @@ -52,8 +52,8 @@ file>

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

    -

    -

    +

    +

     

  • RealtimeSync should be monitoring while Windows is online irrespective of @@ -63,7 +63,7 @@ file>

    a Batch Job for an example how to add a new task. Then change the user to run the task to "SYSTEM" - a special user account always running in the background.

    -

    +


    diff --git a/BUILD/Help/html/Schedule a Batch Job.html b/BUILD/Help/html/Schedule a Batch Job.html index 3f05268b..ee306e47 100644 --- a/BUILD/Help/html/Schedule a Batch Job.html +++ b/BUILD/Help/html/Schedule a Batch Job.html @@ -93,7 +93,7 @@ a Batch Job
  • Enter "Run:" as: <FreeFileSync installation directory>\FreeFileSync.exe <job - name>.ffs_batch

     

    + name>.ffs_batch

     

  • Ubuntu Linux Gnome-schedule:

    diff --git a/BUILD/Help/img/ScheduleBatch.gif b/BUILD/Help/img/ScheduleBatch.gif deleted file mode 100644 index 20d27d6d..00000000 Binary files a/BUILD/Help/img/ScheduleBatch.gif and /dev/null differ diff --git a/BUILD/Help/img/ScheduleBatch.png b/BUILD/Help/img/ScheduleBatch.png new file mode 100644 index 00000000..c991d1fd Binary files /dev/null and b/BUILD/Help/img/ScheduleBatch.png differ diff --git a/BUILD/Help/img/create_shortcut.png b/BUILD/Help/img/create_shortcut.png index fba73ec1..2c42f3a1 100644 Binary files a/BUILD/Help/img/create_shortcut.png and b/BUILD/Help/img/create_shortcut.png differ diff --git a/BUILD/Help/img/schedule_realtimesync.png b/BUILD/Help/img/schedule_realtimesync.png index 285804b4..ac1e2a80 100644 Binary files a/BUILD/Help/img/schedule_realtimesync.png and b/BUILD/Help/img/schedule_realtimesync.png differ diff --git a/BUILD/Help/img/shortcut_properties.png b/BUILD/Help/img/shortcut_properties.png index c801f6d3..1e06b617 100644 Binary files a/BUILD/Help/img/shortcut_properties.png and b/BUILD/Help/img/shortcut_properties.png differ diff --git a/BUILD/Help/img/win7scheduler.png b/BUILD/Help/img/win7scheduler.png index 08314d16..93f1e020 100644 Binary files a/BUILD/Help/img/win7scheduler.png and b/BUILD/Help/img/win7scheduler.png differ diff --git a/BUILD/Languages/arabic.lng b/BUILD/Languages/arabic.lng new file mode 100644 index 00000000..7a3ee9b1 --- /dev/null +++ b/BUILD/Languages/arabic.lng @@ -0,0 +1,1421 @@ +
    + العربية + Awadh Al-Ghaamdi + ar + arabic.png + 6 + n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5 +
    + +Searching for folder %x... +البحث عن المجلد %x... + +Items processed: +معالجة العناصر: + +Items remaining: +العناصر المتبقية: + +Total time: +مجموع الوقت: + +Show in Explorer +إظهار في مستكشف + +Open with default application +فتح باستخدام التطبيق الافتراضي + +Browse directory +تصفح الدليل + +Abort requested: Waiting for current operation to finish... +إحباط وطلبت: في انتظار انتهاء العملية الحالية... + +Failure to create timestamp for versioning: +فشل إنشاء الطابع الزمني للإصدارات: + +RealtimeSync - Automated Synchronization +المزامنة التلقائية + +Error +خطأ + +Select alternate comparison settings +حدد إعدادات المقارنة البديل + +Select alternate synchronization settings +حدد إعدادات المزامنة البديل + +Filter is active +عامل التصفية نشط + +No filter selected +لا يوجد عامل تصفية مختارة + +Remove alternate settings +إزالة إعدادات البديل + +Clear filter settings +مسح إعدادات عامل التصفية + +Save as batch job +حفظ كمهمة دفعية + +Comparison settings +إعدادات المقارنة + +Synchronization settings +إعدادات المزامنة + +About +حول + +Confirm +تأكيد + +Configure filter +تكوين عامل تصفية + +Global settings +الإعدادات العامة + +Find +بحث + +Select time span +حدد الفترة الزمنية + +Invalid command line: +سطر الأوامر غير صالح: + +Info +معلومات + +Warning +تحذير + +Fatal Error +خطأ فادح + +Windows Error Code %x: +رمز خطأ Windows %x: + +Linux Error Code %x: +رمز الخطأ لينكس %x: + +Cannot resolve symbolic link %x. +لا يمكن حل الارتباط الرمزي %x. + +%x MB +%x مب + +%x KB +%x ك. بايت + +%x GB +%x GB + + +1 Byte +%x Bytes + + + +Database file %x is incompatible. +ملف قاعدة البيانات %x غير متوافق. + +Initial synchronization: +المزامنة الأولية: + +Database file %x does not yet exist. +ملف قاعدة البيانات %x غير موجود حتى الآن. + +Database file is corrupt: +ملف قاعدة البيانات معطوبة: + +Out of memory! +نفاد في الذاكرة + +Cannot write file %x. +لا يمكن كتابة ملف %x. + +Cannot read file %x. +لا يمكن قراءة الملف %x. + +Database files do not share a common session. +عدم مشاركة ملفات قاعدة بيانات جلسة عمل مشتركة. + +An exception occurred! +حدث استثناء + +Cannot read file attributes of %x. +لا يمكن قراءة سمات الملف من %x. + +Cannot get process information. +لا يمكن الحصول على معلومات عملية. + +Waiting while directory is locked (%x)... +في انتظار أثناء الدليل مؤمناً (%x)... + +Cannot set directory lock %x. +لا يمكن تعيين الدليل قفل %x. + + +1 sec +%x sec + + + +Error parsing file %x, row %y, column %z. +حدث خطأ أثناء تحليل ملف %x، صف %y، وعمود %z. + +Scanning: +المسح الضوئي: + +Encoding extended time information: %x +ترميز المعلومات تمديد الوقت: %x + + +[1 Thread] +[%x Threads] + + + +/sec +/ثانية + +Cannot find file %x. +لا يمكن العثور على الملف %x. + +File %x does not contain a valid configuration. +لا يحتوي ملف %x تكويناً صحيحاً. + +Configuration file %x loaded partially only. +تحميل ملف التكوين %x جزئيا فقط. + +Cannot access Volume Shadow Copy Service. +لا يمكن الوصول إلى "خدمة نسخة الظل لوحدة التخزين". + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +الرجاء استخدام الإصدار البرنامج 64-بت لإنشاء ملفات الظل الاحتياطية على هذا النظام. + +Cannot load file %x. +لا يمكن تحميل الملف %x. + +Path %x does not contain a volume name. +المسار %x لا يحتوي على اسم وحدة تخزين. + +Volume name %x not part of file name %y! +اسم وحدة التخزين ليست جزءا من اسم الملف %x + +Cannot read the following XML elements: +لا يمكن قراءة عناصر XML التالية: + +&Open... +فتح... + +Save &as... +حفظ باسم... + +&Quit +إنهاء + +&Program +البرنامج + +&Content +المحتوى + +&About +حول + +&Help +تعليمات + +Usage: +الاستخدام: + +1. Select folders to watch. +1. حدد المجلدات لمشاهدة. + +2. Enter a command line. +2. إدخال سطر الأوامر. + +3. Press 'Start'. +3. اضغط على 'ابدأ'. + +To get started just import a .ffs_batch file. +للحصول على بدء تشغيل الاستيراد فقط ملف .ffs_batch. + +Folders to watch +المجلدات لمشاهدة + +Add folder +إضافة مجلد + +Remove folder +إزالة مجلد + +Browse +تصفح + +Select a folder +حدد مجلد + +Idle time [seconds] +وقت الخمول [ثانية] + +Idle time between last detected change and execution of command +وقت الخمول بين آخر تغيير تم الكشف عنها وتنفيذ الأوامر + +Command line +سطر الأوامر + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +يتم تشغيل الأمر إذا: +-تغيير الملفات أو المجلدات الفرعية +--يصل إلى المجلدات الجديدة (مثل إدراج عصا الناقل التسلسلي العام) + + +Start +بدء تشغيل + +&Retry +إعادة المحاولة + +Cancel +إلغاء الأمر + +Build: %x +بناء: %x + +All files +جميع الملفات + +&Restore +استعادة + +&Exit +خروج + +Monitoring active... +الرصد النشط... + +Waiting for missing directories... +في انتظار الدلائل مفقود... + +A folder input field is empty. +حقل إدخال مجلد فارغ. + +Synchronization aborted! +تم إحباط المزامنة + +Synchronization completed with errors! +تزامن إكمال مع وجود خطأ! + +Synchronization completed with warnings. +تزامن الانتهاء مع التحذيرات. + +Nothing to synchronize! +لا شيء لمزامنة + +Synchronization completed successfully. +تمت المزامنة بنجاح + +Saving log file %x... +حفظ ملف السجل %x... + +Press "Switch" to resolve issues in FreeFileSync main dialog. +اضغط على "التبديل" لحل القضايا في الحوار الرئيسي البرنامج. + +Switching to FreeFileSync main dialog... +التحول إلى الحوار الرئيسي البرنامج... + +A new version of FreeFileSync is available: +يتوفر إصدار جديد من البرنامج: + +Download now? +تحميل الآن؟ + +FreeFileSync is up to date! +البرنامج هو الأحدث حتى الآن! + +Information +معلومات + +Unable to connect to sourceforge.net! +غير قادر على الاتصال إلى sourceforge.net! + +Current FreeFileSync version number was not found online! Do you want to check manually? +لم يتم العثور على رقم الإصدار الحالي من البرنامج على الإنترنت! هل تريد التحقق يدوياً؟ + +Do you want FreeFileSync to automatically check for updates every week? +هل تريد التحقق تلقائياً من وجود تحديثات كل أسبوع؟ + +(Requires an Internet connection!) +(يتطلب اتصال إنترنت)! + + + + + +<المجلد> + +Full path +المسار الكامل + +Name +الاسم + +Relative path +مسار نسبي + +Base folder +المجلد الأساسي + +Size +الحجم + +Date +تاريخ + +Extension +ملحق + +Size: +الحجم: + +Date: +التاريخ: + +Action +عمل + +Category +الفئة + +Drag && drop +سحب إفلات + +Close progress dialog +تقدم وثيقة الحوار + +Standby +وضع الاستعداد + +Log off +تسجيل الخروج + +Shut down +إيقاف التشغيل + +Hibernate +السبات + +&New +الجديد + +&Save +حفظ + +Save as &batch job... +حفظ كمهمة دفعية... + +1. &Compare +1-مقارنة + +2. &Synchronize +2. مزامنة + +&Language +اللغة + +&Global settings... +الإعدادات العمومية... + +&Export file list... +تصدير قائمة ملف... + +&Advanced +متقدم + +&Check for new version +التحقق من الإصدار الجديد + +Compare +قارن + +Compare both sides +المقارنة بين كلا الجانبين + +&Abort +إحباط + +Synchronize +مزامنة + +Start synchronization +بدء تشغيل المزامنة + +Add folder pair +إضافة مجلد زوج + +Remove folder pair +إزالة المجلد الثاني + +Swap sides +مبادلة الجانبين + +Open +فتح + +Save +حفظ + +Last used configurations (press DEL to remove from list) +آخر استخدام تكوينات (اضغط DEL لإزالة من القائمة) + +Hide excluded items +إخفاء العناصر المستبعدة + +Show filtered or temporarily excluded files +عرض تمت تصفيتها أو مؤقتاً استبعاد الملفات + +Number of files and folders that will be created +عدد من الملفات والمجلدات التي سيتم إنشاؤها + +Number of files that will be overwritten +عدد الملفات التي سيتم الكتابة فوق + +Number of files and folders that will be deleted +عدد من الملفات والمجلدات التي سيتم حذفها + +Total bytes to copy +إجمالي وحدات البايت لنسخ + +Items found: +العناصر التي تم العثور عليها: + +Speed: +السرعة: + +Time remaining: +الوقت المتبقي: + +Time elapsed: +الوقت المنقضي: + +Batch job +مهمة المجموعة + +Help +تعليمات + +Error handling +معالجة الأخطاء + +Ignore +تجاهل + +Hide all error and warning messages +إخفاء جميع الخطأ ورسائل تحذير + +Pop-up +إطار منبثق + +Show pop-up on errors or warnings +إظهار المنبثقة عن أخطاء أو تحذيرات + +Exit +خروج + +Abort synchronization on first error +إحباط المزامنة على الخطأ الأول + +On completion +وعند الانتهاء + +Show progress dialog +إظهار التقدم في الحوار + +Save log +حفظ سجل + +Select folder to save log files +حدد مجلداً لحفظ ملفات السجل + +Limit +الحد الأقصى + +Limit maximum number of log files +تقييد الحد الأقصى لعدد ملفات السجل + +Select variant +حدد الخيار + + +Files are found equal if + - last write time and date + - file size +are the same + + +تم العثور على ملفات متساوية إذا + -آخر كتابة الوقت والتاريخ + -حجم الملف +هي نفسها + + +File time and size +وقت الملف والحجم + + +Files are found equal if + - file content +is the same + + +تم العثور على ملفات متساوية إذا + --محتوى الملف +هو نفسه + + +File content +محتوى الملف + +Symbolic Link handling +التعامل مع ارتباط رمزي + +OK +حسنا + +<- Two way -> +<- إتجاهان -> + +Identify and propagate changes on both sides using a database. Deletions, renaming and conflicts are detected automatically. +تحديد ونشر التغييرات في كلا الجانبين باستخدام قاعدة بيانات. عمليات حذف وإعادة تسمية والصراعات هي الكشف عن تلقائياً. + +Mirror ->> +مرآة->> + +Mirror backup of left folder. Right folder is modified to exactly match left folder after synchronization. +مرآة احتياطية من المجلد الأيسر. يتم تعديل المجلد الصحيح بالضبط مطابقة المجلد الأيسر بعد المزامنة. + +Update -> +تحديث-> + +Copy new or updated files to right folder. +نسخ الملفات الجديدة أو المحدثة إلى المجلد الصحيح. + +Custom +مخصص + +Configure your own synchronization rules. +تكوين قواعد المزامنة الخاصة بك. + +Deletion handling +التعامل مع الحذف + +Permanent +الدائمة + +Delete or overwrite files permanently +حذف أو الكتابة فوق الملفات بشكل دائم + +Recycle Bin +سلة المحذوفات + +Use Recycle Bin for deleted and overwritten files +استخدام "سلة المحذوفات" للملفات المحذوفة والكتابة + +Versioning +تعيين الإصدار + +Move time-stamped files into specified folder +نقل ختم الوقت الملفات في المجلد المحدد + +Naming convention: +اصطلاح التسمية: + +Configuration +التكوين + +Item exists on left side only +العنصر موجود على الجانب الأيسر فقط + +Item exists on right side only +العنصر موجود في الجانب الأيمن فقط + +Left side is newer +الجانب الأيسر الأحدث + +Right side is newer +الجانب الأيمن الأحدث + +Items have different content +وقد عناصر المحتوى المختلفة + +Conflict/item cannot be categorized +لا يمكن أن تصنف الصراع/الصنف + +Synchronizing... +مزامنة... + +&Pause +إيقاف مؤقت + +Source code written in C++ using: +منتديات عميد التعريب : + +If you like FreeFileSync +المصدر الأول لتعريب البرامج في العالم العربي + +Donate with PayPal +منتديات عميد التعريب + +Many thanks for localization: +شكرا جزيلا للتعريب: + +Feedback and suggestions are welcome +تعليقات واقتراحات موضع ترحيب + +Homepage +الصفحة الرئيسية + +FreeFileSync at Sourceforge +البرنامج في سورس + +Email +البريد الإلكتروني + +Published under the GNU General Public License +نشر تحت رخصة جنو العمومية العامة + +Delete on both sides +حذف على كلا الجانبين + +Delete on both sides even if the file is selected on one side only +حذف على كلا الجانبين حتى إذا تم تحديد الملف على جانب واحد فقط + + +Only files that match all filter settings will be synchronized. +Note: File names must be relative to base directories! + + +وسوف تكون متزامنة فقط الملفات التي تطابق كافة إعدادات عامل التصفية. +ملاحظة: يجب أن تكون أسماء الملفات بالنسبة إلى قاعدة الدلائل! + + +Include +وتشمل + +Exclude +استبعاد + +Time span +الفترة الزمنية + +File size +حجم الملف + +Minimum +الحد الأدنى + +Maximum +الحد الأقصى + +&Default +الافتراضي + +Fail-safe file copy +نسخ ملف آمنة من الفشل + +Write to a temporary file (*.ffs_tmp) first then rename it. This guarantees a consistent state even in case of fatal error. +الكتابة إلى ملف مؤقت (*.ffs_tmp) أولاً، ثم إعادة تسميته. وهذا يضمن حالة متناسقة حتى في حالة حدوث خطأ فادح. + +Copy locked files +نسخ الملفات المقفلة + +Copy shared or locked files using Volume Shadow Copy Service (Requires Administrator rights) +نسخ الملفات المشتركة أو مؤمن باستخدام "خدمة النسخ الاحتياطي لوحدة التخزين" (حقوق المسؤول يتطلب) + +Copy file access permissions +نسخ أذونات الوصول إلى الملف + +Transfer file and folder permissions (Requires Administrator rights) +نقل أذونات الملفات والمجلدات (حقوق المسؤول يتطلب) + +Restore hidden dialogs +استعادة الحوارات الخفية + +External applications +تطبيقات خارجية + +Description +الوصف + +Variant +البديل + +Statistics +الإحصاءات + +Don't show this dialog again +عدم إظهار هذا الحوار مرة أخرى + +Find what: +بحث عن: + +Match case +مطابقة حالة الأحرف + +&Find next +بحث عن التالي + +Operation aborted! +تم إحباط العملية! + +Main bar +شريط الرئيسي + +Folder pairs +أزواج المجلد + +Overview +نظرة عامة + +Filter files +تصفية الملفات + +Select view +حدد طريقة العرض + +Set direction: +تحديد الاتجاه: + +Exclude temporarily +استبعاد مؤقتاً + +Include temporarily +وتشمل مؤقتاً + +Exclude via filter: +استبعاد عن طريق التصفية: + + +<تحديد متعدد> + +Delete +حذف + +Include all +وتشمل جميع + +Exclude all +استبعاد الكل + +Show icons: +إظهار رموز: + +Small +صغيرة + +Medium +متوسطة + +Large +كبيرة + +Select time span... +حدد الفترة الزمنية... + +Default view +طريقة العرض الافتراضية + +Show "%x" +إظهار "%x" + + +<آخر جلسة> + +Folder Comparison and Synchronization +مقارنة بين مجلد وتزامن + +Configuration saved! +حفظ التكوين! + +FreeFileSync batch +دفعة البرنامج + +Never save changes +ابدأ بحفظ التغييرات + +Do you want to save changes to %x? +هل تريد حفظ التغييرات إلى %x؟ + +Do&n't save +لا تحفظ + +Configuration loaded! +تحميل تكوين! + +Hide files that exist on left side only +إخفاء الملفات الموجودة على الجانب الأيسر فقط + +Show files that exist on left side only +إظهار الملفات الموجودة على الجانب الأيسر فقط + +Hide files that exist on right side only +إخفاء الملفات الموجودة على الجانب الأيمن فقط + +Show files that exist on right side only +إظهار الملفات الموجودة على الجانب الأيمن فقط + +Hide files that are newer on left +إخفاء الملفات التي الأحدث على اليسار + +Show files that are newer on left +إظهار الملفات التي الأحدث على اليسار + +Hide files that are newer on right +إخفاء الملفات التي الأحدث في حق + +Show files that are newer on right +إظهار الملفات التي الأحدث في حق + +Hide files that are equal +إخفاء الملفات التي على قدم المساواة + +Show files that are equal +إظهار الملفات التي على قدم المساواة + +Hide files that are different +إخفاء ملفات مختلفة + +Show files that are different +إظهار ملفات مختلفة + +Hide conflicts +إخفاء الصراعات + +Show conflicts +إظهار الصراعات + +Hide files that will be created on the left side +إخفاء الملفات التي سيتم إنشاؤها على الجانب الأيسر + +Show files that will be created on the left side +إظهار الملفات التي سيتم إنشاؤها على الجانب الأيسر + +Hide files that will be created on the right side +إخفاء الملفات التي سيتم إنشاؤها على الجانب الأيمن + +Show files that will be created on the right side +إظهار الملفات التي سيتم إنشاؤها على الجانب الأيمن + +Hide files that will be deleted on the left side +إخفاء الملفات التي سيتم حذفها على الجانب الأيسر + +Show files that will be deleted on the left side +إظهار الملفات التي سيتم حذفها من على الجانب الأيسر + +Hide files that will be deleted on the right side +إخفاء الملفات التي سيتم حذفها على الجانب الأيمن + +Show files that will be deleted on the right side +إظهار الملفات التي سيتم حذفها على الجانب الأيمن + +Hide files that will be overwritten on left side +إخفاء الملفات التي سيتم الكتابة فوق على الجانب الأيسر + +Show files that will be overwritten on left side +إظهار الملفات التي سيتم الكتابة فوق على الجانب الأيسر + +Hide files that will be overwritten on right side +إخفاء الملفات التي سيتم الكتابة فوق الجانب الأيمن + +Show files that will be overwritten on right side +إظهار الملفات التي سيتم الكتابة فوق الجانب الأيمن + +Hide files that won't be copied +إخفاء الملفات التي لن يتم نسخها + +Show files that won't be copied +إظهار الملفات التي لن يتم نسخها + +All folders are in sync! +تكون الكل المجلدات متزامنة + +Comma separated list +قائمة مفصولة بفواصل + +Legend +أسطورة + +File list exported! +تصدير قائمة الملف + + +Object deleted successfully! +%x objects deleted successfully! + + + + +1 directory +%x directories + + + + +1 file +%x files + + + + +%x of 1 row in view +%x of %y rows in view + + + +Ignore further errors +تجاهل المزيد من الأخطاء + +&Ignore +تجاهل + +&Switch +رمز التبديل + +Question +سؤال + +&Yes +نعم + +&No +لا + +Scanning... +المسح الضوئي... + +Comparing content... +مقارنة المحتوى... + +Copy +نسخة + +Paused +تم إيقاف مؤقتاً + +Initializing... +شمل... + +Aborted +تم إحباط + +Completed +أكملت + +Continue +مواصلة + +Pause +إيقاف مؤقت + +Logging +تسجيل الدخول + +Cannot find %x +لا يمكن العثور على %x + +Inactive +غير نشط + +Today +اليوم + +This week +هذا الأسبوع + +This month +هذا الشهر + +This year +هذه السنة + +Last x days +الماضية X الأيام + +Byte +بايت + +KB +كب + +MB +مب + +Filter +عامل التصفية + +Direct +مباشر + +Follow +اتبع + +Copy NTFS permissions +نسخ أذونات NTFS + +Integrate external applications into context menu. The following macros are available: +دمج تطبيقات خارجية في قائمة السياق. كما تتوفر وحدات الماكرو التالية: + +- full file or folder name +-اسم الملف أو المجلد كامل + +- folder part only +--مجلد جزء فقط + +- Other side's counterpart to %item_path% +-النظيرة الجانب الأخرى إلى %item_path % + +- Other side's counterpart to %item_folder% +-النظيرة الجانب الأخرى إلى %item_folder % + +Make hidden warnings and dialogs visible again? +جعل التحذيرات الخفية وحوارات مرئية مرة أخرى؟ + + +Do you really want to move the following object to the Recycle Bin? +Do you really want to move the following %x objects to the Recycle Bin? + + + + +Do you really want to delete the following object? +Do you really want to delete the following %x objects? + + + +Leave as unresolved conflict +ترك كالصراعات التي لم تحل + +Replace +استبدال + +Move files and replace if existing +نقل الملفات واستبدال إذا كان موجود + +Append a timestamp to each file name +إلحاق طابع زمني لكل ملف اسم + +Folder +مجلد + +File +الملف + +YYYY-MM-DD hhmmss +Hhmmss YYYY-MM-DD + +Files +الملفات + +Percentage +النسبة المئوية + +Cannot monitor directory %x. +لا يمكن رصد الدليل %x. + +Conversion error: +خطأ تحويل: + +Cannot delete file %x. +لا يمكن حذف الملف %x. + +The file is locked by another process: +الملف مؤمن من قبل عملية أخرى: + +Cannot move file %x to %y. +لا يمكن نقل الملف %x إلى %y. + +Cannot delete directory %x. +لا يمكن حذف الدليل %x. + +Cannot write file attributes of %x. +لا يمكن كتابة سمات الملف من %x. + +Cannot write modification time of %x. +لا يمكن كتابة وقت التعديل من %x. + +Cannot find system function %x. +لا يمكن العثور على وظيفة نظام %x. + +Cannot read security context of %x. +لا يمكن قراءة سياق الأمان من %x. + +Cannot write security context of %x. +لا يمكن كتابة سياق الأمان من %x. + +Cannot read permissions of %x. +لا يمكن قراءة أذونات %x. + +Cannot write permissions of %x. +لا يمكن كتابة أذونات %x. + +Cannot create directory %x. +لا يمكن إنشاء الدليل %x. + +Cannot copy symbolic link %x to %y. +لا يمكن نسخ الارتباط الرمزي %x إلى %y. + +Cannot copy file %x to %y. +لا يمكن نسخ الملف %x إلى %y. + +Type of item %x is not supported: +نوع عنصر %x غير معتمد: + +Cannot open directory %x. +لا يمكن فتح الدليل %x. + +Cannot enumerate directory %x. +لا يمكن تعداد الدليل %x. + +Detected endless directory recursion. +تم الكشف عن العودية الدليل التي لا نهاية لها. + +%x TB +%x السل + +%x PB +PB %x + +%x% +%%x + + +1 min +%x min + + + + +1 hour +%x hours + + + + +1 day +%x days + + + +Cannot set privilege %x. +لا يمكن تعيين امتياز %x. + +Unable to move %x to the Recycle Bin! +غير قادر على نقل %x إلى "سلة المحذوفات" + +Both sides have changed since last synchronization! +كلا الجانبين قد تغيرت منذ المزامنة الأخيرة + +Cannot determine sync-direction: +لا يمكن تحديد الاتجاه المزامنة: + +No change since last synchronization! +أي تغيير منذ المزامنة الأخيرة + +The corresponding database entries are not in sync considering current settings. +إدخالات قاعدة البيانات المطابقة ليست في تزامن النظر في الإعدادات الحالية. + +Setting default synchronization directions: Old files will be overwritten with newer files. +الإعداد الافتراضي في الاتجاهين المزامنة: ستتم الكتابة فوق الملفات القديمة بالملفات الأحدث. + +Recycle Bin is not available for the following paths! Files will be deleted permanently instead: +سلة المهملات غير متوفر للمسارات التالية! سيتم حذف الملفات بشكل دائم بدلاً من ذلك: + +You can ignore this error to consider the folder as empty. +يمكنك تجاهل هذا الخطأ للنظر في مجلد فارغ. + +Cannot find folder %x. +لا يمكن العثور على المجلد %x. + +Directories are dependent! Be careful when setting up synchronization rules: +تعتمد الدلائل! كن حذراً عند إعداد قواعد المزامنة: + +Start comparison +مقارنة ابدأ + +Preparing synchronization... +إعداد المزامنة... + +Conflict detected: +تم الكشف عن تعارض: + +File %x has an invalid date! +يحتوي الملف %x تاريخ غير صالح + +Files %x have the same date but a different size! +ملفات %x بنفس التاريخ ولكن حجم مختلف + +Items differ in attributes only +تختلف عناصر في سمات فقط + +Symbolic links %x have the same date but a different target. +الارتباطات الرمزية %x لها نفس التاريخ لكن هدفا مختلفاً. + +Comparing content of files %x +مقارنة محتويات ملفات %x + +Comparing files by content failed. +مقارنة الملفات حسب المحتوى التي فشلت. + +Generating file list... +إنشاء قائمة الملفات... + +Both sides are equal +كلا الجانبين على قدم المساواة + +Copy new item to left +نسخ عنصر جديد إلى اليسار + +Copy new item to right +نسخ عنصر جديد إلى اليمين + +Delete left item +حذف العنصر الأيسر + +Delete right item +حذف البند الصحيح + +Move file on left +نقل ملف على اليسار + +Move file on right +نقل ملف على اليمين + +Overwrite left item +الكتابة فوق العنصر الأيسر + +Overwrite right item +الكتابة فوق العنصر الصحيح + +Do nothing +لا تفعل شيئا + +Update attributes on left +تحديث سمات على اليسار + +Update attributes on right +تحديث سمات على حق + +Multiple... +متعددة... + +Deleting file %x +حذف الملف %x + +Deleting folder %x +حذف المجلد %x + +Deleting symbolic link %x +حذف الارتباط الرمزي %x + +Moving file %x to recycle bin +نقل ملف %x سلة المحذوفات + +Moving folder %x to recycle bin +نقل المجلد %x سلة المحذوفات + +Moving symbolic link %x to recycle bin +نقل الارتباط الرمزي %x سلة المحذوفات + +Moving file %x to %y +نقل ملف %x إلى %y + +Moving folder %x to %y +نقل المجلد %x إلى %y + +Moving symbolic link %x to %y +نقل الارتباط الرمزي %x إلى %y + +Removing old versions... +إزالة الإصدارات القديمة... + +Creating file %x +إنشاء ملف %x + +Creating symbolic link %x +إنشاء ارتباط رمزي %x + +Creating folder %x +إنشاء مجلد %x + +Overwriting file %x +الكتابة فوق الملف %x + +Overwriting symbolic link %x +الكتابة فوق الارتباط الرمزي %x + +Verifying file %x +التحقق من ملف %x + +Updating attributes of %x +تحديث سمات %x + +Target folder %x already existing. +المجلد الهدف %x الموجودة بالفعل. + +Target folder input field must not be empty. +يجب أن لا يكون الحقل الإدخال المجلد الهدف فارغة. + +Folder input field for versioning must not be empty. +حقل الإدخال المجلد لتعيين الإصدار ويجب أن لا يكون فارغاً. + +Source folder %x not found. +المجلد المصدر %x لم يتم العثور على. + +The following items have unresolved conflicts and will not be synchronized: +البنود التالية دون حل الصراعات، وسوف لا تكون متزامنة: + +Significant difference detected: +تم الكشف عن فرق كبير: + +More than 50% of the total number of files will be copied or deleted! +أكثر من 50% من إجمالي عدد الملفات التي سيتم نسخها أو حذف + +Not enough free disk space available in: +غير كافية مساحة القرص الحرة المتوفرة في: + +Required: +مطلوب: + +Available: +متاح: + +A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. +سيتم تعديل مجلد الذي هو جزء من أزواج المجلد متعددة. الرجاء مراجعة إعدادات المزامنة. + +Left +اليسار + +Right +حق + +Synchronizing folder pair: +مزامنة مجلد زوج: + +Generating database... +إنشاء قاعدة بيانات... + +Data verification error: Source and target file have different content! +بيانات التحقق خطأ: الملف المصدر والهدف يكون محتوى مختلفة + diff --git a/BUILD/Languages/chinese_simple.lng b/BUILD/Languages/chinese_simple.lng index 57458c14..5c431691 100644 --- a/BUILD/Languages/chinese_simple.lng +++ b/BUILD/Languages/chinese_simple.lng @@ -31,8 +31,8 @@ Abort requested: Waiting for current operation to finish... 请求中止: 正在等待当前操作完成... -Failure to create time stamp for versioning: - +Failure to create timestamp for versioning: +未能为历史版本创建时间标记 RealtimeSync - Automated Synchronization 实时同步 - 自动同步 @@ -59,7 +59,7 @@ 清除过滤器设置 Save as batch job - +另存为批处理作业 Comparison settings 比较设置 @@ -269,7 +269,7 @@ 选择一个文件夹 Idle time [seconds] - +空闲时间[秒] Idle time between last detected change and execution of command 最后检测到改变和命令执行之间的空闲时间 @@ -325,16 +325,16 @@ The command is triggered if: 同步已完成但有错误. Synchronization completed with warnings. - +同步已完成但有警告. Nothing to synchronize! 没有什么可同步! Synchronization completed successfully. - +同步成功完成. Saving log file %x... - +正在保存日志文件 %x... Press "Switch" to resolve issues in FreeFileSync main dialog. 请按下"切换"来解决 FreeFileSync主对话框上的问题. @@ -358,7 +358,7 @@ The command is triggered if: 无法链接到 Sourceforge.net! Current FreeFileSync version number was not found online! Do you want to check manually? - +当前FreeFileSync版本号未能在线找到! 要手工检查更新吗? Do you want FreeFileSync to automatically check for updates every week? 要让 FreeFileSync 保持每周检查一次更新吗? @@ -430,7 +430,7 @@ The command is triggered if: 保存(&S) Save as &batch job... - +另存为批处理作业(&b) 1. &Compare 1. 比较(&C) @@ -478,16 +478,16 @@ The command is triggered if: 两侧互换 Open - +打开 Save - +保存 Last used configurations (press DEL to remove from list) 最后使用的配置(按DEL键将其从列表中移除) Hide excluded items - +隐藏排除项目 Show filtered or temporarily excluded files 显示已被过滤或被临时排除的文件 @@ -547,13 +547,13 @@ The command is triggered if: 在第一个错误时中止同步 On completion - +在完成时 Show progress dialog 显示进度对话框 Save log - +保存日志 Select folder to save log files 选择要保存日志文件的文件夹 @@ -565,7 +565,7 @@ The command is triggered if: 限制日志文件的量大个数 Select variant - +选择不同方式 Files are found equal if @@ -604,7 +604,7 @@ is the same 确定 <- Two way -> - +<-双向-> Identify and propagate changes on both sides using a database. Deletions, renaming and conflicts are detected automatically. 使用一个数据库来识别和传播两边的改变. 删除,重命名和冲突会自动检测. @@ -640,7 +640,7 @@ is the same 回收站 Use Recycle Bin for deleted and overwritten files - +将删除和覆盖文件放到回收站 Versioning 保留历史版本 @@ -649,7 +649,7 @@ is the same 移动时间标记文件到指定的文件夹 Naming convention: - +命名规则: Configuration 配置 @@ -678,7 +678,7 @@ is the same &Pause 暂停(&P) -Source code written in C++ utilizing: +Source code written in C++ using: 源代码用如下C++工具写成: If you like FreeFileSync @@ -1026,7 +1026,7 @@ Note: File names must be relative to base directories! 正在比较文件内容... Copy - +复制 Paused 已暂停 @@ -1101,13 +1101,13 @@ Note: File names must be relative to base directories! - 只对文件夹部分 - Other side's counterpart to %item_path% - +- 另一边的对应项到 %item_path% - Other side's counterpart to %item_folder% - +- 另一边的对应项到 %item_folder% Make hidden warnings and dialogs visible again? - +重新让已经隐藏的警告和对话框变为可见? Do you really want to move the following object to the Recycle Bin? @@ -1129,22 +1129,22 @@ Note: File names must be relative to base directories! 遗留为未解决的冲突 Replace - +替换 Move files and replace if existing - +移动文件, 若文件已存在则替换 Append a timestamp to each file name - +附加时间戳到每一个文件名 Folder - +文件夹 File - +文件 YYYY-MM-DD hhmmss - +YYYY-MM-DD hhmmss Files 文件 @@ -1201,7 +1201,7 @@ Note: File names must be relative to base directories! 无法复制文件 %x 到 %y. Type of item %x is not supported: - +%x 的类型不被支持: Cannot open directory %x. 无法打开目录 %x. @@ -1375,7 +1375,7 @@ Note: File names must be relative to base directories! 正在移动符号连接 %x 到 %y Removing old versions... - +移除旧版本 Creating file %x 正在创建文件 %x @@ -1411,7 +1411,7 @@ Note: File names must be relative to base directories! 无法找到源文件夹 %x. The following items have unresolved conflicts and will not be synchronized: - +如下项目有无法解决的冲突并将不会被同步: Significant difference detected: 已侦测到显著不同: diff --git a/BUILD/Languages/chinese_traditional.lng b/BUILD/Languages/chinese_traditional.lng index 79391ed1..56f832e7 100644 --- a/BUILD/Languages/chinese_traditional.lng +++ b/BUILD/Languages/chinese_traditional.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... 中止請求:正在等待目前操作完成... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: 無法新建時間戳記的版本控制: RealtimeSync - Automated Synchronization @@ -669,7 +669,7 @@ is the same &Pause 暫停(&P) -Source code written in C++ utilizing: +Source code written in C++ using: 使用C++編寫的原始碼 If you like FreeFileSync diff --git a/BUILD/Languages/croatian.lng b/BUILD/Languages/croatian.lng index 1c5adb0d..fc01bb29 100644 --- a/BUILD/Languages/croatian.lng +++ b/BUILD/Languages/croatian.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... Prekid zahtjevan: čekam da se trenutna akcija završi... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: Nije moguća vremenska oznaka za varijantu RealtimeSync - Automated Synchronization @@ -684,7 +684,7 @@ jednak &Pause &Pauziraj -Source code written in C++ utilizing: +Source code written in C++ using: Izvorni kod napisan u C++ uz korištenje: If you like FreeFileSync diff --git a/BUILD/Languages/czech.lng b/BUILD/Languages/czech.lng index 4bc84b0a..dc0ee886 100644 --- a/BUILD/Languages/czech.lng +++ b/BUILD/Languages/czech.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... Požadavek na přerušení: Čekání na ukončení aktuální operace... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: Chyba při vytvoření časové značky verzování: RealtimeSync - Automated Synchronization @@ -684,7 +684,7 @@ je stejný &Pause &Pauza -Source code written in C++ utilizing: +Source code written in C++ using: Zdrojový kód byl napsán kompletně v C++ pomocí: If you like FreeFileSync diff --git a/BUILD/Languages/danish.lng b/BUILD/Languages/danish.lng index 225019ac..5fcf5ec0 100644 --- a/BUILD/Languages/danish.lng +++ b/BUILD/Languages/danish.lng @@ -1,6 +1,6 @@
    Dansk - Kim Monberg + Regmos da_DK denmark.png 2 @@ -11,61 +11,61 @@ Søger efter mappen %x... Items processed: -Enheder behandlet: +Emner behandlet: Items remaining: -Enheder tilbage: +Emner tilbage: Total time: Samlet tid: Show in Explorer -Vis i Explorer +Åben filplacering Open with default application -Åben med standard program +Åben med standardprogram Browse directory -Gennemse Bibliotek +Gennemse mappe Abort requested: Waiting for current operation to finish... -Afbrydelse: Venter på nuværende opgave afsluttes... +Afbrydelse: Venter på aktuel opgave afsluttes... -Failure to create time stamp for versioning: - +Failure to create timestamp for versioning: +Kunne ikke oprette versioneringstid: RealtimeSync - Automated Synchronization -RealtimeSynk - Automatisk Synkronisering +Realtids synk - automatisk synkronisering Error Fejl Select alternate comparison settings -Vælg alternative sammenlignings indstillinger +Tilpas analyse Select alternate synchronization settings -Vælg alternative indstillinger +Tilpas synkronisering Filter is active -Filter er aktivt +Aktivt filter No filter selected Intet filter valgt Remove alternate settings -Fjern alternative indstillinger +Fjern tilpassede indstillinger Clear filter settings -Ryd filter indstillinger +Nulstil filter Save as batch job - +Gem som batchfil Comparison settings -Sammenlignings indstillinger +Analyseindstillinger Synchronization settings -Synkroniserings indstillinger +Synkroniseringsindstillinger About Om @@ -77,13 +77,13 @@ Indstil filter Global settings -Fælles indstillinger +Programindstillinger Find -Find +Søg Select time span -Vælg tids område +Vælg tidsinterval Invalid command line: Ugyldig kommando: @@ -95,16 +95,16 @@ Advarsel Fatal Error -Uoprettelig Fejl +Fatal fejl Windows Error Code %x: -Windows Fejl kode %x: +Windows fejlkode %x: Linux Error Code %x: -Linux Fejl kode %x: +Linux fejlkode %x: Cannot resolve symbolic link %x. -Kan ikke følge linket %x. +Kan ikke følge symlinket %x. %x MB %x MB @@ -120,48 +120,48 @@ %x Bytes -1 Byte -%x Bytes +1 byte +%x bytes Database file %x is incompatible. -Database filen %x er ikke kompatibel. +Databasefilen %x er inkompatibel. Initial synchronization: -Indledende synkronisering: +Forbereder synkronisering: Database file %x does not yet exist. -Database filen %x findes ikke. +Databasefilen %x findes ikke. Database file is corrupt: -Database filen er ødelagt: +Databasefilen er ødelagt: Out of memory! -Ude af hukommelse! +Ikke nok hukommelse! Cannot write file %x. -Kan ikke skrive filen %x. +Kan ikke oprette filen %x. Cannot read file %x. Kan ikke læse filen %x. Database files do not share a common session. -Database filen kan ikke deles. +Databasefiler kan ikke dele handling. An exception occurred! -En undtagelse er opstået! +Undtagelse opstod! Cannot read file attributes of %x. -Kan ikke læse fil attributterne på %x. +Kan ikke læse filattributterne på %x. Cannot get process information. -Kan ikke lave informationer over processen. +Kan ikke hente procesinformation. Waiting while directory is locked (%x)... -Venter mens biblioteket er låst (%x)... +Venter mens mappe låses (%x)... Cannot set directory lock %x. -Kan ikke låse biblioteket %x. +Kan ikke låse mappen %x. 1 sec @@ -173,21 +173,21 @@ Error parsing file %x, row %y, column %z. -Fejl i videregivelse af filen %x, række %y, kolonne %z. +Behandlingsfejl i filen %x, række %y, kolonne %z. Scanning: Skanner: Encoding extended time information: %x -Finder udvidet tids information: %x +Opretter udvidet tidsinformation: %x [1 Thread] [%x Threads] -[1 Tråd] -[%x Tråde] +[1 tråd] +[%x tråde] /sec @@ -197,67 +197,67 @@ Kan ikke finde filen %x. File %x does not contain a valid configuration. -%x indeholder ikke en gyldig konfiguration. +%x indeholder ikke gyldige indstillinger. Configuration file %x loaded partially only. -Konfigurationsfilen %x er kun delvist hentet. +Indstillingsfilen %x er kun delvist indlæst. Cannot access Volume Shadow Copy Service. -Kan ikke få adgang til Drev Kopi Servicen. +Kan ikke opnå adgang til VVS. Please use FreeFileSync 64-bit version to create shadow copies on this system. -Brug venligst FreeFileSync 64-bit version for at lave en drev kopi på dette system. +Brug FreeFileSync 64-bit til at lave VVS kopier på dette system. Cannot load file %x. -Kan ikke læse filen %x. +Kan ikke indlæse filen %x. Path %x does not contain a volume name. -Stien %x indeholder ikke et drev navn. +Stien %x indeholder ikke et drevnavn. Volume name %x not part of file name %y! -Drev navn %x ikke en del af filnavn %y! +Drevnavn %x findes ikke i filnavnet %y! Cannot read the following XML elements: Kan ikke læse følgende XML emner: &Open... -&Åben... +Åben... Save &as... -Gem &som... +Gem som... &Quit -&Afslut +Afslut &Program -&Program +Filer &Content -&Indhold +Åben hjælp &About -&Om +Om &Help -&Hjælp +Hjælp Usage: -Forbrug: +Gør sådan: 1. Select folders to watch. -1. Vælg mappe at overvåge. +1. Vælg mapper til jobbet. 2. Enter a command line. -2. Skriv en kommando linje. +2. Angiv en kommando. 3. Press 'Start'. -3. Tryk 'Start'. +3. Klik 'Start'. To get started just import a .ffs_batch file. -Importer en .ffs_batch for at komme igang. +Importer en .ffs_batchfil (Filer > Åben...) for at komme igang. Folders to watch -Mapper der overvåges +Jobbets mapper Add folder Tilføj mappe @@ -272,13 +272,13 @@ Vælg en mappe Idle time [seconds] - +Køres når computeren har ledige resourcer i [sek.] Idle time between last detected change and execution of command -Tid imellem sidste ændring og udførelse +Tid imellem sidst fundne ændring og udførsel Command line -Kommando linje +Kommando The command is triggered if: @@ -288,65 +288,65 @@ The command is triggered if: Kommandoen udføres hvis: - filer eller undermapper ændres -- nye mapper ankommer (Hvis en USB nøgle sættes til) +- nye mapper findes (f.eks USB nøgle) Start Start &Retry -&Prøv igen +Prøv igen Cancel -Anuller +Annuller Build: %x -Build: %x +Udgivet: %x All files Alle filer &Restore -&Gendan +Vis vindue &Exit -&Afslut +Luk Monitoring active... -Overvågning aktiv... +Overvågning kører... Waiting for missing directories... -Venter på manglende biblioteker... +Venter på manglende mapper... A folder input field is empty. -Et mappe felt er tomt. +Der findes et udefineret mappevalg. Synchronization aborted! Synkronisering afbrudt! Synchronization completed with errors! -Synkronisering afsluttet med fejl! +Synkronisering gennemført med fejl! Synchronization completed with warnings. - +Synkronisering gennemført med advarsel Nothing to synchronize! -Intet at synkronisere! +Alt er synkroniseret! Synchronization completed successfully. - +Synkronisering gennemført Saving log file %x... - +Gemmer rapport %x... Press "Switch" to resolve issues in FreeFileSync main dialog. -Tryk "Skift" for at løse problemer i hoved vinduet. +Klik "Skift" for at løse problemerne fra hovedvinduet. Switching to FreeFileSync main dialog... -Skift til hoved vinduet... +Skifter til hovedvindue... A new version of FreeFileSync is available: -En ny version af FreeFilesyns er tilgængelig: +Opdatering tilgængelig: Download now? Download nu? @@ -358,16 +358,16 @@ Kommandoen udføres hvis: Information Unable to connect to sourceforge.net! -Kan ikke forbinde til sourceforge.net! +Kan ikke kontakte sourceforge.net! Current FreeFileSync version number was not found online! Do you want to check manually? - +Den aktuelle version blev ikke fundet! Vil du kontrollere manuelt Do you want FreeFileSync to automatically check for updates every week? -Skal FreeFileSync automatisk checke efter opdateringer hver uge? +Skal FreeFileSync søge efter opdateringer en gang om ugen? (Requires an Internet connection!) -(Kræver en Internet forbindelse!) +(kræver internetforbindelse!) @@ -376,16 +376,16 @@ Kommandoen udføres hvis: Full path -Fuld Sti +Fuld sti Name Navn Relative path -Filsti +Relativ sti Base folder -Grund mappe +Hovedmappe Size Størrelse @@ -394,10 +394,10 @@ Kommandoen udføres hvis: Dato Extension -Udvidelse +Filtype Size: -Størelse: +Størrelse: Date: Dato: @@ -406,13 +406,13 @@ Kommandoen udføres hvis: Handling Category -Kategori +Status Drag && drop -Træk && slip +Træk emner hertil Close progress dialog -Luk dialog boksen +Luk dialogen Standby Standby @@ -427,43 +427,43 @@ Kommandoen udføres hvis: Dvale &New -&Ny +Ny &Save -&Gem +Gem Save as &batch job... - +Gem som batchfil 1. &Compare -1. &Sammenlign +1. Analyser 2. &Synchronize -2. &Synkroniser +2. Synkroniser &Language -&Sprog +Sprog &Global settings... -&Fælles indstillinger... +Programindstillinger... &Export file list... -&Eksporter fil liste... +Eksporter filliste... &Advanced -&Avanceret +Indstillinger &Check for new version -&Kig efter ny version +Søg efter opdatering Compare -Sammenlign +Analyser Compare both sides -Sammenlign begge sider +Analyser begge sider &Abort -&Afbryd +Afbryd Synchronize Synkroniser @@ -472,103 +472,103 @@ Kommandoen udføres hvis: Start synkronisering Add folder pair -Tilføj mappe par +Tilføj mappepar Remove folder pair -Fjern mappe par +Fjern mappepar Swap sides -Byt sider +Byt side Open - +Åben Save - +Gem job Last used configurations (press DEL to remove from list) -Senest brugte konfigurationer (tryk DEL for at fjerne fra listen) +Senest brugte konfigurationer (tast DEL for at slette) Hide excluded items - +Skjul ekskluderede emner Show filtered or temporarily excluded files -Vis filtrerede eller midlertidigt afviste filer +Vis filtrerede eller midlertidigt ekskluderede filer Number of files and folders that will be created -Antallet af filer og mapper der vil blive oprettet +Antal filer og mapper der oprettes Number of files that will be overwritten -Antal filer der vil blive overskrevet +Antal filer der overskrives Number of files and folders that will be deleted -Antal filer og mapper der vil blive slettet +Antal filer og mapper der slettes Total bytes to copy -Samlet antal bytes der skal kopieres +Antal bytes der kopieres Items found: -Enheder fundet: +Emner fundet: Speed: Hastighed: Time remaining: -Tid tilbage: +Resterende tid: Time elapsed: -Tid forbrugt: +Brugt tid: Batch job -Batch job +Batchfil Create a batch file to automate synchronization. Double-click this file or schedule in your system's task planner: FreeFileSync.exe .ffs_batch -Opret en batch fil for at automatiserer synkronisering. Dobbelt-klik på denne fil eller planlæg den i dit systems opgavestyring: FreeFileSync.exe .ffs_batch +Opret en batchfil til automatisk synkronisering. Dobbeltklik på filen eller brug systemets opgavestyring: FreeFileSync.exe .ffs_batch Help Hjælp Error handling -Fejl håndtering +Fejlhåndtering Ignore Ignorer Hide all error and warning messages -Skjul beskeder om fejl og advarsler +Skjul fejlbeskeder og advarsler Pop-up -Pop-up +Besked Show pop-up on errors or warnings -Vis pop-up ved fejl eller advarsler +Vis fejlbeskeder og advarsler Exit -Afslut +Luk Abort synchronization on first error Stop synkronisering ved første fejl On completion - +Ved gennemført Show progress dialog -Vis dialog boks +Vis fremskridt Save log - +Gem rapport Select folder to save log files -Vælg destinations mappe til log filer +Vælg mappe til rapporter Limit -Grænse +Antal Limit maximum number of log files -Maksimale antal log filer +Begræns antal rapporter Select variant - +Vælg type Files are found equal if @@ -577,14 +577,14 @@ Files are found equal if are the same -Filer bliver set som ens hvis - - sidst skrevne tid og dato - - fil størrelse -er ens +Filer betragtes som ens hvis + - senest ændret + - filstørrelse +er identisk File time and size -Fil tid og størrelse +Størrelse og tid Files are found equal if @@ -592,46 +592,46 @@ Files are found equal if is the same -Filer er ens hvis - - fil indhold -er det samme +Filer betragtes som ens hvis + - filindhold +er identisk File content -Fil indhold +Indhold Symbolic Link handling -Link håndtering +Symlink håndtering OK OK <- Two way -> - +< Tovejs > Identify and propagate changes on both sides using a database. Deletions, renaming and conflicts are detected automatically. -find ændringer på begge sider via. database. Sletninger, omdøbninger og konflikter bliver automatisk fundet. +Find og udbred ændringer på begge sider via en database. Sletninger, omdøbninger og konflikter opdages automatisk. Mirror ->> -Spejl ->> +Spejling >> Mirror backup of left folder. Right folder is modified to exactly match left folder after synchronization. -Spejling af venstre mappe. Højre mappe tilpasses så den ligner den venstre efter synkronisering. +Spejl venstre mappe. Efter synkronisering er højre mappe en kopi af venstre. Update -> -Opdater -> +Opdater > Copy new or updated files to right folder. -Kopier nye eller opdaterede filer til højre mappe. +Kopier nye og opdaterede filer til højre mappe. Custom -Brugerdefineret +Tilpasset Configure your own synchronization rules. -Konfigurer dine egne synkroniserings regler. +Opret dine egne synkroniseringsregler. Deletion handling -Slette håndtering +Sletning Permanent Permanent @@ -640,61 +640,61 @@ er det samme Slet eller overskriv filer permanent Recycle Bin -Skralsespand +Papirkurv Use Recycle Bin for deleted and overwritten files - +Slettede og overskrevne filer flyttes til papirkurv Versioning Versionering Move time-stamped files into specified folder -Flyt tids-stemplede filer til den valgte mappe +Flyt tidsstemplede filer til valgte mappe Naming convention: - +Navneregler: Configuration -Konfiguration +Indstilling Item exists on left side only -Enheden findes kun på venstre side +Emnet findes kun på venstre side Item exists on right side only -Enheden findes kun på højre side +Emnet findes kun på højre side Left side is newer -Venstre side er nyest +Venstre er nyest Right side is newer -Højre side er nyest +Højre er nyest Items have different content -Enhederne har forskelligt indhold +Emnerne har forskelligt indhold Conflict/item cannot be categorized -Advarsel/enheden kan ikke kategoriseres +Konflikt/ukendt emne Synchronizing... Synkroniserer... &Pause -&Pause +Pause -Source code written in C++ utilizing: -Source code skrevet i C++: +Source code written in C++ using: +Kildekoden er skrevet i C++ med hjælp fra: If you like FreeFileSync -Hvis du kan lide FreeFileSync +Er du glad for FreeFileSync Donate with PayPal -Doner med PayPal +Donér med PayPal Many thanks for localization: -Mange tak for oversættelse til: +Tak for oversættelse: Feedback and suggestions are welcome -Feedback og forslag er velkomne +Kritik og forslag er meget velkomne Homepage Hjemmeside @@ -703,7 +703,7 @@ er det samme FreeFileSync på Sourceforge Email -E-mail +Email Published under the GNU General Public License Udgivet under GNU General Public Licence @@ -712,28 +712,28 @@ er det samme Slet på begge sider Delete on both sides even if the file is selected on one side only -Slet på begge sider selvom der kun er valgt en side +Slet på begge sider selvom selvom filen kun er valgt på en side Only files that match all filter settings will be synchronized. Note: File names must be relative to base directories! -Synkroniserer kun filer der passer alle filtre. -Note: Fil navne må skal passe til grund bibliotekerne! +Synkroniserer kun filer der opfylder alle betingelser. +Bemærk: Filnavne skal relatere til grundmapperne! Include Inkluder Exclude -Udeluk +Ekskluder Time span -Tidsrum +Tidsinterval File size -Fil Størrelse +Filstørrelse Minimum Minimum @@ -742,25 +742,25 @@ Note: Fil navne må skal passe til grund bibliotekerne! Maksimum &Default -&Standard +Standard Fail-safe file copy -Fejlsikret fil kopiering +Sikker filkopiering Write to a temporary file (*.ffs_tmp) first then rename it. This guarantees a consistent state even in case of fatal error. -Skriv til en midlertidig fil (*.ffs_tmp) først så omdøb den. Dette garanterer sikkerheden selv ved en kritisk fejl. +Skriv først til en midlertidig fil (*.ffs_tmp) og omdøb derefter. Dette garanterer sikkerheden selv ved fatale fejl. Copy locked files Kopier låste filer Copy shared or locked files using Volume Shadow Copy Service (Requires Administrator rights) -Kopier delte eller låste filer ved hjælp af Drev Kopi Servicen (Kræver Administrator rettigheder) +Kopier delte eller låste filer ved hjælp af VVS (kræver administrative rettigheder) Copy file access permissions -Kopier fil adgangs tilladelser +Kopier adgangstilladelser Transfer file and folder permissions (Requires Administrator rights) -Overfør fil og mappe tilladelser (Kræver Administrator rettigheder) +Overfør fil- og mappetilladelser (kræver administrative rettigheder) Restore hidden dialogs Gendan skjulte dialoger @@ -772,43 +772,43 @@ Note: Fil navne må skal passe til grund bibliotekerne! Beskrivelse Variant -Variation +Jobtype Statistics -Statestik +Statistik Don't show this dialog again -Vis ikke denne dialog boks igen +Vis ikke igen Find what: -Find: +Søg efter: Match case -Sammenlign stor og små bogstaver +Versalfølsom (a/A) &Find next -&Find næste +Find næste Operation aborted! -Operation afbrudt! +Handling afbrudt! Main bar -Hoved værktøjslinjen +Hovedlinie Folder pairs -Mappe par +Mappepar Overview -Overblik +Oversigt Filter files -Filter filer +Filtre Select view -Vælg udseende +Tilpas visning Set direction: -Sæt handlevejen: +Retning: Exclude temporarily Ekskluder midlertidigt @@ -817,10 +817,10 @@ Note: Fil navne må skal passe til grund bibliotekerne! Inkluder midlertidigt Exclude via filter: -Ekskluder via filter: +Ekskluder m. filter: - + Delete Slet @@ -835,19 +835,19 @@ Note: Fil navne må skal passe til grund bibliotekerne! Vis ikoner: Small -Lille +Små Medium -Mellem +Medium Large -Stor +Store Select time span... -Vælg tidsområde... +Vælg tidsinterval... Default view -Standard tilstand +Standardvisning Show "%x" Vis "%x" @@ -856,28 +856,28 @@ Note: Fil navne må skal passe til grund bibliotekerne! Folder Comparison and Synchronization -Mappe sammenligning og synkronisering +Mappeanalyse og synkronisering Configuration saved! -Konfiguration gemt! +Indstillinger gemt! FreeFileSync batch -FreeFileSync batch +FreeFileSync batchfil Never save changes -Gem aldrig ændringer +Gem aldrig Do you want to save changes to %x? -Ønsker du at gemme ændringerne til %x? +Vil du gemme ændringer i %x? Do&n't save Gem ikke Configuration loaded! -Konfiguration hentet! +Indstillinger indlæst! Hide files that exist on left side only -Skjul filder der kun findes på venstre side +Skjul filer der kun findes på venstre side Show files that exist on left side only Vis filer der kun findes på venstre side @@ -889,16 +889,16 @@ Note: Fil navne må skal passe til grund bibliotekerne! Vis filer der kun findes på højre side Hide files that are newer on left -Skjul filer der er nyere til venstre +Skjul nyere filer på venstre side Show files that are newer on left -Vis filer der er nyere til venstre +Vis nyere filer på venstre side Hide files that are newer on right -Skjul filer der er nyere til højre +Skjul nyere filer på højre side Show files that are newer on right -Vis filer der er nyere til højre +Vis nyere filer på højre side Hide files that are equal Skjul ens filer @@ -919,66 +919,66 @@ Note: Fil navne må skal passe til grund bibliotekerne! Vis konflikter Hide files that will be created on the left side -Skjul filer der vil blive oprettet til venstre +Skjul filer der oprettes på venstre side Show files that will be created on the left side -Vis filer der vil blive oprettet til venstre +Vis filer der oprettes på venstre side Hide files that will be created on the right side -Skjul filer der vil blive oprettet til højre +Skjul filer der oprettes på højre side Show files that will be created on the right side -Vis filer der vil blive oprettet til højre +Vis filer der oprettes på højre side Hide files that will be deleted on the left side -Skjul filer der bliver slettet til venstre +Skjul filer der slettes på venstre side Show files that will be deleted on the left side -Vis filer der bliver slettet til venstre +Vis filer der slettes på venstre side Hide files that will be deleted on the right side -Skjul filer der bliver slettet til højre +Skjul filer der slettes på højre side Show files that will be deleted on the right side -Vis filer der bliver slettet til højre +Vis filer der slettes på højre side Hide files that will be overwritten on left side -Skjul filer der bliver overskrevet til venstre +Skjul filer der overskrives på venstre side Show files that will be overwritten on left side -Vis filer der bliver overskrevet til venstre +Vis filer der overskrives på venstre side Hide files that will be overwritten on right side -Skjul filer der bliver overskrevet til højre +Skjul filer der overskrives på højre side Show files that will be overwritten on right side -Vis filer der bliver overskrevet til højre +Vis filer der overskrives på højre side Hide files that won't be copied -Skjul filer der ikke bliver kopieret +Skjul filer der ikke kopieres Show files that won't be copied -Vis filer der ikke bliver kopieret +Vis filer der ikke kopieres All folders are in sync! -Alle mapper er synkroniserede +Alle mapper er synkrone Comma separated list -Komma separeret list +Kommaopdelt liste Legend -Legend +Definitioner File list exported! -Fil listen er eksporteret! +Fillisten blev eksporteret! Object deleted successfully! %x objects deleted successfully! -Emnet er slettet! -%x emner er slettet! +Emnet blev slettet! +%x emner blev slettet! @@ -986,8 +986,8 @@ Note: Fil navne må skal passe til grund bibliotekerne! %x directories -1 bibliotek -%x biblioteker +1 mappe +%x mapper @@ -1012,40 +1012,40 @@ Note: Fil navne må skal passe til grund bibliotekerne! Ignorer fremtidige fejl &Ignore -&Ignorer +Ignorer &Switch -&Skift +Skift Question Spørgsmål &Yes -&Ja +Ja &No -&Nej +Nej Scanning... Skanner... Comparing content... -Sammenligner indhold... +Analyserer indhold... Copy - +Kopier Paused Pauset Initializing... -Initialiserer... +Forbereder... Aborted Afbrudt Completed -Fuldført +Gennemført Continue Fortsæt @@ -1054,7 +1054,7 @@ Note: Fil navne må skal passe til grund bibliotekerne! Pause Logging -Logger +Rapporter Cannot find %x Kan ikke finde %x @@ -1099,30 +1099,30 @@ Note: Fil navne må skal passe til grund bibliotekerne! Kopier NTFS tilladelser Integrate external applications into context menu. The following macros are available: -Integrere eksterne programme. Følgende macroer er mulige: +Integrer eksterne programmer i kontekstmenu. Brug følgende macroer: - full file or folder name -- fulde fil eller mappe navn +Komplet fil- eller mappenavn - folder part only -- kun mappe delen +Kun mappedelen - Other side's counterpart to %item_path% - +Modsat sides pendant til %item_path% - Other side's counterpart to %item_folder% - +Modsat sides pendant til %item_folder% Make hidden warnings and dialogs visible again? - +Vis skjulte advarsler og dialoger igen? Do you really want to move the following object to the Recycle Bin? Do you really want to move the following %x objects to the Recycle Bin? -Vil du flytte emnet til skraldespanden? -Vil du flytte følgende %x emner til skraldespanden? +Vil du flytte emnet til papirkurven? +Vil du flytte følgende %x emner til papirkurven? @@ -1135,25 +1135,25 @@ Note: Fil navne må skal passe til grund bibliotekerne! Leave as unresolved conflict -Efterlad som uløste konflikter +Efterlad som uløst konflikt Replace - +Erstat Move files and replace if existing - +Flyt fil og erstat eventuelt eksisterende Append a timestamp to each file name - +Føj tidsstempel til hvert filnavn Folder - +Mappe File - +Fil YYYY-MM-DD hhmmss - +YYYY-MM-DD hhmmss Files Filer @@ -1162,10 +1162,10 @@ Note: Fil navne må skal passe til grund bibliotekerne! Procent Cannot monitor directory %x. -Kan ikke overvåge biblioteket %x. +Kan ikke overvåge mappen %x. Conversion error: -Konverterings fejl: +Konverteringsfejl: Cannot delete file %x. Kan ikke slette filen %x. @@ -1177,22 +1177,22 @@ Note: Fil navne må skal passe til grund bibliotekerne! Kan ikke flytte filen %x til %y. Cannot delete directory %x. -Kan ikke slette biblioteket %x. +Kan ikke slette mappen %x. Cannot write file attributes of %x. -Kan ikke skrive fil attributter til %x. +Kan ikke skrive filattributter til %x. Cannot write modification time of %x. -Kan ikke opdatere ændringstiden på %x. +Kan ikke opdatere tidsændring på %x. Cannot find system function %x. Kan ikke finde systemfunktionen %x. Cannot read security context of %x. -Kan ikke læse sikkerhedsindstillingerne på %x. +Kan ikke læse sikkerhedsindstillinger på %x. Cannot write security context of %x. -Kan ikke skrive sikkerhedsindstillingerne på %x. +Kan ikke skrive sikkerhedsindstillinger til %x. Cannot read permissions of %x. Kan ikke læse tilladelserne på %x. @@ -1201,25 +1201,25 @@ Note: Fil navne må skal passe til grund bibliotekerne! Kan ikke skrive tilladelserne til %x. Cannot create directory %x. -Kan ikke oprette biblioteket %x. +Kan ikke oprette mappen %x. Cannot copy symbolic link %x to %y. -Kan ikke kopiere linket %x til %y. +Kan ikke kopiere symlinket %x til %y. Cannot copy file %x to %y. Kan ikke kopiere filen %x til %y. Type of item %x is not supported: - +Filtypen %x understøttes ikke: Cannot open directory %x. -Kan ikke åbne bibliotek %x. +Kan ikke åbne mappen %x. Cannot enumerate directory %x. -Kan ikke optælle biblioteket %x. +Kan ikke optælle mappen %x. Detected endless directory recursion. -Opdaget en uendelig biblioteks løkke. +Uendelig mappegentagelse fundet. %x TB %x TB @@ -1244,7 +1244,7 @@ Note: Fil navne må skal passe til grund bibliotekerne! %x hours -1 timer +1 time %x timer @@ -1258,46 +1258,46 @@ Note: Fil navne må skal passe til grund bibliotekerne! Cannot set privilege %x. -Kan ikke sætte privilegier til %x. +Kan ikke sætte %x privilegier. Unable to move %x to the Recycle Bin! -Ude af stand til at flytte %x til skraldespanden! +Kan ikke flytte %x til papirkurv! Both sides have changed since last synchronization! -Begge sider er ændret siden sidste synkronisering! +Begge sider ændret siden sidste synkronisering! Cannot determine sync-direction: -Kan ikke bestemme synkroniseringsvej: +Kan ikke bestemme retning: No change since last synchronization! Ingen ændringer siden sidste synkronisering! The corresponding database entries are not in sync considering current settings. -Databasen er ikke synkroniseret i forhold til nuværende indstillinger. +Databasen er ikke synkroniseret i forhold til aktuelle indstillinger. Setting default synchronization directions: Old files will be overwritten with newer files. -Sætter standard synkroniseringsvej: Gamle filer bliver overskrevet med nyere. +Sætter standardretning: Gamle filer overskrives med nyere. Recycle Bin is not available for the following paths! Files will be deleted permanently instead: -Papirkurven kan ikke bruges på følgende stier! Filerne vil blive slettet permanent i stedet for: +Papirkurven kan ikke bruges på følgende stier! Filer slettes permanent: You can ignore this error to consider the folder as empty. -Du kan ignorere denne fejl og betegne mappen som tom. +Du kan ignorere denne fejl og betragte mappen som tom. Cannot find folder %x. Kan ikke finde mappen %x. Directories are dependent! Be careful when setting up synchronization rules: -Biblioteker er afhængige! Vær forsigtig når du laver synkroniserings reglerne: +Afhængige mapper! Vær forsigtig når du laver synkroniseringsregler: Start comparison -Start sammenligning +Start analyse Preparing synchronization... Forbereder synkronisering... Conflict detected: -Konflik fundet: +Konflikt fundet: File %x has an invalid date! Filen %x har en ugyldig dato! @@ -1306,28 +1306,28 @@ Note: Fil navne må skal passe til grund bibliotekerne! Filerne %x har den samme dato men forskellig størrelse! Items differ in attributes only -Enheder der kun har attributter til forskel +Enhederne har kun attributter til forskel Symbolic links %x have the same date but a different target. -Linksene %x har samme dato men forskellige mål. +Symlinkene %x har samme dato men forskellige mål. Comparing content of files %x Sammenligner indhold af filer %x Comparing files by content failed. -Fejl i sammenligning af filernes indhold. +Fejl ved analyse af filindhold. Generating file list... -Laver fil liste... +Opretter filliste... Both sides are equal Begge sider er ens Copy new item to left -Kopiere nyt emne mod venstre +Kopier nyt emne mod venstre Copy new item to right -Kopiere nyt emne mod højre +Kopier nyt emne mod højre Delete left item Slet emne til venstre @@ -1342,10 +1342,10 @@ Note: Fil navne må skal passe til grund bibliotekerne! flyt filen til højre Overwrite left item -Overskriv emne til venstre +Overskriv venstre emne Overwrite right item -Overskriv emne til højre +Overskriv højre emne Do nothing Gør intet @@ -1360,22 +1360,22 @@ Note: Fil navne må skal passe til grund bibliotekerne! Flere... Deleting file %x -Sletter fil %x +Sletter filen %x Deleting folder %x -Sletter mappe %x +Sletter mappen %x Deleting symbolic link %x -Sletter symbolks link %x +Sletter symlinket %x Moving file %x to recycle bin -Flytter fil %x til papirkurven +Flytter filen %x til papirkurven Moving folder %x to recycle bin Flytter mappe %x til papirkurven Moving symbolic link %x to recycle bin -Flytter link %x til papirkurven +Flytter symlinket %x til papirkurven Moving file %x to %y Flytter filen %x til %y @@ -1384,52 +1384,52 @@ Note: Fil navne må skal passe til grund bibliotekerne! Flytter mappen %x til %y Moving symbolic link %x to %y -Flytter linket %x til %y +Flytter symlinket %x til %y Removing old versions... - +Fjerner gamle udgaver... Creating file %x Opretter filen %x Creating symbolic link %x -Opretter linket %x +Opretter symlinket %x Creating folder %x -Opretter mappe %x +Opretter mappen %x Overwriting file %x Overskriver filen %x Overwriting symbolic link %x -Overskriver linket %x +Overskriver symlinket %x Verifying file %x -Verificerer fil %x +Verificerer filen %x Updating attributes of %x -Opdaterer attributter af %x +Opdaterer attributter for %x Target folder %x already existing. -Destinations mappen %x eksisterer allerede. +Destinationsmappen %x findes allerede. Target folder input field must not be empty. -Destinations mappe feltet må ikke være tomt. +Destinationsmappen skal angives. Folder input field for versioning must not be empty. -Mappe input feltet for versionering må ikke være tomt. +Mappe til versionering skal angives. Source folder %x not found. -Kilde mappen %x blev ikke fundet. +Kildemappen %x blev ikke fundet. The following items have unresolved conflicts and will not be synchronized: - +Følgende emner har uløste konflikter og synkroniseres ikke: Significant difference detected: -Betydelig forskel fundet: +Markant forskel: More than 50% of the total number of files will be copied or deleted! -Mere end 50% af det samlede antal filer vil blive kopieret eller slettet! +Mere end 50% af af filerne kopieres eller slettes! Not enough free disk space available in: Ikke nok ledig diskplads på: @@ -1441,7 +1441,7 @@ Note: Fil navne må skal passe til grund bibliotekerne! Tilgængeligt: A folder will be modified which is part of multiple folder pairs. Please review synchronization settings. -En mappe som er en del af flere mappe parringer vil blive ændret. Gennemse venligst synkroniserings indstillingerne. +Der ændres en mappe der tilhører flere mappepar. Kontroller dine synkroniseringsindstillinger. Left Venstre @@ -1450,11 +1450,11 @@ Note: Fil navne må skal passe til grund bibliotekerne! Højre Synchronizing folder pair: -Synkroniserer mappe par: +Synkroniserer mappepar: Generating database... Opretter database... Data verification error: Source and target file have different content! -Data godkendelses fejl: Kilde og destinations fil har forskelligt indhold! +Godkendelsesfejl: Kilde- og destinationsfil har forskelligt indhold! diff --git a/BUILD/Languages/dutch.lng b/BUILD/Languages/dutch.lng index afe5ce0e..0aff2993 100644 --- a/BUILD/Languages/dutch.lng +++ b/BUILD/Languages/dutch.lng @@ -31,8 +31,8 @@ Abort requested: Waiting for current operation to finish... Bezig met afbreken: Wacht op beëindiging huidige bewerking... -Failure to create time stamp for versioning: - +Failure to create timestamp for versioning: +Het creëren van een tijdsstempen voor versiebeheer is mislukt: RealtimeSync - Automated Synchronization RealtimeSync - Geautomatiseerde Synchronisatie @@ -59,7 +59,7 @@ Verwijder filterinstellingen Save as batch job - +Opslaan als batch opdracht Comparison settings Vergelijksinstellingen @@ -272,7 +272,7 @@ Selecteer een map Idle time [seconds] - +Inactiviteit [seconden] Idle time between last detected change and execution of command Tijd tussen de laatste gedetecteerde verandering en de uitvoering van het commando @@ -328,16 +328,16 @@ De opdracht word geactiveerd als: Synchronisatie is met fouten afgerond! Synchronization completed with warnings. - +Synchronisatie afgerond met waarschuwingen. Nothing to synchronize! Niets om te synchroniseren! Synchronization completed successfully. - +Synchronisatie succesvol. Saving log file %x... - +Opslaan van logbestand %x... Press "Switch" to resolve issues in FreeFileSync main dialog. Druk op "Omschakelen" om problemen op te lossen in het hoofdscherm van FreeFileSync. @@ -361,7 +361,7 @@ De opdracht word geactiveerd als: Kan geen verbinding maken met sourceforge.net! Current FreeFileSync version number was not found online! Do you want to check manually? - +Het huidige FreeFileSync versienummer is niet online gevonden! Wilt u handmatig checken? Do you want FreeFileSync to automatically check for updates every week? Wilt u FreeFileSync elke week automatisch laten controleren of er een nieuwe versie is? @@ -433,7 +433,7 @@ De opdracht word geactiveerd als: O&pslaan Save as &batch job... - +Opslaan als &batch opdracht... 1. &Compare 1. &Vergelijk @@ -481,16 +481,16 @@ De opdracht word geactiveerd als: Wissel zijdes Open - +Open Save - +Opslaan Last used configurations (press DEL to remove from list) Laatst gebruikte instellingen (druk op DEL om iets te verwijderen) Hide excluded items - +Verberg uitgesloten bestanden Show filtered or temporarily excluded files Laat gefilterde of tijdelijk uitgesloten bestanden zien @@ -550,13 +550,13 @@ De opdracht word geactiveerd als: Synchronisatie stoppen bij eerste foutmelding On completion - +Na voltooiing Show progress dialog Toon voortgangsdialoogvenster Save log - +Logbestand opslaan Select folder to save log files Selecteer map om log bestanden op te slaan @@ -568,7 +568,7 @@ De opdracht word geactiveerd als: Limiteer maximaal aantal log bestanden Select variant - +Selecteer variant Files are found equal if @@ -607,7 +607,7 @@ overeenkomt OK <- Two way -> - +<- Twee kanten op -> Identify and propagate changes on both sides using a database. Deletions, renaming and conflicts are detected automatically. Identificeer en verspreid veranderingen aan beide kanten met behulp van een database. Verwijderingen, hernoemingen en conflicten worden automatisch gedetecteerd. @@ -643,7 +643,7 @@ overeenkomt Prullenbak Use Recycle Bin for deleted and overwritten files - +Gebruik prullenbak voor verwijderde en overschreven bestanden Versioning Versiebeheer @@ -652,7 +652,7 @@ overeenkomt Verplaats bestanden met tijdstempel naar specifieke map Naming convention: - +Naamgevingsconventie Configuration Configuratie @@ -681,7 +681,7 @@ overeenkomt &Pause &Pauze -Source code written in C++ utilizing: +Source code written in C++ using: Broncode geschreven in C++ met behulp van: If you like FreeFileSync @@ -960,6 +960,9 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! Show files that won't be copied Toon bestanden die niet gekopiëerd zullen worden +Set as default + + All folders are in sync! Alle mappen zijn gesynchroniseerd! @@ -1033,7 +1036,7 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! Inhoud vergelijken... Copy - +Kopiëren Paused Gepauzeerd @@ -1108,13 +1111,13 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! - alleen het map gedeelte - Other side's counterpart to %item_path% - +- Tegenhanger van de andere kant naar %item_path% - Other side's counterpart to %item_folder% - +- Tegenhanger van de andere kant naar %item_folder% Make hidden warnings and dialogs visible again? - +Verborgen waarschuwingen en dialogen zichtbaar maken? Do you really want to move the following object to the Recycle Bin? @@ -1138,22 +1141,22 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! Beschouw als onopgelost conflict Replace - +Vervangen Move files and replace if existing - +Verplaats bestanden en overschrijf bestaande bestanden Append a timestamp to each file name - +Voeg een timestamp aan elke bestandsnaam toe Folder - +Map File - +Bestand YYYY-MM-DD hhmmss - +JJJJ-MM-DD hhmmss Files Bestanden @@ -1210,7 +1213,7 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! Kan bestand %x niet kopiëren naar %y. Type of item %x is not supported: - +Type van bestand %x is niet ondersteund: Cannot open directory %x. Kan map %x niet openen. @@ -1387,7 +1390,7 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! Bezig met verplaatsen van snelkoppeling %x naar %y Removing old versions... - +Bezig met verwijderen van oude versies... Creating file %x Bestand %x wordt aangemaakt @@ -1423,7 +1426,7 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! Bronmap %x niet gevonden. The following items have unresolved conflicts and will not be synchronized: - +De volgende items hebben onopgeloste conflicten en zullen niet worden gesynchroniseerd: Significant difference detected: Significant verschil gedetecteerd: diff --git a/BUILD/Languages/english_uk.lng b/BUILD/Languages/english_uk.lng index 35ee5f16..fc15752f 100644 --- a/BUILD/Languages/english_uk.lng +++ b/BUILD/Languages/english_uk.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... Abort requested: Waiting for current operation to finish... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: Failure to create time stamp for versioning: RealtimeSync - Automated Synchronization @@ -459,18 +459,12 @@ The command is triggered if: Compare Compare -Compare both sides -Compare both sides - &Abort &Abort Synchronize Synchronise -Start synchronization -Start synchronisation - Add folder pair Add folder pair @@ -480,15 +474,6 @@ The command is triggered if: Swap sides Swap sides -Open -Open - -Save -Save - -Last used configurations (press DEL to remove from list) -Last used configurations (press DEL to remove from list) - Hide excluded items Hide excluded items @@ -681,8 +666,8 @@ is the same &Pause &Pause -Source code written in C++ utilizing: -Source code written in C++ utilising: +Source code written in C++ using: +Source code written in C++ using: If you like FreeFileSync If you like FreeFileSync @@ -771,6 +756,9 @@ Note: File names must be relative to base directories! Description Description +Start synchronization +Start synchronisation + Variant Variant @@ -807,6 +795,15 @@ Note: File names must be relative to base directories! Select view Select view +Open... +Open... + +Save +Save + +Compare both sides +Compare both sides + Set direction: Set direction: @@ -960,6 +957,9 @@ Note: File names must be relative to base directories! Show files that won't be copied Show files that won't be copied +Set as default +Set as default + All folders are in sync! All folders are in sync! @@ -972,15 +972,6 @@ Note: File names must be relative to base directories! File list exported! File list exported! - -Object deleted successfully! -%x objects deleted successfully! - - -Object deleted successfully! -%x objects deleted successfully! - - 1 directory %x directories @@ -1137,15 +1128,15 @@ Note: File names must be relative to base directories! Leave as unresolved conflict Leave as unresolved conflict +Append a timestamp to each file name +Append a timestamp to each file name + Replace Replace Move files and replace if existing Move files and replace if existing -Append a timestamp to each file name -Append a timestamp to each file name - Folder Folder @@ -1278,6 +1269,27 @@ Note: File names must be relative to base directories! Setting default synchronization directions: Old files will be overwritten with newer files. Setting default synchronisation directions: Old files will be overwritten with newer files. +Checking recycle bin availability for folder %x... +Checking recycle bin availability for folder %x... + +Moving file %x to recycle bin +Moving file %x to recycle bin + +Moving folder %x to recycle bin +Moving folder %x to recycle bin + +Moving symbolic link %x to recycle bin +Moving symbolic link %x to recycle bin + +Deleting file %x +Deleting file %x + +Deleting folder %x +Deleting folder %x + +Deleting symbolic link %x +Deleting symbolic link %x + Recycle Bin is not available for the following paths! Files will be deleted permanently instead: Recycle Bin is not available for the following paths! Files will be deleted permanently instead: @@ -1359,24 +1371,6 @@ Note: File names must be relative to base directories! Multiple... Multiple... -Deleting file %x -Deleting file %x - -Deleting folder %x -Deleting folder %x - -Deleting symbolic link %x -Deleting symbolic link %x - -Moving file %x to recycle bin -Moving file %x to recycle bin - -Moving folder %x to recycle bin -Moving folder %x to recycle bin - -Moving symbolic link %x to recycle bin -Moving symbolic link %x to recycle bin - Moving file %x to %y Moving file %x to %y diff --git a/BUILD/Languages/finnish.lng b/BUILD/Languages/finnish.lng index b4115ad9..f5db927c 100644 --- a/BUILD/Languages/finnish.lng +++ b/BUILD/Languages/finnish.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... Keskeytys pyydetty: Odotetaan toiminnon loppumista... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: Ongelma luoda aikaleimaa versionnissa: RealtimeSync - Automated Synchronization @@ -681,7 +681,7 @@ on sama &Pause &Keskeytä -Source code written in C++ utilizing: +Source code written in C++ using: Koodikieli on C++ käyttäen: If you like FreeFileSync diff --git a/BUILD/Languages/french.lng b/BUILD/Languages/french.lng index 20fddc36..7d8e8a4f 100644 --- a/BUILD/Languages/french.lng +++ b/BUILD/Languages/french.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... Abandon demandé : En attente de la fin de l'opération en cours... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: Impossible de créer l'horodatage des versions RealtimeSync - Automated Synchronization @@ -681,7 +681,7 @@ est identique &Pause &Pause -Source code written in C++ utilizing: +Source code written in C++ using: Code source écrit en C++ utilisant : If you like FreeFileSync diff --git a/BUILD/Languages/german.lng b/BUILD/Languages/german.lng index 6ca3b96b..af14bcd2 100644 --- a/BUILD/Languages/german.lng +++ b/BUILD/Languages/german.lng @@ -29,9 +29,9 @@ Verzeichnis öffnen Abort requested: Waiting for current operation to finish... -Abbruch initiiert: Warte, bis aktuelle Operation beendet ist... +Abbruch initiiert: Warte bis die aktuelle Operation beendet ist... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: Der Zeitstempel für die Versionierung kann nicht erstellt werden: RealtimeSync - Automated Synchronization @@ -459,18 +459,12 @@ Die Befehlszeile wird ausgelöst wenn: Compare Vergleichen -Compare both sides -Beide Seiten vergleichen - &Abort &Abbrechen Synchronize Synchronisieren -Start synchronization -Synchronisation starten - Add folder pair Ordnerpaar hinzufügen @@ -480,15 +474,6 @@ Die Befehlszeile wird ausgelöst wenn: Swap sides Seiten vertauschen -Open -Öffnen - -Save -Speichern - -Last used configurations (press DEL to remove from list) -Zuletzt benutzte Konfigurationen (Entf-Taste löscht Einträge) - Hide excluded items Ausgeschlossene Elemente verstecken @@ -681,7 +666,7 @@ gleich ist &Pause &Pause -Source code written in C++ utilizing: +Source code written in C++ using: Sourcecode in C++ geschrieben mit Hilfe von: If you like FreeFileSync @@ -771,6 +756,9 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! Description Beschreibung +Start synchronization +Synchronisation starten + Variant Variante @@ -807,6 +795,15 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! Select view Ansicht auswählen +Open... +Öffnen... + +Save +Speichern + +Compare both sides +Beide Seiten vergleichen + Set direction: Setze Richtung: @@ -960,6 +957,9 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! Show files that won't be copied Dateien die nicht kopiert werden anzeigen +Set as default +Als Standard festlegen + All folders are in sync! Alle Ordner sind synchron! @@ -972,15 +972,6 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! File list exported! Dateiliste exportiert! - -Object deleted successfully! -%x objects deleted successfully! - - -Element erfolgreich gelöscht! -%x Elemente erfolgreich gelöscht! - - 1 directory %x directories @@ -1137,15 +1128,15 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! Leave as unresolved conflict Als unbehandelten Konflikt belassen +Append a timestamp to each file name +Einen Zeitstempel an jeden Dateinamen hinzufügen + Replace Ersetzen Move files and replace if existing Dateien verschieben und bereits vorhandene ersetzen -Append a timestamp to each file name -Einen Zeitstempel an jeden Dateinamen hinzufügen - Folder Ordner @@ -1278,6 +1269,27 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! Setting default synchronization directions: Old files will be overwritten with newer files. Setze Standardwerte für Synchronisationsrichtungen: Alte Dateien werden durch neuere überschrieben. +Checking recycle bin availability for folder %x... +Prüfe Verfügbarkeit des Papierkorbs für Ordner %x... + +Moving file %x to recycle bin +Verschiebe Datei %x in den Papierkorb + +Moving folder %x to recycle bin +Verschiebe Ordner %x in den Papierkorb + +Moving symbolic link %x to recycle bin +Verschiebe Symbolischen Link %x in den Papierkorb + +Deleting file %x +Lösche Datei %x + +Deleting folder %x +Lösche Ordner %x + +Deleting symbolic link %x +Lösche Symbolischen Link %x + Recycle Bin is not available for the following paths! Files will be deleted permanently instead: Der Papierkorb ist für die folgenden Pfade nicht verfügbar! Die Dateien werden stattdessen permanent gelöscht: @@ -1359,24 +1371,6 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! Multiple... Verschiedene... -Deleting file %x -Lösche Datei %x - -Deleting folder %x -Lösche Ordner %x - -Deleting symbolic link %x -Lösche Symbolischen Link %x - -Moving file %x to recycle bin -Verschiebe Datei %x in den Papierkorb - -Moving folder %x to recycle bin -Verschiebe Ordner %x in den Papierkorb - -Moving symbolic link %x to recycle bin -Verschiebe Symbolischen Link %x in den Papierkorb - Moving file %x to %y Verschiebe Datei %x nach %y diff --git a/BUILD/Languages/greek.lng b/BUILD/Languages/greek.lng index fbdf907c..d48de099 100644 --- a/BUILD/Languages/greek.lng +++ b/BUILD/Languages/greek.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... Ζητήθηκε ματαίωση: Αναμονή για την λήξη της τρέχουσας εργασίας... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: RealtimeSync - Automated Synchronization @@ -59,7 +59,7 @@ Διαγραφή όλων των ρυθμίσεων φίλτρου Save as batch job - +Αποθήκευση ως δέσμη ενεργειών Comparison settings Ρυθμίσεις σύγκρισης @@ -89,7 +89,7 @@ Σφάλμα στη γραμμή εντολών: Info -Πληροφορία +Πληροφορίες Warning Προειδοποίηση @@ -224,7 +224,7 @@ Ά&νοιγμα... Save &as... -Απο&θήκευση ως... +Αποθή&κευση ως... &Quit Έ&ξοδος @@ -254,7 +254,7 @@ 3. Πατήστε το 'Έναρξη'. To get started just import a .ffs_batch file. -Για να ξεκινήσετε μπορείτε απλά να εισάγεται ένα αρχείο .ffs_batch. +Για να ξεκινήσετε μπορείτε απλά να εισάγετε ένα αρχείο .ffs_batch. Folders to watch Υποκατάλογοι που θα παρακολουθούνται @@ -272,7 +272,7 @@ Επιλογή υποκαταλόγου Idle time [seconds] - +Λανθάνων χρόνος [δευτερόλεπτα] Idle time between last detected change and execution of command Λανθάνων χρόνος μεταξύ τελευταίας αλλαγής που ανιχνεύθηκε και εκτέλεσης της εντολής @@ -328,16 +328,16 @@ The command is triggered if: Ο συγχρονισμός ολοκληρώθηκε με σφάλματα! Synchronization completed with warnings. - +Ο συγχρονισμός ολοκληρώθηκε με προειδοποιήσεις. Nothing to synchronize! Δεν υπάρχει τίποτα προς συγχρονισμό! Synchronization completed successfully. - +Ο συγχρονισμός ολοκληρώθηκε επιτυχώς. Saving log file %x... - +Αποθήκευση του αρχείου καταγραφής %x... Press "Switch" to resolve issues in FreeFileSync main dialog. Πατήστε "Εναλλαγή" για να επιλύσετε τα θέματα στο κεντρικό παράθυρο του FreeFileSync. @@ -361,7 +361,7 @@ The command is triggered if: Δεν είναι δυνατή η σύνδεση με το sourceforge.net! Current FreeFileSync version number was not found online! Do you want to check manually? - +Η τρέχουσα έκδοση του FreeFileSync δεν βρέθηκε στο δίκτυο! Θέλετε να την αναζητήσετε μόνος σας; Do you want FreeFileSync to automatically check for updates every week? Θέλετε το FreeFileSync να ελέγχει αυτόματα για ενημερώσεις κάθε εβδομάδα; @@ -433,7 +433,7 @@ The command is triggered if: Απο&θήκευση Save as &batch job... - +Αποθήκευση ως δέσ&μη ενεργειών... 1. &Compare 1. &Σύγκριση @@ -459,18 +459,12 @@ The command is triggered if: Compare Σύγκριση -Compare both sides -Σύγκριση των δύο πλευρών - &Abort &Άκυρο Synchronize Συγχρονισμός -Start synchronization -Έναρξη του συγχρονισμού - Add folder pair Προσθήκη ζεύγους υποκαταλόγων @@ -480,17 +474,8 @@ The command is triggered if: Swap sides Ανταλλαγή πλευρών -Open - - -Save - - -Last used configurations (press DEL to remove from list) -Διατάξεις που χρησιμοποιήθηκαν τελευταία (πατήστε DEL για διαγραφή από τη λίστα) - Hide excluded items - +Απόκρυψη στοιχείων που εξαιρέθηκαν Show filtered or temporarily excluded files Εμφάνιση φιλτραρισμένων ή προσωρινά εξαιρεθέντων αρχείων @@ -550,25 +535,25 @@ The command is triggered if: Ματαίωση του συγχρονισμού με το πρώτο σφάλμα On completion - +Μετά την ολοκλήρωση Show progress dialog Εμφάνιση της αναφοράς προόδου Save log - +Αποθήκευση αρχείου καταγραφής Select folder to save log files Επιλογή υποκαταλόγου για την αποθήκευση των αρχείων καταγραφής Limit -Αριθμός εκδόσεων +Μέγιστος αριθμός Limit maximum number of log files Μέγιστος αριθμός αρχείων καταγραφής Select variant - +Επιλογή μεθόδου Files are found equal if @@ -607,7 +592,7 @@ is the same OK <- Two way -> - +<- Διπλής κατεύθυνσης -> Identify and propagate changes on both sides using a database. Deletions, renaming and conflicts are detected automatically. Αναγνώριση και αναπαραγωγή των αλλαγών και στις δύο πλευρές με τη χρήση μιας βάσης δεδομένων. Διαγραφές, μετονομασίες και διενέξεις ανιχνεύονται αυτόματα. @@ -643,7 +628,7 @@ is the same Κάδος Ανακύκλωσης Use Recycle Bin for deleted and overwritten files - +Χρήση του Κάδου Ανακύκλωσης για αρχεία που διαγράφηκαν ή αντικαταστάθηκαν Versioning Διατήρηση παλιών εκδόσεων @@ -652,7 +637,7 @@ is the same Μεταφορά χρονοσημασμένων αρχείων σε συγκεκριμένο υποκατάλογο Naming convention: - +Τρόπος ονομασίας: Configuration Διάταξη @@ -681,7 +666,7 @@ is the same &Pause &Παύση -Source code written in C++ utilizing: +Source code written in C++ using: Ο πηγαίος κώδικας γράφτηκε σε C++ χρησιμοποιώντας τα: If you like FreeFileSync @@ -771,6 +756,9 @@ Note: File names must be relative to base directories! Description Περιγραφή +Start synchronization +Έναρξη του συγχρονισμού + Variant Μέθοδος @@ -807,6 +795,15 @@ Note: File names must be relative to base directories! Select view Επιλογή εμφάνισης +Open... + + +Save +Αποθήκευση + +Compare both sides +Σύγκριση των δύο πλευρών + Set direction: Επιλογή κατεύθυνσης: @@ -960,6 +957,9 @@ Note: File names must be relative to base directories! Show files that won't be copied Εμφάνιση των αρχείων που δε θα αντιγραφούν +Set as default + + All folders are in sync! Όλοι οι υποκατάλογοι είναι συγχρονισμένοι! @@ -972,15 +972,6 @@ Note: File names must be relative to base directories! File list exported! Ο κατάλογος των αρχείων έχει εξαχθεί! - -Object deleted successfully! -%x objects deleted successfully! - - -Το αντικείμενο διαγράφηκε επιτυχώς! -%x αντικείμενα διαγράφηκαν επιτυχώς! - - 1 directory %x directories @@ -1033,7 +1024,7 @@ Note: File names must be relative to base directories! Σύγκριση του περιεχομένου... Copy - +Αντιγραφή Paused Σε παύση @@ -1048,13 +1039,13 @@ Note: File names must be relative to base directories! Ολοκληρώθηκε Continue -Συνέχεια +Συνέχιση Pause Παύση Logging -Καταγραφή μηνυμάτων +Καταγραφέντα μηνύματα Cannot find %x Δεν μπορεί να βρεθεί το %x @@ -1102,19 +1093,19 @@ Note: File names must be relative to base directories! Ένταξη εξωτερικών εφαρμογών στο μενού περιβάλλοντος. Οι ακόλουθες μακροεντολές είναι διαθέσιμες: - full file or folder name -- πλήρε όνομα αρχείου ή υποκαταλόγου +- πλήρες όνομα αρχείου ή υποκαταλόγου - folder part only - μέρος μόνο του υποκαταλόγου - Other side's counterpart to %item_path% - +- Το αντίστοιχο του %item_path% της άλλης πλευράς - Other side's counterpart to %item_folder% - +- Το αντίστοιχο του %item_folder% της άλλης πλευράς Make hidden warnings and dialogs visible again? - +Να επανεμφανιστούν τα κρυμμένα μηνύματα και προειδοποιήσεις; Do you really want to move the following object to the Recycle Bin? @@ -1137,23 +1128,23 @@ Note: File names must be relative to base directories! Leave as unresolved conflict Παράβλεψη ως ανεπίλυτη διένεξη +Append a timestamp to each file name +Προσάρτηση χρονικής σήμανσης σε κάθε όνομα αρχείου + Replace - +Αντικατάσταση προηγούμενης Move files and replace if existing - - -Append a timestamp to each file name - +Μετακίνηση αρχείων και αντικατάσταση Folder - +Υποκατάλογος File - +Αρχείο YYYY-MM-DD hhmmss - +ΕΕΕΕ-ΜΜ-ΗΗ ωωλλδδ Files Αρχεία @@ -1210,7 +1201,7 @@ Note: File names must be relative to base directories! Δεν μπορεί να αντιγραφεί το αρχείο %x στο %y. Type of item %x is not supported: - +Ο τύπος του στοιχείου %x δεν υποστηρίζεται: Cannot open directory %x. Δεν είναι δυνατό το άνοιγμα του υποκαταλόγου %x. @@ -1278,6 +1269,27 @@ Note: File names must be relative to base directories! Setting default synchronization directions: Old files will be overwritten with newer files. Ρύθμιση προεπιλεγμένης κατεύθυνσης συγχρονισμού: Τα νεότερα αρχεία θα αντικαταστήσουν τα παλιότερα. +Checking recycle bin availability for folder %x... + + +Moving file %x to recycle bin +Μεταφορά του αρχείου %x στον κάδο ανακύκλωσης + +Moving folder %x to recycle bin +Μεταφορά του υποκαταλόγου %x στον κάδο ανακύκλωσης + +Moving symbolic link %x to recycle bin +Μεταφορά του συμβολικού δεσμού %x στον κάδο ανακύκλωσης + +Deleting file %x +Διαγραφή του αρχείου %x + +Deleting folder %x +Διαγραφή του υποκαταλόγου %x + +Deleting symbolic link %x +Διαγραφή του συμβολικού δεσμού %x + Recycle Bin is not available for the following paths! Files will be deleted permanently instead: Ο Κάδος Ανακύκλωσης δεν είναι διαθέσιμος για τις ακόλουθες διαδρομές! Τα αρχεία θα διαγραφούν μόνιμα: @@ -1359,24 +1371,6 @@ Note: File names must be relative to base directories! Multiple... Πολλαπλές ρυθμίσεις... -Deleting file %x -Διαγραφή του αρχείου %x - -Deleting folder %x -Διαγραφή του υποκαταλόγου %x - -Deleting symbolic link %x -Διαγραφή του συμβολικού δεσμού %x - -Moving file %x to recycle bin -Μεταφορά του αρχείου %x στον κάδο ανακύκλωσης - -Moving folder %x to recycle bin -Μεταφορά του υποκαταλόγου %x στον κάδο ανακύκλωσης - -Moving symbolic link %x to recycle bin -Μεταφορά του συμβολικού δεσμού %x στον κάδο ανακύκλωσης - Moving file %x to %y Μεταφορά του αρχείου %x στο %y @@ -1387,7 +1381,7 @@ Note: File names must be relative to base directories! Μεταφορά του συμβολικού δεσμού %x στο %y Removing old versions... - +Διαγραφή παλιών εκδόσεων... Creating file %x Δημιουργία του αρχείου %x @@ -1423,7 +1417,7 @@ Note: File names must be relative to base directories! Ο υποκατάλογος %x δε βρέθηκε. The following items have unresolved conflicts and will not be synchronized: - +Τα ακόλουθα στοιχεία έχουν ανεπίλυτες διενέξεις και δε θα συγχρονιστούν: Significant difference detected: Ανιχνεύθηκαν σημαντικές διαφορές: diff --git a/BUILD/Languages/hebrew.lng b/BUILD/Languages/hebrew.lng index 532a3bcc..a7182126 100644 --- a/BUILD/Languages/hebrew.lng +++ b/BUILD/Languages/hebrew.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... התקבלה בקשת ביטול: מחכה לפעולה הנוכחית להסתיים... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: נכשל ייצור תג זמן עבור עדכון גרסאות RealtimeSync - Automated Synchronization @@ -681,7 +681,7 @@ is the same &Pause &עצור -Source code written in C++ utilizing: +Source code written in C++ using: קוד מקור נכתב ב- C++ באמצעות: If you like FreeFileSync diff --git a/BUILD/Languages/hungarian.lng b/BUILD/Languages/hungarian.lng index e0ced192..385eaec3 100644 --- a/BUILD/Languages/hungarian.lng +++ b/BUILD/Languages/hungarian.lng @@ -31,8 +31,8 @@ Abort requested: Waiting for current operation to finish... Megszakítási kérelem: Várakozás a folyamatban lévő művelet befejezésére... -Failure to create time stamp for versioning: - +Failure to create timestamp for versioning: +A verziókövetéshez szükséges időbélyeg létrehozása sikertelen: RealtimeSync - Automated Synchronization RealtimeSync - Automatikus szinkronizálás @@ -59,7 +59,7 @@ Szűrőbeállítások törlése Save as batch job - +Mentés kötegelt feladatként Comparison settings Összehasonlítási beállítások @@ -272,7 +272,7 @@ Mappa kiválasztása Idle time [seconds] - +Tétlenségi idő (másodpercekben) Idle time between last detected change and execution of command Tétlenség időtartama az utolsó változás észlelése és a parancs végrehajtása között @@ -328,16 +328,16 @@ A parancs végrehajtódik, ha: A szinkronizáció befejeződött, de akadtak hibák! Synchronization completed with warnings. - +A szinkronizáció befejeződött figyelmeztetésekkel. Nothing to synchronize! Nincs mit szinkronizálni! Synchronization completed successfully. - +A szinkronizáció sikeresen befejeződött. Saving log file %x... - +Naplófájl mentése a következő fájlba: %x. Press "Switch" to resolve issues in FreeFileSync main dialog. Nyomg meg a "Váltás" gombot az események kezeléséhez a FreeFileSync fő párbeszédablakában. @@ -361,7 +361,7 @@ A parancs végrehajtódik, ha: A csatlakozás a sourceforge.net-hez sikertelen! Current FreeFileSync version number was not found online! Do you want to check manually? - +A jelenlegi FreeFileSync verziószám nem található meg online. Akarod manuálisan ellenőrizni? Do you want FreeFileSync to automatically check for updates every week? Akarod, hogy a FreeFileSync automatikusan minden héten keressen frissítést? @@ -433,7 +433,7 @@ A parancs végrehajtódik, ha: &Mentés Save as &batch job... - +Mentés &kötegelt feladatként... 1. &Compare 1. &Összehasonlítás @@ -481,16 +481,16 @@ A parancs végrehajtódik, ha: Oldalak felcserélése Open - +Megnyitás Save - +Mentés Last used configurations (press DEL to remove from list) Utoljára használt beállítások (DEL billentyűvel törölhető a listából) Hide excluded items - +Kizárt elemek elrejtése Show filtered or temporarily excluded files A szűrt vagy ideiglenesen kizárt fájlok mutatása @@ -550,13 +550,13 @@ A parancs végrehajtódik, ha: Szinkronizáció leállítása az első hibánál On completion - +Befejezés esetén Show progress dialog Folyamatjelző párbeszédablak mutatása Save log - +Naplófájl mentése Select folder to save log files Válaszd ki a mappát a naplófájlok mentéséhez @@ -568,7 +568,7 @@ A parancs végrehajtódik, ha: Naplófájlok maximális számának korlátozása Select variant - +Változat kiválasztása Files are found equal if @@ -605,7 +605,7 @@ A fájlok megegyeznek, ha megegyezik OK <- Two way -> - +<- Kétirányú -> Identify and propagate changes on both sides using a database. Deletions, renaming and conflicts are detected automatically. Változások azonosítása és végrehajtása mindkét oldalon adatbázis segítségével. Automatikusan felismerődnek a törlések, átnevezések és ütközések. @@ -641,7 +641,7 @@ A fájlok megegyeznek, ha megegyezik Lomtár (Recycle Bin) Use Recycle Bin for deleted and overwritten files - +Lomtár használata a törölt és felülírt fájlokhoz Versioning Verziókövetés @@ -650,7 +650,7 @@ A fájlok megegyeznek, ha megegyezik Időbélyeges fájlok mozgatása a megadott mappába Naming convention: - +Elnevezési konvenció: Configuration Beállítás @@ -679,7 +679,7 @@ A fájlok megegyeznek, ha megegyezik &Pause &Szünet -Source code written in C++ utilizing: +Source code written in C++ using: A programot C++-ban fejlesztették a következők felhasználásával: If you like FreeFileSync @@ -958,6 +958,9 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony Show files that won't be copied A nem másolandó fájlok mutatása +Set as default + + All folders are in sync! Minden mappa szinkronban! @@ -970,15 +973,6 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony File list exported! A fájllista exportálása befejeződött! - -Object deleted successfully! -%x objects deleted successfully! - - -Sikeresen töröltük az objektumot! -Sikeresen töröltünk %x objektumot! - - 1 directory %x directories @@ -1031,7 +1025,7 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony Tartalom összehasonlítása... Copy - +Másolás Paused Szüneteltetve @@ -1106,13 +1100,13 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony - csak mappa rész - Other side's counterpart to %item_path% - +- A másik oldal megfelelőjét a következő fájlba: %item_path% - Other side's counterpart to %item_folder% - +- A másik oldal megfelelőjét a következő mappába: %item_path% Make hidden warnings and dialogs visible again? - +Tegyük újra láthatóvá a rejtett figyelmeztetéseket és párbeszédablakokat? Do you really want to move the following object to the Recycle Bin? @@ -1135,23 +1129,23 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony Leave as unresolved conflict Feloldatlan ütközésként hagyni +Append a timestamp to each file name +Időbélyeg hozzáadása a minden fájlnévhez + Replace - +Felülírás Move files and replace if existing - - -Append a timestamp to each file name - +Fájlok mozgatása és felülírása létezés esetén Folder - +Mappa File - +Fájl YYYY-MM-DD hhmmss - +ÉÉÉÉ-HH-NN óóppmm Files Fájlok @@ -1208,7 +1202,7 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony %x fájl másolása a(z) %y fájlba sikertelen. Type of item %x is not supported: - +A(z) %x elem típusa nem támogatott: Cannot open directory %x. A következő mappa megnyitása sikertelen: %x. @@ -1276,6 +1270,27 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony Setting default synchronization directions: Old files will be overwritten with newer files. Alapértelmezett szinkronizációs irányok beállítása: a régebbi fájlok felülíródnak az újabbakkal. +Checking recycle bin availability for folder %x... + + +Moving file %x to recycle bin +%x fájl mozgatása a Lomtárba (Recycle Bin) + +Moving folder %x to recycle bin +%x mappa mozgatása a Lomtárba (Recycle Bin) + +Moving symbolic link %x to recycle bin +%x symlink mozgatása a Lomtárba (Recycle Bin) + +Deleting file %x +Fájl törlése %x + +Deleting folder %x +Mappa törlése %x + +Deleting symbolic link %x +Symlink törlése: %x + Recycle Bin is not available for the following paths! Files will be deleted permanently instead: A Lomtár (Recycle Bin) nem elérhető a következő útvonalakhoz! A fájlok azonnali törlésre kerülnek helyette: @@ -1357,24 +1372,6 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony Multiple... Sokszorosítás -Deleting file %x -Fájl törlése %x - -Deleting folder %x -Mappa törlése %x - -Deleting symbolic link %x -Symlink törlése: %x - -Moving file %x to recycle bin -%x fájl mozgatása a Lomtárba (Recycle Bin) - -Moving folder %x to recycle bin -%x mappa mozgatása a Lomtárba (Recycle Bin) - -Moving symbolic link %x to recycle bin -%x symlink mozgatása a Lomtárba (Recycle Bin) - Moving file %x to %y %x fájl mozgatása ide: %y @@ -1385,7 +1382,7 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony %x symlink mozgatása ide: %y Removing old versions... - +Régi verziók eltávolítása... Creating file %x %x fájl létrehozása @@ -1421,7 +1418,7 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony A következő forrásmappa nem található: %x. The following items have unresolved conflicts and will not be synchronized: - +A következő elemek feloldatlan ütközésekkel rendelkeznek és nem lesznek szinkronizálva: Significant difference detected: Jelentős különbség érzékelve: diff --git a/BUILD/Languages/italian.lng b/BUILD/Languages/italian.lng index aff424ad..14eeaf79 100644 --- a/BUILD/Languages/italian.lng +++ b/BUILD/Languages/italian.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... Selezionata interruazione: conclusione dell'operazione... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: Errore nella creazione di data e ora per il controllo delle versioni: RealtimeSync - Automated Synchronization @@ -681,7 +681,7 @@ I file sono considerati identici se &Pause &Pausa -Source code written in C++ utilizing: +Source code written in C++ using: Codice sorgente scritto in C++ utilizzando: If you like FreeFileSync diff --git a/BUILD/Languages/japanese.lng b/BUILD/Languages/japanese.lng index f5e7eb49..25935c29 100644 --- a/BUILD/Languages/japanese.lng +++ b/BUILD/Languages/japanese.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... ユーザによる中断: 現在の処理を終了しています.. お待ちください... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: バージョン付けのタイムスタンプ作成に失敗: RealtimeSync - Automated Synchronization @@ -678,7 +678,7 @@ is the same &Pause 一時停止(&P) -Source code written in C++ utilizing: +Source code written in C++ using: ソースコードは C++ で書かれています If you like FreeFileSync diff --git a/BUILD/Languages/korean.lng b/BUILD/Languages/korean.lng index 501e38e6..b4b28480 100644 --- a/BUILD/Languages/korean.lng +++ b/BUILD/Languages/korean.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... 사용자에 의한 작업 중단 : 현재 작업 종료 대기 중... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: 버저닝을 위한 타임 스탬프 생성 실패 : RealtimeSync - Automated Synchronization @@ -678,7 +678,7 @@ is the same &Pause 일시정지(&P) -Source code written in C++ utilizing: +Source code written in C++ using: 소스코드는 C++ 언어로 아래 툴을 사용하여 작성되었습니다 : If you like FreeFileSync diff --git a/BUILD/Languages/lithuanian.lng b/BUILD/Languages/lithuanian.lng index 9180212b..4c0b74ec 100644 --- a/BUILD/Languages/lithuanian.lng +++ b/BUILD/Languages/lithuanian.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... Nutraukti: laukiama kol baigsis esama operacija... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: Nepavyo sukurti laiko žymės versijavimui: RealtimeSync - Automated Synchronization @@ -687,7 +687,7 @@ yra toks pats &Pause &Pauzė -Source code written in C++ utilizing: +Source code written in C++ using: Šaltinio kodas parašytas su C++ naudojant: If you like FreeFileSync diff --git a/BUILD/Languages/norwegian.lng b/BUILD/Languages/norwegian.lng index 8850a52b..91672bab 100644 --- a/BUILD/Languages/norwegian.lng +++ b/BUILD/Languages/norwegian.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... Avbrytelse forespurt: Venter på at gjeldende handling avsluttes... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: RealtimeSync - Automated Synchronization @@ -59,7 +59,7 @@ Nullstill filterinnstillinger Save as batch job - +Lagre som sammensatt oppgave Comparison settings Innstillinger for sammenligning @@ -224,7 +224,7 @@ &Åpne Save &as... - +Lagre &som &Quit &Avslutt @@ -272,7 +272,7 @@ Velg en mappe Idle time [seconds] - +Ventetid [sekunder] Idle time between last detected change and execution of command Ventetid mellom forrige endring og utførelse av kommando @@ -328,16 +328,16 @@ Kommandoen utløses hvis: Synkronisering fullført med feil! Synchronization completed with warnings. - +Synkronisering fullført med advarsler. Nothing to synchronize! Ikke noe å synkronisere! Synchronization completed successfully. - +Synkdonisering fullført. Saving log file %x... - +Lagrer loggfil %x... Press "Switch" to resolve issues in FreeFileSync main dialog. Trykk "Skift" for å løse problemer i FreeFileSync hovedvindu. @@ -361,7 +361,7 @@ Kommandoen utløses hvis: Ikke i stand til å koble til sourceforge.net! Current FreeFileSync version number was not found online! Do you want to check manually? - +Nyeste FreeFileSyncversjon ble ikke funnet online! Vil du sjekke manuelt? Do you want FreeFileSync to automatically check for updates every week? Skal FreeFileSync automatisk se etter oppdateringer hver uke? @@ -433,7 +433,7 @@ Kommandoen utløses hvis: &Lagre Save as &batch job... - +Lagre som &sammensatt jobb... 1. &Compare 1. Sammen&lign @@ -459,18 +459,12 @@ Kommandoen utløses hvis: Compare Sammenlign -Compare both sides -Sammenlign begge sider - &Abort &Avbryt Synchronize Synkroniser -Start synchronization -Start synkronisering - Add folder pair Legg til mappepar @@ -480,20 +474,11 @@ Kommandoen utløses hvis: Swap sides Bytt sider -Open - - -Save - - -Last used configurations (press DEL to remove from list) -Sist brukte innstillinger (trykk DEL for å fjerne fra liste) - Hide excluded items - +Gjem ekskluderte elementer Show filtered or temporarily excluded files - +Vis filtrerte eller midlertidig ekskluderte filer Number of files and folders that will be created Antall filer og mapper som vil opprettes @@ -532,43 +517,43 @@ Kommandoen utløses hvis: Feilhåndtering Ignore - +Ignorer Hide all error and warning messages Skjul feilmeldinger og advarsler Pop-up - +Pop-up Show pop-up on errors or warnings Vis pop-upvindu ved feil eller advarsler Exit - +Avslutt Abort synchronization on first error - +Stopp synkronisering ved første feil On completion - +Ved fullføring Show progress dialog Vis dialogboks Save log - +Lagre logg Select folder to save log files - +Velg mappe du vil lagre loggfiler i Limit - +Grense Limit maximum number of log files - +Begrens maks antall loggfiler Select variant - +Velg variant Files are found equal if @@ -607,7 +592,7 @@ er det samme OK <- Two way -> - +<- Toveis -> Identify and propagate changes on both sides using a database. Deletions, renaming and conflicts are detected automatically. Identifiser og spre endringer på begge sider ved å bruke en database. Slettinger, navneendringer og konflikter blir automatisk oppdaget. @@ -634,25 +619,25 @@ er det samme Slette-håndtering Permanent - +Permanent Delete or overwrite files permanently Slett eller overskriv filer permanent Recycle Bin - +Papirkurv Use Recycle Bin for deleted and overwritten files - +Bruk papirkurv for slettede og overskrevne filer Versioning Versjonshåndtering Move time-stamped files into specified folder - +Flytt tidsstemplede filer den valgte mappen Naming convention: - +Navnekonvensjon: Configuration Innstilling @@ -681,7 +666,7 @@ er det samme &Pause &Pause -Source code written in C++ utilizing: +Source code written in C++ using: Kildekode skrevet i C++ med hjelp fra: If you like FreeFileSync @@ -691,7 +676,7 @@ er det samme Doner med PayPal Many thanks for localization: - +Mange takk for lokalisering: Feedback and suggestions are welcome Tilbakemelding og forslag er velkomne @@ -771,6 +756,9 @@ Merk: Filnavn må være relative til basismapper! Description Beskrivelse +Start synchronization +Start synkronisering + Variant Variant @@ -778,7 +766,7 @@ Merk: Filnavn må være relative til basismapper! Statistikk Don't show this dialog again - +Ikke vis dette vinduet igjen Find what: Søk hva: @@ -807,6 +795,15 @@ Merk: Filnavn må være relative til basismapper! Select view Velg visning +Open... + + +Save +Lagre + +Compare both sides +Sammenlign begge sider + Set direction: Still inn retningen: @@ -871,7 +868,7 @@ Merk: Filnavn må være relative til basismapper! Vil du lagre endringene til %x? Do&n't save - +&Ikke lagre Configuration loaded! Innstilling lastet! @@ -960,9 +957,12 @@ Merk: Filnavn må være relative til basismapper! Show files that won't be copied Vis filer som ikke blir kopiert -All folders are in sync! +Set as default +All folders are in sync! +Alle mapper er synkroniserte! + Comma separated list Komma-separert liste @@ -972,15 +972,6 @@ Merk: Filnavn må være relative til basismapper! File list exported! Filliste eksportert! - -Object deleted successfully! -%x objects deleted successfully! - - -Objekt vellykket slettet! -%x objekter vellykket slettet! - - 1 directory %x directories @@ -1033,7 +1024,7 @@ Merk: Filnavn må være relative til basismapper! Sammenligner innhold... Copy - +Kopier Paused Pauset @@ -1108,13 +1099,13 @@ Merk: Filnavn må være relative til basismapper! - kun mapper - Other side's counterpart to %item_path% - +- Den andre sidens motpart til %item_path% - Other side's counterpart to %item_folder% - +- Den andre sidens motpart til %item_folder% Make hidden warnings and dialogs visible again? - +Gjør skjylte advarsler og dialoger synlige igjen? Do you really want to move the following object to the Recycle Bin? @@ -1137,23 +1128,23 @@ Merk: Filnavn må være relative til basismapper! Leave as unresolved conflict Etterlat som uløste konflikter +Append a timestamp to each file name +Legg til et tidsstempel til hvert filnavn + Replace - +Erstatt Move files and replace if existing - - -Append a timestamp to each file name - +Flytt filer og erstatt hvis eksisterer Folder - +Mappe File - +Fil YYYY-MM-DD hhmmss - +ÅÅÅÅ-MM-DD ttmmss Files Filer @@ -1210,13 +1201,13 @@ Merk: Filnavn må være relative til basismapper! Kan ikke kopiere filen %x til %y. Type of item %x is not supported: - +Type element %x er ikke støttet: Cannot open directory %x. - +Kan ikke åpne mappe %x. Cannot enumerate directory %x. - +Kan ikke gjennomgå mappe %x. Detected endless directory recursion. Fant uendelig dyp mappestruktur (lenker til mapper høyere opp i strukturen) @@ -1278,6 +1269,27 @@ Merk: Filnavn må være relative til basismapper! Setting default synchronization directions: Old files will be overwritten with newer files. Stiller inn standard synkroniseringsretning: Gamle filer blir overskrevet med nyere filer. +Checking recycle bin availability for folder %x... + + +Moving file %x to recycle bin +Flytter fil %x til papirkurv + +Moving folder %x to recycle bin +Flytter mappe %x til papirkurv + +Moving symbolic link %x to recycle bin +Flytter symbolsk lenke %x til papirkurv + +Deleting file %x +Sletter fil %x + +Deleting folder %x +Sletter mappe %x + +Deleting symbolic link %x +Sletter symbolsk lenke %x + Recycle Bin is not available for the following paths! Files will be deleted permanently instead: Papirkurv er ikke tilgjengelig for de følgende baner! Filer blir isteden slettet permanent: @@ -1291,7 +1303,7 @@ Merk: Filnavn må være relative til basismapper! Mapper er avhengige av hverandre! Vær forsiktig når du setter opp synkroniseringsregler: Start comparison - +Start sammenligning Preparing synchronization... Forbereder synkronisering... @@ -1306,7 +1318,7 @@ Merk: Filnavn må være relative til basismapper! Filer %x har den samme datoen, men forskjellig størrelse! Items differ in attributes only - +Elementer har kun forskjellige attributter Symbolic links %x have the same date but a different target. Symbolske lenker %x har den samme datoen, men forskjellige mål. @@ -1359,24 +1371,6 @@ Merk: Filnavn må være relative til basismapper! Multiple... Flere... -Deleting file %x -Sletter fil %x - -Deleting folder %x -Sletter mappe %x - -Deleting symbolic link %x -Sletter symbolsk lenke %x - -Moving file %x to recycle bin -Flytter fil %x til papirkurv - -Moving folder %x to recycle bin -Flytter mappe %x til papirkurv - -Moving symbolic link %x to recycle bin -Flytter symbolsk lenke %x til papirkurv - Moving file %x to %y Flytter fil %x til %y @@ -1387,7 +1381,7 @@ Merk: Filnavn må være relative til basismapper! Flytter symbolsk lenke %x til %y Removing old versions... - +Fjerner gamle versjoner... Creating file %x Oppretter fil %x @@ -1423,7 +1417,7 @@ Merk: Filnavn må være relative til basismapper! Kildemappe %x finnes ikke. The following items have unresolved conflicts and will not be synchronized: - +De følgende elementene har uoppklarte konflikter og vil ikke bli synkroniserte: Significant difference detected: Betydelig forskjell oppdaget: @@ -1450,7 +1444,7 @@ Merk: Filnavn må være relative til basismapper! Høyre Synchronizing folder pair: - +Synkroniserer mappepar: Generating database... Oppretter database... diff --git a/BUILD/Languages/polish.lng b/BUILD/Languages/polish.lng index cebe529b..564db6dc 100644 --- a/BUILD/Languages/polish.lng +++ b/BUILD/Languages/polish.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... Żądanie przerwania: Oczekiwanie na koniec aktualnie wykonywanego zadania... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: Nie można utworzyć sygnatury czasu na potrzeby wersjonowania: RealtimeSync - Automated Synchronization @@ -684,7 +684,7 @@ jest identyczna &Pause &Pauza -Source code written in C++ utilizing: +Source code written in C++ using: Kod stworzony w C++ z wykorzystaniem: If you like FreeFileSync diff --git a/BUILD/Languages/portuguese.lng b/BUILD/Languages/portuguese.lng index dfc29c43..a187ff07 100644 --- a/BUILD/Languages/portuguese.lng +++ b/BUILD/Languages/portuguese.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... Abortar pedido: À espera do fim da operação atual... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: Falha ao criar selo temporal para versões anteriores: RealtimeSync - Automated Synchronization @@ -680,7 +680,7 @@ Os ficheiros são considerados iguais se &Pause &Pausa -Source code written in C++ utilizing: +Source code written in C++ using: Código fonte escrito em C++ utilizando: If you like FreeFileSync diff --git a/BUILD/Languages/portuguese_br.lng b/BUILD/Languages/portuguese_br.lng index 3182c088..0d4c2246 100644 --- a/BUILD/Languages/portuguese_br.lng +++ b/BUILD/Languages/portuguese_br.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... Cancelar solicitado: Esperando fim da operação... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: Erro ao criar estampa de tempo para controle de versão: RealtimeSync - Automated Synchronization @@ -681,7 +681,7 @@ Os arquivos são considerados iguais se &Pause &Pausar -Source code written in C++ utilizing: +Source code written in C++ using: Código-fonte escrito em C++ utilizando: If you like FreeFileSync diff --git a/BUILD/Languages/romanian.lng b/BUILD/Languages/romanian.lng index c99a2d55..a0370f4d 100644 --- a/BUILD/Languages/romanian.lng +++ b/BUILD/Languages/romanian.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... Abandonare solicitată: Se așteaptă terminarea operației în curs... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: Crearea marcajului temporal pentru versionare a eșuat RealtimeSync - Automated Synchronization @@ -684,7 +684,7 @@ este același &Pause &Pauzează -Source code written in C++ utilizing: +Source code written in C++ using: Cod sursă scris în C++ folosind: If you like FreeFileSync diff --git a/BUILD/Languages/russian.lng b/BUILD/Languages/russian.lng index 0ffed7e4..597f548c 100644 --- a/BUILD/Languages/russian.lng +++ b/BUILD/Languages/russian.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... Запрос отмены: Ожидайте, пока текущая операция завершится... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: Неспособность создать отметку времени для архивации файлов: RealtimeSync - Automated Synchronization @@ -682,7 +682,7 @@ is the same &Pause &Пауза -Source code written in C++ utilizing: +Source code written in C++ using: Исходный код написан на C++ с использованием: If you like FreeFileSync diff --git a/BUILD/Languages/scottish_gaelic.lng b/BUILD/Languages/scottish_gaelic.lng index 3e49bc3b..b3d635a6 100644 --- a/BUILD/Languages/scottish_gaelic.lng +++ b/BUILD/Languages/scottish_gaelic.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... Tha thu airson sgur dheth: A' feitheamh gus an crìochnaich an gnìomh làithreach... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: Cha b' urrainn dhuinn stampa-ama a chruthachadh airson versioning: RealtimeSync - Automated Synchronization @@ -686,7 +686,7 @@ Bidh dà fhaidhle co-ionnann 'nar beachd-sa &Pause &Cuir 'na stad -Source code written in C++ utilizing: +Source code written in C++ using: Chaidh an còd tùsail a sgrìobhadh ann an C++ le taic: If you like FreeFileSync diff --git a/BUILD/Languages/slovenian.lng b/BUILD/Languages/slovenian.lng index 3abe7273..4a916632 100644 --- a/BUILD/Languages/slovenian.lng +++ b/BUILD/Languages/slovenian.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... Zahtevana je bila prekinitev: čakam, da se zaključi trenutna operacija... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: Napaka pri ustvarjanju časovne oznake pri ustvarjanju različic: RealtimeSync - Automated Synchronization @@ -687,7 +687,7 @@ enaka &Pause &Premor -Source code written in C++ utilizing: +Source code written in C++ using: Izvorna koda napisana v C++ z uporabo: If you like FreeFileSync diff --git a/BUILD/Languages/spanish.lng b/BUILD/Languages/spanish.lng index 7de02d01..ed0286a0 100644 --- a/BUILD/Languages/spanish.lng +++ b/BUILD/Languages/spanish.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... Solicitud de aborto: Esperando a que la operación actual finalice... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: Fallo al crear fecha y hora para el versionado: RealtimeSync - Automated Synchronization @@ -681,7 +681,7 @@ es el mismo &Pause &Pausa -Source code written in C++ utilizing: +Source code written in C++ using: Código fuente escrito en C++ utilizando: If you like FreeFileSync diff --git a/BUILD/Languages/swedish.lng b/BUILD/Languages/swedish.lng index 64a62e20..9a727bfd 100644 --- a/BUILD/Languages/swedish.lng +++ b/BUILD/Languages/swedish.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... Avbryter: Väntar på att aktuell process skall slutföras... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: Kunde inte skapa tidsstämpel för versionshantering RealtimeSync - Automated Synchronization @@ -681,7 +681,7 @@ Filerna betecknas som lika om, &Pause &Paus -Source code written in C++ utilizing: +Source code written in C++ using: Källkod skriven i C++ med hjälp av: If you like FreeFileSync diff --git a/BUILD/Languages/turkish.lng b/BUILD/Languages/turkish.lng index fdbd10b0..570156c8 100644 --- a/BUILD/Languages/turkish.lng +++ b/BUILD/Languages/turkish.lng @@ -31,7 +31,7 @@ Abort requested: Waiting for current operation to finish... Vazgeçildi: Yürürlükteki işlemin bitmesi bekleniyor... -Failure to create time stamp for versioning: +Failure to create timestamp for versioning: Eski sürüm için zaman damgası oluşturulamadı: RealtimeSync - Automated Synchronization @@ -681,7 +681,7 @@ aynı olmalıdır &Pause &Duraklat -Source code written in C++ utilizing: +Source code written in C++ using: Kaynak kodu C++ kullanılarak yazılmıştır: If you like FreeFileSync diff --git a/BUILD/Languages/ukrainian.lng b/BUILD/Languages/ukrainian.lng index 7c184097..a2a0923c 100644 --- a/BUILD/Languages/ukrainian.lng +++ b/BUILD/Languages/ukrainian.lng @@ -31,8 +31,8 @@ Abort requested: Waiting for current operation to finish... Запит переривання: В очікуванні завершення поточної операції... -Failure to create time stamp for versioning: - +Failure to create timestamp for versioning: +Не вдається створити часової мітки для версій: RealtimeSync - Automated Synchronization RealtimeSync - Автоматична синхронізація @@ -59,7 +59,7 @@ Очистити налаштування фільтра Save as batch job - +Зберегти як пакетне завдання Comparison settings Налаштування порівнювання @@ -275,7 +275,7 @@ Вибрати папку Idle time [seconds] - +Час простою [секунд] Idle time between last detected change and execution of command Час простою між виявленням останньої зміни та виконанням команди @@ -331,16 +331,16 @@ The command is triggered if: Синхронізація закінчилася з помилками! Synchronization completed with warnings. - +Синхронізація завершена з попередженнями. Nothing to synchronize! Нічого синхронізувати! Synchronization completed successfully. - +Синхронізація успішно завершена. Saving log file %x... - +Збереження файла журналу %x... Press "Switch" to resolve issues in FreeFileSync main dialog. Натисніть "Змінити" для вирішення питань у головному вікні FreeFileSync. @@ -364,7 +364,7 @@ The command is triggered if: Не можна з’єднатися з sourceforge.net! Current FreeFileSync version number was not found online! Do you want to check manually? - +Номер поточної версії FreeFileSync не знайдений онлайн! Хочете перевірити вручну? Do you want FreeFileSync to automatically check for updates every week? Ви хочете, щоб FreeFileSync автоматично перевіряв наявність оновлень щотижня? @@ -436,7 +436,7 @@ The command is triggered if: Зберегти Save as &batch job... - +Зберегти як пакетне завдання 1. &Compare 1. &Порівняти @@ -484,10 +484,10 @@ The command is triggered if: Поміняти місцями Open - +Відкрити Save - +Зберегти Last used configurations (press DEL to remove from list) @@ -496,7 +496,7 @@ The command is triggered if: Hide excluded items - +Приховати виключені елементи Show filtered or temporarily excluded files Показати відфільтровані чи тимчасово виключені елементи @@ -556,13 +556,13 @@ The command is triggered if: Перервати синхронізацію при першій помилці On completion - +Після завершення Show progress dialog Показувати вікно прогресу Save log - +Зберегти журнал Select folder to save log files Виберіть папку для файлів журналу @@ -574,7 +574,7 @@ The command is triggered if: Обмежити максимальну кількість файлів журналу Select variant - +Виберіть варіант Files are found equal if @@ -609,7 +609,7 @@ is the same OK <- Two way -> - +<- Обидва напрямки -> Identify and propagate changes on both sides using a database. Deletions, renaming and conflicts are detected automatically. Виявити та поширити зміни на обидві сторони використовуючи базу даних. Видалення, перейменування та конфлікти визначаються автоматично. @@ -645,7 +645,7 @@ is the same Корзина Use Recycle Bin for deleted and overwritten files - +Використати Корзину для видалених і перезаписаних файлів Versioning Запис версій @@ -654,7 +654,7 @@ is the same Перемістити файли з часовою міткою у вказану папку Naming convention: - +Метод іменування: Configuration Налаштування @@ -683,7 +683,7 @@ is the same &Pause &Пауза -Source code written in C++ utilizing: +Source code written in C++ using: Код програми написаний на C++ з використанням: If you like FreeFileSync @@ -809,6 +809,12 @@ Note: File names must be relative to base directories! Select view Список файлів +Deleting file %x +Вилучення файлу %x + +Deleting %x + + Set direction: Виберіть напрям: @@ -962,6 +968,9 @@ Note: File names must be relative to base directories! Show files that won't be copied Показати файли, які не будуть зкопійовані +Set as default + + All folders are in sync! Всі папки синхронізовано! @@ -974,16 +983,6 @@ Note: File names must be relative to base directories! File list exported! Список файлів експортовано! - -Object deleted successfully! -%x objects deleted successfully! - - -%x об'єкт успішно вилучено! -%x об'єкти успішно вилучено! -%x об'єктів успішно вилучено! - - 1 directory %x directories @@ -1039,7 +1038,7 @@ Note: File names must be relative to base directories! Порівнювання вмісту... Copy - +Копія Paused Призупинено @@ -1114,13 +1113,13 @@ Note: File names must be relative to base directories! - тільки папка - Other side's counterpart to %item_path% - +- Елемент з протилежної сторони до %item_path% - Other side's counterpart to %item_folder% - +- Елемент з протилежної сторони до %item_folder% Make hidden warnings and dialogs visible again? - +Зробити приховані попередження та діалоги ​​знову видимими? Do you really want to move the following object to the Recycle Bin? @@ -1146,22 +1145,22 @@ Note: File names must be relative to base directories! Залишити як невирішений конфлікт Replace - +Замінити Move files and replace if existing - +Перемістити файли замінюючи існуючі Append a timestamp to each file name - +Додати часову мітку до кожного імені файлу Folder - +Папка File - +Файл YYYY-MM-DD hhmmss - +YYYY-MM-DD hhmmss Files Файли @@ -1218,7 +1217,7 @@ Note: File names must be relative to base directories! Не вдається зкопіювати файл %х до %y. Type of item %x is not supported: - +Тип елемента %х не підтримується: Cannot open directory %x. Не вдається відкрити каталогу %x. @@ -1292,6 +1291,9 @@ Note: File names must be relative to base directories! Старі файли будуть замінені новішими файлами. +Checking recycle bin availability for folder %x... + + Recycle Bin is not available for the following paths! Files will be deleted permanently instead: Корзина для цього шляху недоступна. Файли будуть вилучені назавжди: @@ -1373,9 +1375,6 @@ Note: File names must be relative to base directories! Multiple... Різні варіанти... -Deleting file %x -Вилучення файлу %x - Deleting folder %x Вилучення папки %x @@ -1401,7 +1400,7 @@ Note: File names must be relative to base directories! Переміщення символьного посилання %x до %y Removing old versions... - +Видалення старих версій... Creating file %x Створення файлу %x @@ -1437,7 +1436,7 @@ Note: File names must be relative to base directories! Вихідний каталог %x не знайдено. The following items have unresolved conflicts and will not be synchronized: - +Наступні елементи мають невирішені конфлікти і не будуть синхронізовані: Significant difference detected: Виявлена істотна різниця: diff --git a/BUILD/Resources.zip b/BUILD/Resources.zip index 3b646713..79efed0d 100644 Binary files a/BUILD/Resources.zip and b/BUILD/Resources.zip differ diff --git a/FreeFileSync.cbp b/FreeFileSync.cbp deleted file mode 100644 index f2abfcc6..00000000 --- a/FreeFileSync.cbp +++ /dev/null @@ -1,538 +0,0 @@ - - - - - - diff --git a/FreeFileSync.vcxproj b/FreeFileSync.vcxproj index 60c622f6..5bc30a85 100644 --- a/FreeFileSync.vcxproj +++ b/FreeFileSync.vcxproj @@ -141,6 +141,7 @@ true false true + false Windows diff --git a/RealtimeSync/RealtimeSync.cbp b/RealtimeSync/RealtimeSync.cbp deleted file mode 100644 index a5f82367..00000000 --- a/RealtimeSync/RealtimeSync.cbp +++ /dev/null @@ -1,158 +0,0 @@ - - - - - - diff --git a/RealtimeSync/resources.cpp b/RealtimeSync/resources.cpp index 391a1ddb..87bca60f 100644 --- a/RealtimeSync/resources.cpp +++ b/RealtimeSync/resources.cpp @@ -60,8 +60,8 @@ GlobalResources::GlobalResources() const wxBitmap& GlobalResources::getImageInt(const wxString& name) const { auto it = bitmaps.find(!contains(name, L'.') ? //assume .png ending if nothing else specified - name + L".png" : - name); + name + L".png" : + name); if (it != bitmaps.end()) return it->second; else diff --git a/RealtimeSync/tray_menu.cpp b/RealtimeSync/tray_menu.cpp index 3319f427..eec568ad 100644 --- a/RealtimeSync/tray_menu.cpp +++ b/RealtimeSync/tray_menu.cpp @@ -401,7 +401,7 @@ rts::AbortReason rts::startDirectoryMonitor(const xmlAccess::XmlRealConfig& conf lastChangeDetected = DirWatcher::Entry(); //make sure old name is not shown again after a directory reappears //execute command - auto cmdLineExp = utfCvrtTo(expandMacros(utfCvrtTo(cmdLine))); + auto cmdLineExp = expandMacros(utfCvrtTo(cmdLine)); zen::shellExecute(cmdLineExp, zen::EXEC_TYPE_SYNC); callback.clearSchedule(); } diff --git a/RealtimeSync/watcher.cpp b/RealtimeSync/watcher.cpp index 1380e474..9b1e5a17 100644 --- a/RealtimeSync/watcher.cpp +++ b/RealtimeSync/watcher.cpp @@ -72,7 +72,7 @@ rts::WaitResult rts::waitForChanges(const std::vector& dirNamesNonFmt, { //a non-existent network path may block, so check existence asynchronously! auto ftDirExists = async([=] { return zen::dirExists(dirnameFmt); }); - while (!ftDirExists.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL))) + while (!ftDirExists.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL / 2))) statusHandler.requestUiRefresh(); //may throw! if (!ftDirExists.get()) return WaitResult(dirnameFmt); @@ -151,7 +151,7 @@ rts::WaitResult rts::waitForChanges(const std::vector& dirNamesNonFmt, } } - boost::this_thread::sleep(boost::posix_time::milliseconds(rts::UI_UPDATE_INTERVAL)); + boost::this_thread::sleep(boost::posix_time::milliseconds(rts::UI_UPDATE_INTERVAL / 2)); statusHandler.requestUiRefresh(true); //throw ?: may start sync at this presumably idle time } } @@ -178,7 +178,7 @@ void rts::waitForMissingDirs(const std::vector& dirNamesNonFmt, WaitCal //2. check dir existence return zen::dirExists(dirnameFmt); }); - while (!ftDirExisting.timed_wait(boost::posix_time::milliseconds(rts::UI_UPDATE_INTERVAL))) + while (!ftDirExisting.timed_wait(boost::posix_time::milliseconds(rts::UI_UPDATE_INTERVAL / 2))) statusHandler.requestUiRefresh(); //may throw! if (!ftDirExisting.get()) @@ -191,10 +191,11 @@ void rts::waitForMissingDirs(const std::vector& dirNamesNonFmt, WaitCal return; //wait some time... - assert_static(1000 * CHECK_DIR_INTERVAL % UI_UPDATE_INTERVAL == 0); - for (int i = 0; i < 1000 * CHECK_DIR_INTERVAL / UI_UPDATE_INTERVAL; ++i) + const int refreshInterval = UI_UPDATE_INTERVAL / 2; + assert_static(1000 * CHECK_DIR_INTERVAL % refreshInterval == 0); + for (int i = 0; i < 1000 * CHECK_DIR_INTERVAL / refreshInterval; ++i) { - boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL)); + boost::this_thread::sleep(boost::posix_time::milliseconds(refreshInterval)); statusHandler.requestUiRefresh(); } } diff --git a/algorithm.cpp b/algorithm.cpp index affc5bd1..0c47410a 100644 --- a/algorithm.cpp +++ b/algorithm.cpp @@ -12,12 +12,13 @@ #include #include #include -//#include +#include #include "lib/resources.h" #include "lib/norm_filter.h" #include "lib/db_file.h" #include "lib/cmp_filetime.h" #include "lib/norm_filter.h" +#include "process_callback.h" //for UI_UPDATE_INTERVAL using namespace zen; using namespace std::rel_ops; @@ -53,19 +54,22 @@ private: void operator()(FileMapping& fileObj) const { - switch (fileObj.getCategory()) + const CompareFilesResult cat = fileObj.getCategory(); + + //##################### schedule old temporary files for deletion #################### + if (cat == FILE_LEFT_SIDE_ONLY && endsWith(fileObj.getShortName(), TEMP_FILE_ENDING)) + return fileObj.setSyncDir(SYNC_DIR_LEFT); + else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(fileObj.getShortName(), TEMP_FILE_ENDING)) + return fileObj.setSyncDir(SYNC_DIR_RIGHT); + //#################################################################################### + + switch (cat) { case FILE_LEFT_SIDE_ONLY: - if (endsWith(fileObj.getShortName(), zen::TEMP_FILE_ENDING)) - fileObj.setSyncDir(SYNC_DIR_LEFT); //schedule potentially existing temporary files for deletion - else - fileObj.setSyncDir(dirCfg.exLeftSideOnly); + fileObj.setSyncDir(dirCfg.exLeftSideOnly); break; case FILE_RIGHT_SIDE_ONLY: - if (endsWith(fileObj.getShortName(), zen::TEMP_FILE_ENDING)) - fileObj.setSyncDir(SYNC_DIR_RIGHT); //schedule potentially existing temporary files for deletion - else - fileObj.setSyncDir(dirCfg.exRightSideOnly); + fileObj.setSyncDir(dirCfg.exRightSideOnly); break; case FILE_RIGHT_NEWER: fileObj.setSyncDir(dirCfg.rightNewer); @@ -123,7 +127,16 @@ private: void operator()(DirMapping& dirObj) const { - switch (dirObj.getDirCategory()) + const CompareDirResult cat = dirObj.getDirCategory(); + + //########### schedule abandoned temporary recycle bin directory for deletion ########## + if (cat == DIR_LEFT_SIDE_ONLY && endsWith(dirObj.getShortName(), TEMP_FILE_ENDING)) + return setSyncDirectionRec(SYNC_DIR_LEFT, dirObj); // + else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(dirObj.getShortName(), TEMP_FILE_ENDING)) + return setSyncDirectionRec(SYNC_DIR_RIGHT, dirObj); //don't recurse below! + //####################################################################################### + + switch (cat) { case DIR_LEFT_SIDE_ONLY: dirObj.setSyncDir(dirCfg.exLeftSideOnly); @@ -148,8 +161,8 @@ private: const DirectionSet dirCfg; }; - //--------------------------------------------------------------------------------------------------------------- + struct AllEqual //test if non-equal items exist in scanned data { bool operator()(const HierarchyObject& hierObj) const @@ -385,15 +398,9 @@ private: //##################### schedule old temporary files for deletion #################### if (cat == FILE_LEFT_SIDE_ONLY && endsWith(fileObj.getShortName(), TEMP_FILE_ENDING)) - { - fileObj.setSyncDir(SYNC_DIR_LEFT); - return; - } + return fileObj.setSyncDir(SYNC_DIR_LEFT); else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(fileObj.getShortName(), TEMP_FILE_ENDING)) - { - fileObj.setSyncDir(SYNC_DIR_RIGHT); - return; - } + return fileObj.setSyncDir(SYNC_DIR_RIGHT); //#################################################################################### //try to find corresponding database entry @@ -466,6 +473,13 @@ private: { const CompareDirResult cat = dirObj.getDirCategory(); + //########### schedule abandoned temporary recycle bin directory for deletion ########## + if (cat == DIR_LEFT_SIDE_ONLY && endsWith(dirObj.getShortName(), TEMP_FILE_ENDING)) + return setSyncDirectionRec(SYNC_DIR_LEFT, dirObj); // + else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(dirObj.getShortName(), TEMP_FILE_ENDING)) + return setSyncDirectionRec(SYNC_DIR_RIGHT, dirObj); //don't recurse below! + //####################################################################################### + //try to find corresponding database entry const InSyncDir::DirList::value_type* dbEntry = nullptr; if (dbContainer) @@ -527,9 +541,10 @@ private: { 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) + [&](const typename Container::value_type& item) { - return sameFileTime(std::get<0>(item.first), std::get<0>(key), 2); //respect 2 second FAT/FAT32 precision + 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; } @@ -754,7 +769,6 @@ void zen::setSyncDirectionRec(SyncDirection newDirection, FileSystemObject& fsOb fsObj.accept(recurse); } - //--------------- functions related to filtering ------------------------------------------------------------------------------------ template @@ -1192,51 +1206,139 @@ bool tryReportingError(Function cmd, DeleteFilesHandler& handler) //return "true } } +#ifdef FFS_WIN +//recycleBinStatus() blocks seriously if recycle bin is really full and drive is slow +StatusRecycler recycleBinStatusUpdating(const Zstring& dirname, DeleteFilesHandler& callback) +{ + const std::wstring msg = replaceCpy(_("Checking recycle bin availability for folder %x..."), L"%x", fmtFileName(dirname), false); + + auto ft = async([=] { return recycleBinStatus(dirname); }); + while (!ft.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL / 2))) + callback.reportStatus(msg); //may throw! + return ft.get(); +} +#endif + -struct RemoveCallbackImpl : public zen::CallbackRemoveDir +template +void categorize(const std::set& rowsIn, + std::vector& deletePermanent, + std::vector& deleteRecyler, + bool useRecycleBin, + std::map& hasRecyclerBuffer, + DeleteFilesHandler& callback) { - RemoveCallbackImpl(DeleteFilesHandler& handler) : handler_(handler) {} + auto hasRecycler = [&](const FileSystemObject& fsObj) -> bool + { +#ifdef FFS_WIN + const Zstring& baseDirPf = fsObj.root().getBaseDirPf(); - virtual void notifyFileDeletion(const Zstring& filename) { handler_.notifyDeletion(filename); } - virtual void notifyDirDeletion (const Zstring& dirname) { handler_.notifyDeletion(dirname); } + auto it = hasRecyclerBuffer.find(baseDirPf); + if (it != hasRecyclerBuffer.end()) + return it->second; + return hasRecyclerBuffer.insert(std::make_pair(baseDirPf, recycleBinStatusUpdating(baseDirPf, callback) == STATUS_REC_EXISTS)).first->second; +#else + return true; +#endif + }; -private: - DeleteFilesHandler& handler_; -}; + for (auto it = rowsIn.begin(); it != rowsIn.end(); ++it) + if (!(*it)->isEmpty()) + { + if (useRecycleBin && hasRecycler(**it)) //Windows' ::SHFileOperation() will delete permanently anyway, but we have a superior deletion routine + deleteRecyler.push_back(*it); + else + deletePermanent.push_back(*it); + } +} template -struct PermanentDeleter : public FSObjectVisitor //throw FileError +struct ItemDeleter : public FSObjectVisitor //throw FileError, but nothrow constructor!!! { - PermanentDeleter(DeleteFilesHandler& handler) : remCallback(handler) {} + ItemDeleter(bool useRecycleBin, DeleteFilesHandler& handler) : + handler_(handler), useRecycleBin_(useRecycleBin), remCallback(*this) + { + if (useRecycleBin_) + { + txtRemovingFile = _("Moving file %x to recycle bin" ); + txtRemovingDirectory = _("Moving folder %x to recycle bin" ); + txtRemovingSymlink = _("Moving symbolic link %x to recycle bin"); + } + else + { + txtRemovingFile = _("Deleting file %x" ); + txtRemovingDirectory = _("Deleting folder %x" ); + txtRemovingSymlink = _("Deleting symbolic link %x"); + } + } virtual void visit(const FileMapping& fileObj) { - if (zen::removeFile(fileObj.getFullName())) //throw FileError - remCallback.notifyFileDeletion(fileObj.getFullName()); + notifyFileDeletion(fileObj.getFullName()); + + if (useRecycleBin_) + zen::recycleOrDelete(fileObj.getFullName()); //throw FileError + else + zen::removeFile(fileObj.getFullName()); //throw FileError } virtual void visit(const SymLinkMapping& linkObj) { - switch (linkObj.getLinkType()) - { - case LinkDescriptor::TYPE_DIR: - zen::removeDirectory(linkObj.getFullName(), &remCallback); //throw FileError - break; - case LinkDescriptor::TYPE_FILE: - if (zen::removeFile(linkObj.getFullName())) //throw FileError - remCallback.notifyFileDeletion(linkObj.getFullName()); - break; - } + notifySymlinkDeletion(linkObj.getFullName()); + + if (useRecycleBin_) + zen::recycleOrDelete(linkObj.getFullName()); //throw FileError + else + switch (linkObj.getLinkType()) + { + case LinkDescriptor::TYPE_DIR: + zen::removeDirectory(linkObj.getFullName()); //throw FileError + break; + case LinkDescriptor::TYPE_FILE: + zen::removeFile(linkObj.getFullName()); //throw FileError + break; + } } virtual void visit(const DirMapping& dirObj) { - zen::removeDirectory(dirObj.getFullName(), &remCallback); //throw FileError + notifyDirectoryDeletion(dirObj.getFullName()); //notfied twice! see RemoveCallbackImpl -> no big deal + + if (useRecycleBin_) + zen::recycleOrDelete(dirObj.getFullName()); //throw FileError + else + zen::removeDirectory(dirObj.getFullName(), &remCallback); //throw FileError } private: + struct RemoveCallbackImpl : public zen::CallbackRemoveDir + { + RemoveCallbackImpl(ItemDeleter& itemDeleter) : itemDeleter_(itemDeleter) {} + + virtual void onBeforeFileDeletion(const Zstring& filename) { itemDeleter_.notifyFileDeletion (filename); } + virtual void onBeforeDirDeletion (const Zstring& dirname) { itemDeleter_.notifyDirectoryDeletion(dirname ); } + + private: + ItemDeleter& itemDeleter_; + }; + + void notifyFileDeletion (const Zstring& objName) { notifyItemDeletion(txtRemovingFile , objName); } + void notifyDirectoryDeletion(const Zstring& objName) { notifyItemDeletion(txtRemovingDirectory, objName); } + void notifySymlinkDeletion (const Zstring& objName) { notifyItemDeletion(txtRemovingSymlink , objName); } + + void notifyItemDeletion(const std::wstring& statusText, const Zstring& objName) + { + handler_.reportStatus(replaceCpy(statusText, L"%x", fmtFileName(objName))); + } + + DeleteFilesHandler& handler_; + const bool useRecycleBin_; RemoveCallbackImpl remCallback; + + std::wstring txtRemovingFile; + std::wstring txtRemovingDirectory; + std::wstring txtRemovingSymlink; }; @@ -1245,64 +1347,21 @@ void deleteFromGridAndHDOneSide(std::vector& ptrList, bool useRecycleBin, DeleteFilesHandler& handler) { + ItemDeleter deleter(useRecycleBin, handler); + for (auto it = ptrList.begin(); it != ptrList.end(); ++it) //VS 2010 bug prevents replacing this by std::for_each + lamba { FileSystemObject& fsObj = **it; //all pointers are required(!) to be bound - if (fsObj.isEmpty()) //element may be implicitly deleted, e.g. if parent folder was deleted first - continue; - - tryReportingError([&] + if (!fsObj.isEmpty()) //element may be implicitly deleted, e.g. if parent folder was deleted first + tryReportingError([&] { - if (useRecycleBin) - { - if (zen::recycleOrDelete(fsObj.getFullName())) //throw FileError - handler.notifyDeletion(fsObj.getFullName()); - } - else - { - PermanentDeleter delPerm(handler); //throw FileError - fsObj.accept(delPerm); - } - + fsObj.accept(deleter); //throw FileError fsObj.removeObject(); //if directory: removes recursively! }, handler); } } - - -template -void categorize(const std::set& rowsIn, - std::vector& deletePermanent, - std::vector& deleteRecyler, - bool useRecycleBin, - std::map& hasRecyclerBuffer) -{ - auto hasRecycler = [&](const FileSystemObject& fsObj) -> bool - { -#ifdef FFS_WIN - const Zstring& baseDirPf = fsObj.root().getBaseDirPf(); - - auto it = hasRecyclerBuffer.find(baseDirPf); - if (it != hasRecyclerBuffer.end()) - return it->second; - return hasRecyclerBuffer.insert(std::make_pair(baseDirPf, recycleBinStatus(baseDirPf) == STATUS_REC_EXISTS)).first->second; -#else - return true; -#endif - }; - - for (auto it = rowsIn.begin(); it != rowsIn.end(); ++it) - if (!(*it)->isEmpty()) - { - if (useRecycleBin && hasRecycler(**it)) //Windows' ::SHFileOperation() will delete permanently anyway, but we have a superior deletion routine - deleteRecyler.push_back(*it); - else - deletePermanent.push_back(*it); - } -} } - void zen::deleteFromGridAndHD(const std::vector& rowsToDeleteOnLeft, //refresh GUI grid after deletion to remove invalid rows const std::vector& rowsToDeleteOnRight, //all pointers need to be bound! FolderComparison& folderCmp, //attention: rows will be physically deleted! @@ -1378,8 +1437,8 @@ void zen::deleteFromGridAndHD(const std::vector& rowsToDelete std::vector deleteRecylerRight; std::map hasRecyclerBuffer; - categorize(deleteLeft, deletePermanentLeft, deleteRecylerLeft, useRecycleBin, hasRecyclerBuffer); - categorize(deleteRight, deletePermanentRight, deleteRecylerRight, useRecycleBin, hasRecyclerBuffer); + categorize(deleteLeft, deletePermanentLeft, deleteRecylerLeft, useRecycleBin, hasRecyclerBuffer, statusHandler); + categorize(deleteRight, deletePermanentRight, deleteRecylerRight, useRecycleBin, hasRecyclerBuffer, statusHandler); //windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong if (useRecycleBin && diff --git a/algorithm.h b/algorithm.h index 3fcd9436..1c29a5bd 100644 --- a/algorithm.h +++ b/algorithm.h @@ -36,15 +36,13 @@ void setActiveStatus(bool newStatus, FileSystemObject& fsObj); //activate or //manual deletion of files on main grid -std::pair deleteFromGridAndHDPreview( //returns wxString with elements to be deleted and total count of selected(!) objects, NOT total files/dirs! +std::pair deleteFromGridAndHDPreview( //returns string with elements to be deleted and total count of selected(!) objects, NOT total files/dirs! const std::vector& selectionLeft, //all pointers need to be bound! const std::vector& selectionRight, // bool deleteOnBothSides); -class DeleteFilesHandler +struct DeleteFilesHandler { -public: - DeleteFilesHandler() {} virtual ~DeleteFilesHandler() {} enum Response @@ -54,9 +52,7 @@ public: }; virtual Response reportError (const std::wstring& msg) = 0; virtual void reportWarning(const std::wstring& msg, bool& warningActive) = 0; - - //virtual void totalFilesToDelete(int objectsTotal) = 0; //informs about the total number of files to be deleted - virtual void notifyDeletion(const Zstring& currentObject) = 0; //called for each file/folder that has been deleted + virtual void reportStatus (const std::wstring& msg) = 0; }; void deleteFromGridAndHD(const std::vector& rowsToDeleteOnLeft, //refresh GUI grid after deletion to remove invalid rows const std::vector& rowsToDeleteOnRight, //all pointers need to be bound! diff --git a/comparison.cpp b/comparison.cpp index 0832a5bf..56ec2735 100644 --- a/comparison.cpp +++ b/comparison.cpp @@ -112,7 +112,7 @@ void determineExistentDirs(const std::set& dirnames, const Zstring& dirname = *it; callback.reportStatus(replaceCpy(_("Searching for folder %x..."), L"%x", fmtFileName(dirname), false)); - while (boost::get_system_time() < timeMax && !iterCheckDir->timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL))) + while (boost::get_system_time() < timeMax && !iterCheckDir->timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL / 2))) callback.requestUiRefresh(); //may throw! //only (still) existing files should be included in the list @@ -192,10 +192,10 @@ std::wstring checkFolderDependency(const std::vector& folderPairs if (!dependentDirs.empty()) { warningMsg = _("Directories are dependent! Be careful when setting up synchronization rules:"); - for (auto i = dependentDirs.begin(); i != dependentDirs.end(); ++i) + for (auto it = dependentDirs.begin(); it != dependentDirs.end(); ++it) warningMsg += L"\n\n" + - fmtFileName(i->first) + L"\n" + - fmtFileName(i->second); + fmtFileName(it->first) + L"\n" + + fmtFileName(it->second); } return warningMsg; } @@ -205,40 +205,41 @@ std::wstring checkFolderDependency(const std::vector& folderPairs class CmpCallbackImpl : public CompareCallback { public: - CmpCallbackImpl(ProcessCallback& pc, UInt64& bytesReported) : + CmpCallbackImpl(ProcessCallback& pc, Int64& bytesReported) : pc_(pc), bytesReported_(bytesReported) {} - virtual void updateCompareStatus(UInt64 totalBytes) + virtual void updateCompareStatus(Int64 bytesDelta) { //inform about the (differential) processed amount of data - pc_.updateProcessedData(0, to(totalBytes) - to(bytesReported_)); //throw()! -> this ensures client and service provider are in sync! - bytesReported_ = totalBytes; // + pc_.updateProcessedData(0, bytesDelta); //throw()! -> ensure client and service provider are in sync! + bytesReported_ += bytesDelta; // pc_.requestUiRefresh(); //may throw } private: ProcessCallback& pc_; - UInt64& bytesReported_; + Int64& bytesReported_; }; -bool filesHaveSameContentUpdating(const Zstring& filename1, const Zstring& filename2, UInt64 totalBytesToCmp, ProcessCallback& pc) //throw FileError +bool filesHaveSameContentUpdating(const Zstring& filename1, const Zstring& filename2, Int64 expectedBytesToCmp, ProcessCallback& pc) //throw FileError { - UInt64 bytesReported; //amount of bytes that have been compared and communicated to status handler + Int64 bytesReported; //amount of bytes that have been compared and communicated to status handler - //in error situation: undo communication of processed amount of data - zen::ScopeGuard guardStatistics = zen::makeGuard([&] { pc.updateProcessedData(0, -1 * to(bytesReported)); }); + //error = unexpected increase of total workload + zen::ScopeGuard guardStatistics = zen::makeGuard([&] { pc.updateTotalData(0, bytesReported); }); CmpCallbackImpl callback(pc, bytesReported); bool sameContent = filesHaveSameContent(filename1, filename2, callback); //throw FileError - //inform about the (remaining) processed amount of data - pc.updateProcessedData(0, to(totalBytesToCmp) - to(bytesReported)); - bytesReported = totalBytesToCmp; - guardStatistics.dismiss(); + + //update statistics to consider the real amount of data processed: consider short-cut behavior if first bytes differ! + if (bytesReported != expectedBytesToCmp) + pc.updateTotalData(0, bytesReported - expectedBytesToCmp); + return sameContent; } } @@ -286,7 +287,7 @@ ComparisonBuffer::ComparisonBuffer(const std::set& keysToRead, virtual void reportStatus(const std::wstring& statusMsg, int itemsTotal) { - callback_.updateProcessedData(itemsTotal - itemsReported, 0); //processed data is communicated in subfunctions! + callback_.updateProcessedData(itemsTotal - itemsReported, 0); //processed bytes are reported in subfunctions! itemsReported = itemsTotal; callback_.reportStatus(statusMsg); //may throw @@ -316,7 +317,7 @@ ComparisonBuffer::ComparisonBuffer(const std::set& keysToRead, fillBuffer(keysToRead, //in directoryBuffer, //out cb, - UI_UPDATE_INTERVAL / 4); //every ~25 ms + UI_UPDATE_INTERVAL / 2); //every ~50 ms } @@ -331,7 +332,7 @@ void zen::compare(size_t fileTimeTolerance, //specify process and resource handling priorities std::unique_ptr backgroundPrio; if (runWithBackgroundPriority) - backgroundPrio.reset(new ScheduleForBackgroundProcessing); + backgroundPrio = make_unique(); //prevent operating system going into sleep state PreventStandby dummy2; @@ -699,7 +700,7 @@ void ComparisonBuffer::compareByContent(std::vectorgetFullName(), //throw FileError fileObj->getFullName(), - fileObj->getFileSize(), + to(fileObj->getFileSize()), callback_)) { //Caveat: @@ -715,7 +716,7 @@ void ComparisonBuffer::compareByContent(std::vectorsetCategory(); - callback_.updateProcessedData(1, 0); //processed data is communicated in subfunctions! + callback_.updateProcessedData(1, 0); //processed bytes are reported in subfunctions! }, callback_)) fileObj->setCategoryConflict(_("Conflict detected:") + L"\n" + _("Comparing files by content failed.")); diff --git a/file_hierarchy.h b/file_hierarchy.h index 8f4b1afb..f51e76bb 100644 --- a/file_hierarchy.h +++ b/file_hierarchy.h @@ -721,8 +721,7 @@ const Zstring& FileSystemObject::getShortName() const } -template -inline +template inline Zstring FileSystemObject::getRelativeName() const { return isEmpty() ? Zstring() : parent_.getObjRelativeNamePf() + getShortName(); @@ -743,8 +742,7 @@ Zstring FileSystemObject::getObjShortName() const } -template -inline +template inline Zstring FileSystemObject::getFullName() const { return isEmpty() ? Zstring() : getBaseDirPf() + parent_.getObjRelativeNamePf() + getShortName(); diff --git a/lib/binary.cpp b/lib/binary.cpp index 10994cc9..4ef30c15 100644 --- a/lib/binary.cpp +++ b/lib/binary.cpp @@ -85,7 +85,6 @@ bool zen::filesHaveSameContent(const Zstring& filename1, const Zstring& filename FileInput file2(filename2); // BufferSize bufferSize; - UInt64 bytesCompared; TickVal lastDelayViolation = getTicks(); @@ -96,14 +95,19 @@ bool zen::filesHaveSameContent(const Zstring& filename1, const Zstring& filename const TickVal startTime = getTicks(); - const size_t length1 = file1.read(&memory1[0], bufferSize); //returns actual number of bytes read; throw FileError() - const size_t length2 = file2.read(&memory2[0], bufferSize); // + const size_t length1 = file1.read(&memory1[0], bufferSize); //throw FileError() + const size_t length2 = file2.read(&memory2[0], bufferSize); //returns actual number of bytes read + //send progress updates immediately after reading to reliably allow speed calculations for our clients! + callback.updateCompareStatus(to(std::max(length1, length2))); - const TickVal now = getTicks(); + if (length1 != length2 || ::memcmp(&memory1[0], &memory2[0], length1) != 0) + return false; //-------- dynamically set buffer size to keep callback interval between 100 - 500ms --------------------- if (TICKS_PER_SEC > 0) { + const TickVal now = getTicks(); + const std::int64_t loopTime = dist(startTime, now) * 1000 / TICKS_PER_SEC; //unit: [ms] if (loopTime < 100) { @@ -120,12 +124,6 @@ bool zen::filesHaveSameContent(const Zstring& filename1, const Zstring& filename } } //------------------------------------------------------------------------------------------------ - - if (length1 != length2 || ::memcmp(&memory1[0], &memory2[0], length1) != 0) - return false; - - bytesCompared += length1; - callback.updateCompareStatus(bytesCompared); //send progress updates } while (!file1.eof()); diff --git a/lib/binary.h b/lib/binary.h index 83409e19..8a4abe6b 100644 --- a/lib/binary.h +++ b/lib/binary.h @@ -16,7 +16,7 @@ namespace zen struct CompareCallback { virtual ~CompareCallback() {} - virtual void updateCompareStatus(UInt64 totalBytes) = 0; + virtual void updateCompareStatus(Int64 bytesDelta) = 0; }; bool filesHaveSameContent(const Zstring& filename1, const Zstring& filename2, CompareCallback& callback); //throw FileError diff --git a/lib/dir_exist_async.h b/lib/dir_exist_async.h index a02facad..306c9ab9 100644 --- a/lib/dir_exist_async.h +++ b/lib/dir_exist_async.h @@ -31,7 +31,7 @@ bool dirExistsUpdating(const Zstring& dirname, bool allowUserInteraction, Proces //2. check dir existence return zen::dirExists(dirname); }); - while (!ft.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL))) + while (!ft.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL / 2))) procCallback.requestUiRefresh(); //may throw! return ft.get(); } diff --git a/lib/localization.cpp b/lib/localization.cpp index 47ed1881..78e03545 100644 --- a/lib/localization.cpp +++ b/lib/localization.cpp @@ -28,23 +28,22 @@ namespace class FFSLocale : public TranslationHandler { public: - FFSLocale(const wxString& filename, wxLanguage languageId); //throw lngfile::ParsingError, PluralForm::ParsingError + FFSLocale(const wxString& filename, wxLanguage languageId); //throw lngfile::ParsingError, parse_plural::ParsingError wxLanguage langId() const { return langId_; } virtual std::wstring translate(const std::wstring& text) { //look for translation in buffer table - const Translation::const_iterator it = transMapping.find(text); - if (it != transMapping.end()) + auto it = transMapping.find(text); + if (it != transMapping.end() && !it->second.empty()) return it->second; - return text; //fallback } virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, int n) { - TranslationPlural::const_iterator it = transMappingPl.find(std::make_pair(singular, plural)); + auto it = transMappingPl.find(std::make_pair(singular, plural)); if (it != transMappingPl.end()) { const int formNo = pluralParser->getForm(n); @@ -55,17 +54,17 @@ public: } private: - typedef std::map Translation; + typedef hash_map Translation; //hash_map is 15% faster than std::map on GCC typedef std::map, std::vector > TranslationPlural; Translation transMapping; //map original text |-> translation TranslationPlural transMappingPl; - std::unique_ptr pluralParser; + std::unique_ptr pluralParser; //bound! wxLanguage langId_; }; -FFSLocale::FFSLocale(const wxString& filename, wxLanguage languageId) : langId_(languageId) //throw lngfile::ParsingError, PluralForm::ParsingError +FFSLocale::FFSLocale(const wxString& filename, wxLanguage languageId) : langId_(languageId) //throw lngfile::ParsingError, parse_plural::ParsingError { std::string inputStream; try @@ -86,7 +85,6 @@ FFSLocale::FFSLocale(const wxString& filename, wxLanguage languageId) : langId_( { const std::wstring original = utfCvrtTo(i->first); const std::wstring translation = utfCvrtTo(i->second); - assert(!translation.empty()); transMapping.insert(std::make_pair(original, translation)); } @@ -100,13 +98,10 @@ FFSLocale::FFSLocale(const wxString& filename, wxLanguage languageId) : langId_( for (lngfile::PluralForms::const_iterator j = plForms.begin(); j != plForms.end(); ++j) plFormsWide.push_back(utfCvrtTo(*j)); - assert(!plFormsWide.empty()); - transMappingPl.insert(std::make_pair(std::make_pair(singular, plural), plFormsWide)); } - pluralParser.reset(new PluralForm(header.pluralDefinition)); //throw PluralForm::ParsingError -} + pluralParser.reset(new parse_plural::PluralForm(header.pluralDefinition)); //throw parse_plural::ParsingError } @@ -151,6 +146,7 @@ struct LessTranslation : public std::binary_functionLanguage; newEntry.languageName = utfCvrtTo(lngHeader.languageName); - newEntry.languageFile = utfCvrtTo(*i); + newEntry.languageFile = utfCvrtTo(*it); newEntry.translatorName = utfCvrtTo(lngHeader.translatorName); newEntry.languageFlag = utfCvrtTo(lngHeader.flagFile); locMapping.push_back(newEntry); @@ -205,12 +201,59 @@ ExistingTranslations::ExistingTranslations() } +const std::vector& ExistingTranslations::get() +{ + static ExistingTranslations instance; + return instance.locMapping; +} + + namespace { wxLanguage mapLanguageDialect(wxLanguage language) { - switch (static_cast(language)) //map language dialects + switch (static_cast(language)) //avoid enumeration value wxLANGUAGE_*' not handled in switch [-Wswitch-enum] { + //variants of wxLANGUAGE_ARABIC + case wxLANGUAGE_ARABIC_ALGERIA: + case wxLANGUAGE_ARABIC_BAHRAIN: + case wxLANGUAGE_ARABIC_EGYPT: + case wxLANGUAGE_ARABIC_IRAQ: + case wxLANGUAGE_ARABIC_JORDAN: + case wxLANGUAGE_ARABIC_KUWAIT: + case wxLANGUAGE_ARABIC_LEBANON: + case wxLANGUAGE_ARABIC_LIBYA: + case wxLANGUAGE_ARABIC_MOROCCO: + case wxLANGUAGE_ARABIC_OMAN: + case wxLANGUAGE_ARABIC_QATAR: + case wxLANGUAGE_ARABIC_SAUDI_ARABIA: + case wxLANGUAGE_ARABIC_SUDAN: + case wxLANGUAGE_ARABIC_SYRIA: + case wxLANGUAGE_ARABIC_TUNISIA: + case wxLANGUAGE_ARABIC_UAE: + case wxLANGUAGE_ARABIC_YEMEN: + return wxLANGUAGE_ARABIC; + + //variants of wxLANGUAGE_ENGLISH_UK + case wxLANGUAGE_ENGLISH_AUSTRALIA: + case wxLANGUAGE_ENGLISH_NEW_ZEALAND: + case wxLANGUAGE_ENGLISH_TRINIDAD: + case wxLANGUAGE_ENGLISH_CARIBBEAN: + case wxLANGUAGE_ENGLISH_JAMAICA: + case wxLANGUAGE_ENGLISH_BELIZE: + case wxLANGUAGE_ENGLISH_EIRE: + case wxLANGUAGE_ENGLISH_SOUTH_AFRICA: + case wxLANGUAGE_ENGLISH_ZIMBABWE: + case wxLANGUAGE_ENGLISH_BOTSWANA: + case wxLANGUAGE_ENGLISH_DENMARK: + return wxLANGUAGE_ENGLISH_UK; + + //variants of wxLANGUAGE_ENGLISH_US + case wxLANGUAGE_ENGLISH: + case wxLANGUAGE_ENGLISH_CANADA: + case wxLANGUAGE_ENGLISH_PHILIPPINES: + return wxLANGUAGE_ENGLISH_US; + //variants of wxLANGUAGE_GERMAN case wxLANGUAGE_GERMAN_AUSTRIAN: case wxLANGUAGE_GERMAN_BELGIUM: @@ -281,6 +324,7 @@ wxLanguage mapLanguageDialect(wxLanguage language) case wxLANGUAGE_NORWEGIAN_NYNORSK: return wxLANGUAGE_NORWEGIAN_BOKMAL; + //languages without variants: //case wxLANGUAGE_CZECH: //case wxLANGUAGE_DANISH: //case wxLANGUAGE_FINNISH: @@ -296,41 +340,6 @@ wxLanguage mapLanguageDialect(wxLanguage language) //case wxLANGUAGE_KOREAN: //case wxLANGUAGE_UKRAINIAN: //case wxLANGUAGE_CROATIAN: - - //variants of wxLANGUAGE_ARABIC - case wxLANGUAGE_ARABIC_ALGERIA: - case wxLANGUAGE_ARABIC_BAHRAIN: - case wxLANGUAGE_ARABIC_EGYPT: - case wxLANGUAGE_ARABIC_IRAQ: - case wxLANGUAGE_ARABIC_JORDAN: - case wxLANGUAGE_ARABIC_KUWAIT: - case wxLANGUAGE_ARABIC_LEBANON: - case wxLANGUAGE_ARABIC_LIBYA: - case wxLANGUAGE_ARABIC_MOROCCO: - case wxLANGUAGE_ARABIC_OMAN: - case wxLANGUAGE_ARABIC_QATAR: - case wxLANGUAGE_ARABIC_SAUDI_ARABIA: - case wxLANGUAGE_ARABIC_SUDAN: - case wxLANGUAGE_ARABIC_SYRIA: - case wxLANGUAGE_ARABIC_TUNISIA: - case wxLANGUAGE_ARABIC_UAE: - case wxLANGUAGE_ARABIC_YEMEN: - return wxLANGUAGE_ARABIC; - - //variants of wxLANGUAGE_ENGLISH_UK - case wxLANGUAGE_ENGLISH_AUSTRALIA: - case wxLANGUAGE_ENGLISH_NEW_ZEALAND: - case wxLANGUAGE_ENGLISH_TRINIDAD: - case wxLANGUAGE_ENGLISH_CARIBBEAN: - case wxLANGUAGE_ENGLISH_JAMAICA: - case wxLANGUAGE_ENGLISH_BELIZE: - case wxLANGUAGE_ENGLISH_EIRE: - case wxLANGUAGE_ENGLISH_SOUTH_AFRICA: - case wxLANGUAGE_ENGLISH_ZIMBABWE: - case wxLANGUAGE_ENGLISH_BOTSWANA: - case wxLANGUAGE_ENGLISH_DENMARK: - return wxLANGUAGE_ENGLISH_UK; - default: return language; } @@ -391,7 +400,7 @@ void zen::setLanguage(int language) //throw FileError else try { - zen::setTranslator(new FFSLocale(languageFile, static_cast(language))); //throw lngfile::ParsingError, PluralForm::ParsingError + zen::setTranslator(new FFSLocale(languageFile, static_cast(language))); //throw lngfile::ParsingError, parse_plural::ParsingError } catch (lngfile::ParsingError& e) { @@ -400,7 +409,7 @@ void zen::setLanguage(int language) //throw FileError L"%y", numberTo(e.row + 1)), L"%z", numberTo(e.col + 1))); } - catch (PluralForm::ParsingError&) + catch (parse_plural::ParsingError&) { throw FileError(L"Invalid Plural Form"); } @@ -422,10 +431,3 @@ int zen::retrieveSystemLanguage() { return mapLanguageDialect(static_cast(wxLocale::GetSystemLanguage())); } - - -const std::vector& ExistingTranslations::get() -{ - static ExistingTranslations instance; - return instance.locMapping; -} diff --git a/lib/lock_holder.h b/lib/lock_holder.h index dd997853..d4fe27a9 100644 --- a/lib/lock_holder.h +++ b/lib/lock_holder.h @@ -46,7 +46,7 @@ public: try { - //lock file creation is synchronous and may block noticably for very slow devices (usb sticks, mapped cloud storages) + //lock file creation is synchronous and may block noticeably for very slow devices (usb sticks, mapped cloud storages) procCallback.forceUiRefresh(); //=> make sure the right folder name is shown on GUI during this time! lockHolder.insert(std::make_pair(dirnameFmt, DirLock(dirnameFmt + Zstr("sync") + LOCK_FILE_ENDING, &callback))); } diff --git a/lib/parse_lng.h b/lib/parse_lng.h index 92564a1e..b5afe50c 100644 --- a/lib/parse_lng.h +++ b/lib/parse_lng.h @@ -51,19 +51,7 @@ void parseLng(const std::string& fileStream, TransHeader& header, TranslationMap void parseHeader(const std::string& fileStream, TransHeader& header); //throw ParsingError class TranslationList; //unordered list of unique translation items -void generateLng(const TranslationList& in, const TransHeader& header, std::string& fileStream); - - - - - - - - - - - - +std::string generateLng(const TranslationList& in, const TransHeader& header); @@ -114,7 +102,7 @@ public: } private: - friend void generateLng(const TranslationList& in, const TransHeader& header, std::string& fileStream); + friend std::string generateLng(const TranslationList& in, const TransHeader& header); struct Item {virtual ~Item() {} }; struct RegularItem : public Item { RegularItem(const TranslationMap ::value_type& val) : value(val) {} TranslationMap ::value_type value; }; @@ -218,7 +206,11 @@ private: class Scanner { public: - Scanner(const std::string& fileStream) : stream(fileStream), pos(stream.begin()) {} + Scanner(const std::string& fileStream) : stream(fileStream), pos(stream.begin()) + { + if (zen::startsWith(stream, zen::BYTE_ORDER_MARK_UTF8)) + pos += zen::strLength(zen::BYTE_ORDER_MARK_UTF8); + } Token nextToken() { @@ -228,19 +220,19 @@ public: if (pos == stream.end()) return Token(Token::TK_END); - for (KnownTokens::TokenMap::const_iterator i = KnownTokens::asList().begin(); i != KnownTokens::asList().end(); ++i) - if (startsWith(i->second)) + for (auto it = KnownTokens::asList().begin(); it != KnownTokens::asList().end(); ++it) + if (startsWith(it->second)) { - pos += i->second.size(); - return Token(i->first); + pos += it->second.size(); + return Token(it->first); } //rest must be "text" - std::string::const_iterator textBegin = pos; + std::string::const_iterator itBegin = pos; while (pos != stream.end() && !startsWithKnownTag()) pos = std::find(pos + 1, stream.end(), '<'); - std::string text(textBegin, pos); + std::string text(itBegin, pos); normalize(text); //remove whitespace from end ect. @@ -255,13 +247,8 @@ public: size_t posRow() const //current row beginning with 0 { //count line endings - size_t crSum = 0; //carriage returns - size_t nlSum = 0; //new lines - for (auto it = stream.begin(); it != pos; ++it) - if (*it == '\r') - ++crSum; - else if (*it == '\n') - ++nlSum; + const size_t crSum = std::count(stream.begin(), pos, '\r'); //carriage returns + const size_t nlSum = std::count(stream.begin(), pos, '\n'); //new lines assert(crSum == 0 || nlSum == 0 || crSum == nlSum); return std::max(crSum, nlSum); //be compatible with Linux/Mac/Win } @@ -294,32 +281,15 @@ private: static void normalize(std::string& text) { - //remmove whitespace from end - while (!text.empty() && zen::isWhiteSpace(*text.rbegin())) - text.resize(text.size() - 1); - - //ensure c-style line breaks + zen::trim(text); //remmove whitespace from end //Delimiter: //---------- //Linux: 0xA \n //Mac: 0xD \r //Win: 0xD 0xA \r\n <- language files are in Windows format - if (text.find('\r') != std::string::npos) - { - std::string tmp; - for (std::string::const_iterator i = text.begin(); i != text.end(); ++i) - if (*i == '\r') - { - std::string::const_iterator next = i + 1; - if (next != text.end() && *next == '\n') - ++i; - tmp += '\n'; - } - else - tmp += *i; - text = tmp; - } + zen::replace(text, "\r\n", '\n'); // + zen::replace(text, "\r", '\n'); //ensure c-style line breaks } const std::string stream; @@ -334,7 +304,6 @@ public: void parse(TranslationMap& out, TranslationPluralMap& pluralOut, TransHeader& header) { - //header parseHeader(header); //items @@ -399,9 +368,7 @@ private: nextToken(); } consumeToken(Token::TK_TRG_END); - - if (!translation.empty()) //only add if translation is existing - out.insert(std::make_pair(original, translation)); + out.insert(std::make_pair(original, translation)); } void parsePlural(TranslationPluralMap& pluralOut, int formCount) @@ -437,20 +404,23 @@ private: throw ParsingError(scn.posRow(), scn.posCol()); consumeToken(Token::TK_TRG_END); - - if (!pluralList.empty()) //only add if translation is existing - pluralOut.insert(std::make_pair(SingularPluralPair(engSingular, engPlural), pluralList)); + pluralOut.insert(std::make_pair(SingularPluralPair(engSingular, engPlural), pluralList)); } void nextToken() { tk = scn.nextToken(); } const Token& token() const { return tk; } - void consumeToken(Token::Type t) + void consumeToken(Token::Type t) //throw ParsingError + { + expectToken(t); //throw ParsingError + nextToken(); + } + + void expectToken(Token::Type t) //throw ParsingError { if (token().type != t) throw ParsingError(scn.posRow(), scn.posCol()); - nextToken(); } Scanner scn; @@ -464,24 +434,22 @@ void parseLng(const std::string& fileStream, TransHeader& header, TranslationMap out.clear(); pluralOut.clear(); - //skip UTF-8 Byte Ordering Mark - LngParser prs(zen::startsWith(fileStream, zen::BYTE_ORDER_MARK_UTF8) ? fileStream.substr(3) : fileStream); - prs.parse(out, pluralOut, header); + LngParser(fileStream).parse(out, pluralOut, header); } inline void parseHeader(const std::string& fileStream, TransHeader& header) //throw ParsingError { - //skip UTF-8 Byte Ordering Mark - LngParser prs(zen::startsWith(fileStream, zen::BYTE_ORDER_MARK_UTF8) ? fileStream.substr(3) : fileStream); - prs.parseHeader(header); + LngParser(fileStream).parseHeader(header); } inline void formatMultiLineText(std::string& text) { + assert(!zen::contains(text, "\r\n")); + if (text.find('\n') != std::string::npos) //multiple lines { if (*text.begin() != '\n') @@ -492,49 +460,46 @@ void formatMultiLineText(std::string& text) } -const std::string LB = "\n"; -const std::string TAB = "\t"; - - -void generateLng(const TranslationList& in, const TransHeader& header, std::string& fileStream) +std::string generateLng(const TranslationList& in, const TransHeader& header) { + std::string out; //header - fileStream += KnownTokens::text(Token::TK_HEADER_BEGIN) + LB; + out += KnownTokens::text(Token::TK_HEADER_BEGIN) + '\n'; - fileStream += TAB + KnownTokens::text(Token::TK_LANG_NAME_BEGIN); - fileStream += header.languageName; - fileStream += KnownTokens::text(Token::TK_LANG_NAME_END) + LB; + out += '\t' + KnownTokens::text(Token::TK_LANG_NAME_BEGIN); + out += header.languageName; + out += KnownTokens::text(Token::TK_LANG_NAME_END) + '\n'; - fileStream += TAB + KnownTokens::text(Token::TK_TRANS_NAME_BEGIN); - fileStream += header.translatorName; - fileStream += KnownTokens::text(Token::TK_TRANS_NAME_END) + LB; + out += '\t' + KnownTokens::text(Token::TK_TRANS_NAME_BEGIN); + out += header.translatorName; + out += KnownTokens::text(Token::TK_TRANS_NAME_END) + '\n'; - fileStream += TAB + KnownTokens::text(Token::TK_LOCALE_NAME_BEGIN); - fileStream += header.localeName; - fileStream += KnownTokens::text(Token::TK_LOCALE_NAME_END) + LB; + out += '\t' + KnownTokens::text(Token::TK_LOCALE_NAME_BEGIN); + out += header.localeName; + out += KnownTokens::text(Token::TK_LOCALE_NAME_END) + '\n'; - fileStream += TAB + KnownTokens::text(Token::TK_FLAG_FILE_BEGIN); - fileStream += header.flagFile; - fileStream += KnownTokens::text(Token::TK_FLAG_FILE_END) + LB; + out += '\t' + KnownTokens::text(Token::TK_FLAG_FILE_BEGIN); + out += header.flagFile; + out += KnownTokens::text(Token::TK_FLAG_FILE_END) + '\n'; - fileStream += TAB + KnownTokens::text(Token::TK_PLURAL_COUNT_BEGIN); - fileStream += zen::numberTo(header.pluralCount); - fileStream += KnownTokens::text(Token::TK_PLURAL_COUNT_END) + LB; + out += '\t' + KnownTokens::text(Token::TK_PLURAL_COUNT_BEGIN); + out += zen::numberTo(header.pluralCount); + out += KnownTokens::text(Token::TK_PLURAL_COUNT_END) + '\n'; - fileStream += TAB + KnownTokens::text(Token::TK_PLURAL_DEF_BEGIN); - fileStream += header.pluralDefinition; - fileStream += KnownTokens::text(Token::TK_PLURAL_DEF_END) + LB; + out += '\t' + KnownTokens::text(Token::TK_PLURAL_DEF_BEGIN); + out += header.pluralDefinition; + out += KnownTokens::text(Token::TK_PLURAL_DEF_END) + '\n'; - fileStream += KnownTokens::text(Token::TK_HEADER_END) + LB; + out += KnownTokens::text(Token::TK_HEADER_END) + '\n'; - fileStream += LB; + out += '\n'; //items - for (std::vector::const_iterator i = in.sequence.begin(); i != in.sequence.end(); ++i) + for (auto it = in.sequence.begin(); it != in.sequence.end(); ++it) { - const TranslationList::RegularItem* regular = dynamic_cast(*i); - const TranslationList::PluralItem* plural = dynamic_cast(*i); + const TranslationList::RegularItem* regular = dynamic_cast(*it); + const TranslationList::PluralItem* plural = dynamic_cast(*it); if (regular) { @@ -544,13 +509,13 @@ void generateLng(const TranslationList& in, const TransHeader& header, std::stri formatMultiLineText(original); formatMultiLineText(translation); - fileStream += KnownTokens::text(Token::TK_SRC_BEGIN); - fileStream += original; - fileStream += KnownTokens::text(Token::TK_SRC_END) + LB; + out += KnownTokens::text(Token::TK_SRC_BEGIN); + out += original; + out += KnownTokens::text(Token::TK_SRC_END) + '\n'; - fileStream += KnownTokens::text(Token::TK_TRG_BEGIN); - fileStream += translation; - fileStream += KnownTokens::text(Token::TK_TRG_END) + LB + LB; + out += KnownTokens::text(Token::TK_TRG_BEGIN); + out += translation; + out += KnownTokens::text(Token::TK_TRG_END) + '\n' + '\n'; } else if (plural) @@ -562,34 +527,36 @@ void generateLng(const TranslationList& in, const TransHeader& header, std::stri formatMultiLineText(engSingular); formatMultiLineText(engPlural); - fileStream += KnownTokens::text(Token::TK_SRC_BEGIN) + LB; - fileStream += KnownTokens::text(Token::TK_PLURAL_BEGIN); - fileStream += engSingular; - fileStream += KnownTokens::text(Token::TK_PLURAL_END) + LB; - fileStream += KnownTokens::text(Token::TK_PLURAL_BEGIN); - fileStream += engPlural; - fileStream += KnownTokens::text(Token::TK_PLURAL_END) + LB; - fileStream += KnownTokens::text(Token::TK_SRC_END) + LB; + out += KnownTokens::text(Token::TK_SRC_BEGIN) + '\n'; + out += KnownTokens::text(Token::TK_PLURAL_BEGIN); + out += engSingular; + out += KnownTokens::text(Token::TK_PLURAL_END) + '\n'; + out += KnownTokens::text(Token::TK_PLURAL_BEGIN); + out += engPlural; + out += KnownTokens::text(Token::TK_PLURAL_END) + '\n'; + out += KnownTokens::text(Token::TK_SRC_END) + '\n'; - fileStream += KnownTokens::text(Token::TK_TRG_BEGIN); - if (!forms.empty()) fileStream += LB; + out += KnownTokens::text(Token::TK_TRG_BEGIN); + if (!forms.empty()) out += '\n'; for (PluralForms::const_iterator j = forms.begin(); j != forms.end(); ++j) { std::string plForm = *j; formatMultiLineText(plForm); - fileStream += KnownTokens::text(Token::TK_PLURAL_BEGIN); - fileStream += plForm; - fileStream += KnownTokens::text(Token::TK_PLURAL_END) + LB; + out += KnownTokens::text(Token::TK_PLURAL_BEGIN); + out += plForm; + out += KnownTokens::text(Token::TK_PLURAL_END) + '\n'; } - fileStream += KnownTokens::text(Token::TK_TRG_END) + LB + LB; + out += KnownTokens::text(Token::TK_TRG_END) + '\n' + '\n'; } else { throw std::logic_error("that's what you get for brittle design ;)"); } } + assert(!zen::contains(out, "\r\n") && !zen::contains(out, "\r")); + return zen::replaceCpy(out, '\n', "\r\n"); //back to win line endings } } diff --git a/lib/parse_plural.h b/lib/parse_plural.h index 7af6809e..c3591881 100644 --- a/lib/parse_plural.h +++ b/lib/parse_plural.h @@ -7,11 +7,45 @@ #ifndef PARSE_PLURAL_H_INCLUDED #define PARSE_PLURAL_H_INCLUDED -#include #include #include #include +namespace parse_plural +{ +//expression interface +struct Expression { virtual ~Expression() {} }; + +template +struct Expr : public Expression +{ + typedef T ValueType; + virtual ValueType eval() const = 0; +}; + + +class ParsingError {}; + +class PluralForm +{ +public: + PluralForm(const std::string& stream); //throw ParsingError + int getForm(int n) const { n_ = n ; return expr->eval(); } + +private: + std::shared_ptr> expr; + mutable int n_; +}; + + + + + + + + +//--------------------------- implementation --------------------------- + //http://www.gnu.org/software/hello/manual/gettext/Plural-forms.html //http://translate.sourceforge.net/wiki/l10n/pluralforms /* @@ -52,55 +86,57 @@ pm-expression: variable-number-n-expression constant-number-expression ( expression ) -*/ - -//expression interface -struct Expression { virtual ~Expression() {} }; +.po format,e.g.: (n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2) +*/ -template -struct Expr : public Expression +namespace implementation { - typedef T ValueType; - virtual ValueType eval() const = 0; -}; - //specific binary expression based on STL function objects template struct BinaryExp : public Expr { - typedef const Expr ExpLhs; - typedef const Expr ExpRhs; + typedef std::shared_ptr> ExpLhs; + typedef std::shared_ptr> ExpRhs; - BinaryExp(const ExpLhs& lhs, const ExpRhs& rhs, StlOp biop) : lhs_(lhs), rhs_(rhs), biop_(biop) {} - virtual typename StlOp::result_type eval() const { return biop_(lhs_.eval(), rhs_.eval()); } - const ExpLhs& lhs_; - const ExpRhs& rhs_; + BinaryExp(const ExpLhs& lhs, const ExpRhs& rhs, StlOp biop) : lhs_(lhs), rhs_(rhs), biop_(biop) { assert(lhs && rhs); } + virtual typename StlOp::result_type eval() const { return biop_(lhs_->eval(), rhs_->eval()); } +private: + ExpLhs lhs_; + ExpRhs rhs_; StlOp biop_; }; template inline -BinaryExp makeBiExp(const Expression& lhs, const Expression& rhs, StlOp biop) //throw std::bad_cast +std::shared_ptr> makeBiExp(const std::shared_ptr& lhs, const std::shared_ptr& rhs, StlOp biop) //throw ParsingError { - return BinaryExp(dynamic_cast&>(lhs), //throw std::bad_cast - dynamic_cast&>(rhs), biop); // + auto exLeft = std::dynamic_pointer_cast>(lhs); + auto exRight = std::dynamic_pointer_cast>(rhs); + if (!exLeft || !exRight) + throw ParsingError(); + return std::make_shared>(exLeft, exRight, biop); } template -struct TernaryExp : public Expr +struct ConditionalExp : public Expr { - TernaryExp(const Expr& ifExp, const Expr& thenExp, const Expr& elseExp) : ifExp_(ifExp), thenExp_(thenExp), elseExp_(elseExp) {} - virtual typename Expr::ValueType eval() const { return ifExp_.eval() ? thenExp_.eval() : elseExp_.eval(); } - const Expr& ifExp_; - const Expr& thenExp_; - const Expr& elseExp_; + ConditionalExp(const std::shared_ptr>& ifExp, + const std::shared_ptr>& thenExp, + const std::shared_ptr>& elseExp) : ifExp_(ifExp), thenExp_(thenExp), elseExp_(elseExp) { assert(ifExp && thenExp && elseExp); } + + virtual typename Expr::ValueType eval() const { return ifExp_->eval() ? thenExp_->eval() : elseExp_->eval(); } +private: + std::shared_ptr> ifExp_; + std::shared_ptr> thenExp_; + std::shared_ptr> elseExp_; }; struct ConstNumberExp : public Expr { ConstNumberExp(int n) : n_(n) {} virtual int eval() const { return n_; } +private: int n_; }; @@ -108,296 +144,276 @@ struct VariableNumberNExp : public Expr { VariableNumberNExp(int& n) : n_(n) {} virtual int eval() const { return n_; } +private: int& n_; }; +//------------------------------------------------------------------------------- -class PluralForm +struct Token { -public: - struct ParsingError {}; - - //.po format,e.g.: (n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2) - PluralForm(const std::string& phrase) : n_(0) + enum Type { - Parser(phrase, //in - expr, n_, dump); //out - } + TK_TERNARY_QUEST, + TK_TERNARY_COLON, + TK_OR, + TK_AND, + TK_EQUAL, + TK_NOT_EQUAL, + TK_LESS, + TK_LESS_EQUAL, + TK_GREATER, + TK_GREATER_EQUAL, + TK_MODULUS, + TK_VARIABLE_N, + TK_CONST_NUMBER, + TK_BRACKET_LEFT, + TK_BRACKET_RIGHT, + TK_END + }; - int getForm(int n) const { n_ = n ; return expr->eval(); } + Token(Type t) : type(t), number(0) {} + Token(int num) : type(TK_CONST_NUMBER), number(num) {} -private: - typedef std::list > DumpList; + Type type; + int number; //if type == TK_CONST_NUMBER +}; - struct Token +class Scanner +{ +public: + Scanner(const std::string& stream) : stream_(stream), pos(stream_.begin()) { - enum Type - { - TK_TERNARY_QUEST, - TK_TERNARY_COLON, - TK_OR, - TK_AND, - TK_EQUAL, - TK_NOT_EQUAL, - TK_LESS, - TK_LESS_EQUAL, - TK_GREATER, - TK_GREATER_EQUAL, - TK_MODULUS, - TK_VARIABLE_N, - TK_CONST_NUMBER, - TK_BRACKET_LEFT, - TK_BRACKET_RIGHT, - TK_END - }; - - Token(Type t) : type(t), number(0) {} - Token(int num) : type(TK_CONST_NUMBER), number(num) {} - - Type type; - int number; //if type == TK_CONST_NUMBER - }; + tokens.push_back(std::make_pair("?" , Token::TK_TERNARY_QUEST)); + tokens.push_back(std::make_pair(":" , Token::TK_TERNARY_COLON)); + tokens.push_back(std::make_pair("||", Token::TK_OR )); + tokens.push_back(std::make_pair("&&", Token::TK_AND )); + tokens.push_back(std::make_pair("==", Token::TK_EQUAL )); + tokens.push_back(std::make_pair("!=", Token::TK_NOT_EQUAL )); + tokens.push_back(std::make_pair("<=", Token::TK_LESS_EQUAL )); + tokens.push_back(std::make_pair("<" , Token::TK_LESS )); + tokens.push_back(std::make_pair(">=", Token::TK_GREATER_EQUAL)); + tokens.push_back(std::make_pair(">" , Token::TK_GREATER )); + tokens.push_back(std::make_pair("%" , Token::TK_MODULUS )); + tokens.push_back(std::make_pair("n" , Token::TK_VARIABLE_N )); + tokens.push_back(std::make_pair("N" , Token::TK_VARIABLE_N )); + tokens.push_back(std::make_pair("(" , Token::TK_BRACKET_LEFT )); + tokens.push_back(std::make_pair(")" , Token::TK_BRACKET_RIGHT)); + } - class Scanner + Token nextToken() { - public: - Scanner(const std::string& phrase) : stream(phrase), pos(stream.begin()) - { - tokens.push_back(std::make_pair("?" , Token::TK_TERNARY_QUEST)); - tokens.push_back(std::make_pair(":" , Token::TK_TERNARY_COLON)); - tokens.push_back(std::make_pair("||", Token::TK_OR )); - tokens.push_back(std::make_pair("&&", Token::TK_AND )); - tokens.push_back(std::make_pair("==", Token::TK_EQUAL )); - tokens.push_back(std::make_pair("!=", Token::TK_NOT_EQUAL )); - tokens.push_back(std::make_pair("<=", Token::TK_LESS_EQUAL )); - tokens.push_back(std::make_pair("<" , Token::TK_LESS )); - tokens.push_back(std::make_pair(">=", Token::TK_GREATER_EQUAL)); - tokens.push_back(std::make_pair(">" , Token::TK_GREATER )); - tokens.push_back(std::make_pair("%" , Token::TK_MODULUS )); - tokens.push_back(std::make_pair("n" , Token::TK_VARIABLE_N )); - tokens.push_back(std::make_pair("N" , Token::TK_VARIABLE_N )); - tokens.push_back(std::make_pair("(" , Token::TK_BRACKET_LEFT )); - tokens.push_back(std::make_pair(")" , Token::TK_BRACKET_RIGHT)); - } - - Token nextToken() - { - //skip whitespace - pos = std::find_if(pos, stream.end(), [](char c) { return !zen::isWhiteSpace(c); }); + //skip whitespace + pos = std::find_if(pos, stream_.end(), [](char c) { return !zen::isWhiteSpace(c); }); - if (pos == stream.end()) - return Token::TK_END; + if (pos == stream_.end()) + return Token::TK_END; - for (auto iter = tokens.begin(); iter != tokens.end(); ++iter) - if (startsWith(iter->first)) - { - pos += iter->first.size(); - return Token(iter->second); - } - - auto digitEnd = std::find_if(pos, stream.end(), [](char c) { return !zen::isDigit(c); }); - - if (digitEnd != pos) + for (auto iter = tokens.begin(); iter != tokens.end(); ++iter) + if (startsWith(iter->first)) { - int number = zen::stringTo(std::string(&*pos, digitEnd - pos)); - pos = digitEnd; - return number; + pos += iter->first.size(); + return Token(iter->second); } - throw ParsingError(); //unknown token - } + auto digitEnd = std::find_if(pos, stream_.end(), [](char c) { return !zen::isDigit(c); }); - private: - bool startsWith(const std::string& prefix) const + if (digitEnd != pos) { - if (stream.end() - pos < static_cast(prefix.size())) - return false; - return std::equal(prefix.begin(), prefix.end(), pos); + int number = zen::stringTo(std::string(pos, digitEnd)); + pos = digitEnd; + return number; } - typedef std::vector > TokenList; - TokenList tokens; + throw ParsingError(); //unknown token + } - const std::string stream; - std::string::const_iterator pos; - }; +private: + bool startsWith(const std::string& prefix) const + { + if (stream_.end() - pos < static_cast(prefix.size())) + return false; + return std::equal(prefix.begin(), prefix.end(), pos); + } + typedef std::vector > TokenList; + TokenList tokens; + + const std::string stream_; + std::string::const_iterator pos; +}; + +//------------------------------------------------------------------------------- + +class Parser +{ +public: + Parser(const std::string& stream, int& n) : + scn(stream), + tk(scn.nextToken()), + n_(n) {} - class Parser + std::shared_ptr> parse() //throw ParsingError; return value always bound! { - public: - Parser(const std::string& phrase, //in - const Expr*& expr, int& n, PluralForm::DumpList& dump) : //out - scn(phrase), - tk(scn.nextToken()), - n_(n), - dump_(dump) - { - try - { - const Expression& e = parse(); //throw std::bad_cast, ParsingError - expr = &dynamic_cast&>(e); // - } - catch (std::bad_cast&) { throw ParsingError(); } + auto e = std::dynamic_pointer_cast>(parseExpression()); //throw ParsingError + if (!e) + throw ParsingError(); + expectToken(Token::TK_END); + return e; + } - consumeToken(Token::TK_END); - } +private: + std::shared_ptr parseExpression() { return parseConditional(); }//throw ParsingError - private: - void nextToken() { tk = scn.nextToken(); } - const Token& token() const { return tk; } + std::shared_ptr parseConditional() //throw ParsingError + { + std::shared_ptr e = parseLogicalOr(); - void consumeToken(Token::Type t) //throw ParsingError + if (token().type == Token::TK_TERNARY_QUEST) { - if (token().type != t) - throw ParsingError(); nextToken(); - } - const Expression& parse() { return parseConditional(); }; //throw std::bad_cast, ParsingError + auto ifExp = std::dynamic_pointer_cast>(e); + auto thenExp = std::dynamic_pointer_cast>(parseExpression()); //associativity: <- - const Expression& parseConditional() - { - const Expression& e = parseLogicalOr(); + expectToken(Token::TK_TERNARY_COLON); + nextToken(); - if (token().type == Token::TK_TERNARY_QUEST) - { - nextToken(); - const Expression& thenEx = parse(); //associativity: <- - consumeToken(Token::TK_TERNARY_COLON); - const Expression& elseEx = parse(); // - - return manageObj(TernaryExp(dynamic_cast&>(e), // - dynamic_cast&>(thenEx), //throw std::bad_cast - dynamic_cast&>(elseEx))); // - } - return e; + auto elseExp = std::dynamic_pointer_cast>(parseExpression()); // + if (!ifExp || !thenExp || !elseExp) + throw ParsingError(); + return std::make_shared>(ifExp, thenExp, elseExp); } + return e; + } - const Expression& parseLogicalOr() + std::shared_ptr parseLogicalOr() + { + std::shared_ptr e = parseLogicalAnd(); + while (token().type == Token::TK_OR) //associativity: -> { - const Expression* e = &parseLogicalAnd(); - while (token().type == Token::TK_OR) //associativity: -> - { - nextToken(); - const Expression& rhs = parseLogicalAnd(); - e = &manageObj(makeBiExp(*e, rhs, std::logical_or())); //throw std::bad_cast - } - return *e; + nextToken(); + + std::shared_ptr rhs = parseLogicalAnd(); + e = makeBiExp(e, rhs, std::logical_or()); //throw ParsingError } + return e; + } - const Expression& parseLogicalAnd() + std::shared_ptr parseLogicalAnd() + { + std::shared_ptr e = parseEquality(); + while (token().type == Token::TK_AND) //associativity: -> { - const Expression* e = &parseEquality(); - while (token().type == Token::TK_AND) //associativity: -> - { - nextToken(); - const Expression& rhs = parseEquality(); + nextToken(); + std::shared_ptr rhs = parseEquality(); - e = &manageObj(makeBiExp(*e, rhs, std::logical_and())); //throw std::bad_cast - } - return *e; + e = makeBiExp(e, rhs, std::logical_and()); //throw ParsingError } + return e; + } - const Expression& parseEquality() - { - const Expression& e = parseRelational(); + std::shared_ptr parseEquality() + { + std::shared_ptr e = parseRelational(); - Token::Type t = token().type; - if (t == Token::TK_EQUAL || t == Token::TK_NOT_EQUAL) //associativity: n/a - { - nextToken(); - const Expression& rhs = parseRelational(); + Token::Type t = token().type; + if (t == Token::TK_EQUAL || //associativity: n/a + t == Token::TK_NOT_EQUAL) + { + nextToken(); + std::shared_ptr rhs = parseRelational(); - if (t == Token::TK_EQUAL) return manageObj(makeBiExp(e, rhs, std::equal_to ())); //throw std::bad_cast - if (t == Token::TK_NOT_EQUAL) return manageObj(makeBiExp(e, rhs, std::not_equal_to())); // - } - return e; + if (t == Token::TK_EQUAL) return makeBiExp(e, rhs, std::equal_to ()); //throw ParsingError + if (t == Token::TK_NOT_EQUAL) return makeBiExp(e, rhs, std::not_equal_to()); // } + return e; + } - const Expression& parseRelational() - { - const Expression& e = parseMultiplicative(); + std::shared_ptr parseRelational() + { + std::shared_ptr e = parseMultiplicative(); - Token::Type t = token().type; - if (t == Token::TK_LESS || //associativity: n/a - t == Token::TK_LESS_EQUAL || - t == Token::TK_GREATER || - t == Token::TK_GREATER_EQUAL) - { - nextToken(); - const Expression& rhs = parseMultiplicative(); + Token::Type t = token().type; + if (t == Token::TK_LESS || //associativity: n/a + t == Token::TK_LESS_EQUAL || + t == Token::TK_GREATER || + t == Token::TK_GREATER_EQUAL) + { + nextToken(); + std::shared_ptr rhs = parseMultiplicative(); - if (t == Token::TK_LESS) return manageObj(makeBiExp(e, rhs, std::less ())); // - if (t == Token::TK_LESS_EQUAL) return manageObj(makeBiExp(e, rhs, std::less_equal ())); //throw std::bad_cast - if (t == Token::TK_GREATER) return manageObj(makeBiExp(e, rhs, std::greater ())); // - if (t == Token::TK_GREATER_EQUAL) return manageObj(makeBiExp(e, rhs, std::greater_equal())); // - } - return e; + if (t == Token::TK_LESS) return makeBiExp(e, rhs, std::less ()); // + if (t == Token::TK_LESS_EQUAL) return makeBiExp(e, rhs, std::less_equal ()); //throw ParsingError + if (t == Token::TK_GREATER) return makeBiExp(e, rhs, std::greater ()); // + if (t == Token::TK_GREATER_EQUAL) return makeBiExp(e, rhs, std::greater_equal()); // } + return e; + } - const Expression& parseMultiplicative() - { - const Expression* e = &parsePrimary(); + std::shared_ptr parseMultiplicative() + { + std::shared_ptr e = parsePrimary(); - while (token().type == Token::TK_MODULUS) //associativity: -> - { - nextToken(); - const Expression& rhs = parsePrimary(); + while (token().type == Token::TK_MODULUS) //associativity: -> + { + nextToken(); + std::shared_ptr rhs = parsePrimary(); - //"compile-time" check: n % 0 - if (auto literal = dynamic_cast(&rhs)) - if (literal->eval() == 0) - throw ParsingError(); + //"compile-time" check: n % 0 + if (auto literal = std::dynamic_pointer_cast(rhs)) + if (literal->eval() == 0) + throw ParsingError(); - e = &manageObj(makeBiExp(*e, rhs, std::modulus())); //throw std::bad_cast - } - return *e; + e = makeBiExp(e, rhs, std::modulus()); //throw ParsingError } + return e; + } - const Expression& parsePrimary() + std::shared_ptr parsePrimary() + { + if (token().type == Token::TK_VARIABLE_N) { - if (token().type == Token::TK_VARIABLE_N) - { - nextToken(); - return manageObj(VariableNumberNExp(n_)); - } - else if (token().type == Token::TK_CONST_NUMBER) - { - const int number = token().number; - nextToken(); - return manageObj(ConstNumberExp(number)); - } - else if (token().type == Token::TK_BRACKET_LEFT) - { - nextToken(); - const Expression& e = parse(); - - consumeToken(Token::TK_BRACKET_RIGHT); - return e; - } - else - throw ParsingError(); + nextToken(); + return std::make_shared(n_); } - - template - const T& manageObj(const T& obj) + else if (token().type == Token::TK_CONST_NUMBER) { - dump_.push_back(std::make_shared(obj)); - return static_cast(*dump_.back()); + const int number = token().number; + nextToken(); + return std::make_shared(number); } + else if (token().type == Token::TK_BRACKET_LEFT) + { + nextToken(); + std::shared_ptr e = parseExpression(); - Scanner scn; - Token tk; + expectToken(Token::TK_BRACKET_RIGHT); + nextToken(); + return e; + } + else + throw ParsingError(); + } - int& n_; - DumpList& dump_; //manage polymorphc object lifetimes - }; + void nextToken() { tk = scn.nextToken(); } + const Token& token() const { return tk; } - const Expr* expr; - mutable int n_; + void expectToken(Token::Type t) //throw ParsingError + { + if (token().type != t) + throw ParsingError(); + } - PluralForm::DumpList dump; //manage polymorphc object lifetimes + Scanner scn; + Token tk; + int& n_; }; +} + + +inline +PluralForm::PluralForm(const std::string& stream) : expr(implementation::Parser(stream, n_).parse()) {} //throw ParsingError +} -#endif // PARSE_PLURAL_H_INCLUDED +#endif // PARSE_PLURAL_H_INCLUDED \ No newline at end of file diff --git a/lib/perf_check.cpp b/lib/perf_check.cpp index c6a4e2d1..85a98910 100644 --- a/lib/perf_check.cpp +++ b/lib/perf_check.cpp @@ -98,34 +98,13 @@ wxString PerfCheck::getBytesPerSecond() const const double timeDelta = recordBack.first - recordFront.first; const double dataDelta = recordBack.second.data_ - recordFront.second.data_; - if (!numeric::isNull(timeDelta)) - if (dataDelta > 0) //may be negative if user cancels copying + if (!numeric::isNull(timeDelta) && dataDelta > 0) return filesizeToShortString(zen::Int64(dataDelta * 1000 / timeDelta)) + _("/sec"); } return L"-"; //fallback } -wxString PerfCheck::getOverallBytesPerSecond() const //for all samples -{ - warn_static("WTF!? tihs considers window only!") - - if (!samples.empty()) - { - const auto& recordBack = *samples.rbegin(); - const auto& recordFront = *samples.begin(); - //----------------------------------------------------------------------------------------------- - const double timeDelta = recordBack.first - recordFront.first; - const double dataDelta = recordBack.second.data_ - recordFront.second.data_; - - if (!numeric::isNull(timeDelta)) - if (dataDelta > 0) //may be negative if user cancels copying - return zen::filesizeToShortString(zen::Int64(dataDelta * 1000 / timeDelta)) + _("/sec"); - } - return L"-"; //fallback -} - - /* class for calculation of remaining time: ---------------------------------------- diff --git a/lib/perf_check.h b/lib/perf_check.h index b60c31c9..f314f842 100644 --- a/lib/perf_check.h +++ b/lib/perf_check.h @@ -21,7 +21,6 @@ public: wxString getRemainingTime(double dataRemaining) const; wxString getBytesPerSecond() const; //for window - wxString getOverallBytesPerSecond() const; //for all samples private: const long windowSizeRemTime; //unit: [ms] diff --git a/lib/process_xml.cpp b/lib/process_xml.cpp index a11f841c..e38749f9 100644 --- a/lib/process_xml.cpp +++ b/lib/process_xml.cpp @@ -75,7 +75,7 @@ void setXmlType(XmlDoc& doc, XmlType type) //throw() wxString xmlAccess::getGlobalConfigFile() { - return toWx(zen::getConfigDir()) + wxT("GlobalSettings.xml"); + return toWx(zen::getConfigDir()) + L"GlobalSettings.xml"; } @@ -761,6 +761,64 @@ void writeStruc(const ColumnAttributeNavi& value, XmlElement& output) out.attribute("Width", value.offset_); out.attribute("Stretch", value.stretch_); } + + +template <> inline +bool readStruc(const XmlElement& input, ViewFilterDefault& value) +{ + XmlIn in(input); + + bool success = true; + auto readAttr = [&](XmlIn& elemIn, const char name[], bool& v) + { + if (!elemIn.attribute(name, v)) + success = false; + }; + + XmlIn catView = in["CategoryView"]; + readAttr(catView, "LeftOnly" , value.leftOnly); + readAttr(catView, "RightOnly" , value.rightOnly); + readAttr(catView, "LeftNewer" , value.leftNewer); + readAttr(catView, "RightNewer", value.rightNewer); + readAttr(catView, "Different" , value.different); + readAttr(catView, "Equal" , value.equal); + readAttr(catView, "Conflict" , value.conflict); + + XmlIn actView = in["ActionView"]; + readAttr(actView, "CreateLeft" , value.createLeft); + readAttr(actView, "CreateRight", value.createRight); + readAttr(actView, "UpdateLeft" , value.updateLeft); + readAttr(actView, "UpdateRight", value.updateRight); + readAttr(actView, "DeleteLeft" , value.deleteLeft); + readAttr(actView, "DeleteRight", value.deleteRight); + readAttr(actView, "DoNothing" , value.doNothing); + + return success; //[!] avoid short-circuit evaluation above +} + +template <> inline +void writeStruc(const ViewFilterDefault& value, XmlElement& output) +{ + XmlOut out(output); + + XmlOut catView = out["CategoryView"]; + catView.attribute("LeftOnly" , value.leftOnly); + catView.attribute("RightOnly" , value.rightOnly); + catView.attribute("LeftNewer" , value.leftNewer); + catView.attribute("RightNewer", value.rightNewer); + catView.attribute("Different" , value.different); + catView.attribute("Equal" , value.equal); + catView.attribute("Conflict" , value.conflict); + + XmlOut actView = out["ActionView"]; + actView.attribute("CreateLeft" , value.createLeft); + actView.attribute("CreateRight", value.createRight); + actView.attribute("UpdateLeft" , value.updateLeft); + actView.attribute("UpdateRight", value.updateRight); + actView.attribute("DeleteLeft" , value.deleteLeft); + actView.attribute("DeleteRight", value.deleteRight); + actView.attribute("DoNothing" , value.doNothing); +} } @@ -798,8 +856,12 @@ void readConfig(const XmlIn& in, SyncConfig& syncCfg) in["CustomDeletionFolder"](syncCfg.versioningDirectory);//obsolete name else in["VersioningFolder"](syncCfg.versioningDirectory); + warn_static("remove after migration?") - if (in["VersioningStyle"]) //new parameter + if (in["VersioningFolder"] && + in["VersioningFolder"].get()->getAttribute("Style", syncCfg.versioningStyle)) //new parameter, do not complain when missing + ; + else if (in["VersioningStyle"]) //obsolete name in["VersioningStyle"](syncCfg.versioningStyle); else syncCfg.versioningStyle = VER_STYLE_ADD_TIMESTAMP; //obsolete fallback @@ -921,7 +983,16 @@ void readConfig(const XmlIn& in, xmlAccess::XmlGuiConfig& config) inGuiCfg["HideExcluded"](config.hideExcludedItems); inGuiCfg["HandleError" ](config.handleError); - inGuiCfg["SyncPreviewActive"](config.showSyncAction); + + warn_static("remove after migration?") + if (inGuiCfg["SyncPreviewActive"]) //obsolete name + inGuiCfg["SyncPreviewActive"](config.showSyncAction); + else + { + std::string val; + if (inGuiCfg["MiddleGridView"](val)) //refactor into enum!? + config.showSyncAction = val == "Action"; + } } @@ -1003,7 +1074,7 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config) inWnd.attribute("Maximized", config.gui.isMaximized); XmlIn inManualDel = inWnd["ManualDeletion"]; - inManualDel.attribute("DeleteOnBothSides", config.gui.deleteOnBothSides); + //inManualDel.attribute("DeleteOnBothSides", config.gui.deleteOnBothSides); inManualDel.attribute("UseRecycler" , config.gui.useRecyclerForManualDeletion); inWnd["CaseSensitiveSearch" ](config.gui.textSearchRespectCase); @@ -1031,7 +1102,8 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config) inColRight(config.gui.columnAttribRight); //########################################################### - inWnd["Layout"](config.gui.guiPerspectiveLast); + inWnd["ViewFilterDefault"](config.gui.viewFilterDefault); + inWnd["Layout" ](config.gui.guiPerspectiveLast); //load config file history warn_static("remove after migration?") @@ -1140,7 +1212,7 @@ void writeConfig(const SyncConfig& syncCfg, XmlOut& out) out["DeletionPolicy" ](syncCfg.handleDeletion); out["VersioningFolder"](syncCfg.versioningDirectory); //out["VersioningFolder"].attribute("Limit", syncCfg.versionCountLimit); - out["VersioningStyle"](syncCfg.versioningStyle); + out["VersioningFolder"].attribute("Style", syncCfg.versioningStyle); } @@ -1234,9 +1306,9 @@ void writeConfig(const XmlGuiConfig& config, XmlOut& out) //write GUI specific config data XmlOut outGuiCfg = out["GuiConfig"]; - outGuiCfg["HideExcluded" ](config.hideExcludedItems); - outGuiCfg["HandleError" ](config.handleError); - outGuiCfg["SyncPreviewActive"](config.showSyncAction); + outGuiCfg["HideExcluded" ](config.hideExcludedItems); + outGuiCfg["HandleError" ](config.handleError); + outGuiCfg["MiddleGridView"](config.showSyncAction ? "Action" : "Category"); //refactor into enum!? } void writeConfig(const XmlBatchConfig& config, XmlOut& out) @@ -1296,7 +1368,7 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out) outWnd.attribute("Maximized", config.gui.isMaximized); XmlOut outManualDel = outWnd["ManualDeletion"]; - outManualDel.attribute("DeleteOnBothSides", config.gui.deleteOnBothSides); + //outManualDel.attribute("DeleteOnBothSides", config.gui.deleteOnBothSides); outManualDel.attribute("UseRecycler" , config.gui.useRecyclerForManualDeletion); outWnd["CaseSensitiveSearch" ](config.gui.textSearchRespectCase); @@ -1323,7 +1395,8 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out) outColRight(config.gui.columnAttribRight); //########################################################### - outWnd["Layout"](config.gui.guiPerspectiveLast); + outWnd["ViewFilterDefault"](config.gui.viewFilterDefault); + outWnd["Layout" ](config.gui.guiPerspectiveLast); //load config file history outGui["LastUsedConfig"](config.gui.lastUsedConfigFiles); diff --git a/lib/process_xml.h b/lib/process_xml.h index 29237081..d0396d6e 100644 --- a/lib/process_xml.h +++ b/lib/process_xml.h @@ -112,6 +112,18 @@ enum FileIconSize }; +struct ViewFilterDefault +{ + ViewFilterDefault() : equal(false) + { + leftOnly = rightOnly = leftNewer = rightNewer = different = conflict = true; + createLeft = createRight = updateLeft = updateRight = deleteLeft = deleteRight = doNothing = true; + } + bool equal; + bool leftOnly, rightOnly, leftNewer, rightNewer, different, conflict; //category view + bool createLeft, createRight, updateLeft, updateRight, deleteLeft, deleteRight, doNothing; //action view +}; + wxString getGlobalConfigFile(); struct XmlGlobalSettings @@ -161,7 +173,7 @@ struct XmlGlobalSettings cfgFileHistMax(30), folderHistMax(15), onCompletionHistoryMax(8), - deleteOnBothSides(false), + //deleteOnBothSides(false), useRecyclerForManualDeletion(true), //enable if OS supports it; else user will have to activate first and then get an error message #ifdef FFS_WIN textSearchRespectCase(false), @@ -216,7 +228,7 @@ struct XmlGlobalSettings std::vector onCompletionHistory; size_t onCompletionHistoryMax; - bool deleteOnBothSides; + //bool deleteOnBothSides; bool useRecyclerForManualDeletion; bool textSearchRespectCase; @@ -225,6 +237,7 @@ struct XmlGlobalSettings long lastUpdateCheck; //time of last update check + ViewFilterDefault viewFilterDefault; wxString guiPerspectiveLast; //used by wxAuiManager } gui; diff --git a/lib/resources.cpp b/lib/resources.cpp index 7d46739e..2f7daeaf 100644 --- a/lib/resources.cpp +++ b/lib/resources.cpp @@ -87,8 +87,8 @@ GlobalResources::GlobalResources() const wxBitmap& GlobalResources::getImageInt(const wxString& imageName) const { auto it = bitmaps.find(!contains(imageName, L'.') ? //assume .png ending if nothing else specified - imageName + L".png" : - imageName); + imageName + L".png" : + imageName); if (it != bitmaps.end()) return it->second; else diff --git a/lib/status_handler.cpp b/lib/status_handler.cpp index c24c6f50..fd3b2d96 100644 --- a/lib/status_handler.cpp +++ b/lib/status_handler.cpp @@ -22,7 +22,7 @@ void zen::updateUiNow() namespace { -const std::int64_t TICKS_UPDATE_INTERVAL = UI_UPDATE_INTERVAL* ticksPerSec() / 1000; +const std::int64_t TICKS_UPDATE_INTERVAL = UI_UPDATE_INTERVAL * ticksPerSec() / 1000; TickVal lastExec = getTicks(); }; diff --git a/lib/status_handler.h b/lib/status_handler.h index 93f9892c..ed496824 100644 --- a/lib/status_handler.h +++ b/lib/status_handler.h @@ -67,7 +67,7 @@ protected: refNumbers(numbersTotal_, currentPhase_) = std::make_pair(objectsTotal, dataTotal); } - virtual void updateProcessedData(int objectsDelta, Int64 dataDelta) { updateData(numbersCurrent_, objectsDelta, dataDelta); } //note: these methods should NOT throw in order + virtual void updateProcessedData(int objectsDelta, Int64 dataDelta) { updateData(numbersCurrent_, objectsDelta, dataDelta); } //note: these methods MUST NOT throw in order virtual void updateTotalData (int objectsDelta, Int64 dataDelta) { updateData(numbersTotal_ , objectsDelta, dataDelta); } //to properly allow undoing setting of statistics! virtual void requestUiRefresh() diff --git a/lib/versioning.cpp b/lib/versioning.cpp index d4b6e2b2..a72433cc 100644 --- a/lib/versioning.cpp +++ b/lib/versioning.cpp @@ -226,8 +226,26 @@ private: } -void FileVersioner::revisionFile(const Zstring& sourceFile, const Zstring& relativeName, CallbackMoveFile& callback) //throw FileError +bool FileVersioner::revisionFile(const Zstring& sourceFile, const Zstring& relativeName, CallbackMoveFile& callback) //throw FileError { + struct CallbackMoveFileImpl : public CallbackMoveDir + { + CallbackMoveFileImpl(CallbackMoveFile& callback) : callback_(callback) {} + private: + virtual void onBeforeFileMove(const Zstring& fileFrom, const Zstring& fileTo) {} + virtual void onBeforeDirMove (const Zstring& dirFrom, const Zstring& dirTo ) {} + virtual void updateStatus(Int64 bytesDelta) { callback_.updateStatus(bytesDelta); } + CallbackMoveFile& callback_; + } cb(callback); + + return revisionFileImpl(sourceFile, relativeName, cb); //throw FileError +} + + +bool FileVersioner::revisionFileImpl(const Zstring& sourceFile, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError +{ + bool moveSuccessful = false; + moveItemToVersioning(sourceFile, //throw FileError relativeName, versioningDirectory_, @@ -235,26 +253,38 @@ void FileVersioner::revisionFile(const Zstring& sourceFile, const Zstring& relat versioningStyle_, [&](const Zstring& source, const Zstring& target) { + callback.onBeforeFileMove(source, target); //if we're called by revisionDirImpl() we know that "source" exists! + //when called by revisionFile(), "source" might not exist, however onBeforeFileMove() is not propagated in this case! + struct CopyCallbackImpl : public CallbackCopyFile { - CopyCallbackImpl(CallbackMoveFile& callback) : callback_(callback) {} + CopyCallbackImpl(CallbackMoveDir& callback) : callback_(callback) {} private: virtual void deleteTargetFile(const Zstring& targetFile) { assert(!somethingExists(targetFile)); } virtual void updateCopyStatus(Int64 bytesDelta) { callback_.updateStatus(bytesDelta); } - CallbackMoveFile& callback_; + CallbackMoveDir& callback_; } copyCallback(callback); - callback.onBeforeFileMove(source, target); moveFile(source, target, copyCallback); //throw FileError - callback.objectProcessed(); + moveSuccessful = true; }); + return moveSuccessful; +} + - //fileRelNames.push_back(relativeName); +void FileVersioner::revisionDir(const Zstring& sourceDir, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError +{ + //no error situation if directory is not existing! manual deletion relies on it! + if (!somethingExists(sourceDir)) + return; //neither directory nor any other object (e.g. broken symlink) with that name existing + revisionDirImpl(sourceDir, relativeName, callback); //throw FileError } -void FileVersioner::revisionDir(const Zstring& sourceDir, const Zstring& relativeName, CallbackMoveFile& callback) //throw FileError +void FileVersioner::revisionDirImpl(const Zstring& sourceDir, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError { + 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! { @@ -267,10 +297,7 @@ void FileVersioner::revisionDir(const Zstring& sourceDir, const Zstring& relativ { callback.onBeforeDirMove(source, target); moveDirSymlink(source, target); //throw FileError - callback.objectProcessed(); }); - - //fileRelNames.push_back(relativeName); } else { @@ -278,24 +305,15 @@ void FileVersioner::revisionDir(const Zstring& sourceDir, const Zstring& relativ assert(endsWith(sourceDir, relativeName)); //usually, yes, but we might relax this in the future const Zstring targetDir = appendSeparator(versioningDirectory_) + relativeName; - callback.onBeforeDirMove(sourceDir, targetDir); - //makeDirectory(targetDir); //FileError -> create only when needed in moveFileToVersioning(); avoids empty directories //traverse source directory one level std::vector fileList; //list of *short* names std::vector dirList; // - try { TraverseFilesOneLevel tol(fileList, dirList); //throw FileError traverseFolder(sourceDir, tol); // } - catch (FileError&) - { - if (!somethingExists(sourceDir)) //no source at all is not an error (however a file as source when a directory is expected, *is* an error!) - return; //object *not* processed - throw; - } const Zstring sourceDirPf = appendSeparator(sourceDir); const Zstring relnamePf = appendSeparator(relativeName); @@ -304,33 +322,23 @@ void FileVersioner::revisionDir(const Zstring& sourceDir, const Zstring& relativ std::for_each(fileList.begin(), fileList.end(), [&](const Zstring& shortname) { - revisionFile(sourceDirPf + shortname, //throw FileError - relnamePf + shortname, - callback); + revisionFileImpl(sourceDirPf + shortname, //throw FileError + relnamePf + shortname, + callback); }); //move items in subdirectories std::for_each(dirList.begin(), dirList.end(), [&](const Zstring& shortname) { - revisionDir(sourceDirPf + shortname, //throw FileError - relnamePf + shortname, - callback); + revisionDirImpl(sourceDirPf + shortname, //throw FileError + relnamePf + shortname, + callback); }); //delete source - struct RemoveCallbackImpl : public CallbackRemoveDir - { - RemoveCallbackImpl(CallbackMoveFile& callback) : callback_(callback) {} - private: - virtual void notifyFileDeletion(const Zstring& filename) { callback_.updateStatus(0); } - virtual void notifyDirDeletion (const Zstring& dirname ) { callback_.updateStatus(0); } - CallbackMoveFile& callback_; - } removeCallback(callback); - - removeDirectory(sourceDir, &removeCallback); //throw FileError - - callback.objectProcessed(); + callback.onBeforeDirMove(sourceDir, targetDir); + removeDirectory(sourceDir); //throw FileError } } diff --git a/lib/versioning.h b/lib/versioning.h index 3e0dd33c..faa96359 100644 --- a/lib/versioning.h +++ b/lib/versioning.h @@ -17,6 +17,7 @@ namespace zen { +struct CallbackMoveDir; struct CallbackMoveFile; //e.g. move C:\Source\subdir\Sample.txt -> D:\Revisions\subdir\Sample.txt 2012-05-15 131513.txt @@ -42,15 +43,18 @@ public: timeStamp_(formatTime(Zstr("%Y-%m-%d %H%M%S"), timeStamp)) //e.g. "2012-05-15 131513" { if (timeStamp_.size() != 17) //formatTime() returns empty string on error; unexpected length: e.g. problem in year 10000! - throw FileError(_("Failure to create time stamp for versioning:") + L" \'" + timeStamp_ + L"\'"); + throw FileError(_("Failure to create timestamp for versioning:") + L" \'" + timeStamp_ + L"\'"); } - void revisionFile(const Zstring& sourceFile, const Zstring& relativeName, CallbackMoveFile& callback); //throw FileError - void revisionDir (const Zstring& sourceDir, const Zstring& relativeName, CallbackMoveFile& callback); //throw FileError + bool revisionFile(const Zstring& sourceFile, const Zstring& relativeName, CallbackMoveFile& callback); //throw FileError; return "false" if file is not existing + void revisionDir (const Zstring& sourceDir, const Zstring& relativeName, CallbackMoveDir& callback); //throw FileError //void limitVersions(std::function updateUI); //throw FileError; call when done revisioning! private: + bool revisionFileImpl(const Zstring& sourceFile, const Zstring& relativeName, CallbackMoveDir& callback); //throw FileError + void revisionDirImpl (const Zstring& sourceDir, const Zstring& relativeName, CallbackMoveDir& callback); //throw FileError + const VersioningStyle versioningStyle_; const Zstring versioningDirectory_; const Zstring timeStamp_; @@ -59,22 +63,20 @@ private: }; -struct CallbackMoveFile +struct CallbackMoveFile //see CallbackCopyFile for limitations when throwing exceptions! { - virtual ~CallbackMoveFile() {} //see CallbackCopyFile for limitations when throwing exceptions! - - virtual void onBeforeFileMove(const Zstring& fileFrom, const Zstring& fileTo) = 0; //one call before each (planned) move - virtual void onBeforeDirMove (const Zstring& dirFrom, const Zstring& dirTo ) = 0; // - virtual void objectProcessed() = 0; //one call after each completed move (count objects total) - - //called frequently if move has to revert to copy + delete: - virtual void updateStatus(Int64 bytesDelta) = 0; + virtual ~CallbackMoveFile() {} + virtual void updateStatus(Int64 bytesDelta) = 0; //called frequently if move has to revert to copy + delete: }; +struct CallbackMoveDir //see CallbackCopyFile for limitations when throwing exceptions! +{ + virtual ~CallbackMoveDir() {} - - - + virtual void onBeforeFileMove(const Zstring& fileFrom, const Zstring& fileTo) = 0; //one call for each *existing* object! + virtual void onBeforeDirMove (const Zstring& dirFrom, const Zstring& dirTo ) = 0; // + virtual void updateStatus(Int64 bytesDelta) = 0; //called frequently if move has to revert to copy + delete: +}; diff --git a/process_callback.h b/process_callback.h index 0f540df6..036ad79a 100644 --- a/process_callback.h +++ b/process_callback.h @@ -34,16 +34,20 @@ struct ProcessCallback //note: this one must NOT throw in order to properly allow undoing setting of statistics! //it is in general paired with a call to requestUiRefresh() to compensate! - virtual void updateProcessedData(int objectsDelta, zen::Int64 dataDelta) = 0; //throw()!! + virtual void updateProcessedData(int objectsDelta, zen::Int64 dataDelta) = 0; //noexcept!! virtual void updateTotalData (int objectsDelta, zen::Int64 dataDelta) = 0; // - /*the estimated total may change *during* sync: - 1. move file -> fallback to copy + delete - 2. file copy, actual size changed after comparison or file contains significant ADS data - 3. auto-resolution for failed create operations due to missing source - 4. directory deletion: may contain more items than scanned by FFS: excluded by filter - 5. delete directory to recycler or move to user-defined dir on same volume: no matter how many sub-elements exist, this is only 1 object to process! - 6. user-defined deletion directory on different volume: full file copy required (instead of move) - 7. Copy sparse files */ + /*the estimated and actual total workload may change *during* sync: + 1. detected file can be moved -> fallback to copy + delete + 2. file copy, actual size changed after comparison + 3. file contains significant ADS data, is sparse or compressed + 4. auto-resolution for failed create operations due to missing source + 5. directory deletion: may contain more items than scanned by FFS (excluded by filter) or less (contains followed symlinks) + 6. delete directory to recycler: no matter how many child-elements exist, this is only 1 item to process! + 7. file/directory already deleted externally: nothing to do, 0 logical operations and data + 8. user-defined deletion directory on different volume: full file copy required (instead of move) + 9. Binary file comparison: if files differ at the first few bytes, the result is already known + 10. Error during file copy, retry: bytes were copied => increases total workload! + */ //opportunity to abort must be implemented in a frequently executed method like requestUiRefresh() virtual void requestUiRefresh() = 0; //throw ? diff --git a/synchronization.cpp b/synchronization.cpp index 38a6e32c..b3eaf4e4 100644 --- a/synchronization.cpp +++ b/synchronization.cpp @@ -6,6 +6,7 @@ #include "synchronization.h" #include +#include #include #include #include //get rid!? @@ -43,7 +44,6 @@ int getCUD(const SyncStatistics& stat) } } - void SyncStatistics::init() { createLeft = 0; @@ -52,7 +52,6 @@ void SyncStatistics::init() updateRight = 0; deleteLeft = 0; deleteRight = 0; - //conflict = 0; rowsTotal = 0; } @@ -138,8 +137,6 @@ void SyncStatistics::calcStats(const FileMapping& fileObj) break; case SO_UNRESOLVED_CONFLICT: - //++conflict; - //if (conflictMsgs.size() < MAX_CONFLICTS) //save the first conflict texts conflictMsgs.push_back(std::make_pair(fileObj.getObjRelativeName(), fileObj.getSyncOpConflict())); break; @@ -190,8 +187,6 @@ void SyncStatistics::calcStats(const SymLinkMapping& linkObj) break; case SO_UNRESOLVED_CONFLICT: - //++conflict; - //if (conflictMsgs.size() < MAX_CONFLICTS) //save the first conflict texts conflictMsgs.push_back(std::make_pair(linkObj.getObjRelativeName(), linkObj.getSyncOpConflict())); break; @@ -228,14 +223,7 @@ void SyncStatistics::calcStats(const DirMapping& dirObj) ++deleteRight; break; - case SO_OVERWRITE_LEFT: - case SO_OVERWRITE_RIGHT: - assert(false); - break; - case SO_UNRESOLVED_CONFLICT: - //++conflict; - //if (conflictMsgs.size() < MAX_CONFLICTS) //save the first conflict texts conflictMsgs.push_back(std::make_pair(dirObj.getObjRelativeName(), dirObj.getSyncOpConflict())); break; @@ -247,6 +235,8 @@ void SyncStatistics::calcStats(const DirMapping& dirObj) ++updateRight; break; + case SO_OVERWRITE_LEFT: + case SO_OVERWRITE_RIGHT: case SO_MOVE_LEFT_SOURCE: case SO_MOVE_RIGHT_SOURCE: case SO_MOVE_LEFT_TARGET: @@ -274,9 +264,9 @@ std::vector zen::extractSyncCfg(const MainConfiguration& std::vector output; //process all pairs - for (auto i = allPairs.begin(); i != allPairs.end(); ++i) + for (auto it = allPairs.begin(); it != allPairs.end(); ++it) { - SyncConfig syncCfg = i->altSyncConfig.get() ? *i->altSyncConfig : mainCfg.syncCfg; + SyncConfig syncCfg = it->altSyncConfig.get() ? *it->altSyncConfig : mainCfg.syncCfg; output.push_back( FolderPairSyncCfg(syncCfg.directionCfg.var == DirectionConfig::AUTOMATIC, @@ -286,8 +276,8 @@ std::vector zen::extractSyncCfg(const MainConfiguration& } return output; } -//------------------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------------------ //test if user accidentally selected the wrong folders to sync bool significantDifferenceDetected(const SyncStatistics& folderPairStat) @@ -301,140 +291,28 @@ bool significantDifferenceDetected(const SyncStatistics& folderPairStat) return false; const int nonMatchingRows = folderPairStat.getCreate() + - //folderPairStat.getUpdate() + -> not relevant when testing for "wrong folder selected" - folderPairStat.getDelete () + - folderPairStat.getConflict(); //? + folderPairStat.getDelete(); + //folderPairStat.getUpdate() + -> not relevant when testing for "wrong folder selected" + //folderPairStat.getConflict(); return nonMatchingRows >= 10 && nonMatchingRows > 0.5 * folderPairStat.getRowCount(); } //################################################################################################################# -/* -class PhysicalStatistics //counts *physical* operations, actual items processed (NOT disk accesses) and bytes transferred -{ -public: - PhysicalStatistics(const FolderComparison& folderCmp) : items(0) - { - delType =; - std::for_each(begin(folderCmp), end(folderCmp), [&](const BaseDirMapping& baseMap) { recurse(baseMap); }); - } - - int getItems() const { return items; } - Int64 getBytes() const { return bytes; } - -private: - enum DeletionType -{ - DEL_PERMANENTLY, - DEL_RECYCLE_BIN, - VERSIONING_SAME_VOL, - VERSIONING_DIFF_VOL, -}; - - void recurse(const HierarchyObject& hierObj) - { - std::for_each(hierObj.refSubDirs ().begin(), hierObj.refSubDirs ().end(), [&](const DirMapping& dirObj ) { calcStats(dirObj ); }); - std::for_each(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), [&](const FileMapping& fileObj) { calcStats(fileObj); }); - std::for_each(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(), [&](const SymLinkMapping& linkObj) { calcStats(linkObj); }); - } - - void calcStats(const FileMapping& fileObj) - { - switch (fileObj.getSyncOperation()) //evaluate comparison result and sync direction - { - case SO_CREATE_NEW_LEFT: - items += 2; - bytes += to(fileObj.getFileSize()); - break; - - case SO_CREATE_NEW_RIGHT: - ++items; - bytes += to(fileObj.getFileSize()); - break; - - case SO_DELETE_LEFT: - switch (delType) - { - case DEL_INSTANTLY: - ++items; - break; - case DEL_COPY_DELETE: - break; - } - break; - - case SO_DELETE_RIGHT: - ++items; - break; - - case SO_MOVE_LEFT_TARGET: - case SO_MOVE_RIGHT_TARGET: - ++items; - break; - - case SO_MOVE_LEFT_SOURCE: //ignore; already counted - case SO_MOVE_RIGHT_SOURCE: // - break; - - case SO_OVERWRITE_LEFT: - //todo: delete - items += 2; //read + write - bytes += to(fileObj.getFileSize()); - break; - - case SO_OVERWRITE_RIGHT: - //todo: delete - items += 2; //read + write - bytes += to(fileObj.getFileSize()); - break; - - case SO_COPY_METADATA_TO_LEFT: - case SO_COPY_METADATA_TO_RIGHT: - ++items; - break; - - case SO_UNRESOLVED_CONFLICT: - case SO_DO_NOTHING: - case SO_EQUAL: - break; - } - } - - void calcStats(const SymLinkMapping& linkObj) - { - - } - - void calcStats(const DirMapping& dirObj) - { - //since we model physical stats, we recurse only if deletion variant is "permanently" or "user-defined + different volume", - //else deletion is done as a single physical operation - } - - int items; - Int64 bytes; - -DeletionType delType; -}; -*/ - -//-------------------------------------------------------------------------------------------------------------- - -class DeletionHandling //e.g. generate name of alternate deletion directory (unique for session AND folder pair) +class DeletionHandling //abstract deletion variants: permanently, recycle bin, user-defined directory { public: DeletionHandling(DeletionPolicy handleDel, //nothrow! const Zstring& versioningDir, VersioningStyle versioningStyle, const TimeComp& timeStamp, - size_t folderIndex, const Zstring& baseDirPf, //with separator postfix ProcessCallback& procCallback); ~DeletionHandling() { - try { tryCleanup(false); } //always (try to) clean up, even if synchronization is aborted! - catch (...) {} + try { tryCleanup(false); } + catch (...) {} //always (try to) clean up, even if synchronization is aborted! /* do not allow user callback: - make sure this stays non-blocking! @@ -445,10 +323,9 @@ public: //clean-up temporary directory (recycle bin optimization) void tryCleanup(bool allowUserCallback = true); //throw FileError -> call this in non-exceptional coding, i.e. somewhere after sync! - void removeFile (const Zstring& relativeName); //throw FileError - void removeFolder(const Zstring& relativeName) { removeFolderInt(relativeName, nullptr, nullptr); }; //throw FileError - void removeFolderUpdateStatistics(const Zstring& relativeName, int objectsExpected, Int64 dataExpected) { removeFolderInt(relativeName, &objectsExpected, &dataExpected); }; //throw FileError - //in contrast to "removeFolder()" this function will update statistics! + template void removeFileUpdating(const Zstring& relativeName, Int64 bytesExpected, Function notifyItemDeletion); //throw FileError + template void removeDirUpdating (const Zstring& relativeName, Int64 bytesExpected, Function notifyItemDeletion); //reports ONLY data delta via updateProcessedData()! + template void removeLinkUpdating(const Zstring& relativeName, Int64 bytesExpected, Function notifyItemDeletion, LinkDescriptor::LinkType lt); // const std::wstring& getTxtRemovingFile () const { return txtRemovingFile; } // const std::wstring& getTxtRemovingSymLink() const { return txtRemovingSymlink; } //buffered status texts @@ -464,7 +341,6 @@ private: DeletionHandling(const DeletionHandling&); DeletionHandling& operator=(const DeletionHandling&); - void removeFolderInt(const Zstring& relativeName, const int* objectsExpected, const Int64* dataExpected); //throw FileError void setDeletionPolicy(DeletionPolicy newPolicy); FileVersioner& getOrCreateVersioner() //throw FileError! => dont create in DeletionHandling()!!! @@ -481,8 +357,9 @@ private: const TimeComp timeStamp_; #ifdef FFS_WIN - Zstring recyclerTmpDirPf; //temporary folder holding files/folders for *deferred* recycling (postfixed with file name separator) - std::vector toBeRecycled; //full path of files located in temporary folder, waiting to be recycled + Zstring getOrCreateRecyclerTempDirPf(); //throw FileError + Zstring recyclerTmpDir; //temporary folder holding files/folders for *deferred* recycling + std::vector toBeRecycled; //full path of files located in temporary folder, waiting for batch-recycling bool recFallbackDelPermantently; #endif @@ -499,12 +376,28 @@ private: bool cleanedUp; }; +namespace +{ +#ifdef FFS_WIN +//recycleBinStatus() blocks seriously if recycle bin is really full and drive is slow +StatusRecycler recycleBinStatusUpdating(const Zstring& dirname, ProcessCallback& procCallback) +{ + procCallback.reportStatus(replaceCpy(_("Checking recycle bin availability for folder %x..."), L"%x", fmtFileName(dirname), false)); + + auto ft = async([=] { return recycleBinStatus(dirname); }); + + while (!ft.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL / 2))) + procCallback.requestUiRefresh(); //may throw! + return ft.get(); +} +#endif +} + DeletionHandling::DeletionHandling(DeletionPolicy handleDel, //nothrow! const Zstring& versioningDir, VersioningStyle versioningStyle, const TimeComp& timeStamp, - size_t folderIndex, const Zstring& baseDirPf, //with separator postfix ProcessCallback& procCallback) : procCallback_(procCallback), @@ -520,21 +413,11 @@ DeletionHandling::DeletionHandling(DeletionPolicy handleDel, //nothrow! { #ifdef FFS_WIN if (!baseDirPf.empty()) - if (handleDel == DELETE_TO_RECYCLER && recycleBinStatus(baseDirPf) != STATUS_REC_EXISTS) + if (handleDel == DELETE_TO_RECYCLER && recycleBinStatusUpdating(baseDirPf, procCallback_) != STATUS_REC_EXISTS) { handleDel = DELETE_PERMANENTLY; //Windows' ::SHFileOperation() will do this anyway, but we have a better and faster deletion routine (e.g. on networks) recFallbackDelPermantently = true; } - - //assemble temporary recycler bin directory - if (!baseDirPf_.empty()) - { - Zstring tempDir = baseDirPf_ + Zstr("FFS ") + formatTime(Zstr("%Y-%m-%d %H%M%S"), timeStamp); - if (folderIndex > 0) - tempDir += Zstr(" [") + numberTo(folderIndex + 1) + Zstr("]"); //e.g. C:\Source\FFS 2012-05-15 131513 [2] - - recyclerTmpDirPf = appendSeparator(tempDir); - } #endif setDeletionPolicy(handleDel); @@ -590,6 +473,51 @@ private: ProcessCallback& statusHandler_; const std::wstring txtRecyclingFile; }; + +Zstring createUniqueRandomTempDir(const Zstring& baseDirPf) //throw FileError +{ + assert(endsWith(baseDirPf, FILE_NAME_SEPARATOR)); + + //1. generate random directory name + static std::default_random_engine rng(std::time(nullptr)); //a pseudo-random number engine with seconds-precision seed is sufficient! + //the alternative std::random_device may not always be available and can even throw an exception! + + const Zstring chars(Zstr("abcdefghijklmnopqrstuvwxyz") + Zstr("1234567890")); + std::uniform_int_distribution distrib(0, chars.size() - 1); //takes closed range + + auto generatePath = [&]() -> Zstring //e.g. C:\Source\3vkf74fq.ffs_tmp + { + Zstring path = baseDirPf; + for (int i = 0; i < 8; ++i) + path += chars[distrib(rng)]; + return path + TEMP_FILE_ENDING; + }; + + //2. ensure uniqueness (at least for this base directory) + for (;;) + try + { + Zstring dirname = generatePath(); + makeNewDirectory(dirname, Zstring(), false); //FileError, ErrorTargetExisting + return dirname; + } + catch (const ErrorTargetExisting&) {} +} +} + +//create + returns temporary directory postfixed with file name separator +//to support later cleanup if automatic deletion fails for whatever reason +Zstring DeletionHandling::getOrCreateRecyclerTempDirPf() //throw FileError +{ + assert(!baseDirPf_.empty()); + if (baseDirPf_.empty()) + return Zstring(); + + if (recyclerTmpDir.empty()) + recyclerTmpDir = createUniqueRandomTempDir(baseDirPf_); //throw FileError + //assemble temporary recycle bin directory with random name and .ffs_tmp ending + return appendSeparator(recyclerTmpDir); } #endif @@ -604,14 +532,14 @@ void DeletionHandling::tryCleanup(bool allowUserCallback) //throw FileError case DELETE_TO_RECYCLER: #ifdef FFS_WIN - if (!recyclerTmpDirPf.empty()) + if (!recyclerTmpDir.empty()) { //move content of temporary directory to recycle bin in a single call CallbackMassRecycling cbmr(procCallback_); recycleOrDelete(toBeRecycled, allowUserCallback ? &cbmr : nullptr); //throw FileError //clean up temp directory itself (should contain remnant empty directories only) - removeDirectory(beforeLast(recyclerTmpDirPf, FILE_NAME_SEPARATOR)); //throw FileError + removeDirectory(recyclerTmpDir); //throw FileError } #endif break; @@ -634,169 +562,178 @@ void DeletionHandling::tryCleanup(bool allowUserCallback) //throw FileError } } - namespace { -class CallbackMoveFileImpl : public CallbackMoveFile +template +struct CallbackRemoveDirImpl : public CallbackRemoveDir { -public: - CallbackMoveFileImpl(ProcessCallback& statusHandler, - int* objectsReported) : - statusHandler_ (statusHandler), - objectsReported_(objectsReported), - txtMovingFile (_("Moving file %x to %y")), - txtMovingFolder (_("Moving folder %x to %y")) {} + CallbackRemoveDirImpl(ProcessCallback& statusHandler, + const DeletionHandling& delHandling, + Function notifyItemDeletion) : + statusHandler_(statusHandler), + notifyItemDeletion_(notifyItemDeletion), + txtDeletingFile (delHandling.getTxtRemovingFile()), + txtDeletingFolder(delHandling.getTxtRemovingDir ()) {} private: - virtual void onBeforeFileMove(const Zstring& fileFrom, const Zstring& fileTo) { reportStatus(txtMovingFile, fileFrom, fileTo); } - virtual void onBeforeDirMove (const Zstring& dirFrom, const Zstring& dirTo ) { reportStatus(txtMovingFolder, dirFrom, dirTo); } - virtual void objectProcessed() //one call after each processed move - { - if (objectsReported_) - { - statusHandler_.updateProcessedData(1, 0); - ++*objectsReported_; - } - } + virtual void onBeforeFileDeletion(const Zstring& filename) { notifyDeletion(txtDeletingFile, filename); } + virtual void onBeforeDirDeletion (const Zstring& dirname ) { notifyDeletion(txtDeletingFolder, dirname ); } - virtual void updateStatus(Int64 bytesDelta) + void notifyDeletion(const std::wstring& statusText, const Zstring& objName) { - //statusHandler_.updateProcessedData(0, bytesDelta); - //bytesReported_ += bytesDelta; -> statistics model *logical* operations! as such a file delete is only (1 obj/0 bytes)! Doesn't matter if it's actually copy + delete - - statusHandler_.requestUiRefresh(); + notifyItemDeletion_(); //it would be more correct to report *after* work was done! + statusHandler_.reportStatus(replaceCpy(statusText, L"%x", fmtFileName(objName))); } - void reportStatus(const std::wstring& statusText, const Zstring& fileFrom, const Zstring& fileTo) const - { - statusHandler_.reportStatus(replaceCpy(replaceCpy(statusText, L"%x", fmtFileName(fileFrom)), L"%y", fmtFileName(fileTo))); - }; - ProcessCallback& statusHandler_; - int* objectsReported_; //optional - - const std::wstring txtMovingFile; - const std::wstring txtMovingFolder; + Function notifyItemDeletion_; + const std::wstring txtDeletingFile; + const std::wstring txtDeletingFolder; }; -struct CallbackRemoveDirImpl : public CallbackRemoveDir +template +class CallbackMoveDirImpl : public CallbackMoveDir { - CallbackRemoveDirImpl(ProcessCallback& statusHandler, - const DeletionHandling& delHandling, - int* objectsReported) : - statusHandler_(statusHandler), - delHandling_(delHandling), - objectsReported_(objectsReported) {} +public: + CallbackMoveDirImpl(ProcessCallback& callback, + Int64& bytesReported, + Function notifyItemDeletion) : + callback_ (callback), + bytesReported_(bytesReported), + notifyItemDeletion_(notifyItemDeletion), + txtMovingFile (_("Moving file %x to %y")), + txtMovingFolder (_("Moving folder %x to %y")) {} private: - virtual void notifyFileDeletion(const Zstring& filename) { processSingleObject(delHandling_.getTxtRemovingFile(), filename); } - virtual void notifyDirDeletion (const Zstring& dirname ) { processSingleObject(delHandling_.getTxtRemovingDir (), dirname ); } + virtual void onBeforeFileMove(const Zstring& fileFrom, const Zstring& fileTo) { notifyMove(txtMovingFile, fileFrom, fileTo); } + virtual void onBeforeDirMove (const Zstring& dirFrom, const Zstring& dirTo ) { notifyMove(txtMovingFolder, dirFrom, dirTo); } - void processSingleObject(const std::wstring& statusText, const Zstring& objName) + void notifyMove(const std::wstring& statusText, const Zstring& fileFrom, const Zstring& fileTo) const { - statusHandler_.reportStatus(replaceCpy(statusText, L"%x", fmtFileName(objName))); + notifyItemDeletion_(); //it would be more correct to report *after* work was done! + callback_.reportStatus(replaceCpy(replaceCpy(statusText, L"%x", fmtFileName(fileFrom)), L"%y", fmtFileName(fileTo))); + }; - if (objectsReported_) - { - statusHandler_.updateProcessedData(1, 0); - ++*objectsReported_; - } + virtual void updateStatus(Int64 bytesDelta) + { + callback_.updateProcessedData(0, bytesDelta); //throw()! -> ensure client and service provider are in sync! + bytesReported_ += bytesDelta; // + + callback_.requestUiRefresh(); //may throw } - ProcessCallback& statusHandler_; - const DeletionHandling& delHandling_; - int* objectsReported_; //optional + ProcessCallback& callback_; + Int64& bytesReported_; + Function notifyItemDeletion_; + const std::wstring txtMovingFile; + const std::wstring txtMovingFolder; }; } -void DeletionHandling::removeFile(const Zstring& relativeName) +template +void DeletionHandling::removeDirUpdating(const Zstring& relativeName, Int64 bytesExpected, Function notifyItemDeletion) //throw FileError { + assert(!baseDirPf_.empty()); const Zstring fullName = baseDirPf_ + relativeName; + Int64 bytesReported; + ScopeGuard guardStatistics = makeGuard([&] { procCallback_.updateTotalData(0, bytesReported); }); //error = unexpected increase of total workload + switch (deletionPolicy_) { case DELETE_PERMANENTLY: - zen::removeFile(fullName); - //[!] scope specifier resolves nameclash! - break; + { + CallbackRemoveDirImpl remDirCallback(procCallback_, *this, notifyItemDeletion); + removeDirectory(fullName, &remDirCallback); + } + break; case DELETE_TO_RECYCLER: + { #ifdef FFS_WIN - { - const Zstring targetFile = recyclerTmpDirPf + relativeName; //ends with path separator + const Zstring targetDir = getOrCreateRecyclerTempDirPf() + relativeName; //throw FileError + bool deleted = false; - auto moveToTempDir = [&] - { - //performance optimization: Instead of moving each object into recycle bin separately, - //we rename them one by one into a temporary directory and batch-recycle this directory after sync - renameFile(fullName, targetFile); //throw FileError - toBeRecycled.push_back(targetFile); - }; + auto moveToTempDir = [&] + { + //performance optimization: Instead of moving each object into recycle bin separately, + //we rename them one by one into a temporary directory and batch-recycle this directory after sync + renameFile(fullName, targetDir); //throw FileError + this->toBeRecycled.push_back(targetDir); + deleted = true; + }; - try - { - moveToTempDir(); //throw FileError - } - catch (FileError&) + try + { + moveToTempDir(); //throw FileError + } + catch (FileError&) + { + if (somethingExists(fullName)) { - if (somethingExists(fullName)) + const Zstring targetSuperDir = beforeLast(targetDir, FILE_NAME_SEPARATOR); + if (!dirExists(targetSuperDir)) { - const Zstring targetDir = beforeLast(targetFile, FILE_NAME_SEPARATOR); - if (!dirExists(targetDir)) - { - makeDirectory(targetDir); //throw FileError -> may legitimately fail on Linux if permissions are missing - moveToTempDir(); //throw FileError -> this should work now! - } - else - throw; + makeDirectory(targetSuperDir); //throw FileError -> may legitimately fail on Linux if permissions are missing + moveToTempDir(); //throw FileError -> this should work now! } + else + throw; } } #elif defined FFS_LINUX - recycleOrDelete(fullName); //throw FileError + const bool deleted = recycleOrDelete(fullName); //throw FileError #endif - break; + if (deleted) + notifyItemDeletion(); //moving to recycler is ONE logical operation, irrespective of the number of child elements! + } + break; case DELETE_TO_VERSIONING: { - CallbackMoveFileImpl callback(procCallback_, nullptr); //we do *not* report statistics in this method - getOrCreateVersioner().revisionFile(fullName, relativeName, callback); //throw FileError + CallbackMoveDirImpl callback(procCallback_, bytesReported, notifyItemDeletion); + getOrCreateVersioner().revisionDir(fullName, relativeName, callback); //throw FileError } break; } + + //update statistics to consider the real amount of data + guardStatistics.dismiss(); + if (bytesReported != bytesExpected) + procCallback_.updateTotalData(0, bytesReported - bytesExpected); //noexcept! } -void DeletionHandling::removeFolderInt(const Zstring& relativeName, const int* objectsExpected, const Int64* dataExpected) //throw FileError +template +void DeletionHandling::removeFileUpdating(const Zstring& relativeName, Int64 bytesExpected, Function notifyItemDeletion) //throw FileError { + assert(!baseDirPf_.empty()); const Zstring fullName = baseDirPf_ + relativeName; - int objectsReported = 0; //use *only* if "objectsExpected" is bound! - //in error situation: undo communication of processed amount of data - ScopeGuard guardStatistics = makeGuard([&] { procCallback_.updateProcessedData(-objectsReported, 0); }); + Int64 bytesReported; + auto guardStatistics = makeGuard([&] { procCallback_.updateTotalData(0, bytesReported); }); //error = unexpected increase of total workload + bool deleted = false; switch (deletionPolicy_) { case DELETE_PERMANENTLY: - { - CallbackRemoveDirImpl remDirCallback(procCallback_, *this, objectsExpected ? &objectsReported : nullptr); - removeDirectory(fullName, &remDirCallback); - } - break; + deleted = zen::removeFile(fullName); //[!] scope specifier resolves nameclash! + break; case DELETE_TO_RECYCLER: #ifdef FFS_WIN { - const Zstring targetDir = recyclerTmpDirPf + relativeName; + const Zstring targetFile = getOrCreateRecyclerTempDirPf() + relativeName; //throw FileError auto moveToTempDir = [&] { //performance optimization: Instead of moving each object into recycle bin separately, //we rename them one by one into a temporary directory and batch-recycle this directory after sync - renameFile(fullName, targetDir); //throw FileError - toBeRecycled.push_back(targetDir); + renameFile(fullName, targetFile); //throw FileError + this->toBeRecycled.push_back(targetFile); + deleted = true; }; try @@ -807,10 +744,10 @@ void DeletionHandling::removeFolderInt(const Zstring& relativeName, const int* o { if (somethingExists(fullName)) { - const Zstring targetSuperDir = beforeLast(targetDir, FILE_NAME_SEPARATOR); - if (!dirExists(targetSuperDir)) + const Zstring targetDir = beforeLast(targetFile, FILE_NAME_SEPARATOR); + if (!dirExists(targetDir)) { - makeDirectory(targetSuperDir); //throw FileError -> may legitimately fail on Linux if permissions are missing + makeDirectory(targetDir); //throw FileError -> may legitimately fail on Linux if permissions are missing moveToTempDir(); //throw FileError -> this should work now! } else @@ -819,31 +756,51 @@ void DeletionHandling::removeFolderInt(const Zstring& relativeName, const int* o } } #elif defined FFS_LINUX - recycleOrDelete(fullName); //throw FileError + deleted = recycleOrDelete(fullName); //throw FileError #endif - - if (objectsExpected) //even though we have only one disk access, we completed "objectsExpected" logical operations! - { - procCallback_.updateProcessedData(*objectsExpected, 0); - objectsReported += *objectsExpected; - } break; case DELETE_TO_VERSIONING: { - CallbackMoveFileImpl callback(procCallback_, objectsExpected ? &objectsReported : nullptr); - getOrCreateVersioner().revisionDir(fullName, relativeName, callback); //throw FileError + struct CallbackMoveFileImpl : public CallbackMoveFile + { + CallbackMoveFileImpl(ProcessCallback& callback, Int64& bytes) : callback_(callback), bytesReported_(bytes) {} + + private: + virtual void updateStatus(Int64 bytesDelta) + { + callback_.updateProcessedData(0, bytesDelta); //throw()! -> ensure client and service provider are in sync! + bytesReported_ += bytesDelta; // + + callback_.requestUiRefresh(); //may throw + } + ProcessCallback& callback_; + Int64& bytesReported_; + } cb(procCallback_, bytesReported); + + deleted = getOrCreateVersioner().revisionFile(fullName, relativeName, cb); //throw FileError } break; } + if (deleted) + notifyItemDeletion(); + + //update statistics to consider the real amount of data + guardStatistics.dismiss(); + if (bytesReported != bytesExpected) + procCallback_.updateTotalData(0, bytesReported - bytesExpected); //noexcept! +} - //inform about the (remaining) processed amount of data - if (objectsExpected && dataExpected) +template inline +void DeletionHandling::removeLinkUpdating(const Zstring& relativeName, Int64 bytesExpected, Function notifyItemDeletion, LinkDescriptor::LinkType lt) //throw FileError +{ + switch (lt) { - guardStatistics.dismiss(); + case LinkDescriptor::TYPE_DIR: + return removeDirUpdating(relativeName, bytesExpected, notifyItemDeletion); //throw FileError - if (*objectsExpected != objectsReported || *dataExpected != 0) //adjust total: may have changed after comparison! - procCallback_.updateTotalData(objectsReported - *objectsExpected, -*dataExpected); + case LinkDescriptor::TYPE_FILE: //Windows: true file symlink; Linux: file-link or broken link + return removeFileUpdating(relativeName, bytesExpected, notifyItemDeletion); //throw FileError } } @@ -1033,9 +990,8 @@ private: void reportInfo (const std::wstring& rawText, const Zstring& objname) const { procCallback_.reportInfo (replaceCpy(rawText, L"%x", fmtFileName(objname))); }; void reportStatus(const std::wstring& rawText, const Zstring& objname) const { procCallback_.reportStatus(replaceCpy(rawText, L"%x", fmtFileName(objname))); }; - //more low level helper - template - void copyFileUpdatingTo(const FileMapping& fileObj, const DelTargetCommand& cmd, FileAttrib& newAttr) const; + template + FileAttrib copyFileUpdatingTo(const FileMapping& fileObj, Function delTargetCommand) const; //throw FileError; reports data delta via updateProcessedData() void verifyFileCopy(const Zstring& source, const Zstring& target) const; template @@ -1115,7 +1071,7 @@ Zstring findUnusedTempName(const Zstring& filename) { Zstring output = filename + zen::TEMP_FILE_ENDING; - //ensure uniqueness + //ensure uniqueness (+ minor file system race condition!) for (int i = 1; somethingExists(output); ++i) output = filename + Zchar('_') + numberTo(i) + zen::TEMP_FILE_ENDING; @@ -1437,8 +1393,8 @@ void SynchronizeFolderPair::runPass(HierarchyObject& hierObj) this->runPass(dirObj); //recurse }); } -//--------------------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------------------- namespace { @@ -1503,26 +1459,25 @@ void SynchronizeFolderPair::synchronizeFileInt(FileMapping& fileObj, SyncOperati case SO_CREATE_NEW_RIGHT: { const Zstring& target = fileObj.getBaseDirPf() + fileObj.getRelativeName(); //can't use "getFullName" as target is not yet existing - reportInfo(txtCreatingFile, target); try { - FileAttrib newAttr; - copyFileUpdatingTo(fileObj, [] {}, /*no target to delete*/ newAttr); //throw FileError - procCallback_.updateProcessedData(1, 0); //processed data is communicated in copyFileUpdatingTo()! + const FileAttrib newAttr = copyFileUpdatingTo(fileObj, [] {} /*no target to delete*/); //throw FileError const FileDescriptor descrSource(newAttr.modificationTime, newAttr.fileSize, newAttr.sourceFileId); const FileDescriptor descrTarget(newAttr.modificationTime, newAttr.fileSize, newAttr.targetFileId); fileObj.syncTo(descrTarget, &descrSource); //update FileMapping + + procCallback_.updateProcessedData(1, 0); //processed bytes are reported in copyFileUpdatingTo()! } catch (FileError&) { - if (somethingExists(fileObj.getFullName())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should not be quiet about it! + if (somethingExists(fileObj.getFullName())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should error out! throw; //source deleted meanwhile...nothing was done (logical point of view!) - procCallback_.updateTotalData(-1, -to(fileObj.getFileSize())); fileObj.removeObject(); + procCallback_.updateTotalData(-1, -to(fileObj.getFileSize())); } } break; @@ -1530,11 +1485,23 @@ void SynchronizeFolderPair::synchronizeFileInt(FileMapping& fileObj, SyncOperati case SO_DELETE_LEFT: case SO_DELETE_RIGHT: reportInfo(getDelHandling().getTxtRemovingFile(), fileObj.getFullName()); + { + int objectsReported = 0; + auto guardStatistics = makeGuard([&] { procCallback_.updateTotalData(objectsReported, 0); }); //error = unexpected increase of total workload + const int objectsExpected = 1; + const Int64 bytesExpected = 0; - getDelHandling().removeFile(fileObj.getObjRelativeName()); //throw FileError - fileObj.removeObject(); //update FileMapping + getDelHandling().removeFileUpdating(fileObj.getObjRelativeName(), bytesExpected, [&] //throw FileError + { + procCallback_.updateProcessedData(1, 0); //noexcept + ++objectsReported; + }); - procCallback_.updateProcessedData(1, 0); + guardStatistics.dismiss(); //update statistics to consider the real amount of data + if (objectsReported != objectsExpected) + procCallback_.updateTotalData(objectsReported - objectsExpected, 0); //noexcept! + } + fileObj.removeObject(); //update FileMapping break; case SO_MOVE_LEFT_SOURCE: @@ -1562,8 +1529,8 @@ void SynchronizeFolderPair::synchronizeFileInt(FileMapping& fileObj, SyncOperati sourceObj->getFileSize (), sourceObj->getFileId ()); - sourceObj->removeObject(); // - targetObj->syncTo(descrTarget); //update FileMapping + sourceObj->removeObject(); //update FileMapping + targetObj->syncTo(descrTarget); // procCallback_.updateProcessedData(1, 0); } @@ -1573,25 +1540,24 @@ void SynchronizeFolderPair::synchronizeFileInt(FileMapping& fileObj, SyncOperati case SO_OVERWRITE_RIGHT: { const Zstring& target = fileObj.getBaseDirPf() + fileObj.getRelativeName(); //respect differences in case of source object - reportInfo(txtOverwritingFile, target); - FileAttrib newAttr; - copyFileUpdatingTo(fileObj, [&] //delete target at appropriate time + const FileAttrib newAttr = copyFileUpdatingTo(fileObj, [&] //delete target at appropriate time { reportStatus(this->getDelHandling().getTxtRemovingFile(), fileObj.getFullName()); - this->getDelHandling().removeFile(fileObj.getObjRelativeName()); //throw FileError - fileObj.removeObject(); //remove file from FileMapping, to keep in sync (if subsequent copying fails!!) + this->getDelHandling().removeFileUpdating(fileObj.getObjRelativeName(), 0, []{}); //throw FileError; + //no (logical) item count update desired - but total byte count may change, e.g. move(copy) deleted file to versioning dir + fileObj.removeObject(); //update FileMapping reportStatus(txtOverwritingFile, target); //restore status text copy file - }, newAttr); + }); const FileDescriptor descrSource(newAttr.modificationTime, newAttr.fileSize, newAttr.sourceFileId); const FileDescriptor descrTarget(newAttr.modificationTime, newAttr.fileSize, newAttr.targetFileId); fileObj.syncTo(descrTarget, &descrSource); //update FileMapping - procCallback_.updateProcessedData(1, 0); + procCallback_.updateProcessedData(1, 0); //we model "delete + copy" as ONE logical operation } break; @@ -1649,20 +1615,6 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymLinkMapping& linkObj, SyncOper { static const SelectedSide sideSrc = OtherSide::result; - auto deleteSymlink = [&] - { - switch (linkObj.getLinkType()) - { - case LinkDescriptor::TYPE_DIR: - this->getDelHandling().removeFolder(linkObj.getObjRelativeName()); //throw FileError - break; - - case LinkDescriptor::TYPE_FILE: //Windows: true file symlink; Linux: file-link or broken link - this->getDelHandling().removeFile(linkObj.getObjRelativeName()); //throw FileError - break; - } - }; - switch (syncOp) { case SO_CREATE_NEW_LEFT: @@ -1675,17 +1627,17 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymLinkMapping& linkObj, SyncOper try { zen::copySymlink(linkObj.getFullName(), target, copyFilePermissions_); //throw FileError - procCallback_.updateProcessedData(1, 0); - linkObj.copyTo(); //update SymLinkMapping + + procCallback_.updateProcessedData(1, 0); } catch (FileError&) { if (somethingExists(linkObj.getFullName())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should not be quiet about it! throw; //source deleted meanwhile...nothing was done (logical point of view!) - procCallback_.updateTotalData(-1, 0); linkObj.removeObject(); + procCallback_.updateTotalData(-1, 0); } } break; @@ -1693,12 +1645,23 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymLinkMapping& linkObj, SyncOper case SO_DELETE_LEFT: case SO_DELETE_RIGHT: reportInfo(getDelHandling().getTxtRemovingSymLink(), linkObj.getFullName()); + { + int objectsReported = 0; + auto guardStatistics = makeGuard([&] { procCallback_.updateTotalData(objectsReported, 0); }); //error = unexpected increase of total workload + const int objectsExpected = 1; + const Int64 bytesExpected = 0; - deleteSymlink(); //throw FileError + getDelHandling().removeLinkUpdating(linkObj.getObjRelativeName(), bytesExpected, [&] //throw FileError + { + procCallback_.updateProcessedData(1, 0); //noexcept + ++objectsReported; + }, linkObj.getLinkType()); + guardStatistics.dismiss(); //update statistics to consider the real amount of data + if (objectsReported != objectsExpected) + procCallback_.updateTotalData(objectsReported - objectsExpected, 0); //noexcept! + } linkObj.removeObject(); //update SymLinkMapping - - procCallback_.updateProcessedData(1, 0); break; case SO_OVERWRITE_LEFT: @@ -1709,14 +1672,14 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymLinkMapping& linkObj, SyncOper reportInfo(txtOverwritingLink, target); reportStatus(getDelHandling().getTxtRemovingSymLink(), linkObj.getFullName()); - deleteSymlink(); //throw FileError + getDelHandling().removeLinkUpdating(linkObj.getObjRelativeName(), 0, []{}, linkObj.getLinkType()); //throw FileError linkObj.removeObject(); //remove file from FileMapping, to keep in sync (if subsequent copying fails!!) reportStatus(txtOverwritingLink, target); //restore status text zen::copySymlink(linkObj.getFullName(), target, copyFilePermissions_); //throw FileError linkObj.copyTo(); //update SymLinkMapping - procCallback_.updateProcessedData(1, 0); + procCallback_.updateProcessedData(1, 0); //we model "delete + copy" as ONE logical operation } break; @@ -1730,7 +1693,6 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymLinkMapping& linkObj, SyncOper if (!sameFileTime(linkObj.getLastWriteTime(), linkObj.getLastWriteTime(), 2)) //respect 2 second FAT/FAT32 precision setFileTime(linkObj.getFullName(), linkObj.getLastWriteTime(), SYMLINK_DIRECT); //throw FileError - linkObj.copyTo(); //-> both sides *should* be completely equal now... procCallback_.updateProcessedData(1, 0); @@ -1777,32 +1739,57 @@ void SynchronizeFolderPair::synchronizeFolderInt(DirMapping& dirObj, SyncOperati { case SO_CREATE_NEW_LEFT: case SO_CREATE_NEW_RIGHT: - if (somethingExists(dirObj.getFullName())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should not be quiet about it! + if (somethingExists(dirObj.getFullName())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should error out! { const Zstring& target = dirObj.getBaseDirPf() + dirObj.getRelativeName(); reportInfo(txtCreatingFolder, target); try { - makeNewDirectory(target, dirObj.getFullName(), copyFilePermissions_); //no symlink copying! + makeNewDirectory(target, dirObj.getFullName(), copyFilePermissions_); //throw FileError, ErrorTargetExisting } - catch (const ErrorTargetExisting&) { if (!dirExists(target)) throw; } //clash with file (dir-symlink is okay) - + catch (const ErrorTargetExisting&) { if (!dirExists(target)) throw; } //detect clash with file (dir-symlink OTOH is okay) dirObj.copyTo(); //update DirMapping procCallback_.updateProcessedData(1, 0); } else //source deleted meanwhile...nothing was done (logical point of view!) -> uh....what about a temporary network drop??? { - // throw FileError + dirObj.refSubFiles().clear(); // + dirObj.refSubLinks().clear(); //update DirMapping + dirObj.refSubDirs ().clear(); // + dirObj.removeObject(); // + const SyncStatistics subStats(dirObj); procCallback_.updateTotalData(-getCUD(subStats) - 1, -subStats.getDataToProcess()); + } + break; - dirObj.refSubFiles().clear(); //...then remove sub-objects - dirObj.refSubLinks().clear(); // - dirObj.refSubDirs ().clear(); // - dirObj.removeObject(); + case SO_DELETE_LEFT: + case SO_DELETE_RIGHT: + reportInfo(getDelHandling().getTxtRemovingDir(), dirObj.getFullName()); + { + int objectsReported = 0; + auto guardStatistics = makeGuard([&] { procCallback_.updateTotalData(objectsReported, 0); }); //error = unexpected increase of total workload + const SyncStatistics subStats(dirObj); //counts sub-objects only! + const int objectsExpected = 1 + getCUD(subStats); + const Int64 bytesExpected = subStats.getDataToProcess(); + assert(bytesExpected == 0); + + getDelHandling().removeDirUpdating(dirObj.getObjRelativeName(), bytesExpected, [&] //throw FileError + { + procCallback_.updateProcessedData(1, 0); //noexcept + ++objectsReported; + }); + + guardStatistics.dismiss(); //update statistics to consider the real amount of data + if (objectsReported != objectsExpected) + procCallback_.updateTotalData(objectsReported - objectsExpected, 0); //noexcept! } + dirObj.refSubFiles().clear(); // + dirObj.refSubLinks().clear(); //update DirMapping + dirObj.refSubDirs ().clear(); // + dirObj.removeObject(); // break; case SO_COPY_METADATA_TO_LEFT: @@ -1812,29 +1799,12 @@ void SynchronizeFolderPair::synchronizeFolderInt(DirMapping& dirObj, SyncOperati if (dirObj.getShortName() != dirObj.getShortName()) //adapt difference in case (windows only) renameFile(dirObj.getFullName(), beforeLast(dirObj.getFullName(), FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + dirObj.getShortName()); //throw FileError - //copyFileTimes -> useless at this time: modification time changes with each child-object creation/deletion - + //copyFileTimes -> useless: modification time changes with each child-object creation/deletion dirObj.copyTo(); //-> both sides *should* be completely equal now... procCallback_.updateProcessedData(1, 0); break; - case SO_DELETE_LEFT: - case SO_DELETE_RIGHT: - { - reportInfo(getDelHandling().getTxtRemovingDir(), dirObj.getFullName()); - - const SyncStatistics subStats(dirObj); //counts sub-objects only! - getDelHandling().removeFolderUpdateStatistics(dirObj.getObjRelativeName(), 1 + getCUD(subStats), subStats.getDataToProcess()); //throw FileError - //this call covers progress indicator updates for dir + sub-objects! - - dirObj.refSubFiles().clear(); //...then remove everything - dirObj.refSubLinks().clear(); // - dirObj.refSubDirs ().clear(); // - dirObj.removeObject(); // - } - break; - case SO_OVERWRITE_RIGHT: case SO_OVERWRITE_LEFT: case SO_MOVE_LEFT_SOURCE: @@ -1936,7 +1906,7 @@ void zen::synchronize(const TimeComp& timeStamp, //specify process and resource handling priorities std::unique_ptr backgroundPrio; if (runWithBackgroundPriority) - backgroundPrio.reset(new ScheduleForBackgroundProcessing); + backgroundPrio = make_unique(); //prevent operating system going into sleep state PreventStandby dummy; @@ -1971,7 +1941,6 @@ void zen::synchronize(const TimeComp& timeStamp, folderPairCfg.versioningFolder, folderPairCfg.versioningStyle_, timeStamp, - folderIndex, j->getBaseDirPf(), callback); @@ -1979,7 +1948,6 @@ void zen::synchronize(const TimeComp& timeStamp, folderPairCfg.versioningFolder, folderPairCfg.versioningStyle_, timeStamp, - folderIndex, j->getBaseDirPf(), callback); } @@ -2209,11 +2177,11 @@ void zen::synchronize(const TimeComp& timeStamp, { std::wstring warningMessage = _("Not enough free disk space available in:"); - for (auto i = diskSpaceMissing.begin(); i != diskSpaceMissing.end(); ++i) + for (auto it = diskSpaceMissing.begin(); it != diskSpaceMissing.end(); ++it) warningMessage += std::wstring(L"\n\n") + - fmtFileName(i->first) + L"\n" + - _("Required:") + L" " + filesizeToShortString(i->second.first) + L"\n" + - _("Available:") + L" " + filesizeToShortString(i->second.second); + fmtFileName(it->first) + L"\n" + + _("Required:") + L" " + filesizeToShortString(it->second.first) + L"\n" + + _("Available:") + L" " + filesizeToShortString(it->second.second); callback.reportWarning(warningMessage, warnings.warningNotEnoughDiskSpace); } @@ -2246,8 +2214,8 @@ void zen::synchronize(const TimeComp& timeStamp, if (!conflictDirs.empty()) { std::wstring warningMessage = _("A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.") + L"\n"; - for (auto i = conflictDirs.begin(); i != conflictDirs.end(); ++i) - warningMessage += L"\n" + fmtFileName(*i); + for (auto it = conflictDirs.begin(); it != conflictDirs.end(); ++it) + warningMessage += L"\n" + fmtFileName(*it); callback.reportWarning(warningMessage, warnings.warningFolderPairRaceCondition); } @@ -2343,61 +2311,58 @@ void zen::synchronize(const TimeComp& timeStamp, } } - //########################################################################################### -//callback functionality for smooth progress indicators -template +template class WhileCopying : public zen::CallbackCopyFile { public: WhileCopying(Int64& bytesReported, ProcessCallback& statusHandler, - const DelTargetCommand& cmd) : + Function delTargetCmd) : bytesReported_(bytesReported), statusHandler_(statusHandler), - cmd_(cmd) {} + delTargetCmd_(std::move(delTargetCmd)) {} - virtual void deleteTargetFile(const Zstring& targetFile) { cmd_(); } + virtual void deleteTargetFile(const Zstring& targetFile) { delTargetCmd_(); } virtual void updateCopyStatus(Int64 bytesDelta) { - //inform about the (differential) processed amount of data - statusHandler_.updateProcessedData(0, bytesDelta); //throw()! -> this ensures client and service provider are in sync! + statusHandler_.updateProcessedData(0, bytesDelta); //throw()! -> ensure client and service provider are in sync! bytesReported_ += bytesDelta; // statusHandler_.requestUiRefresh(); //may throw } private: - Int64& bytesReported_; + Int64& bytesReported_; ProcessCallback& statusHandler_; - DelTargetCommand cmd_; + Function delTargetCmd_; }; -//copy file while refreshing UI -template -void SynchronizeFolderPair::copyFileUpdatingTo(const FileMapping& fileObj, const DelTargetCommand& cmd, FileAttrib& newAttr) const +//throw FileError; reports data delta via updateProcessedData() +template +FileAttrib SynchronizeFolderPair::copyFileUpdatingTo(const FileMapping& fileObj, Function delTargetCommand) const //returns current attributes of source file { - const Int64 expectedBytesToCpy = to(fileObj.getFileSize::result>()); - Zstring source = fileObj.getFullName::result>(); - const Zstring& target = fileObj.getBaseDirPf() + fileObj.getRelativeName::result>(); + static const SelectedSide sideSrc = OtherSide::result; + + FileAttrib newAttr; + const Int64 bytesExpected = to(fileObj.getFileSize()); + Zstring source = fileObj.getFullName(); + const Zstring& target = fileObj.getBaseDirPf() + fileObj.getRelativeName(); Int64 bytesReported; auto copyOperation = [&] { - //start of (possibly) long-running copy process: ensure status updates are performed regularly - - //in error situation: undo communication of processed amount of data auto guardStatistics = makeGuard([&] { - procCallback_.updateProcessedData(0, -bytesReported); + procCallback_.updateTotalData(0, bytesReported); //error = unexpected increase of total workload bytesReported = 0; }); - WhileCopying callback(bytesReported, procCallback_, cmd); + WhileCopying callback(bytesReported, procCallback_, delTargetCommand); copyFile(source, //type File implicitly means symlinks need to be dereferenced! target, @@ -2415,11 +2380,10 @@ void SynchronizeFolderPair::copyFileUpdatingTo(const FileMapping& fileObj, const } //#################### /Verification ############################# - //update statistics to consider the real amount of data, e.g. more than the "file size" for ADS streams or file changed in the meantime! - if (bytesReported != expectedBytesToCpy) - procCallback_.updateTotalData(0, bytesReported - expectedBytesToCpy); - - //we model physical statistic numbers => adjust total: consider ADS, sparse, compressed files -> transferred bytes may differ from file size (which is just a rough guess)! + //update statistics to consider the real amount of data, e.g. more than the "file size" for ADS streams, + //less for sparse and compressed files, or file changed in the meantime! + if (bytesReported != bytesExpected) + procCallback_.updateTotalData(0, bytesReported - bytesExpected); //noexcept! guardStatistics.dismiss(); }; @@ -2450,6 +2414,8 @@ void SynchronizeFolderPair::copyFileUpdatingTo(const FileMapping& fileObj, const #else copyOperation(); #endif + + return newAttr; } diff --git a/synchronization.h b/synchronization.h index 3a3dcec1..e9d15e29 100644 --- a/synchronization.h +++ b/synchronization.h @@ -41,7 +41,6 @@ public: size_t getRowCount() const { return rowsTotal; } private: - //static const size_t MAX_CONFLICTS = 3; void init(); void recurse(const HierarchyObject& hierObj); @@ -53,7 +52,6 @@ private: int createLeft, createRight; int updateLeft, updateRight; int deleteLeft, deleteRight; - // int conflict; ConflictTexts conflictMsgs; //conflict texts to display as a warning message Int64 dataToProcess; size_t rowsTotal; diff --git a/ui/batch_status_handler.cpp b/ui/batch_status_handler.cpp index ebfa84a0..9e8b1489 100644 --- a/ui/batch_status_handler.cpp +++ b/ui/batch_status_handler.cpp @@ -225,7 +225,7 @@ BatchStatusHandler::~BatchStatusHandler() showFinalResults = false; //take precedence over current visibility status else if (!finalCommand.empty()) { - auto cmdexp = utfCvrtTo(expandMacros(utfCvrtTo(finalCommand))); + auto cmdexp = expandMacros(utfCvrtTo(finalCommand)); shellExecute(cmdexp); } } @@ -262,17 +262,7 @@ void BatchStatusHandler::updateProcessedData(int objectsDelta, Int64 dataDelta) { StatusHandler::updateProcessedData(objectsDelta, dataDelta); - switch (currentPhase()) - { - case ProcessCallback::PHASE_NONE: - assert(false); - case ProcessCallback::PHASE_SCANNING: - break; - case ProcessCallback::PHASE_COMPARING_CONTENT: - case ProcessCallback::PHASE_SYNCHRONIZING: - syncStatusFrame.reportCurrentBytes(getDataCurrent(currentPhase())); - break; - } + syncStatusFrame.notifyProgressChange(); //noexcept //note: this method should NOT throw in order to properly allow undoing setting of statistics! } @@ -415,7 +405,7 @@ void BatchStatusHandler::reportFatalError(const std::wstring& errorMessage) void BatchStatusHandler::forceUiRefresh() { - syncStatusFrame.updateProgress(); + syncStatusFrame.updateGui(); } diff --git a/ui/check_version.cpp b/ui/check_version.cpp index c9d2049d..922708c7 100644 --- a/ui/check_version.cpp +++ b/ui/check_version.cpp @@ -68,7 +68,7 @@ public: if (statusCode != HTTP_STATUS_OK) throw InternetConnectionError(); //e.g. 404 - HTTP_STATUS_NOT_FOUND - guardRequest.dismiss(); + guardRequest .dismiss(); guardInternet.dismiss(); } @@ -113,10 +113,7 @@ bool canAccessUrl(const wchar_t* url) //throw () (void)WinInetAccess(url); //throw InternetConnectionError return true; } - catch (const InternetConnectionError&) - { - return false; - } + catch (const InternetConnectionError&) { return false; } } diff --git a/ui/custom_grid.cpp b/ui/custom_grid.cpp index ffa0a9bc..e1e2b5a2 100644 --- a/ui/custom_grid.cpp +++ b/ui/custom_grid.cpp @@ -1171,7 +1171,7 @@ private: toolTip.hide(); //if invalid row... } - virtual wxString getToolTip(ColumnType colType) const { return showSyncAction_ ? _("Action") : _("Category"); } + virtual wxString getToolTip(ColumnType colType) const { return showSyncAction_ ? _("Action") + L" (F8)" : _("Category") + L" (F8)"; } bool showSyncAction_; std::unique_ptr> highlight; //(row, block) current mouse highlight diff --git a/ui/grid_view.cpp b/ui/grid_view.cpp index dbaff5cd..9a5143c1 100644 --- a/ui/grid_view.cpp +++ b/ui/grid_view.cpp @@ -75,7 +75,7 @@ void GridView::updateView(Predicate pred) //"this->" required by two-pass lookup as enforced by GCC 4.7 //save row position to identify first child *on sorted subview* of DirMapping or BaseDirMapping in case latter are filtered out - const HierarchyObject* parent = &(fsObj->parent()); + const HierarchyObject* parent = &fsObj->parent(); for (;;) //map all yet unassociated parents to this row { const auto rv = this->rowPositionsFirstChild.insert(std::make_pair(parent, viewRef.size())); diff --git a/ui/gui_generated.cpp b/ui/gui_generated.cpp index 43a14ae3..84d86ceb 100644 --- a/ui/gui_generated.cpp +++ b/ui/gui_generated.cpp @@ -133,7 +133,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const m_buttonCompare = new zen::BitmapButton( m_panelTopButtons, wxID_OK, _("Compare"), wxDefaultPosition, wxSize( 180,46 ), 0 ); m_buttonCompare->SetDefault(); m_buttonCompare->SetFont( wxFont( 14, 74, 90, 92, false, wxEmptyString ) ); - m_buttonCompare->SetToolTip( _("Compare both sides") ); + m_buttonCompare->SetToolTip( _("dummy") ); bSizer30->Add( m_buttonCompare, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -184,11 +184,11 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const fgSizer12->Add( m_bpButtonSyncConfig, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 3 ); - m_buttonStartSync = new zen::BitmapButton( m_panelTopButtons, wxID_ANY, _("Synchronize"), wxDefaultPosition, wxSize( 180,46 ), 0 ); - m_buttonStartSync->SetFont( wxFont( 14, 74, 90, 92, false, wxEmptyString ) ); - m_buttonStartSync->SetToolTip( _("Start synchronization") ); + m_buttonSync = new zen::BitmapButton( m_panelTopButtons, wxID_ANY, _("Synchronize"), wxDefaultPosition, wxSize( 180,46 ), 0 ); + m_buttonSync->SetFont( wxFont( 14, 74, 90, 92, false, wxEmptyString ) ); + m_buttonSync->SetToolTip( _("dummy") ); - fgSizer12->Add( m_buttonStartSync, 0, wxALIGN_CENTER_VERTICAL, 5 ); + fgSizer12->Add( m_buttonSync, 0, wxALIGN_CENTER_VERTICAL, 5 ); bSizer1551->Add( fgSizer12, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM, 5 ); @@ -386,6 +386,10 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizer451 = new wxBoxSizer( wxHORIZONTAL ); bSizer451->SetMinSize( wxSize( -1,22 ) ); + bSizerFileStatus = new wxBoxSizer( wxHORIZONTAL ); + + bSizerStatusLeft = new wxBoxSizer( wxHORIZONTAL ); + wxBoxSizer* bSizer53; bSizer53 = new wxBoxSizer( wxHORIZONTAL ); @@ -436,25 +440,30 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizer53->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); - bSizer451->Add( bSizer53, 1, wxALIGN_BOTTOM|wxEXPAND, 5 ); + bSizerStatusLeft->Add( bSizer53, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); m_staticline9 = new wxStaticLine( m_panelStatusBar, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer451->Add( m_staticline9, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxEXPAND, 2 ); + bSizerStatusLeft->Add( m_staticline9, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxTOP, 2 ); - bSizer451->Add( 26, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); + bSizerFileStatus->Add( bSizerStatusLeft, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerFileStatus->Add( 26, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); m_staticTextStatusMiddle = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextStatusMiddle->Wrap( -1 ); m_staticTextStatusMiddle->SetFont( wxFont( 8, 70, 90, 92, false, wxEmptyString ) ); - bSizer451->Add( m_staticTextStatusMiddle, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + bSizerFileStatus->Add( m_staticTextStatusMiddle, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + bSizerFileStatus->Add( 26, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); - bSizer451->Add( 26, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); + bSizerStatusRight = new wxBoxSizer( wxHORIZONTAL ); m_staticline10 = new wxStaticLine( m_panelStatusBar, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer451->Add( m_staticline10, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP, 2 ); + bSizerStatusRight->Add( m_staticline10, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP, 2 ); wxBoxSizer* bSizer52; bSizer52 = new wxBoxSizer( wxHORIZONTAL ); @@ -506,7 +515,19 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizer52->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); - bSizer451->Add( bSizer52, 1, wxALIGN_BOTTOM|wxEXPAND, 5 ); + bSizerStatusRight->Add( bSizer52, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizerFileStatus->Add( bSizerStatusRight, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer451->Add( bSizerFileStatus, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_staticTextFullStatus = new wxStaticText( m_panelStatusBar, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextFullStatus->Wrap( -1 ); + m_staticTextFullStatus->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + + bSizer451->Add( m_staticTextFullStatus, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); m_panelStatusBar->SetSizer( bSizer451 ); @@ -526,13 +547,13 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer151; bSizer151 = new wxBoxSizer( wxHORIZONTAL ); - m_bpButtonLoad = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - m_bpButtonLoad->SetToolTip( _("Open") ); + m_bpButtonOpen = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonOpen->SetToolTip( _("dummy") ); - bSizer151->Add( m_bpButtonLoad, 0, wxALIGN_CENTER_VERTICAL, 5 ); + bSizer151->Add( m_bpButtonOpen, 0, wxALIGN_CENTER_VERTICAL, 5 ); m_bpButtonSave = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - m_bpButtonSave->SetToolTip( _("Save") ); + m_bpButtonSave->SetToolTip( _("dummy") ); bSizer151->Add( m_bpButtonSave, 0, wxALIGN_CENTER_VERTICAL, 5 ); @@ -545,7 +566,6 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizerConfig->Add( bSizer151, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); m_listBoxHistory = new wxListBox( m_panelConfig, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_EXTENDED|wxLB_NEEDED_SB|wxLB_SORT ); - m_listBoxHistory->SetToolTip( _("Last used configurations (press DEL to remove from list)") ); m_listBoxHistory->SetMinSize( wxSize( -1,40 ) ); bSizerConfig->Add( m_listBoxHistory, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); @@ -778,47 +798,47 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizerViewFilter->Add( 0, 0, 1, wxEXPAND, 5 ); - m_bpButtonSyncCreateLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonSyncCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_bpButtonShowCreateLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonSyncDirOverwLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonSyncDirOverwLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_bpButtonShowUpdateLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowUpdateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonSyncDeleteLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonSyncDeleteLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_bpButtonShowDeleteLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowDeleteLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonLeftOnly = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonLeftOnly, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_bpButtonShowLeftOnly = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowLeftOnly, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonLeftNewer = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonLeftNewer, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_bpButtonShowLeftNewer = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowLeftNewer, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonEqual = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonEqual, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_bpButtonShowEqual = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowEqual, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonDifferent = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonDifferent, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_bpButtonShowDifferent = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowDifferent, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonSyncDirNone = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonSyncDirNone, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_bpButtonShowDoNothing = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowDoNothing, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonRightNewer = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonRightNewer, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_bpButtonShowRightNewer = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowRightNewer, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonRightOnly = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonRightOnly, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_bpButtonShowRightOnly = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowRightOnly, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonSyncDeleteRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonSyncDeleteRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_bpButtonShowDeleteRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowDeleteRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonSyncDirOverwRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonSyncDirOverwRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_bpButtonShowUpdateRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowUpdateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonSyncCreateRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonSyncCreateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_bpButtonShowCreateRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowCreateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonConflict = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); - bSizerViewFilter->Add( m_bpButtonConflict, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_bpButtonShowConflict = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizerViewFilter->Add( m_bpButtonShowConflict, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); bSizerViewFilter->Add( 0, 0, 1, wxEXPAND, 5 ); @@ -850,33 +870,51 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const this->Connect( m_menuItemAbout->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuAbout ) ); m_buttonCompare->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnCompare ), NULL, this ); m_bpButtonCmpConfig->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnCmpSettings ), NULL, this ); + m_bpButtonCmpConfig->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnCompSettingsContext ), NULL, this ); m_bpButtonSyncConfig->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncSettings ), NULL, this ); - m_buttonStartSync->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnStartSync ), NULL, this ); + m_bpButtonSyncConfig->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnSyncSettingsContext ), NULL, this ); + m_buttonSync->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnStartSync ), NULL, this ); m_bpButtonAddPair->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnAddFolderPair ), NULL, this ); m_bpButtonRemovePair->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnRemoveTopFolderPair ), NULL, this ); m_bpButtonSwapSides->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSwapSides ), NULL, this ); - m_bpButtonLoad->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigLoad ), NULL, this ); + m_bpButtonOpen->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigLoad ), NULL, this ); m_bpButtonSave->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigSave ), NULL, this ); m_bpButtonBatchJob->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSaveAsBatchJob ), NULL, this ); m_listBoxHistory->Connect( wxEVT_CHAR, wxKeyEventHandler( MainDialogGenerated::OnCfgHistoryKeyEvent ), NULL, this ); m_listBoxHistory->Connect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnLoadFromHistory ), NULL, this ); m_listBoxHistory->Connect( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, wxCommandEventHandler( MainDialogGenerated::OnLoadFromHistoryDoubleClick ), NULL, this ); + m_listBoxHistory->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnCfgHistoryRightClick ), NULL, this ); m_bpButtonFilter->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigureFilter ), NULL, this ); + m_bpButtonFilter->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnGlobalFilterContext ), NULL, this ); m_checkBoxHideExcluded->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnShowExcluded ), NULL, this ); - m_bpButtonSyncCreateLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncCreateLeft ), NULL, this ); - m_bpButtonSyncDirOverwLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDirLeft ), NULL, this ); - m_bpButtonSyncDeleteLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDeleteLeft ), NULL, this ); - m_bpButtonLeftOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnLeftOnlyFiles ), NULL, this ); - m_bpButtonLeftNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnLeftNewerFiles ), NULL, this ); - m_bpButtonEqual->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnEqualFiles ), NULL, this ); - m_bpButtonDifferent->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnDifferentFiles ), NULL, this ); - m_bpButtonSyncDirNone->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDirNone ), NULL, this ); - m_bpButtonRightNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnRightNewerFiles ), NULL, this ); - m_bpButtonRightOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnRightOnlyFiles ), NULL, this ); - m_bpButtonSyncDeleteRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDeleteRight ), NULL, this ); - m_bpButtonSyncDirOverwRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDirRight ), NULL, this ); - m_bpButtonSyncCreateRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncCreateRight ), NULL, this ); - m_bpButtonConflict->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConflictFiles ), NULL, this ); + m_bpButtonShowCreateLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowCreateLeft->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowUpdateLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowUpdateLeft->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowDeleteLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowDeleteLeft->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowLeftOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowLeftOnly->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowLeftNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowLeftNewer->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowEqual->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowEqual->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowDifferent->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowDifferent->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowDoNothing->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowDoNothing->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowRightNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowRightNewer->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowRightOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowRightOnly->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowDeleteRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowDeleteRight->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowUpdateRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowUpdateRight->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowCreateRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowCreateRight->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowConflict->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowConflict->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); } MainDialogGenerated::~MainDialogGenerated() @@ -898,33 +936,51 @@ MainDialogGenerated::~MainDialogGenerated() this->Disconnect( wxID_ABOUT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuAbout ) ); m_buttonCompare->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnCompare ), NULL, this ); m_bpButtonCmpConfig->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnCmpSettings ), NULL, this ); + m_bpButtonCmpConfig->Disconnect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnCompSettingsContext ), NULL, this ); m_bpButtonSyncConfig->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncSettings ), NULL, this ); - m_buttonStartSync->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnStartSync ), NULL, this ); + m_bpButtonSyncConfig->Disconnect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnSyncSettingsContext ), NULL, this ); + m_buttonSync->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnStartSync ), NULL, this ); m_bpButtonAddPair->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnAddFolderPair ), NULL, this ); m_bpButtonRemovePair->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnRemoveTopFolderPair ), NULL, this ); m_bpButtonSwapSides->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSwapSides ), NULL, this ); - m_bpButtonLoad->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigLoad ), NULL, this ); + m_bpButtonOpen->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigLoad ), NULL, this ); m_bpButtonSave->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigSave ), NULL, this ); m_bpButtonBatchJob->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSaveAsBatchJob ), NULL, this ); m_listBoxHistory->Disconnect( wxEVT_CHAR, wxKeyEventHandler( MainDialogGenerated::OnCfgHistoryKeyEvent ), NULL, this ); m_listBoxHistory->Disconnect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnLoadFromHistory ), NULL, this ); m_listBoxHistory->Disconnect( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, wxCommandEventHandler( MainDialogGenerated::OnLoadFromHistoryDoubleClick ), NULL, this ); + m_listBoxHistory->Disconnect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnCfgHistoryRightClick ), NULL, this ); 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_bpButtonSyncCreateLeft->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncCreateLeft ), NULL, this ); - m_bpButtonSyncDirOverwLeft->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDirLeft ), NULL, this ); - m_bpButtonSyncDeleteLeft->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDeleteLeft ), NULL, this ); - m_bpButtonLeftOnly->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnLeftOnlyFiles ), NULL, this ); - m_bpButtonLeftNewer->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnLeftNewerFiles ), NULL, this ); - m_bpButtonEqual->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnEqualFiles ), NULL, this ); - m_bpButtonDifferent->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnDifferentFiles ), NULL, this ); - m_bpButtonSyncDirNone->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDirNone ), NULL, this ); - m_bpButtonRightNewer->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnRightNewerFiles ), NULL, this ); - m_bpButtonRightOnly->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnRightOnlyFiles ), NULL, this ); - m_bpButtonSyncDeleteRight->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDeleteRight ), NULL, this ); - m_bpButtonSyncDirOverwRight->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDirRight ), NULL, this ); - m_bpButtonSyncCreateRight->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncCreateRight ), NULL, this ); - m_bpButtonConflict->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConflictFiles ), 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 ); + m_bpButtonShowUpdateLeft->Disconnect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowDeleteLeft->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowDeleteLeft->Disconnect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowLeftOnly->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowLeftOnly->Disconnect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowLeftNewer->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowLeftNewer->Disconnect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowEqual->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowEqual->Disconnect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowDifferent->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowDifferent->Disconnect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowDoNothing->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowDoNothing->Disconnect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowRightNewer->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowRightNewer->Disconnect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowRightOnly->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowRightOnly->Disconnect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowDeleteRight->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowDeleteRight->Disconnect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowUpdateRight->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowUpdateRight->Disconnect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowCreateRight->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowCreateRight->Disconnect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); + m_bpButtonShowConflict->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this ); + m_bpButtonShowConflict->Disconnect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this ); } @@ -2322,7 +2378,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizerCodeInfo = new wxBoxSizer( wxVERTICAL ); - m_staticText72 = new wxStaticText( m_panel33, wxID_ANY, _("Source code written in C++ utilizing:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText72 = new wxStaticText( m_panel33, wxID_ANY, _("Source code written in C++ using:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText72->Wrap( -1 ); m_staticText72->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); @@ -2334,35 +2390,30 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS wxBoxSizer* bSizer171; bSizer171 = new wxBoxSizer( wxHORIZONTAL ); - m_hyperlink9 = new wxHyperlinkCtrl( m_panel33, wxID_ANY, _("MinGW"), wxT("http://www.mingw.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink9->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - bSizer171->Add( m_hyperlink9, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - m_hyperlink11 = new wxHyperlinkCtrl( m_panel33, wxID_ANY, _("MS Visual C++"), wxT("http://msdn.microsoft.com/library/60k1461a.aspx"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); m_hyperlink11->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); bSizer171->Add( m_hyperlink11, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + m_hyperlink9 = new wxHyperlinkCtrl( m_panel33, wxID_ANY, _("MinGW"), wxT("http://www.mingw.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink9->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + bSizer171->Add( m_hyperlink9, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + m_hyperlink10 = new wxHyperlinkCtrl( m_panel33, wxID_ANY, _("Code::Blocks"), wxT("http://www.codeblocks.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); m_hyperlink10->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); bSizer171->Add( m_hyperlink10, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - m_hyperlink13 = new wxHyperlinkCtrl( m_panel33, wxID_ANY, _("Boost"), wxT("http://www.boost.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink13->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - bSizer171->Add( m_hyperlink13, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - m_hyperlink7 = new wxHyperlinkCtrl( m_panel33, wxID_ANY, _("wxWidgets"), wxT("http://www.wxwidgets.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); m_hyperlink7->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); bSizer171->Add( m_hyperlink7, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - m_hyperlink16 = new wxHyperlinkCtrl( m_panel33, wxID_ANY, _("Artistic Style"), wxT("http://astyle.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink16->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink14 = new wxHyperlinkCtrl( m_panel33, wxID_ANY, _("wxFormBuilder"), wxT("http://wxformbuilder.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink14->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - bSizer171->Add( m_hyperlink16, 0, wxALIGN_CENTER_VERTICAL, 5 ); + bSizer171->Add( m_hyperlink14, 0, wxALIGN_CENTER_VERTICAL, 5 ); bSizer167->Add( bSizer171, 0, wxALIGN_CENTER_HORIZONTAL|wxBOTTOM, 5 ); @@ -2370,16 +2421,21 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS wxBoxSizer* bSizer172; bSizer172 = new wxBoxSizer( wxHORIZONTAL ); - m_hyperlink8 = new wxHyperlinkCtrl( m_panel33, wxID_ANY, _("Loki"), wxT("http://loki-lib.sourceforge.net/"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink8->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - bSizer172->Add( m_hyperlink8, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - m_hyperlink15 = new wxHyperlinkCtrl( m_panel33, wxID_ANY, _("zenXML"), wxT("http://zenxml.sourceforge.net/"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); m_hyperlink15->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); bSizer172->Add( m_hyperlink15, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + m_hyperlink13 = new wxHyperlinkCtrl( m_panel33, wxID_ANY, _("Boost"), wxT("http://www.boost.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink13->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + bSizer172->Add( m_hyperlink13, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + + m_hyperlink16 = new wxHyperlinkCtrl( m_panel33, wxID_ANY, _("Artistic Style"), wxT("http://astyle.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink16->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + bSizer172->Add( m_hyperlink16, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + m_hyperlink12 = new wxHyperlinkCtrl( m_panel33, wxID_ANY, _("Google Test"), wxT("http://code.google.com/p/googletest"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); m_hyperlink12->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); @@ -2388,12 +2444,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS m_hyperlink18 = new wxHyperlinkCtrl( m_panel33, wxID_ANY, _("Unicode NSIS"), wxT("http://www.scratchpaper.com"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); m_hyperlink18->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - bSizer172->Add( m_hyperlink18, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - - m_hyperlink14 = new wxHyperlinkCtrl( m_panel33, wxID_ANY, _("wxFormBuilder"), wxT("http://wxformbuilder.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); - m_hyperlink14->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - bSizer172->Add( m_hyperlink14, 0, wxALIGN_CENTER_VERTICAL, 5 ); + bSizer172->Add( m_hyperlink18, 0, wxALIGN_CENTER_VERTICAL, 5 ); bSizer167->Add( bSizer172, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); diff --git a/ui/gui_generated.h b/ui/gui_generated.h index bf93658b..f772f567 100644 --- a/ui/gui_generated.h +++ b/ui/gui_generated.h @@ -93,7 +93,7 @@ protected: wxBitmapButton* m_bpButtonCmpConfig; wxStaticText* m_staticTextSyncVariant; wxBitmapButton* m_bpButtonSyncConfig; - zen::BitmapButton* m_buttonStartSync; + zen::BitmapButton* m_buttonSync; wxPanel* m_panelDirectoryPairs; wxStaticText* m_staticTextResolvedPathL; wxBitmapButton* m_bpButtonAddPair; @@ -111,6 +111,8 @@ protected: zen::Grid* m_gridMainC; zen::Grid* m_gridMainR; wxPanel* m_panelStatusBar; + wxBoxSizer* bSizerFileStatus; + wxBoxSizer* bSizerStatusLeft; wxBoxSizer* bSizerStatusLeftDirectories; wxStaticBitmap* m_bitmapSmallDirectoryLeft; wxStaticText* m_staticTextStatusLeftDirs; @@ -120,6 +122,7 @@ protected: wxStaticText* m_staticTextStatusLeftBytes; wxStaticLine* m_staticline9; wxStaticText* m_staticTextStatusMiddle; + wxBoxSizer* bSizerStatusRight; wxStaticLine* m_staticline10; wxBoxSizer* bSizerStatusRightDirectories; wxStaticBitmap* m_bitmapSmallDirectoryRight; @@ -128,9 +131,10 @@ protected: wxStaticBitmap* m_bitmapSmallFileRight; wxStaticText* m_staticTextStatusRightFiles; wxStaticText* m_staticTextStatusRightBytes; + wxStaticText* m_staticTextFullStatus; wxPanel* m_panelConfig; wxBoxSizer* bSizerConfig; - wxBitmapButton* m_bpButtonLoad; + wxBitmapButton* m_bpButtonOpen; wxBitmapButton* m_bpButtonSave; wxBitmapButton* m_bpButtonBatchJob; wxListBox* m_listBoxHistory; @@ -155,20 +159,20 @@ protected: wxStaticText* m_staticTextCreateRight; wxPanel* m_panelViewFilter; wxBoxSizer* bSizerViewFilter; - ToggleButton* m_bpButtonSyncCreateLeft; - ToggleButton* m_bpButtonSyncDirOverwLeft; - ToggleButton* m_bpButtonSyncDeleteLeft; - ToggleButton* m_bpButtonLeftOnly; - ToggleButton* m_bpButtonLeftNewer; - ToggleButton* m_bpButtonEqual; - ToggleButton* m_bpButtonDifferent; - ToggleButton* m_bpButtonSyncDirNone; - ToggleButton* m_bpButtonRightNewer; - ToggleButton* m_bpButtonRightOnly; - ToggleButton* m_bpButtonSyncDeleteRight; - ToggleButton* m_bpButtonSyncDirOverwRight; - ToggleButton* m_bpButtonSyncCreateRight; - ToggleButton* m_bpButtonConflict; + ToggleButton* m_bpButtonShowCreateLeft; + ToggleButton* m_bpButtonShowUpdateLeft; + ToggleButton* m_bpButtonShowDeleteLeft; + ToggleButton* m_bpButtonShowLeftOnly; + ToggleButton* m_bpButtonShowLeftNewer; + ToggleButton* m_bpButtonShowEqual; + ToggleButton* m_bpButtonShowDifferent; + ToggleButton* m_bpButtonShowDoNothing; + ToggleButton* m_bpButtonShowRightNewer; + ToggleButton* m_bpButtonShowRightOnly; + ToggleButton* m_bpButtonShowDeleteRight; + ToggleButton* m_bpButtonShowUpdateRight; + ToggleButton* m_bpButtonShowCreateRight; + ToggleButton* m_bpButtonShowConflict; // Virtual event handlers, overide them in your derived class virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } @@ -186,29 +190,21 @@ protected: virtual void OnMenuCheckVersion( wxCommandEvent& event ) { event.Skip(); } virtual void OnMenuAbout( wxCommandEvent& event ) { event.Skip(); } virtual void OnCmpSettings( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCompSettingsContext( wxMouseEvent& event ) { event.Skip(); } virtual void OnSyncSettings( wxCommandEvent& event ) { event.Skip(); } + virtual void OnSyncSettingsContext( wxMouseEvent& event ) { event.Skip(); } virtual void OnAddFolderPair( wxCommandEvent& event ) { event.Skip(); } virtual void OnRemoveTopFolderPair( wxCommandEvent& event ) { event.Skip(); } virtual void OnSwapSides( wxCommandEvent& event ) { event.Skip(); } virtual void OnCfgHistoryKeyEvent( wxKeyEvent& event ) { event.Skip(); } virtual void OnLoadFromHistory( wxCommandEvent& event ) { event.Skip(); } virtual void OnLoadFromHistoryDoubleClick( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCfgHistoryRightClick( wxMouseEvent& event ) { event.Skip(); } virtual void OnConfigureFilter( wxCommandEvent& event ) { event.Skip(); } + virtual void OnGlobalFilterContext( wxMouseEvent& event ) { event.Skip(); } virtual void OnShowExcluded( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSyncCreateLeft( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSyncDirLeft( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSyncDeleteLeft( wxCommandEvent& event ) { event.Skip(); } - virtual void OnLeftOnlyFiles( wxCommandEvent& event ) { event.Skip(); } - virtual void OnLeftNewerFiles( wxCommandEvent& event ) { event.Skip(); } - virtual void OnEqualFiles( wxCommandEvent& event ) { event.Skip(); } - virtual void OnDifferentFiles( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSyncDirNone( wxCommandEvent& event ) { event.Skip(); } - virtual void OnRightNewerFiles( wxCommandEvent& event ) { event.Skip(); } - virtual void OnRightOnlyFiles( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSyncDeleteRight( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSyncDirRight( wxCommandEvent& event ) { event.Skip(); } - virtual void OnSyncCreateRight( wxCommandEvent& event ) { event.Skip(); } - virtual void OnConflictFiles( wxCommandEvent& event ) { event.Skip(); } + virtual void OnToggleViewButton( wxCommandEvent& event ) { event.Skip(); } + virtual void OnViewButtonRightClick( wxMouseEvent& event ) { event.Skip(); } public: @@ -594,17 +590,16 @@ protected: wxPanel* m_panel33; wxBoxSizer* bSizerCodeInfo; wxStaticText* m_staticText72; - wxHyperlinkCtrl* m_hyperlink9; wxHyperlinkCtrl* m_hyperlink11; + wxHyperlinkCtrl* m_hyperlink9; wxHyperlinkCtrl* m_hyperlink10; - wxHyperlinkCtrl* m_hyperlink13; wxHyperlinkCtrl* m_hyperlink7; - wxHyperlinkCtrl* m_hyperlink16; - wxHyperlinkCtrl* m_hyperlink8; + wxHyperlinkCtrl* m_hyperlink14; wxHyperlinkCtrl* m_hyperlink15; + wxHyperlinkCtrl* m_hyperlink13; + wxHyperlinkCtrl* m_hyperlink16; wxHyperlinkCtrl* m_hyperlink12; wxHyperlinkCtrl* m_hyperlink18; - wxHyperlinkCtrl* m_hyperlink14; wxHyperlinkCtrl* m_hyperlink21; wxPanel* m_panel40; wxPanel* m_panel39; diff --git a/ui/gui_status_handler.cpp b/ui/gui_status_handler.cpp index 0f915ed9..5d3a1f36 100644 --- a/ui/gui_status_handler.cpp +++ b/ui/gui_status_handler.cpp @@ -250,7 +250,7 @@ SyncStatusHandler::~SyncStatusHandler() showFinalResults = false; //take precedence over current visibility status else if (!finalCommand.empty()) { - auto cmdexp = utfCvrtTo(expandMacros(utfCvrtTo(finalCommand))); + auto cmdexp = expandMacros(utfCvrtTo(finalCommand)); shellExecute(cmdexp); } } @@ -283,7 +283,7 @@ void SyncStatusHandler::initNewPhase(int objectsTotal, Int64 dataTotal, Phase ph void SyncStatusHandler::updateProcessedData(int objectsDelta, Int64 dataDelta) { StatusHandler::updateProcessedData(objectsDelta, dataDelta); - syncStatusFrame.reportCurrentBytes(getDataCurrent(currentPhase())); //throw () + syncStatusFrame.notifyProgressChange(); //noexcept //note: this method should NOT throw in order to properly allow undoing setting of statistics! } @@ -410,7 +410,7 @@ void SyncStatusHandler::reportWarning(const std::wstring& warningMessage, bool& void SyncStatusHandler::forceUiRefresh() { - syncStatusFrame.updateProgress(); + syncStatusFrame.updateGui(); } diff --git a/ui/main_dlg.cpp b/ui/main_dlg.cpp index c3f33db5..cae45cd1 100644 --- a/ui/main_dlg.cpp +++ b/ui/main_dlg.cpp @@ -5,57 +5,44 @@ // ************************************************************************** #include "main_dlg.h" -#include -#include #include -#include -#include #include #include #include -#include -#include -#include #include #include +#include +#include +#include +#include #include -#include "folder_history_box.h" #include -#include "../comparison.h" -#include "../synchronization.h" -#include "../algorithm.h" +#include #include +#include +#include +#include +#include #include "check_version.h" #include "gui_status_handler.h" #include "sync_cfg.h" #include "small_dlgs.h" -#include #include "progress_indicator.h" #include "msg_popup.h" -#include "../structures.h" -#include "grid_view.h" +#include "folder_pair.h" +#include "search.h" +#include "batch_config.h" +#include "triple_splitter.h" +#include "../comparison.h" +#include "../synchronization.h" +#include "../algorithm.h" #include "../lib/resources.h" -#include -#include -#include -#include #include "../lib/resolve_path.h" #include "../lib/ffs_paths.h" -#include -#include "folder_pair.h" -#include -#include "search.h" #include "../lib/help_provider.h" -#include "batch_config.h" -#include #include "../lib/lock_holder.h" -#include #include "../lib/localization.h" -#include -#include -#include -#include "../lib/error_log.h" -#include "triple_splitter.h" +#include using namespace zen; using namespace std::rel_ops; @@ -281,12 +268,13 @@ public: virtual bool allowMove(const wxMouseEvent& event) { - wxPanel* panel = dynamic_cast(event.GetEventObject()); - - const wxAuiPaneInfo& paneInfo = mainDlg_.auiMgr.GetPane(panel); - if (paneInfo.IsOk() && - paneInfo.IsFloating()) - return false; //prevent main dialog move + if (wxPanel* panel = dynamic_cast(event.GetEventObject())) + { + const wxAuiPaneInfo& paneInfo = mainDlg_.auiMgr.GetPane(panel); + if (paneInfo.IsOk() && + paneInfo.IsFloating()) + return false; //prevent main dialog move + } return true; //allow dialog move } @@ -507,7 +495,7 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg, wxAuiPaneInfo().Name(wxT("Panel5")).Layer(4).Bottom().Row(1).Position(1).Caption(_("Filter files")).MinSize(m_bpButtonFilter->GetSize().GetWidth(), m_panelFilter->GetSize().GetHeight())); auiMgr.AddPane(m_panelViewFilter, - wxAuiPaneInfo().Name(wxT("Panel6")).Layer(4).Bottom().Row(1).Position(2).Caption(_("Select view")).MinSize(m_bpButtonSyncDirNone->GetSize().GetWidth(), m_panelViewFilter->GetSize().GetHeight())); + wxAuiPaneInfo().Name(wxT("Panel6")).Layer(4).Bottom().Row(1).Position(2).Caption(_("Select view")).MinSize(m_bpButtonShowDoNothing->GetSize().GetWidth(), m_panelViewFilter->GetSize().GetHeight())); auiMgr.AddPane(m_panelStatistics, wxAuiPaneInfo().Name(wxT("Panel7")).Layer(4).Bottom().Row(1).Position(3).Caption(_("Statistics")).MinSize(m_bitmapData->GetSize().GetWidth() + m_staticTextData->GetSize().GetWidth(), m_panelStatistics->GetSize().GetHeight())); @@ -536,19 +524,14 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg, m_panelStatusBar ->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(MainDialog::OnContextSetLayout), nullptr, this); //---------------------------------------------------------------------------------- - //register context: quick variant selection - m_bpButtonCmpConfig ->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler (MainDialog::OnCompSettingsContext), nullptr, this); - m_bpButtonSyncConfig->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler (MainDialog::OnSyncSettingsContext), nullptr, this); - m_bpButtonFilter ->Connect(wxEVT_RIGHT_DOWN, wxCommandEventHandler(MainDialog::OnGlobalFilterContext), nullptr, this); - //sort grids - m_gridMainL->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickL ), nullptr, this ); - m_gridMainC->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickC ), nullptr, this ); - m_gridMainR->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickR ), nullptr, this ); + m_gridMainL->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickL ), nullptr, this); + m_gridMainC->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickC ), nullptr, this); + m_gridMainR->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickR ), nullptr, this); - m_gridMainL->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextL ), nullptr, this ); - m_gridMainC->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextC ), nullptr, this ); - m_gridMainR->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextR ), nullptr, this ); + m_gridMainL->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextL ), nullptr, this); + m_gridMainC->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextC ), nullptr, this); + m_gridMainR->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextR ), nullptr, this); //grid context menu m_gridMainL->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onMainGridContextL), nullptr, this); @@ -561,8 +544,14 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg, m_gridNavi->Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(MainDialog::onNaviSelection), nullptr, this); - gridDataView.reset(new zen::GridView); - treeDataView.reset(new zen::TreeView); + //set tool tips with (non-translated!) short cut hint + m_bpButtonOpen ->SetToolTip(_("Open...") + L" (Ctrl+O)"); + m_bpButtonSave ->SetToolTip(_("Save") + L" (Ctrl+S)"); + m_buttonCompare->SetToolTip(_("Compare both sides") + L" (F5)"); + m_buttonSync ->SetToolTip(_("Start synchronization") + L" (F6)"); + + gridDataView = std::make_shared(); + treeDataView = std::make_shared(); cleanedUp = false; processingGlobalKeyEvent = false; @@ -579,7 +568,7 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg, //init handling of first folder pair firstFolderPair.reset(new DirectoryPairFirst(*this)); - //initViewFilterButtons(); + initViewFilterButtons(); //init grid settings gridview::init(*m_gridMainL, *m_gridMainC, *m_gridMainR, gridDataView); @@ -593,7 +582,7 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg, m_buttonCompare ->setBitmapFront(GlobalResources::getImage(L"compare"), 5); m_bpButtonSyncConfig->SetBitmapLabel(GlobalResources::getImage(L"syncConfig")); m_bpButtonCmpConfig ->SetBitmapLabel(GlobalResources::getImage(L"cmpConfig")); - m_bpButtonLoad ->SetBitmapLabel(GlobalResources::getImage(L"load")); + m_bpButtonOpen ->SetBitmapLabel(GlobalResources::getImage(L"load")); m_bpButtonBatchJob ->SetBitmapLabel(GlobalResources::getImage(L"batch")); m_bpButtonAddPair ->SetBitmapLabel(GlobalResources::getImage(L"item_add")); @@ -1060,29 +1049,28 @@ std::vector MainDialog::getTreeSelection() const //Exception class used to abort the "compare" and "sync" process class AbortDeleteProcess {}; -class ManualDeletionHandler : private wxEvtHandler, public DeleteFilesHandler +class ManualDeletionHandler : private wxEvtHandler, public DeleteFilesHandler //throw AbortDeleteProcess { public: - ManualDeletionHandler(MainDialog* main) : + ManualDeletionHandler(MainDialog& main) : mainDlg(main), abortRequested(false), - ignoreErrors(false), - deletionCount(0) + ignoreErrors(false) { - mainDlg->disableAllElements(true); //disable everything except abort button + mainDlg.disableAllElements(true); //disable everything except abort button //register abort button - mainDlg->m_buttonAbort->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ManualDeletionHandler::OnAbortDeletion), nullptr, this ); - mainDlg->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(ManualDeletionHandler::OnKeyPressed), nullptr, this); + mainDlg.m_buttonAbort->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ManualDeletionHandler::OnAbortDeletion), nullptr, this ); + mainDlg.Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(ManualDeletionHandler::OnKeyPressed), nullptr, this); } ~ManualDeletionHandler() { //de-register abort button - mainDlg->Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(ManualDeletionHandler::OnKeyPressed), nullptr, this); - mainDlg->m_buttonAbort->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ManualDeletionHandler::OnAbortDeletion ), nullptr, this ); + mainDlg.Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(ManualDeletionHandler::OnKeyPressed), nullptr, this); + mainDlg.m_buttonAbort->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ManualDeletionHandler::OnAbortDeletion ), nullptr, this ); - mainDlg->enableAllElements(); + mainDlg.enableAllElements(); } virtual Response reportError(const std::wstring& msg) @@ -1090,9 +1078,9 @@ public: if (ignoreErrors) return DeleteFilesHandler::IGNORE_ERROR; - updateGUI(); + forceUiRefresh(); bool ignoreNextErrors = false; - switch (showErrorDlg(mainDlg, + switch (showErrorDlg(&mainDlg, ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_RETRY | ReturnErrorDlg::BUTTON_CANCEL, msg, &ignoreNextErrors)) { @@ -1114,9 +1102,9 @@ public: if (!warningActive || ignoreErrors) return; - updateGUI(); + forceUiRefresh(); bool dontWarnAgain = false; - switch (showWarningDlg(mainDlg, ReturnWarningDlg::BUTTON_IGNORE | ReturnWarningDlg::BUTTON_CANCEL, msg, dontWarnAgain)) + switch (showWarningDlg(&mainDlg, ReturnWarningDlg::BUTTON_IGNORE | ReturnWarningDlg::BUTTON_CANCEL, msg, dontWarnAgain)) { case ReturnWarningDlg::BUTTON_SWITCH: assert(false); @@ -1129,26 +1117,29 @@ public: } } - virtual void notifyDeletion(const Zstring& currentObject) //called for each file/folder that has been deleted + virtual void reportStatus (const std::wstring& msg) { - ++deletionCount; - updateGUI(); + statusMsg = msg; + requestUiRefresh(); } private: - void updateGUI() + virtual void requestUiRefresh() { if (updateUiIsAllowed()) //test if specific time span between ui updates is over - { - mainDlg->setStatusInformation(replaceCpy(_P("Object deleted successfully!", "%x objects deleted successfully!", deletionCount), - L"%x", zen::toGuiString(deletionCount), false)); - updateUiNow(); - } + forceUiRefresh(); if (abortRequested) //test after (implicit) call to wxApp::Yield() throw AbortDeleteProcess(); } + void forceUiRefresh() + { + //std::wstring msg = toGuiString(deletionCount) + + mainDlg.setStatusBarFullText(statusMsg); + updateUiNow(); + } + //context: C callstack message loop => throw()! void OnAbortDeletion(wxCommandEvent& event) //handle abort button click { @@ -1167,11 +1158,12 @@ private: event.Skip(); } - MainDialog* const mainDlg; + MainDialog& mainDlg; bool abortRequested; bool ignoreErrors; - size_t deletionCount; + //size_t deletionCount; // + std::wstring statusMsg; //status reporting }; @@ -1180,184 +1172,223 @@ void MainDialog::deleteSelectedFiles(const std::vector& selec { if (!selectionLeft.empty() || !selectionRight.empty()) { + wxBusyCursor dummy; //show hourglass cursor + wxWindow* oldFocus = wxWindow::FindFocus(); ZEN_ON_SCOPE_EXIT(if (oldFocus) oldFocus->SetFocus();) - if (zen::showDeleteDialog(this, - selectionLeft, - selectionRight, - globalCfg.gui.deleteOnBothSides, - globalCfg.gui.useRecyclerForManualDeletion) == ReturnSmallDlg::BUTTON_OKAY) + bool deleteOnBothSides = false; //let's keep this disabled by default -> don't save + + if (zen::showDeleteDialog(this, + selectionLeft, + selectionRight, + deleteOnBothSides, + globalCfg.gui.useRecyclerForManualDeletion) == ReturnSmallDlg::BUTTON_OKAY) + { + try { - try - { - //handle errors when deleting files/folders - ManualDeletionHandler statusHandler(this); - - zen::deleteFromGridAndHD(selectionLeft, - selectionRight, - folderCmp, - extractDirectionCfg(getConfig().mainCfg), - globalCfg.gui.deleteOnBothSides, - globalCfg.gui.useRecyclerForManualDeletion, - statusHandler, - globalCfg.optDialogs.warningRecyclerMissing); - - gridview::clearSelection(*m_gridMainL, *m_gridMainC, *m_gridMainR); //do not clear, if aborted! - } - catch (AbortDeleteProcess&) {} + //handle errors when deleting files/folders + ManualDeletionHandler statusHandler(*this); + + zen::deleteFromGridAndHD(selectionLeft, + selectionRight, + folderCmp, + extractDirectionCfg(getConfig().mainCfg), + deleteOnBothSides, + globalCfg.gui.useRecyclerForManualDeletion, + statusHandler, + globalCfg.optDialogs.warningRecyclerMissing); + + gridview::clearSelection(*m_gridMainL, *m_gridMainC, *m_gridMainR); //do not clear, if aborted! + } + catch (AbortDeleteProcess&) {} - //remove rows that are empty: just a beautification, invalid rows shouldn't cause issues - gridDataView->removeInvalidRows(); + //remove rows that are empty: just a beautification, invalid rows shouldn't cause issues + gridDataView->removeInvalidRows(); - //redraw grid neccessary to update new dimensions and for UI-Backend data linkage - updateGui(); //call immediately after deleteFromGridAndHD!!! - } + //redraw grid neccessary to update new dimensions and for UI-Backend data linkage + updateGui(); //call immediately after deleteFromGridAndHD!!! + } } } - +namespace +{ template -wxString extractLastValidDir(const FileSystemObject& fsObj) +Zstring getExistingParentFolder(const FileSystemObject& fsObj) { - Zstring fullname = fsObj.getBaseDirPf() + fsObj.getObjRelativeName(); //full name even if FileSystemObject::isEmpty() == true + const DirMapping* dirObj = dynamic_cast(&fsObj); + if (!dirObj) + dirObj = dynamic_cast(&fsObj.parent()); - while (!fullname.empty() && !dirExists(fullname)) //bad algorithm: this one should better retrieve the status from fsObj - fullname = beforeLast(fullname, FILE_NAME_SEPARATOR); + while (dirObj) + { + if (!dirObj->isEmpty()) + return dirObj->getFullName(); - return toWx(fullname); + dirObj = dynamic_cast(&dirObj->parent()); + } + return fsObj.getBaseDirPf(); +} } - -void MainDialog::openExternalApplication(const wxString& commandline, const zen::FileSystemObject* fsObj, bool leftSide) //fsObj may be nullptr +void MainDialog::openExternalApplication(const wxString& commandline, const std::vector& selection, bool leftSide) { if (commandline.empty()) return; - wxString name; - wxString nameCo; - wxString dir; - wxString dirCo; + auto selectionTmp = selection; - if (fsObj) + const bool openFileBrowserRequested = [&]() -> bool { - name = toWx(fsObj->getFullName()); //empty if obj not existing - dir = toWx(beforeLast(fsObj->getFullName(), FILE_NAME_SEPARATOR)); //small issue: if obj does not exist but parent exists, this one erronously returns empty + xmlAccess::XmlGlobalSettings::Gui dummy; + return !dummy.externelApplications.empty() && dummy.externelApplications[0].second == commandline; + }(); - nameCo = toWx(fsObj->getFullName()); - dirCo = toWx(beforeLast(fsObj->getFullName(), FILE_NAME_SEPARATOR)); + //support fallback instead of an error in this special case + if (openFileBrowserRequested) + { + if (selectionTmp.size() > 1) //do not open more than one explorer instance! + selectionTmp.resize(1); // + + if (selectionTmp.empty() || + (leftSide && selectionTmp[0]->isEmpty()) || + (!leftSide && selectionTmp[0]->isEmpty())) + { + Zstring fallbackDir; + if (selectionTmp.empty()) + fallbackDir = leftSide ? + getFormattedDirectoryName(toZ(firstFolderPair->getLeftDir())) : + getFormattedDirectoryName(toZ(firstFolderPair->getRightDir())); + + else + fallbackDir = leftSide ? + getExistingParentFolder(*selectionTmp[0]) : + getExistingParentFolder(*selectionTmp[0]); +#ifdef FFS_WIN + zen::shellExecute(L"\"" + fallbackDir + L"\""); +#elif defined FFS_LINUX + zen::shellExecute("xdg-open \"" + fallbackDir + "\""); +#endif + return; + } } - if (!leftSide) + //regular command evaluation + for (auto it = selectionTmp.begin(); it != selectionTmp.end(); ++it) //context menu calls this function only if selection is not empty! { - std::swap(name, nameCo); - std::swap(dir, dirCo); - } + const FileSystemObject* fsObj = *it; - wxString command = commandline; + Zstring path1 = fsObj->getBaseDirPf() + fsObj->getObjRelativeName(); //full path, even if item is not existing! + Zstring dir1 = beforeLast(path1, FILE_NAME_SEPARATOR); //Win: wrong for root paths like "C:\file.txt" - auto tryReplace = [&](wxString phrase, const wxString& replacement) -> bool - { - wxString cmdTmp = command.Upper(); //case insensitive search - phrase.MakeUpper(); // + Zstring path2 = fsObj->getBaseDirPf() + fsObj->getObjRelativeName(); + Zstring dir2 = beforeLast(path2, FILE_NAME_SEPARATOR); - size_t pos = cmdTmp.find(phrase); - if (pos != wxString::npos) + if (!leftSide) { - command.replace(pos, phrase.size(), replacement); - if (replacement.empty()) - return false; + std::swap(path1, path2); + std::swap(dir1, dir2); } - return true; - }; - bool expandSuccess = - /**/ tryReplace(L"%item_path%" , name ); //prevent short-cut behavior! - expandSuccess = tryReplace(L"%item_folder%" , dir ) && expandSuccess; // - expandSuccess = tryReplace(L"%item2_path%" , nameCo) && expandSuccess; // - expandSuccess = tryReplace(L"%item2_folder%", dirCo ) && expandSuccess; // + Zstring command = utfCvrtTo(commandline); + replace(command, Zstr("%item_path%"), path1); + replace(command, Zstr("%item2_path%"), path2); + replace(command, Zstr("%item_folder%"), dir1 ); + replace(command, Zstr("%item2_folder%"), dir2 ); - const bool openFileBrowser = [&]() -> bool - { - xmlAccess::XmlGlobalSettings::Gui dummy; - return !dummy.externelApplications.empty() && dummy.externelApplications[0].second == commandline; - }(); + auto cmdExp = expandMacros(command); + zen::shellExecute(cmdExp); //shows error message if command is malformed + } +} - if (!openFileBrowser || expandSuccess) + +void MainDialog::setStatusBarFileStatistics(size_t filesOnLeftView, + size_t foldersOnLeftView, + size_t filesOnRightView, + size_t foldersOnRightView, + UInt64 filesizeLeftView, + UInt64 filesizeRightView) +{ + wxWindowUpdateLocker dummy(m_panelStatusBar); //avoid display distortion + + //select state + bSizerFileStatus->Show(true); + m_staticTextFullStatus->Hide(); + + //fill statistics + //update status information + bSizerStatusLeftDirectories->Show(foldersOnLeftView > 0); + bSizerStatusLeftFiles ->Show(filesOnLeftView > 0); + + setText(*m_staticTextStatusLeftDirs, replaceCpy(_P("1 directory", "%x directories", foldersOnLeftView), L"%x", toGuiString(foldersOnLeftView), false)); + setText(*m_staticTextStatusLeftFiles, replaceCpy(_P("1 file", "%x files", filesOnLeftView), L"%x", toGuiString(filesOnLeftView), false)); + setText(*m_staticTextStatusLeftBytes, filesizeToShortString(to(filesizeLeftView))); + + wxString statusMiddleNew; + if (gridDataView->rowsTotal() > 0) { - auto cmdExp = utfCvrtTo(expandMacros(utfCvrtTo(command))); - zen::shellExecute(cmdExp); //just execute, show error message if command is malformed + 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); } - else //failed to expand file browser command: support built-in fallback instead of an error! - { - wxString fallbackDir; - if (fsObj) - fallbackDir = leftSide ? - extractLastValidDir(*fsObj) : - extractLastValidDir(*fsObj); - if (fallbackDir.empty()) - fallbackDir = leftSide ? - toWx(zen::getFormattedDirectoryName(toZ(firstFolderPair->getLeftDir()))) : - toWx(zen::getFormattedDirectoryName(toZ(firstFolderPair->getRightDir()))); + bSizerStatusRightDirectories->Show(foldersOnRightView > 0); + bSizerStatusRightFiles ->Show(filesOnRightView > 0); -#ifdef FFS_WIN - zen::shellExecute(wxString(L"\"") + fallbackDir + L"\""); -#elif defined FFS_LINUX - zen::shellExecute(wxString(L"xdg-open \"") + fallbackDir + L"\""); -#endif - } + setText(*m_staticTextStatusRightDirs, replaceCpy(_P("1 directory", "%x directories", foldersOnRightView), L"%x", toGuiString(foldersOnRightView), false)); + setText(*m_staticTextStatusRightFiles, replaceCpy(_P("1 file", "%x files", filesOnRightView), L"%x", toGuiString(filesOnRightView), false)); + setText(*m_staticTextStatusRightBytes, filesizeToShortString(to(filesizeRightView))); + + + //fill middle text (considering flashStatusInformation()) + if (!oldStatusMsg) + setText(*m_staticTextStatusMiddle, statusMiddleNew); + else + *oldStatusMsg = statusMiddleNew; + + m_panelStatusBar->Layout(); } -void MainDialog::setStatusInformation(const wxString& msg) +void MainDialog::setStatusBarFullText(const wxString& msg) { - if (statusMsgStack.empty()) - { - if (m_staticTextStatusMiddle->GetLabel() != msg) - { - m_staticTextStatusMiddle->SetLabel(msg); - m_panelStatusBar->Layout(); - } - } - else - statusMsgStack[0] = msg; //statusMsgStack, index 0 is main status, while 1, 2, ... are temporary status texts in reverse order of screen appearance + //select state + bSizerFileStatus->Show(false); + m_staticTextFullStatus->Show(); + + //update status information + setText(*m_staticTextFullStatus, msg); + m_panelStatusBar->Layout(); } void MainDialog::flashStatusInformation(const wxString& text) { - if (statusMsgStack.empty()) - { - statusMsgStack.push_back(m_staticTextStatusMiddle->GetLabel()); + if (!oldStatusMsg) + oldStatusMsg = make_unique(m_staticTextStatusMiddle->GetLabel()); - lastStatusChange = wxGetLocalTimeMillis(); - m_staticTextStatusMiddle->SetLabel(text); - m_staticTextStatusMiddle->SetForegroundColour(wxColour(31, 57, 226)); //highlight color: blue - m_panelStatusBar->Layout(); - } - else - statusMsgStack.insert(statusMsgStack.begin() + 1, text); + lastStatusChange = wxGetLocalTimeMillis(); + m_staticTextStatusMiddle->SetLabel(text); + m_staticTextStatusMiddle->SetForegroundColour(wxColour(31, 57, 226)); //highlight color: blue + m_panelStatusBar->Layout(); } void MainDialog::OnIdleEvent(wxEvent& event) { //small routine to restore status information after some time - if (!statusMsgStack.empty()) //check if there is some work to do + if (oldStatusMsg) //check if there is some work to do { wxMilliClock_t currentTime = wxGetLocalTimeMillis(); - if (currentTime - lastStatusChange > 2500) //restore stackObject after two seconds + if (numeric::dist(currentTime, lastStatusChange) > 2500) //restore stackObject after two seconds { lastStatusChange = currentTime; - m_staticTextStatusMiddle->SetLabel(statusMsgStack.back()); - statusMsgStack.pop_back(); - - if (statusMsgStack.empty()) - m_staticTextStatusMiddle->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); //reset color - + m_staticTextStatusMiddle->SetLabel(*oldStatusMsg); + m_staticTextStatusMiddle->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); //reset color m_panelStatusBar->Layout(); + oldStatusMsg.reset(); } } @@ -1367,7 +1398,7 @@ void MainDialog::OnIdleEvent(wxEvent& event) void MainDialog::disableAllElements(bool enableAbort) { - //when changing consider: comparison, synchronization, manual deletion + //when changing consider: comparison, synchronization, manual deletion EnableCloseButton(false); //not allowed for synchronization! progress indicator is top window! @@ -1377,13 +1408,14 @@ void MainDialog::disableAllElements(bool enableAbort) m_panelFilter ->Disable(); m_panelConfig ->Disable(); m_bpButtonSyncConfig ->Disable(); - m_buttonStartSync ->Disable(); + m_buttonSync ->Disable(); m_gridMainL ->Disable(); m_gridMainC ->Disable(); m_gridMainR ->Disable(); m_panelStatistics ->Disable(); m_gridNavi ->Disable(); m_panelDirectoryPairs->Disable(); + m_splitterMain ->Disable(); m_menubar1->EnableTop(0, false); m_menubar1->EnableTop(1, false); m_menubar1->EnableTop(2, false); @@ -1415,13 +1447,14 @@ void MainDialog::enableAllElements() m_panelFilter ->Enable(); m_panelConfig ->Enable(); m_bpButtonSyncConfig ->Enable(); - m_buttonStartSync ->Enable(); + m_buttonSync ->Enable(); m_gridMainL ->Enable(); m_gridMainC ->Enable(); m_gridMainR ->Enable(); m_panelStatistics ->Enable(); m_gridNavi ->Enable(); m_panelDirectoryPairs->Enable(); + m_splitterMain ->Enable(); m_menubar1->EnableTop(0, true); m_menubar1->EnableTop(1, true); m_menubar1->EnableTop(2, true); @@ -1643,12 +1676,8 @@ void MainDialog::onGridButtonEvent(wxKeyEvent& event, Grid& grid, bool leftSide) case WXK_RETURN: case WXK_NUMPAD_ENTER: if (!globalCfg.gui.externelApplications.empty()) - { - const wxString commandline = globalCfg.gui.externelApplications[0].second; //open with first external application - auto cursorPos = grid.getGridCursor(); - const size_t row = cursorPos.first; - openExternalApplication(commandline, gridDataView->getObject(row), leftSide); - } + openExternalApplication(globalCfg.gui.externelApplications[0].second, //open with first external application + getGridSelection(), leftSide); return; } @@ -1832,7 +1861,7 @@ void MainDialog::onNaviGridContext(GridClickEvent& event) std::swap(shortCutLeft, shortCutRight); menu.addItem(_("Set direction:") + L" ->" + shortCutRight, [this, &selection] { setSyncDirManually(selection, SYNC_DIR_RIGHT); }, &opRight); - menu.addItem(_("Set direction:") + L" -" L"\tAlt+Up", [this, &selection] { setSyncDirManually(selection, SYNC_DIR_NONE); }, &opNone); + menu.addItem(_("Set direction:") + L" -" L"\tAlt+Down", [this, &selection] { setSyncDirManually(selection, SYNC_DIR_NONE); }, &opNone); menu.addItem(_("Set direction:") + L" <-" + shortCutLeft, [this, &selection] { setSyncDirManually(selection, SYNC_DIR_LEFT); }, &opLeft); //Gtk needs a direction, "<-", because it has no context menu icons! //Gtk requires "no spaces" for shortcut identifiers! @@ -1867,7 +1896,7 @@ void MainDialog::onNaviGridContext(GridClickEvent& event) //---------------------------------------------------------------------------------------------------- //CONTEXT_DELETE_FILES menu.addSeparator(); - menu.addItem(_("Delete") + L"\tDel", [&] { deleteSelectedFiles(selection, selection); }, nullptr, !selection.empty()); + menu.addItem(_("Delete") + L"\tDelete", [&] { deleteSelectedFiles(selection, selection); }, nullptr, !selection.empty()); menu.popup(*this); } @@ -1924,7 +1953,7 @@ void MainDialog::onMainGridContextRim(bool leftSide) std::swap(shortCutLeft, shortCutRight); menu.addItem(_("Set direction:") + L" ->" + shortCutRight, [this, &selection] { setSyncDirManually(selection, SYNC_DIR_RIGHT); }, &opRight); - menu.addItem(_("Set direction:") + L" -" L"\tAlt+Up", [this, &selection] { setSyncDirManually(selection, SYNC_DIR_NONE); }, &opNone); + menu.addItem(_("Set direction:") + L" -" L"\tAlt+Down", [this, &selection] { setSyncDirManually(selection, SYNC_DIR_NONE); }, &opNone); menu.addItem(_("Set direction:") + L" <-" + shortCutLeft, [this, &selection] { setSyncDirManually(selection, SYNC_DIR_LEFT); }, &opLeft); //Gtk needs a direction, "<-", because it has no context menu icons! //Gtk requires "no spaces" for shortcut identifiers! @@ -1987,26 +2016,26 @@ void MainDialog::onMainGridContextRim(bool leftSide) it != globalCfg.gui.externelApplications.end(); ++it) { - //some trick to translate default external apps on the fly: 1. "open in explorer" 2. "start directly" + //translate default external apps on the fly: 1. "open in explorer" 2. "start directly" wxString description = zen::implementation::translate(it->first); if (description.empty()) description = L" "; //wxWidgets doesn't like empty items - const wxString command = it->second; + const wxString command = it->second; //COPY into lambda - auto openApp = [this, &selection, leftSide, command] { openExternalApplication(command, selection.empty() ? nullptr : selection[0], leftSide); }; + auto openApp = [this, &selection, leftSide, command] { openExternalApplication(command, selection, leftSide); }; if (it == globalCfg.gui.externelApplications.begin()) - menu.addItem(description + L"\tEnter", openApp); - else - menu.addItem(description, openApp, nullptr, !selection.empty()); + description += L"\tEnter"; + + menu.addItem(description, openApp, nullptr, !selection.empty()); } } //---------------------------------------------------------------------------------------------------- //CONTEXT_DELETE_FILES menu.addSeparator(); - menu.addItem(_("Delete") + L"\tDel", [this] + menu.addItem(_("Delete") + L"\tDelete", [this] { deleteSelectedFiles( getGridSelection(true, false), @@ -2724,38 +2753,48 @@ bool MainDialog::loadConfiguration(const std::vector& filenames) } } - -void MainDialog::OnCfgHistoryKeyEvent(wxKeyEvent& event) +void MainDialog::deleteSelectedCfgHistoryItems() { - const int keyCode = event.GetKeyCode(); - if (keyCode == WXK_DELETE || keyCode == WXK_NUMPAD_DELETE) + wxArrayInt tmp; + m_listBoxHistory->GetSelections(tmp); + + std::set selections(tmp.begin(), tmp.end()); //sort ascending! + + int shift = 0; + std::for_each(selections.begin(), selections.end(), + [&](int pos) + { + m_listBoxHistory->Delete(pos + shift); + --shift; + }); + + //set active selection on next element to allow "batch-deletion" by holding down DEL key + if (!selections.empty() && m_listBoxHistory->GetCount() > 0) { - //delete currently selected config history items - wxArrayInt tmp; - m_listBoxHistory->GetSelections(tmp); + int newSelection = *selections.begin(); + if (newSelection >= static_cast(m_listBoxHistory->GetCount())) + newSelection = m_listBoxHistory->GetCount() - 1; + m_listBoxHistory->SetSelection(newSelection); + } +} - std::set selections(tmp.begin(), tmp.end()); //sort ascending! - int shift = 0; - std::for_each(selections.begin(), selections.end(), - [&](int pos) - { - m_listBoxHistory->Delete(pos + shift); - --shift; - }); +void MainDialog::OnCfgHistoryRightClick(wxMouseEvent& event) +{ + ContextMenu menu; + menu.addItem(_("Delete"), [this] { deleteSelectedCfgHistoryItems(); }); + menu.popup(*this); +} - //set active selection on next element to allow "batch-deletion" by holding down DEL key - if (!selections.empty() && m_listBoxHistory->GetCount() > 0) - { - int newSelection = *selections.begin(); - if (newSelection >= static_cast(m_listBoxHistory->GetCount())) - newSelection = m_listBoxHistory->GetCount() - 1; - m_listBoxHistory->SetSelection(newSelection); - } +void MainDialog::OnCfgHistoryKeyEvent(wxKeyEvent& event) +{ + const int keyCode = event.GetKeyCode(); + if (keyCode == WXK_DELETE || keyCode == WXK_NUMPAD_DELETE) + { + deleteSelectedCfgHistoryItems(); return; //"swallow" event } - event.Skip(); } @@ -2840,7 +2879,7 @@ void MainDialog::setConfig(const xmlAccess::XmlGuiConfig& newGuiCfg, const std:: //evaluate new settings... //(re-)set view filter buttons - initViewFilterButtons(newGuiCfg.mainCfg); + setViewFilterDefault(); updateFilterButtons(); @@ -2966,7 +3005,7 @@ void MainDialog::OnConfigureFilter(wxCommandEvent& event) } -void MainDialog::OnGlobalFilterContext(wxCommandEvent& event) +void MainDialog::OnGlobalFilterContext(wxMouseEvent& event) { ContextMenu menu; @@ -2983,101 +3022,15 @@ void MainDialog::OnGlobalFilterContext(wxCommandEvent& event) } -void MainDialog::OnLeftOnlyFiles(wxCommandEvent& event) -{ - m_bpButtonLeftOnly->toggle(); - updateGui(); -} - - -void MainDialog::OnLeftNewerFiles(wxCommandEvent& event) -{ - m_bpButtonLeftNewer->toggle(); - updateGui(); -} - - -void MainDialog::OnDifferentFiles(wxCommandEvent& event) -{ - m_bpButtonDifferent->toggle(); - updateGui(); -} - - -void MainDialog::OnRightNewerFiles(wxCommandEvent& event) -{ - m_bpButtonRightNewer->toggle(); - updateGui(); -} - - -void MainDialog::OnRightOnlyFiles(wxCommandEvent& event) -{ - m_bpButtonRightOnly->toggle(); - updateGui(); -} - - -void MainDialog::OnEqualFiles(wxCommandEvent& event) -{ - m_bpButtonEqual->toggle(); - updateGui(); -} - - -void MainDialog::OnConflictFiles(wxCommandEvent& event) +void MainDialog::OnToggleViewButton(wxCommandEvent& event) { - m_bpButtonConflict->toggle(); - updateGui(); -} - - -void MainDialog::OnSyncCreateLeft(wxCommandEvent& event) -{ - m_bpButtonSyncCreateLeft->toggle(); - updateGui(); -} - - -void MainDialog::OnSyncCreateRight(wxCommandEvent& event) -{ - m_bpButtonSyncCreateRight->toggle(); - updateGui(); -} - - -void MainDialog::OnSyncDeleteLeft(wxCommandEvent& event) -{ - m_bpButtonSyncDeleteLeft->toggle(); - updateGui(); -} - - -void MainDialog::OnSyncDeleteRight(wxCommandEvent& event) -{ - m_bpButtonSyncDeleteRight->toggle(); - updateGui(); -} - - -void MainDialog::OnSyncDirLeft(wxCommandEvent& event) -{ - m_bpButtonSyncDirOverwLeft->toggle(); - updateGui(); -} - - -void MainDialog::OnSyncDirRight(wxCommandEvent& event) -{ - m_bpButtonSyncDirOverwRight->toggle(); - updateGui(); -} - - -void MainDialog::OnSyncDirNone(wxCommandEvent& event) -{ - m_bpButtonSyncDirNone->toggle(); - updateGui(); + if (auto button = dynamic_cast(event.GetEventObject())) + { + button->toggle(); + updateGui(); + } + else + assert(false); } @@ -3102,135 +3055,136 @@ wxBitmap buttonReleased(const std::string& name) } -void MainDialog::initViewFilterButtons(const MainConfiguration& mainCfg) +void MainDialog::initViewFilterButtons() { //compare result buttons - m_bpButtonLeftOnly->init(buttonPressed("leftOnly"), - buttonReleased("leftOnly"), - _("Hide files that exist on left side only"), - _("Show files that exist on left side only")); - - m_bpButtonRightOnly->init(buttonPressed("rightOnly"), - buttonReleased("rightOnly"), - _("Hide files that exist on right side only"), - _("Show files that exist on right side only")); - - m_bpButtonLeftNewer->init(buttonPressed("leftNewer"), - buttonReleased("leftNewer"), - _("Hide files that are newer on left"), - _("Show files that are newer on left")); - - m_bpButtonRightNewer->init(buttonPressed("rightNewer"), - buttonReleased("rightNewer"), - _("Hide files that are newer on right"), - _("Show files that are newer on right")); - - m_bpButtonEqual->init(buttonPressed("equal"), - buttonReleased("equal"), - _("Hide files that are equal"), - _("Show files that are equal")); - - m_bpButtonDifferent->init(buttonPressed("different"), - buttonReleased("different"), - _("Hide files that are different"), - _("Show files that are different")); - - m_bpButtonConflict->init(buttonPressed("conflict"), - buttonReleased("conflict"), - _("Hide conflicts"), - _("Show conflicts")); + m_bpButtonShowLeftOnly->init(buttonPressed("leftOnly"), + buttonReleased("leftOnly"), + _("Hide files that exist on left side only"), + _("Show files that exist on left side only")); + + m_bpButtonShowRightOnly->init(buttonPressed("rightOnly"), + buttonReleased("rightOnly"), + _("Hide files that exist on right side only"), + _("Show files that exist on right side only")); + + m_bpButtonShowLeftNewer->init(buttonPressed("leftNewer"), + buttonReleased("leftNewer"), + _("Hide files that are newer on left"), + _("Show files that are newer on left")); + + m_bpButtonShowRightNewer->init(buttonPressed("rightNewer"), + buttonReleased("rightNewer"), + _("Hide files that are newer on right"), + _("Show files that are newer on right")); + + m_bpButtonShowEqual->init(buttonPressed("equal"), + buttonReleased("equal"), + _("Hide files that are equal"), + _("Show files that are equal")); + + m_bpButtonShowDifferent->init(buttonPressed("different"), + buttonReleased("different"), + _("Hide files that are different"), + _("Show files that are different")); + + m_bpButtonShowConflict->init(buttonPressed("conflict"), + buttonReleased("conflict"), + _("Hide conflicts"), + _("Show conflicts")); //sync preview buttons - m_bpButtonSyncCreateLeft->init(buttonPressed("createLeft"), + m_bpButtonShowCreateLeft->init(buttonPressed("createLeft"), buttonReleased("createLeft"), _("Hide files that will be created on the left side"), _("Show files that will be created on the left side")); - m_bpButtonSyncCreateRight->init(buttonPressed("createRight"), + m_bpButtonShowCreateRight->init(buttonPressed("createRight"), buttonReleased("createRight"), _("Hide files that will be created on the right side"), _("Show files that will be created on the right side")); - m_bpButtonSyncDeleteLeft->init(buttonPressed("deleteLeft"), + m_bpButtonShowDeleteLeft->init(buttonPressed("deleteLeft"), buttonReleased("deleteLeft"), _("Hide files that will be deleted on the left side"), _("Show files that will be deleted on the left side")); - m_bpButtonSyncDeleteRight->init(buttonPressed("deleteRight"), + m_bpButtonShowDeleteRight->init(buttonPressed("deleteRight"), buttonReleased("deleteRight"), _("Hide files that will be deleted on the right side"), _("Show files that will be deleted on the right side")); - m_bpButtonSyncDirOverwLeft->init(buttonPressed("updateLeft"), - buttonReleased("updateLeft"), - _("Hide files that will be overwritten on left side"), - _("Show files that will be overwritten on left side")); + m_bpButtonShowUpdateLeft->init(buttonPressed("updateLeft"), + buttonReleased("updateLeft"), + _("Hide files that will be overwritten on left side"), + _("Show files that will be overwritten on left side")); - m_bpButtonSyncDirOverwRight->init(buttonPressed("updateRight"), - buttonReleased("updateRight"), - _("Hide files that will be overwritten on right side"), - _("Show files that will be overwritten on right side")); + m_bpButtonShowUpdateRight->init(buttonPressed("updateRight"), + buttonReleased("updateRight"), + _("Hide files that will be overwritten on right side"), + _("Show files that will be overwritten on right side")); - m_bpButtonSyncDirNone->init(buttonPressed("none"), - buttonReleased("none"), - _("Hide files that won't be copied"), - _("Show files that won't be copied")); + m_bpButtonShowDoNothing->init(buttonPressed("none"), + buttonReleased("none"), + _("Hide files that won't be copied"), + _("Show files that won't be copied")); +} - //compare result buttons - m_bpButtonLeftOnly-> setActive(true); - m_bpButtonRightOnly-> setActive(true); - m_bpButtonLeftNewer-> setActive(true); - m_bpButtonRightNewer->setActive(true); - m_bpButtonDifferent-> setActive(true); - m_bpButtonConflict-> setActive(true); - //sync preview buttons - m_bpButtonSyncCreateLeft-> setActive(true); - m_bpButtonSyncCreateRight-> setActive(true); - m_bpButtonSyncDeleteLeft-> setActive(true); - m_bpButtonSyncDeleteRight-> setActive(true); - m_bpButtonSyncDirOverwLeft-> setActive(true); - m_bpButtonSyncDirOverwRight->setActive(true); +void MainDialog::setViewFilterDefault() +{ + auto setButton = [](ToggleButton* tb, bool value) { tb->setActive(value); }; - m_bpButtonEqual->setActive(false); + const auto& def = globalCfg.gui.viewFilterDefault; + setButton(m_bpButtonShowLeftOnly, def.leftOnly); + setButton(m_bpButtonShowRightOnly, def.rightOnly); + setButton(m_bpButtonShowLeftNewer, def.leftNewer); + setButton(m_bpButtonShowRightNewer, def.rightNewer); + setButton(m_bpButtonShowEqual, def.equal); + setButton(m_bpButtonShowDifferent, def.different); + setButton(m_bpButtonShowConflict, def.conflict); - //special case "m_bpButtonSyncDirNone": set always active, unless sync direction "none" is part of the rule set: - //e.g. for a "custom" config or "update" variant. Otherwise rows with sync direction "none" can only occur on grid if the user manually - //sets them, in which case these rows should not be hidden immediately, so m_bpButtonSyncDirNone must be active - const std::vector& cmpCfg = extractCompareCfg(mainCfg); - const bool syncDirNonePartOfConfig = std::any_of(cmpCfg.begin(), cmpCfg.end(), - [&](const FolderPairCfg& fpCfg) -> bool - { - //attention: following is quite an amount of implicit/redundant knowledge here... let's hope our model is fundamental enough to not change any time soon! - - if (fpCfg.directionCfg.var == DirectionConfig::AUTOMATIC) - return false; + setButton(m_bpButtonShowCreateLeft, def.createLeft); + setButton(m_bpButtonShowCreateRight,def.createRight); + setButton(m_bpButtonShowUpdateLeft, def.updateLeft); + setButton(m_bpButtonShowUpdateRight,def.updateRight); + setButton(m_bpButtonShowDeleteLeft, def.deleteLeft); + setButton(m_bpButtonShowDeleteRight,def.deleteRight); + setButton(m_bpButtonShowDoNothing, def.doNothing); +} - const DirectionSet dirSet = extractDirections(fpCfg.directionCfg); - switch (fpCfg.compareVar) - { - case CMP_BY_TIME_SIZE: - return dirSet.exLeftSideOnly == SYNC_DIR_NONE || - dirSet.exRightSideOnly == SYNC_DIR_NONE || - dirSet.leftNewer == SYNC_DIR_NONE || - dirSet.rightNewer == SYNC_DIR_NONE; - //dirSet.different == SYNC_DIR_NONE || - //dirSet.conflict == SYNC_DIR_NONE; +void MainDialog::OnViewButtonRightClick(wxMouseEvent& event) +{ + auto setButtonDefault = [](const ToggleButton* tb, bool& defaultValue) + { + if (tb->IsShown()) + defaultValue = tb->isActive(); + }; - case CMP_BY_CONTENT: - return dirSet.exLeftSideOnly == SYNC_DIR_NONE || - dirSet.exRightSideOnly == SYNC_DIR_NONE || - //dirSet.leftNewer == SYNC_DIR_NONE || - //dirSet.rightNewer == SYNC_DIR_NONE || - dirSet.different == SYNC_DIR_NONE; - //dirSet.conflict == SYNC_DIR_NONE; - } - assert(false); - return false; - }); + auto setDefault = [&] + { + auto& def = globalCfg.gui.viewFilterDefault; + setButtonDefault(m_bpButtonShowLeftOnly, def.leftOnly); + setButtonDefault(m_bpButtonShowRightOnly, def.rightOnly); + setButtonDefault(m_bpButtonShowLeftNewer, def.leftNewer); + setButtonDefault(m_bpButtonShowRightNewer, def.rightNewer); + setButtonDefault(m_bpButtonShowEqual, def.equal); + setButtonDefault(m_bpButtonShowDifferent, def.different); + setButtonDefault(m_bpButtonShowConflict, def.conflict); + + setButtonDefault(m_bpButtonShowCreateLeft, def.createLeft); + setButtonDefault(m_bpButtonShowCreateRight, def.createRight); + setButtonDefault(m_bpButtonShowUpdateLeft, def.updateLeft); + setButtonDefault(m_bpButtonShowUpdateRight, def.updateRight); + setButtonDefault(m_bpButtonShowDeleteLeft, def.deleteLeft); + setButtonDefault(m_bpButtonShowDeleteRight, def.deleteRight); + setButtonDefault(m_bpButtonShowDoNothing, def.doNothing); + }; - m_bpButtonSyncDirNone->setActive(!syncDirNonePartOfConfig); + ContextMenu menu; + menu.addItem( _("Set as default"), setDefault); + menu.popup(*this); } @@ -3316,7 +3270,7 @@ void MainDialog::OnCompare(wxCommandEvent& event) treeDataView->setData(folderCmp); // updateGui(); - // if (m_buttonStartSync->IsShownOnScreen()) m_buttonStartSync->SetFocus(); + // if (m_buttonSync->IsShownOnScreen()) m_buttonSync->SetFocus(); gridview::clearSelection(*m_gridMainL, *m_gridMainC, *m_gridMainR); m_gridNavi->clearSelection(); @@ -3353,13 +3307,13 @@ void MainDialog::updateGui() //update sync button enabled/disabled status if (!folderCmp.empty()) { - m_buttonStartSync->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT)); - m_buttonStartSync->setBitmapFront(GlobalResources::getImage(L"sync"), 5); + m_buttonSync->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT)); + m_buttonSync->setBitmapFront(GlobalResources::getImage(L"sync"), 5); } else { - m_buttonStartSync->SetForegroundColour(wxColor(128, 128, 128)); //Some colors seem to have problems with 16-bit desktop color, well this one hasn't! - m_buttonStartSync->setBitmapFront(greyScale(GlobalResources::getImage(L"sync")), 5); + m_buttonSync->SetForegroundColour(wxColor(128, 128, 128)); //Some colors seem to have problems with 16-bit desktop color, well this one hasn't! + m_buttonSync->setBitmapFront(greyScale(GlobalResources::getImage(L"sync")), 5); } auiMgr.Update(); //fix small display distortion, if view filter panel is empty @@ -3562,9 +3516,13 @@ void MainDialog::onGridDoubleClickR(GridClickEvent& event) void MainDialog::onGridDoubleClickRim(size_t row, bool leftSide) { if (!globalCfg.gui.externelApplications.empty()) - openExternalApplication(globalCfg.gui.externelApplications[0].second, - gridDataView->getObject(row), //optional - leftSide); + { + std::vector selection; + if (FileSystemObject* fsObj = gridDataView->getObject(row)) //selection must be a list of BOUND pointers! + selection.push_back(fsObj); + + openExternalApplication(globalCfg.gui.externelApplications[0].second, selection, leftSide); + } } @@ -3620,26 +3578,26 @@ void MainDialog::OnSwapSides(wxCommandEvent& event) } //swap view filter - bool tmp = m_bpButtonLeftOnly->isActive(); - m_bpButtonLeftOnly->setActive(m_bpButtonRightOnly->isActive()); - m_bpButtonRightOnly->setActive(tmp); + bool tmp = m_bpButtonShowLeftOnly->isActive(); + m_bpButtonShowLeftOnly->setActive(m_bpButtonShowRightOnly->isActive()); + m_bpButtonShowRightOnly->setActive(tmp); - tmp = m_bpButtonLeftNewer->isActive(); - m_bpButtonLeftNewer->setActive(m_bpButtonRightNewer->isActive()); - m_bpButtonRightNewer->setActive(tmp); + tmp = m_bpButtonShowLeftNewer->isActive(); + m_bpButtonShowLeftNewer->setActive(m_bpButtonShowRightNewer->isActive()); + m_bpButtonShowRightNewer->setActive(tmp); /* for sync preview and "mirror" variant swapping may create strange effect: - tmp = m_bpButtonSyncCreateLeft->isActive(); - m_bpButtonSyncCreateLeft->setActive(m_bpButtonSyncCreateRight->isActive()); - m_bpButtonSyncCreateRight->setActive(tmp); + tmp = m_bpButtonShowCreateLeft->isActive(); + m_bpButtonShowCreateLeft->setActive(m_bpButtonShowCreateRight->isActive()); + m_bpButtonShowCreateRight->setActive(tmp); - tmp = m_bpButtonSyncDeleteLeft->isActive(); - m_bpButtonSyncDeleteLeft->setActive(m_bpButtonSyncDeleteRight->isActive()); - m_bpButtonSyncDeleteRight->setActive(tmp); + tmp = m_bpButtonShowDeleteLeft->isActive(); + m_bpButtonShowDeleteLeft->setActive(m_bpButtonShowDeleteRight->isActive()); + m_bpButtonShowDeleteRight->setActive(tmp); - tmp = m_bpButtonSyncDirOverwLeft->isActive(); - m_bpButtonSyncDirOverwLeft->setActive(m_bpButtonSyncDirOverwRight->isActive()); - m_bpButtonSyncDirOverwRight->setActive(tmp); + tmp = m_bpButtonShowUpdateLeft->isActive(); + m_bpButtonShowUpdateLeft->setActive(m_bpButtonShowUpdateRight->isActive()); + m_bpButtonShowUpdateRight->setActive(tmp); */ //swap grid information @@ -3659,34 +3617,34 @@ void MainDialog::updateGridViewData() zen::UInt64 filesizeRightView; //disable all buttons per default - m_bpButtonLeftOnly-> Show(false); - m_bpButtonRightOnly-> Show(false); - m_bpButtonLeftNewer-> Show(false); - m_bpButtonRightNewer->Show(false); - m_bpButtonDifferent-> Show(false); - m_bpButtonEqual-> Show(false); - m_bpButtonConflict-> Show(false); - - m_bpButtonSyncCreateLeft-> Show(false); - m_bpButtonSyncCreateRight-> Show(false); - m_bpButtonSyncDeleteLeft-> Show(false); - m_bpButtonSyncDeleteRight-> Show(false); - m_bpButtonSyncDirOverwLeft-> Show(false); - m_bpButtonSyncDirOverwRight->Show(false); - m_bpButtonSyncDirNone-> Show(false); + m_bpButtonShowLeftOnly ->Show(false); + m_bpButtonShowRightOnly ->Show(false); + m_bpButtonShowLeftNewer ->Show(false); + m_bpButtonShowRightNewer->Show(false); + m_bpButtonShowDifferent ->Show(false); + m_bpButtonShowEqual ->Show(false); + m_bpButtonShowConflict ->Show(false); + + m_bpButtonShowCreateLeft ->Show(false); + m_bpButtonShowCreateRight->Show(false); + m_bpButtonShowDeleteLeft ->Show(false); + m_bpButtonShowDeleteRight->Show(false); + m_bpButtonShowUpdateLeft ->Show(false); + m_bpButtonShowUpdateRight->Show(false); + m_bpButtonShowDoNothing ->Show(false); if (showSyncAction_) { const GridView::StatusSyncPreview result = gridDataView->updateSyncPreview(currentCfg.hideExcludedItems, - m_bpButtonSyncCreateLeft-> isActive(), - m_bpButtonSyncCreateRight-> isActive(), - m_bpButtonSyncDeleteLeft-> isActive(), - m_bpButtonSyncDeleteRight-> isActive(), - m_bpButtonSyncDirOverwLeft-> isActive(), - m_bpButtonSyncDirOverwRight->isActive(), - m_bpButtonSyncDirNone-> isActive(), - m_bpButtonEqual-> isActive(), - m_bpButtonConflict-> isActive()); + m_bpButtonShowCreateLeft ->isActive(), + m_bpButtonShowCreateRight->isActive(), + m_bpButtonShowDeleteLeft ->isActive(), + m_bpButtonShowDeleteRight->isActive(), + m_bpButtonShowUpdateLeft ->isActive(), + m_bpButtonShowUpdateRight->isActive(), + m_bpButtonShowDoNothing ->isActive(), + m_bpButtonShowEqual ->isActive(), + m_bpButtonShowConflict ->isActive()); filesOnLeftView = result.filesOnLeftView; foldersOnLeftView = result.foldersOnLeftView; filesOnRightView = result.filesOnRightView; @@ -3696,25 +3654,25 @@ void MainDialog::updateGridViewData() //sync preview buttons - m_bpButtonSyncCreateLeft-> Show(result.existsSyncCreateLeft); - m_bpButtonSyncCreateRight-> Show(result.existsSyncCreateRight); - m_bpButtonSyncDeleteLeft-> Show(result.existsSyncDeleteLeft); - m_bpButtonSyncDeleteRight-> Show(result.existsSyncDeleteRight); - m_bpButtonSyncDirOverwLeft-> Show(result.existsSyncDirLeft); - m_bpButtonSyncDirOverwRight->Show(result.existsSyncDirRight); - m_bpButtonSyncDirNone-> Show(result.existsSyncDirNone); - m_bpButtonEqual-> Show(result.existsSyncEqual); - m_bpButtonConflict-> Show(result.existsConflict); - - if (m_bpButtonSyncCreateLeft-> IsShown() || - m_bpButtonSyncCreateRight-> IsShown() || - m_bpButtonSyncDeleteLeft-> IsShown() || - m_bpButtonSyncDeleteRight-> IsShown() || - m_bpButtonSyncDirOverwLeft-> IsShown() || - m_bpButtonSyncDirOverwRight->IsShown() || - m_bpButtonSyncDirNone-> IsShown() || - m_bpButtonEqual-> IsShown() || - m_bpButtonConflict-> IsShown()) + m_bpButtonShowCreateLeft ->Show(result.existsSyncCreateLeft); + m_bpButtonShowCreateRight ->Show(result.existsSyncCreateRight); + m_bpButtonShowDeleteLeft ->Show(result.existsSyncDeleteLeft); + m_bpButtonShowDeleteRight ->Show(result.existsSyncDeleteRight); + m_bpButtonShowUpdateLeft ->Show(result.existsSyncDirLeft); + m_bpButtonShowUpdateRight ->Show(result.existsSyncDirRight); + m_bpButtonShowDoNothing ->Show(result.existsSyncDirNone); + m_bpButtonShowEqual ->Show(result.existsSyncEqual); + m_bpButtonShowConflict ->Show(result.existsConflict); + + 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()) { m_panelViewFilter->Show(); m_panelViewFilter->Layout(); @@ -3726,13 +3684,13 @@ void MainDialog::updateGridViewData() else { const GridView::StatusCmpResult result = gridDataView->updateCmpResult(currentCfg.hideExcludedItems, - m_bpButtonLeftOnly-> isActive(), - m_bpButtonRightOnly-> isActive(), - m_bpButtonLeftNewer-> isActive(), - m_bpButtonRightNewer->isActive(), - m_bpButtonDifferent-> isActive(), - m_bpButtonEqual-> isActive(), - m_bpButtonConflict-> isActive()); + m_bpButtonShowLeftOnly ->isActive(), + m_bpButtonShowRightOnly ->isActive(), + m_bpButtonShowLeftNewer ->isActive(), + m_bpButtonShowRightNewer->isActive(), + m_bpButtonShowDifferent ->isActive(), + m_bpButtonShowEqual ->isActive(), + m_bpButtonShowConflict ->isActive()); filesOnLeftView = result.filesOnLeftView; foldersOnLeftView = result.foldersOnLeftView; filesOnRightView = result.filesOnRightView; @@ -3741,21 +3699,21 @@ void MainDialog::updateGridViewData() filesizeRightView = result.filesizeRightView; //comparison result view buttons - m_bpButtonLeftOnly-> Show(result.existsLeftOnly); - m_bpButtonRightOnly-> Show(result.existsRightOnly); - m_bpButtonLeftNewer-> Show(result.existsLeftNewer); - m_bpButtonRightNewer->Show(result.existsRightNewer); - m_bpButtonDifferent-> Show(result.existsDifferent); - m_bpButtonEqual-> Show(result.existsEqual); - m_bpButtonConflict-> Show(result.existsConflict); - - if (m_bpButtonLeftOnly-> IsShown() || - m_bpButtonRightOnly-> IsShown() || - m_bpButtonLeftNewer-> IsShown() || - m_bpButtonRightNewer->IsShown() || - m_bpButtonDifferent-> IsShown() || - m_bpButtonEqual-> IsShown() || - m_bpButtonConflict-> IsShown()) + m_bpButtonShowLeftOnly ->Show(result.existsLeftOnly); + m_bpButtonShowRightOnly ->Show(result.existsRightOnly); + m_bpButtonShowLeftNewer ->Show(result.existsLeftNewer); + m_bpButtonShowRightNewer->Show(result.existsRightNewer); + m_bpButtonShowDifferent ->Show(result.existsDifferent); + m_bpButtonShowEqual ->Show(result.existsEqual); + m_bpButtonShowConflict ->Show(result.existsConflict); + + if (m_bpButtonShowLeftOnly ->IsShown() || + m_bpButtonShowRightOnly ->IsShown() || + m_bpButtonShowLeftNewer ->IsShown() || + m_bpButtonShowRightNewer->IsShown() || + m_bpButtonShowDifferent ->IsShown() || + m_bpButtonShowEqual ->IsShown() || + m_bpButtonShowConflict ->IsShown()) { m_panelViewFilter->Show(); m_panelViewFilter->Layout(); @@ -3769,58 +3727,33 @@ void MainDialog::updateGridViewData() //navigation tree if (showSyncAction_) treeDataView->updateSyncPreview(currentCfg.hideExcludedItems, - m_bpButtonSyncCreateLeft-> isActive(), - m_bpButtonSyncCreateRight-> isActive(), - m_bpButtonSyncDeleteLeft-> isActive(), - m_bpButtonSyncDeleteRight-> isActive(), - m_bpButtonSyncDirOverwLeft-> isActive(), - m_bpButtonSyncDirOverwRight->isActive(), - m_bpButtonSyncDirNone-> isActive(), - m_bpButtonEqual-> isActive(), - m_bpButtonConflict-> isActive()); + m_bpButtonShowCreateLeft ->isActive(), + m_bpButtonShowCreateRight->isActive(), + m_bpButtonShowDeleteLeft ->isActive(), + m_bpButtonShowDeleteRight->isActive(), + m_bpButtonShowUpdateLeft ->isActive(), + m_bpButtonShowUpdateRight->isActive(), + m_bpButtonShowDoNothing ->isActive(), + m_bpButtonShowEqual ->isActive(), + m_bpButtonShowConflict ->isActive()); else treeDataView->updateCmpResult(currentCfg.hideExcludedItems, - m_bpButtonLeftOnly-> isActive(), - m_bpButtonRightOnly-> isActive(), - m_bpButtonLeftNewer-> isActive(), - m_bpButtonRightNewer->isActive(), - m_bpButtonDifferent-> isActive(), - m_bpButtonEqual-> isActive(), - m_bpButtonConflict-> isActive()); + m_bpButtonShowLeftOnly ->isActive(), + m_bpButtonShowRightOnly ->isActive(), + m_bpButtonShowLeftNewer ->isActive(), + m_bpButtonShowRightNewer->isActive(), + m_bpButtonShowDifferent ->isActive(), + m_bpButtonShowEqual ->isActive(), + m_bpButtonShowConflict ->isActive()); m_gridNavi->Refresh(); - - //status bar - wxWindowUpdateLocker dummy(m_panelStatusBar); //avoid display distortion - - //################################################# - //update status information - bSizerStatusLeftDirectories->Show(foldersOnLeftView > 0); - bSizerStatusLeftFiles ->Show(filesOnLeftView > 0); - - setText(*m_staticTextStatusLeftDirs, replaceCpy(_P("1 directory", "%x directories", foldersOnLeftView), L"%x", toGuiString(foldersOnLeftView), false)); - setText(*m_staticTextStatusLeftFiles, replaceCpy(_P("1 file", "%x files", filesOnLeftView), L"%x", toGuiString(filesOnLeftView), false)); - setText(*m_staticTextStatusLeftBytes, filesizeToShortString(to(filesizeLeftView))); - - { - 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); - } - setStatusInformation(statusMiddleNew); - } - - bSizerStatusRightDirectories->Show(foldersOnRightView > 0); - bSizerStatusRightFiles ->Show(filesOnRightView > 0); - - setText(*m_staticTextStatusRightDirs, replaceCpy(_P("1 directory", "%x directories", foldersOnRightView), L"%x", toGuiString(foldersOnRightView), false)); - setText(*m_staticTextStatusRightFiles, replaceCpy(_P("1 file", "%x files", filesOnRightView), L"%x", toGuiString(filesOnRightView), false)); - setText(*m_staticTextStatusRightBytes, filesizeToShortString(to(filesizeRightView))); - - m_panelStatusBar->Layout(); + //update status bar information + setStatusBarFileStatistics(filesOnLeftView, + foldersOnLeftView, + filesOnRightView, + foldersOnRightView, + filesizeLeftView, + filesizeRightView); } @@ -3898,10 +3831,10 @@ 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::const_iterator i = additionalFolderPairs.begin(); i != additionalFolderPairs.end(); ++i) - if (eventObj == (*i)->m_bpButtonRemovePair) + for (std::vector::const_iterator it = additionalFolderPairs.begin(); it != additionalFolderPairs.end(); ++it) + if (eventObj == (*it)->m_bpButtonRemovePair) { - removeAddFolderPair(i - additionalFolderPairs.begin()); + removeAddFolderPair(it - additionalFolderPairs.begin()); return; } } @@ -4007,10 +3940,10 @@ void MainDialog::addFolderPair(const std::vector& newPairs, bool for (auto it = newPairs.begin(); it != newPairs.end(); ++it)//set alternate configuration newEntries[it - newPairs.begin()]->setValues(toWx(it->leftDirectory), - toWx(it->rightDirectory), - it->altCmpConfig, - it->altSyncConfig, - it->localFilter); + toWx(it->rightDirectory), + it->altCmpConfig, + it->altSyncConfig, + it->localFilter); clearGrid(); //+ GUI update } @@ -4302,7 +4235,6 @@ void MainDialog::switchProgramLanguage(int langID) void MainDialog::OnMenuLanguageSwitch(wxCommandEvent& event) { std::map::const_iterator it = languageMenuItemMap.find(event.GetId()); - if (it != languageMenuItemMap.end()) switchProgramLanguage(it->second); } diff --git a/ui/main_dlg.h b/ui/main_dlg.h index 4f7a6bbd..b5492101 100644 --- a/ui/main_dlg.h +++ b/ui/main_dlg.h @@ -90,8 +90,10 @@ private: //used when saving configuration std::vector activeConfigFiles; //name of currently loaded config file (may be more than 1) - void initViewFilterButtons(const zen::MainConfiguration& mainCfg); - void updateFilterButtons(); + void updateFilterButtons(); //file exclusion + + void initViewFilterButtons(); + void setViewFilterDefault(); void addFileToCfgHistory(const std::vector& filenames); //= update/insert + apply selection @@ -119,14 +121,15 @@ private: void deleteSelectedFiles(const std::vector& selectionLeft, const std::vector& selectionRight); - void openExternalApplication(const wxString& commandline, const zen::FileSystemObject* fsObj, bool leftSide); //fsObj may be nullptr + void openExternalApplication(const wxString& commandline, const std::vector& selection, bool leftSide); //selection may be empty //work to be done in idle time void OnIdleEvent(wxEvent& event); - //delayed status information restore - void setStatusInformation(const wxString& msg); //set main status - void flashStatusInformation(const wxString& msg); //temporarily show different status + //status bar supports one of the following two states at a time: + void setStatusBarFullText(const wxString& msg); + void setStatusBarFileStatistics(size_t filesOnLeftView, size_t foldersOnLeftView, size_t filesOnRightView, size_t foldersOnRightView, zen::UInt64 filesizeLeftView, zen::UInt64 filesizeRightView); + void flashStatusInformation(const wxString& msg); //temporarily show different status (only valid for setStatusBarFullText) //events void onGridButtonEventL(wxKeyEvent& event); @@ -138,9 +141,10 @@ private: void OnContextSetLayout(wxMouseEvent& event); void OnGlobalKeyEvent (wxKeyEvent& event); - void OnCompSettingsContext(wxMouseEvent& event); - void OnSyncSettingsContext(wxMouseEvent& event); - void OnGlobalFilterContext(wxCommandEvent& event); + virtual void OnCompSettingsContext(wxMouseEvent& event); + virtual void OnSyncSettingsContext(wxMouseEvent& event); + virtual void OnGlobalFilterContext(wxMouseEvent& event); + virtual void OnViewButtonRightClick(wxMouseEvent& event); void applyCompareConfig(bool switchMiddleGrid = false); @@ -176,21 +180,7 @@ private: void onGridLabelContextR(zen::GridClickEvent& event); void onGridLabelContext(zen::Grid& grid, zen::ColumnTypeRim type, const std::vector& defaultColumnAttributes); - void OnLeftOnlyFiles (wxCommandEvent& event); - void OnRightOnlyFiles (wxCommandEvent& event); - void OnLeftNewerFiles (wxCommandEvent& event); - void OnRightNewerFiles(wxCommandEvent& event); - void OnEqualFiles (wxCommandEvent& event); - void OnDifferentFiles (wxCommandEvent& event); - void OnConflictFiles (wxCommandEvent& event); - - void OnSyncCreateLeft (wxCommandEvent& event); - void OnSyncCreateRight(wxCommandEvent& event); - void OnSyncDeleteLeft (wxCommandEvent& event); - void OnSyncDeleteRight(wxCommandEvent& event); - void OnSyncDirLeft (wxCommandEvent& event); - void OnSyncDirRight (wxCommandEvent& event); - void OnSyncDirNone (wxCommandEvent& event); + void OnToggleViewButton(wxCommandEvent& event); void OnConfigNew (wxCommandEvent& event); void OnConfigSave (wxCommandEvent& event); @@ -200,7 +190,10 @@ private: void OnLoadFromHistory(wxCommandEvent& event); void OnLoadFromHistoryDoubleClick(wxCommandEvent& event); + void deleteSelectedCfgHistoryItems(); + void OnCfgHistoryKeyEvent(wxKeyEvent& event); + void OnCfgHistoryRightClick(wxMouseEvent& event); void OnRegularUpdateCheck(wxIdleEvent& event); void OnLayoutWindowAsync (wxIdleEvent& event); @@ -270,7 +263,7 @@ private: //*********************************************** //status information wxLongLong lastStatusChange; - std::vector statusMsgStack; + std::unique_ptr oldStatusMsg; //compare status panel (hidden on start, shown when comparing) std::unique_ptr compareStatus; //always bound diff --git a/ui/progress_indicator.cpp b/ui/progress_indicator.cpp index 7fb04f93..13751541 100644 --- a/ui/progress_indicator.cpp +++ b/ui/progress_indicator.cpp @@ -123,7 +123,7 @@ void CompareStatus::CompareStatusImpl::finalize() void CompareStatus::CompareStatusImpl::switchToCompareBytewise() { //start to measure perf - perf.reset(new PerfCheck(WINDOW_REMAINING_TIME, WINDOW_BYTES_PER_SEC)); + perf = make_unique(WINDOW_REMAINING_TIME, WINDOW_BYTES_PER_SEC); lastStatCallSpeed = -1000000; //some big number lastStatCallRemTime = -1000000; @@ -720,9 +720,9 @@ namespace class GraphDataBytes : public GraphData { public: - void addRecord(double dataCurrent, long timeMs) + void addRecord(double currentBytes, long timeMs) { - data.insert(data.end(), std::make_pair(timeMs, dataCurrent)); + data.insert(data.end(), std::make_pair(timeMs, currentBytes)); //documentation differs about whether "hint" should be before or after the to be inserted element! //however "std::map<>::end()" is interpreted correctly by GCC and VS2010 @@ -868,8 +868,8 @@ public: ~SyncStatusImpl(); void initNewPhase(); - void reportCurrentBytes(Int64 currentData); - void updateProgress(bool allowYield = true); + void notifyProgressChange(); + void updateGui(bool allowYield = true); //call this in StatusUpdater derived class destructor at the LATEST(!) to prevent access to currentStatusUpdater void processHasFinished(SyncResult resultId, const ErrorLog& log); @@ -922,6 +922,8 @@ private: std::unique_ptr perf; long lastStatCallSpeed; //used for calculating intervals between collecting perf samples long lastStatCallRemTime; // + //help calculate total speed + long phaseStartMs; //begin of current phase in [ms] std::shared_ptr graphDataBytes; std::shared_ptr graphDataBytesTotal; @@ -953,7 +955,8 @@ SyncStatus::SyncStatusImpl::SyncStatusImpl(AbortCallback& abortCb, finalResult(RESULT_ABORTED), //dummy value isZombie(false), lastStatCallSpeed (-1000000), //some big number - lastStatCallRemTime(-1000000) + lastStatCallRemTime(-1000000), + phaseStartMs(0) { #ifdef FFS_WIN new MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" @@ -1002,13 +1005,13 @@ SyncStatus::SyncStatusImpl::SyncStatusImpl(AbortCallback& abortCb, graphDataBytesTotal = std::make_shared(); m_panelGraph->setAttributes(Graph2D::MainAttributes(). - setLabelX(Graph2D::X_LABEL_BOTTOM, 20, std::make_shared()). - setLabelY(Graph2D::Y_LABEL_RIGHT, 70, std::make_shared())); + setLabelX(Graph2D::X_LABEL_BOTTOM, 20, std::make_shared()). + setLabelY(Graph2D::Y_LABEL_RIGHT, 70, std::make_shared())); m_panelGraph->setData(graphDataBytes, - Graph2D::CurveAttributes().setLineWidth(2) - .setColor (wxColor( 0, 192, 0)) //medium green - .fillCurveArea(wxColor(192, 255, 192))); //faint green + Graph2D::CurveAttributes().setLineWidth(2) + .setColor (wxColor( 0, 192, 0)) //medium green + .fillCurveArea(wxColor(192, 255, 192))); //faint green m_panelGraph->addData(graphDataBytesTotal, Graph2D::CurveAttributes().setLineWidth(2).setColor(wxColor(0, 64, 0))); //dark green @@ -1066,25 +1069,44 @@ void SyncStatus::SyncStatusImpl::initNewPhase() //reset graph (e.g. after binary comparison) graphDataBytes->clear(); - reportCurrentBytes(0); + notifyProgressChange(); //start new measurement - perf.reset(new PerfCheck(WINDOW_REMAINING_TIME, WINDOW_BYTES_PER_SEC)); + perf = make_unique(WINDOW_REMAINING_TIME, WINDOW_BYTES_PER_SEC); lastStatCallSpeed = -1000000; //some big number lastStatCallRemTime = -1000000; + phaseStartMs = timeElapsed.Time(); + //set to 0 even if totalDataToProcess is 0: due to a bug in wxGauge::SetValue, it doesn't change to determinate mode when setting the old value again - //so give updateProgress() a chance to set a different value + //so give updateGui() a chance to set a different value m_gauge1->SetValue(0); - updateProgress(false); + updateGui(false); } -void SyncStatus::SyncStatusImpl::reportCurrentBytes(Int64 currentData) +void SyncStatus::SyncStatusImpl::notifyProgressChange() //noexcept! { - //add sample for perf measurements + calc. of remaining time - graphDataBytes->addRecord(to(currentData), timeElapsed.Time()); + if (syncStat_) + { + switch (syncStat_->currentPhase()) + { + case ProcessCallback::PHASE_NONE: + assert(false); + case ProcessCallback::PHASE_SCANNING: + break; + case ProcessCallback::PHASE_COMPARING_CONTENT: + case ProcessCallback::PHASE_SYNCHRONIZING: + { + const double currentData = to(syncStat_->getDataCurrent(syncStat_->currentPhase())); + + //add sample for perf measurements + calc. of remaining time + graphDataBytes->addRecord(currentData, timeElapsed.Time()); + } + break; + } + } } @@ -1180,7 +1202,7 @@ void SyncStatus::SyncStatusImpl::setExternalStatus(const wxString& status, const } -void SyncStatus::SyncStatusImpl::updateProgress(bool allowYield) +void SyncStatus::SyncStatusImpl::updateGui(bool allowYield) { assert(syncStat_); if (!syncStat_) //no sync running!! @@ -1318,7 +1340,7 @@ void SyncStatus::SyncStatusImpl::updateProgress(bool allowYield) while (paused_) { wxMilliSleep(UI_UPDATE_INTERVAL); - updateUiNow(); //receive UI message that ends pause + updateUiNow(); //receive UI message that end pause } } /* @@ -1454,7 +1476,7 @@ void SyncStatus::SyncStatusImpl::closeWindowDirectly() //this should really be c //------- change class state ------- abortCb_ = nullptr; //avoid callback to (maybe) deleted parent process - syncStat_ = nullptr; //set *after* last call to "updateProgress" + syncStat_ = nullptr; //set *after* last call to "updateGui" //---------------------------------- Close(); @@ -1471,7 +1493,8 @@ void SyncStatus::SyncStatusImpl::processHasFinished(SyncResult resultId, const E paused_ = false; //you never know? //update numbers one last time (as if sync were still running) - updateProgress(false); + notifyProgressChange(); //make one last graph entry at the *current* time + updateGui(false); switch (syncStat_->currentPhase()) //no matter if paused or not { @@ -1491,9 +1514,15 @@ void SyncStatus::SyncStatusImpl::processHasFinished(SyncResult resultId, const E assert(dataCurrent <= dataTotal); //set overall speed (instead of current speed) - assert(perf); - if (perf) - m_staticTextSpeed->SetLabel(perf->getOverallBytesPerSecond()); //note: we can't simply divide "sync total bytes" by "timeElapsed" + auto getOverallBytesPerSecond = [&]() -> wxString + { + const long timeDelta = timeElapsed.Time() - phaseStartMs; //we need to consider "time within current phase" not total "timeElapsed"! + if (timeDelta != 0) + return filesizeToShortString(dataCurrent * 1000 / timeDelta) + _("/sec"); + return L"-"; //fallback + }; + + m_staticTextSpeed->SetLabel(getOverallBytesPerSecond()); //show new element "items processed" m_staticTextLabelItemsProc->Show(true); @@ -1514,7 +1543,7 @@ void SyncStatus::SyncStatusImpl::processHasFinished(SyncResult resultId, const E //------- change class state ------- abortCb_ = nullptr; //avoid callback to (maybe) deleted parent process - syncStat_ = nullptr; //set *after* last call to "updateProgress" + syncStat_ = nullptr; //set *after* last call to "updateGui" finalResult = resultId; //---------------------------------- @@ -1665,7 +1694,7 @@ void SyncStatus::SyncStatusImpl::minimizeToTray() } if (syncStat_) - updateProgress(false); //set tray tooltip + progress: e.g. no updates while paused + updateGui(false); //set tray tooltip + progress: e.g. no updates while paused Hide(); if (mainDialog) @@ -1693,7 +1722,7 @@ void SyncStatus::SyncStatusImpl::resumeFromSystray() updateDialogStatus(); //restore Windows 7 task bar status (e.g. required in pause mode) if (syncStat_) - updateProgress(false); //restore Windows 7 task bar progress (e.g. required in pause mode) + updateGui(false); //restore Windows 7 task bar progress (e.g. required in pause mode) } @@ -1713,7 +1742,7 @@ SyncStatus::SyncStatus(AbortCallback& abortCb, if (showProgress) { pimpl->Show(); - pimpl->updateProgress(false); //clear gui flicker, remove dummy texts: window must be visible to make this work! + pimpl->updateGui(false); //clear gui flicker, remove dummy texts: window must be visible to make this work! } else pimpl->minimizeToTray(); @@ -1734,14 +1763,14 @@ void SyncStatus::initNewPhase() pimpl->initNewPhase(); } -void SyncStatus::reportCurrentBytes(Int64 currentData) +void SyncStatus::notifyProgressChange() { - pimpl->reportCurrentBytes(currentData); + pimpl->notifyProgressChange(); } -void SyncStatus::updateProgress() +void SyncStatus::updateGui() { - pimpl->updateProgress(); + pimpl->updateGui(); } std::wstring SyncStatus::getExecWhenFinishedCommand() const diff --git a/ui/progress_indicator.h b/ui/progress_indicator.h index b3d7ff2f..e4995824 100644 --- a/ui/progress_indicator.h +++ b/ui/progress_indicator.h @@ -49,8 +49,8 @@ public: void initNewPhase(); //call after "StatusHandler::initNewPhase" - void reportCurrentBytes(zen::Int64 currentData); //throw (), required by graph! - void updateProgress(); + void notifyProgressChange(); //throw (), required by graph! + void updateGui(); std::wstring getExecWhenFinishedCommand() const; //final value (after possible user modification) diff --git a/ui/sync_cfg.cpp b/ui/sync_cfg.cpp index dc82ffbb..3894eb24 100644 --- a/ui/sync_cfg.cpp +++ b/ui/sync_cfg.cpp @@ -229,8 +229,8 @@ SyncCfgDialog::SyncCfgDialog(wxWindow* parent, m_bitmapDatabase ->SetBitmap(GlobalResources::getImage(L"database")); enumVersioningStyle. - add(VER_STYLE_REPLACE, _("Replace"), _("Move files and replace if existing")). - add(VER_STYLE_ADD_TIMESTAMP, _("Versioning"), _("Append a timestamp to each file name")); + add(VER_STYLE_ADD_TIMESTAMP, _("Versioning"), _("Append a timestamp to each file name")). + add(VER_STYLE_REPLACE, _("Replace"), _("Move files and replace if existing")); //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 5a63e579..3025dbac 100644 --- a/ui/tree_view.cpp +++ b/ui/tree_view.cpp @@ -395,6 +395,12 @@ TreeView::NodeStatus TreeView::getStatus(size_t row) const void TreeView::expandNode(size_t row) { + if (getStatus(row) != TreeView::STATUS_REDUCED) + { + assert(false); + return; + } + if (row < flatTree.size()) { std::vector newLines; @@ -1005,6 +1011,7 @@ private: { case WXK_LEFT: case WXK_NUMPAD_LEFT: + case WXK_NUMPAD_SUBTRACT: //http://msdn.microsoft.com/en-us/library/ms971323.aspx#atg_keyboardshortcuts_windows_shortcut_keys if (treeDataView_) switch (treeDataView_->getStatus(row)) { @@ -1022,6 +1029,7 @@ private: case WXK_RIGHT: case WXK_NUMPAD_RIGHT: + case WXK_NUMPAD_ADD: if (treeDataView_) switch (treeDataView_->getStatus(row)) { diff --git a/ui/triple_splitter.cpp b/ui/triple_splitter.cpp index 8132fd13..f6ef006d 100644 --- a/ui/triple_splitter.cpp +++ b/ui/triple_splitter.cpp @@ -198,8 +198,12 @@ void TripleSplitter::onMouseMovement(wxMouseEvent& event) } else { - //we receive those only while above the sash, not the managed windows! - SetCursor(wxCURSOR_SIZEWE); //set window-local only! + //we receive those only while above the sash, not the managed windows (except when the managed windows are disabled!) + const int posX = event.GetPosition().x; + if (hitOnSashLine(posX)) + SetCursor(wxCURSOR_SIZEWE); //set window-local only! + else + SetCursor(*wxSTANDARD_CURSOR); } event.Skip(); } diff --git a/version/version.h b/version/version.h index 12a2a584..f5718e9d 100644 --- a/version/version.h +++ b/version/version.h @@ -3,7 +3,7 @@ namespace zen { -const wchar_t currentVersion[] = L"5.11"; //internal linkage! +const wchar_t currentVersion[] = L"5.12"; //internal linkage! } #endif diff --git a/version/version.rc b/version/version.rc index edfff0a6..c5196a6f 100644 --- a/version/version.rc +++ b/version/version.rc @@ -1,2 +1,2 @@ -#define FREEFILESYNC_VER 5,11,0,0 -#define FREEFILESYNC_VER_STR "5.11\0" +#define FREEFILESYNC_VER 5,12,0,0 +#define FREEFILESYNC_VER_STR "5.12\0" diff --git a/wx+/graph.cpp b/wx+/graph.cpp index 30677c80..a32c8e22 100644 --- a/wx+/graph.cpp +++ b/wx+/graph.cpp @@ -18,8 +18,6 @@ using namespace numeric; //todo: support zoom via mouse wheel -warn_static("reviewreviewreviewreviewreview") - const wxEventType zen::wxEVT_GRAPH_SELECTION = wxNewEventType(); const std::shared_ptr Graph2D::MainAttributes::defaultFormat = std::make_shared(); //for some buggy reason MSVC isn't able to use a temporary as a default argument @@ -85,9 +83,9 @@ wxColor getDefaultColor(size_t pos) return wxColor(75, 31, 111); //purple case 9: return wxColor(255, 149, 14); //orange - default: - return *wxBLACK; } + assert(false); + return *wxBLACK; } @@ -107,7 +105,6 @@ public: { return (realPos - min_) * scaleToScr; } - int realToScreenRound(double realPos) const //useful to find "proper" y-pixel positions { return numeric::round(realToScreen(realPos)); @@ -120,7 +117,7 @@ private: }; -//enlarge range to a multiple of a "useful" block size +//enlarge value range to display to a multiple of a "useful" block size void widenRange(double& valMin, double& valMax, //in/out int& blockCount, //out int graphAreaSize, //in pixel @@ -162,10 +159,10 @@ void drawXLabel(wxDC& dc, double xMin, double xMax, int blockCount, const Conver const int x = graphArea.x + cvrtX.realToScreenRound(valX); if (graphArea.height > 0) - dc.DrawLine(wxPoint(x, graphArea.y), wxPoint(x, graphArea.y + graphArea.height)); + dc.DrawLine(wxPoint(x, graphArea.y), wxPoint(x, graphArea.y + graphArea.height)); //wxDC::DrawLine() doesn't draw last pixel //draw x axis labels - const wxString label = labelFmt.formatText(xMin + i * valRangePerBlock, valRangePerBlock); + const wxString label = labelFmt.formatText(valX, valRangePerBlock); wxSize labelExtent = dc.GetMultiLineTextExtent(label); dc.DrawText(label, wxPoint(x - labelExtent.GetWidth() / 2, labelArea.y + (labelArea.height - labelExtent.GetHeight()) / 2)); //center } @@ -191,7 +188,7 @@ void drawYLabel(wxDC& dc, double yMin, double yMax, int blockCount, const Conver const int y = graphArea.y + cvrtY.realToScreenRound(valY); if (graphArea.width > 0) - dc.DrawLine(wxPoint(graphArea.x, y), wxPoint(graphArea.x + graphArea.width, y)); + dc.DrawLine(wxPoint(graphArea.x, y), wxPoint(graphArea.x + graphArea.width, y)); //wxDC::DrawLine() doesn't draw last pixel //draw y axis labels const wxString label = labelFmt.formatText(valY, valRangePerBlock); @@ -206,15 +203,14 @@ void subsample(StdContainter& cont, size_t factor) { if (factor <= 1) return; - auto iterOut = cont.begin(); - for (auto iterIn = cont.begin(); cont.end() - iterIn >= static_cast(factor); iterIn += factor) //don't even let iterator point out of range! - *iterOut++ = std::accumulate(iterIn, iterIn + factor, 0.0) / static_cast(factor); + auto itOut = cont.begin(); + for (auto itIn = cont.begin(); cont.end() - itIn >= static_cast(factor); itIn += factor) //don't even let iterator point out of range! + *itOut++ = std::accumulate(itIn, itIn + factor, 0.0) / static_cast(factor); - cont.erase(iterOut, cont.end()); + cont.erase(itOut, cont.end()); } } - Graph2D::Graph2D(wxWindow* parent, wxWindowID winid, const wxPoint& pos, @@ -223,12 +219,11 @@ Graph2D::Graph2D(wxWindow* parent, const wxString& name) : wxPanel(parent, winid, pos, size, style, name) { Connect(wxEVT_PAINT, wxPaintEventHandler(Graph2D::onPaintEvent), nullptr, this); - Connect(wxEVT_SIZE, wxSizeEventHandler (Graph2D::onSizeEvent ), nullptr, this); + Connect(wxEVT_SIZE, wxSizeEventHandler (Graph2D::onSizeEvent ), nullptr, this); //http://wiki.wxwidgets.org/Flicker-Free_Drawing Connect(wxEVT_ERASE_BACKGROUND, wxEraseEventHandler(Graph2D::onEraseBackGround), nullptr, this); //SetDoubleBuffered(true); slow as hell! - #if wxCHECK_VERSION(2, 9, 1) SetBackgroundStyle(wxBG_STYLE_PAINT); #else @@ -256,7 +251,6 @@ void Graph2D::OnMouseLeftDown(wxMouseEvent& event) if (!event.ControlDown()) oldSel.clear(); - Refresh(); } @@ -265,7 +259,7 @@ void Graph2D::OnMouseMovement(wxMouseEvent& event) { if (activeSel.get()) { - activeSel->refCurrentPos() = event.GetPosition(); + activeSel->refCurrentPos() = event.GetPosition(); //corresponding activeSel->refSelection() is updated in Graph2D::render() Refresh(); } } @@ -277,12 +271,11 @@ void Graph2D::OnMouseLeftUp(wxMouseEvent& event) { if (activeSel->getStartPos() != activeSel->refCurrentPos()) //if it's just a single mouse click: discard selection { - //fire off GraphSelectEvent - GraphSelectEvent selEvent(activeSel->refSelection()); + GraphSelectEvent selEvent(activeSel->refSelection()); //fire off GraphSelectEvent if (wxEvtHandler* handler = GetEventHandler()) handler->AddPendingEvent(selEvent); - oldSel.push_back(activeSel->refSelection()); + oldSel.push_back(activeSel->refSelection()); //commit selection } activeSel.reset(); @@ -298,50 +291,45 @@ void Graph2D::OnMouseCaptureLost(wxMouseCaptureLostEvent& event) } -void Graph2D::setData(const std::shared_ptr& data, const CurveAttributes& la) +void Graph2D::setData(const std::shared_ptr& data, const CurveAttributes& ca) { curves_.clear(); - addData(data, la); + addData(data, ca); } -void Graph2D::addData(const std::shared_ptr& data, const CurveAttributes& la) +void Graph2D::addData(const std::shared_ptr& data, const CurveAttributes& ca) { - CurveAttributes newAttr = la; + CurveAttributes newAttr = ca; if (newAttr.autoColor) newAttr.setColor(getDefaultColor(curves_.size())); curves_.push_back(std::make_pair(data, newAttr)); Refresh(); } - -namespace +namespace //putting this into function scope makes MSVC crash... { -class DcBackgroundChanger +struct CurveSamples { -public: - DcBackgroundChanger(wxDC& dc, const wxBrush& brush) : dc_(dc), old(dc.GetBackground()) { dc.SetBackground(brush); } - ~DcBackgroundChanger() { if (old.Ok()) dc_.SetBackground(old); } -private: - wxDC& dc_; - const wxBrush old; + CurveSamples() : offsetX(0) {} + std::vector yValues; //actual y-values at each screen pixel position + int offsetX; //x-value offset in pixels }; } - void Graph2D::render(wxDC& dc) const { + const wxRect clientRect = GetClientRect(); //DON'T use wxDC::GetSize()! DC may be larger than visible area! { - //clear everything, set label background color - // const wxColor backColor = wxPanel::GetClassDefaultAttributes().colBg != wxNullColour ? - // wxPanel::GetClassDefaultAttributes().colBg : - // wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE); - const wxColor backColor = GetBackgroundColour(); //user-configurable! - DcBackgroundChanger dummy(dc, backColor); //use wxDC::SetBackground instead of wxDC::SetBrush - dc.Clear(); + //clear complete client area; set label background color + const wxColor backCol = GetBackgroundColour(); //user-configurable! + //wxPanel::GetClassDefaultAttributes().colBg : + //wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE); + wxDCPenChanger dummy (dc, backCol); + wxDCBrushChanger dummy2(dc, backCol); + dc.DrawRectangle(clientRect); } - //note: DON'T use wxDC::GetSize()! DC may be larger than visible area! /* ----------------------- | | x-label | @@ -349,8 +337,7 @@ void Graph2D::render(wxDC& dc) const |y-label | graph area | |---------------------- */ - const wxRect clientRect = GetClientSize(); //data window only - wxRect graphArea = clientRect; //data window only + wxRect graphArea = clientRect; int xLabelPosY = clientRect.y; int yLabelPosX = clientRect.x; @@ -367,7 +354,6 @@ void Graph2D::render(wxDC& dc) const case X_LABEL_NONE: break; } - switch (attr.labelposY) { case Y_LABEL_LEFT: @@ -383,17 +369,16 @@ void Graph2D::render(wxDC& dc) const } { - //paint actual graph background (without labels) - DcBackgroundChanger dummy(dc, *wxWHITE); //accessibility: we have to set both back- and foreground colors or none at all! - wxDCPenChanger dummy2(dc, wxColour(130, 135, 144)); //medium grey, the same Win7 uses for other frame borders - //dc.DrawRectangle(static_cast(graphArea).Inflate(1, 1)); //correct wxWidgets design mistakes + //paint graph background (excluding label area) + wxDCPenChanger dummy (dc, wxColour(130, 135, 144)); //medium grey, the same Win7 uses for other frame borders + wxDCBrushChanger dummy2(dc, *wxWHITE); //accessibility: we have to set both back- and foreground colors or none at all! dc.DrawRectangle(graphArea); - graphArea.Deflate(1, 1); //do not draw on border + graphArea.Deflate(1, 1); //attention more wxWidgets design mistakes: behavior of wxRect::Deflate depends on object being const/non-const!!! } //set label areas respecting graph area border! - wxRect xLabelArea(graphArea.x, xLabelPosY, graphArea.width, attr.xLabelHeight); - wxRect yLabelArea(yLabelPosX, graphArea.y, attr.yLabelWidth, graphArea.height); + const wxRect xLabelArea(graphArea.x, xLabelPosY, graphArea.width, attr.xLabelHeight); + const wxRect yLabelArea(yLabelPosX, graphArea.y, attr.yLabelWidth, graphArea.height); const wxPoint graphAreaOrigin = graphArea.GetTopLeft(); @@ -426,7 +411,7 @@ void Graph2D::render(wxDC& dc) const *attr.labelFmtX); //detect y value range - std::vector, int>> yValuesList(curves_.size()); + std::vector yValuesList(curves_.size()); double minY = attr.minYauto ? std::numeric_limits::infinity() : attr.minY; //automatic: ensure values are initialized by first curve double maxY = attr.maxYauto ? -std::numeric_limits::infinity() : attr.maxY; // { @@ -434,34 +419,26 @@ void Graph2D::render(wxDC& dc) const const ConvertCoord cvrtX(minX, maxX, graphArea.width * AVG_FACTOR); for (auto it = curves_.begin(); it != curves_.end(); ++it) - if (it->first.get()) + if (const GraphData* graph = it->first.get()) { - const size_t index = it - curves_.begin(); - const GraphData& graph = *it->first; - - std::vector& yValues = yValuesList[index].first; //actual y-values - int& offsetX = yValuesList[index].second; //x-value offset in pixel + CurveSamples& samples = yValuesList[it - curves_.begin()]; { - const double xBegin = graph.getXBegin(); - const double xEnd = graph.getXEnd(); - - const int posFirst = std::ceil(cvrtX.realToScreen(std::max(xBegin, minX))); //apply min/max *before* calling realToScreen()! - const int posLast = std::ceil(cvrtX.realToScreen(std::min(xEnd, maxX))); //do not step outside [xBegin, xEnd) range => 2 x ceil! - //conversion from std::ceil double to int is loss-free for full value range of int! tested successfully on MSVC + const int posFirst = std::ceil(cvrtX.realToScreen(std::max(graph->getXBegin(), minX))); //do not step outside [xBegin, xEnd) range => 2 x ceil! + const int posLast = std::ceil(cvrtX.realToScreen(std::min(graph->getXEnd (), maxX))); // + //conversion from std::ceil double return valute to int is loss-free for full value range of 32-bit int! tested successfully on MSVC for (int i = posFirst; i < posLast; ++i) - yValues.push_back(graph.getValue(cvrtX.screenToReal(i))); + samples.yValues.push_back(graph->getValue(cvrtX.screenToReal(i))); - subsample(yValues, AVG_FACTOR); - offsetX = posFirst / AVG_FACTOR; + subsample(samples.yValues, AVG_FACTOR); + samples.offsetX = posFirst / AVG_FACTOR; } - - if (!yValues.empty()) + if (!samples.yValues.empty()) { if (attr.minYauto) - minY = std::min(minY, *std::min_element(yValues.begin(), yValues.end())); + minY = std::min(minY, *std::min_element(samples.yValues.begin(), samples.yValues.end())); if (attr.maxYauto) - maxY = std::max(maxY, *std::max_element(yValues.begin(), yValues.end())); + maxY = std::max(maxY, *std::max_element(samples.yValues.begin(), samples.yValues.end())); } } } @@ -484,25 +461,24 @@ void Graph2D::render(wxDC& dc) const { if (index < yValuesList.size()) { - const std::vector& yValues = yValuesList[index].first; //actual y-values - const int offsetX = yValuesList[index].second; //x-value offset in pixel + CurveSamples& samples = yValuesList[index]; - for (auto i = yValues.begin(); i != yValues.end(); ++i) - points.push_back(wxPoint(offsetX + (i - yValues.begin()), - cvrtY.realToScreenRound(*i)) + graphAreaOrigin); + for (auto it = samples.yValues.begin(); it != samples.yValues.end(); ++it) + points.push_back(wxPoint(samples.offsetX + (it - samples.yValues.begin()), + cvrtY.realToScreenRound(*it)) + graphAreaOrigin); } }; //update active mouse selection if (activeSel.get() && - graphArea.width > 0 && graphArea.height > 0) + graphArea.width > 0 && graphArea.height > 0) { - wxPoint startPos = activeSel->getStartPos() - graphAreaOrigin; //pos relative to graphArea + wxPoint startPos = activeSel->getStartPos() - graphAreaOrigin; //make relative to graphArea wxPoint currentPos = activeSel->refCurrentPos() - graphAreaOrigin; //normalize positions: a mouse selection is symmetric and *not* an half-open range! - confine(startPos .x, 0, graphArea.width - 1); - confine(currentPos.x, 0, graphArea.width - 1); + confine(startPos .x, 0, graphArea.width - 1); + confine(currentPos.x, 0, graphArea.width - 1); confine(startPos .y, 0, graphArea.height - 1); confine(currentPos.y, 0, graphArea.height - 1); @@ -544,14 +520,14 @@ void Graph2D::render(wxDC& dc) const wxDCBrushChanger dummy(dc, wxColor(168, 202, 236)); //light blue wxDCPenChanger dummy2(dc, wxColor(51, 153, 255)); //dark blue - for (auto i = allSelections.begin(); i != allSelections.end(); ++i) + for (auto it = allSelections.begin(); it != allSelections.end(); ++it) { //harmonize with active mouse selection above! - wxPoint pixelFrom(cvrtX.realToScreenRound(i->from.x), - cvrtY.realToScreenRound(i->from.y)); - wxPoint pixelTo(cvrtX.realToScreenRound(i->to.x), - cvrtY.realToScreenRound(i->to.y)); - //convert half-open to inclusive ranges for use with wxDC::DrawRectangle + wxPoint pixelFrom(cvrtX.realToScreenRound(it->from.x), + cvrtY.realToScreenRound(it->from.y)); + wxPoint pixelTo(cvrtX.realToScreenRound(it->to.x), + cvrtY.realToScreenRound(it->to.y)); + //convert half-open to inclusive ranges for use with wxDC::DrawRectangle if (pixelFrom.x != pixelTo.x) //no matter how small the selection, always draw at least one pixel! { pixelFrom.x -= pixelFrom.x < pixelTo.x ? 0 : 1; @@ -562,8 +538,8 @@ void Graph2D::render(wxDC& dc) const pixelFrom.y -= pixelFrom.y < pixelTo.y ? 0 : 1; pixelTo .y -= pixelFrom.y < pixelTo.y ? 1 : 0; } - confine(pixelFrom.x, 0, graphArea.width - 1); - confine(pixelTo .x, 0, graphArea.width - 1); + confine(pixelFrom.x, 0, graphArea.width - 1); + confine(pixelTo .x, 0, graphArea.width - 1); confine(pixelFrom.y, 0, graphArea.height - 1); confine(pixelTo .y, 0, graphArea.height - 1); @@ -600,7 +576,7 @@ void Graph2D::render(wxDC& dc) const { wxDCPenChanger dummy(dc, wxPen(it->second.color, it->second.lineWidth)); dc.DrawLines(static_cast(points.size()), &points[0]); - dc.DrawPoint(points.back()); //last pixel omitted by DrawLines + dc.DrawPoint(points.back()); //wxDC::DrawLines() doesn't draw last pixel } } } diff --git a/wx+/graph.h b/wx+/graph.h index f5e38851..8f816b08 100644 --- a/wx+/graph.h +++ b/wx+/graph.h @@ -39,7 +39,6 @@ struct GraphData //reference data implementation - class RangeData : public GraphData { public: @@ -57,7 +56,7 @@ private: std::vector data; }; - +/* //reference data implementation class VectorData : public GraphData { @@ -75,6 +74,7 @@ private: std::vector data; }; +*/ //------------------------------------------------------------------------------------------------------------ struct LabelFormatter @@ -88,7 +88,7 @@ struct LabelFormatter virtual wxString formatText(double value, double optimalBlockSize) const = 0; }; -double nextNiceNumber(double blockSize); //round to next number which is convenient to read, e.g. 2.13 -> 2; 2.7 -> 2.5; 7 -> 5 +double nextNiceNumber(double blockSize); //round to next number which is convenient to read, e.g. 2.13 -> 2; 2.7 -> 2.5 struct DecimalNumberFormatter : public LabelFormatter { @@ -97,11 +97,11 @@ struct DecimalNumberFormatter : public LabelFormatter }; //------------------------------------------------------------------------------------------------------------ + //emit data selection event //Usage: wnd.Connect(wxEVT_GRAPH_SELECTION, GraphSelectEventHandler(MyDlg::OnGraphSelection), nullptr, this); // void MyDlg::OnGraphSelection(GraphSelectEvent& event); - extern const wxEventType wxEVT_GRAPH_SELECTION; struct SelectionBlock @@ -122,7 +122,6 @@ class GraphSelectEvent : public wxCommandEvent { public: GraphSelectEvent(const SelectionBlock& selBlock) : wxCommandEvent(wxEVT_GRAPH_SELECTION), selBlock_(selBlock) {} - virtual wxEvent* Clone() const { return new GraphSelectEvent(selBlock_); } SelectionBlock getSelection() { return selBlock_; } @@ -169,8 +168,8 @@ public: int lineWidth; }; - void setData(const std::shared_ptr& data, const CurveAttributes& attr = CurveAttributes()); - void addData(const std::shared_ptr& data, const CurveAttributes& attr = CurveAttributes()); + void setData(const std::shared_ptr& data, const CurveAttributes& ca = CurveAttributes()); + void addData(const std::shared_ptr& data, const CurveAttributes& ca = CurveAttributes()); enum PosLabelY { @@ -214,14 +213,13 @@ public: labelFmtY(std::make_shared()), mouseSelMode(SELECT_RECTANGLE) {} - MainAttributes& setMinX(double newMinX) { minX = newMinX; minXauto = false; return *this; } MainAttributes& setMaxX(double newMaxX) { maxX = newMaxX; maxXauto = false; return *this; } MainAttributes& setMinY(double newMinY) { minY = newMinY; minYauto = false; return *this; } MainAttributes& setMaxY(double newMaxY) { maxY = newMaxY; maxYauto = false; return *this; } - MainAttributes& setAutoSize() { minXauto = true; maxXauto = true; minYauto = true; maxYauto = true; return *this; } + MainAttributes& setAutoSize() { minXauto = maxXauto = minYauto = maxYauto = true; return *this; } static const std::shared_ptr defaultFormat; @@ -268,7 +266,6 @@ public: void setAttributes(const MainAttributes& newAttr) { attr = newAttr; Refresh(); } MainAttributes getAttributes() const { return attr; } - std::vector getSelections() const { return oldSel; } void setSelections(const std::vector& sel) { @@ -299,7 +296,7 @@ private: wxPoint getStartPos() const { return posDragStart_; } wxPoint& refCurrentPos() { return posDragCurrent; } - SelectionBlock& refSelection() { return selBlock; } //set when selection is drawn: this is fine, 'cause only what's shown should be selected! + SelectionBlock& refSelection() { return selBlock; } //updated in Graph2d::render(): this is fine, since only what's shown is selected! private: wxWindow& wnd_; @@ -319,5 +316,4 @@ private: }; } - #endif //WX_PLOT_HEADER_2344252459 diff --git a/wx+/grid.cpp b/wx+/grid.cpp index 5c9d3dc8..f6d0e6b8 100644 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -328,6 +328,8 @@ public: Connect(wxEVT_CHAR, wxKeyEventHandler(SubWindow::onChar ), nullptr, this); Connect(wxEVT_KEY_UP, wxKeyEventHandler(SubWindow::onKeyUp ), nullptr, this); Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(SubWindow::onKeyDown), nullptr, this); + + assert(GetClientAreaOrigin() == wxPoint()); //generally assumed when dealing with coordinates below } Grid& refParent() { return parent_; } @@ -483,6 +485,7 @@ public: wxRect getRowLabelArea(ptrdiff_t row) const { + assert(GetClientAreaOrigin() == wxPoint()); return wxRect(wxPoint(0, rowHeight * row), wxSize(GetClientSize().GetWidth(), rowHeight)); } @@ -1304,9 +1307,9 @@ private: } ~MouseSelection() { if (wnd_.HasCapture()) wnd_.ReleaseMouse(); } - size_t getStartRow () const { return rowStart_; } - size_t getComponentPos () const { return compPos_; } - size_t getCurrentRow () const { return rowCurrent_; } + size_t getStartRow () const { return rowStart_; } + size_t getComponentPos () const { return compPos_; } + size_t getCurrentRow () const { return rowCurrent_; } bool isPositiveSelect() const { return positiveSelect_; } //are we selecting or unselecting? void evalMousePos() @@ -1322,6 +1325,7 @@ private: wxMouseState mouseState = wxGetMouseState(); const wxPoint clientPos = wnd_.ScreenToClient(wxPoint(mouseState.GetX(), mouseState.GetY())); const wxSize clientSize = wnd_.GetClientSize(); + assert(wnd_.GetClientAreaOrigin() == wxPoint()); //scroll while dragging mouse const int overlapPixY = clientPos.y < 0 ? clientPos.y : diff --git a/wx+/pch.h b/wx+/pch.h index b27cc656..5bfdb6cb 100644 --- a/wx+/pch.h +++ b/wx+/pch.h @@ -13,11 +13,11 @@ #endif //##################################################### + // basic wxWidgets headers #ifndef WX_PRECOMP #define WX_PRECOMP #endif - #include //includes //other wxWidgets headers @@ -37,7 +37,8 @@ #include #include #include -#include +//#include -> MSVC: avoid annoying IntelliSense error: wxZipStreamLink +//#include #include #include #include @@ -70,15 +71,11 @@ #include #include #include -#include #include #include #include #include -//##################################################### -// #include other rarely changing headers here - //STL headers #include #include diff --git a/wx+/shell_execute.h b/wx+/shell_execute.h index 6a2920e9..2531c91f 100644 --- a/wx+/shell_execute.h +++ b/wx+/shell_execute.h @@ -35,7 +35,7 @@ enum ExecutionType EXEC_TYPE_ASYNC }; -void shellExecute(const wxString& command, ExecutionType type = EXEC_TYPE_ASYNC) +void shellExecute(const Zstring& command, ExecutionType type = EXEC_TYPE_ASYNC) { #ifdef FFS_WIN //parse commandline @@ -47,8 +47,8 @@ void shellExecute(const wxString& command, ExecutionType type = EXEC_TYPE_ASYNC) ::LocalFree(tmp); } - wxString filename; - wxString arguments; + std::wstring filename; + std::wstring arguments; if (!argv.empty()) { filename = argv[0]; @@ -86,10 +86,10 @@ void shellExecute(const wxString& command, ExecutionType type = EXEC_TYPE_ASYNC) if (type == EXEC_TYPE_SYNC) { //Posix::system - execute a shell command - int rv = ::system(utfCvrtTo(command).c_str()); //do NOT use std::system as its documentation says nothing about "WEXITSTATUS(rv)", ect... + int rv = ::system(command.c_str()); //do NOT use std::system as its documentation says nothing about "WEXITSTATUS(rv)", ect... if (rv == -1 || WEXITSTATUS(rv) == 127) //http://linux.die.net/man/3/system "In case /bin/sh could not be executed, the exit status will be that of a command that does exit(127)" { - wxMessageBox(_("Invalid command line:") + L"\n" + command); + wxMessageBox(_("Invalid command line:") + L"\n" + utfCvrtTo(command)); return; } } @@ -100,7 +100,7 @@ void shellExecute(const wxString& command, ExecutionType type = EXEC_TYPE_ASYNC) //by default wxExecute uses a zero sized dummy window as a hack to keep focus which leaves a useless empty icon in ALT-TAB list //=> use wxEXEC_NODISABLE and roll our own window disabler! (see comment in app.cpp: void *wxGUIAppTraits::BeforeChildWaitLoop()) wxWindowDisabler dummy; //disables all top level windows - wxExecute(command, wxEXEC_ASYNC | wxEXEC_NODISABLE); + wxExecute(utfCvrtTo(command), wxEXEC_ASYNC | wxEXEC_NODISABLE); wxLog::FlushActive(); //show wxWidgets error messages (if any) } #endif diff --git a/zen/IFileOperation/file_op.cpp b/zen/IFileOperation/file_op.cpp index 5d4cfdc9..0691ac5b 100644 --- a/zen/IFileOperation/file_op.cpp +++ b/zen/IFileOperation/file_op.cpp @@ -84,14 +84,14 @@ public: //IFileOperationProgressSink virtual HRESULT STDMETHODCALLTYPE StartOperations() { return S_OK; } virtual HRESULT STDMETHODCALLTYPE FinishOperations(HRESULT hrResult) { return S_OK; } - virtual HRESULT STDMETHODCALLTYPE PreRenameItem(DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; } - virtual HRESULT STDMETHODCALLTYPE PostRenameItem(DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_string LPCWSTR pszNewName, HRESULT hrRename, __RPC__in_opt IShellItem* psiNewlyCreated) { return S_OK; } - virtual HRESULT STDMETHODCALLTYPE PreMoveItem(DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; } - virtual HRESULT STDMETHODCALLTYPE PostMoveItem(DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName, HRESULT hrMove, __RPC__in_opt IShellItem* psiNewlyCreated) { return S_OK; } - virtual HRESULT STDMETHODCALLTYPE PreCopyItem(DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; } - virtual HRESULT STDMETHODCALLTYPE PostCopyItem(DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName, HRESULT hrCopy, __RPC__in_opt IShellItem* psiNewlyCreated) { return S_OK; } - virtual HRESULT STDMETHODCALLTYPE PreNewItem(DWORD dwFlags, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; } - virtual HRESULT STDMETHODCALLTYPE PostNewItem(DWORD dwFlags, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName, __RPC__in_opt_string LPCWSTR pszTemplateName, DWORD dwFileAttributes, HRESULT hrNew, __RPC__in_opt IShellItem* psiNewItem) { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE PreRenameItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE PostRenameItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_string LPCWSTR pszNewName, HRESULT hrRename, __RPC__in_opt IShellItem* psiNewlyCreated) { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE PreMoveItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE PostMoveItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName, HRESULT hrMove, __RPC__in_opt IShellItem* psiNewlyCreated) { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE PreCopyItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE PostCopyItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName, HRESULT hrCopy, __RPC__in_opt IShellItem* psiNewlyCreated) { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE PreNewItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE PostNewItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName, __RPC__in_opt_string LPCWSTR pszTemplateName, DWORD dwFileAttributes, HRESULT hrNew, __RPC__in_opt IShellItem* psiNewItem) { return S_OK; } virtual HRESULT STDMETHODCALLTYPE PreDeleteItem(DWORD dwFlags, __RPC__in_opt IShellItem* psiItem) { @@ -138,8 +138,8 @@ public: //=> defer cancellation to PreDeleteItem()/PostDeleteItem() return S_OK; } - virtual HRESULT STDMETHODCALLTYPE ResetTimer() { return S_OK; } - virtual HRESULT STDMETHODCALLTYPE PauseTimer() { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE ResetTimer () { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE PauseTimer () { return S_OK; } virtual HRESULT STDMETHODCALLTYPE ResumeTimer() { return S_OK; } //call after IFileOperation::PerformOperations() diff --git a/zen/basic_math.h b/zen/basic_math.h index f8a7affd..d83a7f77 100644 --- a/zen/basic_math.h +++ b/zen/basic_math.h @@ -146,7 +146,7 @@ T confineCpy(const T& val, const T& minVal, const T& maxVal) } template inline -void confine(T& val, const T& minVal, const T& maxVal) //name trim? +void confine(T& val, const T& minVal, const T& maxVal) //name trim, clamp? { assert(minVal <= maxVal); if (val < minVal) diff --git a/zen/debug_memory_leaks.cpp b/zen/debug_memory_leaks.cpp index 990f2ec7..2359b6ef 100644 --- a/zen/debug_memory_leaks.cpp +++ b/zen/debug_memory_leaks.cpp @@ -8,7 +8,7 @@ //Usage: just include this file into a Visual Studio project -#ifndef NDEBUG +#ifdef _DEBUG //When _DEBUG is not defined, calls to _CrtSetDbgFlag are removed during preprocessing. #define _CRTDBG_MAP_ALLOC // #include //keep this order: "The #include statements must be in the order shown here. If you change the order, the functions you use may not work properly." #include //overwrites "operator new" ect; no need to include this in every compilation unit! @@ -27,4 +27,4 @@ struct OnStartup } dummy; } -#endif \ No newline at end of file +#endif //_DEBUG \ No newline at end of file diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp index c052435a..4f34814f 100644 --- a/zen/file_handling.cpp +++ b/zen/file_handling.cpp @@ -479,8 +479,7 @@ Zstring findUnused8Dot3Name(const Zstring& filename) //find a unique 8.3 short n if (!somethingExists(output)) //ensure uniqueness return output; } - - throw std::runtime_error(std::string("100000000 files, one for each number, exist in this directory? You're kidding...\n") + utfCvrtTo(pathPrefix)); + throw std::runtime_error(std::string("100000000 files, one for each number, exist in this directory? You're kidding...") + utfCvrtTo(pathPrefix)); } @@ -566,10 +565,12 @@ void zen::renameFile(const Zstring& oldName, const Zstring& newName) //throw Fil } -class FilesDirsOnlyTraverser : public zen::TraverseCallback +namespace +{ +class CollectFilesFlat : public zen::TraverseCallback { public: - FilesDirsOnlyTraverser(std::vector& files, std::vector& dirs) : + CollectFilesFlat(std::vector& files, std::vector& dirs) : m_files(files), m_dirs(dirs) {} @@ -593,19 +594,17 @@ public: virtual HandleError onError(const std::wstring& msg) { throw FileError(msg); } private: - FilesDirsOnlyTraverser(const FilesDirsOnlyTraverser&); - FilesDirsOnlyTraverser& operator=(const FilesDirsOnlyTraverser&); + CollectFilesFlat(const CollectFilesFlat&); + CollectFilesFlat& operator=(const CollectFilesFlat&); std::vector& m_files; std::vector& m_dirs; }; -void zen::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback) +void removeDirectoryImpl(const Zstring& directory, CallbackRemoveDir* callback) //throw FileError { - //no error situation if directory is not existing! manual deletion relies on it! - if (!somethingExists(directory)) - return; //neither directory nor any other object (e.g. broken symlink) with that name existing + assert(somethingExists(directory)); //[!] #ifdef FFS_WIN const Zstring directoryFmt = applyLongPathPrefix(directory); //support for \\?\-prefix @@ -617,55 +616,66 @@ void zen::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback) //attention: check if directory is a symlink! Do NOT traverse into it deleting contained files!!! if (symlinkExists(directory)) //remove symlink directly { + if (callback) callback->onBeforeDirDeletion(directory); //once per symlink #ifdef FFS_WIN if (!::RemoveDirectory(directoryFmt.c_str())) #elif defined FFS_LINUX if (::unlink(directory.c_str()) != 0) #endif throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted()); - - if (callback) - callback->notifyDirDeletion(directory); //once per symlink - return; } - - std::vector fileList; - std::vector dirList; + else { - //get all files and directories from current directory (WITHOUT subdirectories!) - FilesDirsOnlyTraverser traverser(fileList, dirList); - traverseFolder(directory, traverser); //don't follow symlinks - } + std::vector fileList; + std::vector dirList; + { + //get all files and directories from current directory (WITHOUT subdirectories!) + CollectFilesFlat cff(fileList, dirList); + traverseFolder(directory, cff); //don't follow symlinks + } - //delete directories recursively - for (auto it = dirList.begin(); it != dirList.end(); ++it) - removeDirectory(*it, callback); //call recursively to correctly handle symbolic links + //delete directories recursively + std::for_each(dirList.begin(), dirList.end(), + [&](const Zstring& dirname) + { + removeDirectoryImpl(dirname, callback); //throw FileError; call recursively to correctly handle symbolic links + }); - //delete files - for (auto it = fileList.begin(); it != fileList.end(); ++it) - { - const bool workDone = removeFile(*it); - if (callback && workDone) - callback->notifyFileDeletion(*it); //call once per file - } + //delete files + std::for_each(fileList.begin(), fileList.end(), + [&](const Zstring& filename) + { + if (callback) callback->onBeforeFileDeletion(filename); //call once per file + removeFile(filename); //throw FileError + }); - //parent directory is deleted last + //parent directory is deleted last + if (callback) callback->onBeforeDirDeletion(directory); //and once per folder #ifdef FFS_WIN - if (!::RemoveDirectory(directoryFmt.c_str())) + if (!::RemoveDirectory(directoryFmt.c_str())) #else - if (::rmdir(directory.c_str()) != 0) + if (::rmdir(directory.c_str()) != 0) #endif - throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted()); - //may spuriously fail with ERROR_DIR_NOT_EMPTY(145) even though all child items have - //successfully been *marked* for deletion, but some application still has a handle open! - //e.g. Open "C:\Test\Dir1\Dir2" (filled with lots of files) in Explorer, then delete "C:\Test\Dir1" via ::RemoveDirectory() => Error 145 - //Sample code: http://us.generation-nt.com/answer/createfile-directory-handles-removing-parent-help-29126332.html - - if (callback) - callback->notifyDirDeletion(directory); //and once per folder + throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted()); + //may spuriously fail with ERROR_DIR_NOT_EMPTY(145) even though all child items have + //successfully been *marked* for deletion, but some application still has a handle open! + //e.g. Open "C:\Test\Dir1\Dir2" (filled with lots of files) in Explorer, then delete "C:\Test\Dir1" via ::RemoveDirectory() => Error 145 + //Sample code: http://us.generation-nt.com/answer/createfile-directory-handles-removing-parent-help-29126332.html + } +} } +void zen::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback) +{ + //no error situation if directory is not existing! manual deletion relies on it! + if (!somethingExists(directory)) + return; //neither directory nor any other object (e.g. broken symlink) with that name existing + removeDirectoryImpl(directory, callback); +} + + + void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, ProcSymlink procSl) //throw FileError { #ifdef FFS_WIN @@ -917,11 +927,6 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr assert(::CompareFileTime(&creationTimeDbg, &creationTime) == 0); assert(::CompareFileTime(&lastWriteTimeDbg, &lastWriteTime) == 0); } - //CAVEAT on FAT/FAT32: the sequence of deleting the target file and renaming "file.txt.ffs_tmp" to "file.txt" seems to - //NOT PRESERVE the creation time of the .ffs_tmp file, but "reuses" whatever creation time the old "file.txt" had! - //this problem is therefore NOT detected by the check above! - //However during the next comparison the DST hack will be applied correctly. - #endif #elif defined FFS_LINUX @@ -2303,6 +2308,27 @@ void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPath //perf: this call is REALLY expensive on unbuffered volumes! ~40% performance decrease on FAT USB stick! renameFile(temporary, targetFile); //throw FileError + /* + CAVEAT on FAT/FAT32: the sequence of deleting the target file and renaming "file.txt.ffs_tmp" to "file.txt" does + NOT PRESERVE the creation time of the .ffs_tmp file, but SILENTLY "reuses" whatever creation time the old "file.txt" had! + This "feature" is called "File System Tunneling": + http://blogs.msdn.com/b/oldnewthing/archive/2005/07/15/439261.aspx + http://support.microsoft.com/kb/172190/en-us + + However during the next comparison the DST hack will be applied correctly since the DST-hash of the mod.time is invalid. + + EXCEPTION: the hash may match!!! reproduce: + 1. set system time back to date within previous DST + 2. save some file on FAT32 usb stick and FFS-compare to make sure the DST hack is applied correctly + 4. pull out usb stick, put back in + 3. restore system time + 4. copy file from USB to local drive via explorer + => + NTFS <-> FAT, file exists on both sides; mod times match, DST hack on USB stick causes 1-hour offset when comparing in FFS. + When syncing modification time is copied correctly, but new DST hack fails to apply and old creation time is reused (see above). + Unfortunately, the old DST hash matches mod time! => On next comparison FFS will *still* see both sides as different!!!!!!!!! + */ + guardTempFile.dismiss(); } else diff --git a/zen/file_handling.h b/zen/file_handling.h index e9e1685d..5739dc2a 100644 --- a/zen/file_handling.h +++ b/zen/file_handling.h @@ -47,7 +47,7 @@ UInt64 getFilesize(const Zstring& filename); //throw FileError UInt64 getFreeDiskSpace(const Zstring& path); //throw FileError //file handling -bool removeFile(const Zstring& filename); //throw FileError; return "true" if file was actually deleted +bool removeFile(const Zstring& filename); //throw FileError; return "false" if file is not existing void removeDirectory(const Zstring& directory, CallbackRemoveDir* callback = nullptr); //throw FileError //rename file or directory: no copying!!! @@ -57,7 +57,7 @@ bool supportsPermissions(const Zstring& dirname); //throw FileError, derefernces //creates superdirectories automatically: void makeDirectory(const Zstring& directory); //throw FileError; do nothing if directory already exists! -void makeNewDirectory(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions); //FileError, ErrorTargetExisting +void makeNewDirectory(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions); //throw FileError, ErrorTargetExisting struct FileAttrib { @@ -85,16 +85,15 @@ void copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copy struct CallbackRemoveDir { virtual ~CallbackRemoveDir() {} - virtual void notifyFileDeletion(const Zstring& filename) = 0; //one call for each (existing) object! - virtual void notifyDirDeletion (const Zstring& dirname ) = 0; // + virtual void onBeforeFileDeletion(const Zstring& filename) = 0; //one call for each *existing* object! + virtual void onBeforeDirDeletion (const Zstring& dirname ) = 0; // }; - struct CallbackCopyFile { virtual ~CallbackCopyFile() {} - //if target is existing user needs to implement deletion: copyFile() NEVER deletes target if already existing! + //if target is existing user needs to implement deletion: copyFile() NEVER overwrites target if already existing! //if transactionalCopy == true, full read access on source had been proven at this point, so it's safe to delete it. virtual void deleteTargetFile(const Zstring& targetFile) = 0; //may throw exceptions diff --git a/zen/file_traverser.h b/zen/file_traverser.h index d8a99a4d..c8ef6550 100644 --- a/zen/file_traverser.h +++ b/zen/file_traverser.h @@ -12,7 +12,6 @@ #include "int64.h" #include "file_id_def.h" - //advanced file traverser returning metadata and hierarchical information on files and directories namespace zen @@ -25,8 +24,8 @@ struct TraverseCallback { UInt64 fileSize; //unit: bytes! Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC - FileId id; //optional: may be initial! - //bool isFollowedSymlink; + FileId id; //optional: initial if not supported! + //std::unique_ptr symlinkInfo; //only filled if file is dereferenced symlink }; struct SymlinkInfo @@ -48,7 +47,6 @@ struct TraverseCallback ON_ERROR_IGNORE }; - //overwrite these virtual methods virtual std::shared_ptr //nullptr: ignore directory, non-nullptr: traverse into using the (new) callback /**/ onDir (const Zchar* shortName, const Zstring& fullName) = 0; virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details) = 0; diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp index 8e5b04d3..6455029f 100644 --- a/zen/format_unit.cpp +++ b/zen/format_unit.cpp @@ -199,7 +199,7 @@ private: //convert LOCALE_SGROUPING to Grouping: http://blogs.msdn.com/b/oldnewthing/archive/2006/04/18/578251.aspx replace(grouping, L';', L""); if (endsWith(grouping, L'0')) - grouping.resize(grouping.size() - 1); + grouping.pop_back(); else grouping += L'0'; fmt.Grouping = stringTo(grouping); @@ -253,7 +253,7 @@ std::wstring zen::ffs_Impl::includeNumberSeparator(const std::wstring& number) if (i <= 3) break; i -= 3; - if (!isDigit(output[i - 1])) + if (!isDigit(output[i - 1])) //stop on +, - signs break; output.insert(i, thousandSep); } diff --git a/zen/guid.h b/zen/guid.h index 823f4431..0c03bd9f 100644 --- a/zen/guid.h +++ b/zen/guid.h @@ -28,7 +28,8 @@ namespace zen inline std::string generateGUID() //creates a 16 byte GUID { - boost::uuids::uuid nativeRep = boost::uuids::random_generator()(); //generator is thread-safe like an int + boost::uuids::uuid nativeRep = boost::uuids::random_generator()(); + //generator is only thread-safe like an int, so we keep it local until we need to optimize perf //perf: generator: 0.22ms per call; retrieve GUID: 0.12s per call return std::string(nativeRep.begin(), nativeRep.end()); } diff --git a/zen/perf.h b/zen/perf.h index 92350602..4a334bff 100644 --- a/zen/perf.h +++ b/zen/perf.h @@ -29,11 +29,13 @@ public: class TimerError {}; ZEN_DEPRECATE - PerfTimer() : ticksPerSec_(ticksPerSec()), startTime(), resultShown(false) + PerfTimer() : //throw TimerError + ticksPerSec_(ticksPerSec()), startTime(), resultShown(false) { //std::clock() - "counts CPU time in C and wall time in VC++" - WTF!??? #ifdef FFS_WIN - if (::SetThreadAffinityMask(::GetCurrentThread(), 1) == 0) throw TimerError(); //"should not be required unless there are bugs in BIOS or HAL" - msdn, QueryPerformanceCounter + if (::SetThreadAffinityMask(::GetCurrentThread(), 1) == 0) //"should not be required unless there are bugs in BIOS or HAL" - msdn, QueryPerformanceCounter + throw TimerError(); #endif startTime = getTicks(); if (ticksPerSec_ == 0 || !startTime.isValid()) diff --git a/zen/process_priority.h b/zen/process_priority.h index 15266b28..c0bae667 100644 --- a/zen/process_priority.h +++ b/zen/process_priority.h @@ -12,7 +12,7 @@ namespace zen struct PreventStandby //signal a "busy" state to the operating system { #ifdef FFS_WIN - PreventStandby() { ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED /* | ES_AWAYMODE_REQUIRED*/ ); } + PreventStandby () { ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED /* | ES_AWAYMODE_REQUIRED*/ ); } ~PreventStandby() { ::SetThreadExecutionState(ES_CONTINUOUS); } #endif }; @@ -26,8 +26,8 @@ struct ScheduleForBackgroundProcessing //lower CPU and file I/O priorities #define PROCESS_MODE_BACKGROUND_END 0x00200000 // #endif - ScheduleForBackgroundProcessing() { ::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_BEGIN); } //this call lowers CPU priority, too!! - ~ScheduleForBackgroundProcessing() { ::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_END); } + ScheduleForBackgroundProcessing () { ::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_BEGIN); } //this call lowers CPU priority, too!! + ~ScheduleForBackgroundProcessing() { ::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_END ); } #elif defined FFS_LINUX /* diff --git a/zen/recycler.h b/zen/recycler.h index 4d33477d..8aca0ff3 100644 --- a/zen/recycler.h +++ b/zen/recycler.h @@ -42,13 +42,14 @@ enum StatusRecycler STATUS_REC_UNKNOWN }; StatusRecycler recycleBinStatus(const Zstring& pathName); //test existence of Recycle Bin API for certain path +//Win: blocks heavily if recycle bin is really full and drive is slow!!! struct CallbackRecycling { virtual ~CallbackRecycling() {} //may throw: first exception is swallowed, updateStatus() is then called again where it should throw again and the exception will propagate as expected - virtual void updateStatus(const Zstring& currentItem) = 0; + virtual void updateStatus(const Zstring& currentItem) = 0; //currentItem may be empty }; void recycleOrDelete(const std::vector& filenames, //throw FileError, return "true" if file/dir was actually deleted diff --git a/zen/string_base.h b/zen/string_base.h index bfe573e9..e4e21716 100644 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -15,7 +15,6 @@ //Zbase - a policy based string class optimizing performance and genericity - namespace zen { /* @@ -59,8 +58,8 @@ template //Allocator Policy +template //Allocator Policy class StorageDeepCopy : public AP { protected: @@ -112,8 +111,8 @@ private: }; -template //Allocator Policy +template //Allocator Policy class StorageRefCountThreadSafe : public AP { protected: @@ -169,7 +168,7 @@ private: { Descriptor(long rc, size_t len, size_t cap) : refCount(rc), - length(static_cast(len)), + length (static_cast(len)), capacity(static_cast(cap)) {} boost::detail::atomic_count refCount; //practically no perf loss: ~0.2%! (FFS comparison) @@ -181,8 +180,8 @@ private: static Descriptor* descr( Char* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; } static const Descriptor* descr(const Char* ptr) { return reinterpret_cast(ptr) - 1; } }; -//################################################################################################################################################################ +//################################################################################################################################################################ //perf note: interstingly StorageDeepCopy and StorageRefCountThreadSafe show same performance in FFS comparison @@ -386,7 +385,7 @@ size_t Zbase::find(const Zbase& str, size_t pos) const assert(pos <= length()); const Char* thisEnd = end(); //respect embedded 0 const Char* it = std::search(begin() + pos, thisEnd, - str.begin(), str.end()); + str.begin(), str.end()); return it == thisEnd ? npos : it - begin(); } @@ -397,7 +396,7 @@ size_t Zbase::find(const Char* str, size_t pos) const assert(pos <= length()); const Char* thisEnd = end(); //respect embedded 0 const Char* it = std::search(begin() + pos, thisEnd, - str, str + strLength(str)); + str, str + strLength(str)); return it == thisEnd ? npos : it - begin(); } @@ -433,7 +432,7 @@ size_t Zbase::rfind(const Char* str, size_t pos) const const Char* currEnd = pos == npos ? end() : begin() + std::min(pos + strLen, length()); const Char* it = search_last(begin(), currEnd, - str, str + strLen); + str, str + strLen); return it == currEnd ? npos : it - begin(); } diff --git a/zen/thread.h b/zen/thread.h index 31d762c7..43917d13 100644 --- a/zen/thread.h +++ b/zen/thread.h @@ -33,8 +33,11 @@ namespace zen { -//until std::async is available: /* +std::async replacement without crappy semantics: + 1. guaranteed to run asynchronous + 2. does not follow C++11 [futures.async], Paragraph 5, where std::future waits for thread in destructor + Example: Zstring dirname = ... auto ft = zen::async([=](){ return zen::dirExists(dirname); }); @@ -93,10 +96,13 @@ private: template inline auto async2(Function fun) -> boost::unique_future //support for workaround of VS2010 bug: bool (*fun)(); decltype(fun()) == int! { - boost::packaged_task pt(std::move(fun)); //packaged task seems to even require r-value reference: https://sourceforge.net/p/freefilesync/bugs/234/ +#if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK //mirror "boost/thread/future.hpp", hopefully they know what they're doing + boost::packaged_task pt(std::move(fun)); //packaged task seems to even require r-value reference: https://sourceforge.net/p/freefilesync/bugs/234/ +#else + boost::packaged_task pt(std::move(fun)); +#endif auto fut = pt.get_future(); - boost::thread t(std::move(pt)); - t.detach(); //we have to be explicit since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!! + boost::thread(std::move(pt)).detach(); //we have to explicitly detach since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!! return std::move(fut); //compiler error without "move", why needed??? } -- cgit