diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:22:55 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:22:55 +0200 |
commit | c4182431ac7d9d306fdd2267e082fa4cec6fec2c (patch) | |
tree | 865cca543c062b7af2586f85cee19f9af4e7614d | |
parent | 5.11 (diff) | |
download | FreeFileSync-c4182431ac7d9d306fdd2267e082fa4cec6fec2c.tar.gz FreeFileSync-c4182431ac7d9d306fdd2267e082fa4cec6fec2c.tar.bz2 FreeFileSync-c4182431ac7d9d306fdd2267e082fa4cec6fec2c.zip |
5.12
110 files changed, 4406 insertions, 3631 deletions
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 Binary files differindex e511c8e0..deb1bb4f 100644 --- a/BUILD/FreeFileSync.chm +++ b/BUILD/FreeFileSync.chm 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 @@ <HEAD> <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=windows-1252"> <TITLE></TITLE> - <META NAME="GENERATOR" CONTENT="OpenOffice.org 3.2 (Win32)"> + <META NAME="GENERATOR" CONTENT="OpenOffice.org 3.4.1 (Win32)"> <META NAME="CREATED" CONTENT="20091206;16574000"> - <META NAME="CHANGED" CONTENT="20120702;13434800"> + <META NAME="CHANGED" CONTENT="20130120;18372197"> <META NAME="Info 1" CONTENT=""> <META NAME="Info 2" CONTENT=""> <META NAME="Info 3" CONTENT=""> @@ -27,7 +27,7 @@ <BODY LANG="en-US" DIR="LTR"> <H2 CLASS="western"><FONT FACE="Tahoma, sans-serif"><FONT SIZE=4 STYLE="font-size: 15pt">Comparison Settings</FONT></FONT></H2> -<P STYLE="margin-bottom: 0cm"><IMG SRC="../img/CmpSettings.png" NAME="Grafik1" ALIGN=BOTTOM BORDER=0></P> +<P STYLE="margin-bottom: 0cm"><IMG SRC="../img/CmpSettings.png" NAME="Grafik1" ALIGN=BOTTOM WIDTH=219 HEIGHT=267 BORDER=0></P> <H3 CLASS="western" STYLE="page-break-after: avoid"><FONT FACE="Tahoma, sans-serif">I. Compare by "File time and size"</FONT></H3> <P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">This @@ -65,21 +65,6 @@ distinguished:</FONT></P> </UL> </OL> </OL> -<P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">This -results into the following categories:</FONT></P> -<UL> - <LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">left - only</FONT></P> - <LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">right - only</FONT></P> - <LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">left - newer</FONT></P> - <LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">right - newer</FONT></P> - <LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">equal</FONT></P> - <LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">conflict - (same date, different size)</FONT></P> -</UL> <P STYLE="margin-bottom: 0cm"><BR> </P> <H3 CLASS="western" STYLE="page-break-after: avoid"><FONT FACE="Tahoma, sans-serif">II. @@ -110,17 +95,6 @@ modification time is not taken into account at all.</FONT></P> </UL> </OL> </OL> -<P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">As a -result the files are separated into the following categories:</FONT></P> -<UL> - <LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">left - only</FONT></P> - <LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">right - only</FONT></P> - <LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">equal</FONT></P> - <LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">different - content</FONT></P> -</UL> <P STYLE="margin-bottom: 0cm"><BR> </P> <H3 CLASS="western"><FONT FACE="Tahoma, sans-serif">Symbolic link @@ -151,7 +125,7 @@ called symlinks or soft links):</FONT></P> <P ALIGN=LEFT STYLE="margin-right: 0.98cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>Note</B></FONT></P> <LI><P ALIGN=LEFT STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">Under Windows the symbolic link options apply to all symbolic links, - <I>"volume mount points" </I>and <I>"NTFS junction + <I>"volume mount points" </I>and <I>"NTFS junction points"</I>.</FONT></P> <LI><P ALIGN=LEFT STYLE="margin-right: 0.98cm; margin-bottom: 0cm; font-style: normal"> <FONT FACE="Tahoma, sans-serif">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 @@ <HEAD> <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=windows-1252"> <TITLE></TITLE> - <META NAME="GENERATOR" CONTENT="OpenOffice.org 3.2 (Win32)"> + <META NAME="GENERATOR" CONTENT="OpenOffice.org 3.4.1 (Win32)"> <META NAME="CREATED" CONTENT="20091208;20054200"> - <META NAME="CHANGED" CONTENT="20120511;23112900"> + <META NAME="CHANGED" CONTENT="20130116;18385122"> <META NAME="Info 1" CONTENT=""> <META NAME="Info 2" CONTENT=""> <META NAME="Info 3" CONTENT=""> @@ -29,10 +29,10 @@ saving time </SPAN><SPAN STYLE="font-style: normal"><SPAN STYLE="font-weight: no common problem synchronization software has to deal with are +-1 hour file time shifts after a Daylight Saving Time (DST) switch has occurred. This can be observed for example when a FAT-formatted -volume is compared against an NTFS volume as frequently happening -with USB memory sticks. Files that previously appeared to be in sync -are now shown with an one hour modification time offset, although -they have not been modified by the user or by other means.</FONT></P> +volume is compared against an NTFS volume as frequently happens with +USB memory sticks. Files that previously appeared to be in sync are +now shown with a one hour modification time offset, although they +have not been modified by the user or by other means.</FONT></P> <P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">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 @@ <HEAD> <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=windows-1252"> <TITLE></TITLE> - <META NAME="GENERATOR" CONTENT="OpenOffice.org 3.2 (Win32)"> + <META NAME="GENERATOR" CONTENT="OpenOffice.org 3.4.1 (Win32)"> <META NAME="CREATED" CONTENT="20091206;16574000"> - <META NAME="CHANGED" CONTENT="20120511;23112100"> + <META NAME="CHANGED" CONTENT="20130109;13371476"> <META NAME="Info 1" CONTENT=""> <META NAME="Info 2" CONTENT=""> <META NAME="Info 3" CONTENT=""> @@ -44,7 +44,7 @@ Exclude items for m<SPAN STYLE="font-weight: normal">irror-sync from <SPAN STYLE="font-weight: normal">to </SPAN><FONT FACE="Courier New, monospace"><SPAN STYLE="font-weight: normal">D:\Target</SPAN></FONT></FONT></P> <P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen1" DIR="LTR" STYLE="float: left; width: 80%; height: 0.14cm; border: none; padding: 0cm; background: #e6e6e6"> <P STYLE="margin-left: 0.82cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-weight: normal">Single - file </SPAN></FONT><FONT FACE="Courier New, monospace">C:\Source\file.txt:<BR></FONT> <FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-weight: normal">Exclude: + file </SPAN></FONT><FONT FACE="Courier New, monospace">C:\Source\file.txt:<BR></FONT> <FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-weight: normal">Exclude: </SPAN></FONT><FONT FACE="Courier New, monospace">\file.txt</FONT></P> <P STYLE="margin-left: 0.82cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-weight: normal">Single folder </SPAN></FONT><FONT FACE="Courier New, monospace">C:\Source\subfolder:<BR></FONT> <FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-weight: normal">Exclude: @@ -64,7 +64,7 @@ Exclude items for m<SPAN STYLE="font-weight: normal">irror-sync from in their path:<BR></SPAN></FONT> <FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-weight: normal">Exclude: </SPAN></FONT><FONT FACE="Courier New, monospace">*temp*</FONT></P> <P ALIGN=LEFT STYLE="margin-left: 0.82cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-weight: normal">Multiple - entries separated by semicolon:<BR></SPAN> <SPAN STYLE="font-weight: normal">Exclude: + entries separated by semicolon:<BR></SPAN><FONT FACE="Times New Roman, serif"><SPAN STYLE="font-weight: normal"> </SPAN></FONT><SPAN STYLE="font-weight: normal">Exclude: </SPAN><FONT FACE="Courier New, monospace"><SPAN STYLE="font-weight: normal">*.tmp; *.doc; *.bak</SPAN></FONT></FONT></P> </SPAN><BR CLEAR=LEFT><BR> 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 @@ <HEAD> <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=windows-1252"> <TITLE></TITLE> - <META NAME="GENERATOR" CONTENT="OpenOffice.org 3.2 (Win32)"> + <META NAME="GENERATOR" CONTENT="OpenOffice.org 3.4.1 (Win32)"> <META NAME="CREATED" CONTENT="20091206;16574000"> - <META NAME="CHANGED" CONTENT="20120904;11283300"> + <META NAME="CHANGED" CONTENT="20130122;19292537"> <META NAME="Info 1" CONTENT=""> <META NAME="Info 2" CONTENT=""> <META NAME="Info 3" CONTENT=""> @@ -26,28 +26,28 @@ applications</FONT></FONT></H2> <P STYLE="margin-bottom: 0cm"><BR> </P> <P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">By -default FreeFileSync opens the operating system's standard file -browser on each mouse double-click by invoking "<FONT FACE="Courier New, monospace">explorer +default FreeFileSync opens the operating system's file browser on +each mouse double-click by invoking "<FONT FACE="Courier New, monospace">explorer /select, "%item_path%"</FONT>" on Windows and "<FONT FACE="Courier New, monospace">xdg-open "%item_folder%"</FONT>" on Linux.</FONT></P> <P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">However -you are free to integrate other external applications into -FreeFileSync: navigate to "</FONT><FONT FACE="Tahoma, sans-serif"><I>Menu --> Advanced -> Global settings: External Applications</I></FONT><FONT FACE="Tahoma, sans-serif">" -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:</FONT></P> +you may integrate other external applications into FreeFileSync: +navigate to "</FONT><FONT FACE="Tahoma, sans-serif"><I>Menu -> +Advanced -> Global settings: External Applications</I></FONT><FONT FACE="Tahoma, sans-serif">" +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:</FONT></P> <P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen3" DIR="LTR" STYLE="float: left; width: 80%; height: 0.14cm; border: none; padding: 0cm; background: #e6e6e6"> <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-right: 0.98cm; margin-bottom: 0cm"> <FONT FACE="Courier New, monospace">%item_path% - <FONT FACE="Tahoma, sans-serif">full file or folder name</FONT><BR>%item_folder% - <FONT FACE="Tahoma, sans-serif">folder - part only</FONT><BR>%item2_path% - <FONT FACE="Tahoma, sans-serif">Other - side's counterpart to %item_path%</FONT><BR>%item2_folder% - - <FONT FACE="Tahoma, sans-serif">Other side's counterpart to - %item_folder%</FONT></FONT></P> + part only</FONT><BR>%item2_path% - + <FONT FACE="Tahoma, sans-serif">Counterpart of %item_path% on the + opposite grid</FONT><BR>%item2_folder% - <FONT FACE="Tahoma, sans-serif">Counterpart + of %item_folder% on the opposite grid</FONT></FONT></P> </SPAN><BR CLEAR=LEFT><BR> </P> <P STYLE="margin-bottom: 0cm"><BR> 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.</FONT></P> integrated into the operating system's auto start facility:<BR> <FONT FACE="Courier New, monospace">"C:\Program Files\FreeFileSync\RealtimeSync.exe" - "C:\MyConfig.ffs_real"<BR></FONT> <FONT FACE="Courier New, monospace">"C:\Program + "C:\SyncJob.ffs_real"<BR></FONT> <FONT FACE="Courier New, monospace">"C:\Program Files\FreeFileSync\RealtimeSync.exe" "C:\SyncJob.ffs_batch"</FONT><BR> </FONT></P> <LI><P ALIGN=LEFT STYLE="margin-right: 0.98cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">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 @@ <HEAD> <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=windows-1252"> <TITLE></TITLE> - <META NAME="GENERATOR" CONTENT="OpenOffice.org 3.2 (Win32)"> + <META NAME="GENERATOR" CONTENT="OpenOffice.org 3.4.1 (Win32)"> <META NAME="CREATED" CONTENT="20091206;16574000"> - <META NAME="CHANGED" CONTENT="20120905;21304100"> + <META NAME="CHANGED" CONTENT="20130126;13004654"> <META NAME="Info 1" CONTENT=""> <META NAME="Info 2" CONTENT=""> <META NAME="Info 3" CONTENT=""> @@ -41,7 +41,7 @@ file></SPAN></I></FONT></P> <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-right: 0.98cm; margin-bottom: 0cm"> <FONT FACE="Courier New, monospace">"C:\Program Files\FreeFileSync\RealtimeSync.exe" "C:\some - folder\SyncJob.ffs_batch"</FONT></P> + folder\SyncJob.ffs_real"</FONT></P> </SPAN><BR CLEAR=LEFT> </P> </UL> @@ -52,8 +52,8 @@ file></SPAN></I></FONT></P> should be monitoring only while a specific user is logged in: </SPAN></SPAN>Create a new shortcut, enter the command line from above as target and place it into the user's autostart folder.</FONT></P> - <P STYLE="margin-bottom: 0cm"><IMG SRC="../img/create_shortcut.png" NAME="Grafik3" ALIGN=BOTTOM BORDER=0></P> - <P STYLE="margin-bottom: 0cm"><IMG SRC="../img/shortcut_properties.png" NAME="Grafik4" ALIGN=BOTTOM BORDER=0></P> + <P STYLE="margin-bottom: 0cm"><IMG SRC="../img/create_shortcut.png" NAME="Grafik3" ALIGN=BOTTOM WIDTH=626 HEIGHT=338 BORDER=0></P> + <P STYLE="margin-bottom: 0cm"><IMG SRC="../img/shortcut_properties.png" NAME="Grafik4" ALIGN=BOTTOM WIDTH=377 HEIGHT=451 BORDER=0></P> <P STYLE="margin-bottom: 0cm"> </P> <LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">RealtimeSync should be monitoring while Windows is online irrespective of @@ -63,7 +63,7 @@ file></SPAN></I></FONT></P> a Batch Job</A> 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.</FONT></P> - <P STYLE="margin-bottom: 0cm"><IMG SRC="../img/schedule_realtimesync.png" NAME="Grafik1" ALIGN=BOTTOM BORDER=0></P> + <P STYLE="margin-bottom: 0cm"><IMG SRC="../img/schedule_realtimesync.png" NAME="Grafik1" ALIGN=BOTTOM WIDTH=708 HEIGHT=284 BORDER=0></P> </OL> <P STYLE="margin-bottom: 0cm"><BR> </P> 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</FONT></FONT></H2> <LI><P STYLE="margin-bottom: 0cm; font-weight: normal"><FONT FACE="Tahoma, sans-serif">Enter "Run:" as: <FONT FACE="Courier New, monospace"><FreeFileSync installation directory>\FreeFileSync.exe <job - name>.ffs_batch</FONT><BR><IMG SRC="../img/ScheduleBatch.gif" NAME="Grafik2" ALIGN=BOTTOM BORDER=0><BR> </FONT></P> + name>.ffs_batch</FONT><BR><IMG SRC="../img/ScheduleBatch.png" NAME="Grafik2" ALIGN=BOTTOM BORDER=0><BR> </FONT></P> </UL> <LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>Ubuntu Linux Gnome-schedule:</B></FONT></P> diff --git a/BUILD/Help/img/ScheduleBatch.gif b/BUILD/Help/img/ScheduleBatch.gif Binary files differdeleted file mode 100644 index 20d27d6d..00000000 --- a/BUILD/Help/img/ScheduleBatch.gif +++ /dev/null diff --git a/BUILD/Help/img/ScheduleBatch.png b/BUILD/Help/img/ScheduleBatch.png Binary files differnew file mode 100644 index 00000000..c991d1fd --- /dev/null +++ b/BUILD/Help/img/ScheduleBatch.png diff --git a/BUILD/Help/img/create_shortcut.png b/BUILD/Help/img/create_shortcut.png Binary files differindex fba73ec1..2c42f3a1 100644 --- a/BUILD/Help/img/create_shortcut.png +++ b/BUILD/Help/img/create_shortcut.png diff --git a/BUILD/Help/img/schedule_realtimesync.png b/BUILD/Help/img/schedule_realtimesync.png Binary files differindex 285804b4..ac1e2a80 100644 --- a/BUILD/Help/img/schedule_realtimesync.png +++ b/BUILD/Help/img/schedule_realtimesync.png diff --git a/BUILD/Help/img/shortcut_properties.png b/BUILD/Help/img/shortcut_properties.png Binary files differindex c801f6d3..1e06b617 100644 --- a/BUILD/Help/img/shortcut_properties.png +++ b/BUILD/Help/img/shortcut_properties.png diff --git a/BUILD/Help/img/win7scheduler.png b/BUILD/Help/img/win7scheduler.png Binary files differindex 08314d16..93f1e020 100644 --- a/BUILD/Help/img/win7scheduler.png +++ b/BUILD/Help/img/win7scheduler.png 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 @@ +<header> + <language name>العربية</language name> + <translator>Awadh Al-Ghaamdi</translator> + <locale>ar</locale> + <flag file>arabic.png</flag file> + <plural forms>6</plural forms> + <plural definition>n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5</plural definition> +</header> + +<source>Searching for folder %x...</source> +<target>البحث عن المجلد %x...</target> + +<source>Items processed:</source> +<target>معالجة العناصر:</target> + +<source>Items remaining:</source> +<target>العناصر المتبقية:</target> + +<source>Total time:</source> +<target>مجموع الوقت:</target> + +<source>Show in Explorer</source> +<target>إظهار في مستكشف</target> + +<source>Open with default application</source> +<target>فتح باستخدام التطبيق الافتراضي</target> + +<source>Browse directory</source> +<target>تصفح الدليل</target> + +<source>Abort requested: Waiting for current operation to finish...</source> +<target>إحباط وطلبت: في انتظار انتهاء العملية الحالية...</target> + +<source>Failure to create timestamp for versioning:</source> +<target>فشل إنشاء الطابع الزمني للإصدارات:</target> + +<source>RealtimeSync - Automated Synchronization</source> +<target>المزامنة التلقائية</target> + +<source>Error</source> +<target>خطأ</target> + +<source>Select alternate comparison settings</source> +<target>حدد إعدادات المقارنة البديل</target> + +<source>Select alternate synchronization settings</source> +<target>حدد إعدادات المزامنة البديل</target> + +<source>Filter is active</source> +<target>عامل التصفية نشط</target> + +<source>No filter selected</source> +<target>لا يوجد عامل تصفية مختارة</target> + +<source>Remove alternate settings</source> +<target>إزالة إعدادات البديل</target> + +<source>Clear filter settings</source> +<target>مسح إعدادات عامل التصفية</target> + +<source>Save as batch job</source> +<target>حفظ كمهمة دفعية</target> + +<source>Comparison settings</source> +<target>إعدادات المقارنة</target> + +<source>Synchronization settings</source> +<target>إعدادات المزامنة</target> + +<source>About</source> +<target>حول</target> + +<source>Confirm</source> +<target>تأكيد</target> + +<source>Configure filter</source> +<target>تكوين عامل تصفية</target> + +<source>Global settings</source> +<target>الإعدادات العامة</target> + +<source>Find</source> +<target>بحث</target> + +<source>Select time span</source> +<target>حدد الفترة الزمنية</target> + +<source>Invalid command line:</source> +<target>سطر الأوامر غير صالح:</target> + +<source>Info</source> +<target>معلومات</target> + +<source>Warning</source> +<target>تحذير</target> + +<source>Fatal Error</source> +<target>خطأ فادح</target> + +<source>Windows Error Code %x:</source> +<target>رمز خطأ Windows %x:</target> + +<source>Linux Error Code %x:</source> +<target>رمز الخطأ لينكس %x:</target> + +<source>Cannot resolve symbolic link %x.</source> +<target>لا يمكن حل الارتباط الرمزي %x.</target> + +<source>%x MB</source> +<target>%x مب</target> + +<source>%x KB</source> +<target>%x ك. بايت</target> + +<source>%x GB</source> +<target>%x GB</target> + +<source> +<pluralform>1 Byte</pluralform> +<pluralform>%x Bytes</pluralform> +</source> +<target></target> + +<source>Database file %x is incompatible.</source> +<target>ملف قاعدة البيانات %x غير متوافق.</target> + +<source>Initial synchronization:</source> +<target>المزامنة الأولية:</target> + +<source>Database file %x does not yet exist.</source> +<target>ملف قاعدة البيانات %x غير موجود حتى الآن.</target> + +<source>Database file is corrupt:</source> +<target>ملف قاعدة البيانات معطوبة:</target> + +<source>Out of memory!</source> +<target>نفاد في الذاكرة</target> + +<source>Cannot write file %x.</source> +<target>لا يمكن كتابة ملف %x.</target> + +<source>Cannot read file %x.</source> +<target>لا يمكن قراءة الملف %x.</target> + +<source>Database files do not share a common session.</source> +<target>عدم مشاركة ملفات قاعدة بيانات جلسة عمل مشتركة.</target> + +<source>An exception occurred!</source> +<target>حدث استثناء</target> + +<source>Cannot read file attributes of %x.</source> +<target>لا يمكن قراءة سمات الملف من %x.</target> + +<source>Cannot get process information.</source> +<target>لا يمكن الحصول على معلومات عملية.</target> + +<source>Waiting while directory is locked (%x)...</source> +<target>في انتظار أثناء الدليل مؤمناً (%x)...</target> + +<source>Cannot set directory lock %x.</source> +<target>لا يمكن تعيين الدليل قفل %x.</target> + +<source> +<pluralform>1 sec</pluralform> +<pluralform>%x sec</pluralform> +</source> +<target></target> + +<source>Error parsing file %x, row %y, column %z.</source> +<target>حدث خطأ أثناء تحليل ملف %x، صف %y، وعمود %z.</target> + +<source>Scanning:</source> +<target>المسح الضوئي:</target> + +<source>Encoding extended time information: %x</source> +<target>ترميز المعلومات تمديد الوقت: %x</target> + +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> + +<source>/sec</source> +<target>/ثانية</target> + +<source>Cannot find file %x.</source> +<target>لا يمكن العثور على الملف %x.</target> + +<source>File %x does not contain a valid configuration.</source> +<target>لا يحتوي ملف %x تكويناً صحيحاً.</target> + +<source>Configuration file %x loaded partially only.</source> +<target>تحميل ملف التكوين %x جزئيا فقط.</target> + +<source>Cannot access Volume Shadow Copy Service.</source> +<target>لا يمكن الوصول إلى "خدمة نسخة الظل لوحدة التخزين".</target> + +<source>Please use FreeFileSync 64-bit version to create shadow copies on this system.</source> +<target>الرجاء استخدام الإصدار البرنامج 64-بت لإنشاء ملفات الظل الاحتياطية على هذا النظام.</target> + +<source>Cannot load file %x.</source> +<target>لا يمكن تحميل الملف %x.</target> + +<source>Path %x does not contain a volume name.</source> +<target>المسار %x لا يحتوي على اسم وحدة تخزين.</target> + +<source>Volume name %x not part of file name %y!</source> +<target>اسم وحدة التخزين ليست جزءا من اسم الملف %x</target> + +<source>Cannot read the following XML elements:</source> +<target>لا يمكن قراءة عناصر XML التالية:</target> + +<source>&Open...</source> +<target>فتح...</target> + +<source>Save &as...</source> +<target>حفظ باسم...</target> + +<source>&Quit</source> +<target>إنهاء</target> + +<source>&Program</source> +<target>البرنامج</target> + +<source>&Content</source> +<target>المحتوى</target> + +<source>&About</source> +<target>حول</target> + +<source>&Help</source> +<target>تعليمات</target> + +<source>Usage:</source> +<target>الاستخدام:</target> + +<source>1. Select folders to watch.</source> +<target>1. حدد المجلدات لمشاهدة.</target> + +<source>2. Enter a command line.</source> +<target>2. إدخال سطر الأوامر.</target> + +<source>3. Press 'Start'.</source> +<target>3. اضغط على 'ابدأ'.</target> + +<source>To get started just import a .ffs_batch file.</source> +<target>للحصول على بدء تشغيل الاستيراد فقط ملف .ffs_batch.</target> + +<source>Folders to watch</source> +<target>المجلدات لمشاهدة</target> + +<source>Add folder</source> +<target>إضافة مجلد</target> + +<source>Remove folder</source> +<target>إزالة مجلد</target> + +<source>Browse</source> +<target>تصفح</target> + +<source>Select a folder</source> +<target>حدد مجلد</target> + +<source>Idle time [seconds]</source> +<target>وقت الخمول [ثانية]</target> + +<source>Idle time between last detected change and execution of command</source> +<target>وقت الخمول بين آخر تغيير تم الكشف عنها وتنفيذ الأوامر</target> + +<source>Command line</source> +<target>سطر الأوامر</target> + +<source> +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) +</source> +<target> +يتم تشغيل الأمر إذا: +-تغيير الملفات أو المجلدات الفرعية +--يصل إلى المجلدات الجديدة (مثل إدراج عصا الناقل التسلسلي العام) +</target> + +<source>Start</source> +<target>بدء تشغيل</target> + +<source>&Retry</source> +<target>إعادة المحاولة</target> + +<source>Cancel</source> +<target>إلغاء الأمر</target> + +<source>Build: %x</source> +<target>بناء: %x</target> + +<source>All files</source> +<target>جميع الملفات</target> + +<source>&Restore</source> +<target>استعادة</target> + +<source>&Exit</source> +<target>خروج</target> + +<source>Monitoring active...</source> +<target>الرصد النشط...</target> + +<source>Waiting for missing directories...</source> +<target>في انتظار الدلائل مفقود...</target> + +<source>A folder input field is empty.</source> +<target>حقل إدخال مجلد فارغ.</target> + +<source>Synchronization aborted!</source> +<target>تم إحباط المزامنة</target> + +<source>Synchronization completed with errors!</source> +<target>تزامن إكمال مع وجود خطأ!</target> + +<source>Synchronization completed with warnings.</source> +<target>تزامن الانتهاء مع التحذيرات.</target> + +<source>Nothing to synchronize!</source> +<target>لا شيء لمزامنة</target> + +<source>Synchronization completed successfully.</source> +<target>تمت المزامنة بنجاح</target> + +<source>Saving log file %x...</source> +<target>حفظ ملف السجل %x...</target> + +<source>Press "Switch" to resolve issues in FreeFileSync main dialog.</source> +<target>اضغط على "التبديل" لحل القضايا في الحوار الرئيسي البرنامج.</target> + +<source>Switching to FreeFileSync main dialog...</source> +<target>التحول إلى الحوار الرئيسي البرنامج...</target> + +<source>A new version of FreeFileSync is available:</source> +<target>يتوفر إصدار جديد من البرنامج:</target> + +<source>Download now?</source> +<target>تحميل الآن؟</target> + +<source>FreeFileSync is up to date!</source> +<target>البرنامج هو الأحدث حتى الآن!</target> + +<source>Information</source> +<target>معلومات</target> + +<source>Unable to connect to sourceforge.net!</source> +<target>غير قادر على الاتصال إلى sourceforge.net!</target> + +<source>Current FreeFileSync version number was not found online! Do you want to check manually?</source> +<target>لم يتم العثور على رقم الإصدار الحالي من البرنامج على الإنترنت! هل تريد التحقق يدوياً؟</target> + +<source>Do you want FreeFileSync to automatically check for updates every week?</source> +<target>هل تريد التحقق تلقائياً من وجود تحديثات كل أسبوع؟</target> + +<source>(Requires an Internet connection!)</source> +<target>(يتطلب اتصال إنترنت)!</target> + +<source><Symlink></source> +<target><Symlink></target> + +<source><Folder></source> +<target><المجلد></target> + +<source>Full path</source> +<target>المسار الكامل</target> + +<source>Name</source> +<target>الاسم</target> + +<source>Relative path</source> +<target>مسار نسبي</target> + +<source>Base folder</source> +<target>المجلد الأساسي</target> + +<source>Size</source> +<target>الحجم</target> + +<source>Date</source> +<target>تاريخ</target> + +<source>Extension</source> +<target>ملحق</target> + +<source>Size:</source> +<target>الحجم:</target> + +<source>Date:</source> +<target>التاريخ:</target> + +<source>Action</source> +<target>عمل</target> + +<source>Category</source> +<target>الفئة</target> + +<source>Drag && drop</source> +<target>سحب إفلات</target> + +<source>Close progress dialog</source> +<target>تقدم وثيقة الحوار</target> + +<source>Standby</source> +<target>وضع الاستعداد</target> + +<source>Log off</source> +<target>تسجيل الخروج</target> + +<source>Shut down</source> +<target>إيقاف التشغيل</target> + +<source>Hibernate</source> +<target>السبات</target> + +<source>&New</source> +<target>الجديد</target> + +<source>&Save</source> +<target>حفظ</target> + +<source>Save as &batch job...</source> +<target>حفظ كمهمة دفعية...</target> + +<source>1. &Compare</source> +<target>1-مقارنة</target> + +<source>2. &Synchronize</source> +<target>2. مزامنة</target> + +<source>&Language</source> +<target>اللغة</target> + +<source>&Global settings...</source> +<target>الإعدادات العمومية...</target> + +<source>&Export file list...</source> +<target>تصدير قائمة ملف...</target> + +<source>&Advanced</source> +<target>متقدم</target> + +<source>&Check for new version</source> +<target>التحقق من الإصدار الجديد</target> + +<source>Compare</source> +<target>قارن</target> + +<source>Compare both sides</source> +<target>المقارنة بين كلا الجانبين</target> + +<source>&Abort</source> +<target>إحباط</target> + +<source>Synchronize</source> +<target>مزامنة</target> + +<source>Start synchronization</source> +<target>بدء تشغيل المزامنة</target> + +<source>Add folder pair</source> +<target>إضافة مجلد زوج</target> + +<source>Remove folder pair</source> +<target>إزالة المجلد الثاني</target> + +<source>Swap sides</source> +<target>مبادلة الجانبين</target> + +<source>Open</source> +<target>فتح</target> + +<source>Save</source> +<target>حفظ</target> + +<source>Last used configurations (press DEL to remove from list)</source> +<target>آخر استخدام تكوينات (اضغط DEL لإزالة من القائمة)</target> + +<source>Hide excluded items</source> +<target>إخفاء العناصر المستبعدة</target> + +<source>Show filtered or temporarily excluded files</source> +<target>عرض تمت تصفيتها أو مؤقتاً استبعاد الملفات</target> + +<source>Number of files and folders that will be created</source> +<target>عدد من الملفات والمجلدات التي سيتم إنشاؤها</target> + +<source>Number of files that will be overwritten</source> +<target>عدد الملفات التي سيتم الكتابة فوق</target> + +<source>Number of files and folders that will be deleted</source> +<target>عدد من الملفات والمجلدات التي سيتم حذفها</target> + +<source>Total bytes to copy</source> +<target>إجمالي وحدات البايت لنسخ</target> + +<source>Items found:</source> +<target>العناصر التي تم العثور عليها:</target> + +<source>Speed:</source> +<target>السرعة:</target> + +<source>Time remaining:</source> +<target>الوقت المتبقي:</target> + +<source>Time elapsed:</source> +<target>الوقت المنقضي:</target> + +<source>Batch job</source> +<target>مهمة المجموعة</target> + +<source>Help</source> +<target>تعليمات</target> + +<source>Error handling</source> +<target>معالجة الأخطاء</target> + +<source>Ignore</source> +<target>تجاهل</target> + +<source>Hide all error and warning messages</source> +<target>إخفاء جميع الخطأ ورسائل تحذير</target> + +<source>Pop-up</source> +<target>إطار منبثق</target> + +<source>Show pop-up on errors or warnings</source> +<target>إظهار المنبثقة عن أخطاء أو تحذيرات</target> + +<source>Exit</source> +<target>خروج</target> + +<source>Abort synchronization on first error</source> +<target>إحباط المزامنة على الخطأ الأول</target> + +<source>On completion</source> +<target>وعند الانتهاء</target> + +<source>Show progress dialog</source> +<target>إظهار التقدم في الحوار</target> + +<source>Save log</source> +<target>حفظ سجل</target> + +<source>Select folder to save log files</source> +<target>حدد مجلداً لحفظ ملفات السجل</target> + +<source>Limit</source> +<target>الحد الأقصى</target> + +<source>Limit maximum number of log files</source> +<target>تقييد الحد الأقصى لعدد ملفات السجل</target> + +<source>Select variant</source> +<target>حدد الخيار</target> + +<source> +Files are found equal if + - last write time and date + - file size +are the same +</source> +<target> +تم العثور على ملفات متساوية إذا + -آخر كتابة الوقت والتاريخ + -حجم الملف +هي نفسها +</target> + +<source>File time and size</source> +<target>وقت الملف والحجم</target> + +<source> +Files are found equal if + - file content +is the same +</source> +<target> +تم العثور على ملفات متساوية إذا + --محتوى الملف +هو نفسه +</target> + +<source>File content</source> +<target>محتوى الملف</target> + +<source>Symbolic Link handling</source> +<target>التعامل مع ارتباط رمزي</target> + +<source>OK</source> +<target>حسنا</target> + +<source><- Two way -></source> +<target><- إتجاهان -></target> + +<source>Identify and propagate changes on both sides using a database. Deletions, renaming and conflicts are detected automatically.</source> +<target>تحديد ونشر التغييرات في كلا الجانبين باستخدام قاعدة بيانات. عمليات حذف وإعادة تسمية والصراعات هي الكشف عن تلقائياً.</target> + +<source>Mirror ->></source> +<target>مرآة->></target> + +<source>Mirror backup of left folder. Right folder is modified to exactly match left folder after synchronization.</source> +<target>مرآة احتياطية من المجلد الأيسر. يتم تعديل المجلد الصحيح بالضبط مطابقة المجلد الأيسر بعد المزامنة.</target> + +<source>Update -></source> +<target>تحديث-></target> + +<source>Copy new or updated files to right folder.</source> +<target>نسخ الملفات الجديدة أو المحدثة إلى المجلد الصحيح.</target> + +<source>Custom</source> +<target>مخصص</target> + +<source>Configure your own synchronization rules.</source> +<target>تكوين قواعد المزامنة الخاصة بك.</target> + +<source>Deletion handling</source> +<target>التعامل مع الحذف</target> + +<source>Permanent</source> +<target>الدائمة</target> + +<source>Delete or overwrite files permanently</source> +<target>حذف أو الكتابة فوق الملفات بشكل دائم</target> + +<source>Recycle Bin</source> +<target>سلة المحذوفات</target> + +<source>Use Recycle Bin for deleted and overwritten files</source> +<target>استخدام "سلة المحذوفات" للملفات المحذوفة والكتابة</target> + +<source>Versioning</source> +<target>تعيين الإصدار</target> + +<source>Move time-stamped files into specified folder</source> +<target>نقل ختم الوقت الملفات في المجلد المحدد</target> + +<source>Naming convention:</source> +<target>اصطلاح التسمية:</target> + +<source>Configuration</source> +<target>التكوين</target> + +<source>Item exists on left side only</source> +<target>العنصر موجود على الجانب الأيسر فقط</target> + +<source>Item exists on right side only</source> +<target>العنصر موجود في الجانب الأيمن فقط</target> + +<source>Left side is newer</source> +<target>الجانب الأيسر الأحدث</target> + +<source>Right side is newer</source> +<target>الجانب الأيمن الأحدث</target> + +<source>Items have different content</source> +<target>وقد عناصر المحتوى المختلفة</target> + +<source>Conflict/item cannot be categorized</source> +<target>لا يمكن أن تصنف الصراع/الصنف</target> + +<source>Synchronizing...</source> +<target>مزامنة...</target> + +<source>&Pause</source> +<target>إيقاف مؤقت</target> + +<source>Source code written in C++ using:</source> +<target>منتديات عميد التعريب :</target> + +<source>If you like FreeFileSync</source> +<target>المصدر الأول لتعريب البرامج في العالم العربي</target> + +<source>Donate with PayPal</source> +<target>منتديات عميد التعريب</target> + +<source>Many thanks for localization:</source> +<target>شكرا جزيلا للتعريب:</target> + +<source>Feedback and suggestions are welcome</source> +<target>تعليقات واقتراحات موضع ترحيب</target> + +<source>Homepage</source> +<target>الصفحة الرئيسية</target> + +<source>FreeFileSync at Sourceforge</source> +<target>البرنامج في سورس</target> + +<source>Email</source> +<target>البريد الإلكتروني</target> + +<source>Published under the GNU General Public License</source> +<target>نشر تحت رخصة جنو العمومية العامة</target> + +<source>Delete on both sides</source> +<target>حذف على كلا الجانبين</target> + +<source>Delete on both sides even if the file is selected on one side only</source> +<target>حذف على كلا الجانبين حتى إذا تم تحديد الملف على جانب واحد فقط</target> + +<source> +Only files that match all filter settings will be synchronized. +Note: File names must be relative to base directories! +</source> +<target> +وسوف تكون متزامنة فقط الملفات التي تطابق كافة إعدادات عامل التصفية. +ملاحظة: يجب أن تكون أسماء الملفات بالنسبة إلى قاعدة الدلائل! +</target> + +<source>Include</source> +<target>وتشمل</target> + +<source>Exclude</source> +<target>استبعاد</target> + +<source>Time span</source> +<target>الفترة الزمنية</target> + +<source>File size</source> +<target>حجم الملف</target> + +<source>Minimum</source> +<target>الحد الأدنى</target> + +<source>Maximum</source> +<target>الحد الأقصى</target> + +<source>&Default</source> +<target>الافتراضي</target> + +<source>Fail-safe file copy</source> +<target>نسخ ملف آمنة من الفشل</target> + +<source>Write to a temporary file (*.ffs_tmp) first then rename it. This guarantees a consistent state even in case of fatal error.</source> +<target>الكتابة إلى ملف مؤقت (*.ffs_tmp) أولاً، ثم إعادة تسميته. وهذا يضمن حالة متناسقة حتى في حالة حدوث خطأ فادح.</target> + +<source>Copy locked files</source> +<target>نسخ الملفات المقفلة</target> + +<source>Copy shared or locked files using Volume Shadow Copy Service (Requires Administrator rights)</source> +<target>نسخ الملفات المشتركة أو مؤمن باستخدام "خدمة النسخ الاحتياطي لوحدة التخزين" (حقوق المسؤول يتطلب)</target> + +<source>Copy file access permissions</source> +<target>نسخ أذونات الوصول إلى الملف</target> + +<source>Transfer file and folder permissions (Requires Administrator rights)</source> +<target>نقل أذونات الملفات والمجلدات (حقوق المسؤول يتطلب)</target> + +<source>Restore hidden dialogs</source> +<target>استعادة الحوارات الخفية</target> + +<source>External applications</source> +<target>تطبيقات خارجية</target> + +<source>Description</source> +<target>الوصف</target> + +<source>Variant</source> +<target>البديل</target> + +<source>Statistics</source> +<target>الإحصاءات</target> + +<source>Don't show this dialog again</source> +<target>عدم إظهار هذا الحوار مرة أخرى</target> + +<source>Find what:</source> +<target>بحث عن:</target> + +<source>Match case</source> +<target>مطابقة حالة الأحرف</target> + +<source>&Find next</source> +<target>بحث عن التالي</target> + +<source>Operation aborted!</source> +<target>تم إحباط العملية!</target> + +<source>Main bar</source> +<target>شريط الرئيسي</target> + +<source>Folder pairs</source> +<target>أزواج المجلد</target> + +<source>Overview</source> +<target>نظرة عامة</target> + +<source>Filter files</source> +<target>تصفية الملفات</target> + +<source>Select view</source> +<target>حدد طريقة العرض</target> + +<source>Set direction:</source> +<target>تحديد الاتجاه:</target> + +<source>Exclude temporarily</source> +<target>استبعاد مؤقتاً</target> + +<source>Include temporarily</source> +<target>وتشمل مؤقتاً</target> + +<source>Exclude via filter:</source> +<target>استبعاد عن طريق التصفية:</target> + +<source><multiple selection></source> +<target><تحديد متعدد></target> + +<source>Delete</source> +<target>حذف</target> + +<source>Include all</source> +<target>وتشمل جميع</target> + +<source>Exclude all</source> +<target>استبعاد الكل</target> + +<source>Show icons:</source> +<target>إظهار رموز:</target> + +<source>Small</source> +<target>صغيرة</target> + +<source>Medium</source> +<target>متوسطة</target> + +<source>Large</source> +<target>كبيرة</target> + +<source>Select time span...</source> +<target>حدد الفترة الزمنية...</target> + +<source>Default view</source> +<target>طريقة العرض الافتراضية</target> + +<source>Show "%x"</source> +<target>إظهار "%x"</target> + +<source><Last session></source> +<target><آخر جلسة></target> + +<source>Folder Comparison and Synchronization</source> +<target>مقارنة بين مجلد وتزامن</target> + +<source>Configuration saved!</source> +<target>حفظ التكوين!</target> + +<source>FreeFileSync batch</source> +<target>دفعة البرنامج</target> + +<source>Never save changes</source> +<target>ابدأ بحفظ التغييرات</target> + +<source>Do you want to save changes to %x?</source> +<target>هل تريد حفظ التغييرات إلى %x؟</target> + +<source>Do&n't save</source> +<target>لا تحفظ</target> + +<source>Configuration loaded!</source> +<target>تحميل تكوين!</target> + +<source>Hide files that exist on left side only</source> +<target>إخفاء الملفات الموجودة على الجانب الأيسر فقط</target> + +<source>Show files that exist on left side only</source> +<target>إظهار الملفات الموجودة على الجانب الأيسر فقط</target> + +<source>Hide files that exist on right side only</source> +<target>إخفاء الملفات الموجودة على الجانب الأيمن فقط</target> + +<source>Show files that exist on right side only</source> +<target>إظهار الملفات الموجودة على الجانب الأيمن فقط</target> + +<source>Hide files that are newer on left</source> +<target>إخفاء الملفات التي الأحدث على اليسار</target> + +<source>Show files that are newer on left</source> +<target>إظهار الملفات التي الأحدث على اليسار</target> + +<source>Hide files that are newer on right</source> +<target>إخفاء الملفات التي الأحدث في حق</target> + +<source>Show files that are newer on right</source> +<target>إظهار الملفات التي الأحدث في حق</target> + +<source>Hide files that are equal</source> +<target>إخفاء الملفات التي على قدم المساواة</target> + +<source>Show files that are equal</source> +<target>إظهار الملفات التي على قدم المساواة</target> + +<source>Hide files that are different</source> +<target>إخفاء ملفات مختلفة</target> + +<source>Show files that are different</source> +<target>إظهار ملفات مختلفة</target> + +<source>Hide conflicts</source> +<target>إخفاء الصراعات</target> + +<source>Show conflicts</source> +<target>إظهار الصراعات</target> + +<source>Hide files that will be created on the left side</source> +<target>إخفاء الملفات التي سيتم إنشاؤها على الجانب الأيسر</target> + +<source>Show files that will be created on the left side</source> +<target>إظهار الملفات التي سيتم إنشاؤها على الجانب الأيسر</target> + +<source>Hide files that will be created on the right side</source> +<target>إخفاء الملفات التي سيتم إنشاؤها على الجانب الأيمن</target> + +<source>Show files that will be created on the right side</source> +<target>إظهار الملفات التي سيتم إنشاؤها على الجانب الأيمن</target> + +<source>Hide files that will be deleted on the left side</source> +<target>إخفاء الملفات التي سيتم حذفها على الجانب الأيسر</target> + +<source>Show files that will be deleted on the left side</source> +<target>إظهار الملفات التي سيتم حذفها من على الجانب الأيسر</target> + +<source>Hide files that will be deleted on the right side</source> +<target>إخفاء الملفات التي سيتم حذفها على الجانب الأيمن</target> + +<source>Show files that will be deleted on the right side</source> +<target>إظهار الملفات التي سيتم حذفها على الجانب الأيمن</target> + +<source>Hide files that will be overwritten on left side</source> +<target>إخفاء الملفات التي سيتم الكتابة فوق على الجانب الأيسر</target> + +<source>Show files that will be overwritten on left side</source> +<target>إظهار الملفات التي سيتم الكتابة فوق على الجانب الأيسر</target> + +<source>Hide files that will be overwritten on right side</source> +<target>إخفاء الملفات التي سيتم الكتابة فوق الجانب الأيمن</target> + +<source>Show files that will be overwritten on right side</source> +<target>إظهار الملفات التي سيتم الكتابة فوق الجانب الأيمن</target> + +<source>Hide files that won't be copied</source> +<target>إخفاء الملفات التي لن يتم نسخها</target> + +<source>Show files that won't be copied</source> +<target>إظهار الملفات التي لن يتم نسخها</target> + +<source>All folders are in sync!</source> +<target>تكون الكل المجلدات متزامنة</target> + +<source>Comma separated list</source> +<target>قائمة مفصولة بفواصل</target> + +<source>Legend</source> +<target>أسطورة</target> + +<source>File list exported!</source> +<target>تصدير قائمة الملف</target> + +<source> +<pluralform>Object deleted successfully!</pluralform> +<pluralform>%x objects deleted successfully!</pluralform> +</source> +<target></target> + +<source> +<pluralform>1 directory</pluralform> +<pluralform>%x directories</pluralform> +</source> +<target></target> + +<source> +<pluralform>1 file</pluralform> +<pluralform>%x files</pluralform> +</source> +<target></target> + +<source> +<pluralform>%x of 1 row in view</pluralform> +<pluralform>%x of %y rows in view</pluralform> +</source> +<target></target> + +<source>Ignore further errors</source> +<target>تجاهل المزيد من الأخطاء</target> + +<source>&Ignore</source> +<target>تجاهل</target> + +<source>&Switch</source> +<target>رمز التبديل</target> + +<source>Question</source> +<target>سؤال</target> + +<source>&Yes</source> +<target>نعم</target> + +<source>&No</source> +<target>لا</target> + +<source>Scanning...</source> +<target>المسح الضوئي...</target> + +<source>Comparing content...</source> +<target>مقارنة المحتوى...</target> + +<source>Copy</source> +<target>نسخة</target> + +<source>Paused</source> +<target>تم إيقاف مؤقتاً</target> + +<source>Initializing...</source> +<target>شمل...</target> + +<source>Aborted</source> +<target>تم إحباط</target> + +<source>Completed</source> +<target>أكملت</target> + +<source>Continue</source> +<target>مواصلة</target> + +<source>Pause</source> +<target>إيقاف مؤقت</target> + +<source>Logging</source> +<target>تسجيل الدخول</target> + +<source>Cannot find %x</source> +<target>لا يمكن العثور على %x</target> + +<source>Inactive</source> +<target>غير نشط</target> + +<source>Today</source> +<target>اليوم</target> + +<source>This week</source> +<target>هذا الأسبوع</target> + +<source>This month</source> +<target>هذا الشهر</target> + +<source>This year</source> +<target>هذه السنة</target> + +<source>Last x days</source> +<target>الماضية X الأيام</target> + +<source>Byte</source> +<target>بايت</target> + +<source>KB</source> +<target>كب</target> + +<source>MB</source> +<target>مب</target> + +<source>Filter</source> +<target>عامل التصفية</target> + +<source>Direct</source> +<target>مباشر</target> + +<source>Follow</source> +<target>اتبع</target> + +<source>Copy NTFS permissions</source> +<target>نسخ أذونات NTFS</target> + +<source>Integrate external applications into context menu. The following macros are available:</source> +<target>دمج تطبيقات خارجية في قائمة السياق. كما تتوفر وحدات الماكرو التالية:</target> + +<source>- full file or folder name</source> +<target>-اسم الملف أو المجلد كامل</target> + +<source>- folder part only</source> +<target>--مجلد جزء فقط</target> + +<source>- Other side's counterpart to %item_path%</source> +<target>-النظيرة الجانب الأخرى إلى %item_path %</target> + +<source>- Other side's counterpart to %item_folder%</source> +<target>-النظيرة الجانب الأخرى إلى %item_folder %</target> + +<source>Make hidden warnings and dialogs visible again?</source> +<target>جعل التحذيرات الخفية وحوارات مرئية مرة أخرى؟</target> + +<source> +<pluralform>Do you really want to move the following object to the Recycle Bin?</pluralform> +<pluralform>Do you really want to move the following %x objects to the Recycle Bin?</pluralform> +</source> +<target></target> + +<source> +<pluralform>Do you really want to delete the following object?</pluralform> +<pluralform>Do you really want to delete the following %x objects?</pluralform> +</source> +<target></target> + +<source>Leave as unresolved conflict</source> +<target>ترك كالصراعات التي لم تحل</target> + +<source>Replace</source> +<target>استبدال</target> + +<source>Move files and replace if existing</source> +<target>نقل الملفات واستبدال إذا كان موجود</target> + +<source>Append a timestamp to each file name</source> +<target>إلحاق طابع زمني لكل ملف اسم</target> + +<source>Folder</source> +<target>مجلد</target> + +<source>File</source> +<target>الملف</target> + +<source>YYYY-MM-DD hhmmss</source> +<target>Hhmmss YYYY-MM-DD</target> + +<source>Files</source> +<target>الملفات</target> + +<source>Percentage</source> +<target>النسبة المئوية</target> + +<source>Cannot monitor directory %x.</source> +<target>لا يمكن رصد الدليل %x.</target> + +<source>Conversion error:</source> +<target>خطأ تحويل:</target> + +<source>Cannot delete file %x.</source> +<target>لا يمكن حذف الملف %x.</target> + +<source>The file is locked by another process:</source> +<target>الملف مؤمن من قبل عملية أخرى:</target> + +<source>Cannot move file %x to %y.</source> +<target>لا يمكن نقل الملف %x إلى %y.</target> + +<source>Cannot delete directory %x.</source> +<target>لا يمكن حذف الدليل %x.</target> + +<source>Cannot write file attributes of %x.</source> +<target>لا يمكن كتابة سمات الملف من %x.</target> + +<source>Cannot write modification time of %x.</source> +<target>لا يمكن كتابة وقت التعديل من %x.</target> + +<source>Cannot find system function %x.</source> +<target>لا يمكن العثور على وظيفة نظام %x.</target> + +<source>Cannot read security context of %x.</source> +<target>لا يمكن قراءة سياق الأمان من %x.</target> + +<source>Cannot write security context of %x.</source> +<target>لا يمكن كتابة سياق الأمان من %x.</target> + +<source>Cannot read permissions of %x.</source> +<target>لا يمكن قراءة أذونات %x.</target> + +<source>Cannot write permissions of %x.</source> +<target>لا يمكن كتابة أذونات %x.</target> + +<source>Cannot create directory %x.</source> +<target>لا يمكن إنشاء الدليل %x.</target> + +<source>Cannot copy symbolic link %x to %y.</source> +<target>لا يمكن نسخ الارتباط الرمزي %x إلى %y.</target> + +<source>Cannot copy file %x to %y.</source> +<target>لا يمكن نسخ الملف %x إلى %y.</target> + +<source>Type of item %x is not supported:</source> +<target>نوع عنصر %x غير معتمد:</target> + +<source>Cannot open directory %x.</source> +<target>لا يمكن فتح الدليل %x.</target> + +<source>Cannot enumerate directory %x.</source> +<target>لا يمكن تعداد الدليل %x.</target> + +<source>Detected endless directory recursion.</source> +<target>تم الكشف عن العودية الدليل التي لا نهاية لها.</target> + +<source>%x TB</source> +<target>%x السل</target> + +<source>%x PB</source> +<target>PB %x</target> + +<source>%x%</source> +<target>%%x</target> + +<source> +<pluralform>1 min</pluralform> +<pluralform>%x min</pluralform> +</source> +<target></target> + +<source> +<pluralform>1 hour</pluralform> +<pluralform>%x hours</pluralform> +</source> +<target></target> + +<source> +<pluralform>1 day</pluralform> +<pluralform>%x days</pluralform> +</source> +<target></target> + +<source>Cannot set privilege %x.</source> +<target>لا يمكن تعيين امتياز %x.</target> + +<source>Unable to move %x to the Recycle Bin!</source> +<target>غير قادر على نقل %x إلى "سلة المحذوفات"</target> + +<source>Both sides have changed since last synchronization!</source> +<target>كلا الجانبين قد تغيرت منذ المزامنة الأخيرة</target> + +<source>Cannot determine sync-direction:</source> +<target>لا يمكن تحديد الاتجاه المزامنة:</target> + +<source>No change since last synchronization!</source> +<target>أي تغيير منذ المزامنة الأخيرة</target> + +<source>The corresponding database entries are not in sync considering current settings.</source> +<target>إدخالات قاعدة البيانات المطابقة ليست في تزامن النظر في الإعدادات الحالية.</target> + +<source>Setting default synchronization directions: Old files will be overwritten with newer files.</source> +<target>الإعداد الافتراضي في الاتجاهين المزامنة: ستتم الكتابة فوق الملفات القديمة بالملفات الأحدث.</target> + +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target>سلة المهملات غير متوفر للمسارات التالية! سيتم حذف الملفات بشكل دائم بدلاً من ذلك:</target> + +<source>You can ignore this error to consider the folder as empty.</source> +<target>يمكنك تجاهل هذا الخطأ للنظر في مجلد فارغ.</target> + +<source>Cannot find folder %x.</source> +<target>لا يمكن العثور على المجلد %x.</target> + +<source>Directories are dependent! Be careful when setting up synchronization rules:</source> +<target>تعتمد الدلائل! كن حذراً عند إعداد قواعد المزامنة:</target> + +<source>Start comparison</source> +<target>مقارنة ابدأ</target> + +<source>Preparing synchronization...</source> +<target>إعداد المزامنة...</target> + +<source>Conflict detected:</source> +<target>تم الكشف عن تعارض:</target> + +<source>File %x has an invalid date!</source> +<target>يحتوي الملف %x تاريخ غير صالح</target> + +<source>Files %x have the same date but a different size!</source> +<target>ملفات %x بنفس التاريخ ولكن حجم مختلف</target> + +<source>Items differ in attributes only</source> +<target>تختلف عناصر في سمات فقط</target> + +<source>Symbolic links %x have the same date but a different target.</source> +<target>الارتباطات الرمزية %x لها نفس التاريخ لكن هدفا مختلفاً.</target> + +<source>Comparing content of files %x</source> +<target>مقارنة محتويات ملفات %x</target> + +<source>Comparing files by content failed.</source> +<target>مقارنة الملفات حسب المحتوى التي فشلت.</target> + +<source>Generating file list...</source> +<target>إنشاء قائمة الملفات...</target> + +<source>Both sides are equal</source> +<target>كلا الجانبين على قدم المساواة</target> + +<source>Copy new item to left</source> +<target>نسخ عنصر جديد إلى اليسار</target> + +<source>Copy new item to right</source> +<target>نسخ عنصر جديد إلى اليمين</target> + +<source>Delete left item</source> +<target>حذف العنصر الأيسر</target> + +<source>Delete right item</source> +<target>حذف البند الصحيح</target> + +<source>Move file on left</source> +<target>نقل ملف على اليسار</target> + +<source>Move file on right</source> +<target>نقل ملف على اليمين</target> + +<source>Overwrite left item</source> +<target>الكتابة فوق العنصر الأيسر</target> + +<source>Overwrite right item</source> +<target>الكتابة فوق العنصر الصحيح</target> + +<source>Do nothing</source> +<target>لا تفعل شيئا</target> + +<source>Update attributes on left</source> +<target>تحديث سمات على اليسار</target> + +<source>Update attributes on right</source> +<target>تحديث سمات على حق</target> + +<source>Multiple...</source> +<target>متعددة...</target> + +<source>Deleting file %x</source> +<target>حذف الملف %x</target> + +<source>Deleting folder %x</source> +<target>حذف المجلد %x</target> + +<source>Deleting symbolic link %x</source> +<target>حذف الارتباط الرمزي %x</target> + +<source>Moving file %x to recycle bin</source> +<target>نقل ملف %x سلة المحذوفات</target> + +<source>Moving folder %x to recycle bin</source> +<target>نقل المجلد %x سلة المحذوفات</target> + +<source>Moving symbolic link %x to recycle bin</source> +<target>نقل الارتباط الرمزي %x سلة المحذوفات</target> + +<source>Moving file %x to %y</source> +<target>نقل ملف %x إلى %y</target> + +<source>Moving folder %x to %y</source> +<target>نقل المجلد %x إلى %y</target> + +<source>Moving symbolic link %x to %y</source> +<target>نقل الارتباط الرمزي %x إلى %y</target> + +<source>Removing old versions...</source> +<target>إزالة الإصدارات القديمة...</target> + +<source>Creating file %x</source> +<target>إنشاء ملف %x</target> + +<source>Creating symbolic link %x</source> +<target>إنشاء ارتباط رمزي %x</target> + +<source>Creating folder %x</source> +<target>إنشاء مجلد %x</target> + +<source>Overwriting file %x</source> +<target>الكتابة فوق الملف %x</target> + +<source>Overwriting symbolic link %x</source> +<target>الكتابة فوق الارتباط الرمزي %x</target> + +<source>Verifying file %x</source> +<target>التحقق من ملف %x</target> + +<source>Updating attributes of %x</source> +<target>تحديث سمات %x</target> + +<source>Target folder %x already existing.</source> +<target>المجلد الهدف %x الموجودة بالفعل.</target> + +<source>Target folder input field must not be empty.</source> +<target>يجب أن لا يكون الحقل الإدخال المجلد الهدف فارغة.</target> + +<source>Folder input field for versioning must not be empty.</source> +<target>حقل الإدخال المجلد لتعيين الإصدار ويجب أن لا يكون فارغاً.</target> + +<source>Source folder %x not found.</source> +<target>المجلد المصدر %x لم يتم العثور على.</target> + +<source>The following items have unresolved conflicts and will not be synchronized:</source> +<target>البنود التالية دون حل الصراعات، وسوف لا تكون متزامنة:</target> + +<source>Significant difference detected:</source> +<target>تم الكشف عن فرق كبير:</target> + +<source>More than 50% of the total number of files will be copied or deleted!</source> +<target>أكثر من 50% من إجمالي عدد الملفات التي سيتم نسخها أو حذف</target> + +<source>Not enough free disk space available in:</source> +<target>غير كافية مساحة القرص الحرة المتوفرة في:</target> + +<source>Required:</source> +<target>مطلوب:</target> + +<source>Available:</source> +<target>متاح:</target> + +<source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> +<target>سيتم تعديل مجلد الذي هو جزء من أزواج المجلد متعددة. الرجاء مراجعة إعدادات المزامنة.</target> + +<source>Left</source> +<target>اليسار</target> + +<source>Right</source> +<target>حق</target> + +<source>Synchronizing folder pair:</source> +<target>مزامنة مجلد زوج:</target> + +<source>Generating database...</source> +<target>إنشاء قاعدة بيانات...</target> + +<source>Data verification error: Source and target file have different content!</source> +<target>بيانات التحقق خطأ: الملف المصدر والهدف يكون محتوى مختلفة</target> + diff --git a/BUILD/Languages/chinese_simple.lng b/BUILD/Languages/chinese_simple.lng index 57458c14..5c431691 100644 --- a/BUILD/Languages/chinese_simple.lng +++ b/BUILD/Languages/chinese_simple.lng @@ -31,8 +31,8 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>请求中止: 正在等待当前操作完成...</target> -<source>Failure to create time stamp for versioning:</source> -<target></target> +<source>Failure to create timestamp for versioning:</source> +<target>未能为历史版本创建时间标记</target> <source>RealtimeSync - Automated Synchronization</source> <target>实时同步 - 自动同步</target> @@ -59,7 +59,7 @@ <target>清除过滤器设置</target> <source>Save as batch job</source> -<target></target> +<target>另存为批处理作业</target> <source>Comparison settings</source> <target>比较设置</target> @@ -269,7 +269,7 @@ <target>选择一个文件夹</target> <source>Idle time [seconds]</source> -<target></target> +<target>空闲时间[秒]</target> <source>Idle time between last detected change and execution of command</source> <target>最后检测到改变和命令执行之间的空闲时间</target> @@ -325,16 +325,16 @@ The command is triggered if: <target>同步已完成但有错误.</target> <source>Synchronization completed with warnings.</source> -<target></target> +<target>同步已完成但有警告.</target> <source>Nothing to synchronize!</source> <target>没有什么可同步!</target> <source>Synchronization completed successfully.</source> -<target></target> +<target>同步成功完成.</target> <source>Saving log file %x...</source> -<target></target> +<target>正在保存日志文件 %x...</target> <source>Press "Switch" to resolve issues in FreeFileSync main dialog.</source> <target>请按下"切换"来解决 FreeFileSync主对话框上的问题.</target> @@ -358,7 +358,7 @@ The command is triggered if: <target>无法链接到 Sourceforge.net!</target> <source>Current FreeFileSync version number was not found online! Do you want to check manually?</source> -<target></target> +<target>当前FreeFileSync版本号未能在线找到! 要手工检查更新吗?</target> <source>Do you want FreeFileSync to automatically check for updates every week?</source> <target>要让 FreeFileSync 保持每周检查一次更新吗?</target> @@ -430,7 +430,7 @@ The command is triggered if: <target>保存(&S)</target> <source>Save as &batch job...</source> -<target></target> +<target>另存为批处理作业(&b)</target> <source>1. &Compare</source> <target>1. 比较(&C)</target> @@ -478,16 +478,16 @@ The command is triggered if: <target>两侧互换</target> <source>Open</source> -<target></target> +<target>打开</target> <source>Save</source> -<target></target> +<target>保存</target> <source>Last used configurations (press DEL to remove from list)</source> <target>最后使用的配置(按DEL键将其从列表中移除)</target> <source>Hide excluded items</source> -<target></target> +<target>隐藏排除项目</target> <source>Show filtered or temporarily excluded files</source> <target>显示已被过滤或被临时排除的文件</target> @@ -547,13 +547,13 @@ The command is triggered if: <target>在第一个错误时中止同步</target> <source>On completion</source> -<target></target> +<target>在完成时</target> <source>Show progress dialog</source> <target>显示进度对话框</target> <source>Save log</source> -<target></target> +<target>保存日志</target> <source>Select folder to save log files</source> <target>选择要保存日志文件的文件夹</target> @@ -565,7 +565,7 @@ The command is triggered if: <target>限制日志文件的量大个数</target> <source>Select variant</source> -<target></target> +<target>选择不同方式</target> <source> Files are found equal if @@ -604,7 +604,7 @@ is the same <target>确定</target> <source><- Two way -></source> -<target></target> +<target><-双向-></target> <source>Identify and propagate changes on both sides using a database. Deletions, renaming and conflicts are detected automatically.</source> <target>使用一个数据库来识别和传播两边的改变. 删除,重命名和冲突会自动检测.</target> @@ -640,7 +640,7 @@ is the same <target>回收站</target> <source>Use Recycle Bin for deleted and overwritten files</source> -<target></target> +<target>将删除和覆盖文件放到回收站</target> <source>Versioning</source> <target>保留历史版本</target> @@ -649,7 +649,7 @@ is the same <target>移动时间标记文件到指定的文件夹</target> <source>Naming convention:</source> -<target></target> +<target>命名规则:</target> <source>Configuration</source> <target>配置</target> @@ -678,7 +678,7 @@ is the same <source>&Pause</source> <target>暂停(&P)</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>源代码用如下C++工具写成:</target> <source>If you like FreeFileSync</source> @@ -1026,7 +1026,7 @@ Note: File names must be relative to base directories! <target>正在比较文件内容...</target> <source>Copy</source> -<target></target> +<target>复制</target> <source>Paused</source> <target>已暂停</target> @@ -1101,13 +1101,13 @@ Note: File names must be relative to base directories! <target>- 只对文件夹部分</target> <source>- Other side's counterpart to %item_path%</source> -<target></target> +<target>- 另一边的对应项到 %item_path%</target> <source>- Other side's counterpart to %item_folder%</source> -<target></target> +<target>- 另一边的对应项到 %item_folder%</target> <source>Make hidden warnings and dialogs visible again?</source> -<target></target> +<target>重新让已经隐藏的警告和对话框变为可见?</target> <source> <pluralform>Do you really want to move the following object to the Recycle Bin?</pluralform> @@ -1129,22 +1129,22 @@ Note: File names must be relative to base directories! <target>遗留为未解决的冲突</target> <source>Replace</source> -<target></target> +<target>替换</target> <source>Move files and replace if existing</source> -<target></target> +<target>移动文件, 若文件已存在则替换</target> <source>Append a timestamp to each file name</source> -<target></target> +<target>附加时间戳到每一个文件名</target> <source>Folder</source> -<target></target> +<target>文件夹</target> <source>File</source> -<target></target> +<target>文件</target> <source>YYYY-MM-DD hhmmss</source> -<target></target> +<target>YYYY-MM-DD hhmmss</target> <source>Files</source> <target>文件</target> @@ -1201,7 +1201,7 @@ Note: File names must be relative to base directories! <target>无法复制文件 %x 到 %y.</target> <source>Type of item %x is not supported:</source> -<target></target> +<target>%x 的类型不被支持:</target> <source>Cannot open directory %x.</source> <target>无法打开目录 %x.</target> @@ -1375,7 +1375,7 @@ Note: File names must be relative to base directories! <target>正在移动符号连接 %x 到 %y</target> <source>Removing old versions...</source> -<target></target> +<target>移除旧版本</target> <source>Creating file %x</source> <target>正在创建文件 %x</target> @@ -1411,7 +1411,7 @@ Note: File names must be relative to base directories! <target>无法找到源文件夹 %x.</target> <source>The following items have unresolved conflicts and will not be synchronized:</source> -<target></target> +<target>如下项目有无法解决的冲突并将不会被同步:</target> <source>Significant difference detected:</source> <target>已侦测到显著不同:</target> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>中止請求:正在等待目前操作完成...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>無法新建時間戳記的版本控制:</target> <source>RealtimeSync - Automated Synchronization</source> @@ -669,7 +669,7 @@ is the same <source>&Pause</source> <target>暫停(&P)</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>使用C++編寫的原始碼</target> <source>If you like FreeFileSync</source> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Prekid zahtjevan: čekam da se trenutna akcija završi...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>Nije moguća vremenska oznaka za varijantu</target> <source>RealtimeSync - Automated Synchronization</source> @@ -684,7 +684,7 @@ jednak <source>&Pause</source> <target>&Pauziraj</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Izvorni kod napisan u C++ uz korištenje:</target> <source>If you like FreeFileSync</source> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Požadavek na přerušení: Čekání na ukončení aktuální operace...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>Chyba při vytvoření časové značky verzování:</target> <source>RealtimeSync - Automated Synchronization</source> @@ -684,7 +684,7 @@ je stejný <source>&Pause</source> <target>&Pauza</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Zdrojový kód byl napsán kompletně v C++ pomocí:</target> <source>If you like FreeFileSync</source> 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 @@ <header> <language name>Dansk</language name> - <translator>Kim Monberg</translator> + <translator>Regmos</translator> <locale>da_DK</locale> <flag file>denmark.png</flag file> <plural forms>2</plural forms> @@ -11,61 +11,61 @@ <target>Søger efter mappen %x...</target> <source>Items processed:</source> -<target>Enheder behandlet:</target> +<target>Emner behandlet:</target> <source>Items remaining:</source> -<target>Enheder tilbage:</target> +<target>Emner tilbage:</target> <source>Total time:</source> <target>Samlet tid:</target> <source>Show in Explorer</source> -<target>Vis i Explorer</target> +<target>Åben filplacering</target> <source>Open with default application</source> -<target>Åben med standard program</target> +<target>Åben med standardprogram</target> <source>Browse directory</source> -<target>Gennemse Bibliotek</target> +<target>Gennemse mappe</target> <source>Abort requested: Waiting for current operation to finish...</source> -<target>Afbrydelse: Venter på nuværende opgave afsluttes...</target> +<target>Afbrydelse: Venter på aktuel opgave afsluttes...</target> -<source>Failure to create time stamp for versioning:</source> -<target></target> +<source>Failure to create timestamp for versioning:</source> +<target>Kunne ikke oprette versioneringstid:</target> <source>RealtimeSync - Automated Synchronization</source> -<target>RealtimeSynk - Automatisk Synkronisering</target> +<target>Realtids synk - automatisk synkronisering</target> <source>Error</source> <target>Fejl</target> <source>Select alternate comparison settings</source> -<target>Vælg alternative sammenlignings indstillinger</target> +<target>Tilpas analyse</target> <source>Select alternate synchronization settings</source> -<target>Vælg alternative indstillinger</target> +<target>Tilpas synkronisering</target> <source>Filter is active</source> -<target>Filter er aktivt</target> +<target>Aktivt filter</target> <source>No filter selected</source> <target>Intet filter valgt</target> <source>Remove alternate settings</source> -<target>Fjern alternative indstillinger</target> +<target>Fjern tilpassede indstillinger</target> <source>Clear filter settings</source> -<target>Ryd filter indstillinger</target> +<target>Nulstil filter</target> <source>Save as batch job</source> -<target></target> +<target>Gem som batchfil</target> <source>Comparison settings</source> -<target>Sammenlignings indstillinger</target> +<target>Analyseindstillinger</target> <source>Synchronization settings</source> -<target>Synkroniserings indstillinger</target> +<target>Synkroniseringsindstillinger</target> <source>About</source> <target>Om</target> @@ -77,13 +77,13 @@ <target>Indstil filter</target> <source>Global settings</source> -<target>Fælles indstillinger</target> +<target>Programindstillinger</target> <source>Find</source> -<target>Find</target> +<target>Søg</target> <source>Select time span</source> -<target>Vælg tids område</target> +<target>Vælg tidsinterval</target> <source>Invalid command line:</source> <target>Ugyldig kommando:</target> @@ -95,16 +95,16 @@ <target>Advarsel</target> <source>Fatal Error</source> -<target>Uoprettelig Fejl</target> +<target>Fatal fejl</target> <source>Windows Error Code %x:</source> -<target>Windows Fejl kode %x:</target> +<target>Windows fejlkode %x:</target> <source>Linux Error Code %x:</source> -<target>Linux Fejl kode %x:</target> +<target>Linux fejlkode %x:</target> <source>Cannot resolve symbolic link %x.</source> -<target>Kan ikke følge linket %x.</target> +<target>Kan ikke følge symlinket %x.</target> <source>%x MB</source> <target>%x MB</target> @@ -120,48 +120,48 @@ <pluralform>%x Bytes</pluralform> </source> <target> -<pluralform>1 Byte</pluralform> -<pluralform>%x Bytes</pluralform> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> </target> <source>Database file %x is incompatible.</source> -<target>Database filen %x er ikke kompatibel.</target> +<target>Databasefilen %x er inkompatibel.</target> <source>Initial synchronization:</source> -<target>Indledende synkronisering:</target> +<target>Forbereder synkronisering:</target> <source>Database file %x does not yet exist.</source> -<target>Database filen %x findes ikke.</target> +<target>Databasefilen %x findes ikke.</target> <source>Database file is corrupt:</source> -<target>Database filen er ødelagt:</target> +<target>Databasefilen er ødelagt:</target> <source>Out of memory!</source> -<target>Ude af hukommelse!</target> +<target>Ikke nok hukommelse!</target> <source>Cannot write file %x.</source> -<target>Kan ikke skrive filen %x.</target> +<target>Kan ikke oprette filen %x.</target> <source>Cannot read file %x.</source> <target>Kan ikke læse filen %x.</target> <source>Database files do not share a common session.</source> -<target>Database filen kan ikke deles.</target> +<target>Databasefiler kan ikke dele handling.</target> <source>An exception occurred!</source> -<target>En undtagelse er opstået!</target> +<target>Undtagelse opstod!</target> <source>Cannot read file attributes of %x.</source> -<target>Kan ikke læse fil attributterne på %x.</target> +<target>Kan ikke læse filattributterne på %x.</target> <source>Cannot get process information.</source> -<target>Kan ikke lave informationer over processen.</target> +<target>Kan ikke hente procesinformation.</target> <source>Waiting while directory is locked (%x)...</source> -<target>Venter mens biblioteket er låst (%x)...</target> +<target>Venter mens mappe låses (%x)...</target> <source>Cannot set directory lock %x.</source> -<target>Kan ikke låse biblioteket %x.</target> +<target>Kan ikke låse mappen %x.</target> <source> <pluralform>1 sec</pluralform> @@ -173,21 +173,21 @@ </target> <source>Error parsing file %x, row %y, column %z.</source> -<target>Fejl i videregivelse af filen %x, række %y, kolonne %z.</target> +<target>Behandlingsfejl i filen %x, række %y, kolonne %z.</target> <source>Scanning:</source> <target>Skanner:</target> <source>Encoding extended time information: %x</source> -<target>Finder udvidet tids information: %x</target> +<target>Opretter udvidet tidsinformation: %x</target> <source> <pluralform>[1 Thread]</pluralform> <pluralform>[%x Threads]</pluralform> </source> <target> -<pluralform>[1 Tråd]</pluralform> -<pluralform>[%x Tråde]</pluralform> +<pluralform>[1 tråd]</pluralform> +<pluralform>[%x tråde]</pluralform> </target> <source>/sec</source> @@ -197,67 +197,67 @@ <target>Kan ikke finde filen %x.</target> <source>File %x does not contain a valid configuration.</source> -<target>%x indeholder ikke en gyldig konfiguration.</target> +<target>%x indeholder ikke gyldige indstillinger.</target> <source>Configuration file %x loaded partially only.</source> -<target>Konfigurationsfilen %x er kun delvist hentet.</target> +<target>Indstillingsfilen %x er kun delvist indlæst.</target> <source>Cannot access Volume Shadow Copy Service.</source> -<target>Kan ikke få adgang til Drev Kopi Servicen.</target> +<target>Kan ikke opnå adgang til VVS.</target> <source>Please use FreeFileSync 64-bit version to create shadow copies on this system.</source> -<target>Brug venligst FreeFileSync 64-bit version for at lave en drev kopi på dette system.</target> +<target>Brug FreeFileSync 64-bit til at lave VVS kopier på dette system.</target> <source>Cannot load file %x.</source> -<target>Kan ikke læse filen %x.</target> +<target>Kan ikke indlæse filen %x.</target> <source>Path %x does not contain a volume name.</source> -<target>Stien %x indeholder ikke et drev navn.</target> +<target>Stien %x indeholder ikke et drevnavn.</target> <source>Volume name %x not part of file name %y!</source> -<target>Drev navn %x ikke en del af filnavn %y!</target> +<target>Drevnavn %x findes ikke i filnavnet %y!</target> <source>Cannot read the following XML elements:</source> <target>Kan ikke læse følgende XML emner:</target> <source>&Open...</source> -<target>&Åben...</target> +<target>Åben...</target> <source>Save &as...</source> -<target>Gem &som...</target> +<target>Gem som...</target> <source>&Quit</source> -<target>&Afslut</target> +<target>Afslut</target> <source>&Program</source> -<target>&Program</target> +<target>Filer</target> <source>&Content</source> -<target>&Indhold</target> +<target>Åben hjælp</target> <source>&About</source> -<target>&Om</target> +<target>Om</target> <source>&Help</source> -<target>&Hjælp</target> +<target>Hjælp</target> <source>Usage:</source> -<target>Forbrug:</target> +<target>Gør sådan:</target> <source>1. Select folders to watch.</source> -<target>1. Vælg mappe at overvåge.</target> +<target>1. Vælg mapper til jobbet.</target> <source>2. Enter a command line.</source> -<target>2. Skriv en kommando linje.</target> +<target>2. Angiv en kommando.</target> <source>3. Press 'Start'.</source> -<target>3. Tryk 'Start'.</target> +<target>3. Klik 'Start'.</target> <source>To get started just import a .ffs_batch file.</source> -<target>Importer en .ffs_batch for at komme igang.</target> +<target>Importer en .ffs_batchfil (Filer > Åben...) for at komme igang.</target> <source>Folders to watch</source> -<target>Mapper der overvåges</target> +<target>Jobbets mapper</target> <source>Add folder</source> <target>Tilføj mappe</target> @@ -272,13 +272,13 @@ <target>Vælg en mappe</target> <source>Idle time [seconds]</source> -<target></target> +<target>Køres når computeren har ledige resourcer i [sek.]</target> <source>Idle time between last detected change and execution of command</source> -<target>Tid imellem sidste ændring og udførelse</target> +<target>Tid imellem sidst fundne ændring og udførsel</target> <source>Command line</source> -<target>Kommando linje</target> +<target>Kommando</target> <source> The command is triggered if: @@ -288,65 +288,65 @@ The command is triggered if: <target> 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) </target> <source>Start</source> <target>Start</target> <source>&Retry</source> -<target>&Prøv igen</target> +<target>Prøv igen</target> <source>Cancel</source> -<target>Anuller</target> +<target>Annuller</target> <source>Build: %x</source> -<target>Build: %x</target> +<target>Udgivet: %x</target> <source>All files</source> <target>Alle filer</target> <source>&Restore</source> -<target>&Gendan</target> +<target>Vis vindue</target> <source>&Exit</source> -<target>&Afslut</target> +<target>Luk</target> <source>Monitoring active...</source> -<target>Overvågning aktiv...</target> +<target>Overvågning kører...</target> <source>Waiting for missing directories...</source> -<target>Venter på manglende biblioteker...</target> +<target>Venter på manglende mapper...</target> <source>A folder input field is empty.</source> -<target>Et mappe felt er tomt.</target> +<target>Der findes et udefineret mappevalg.</target> <source>Synchronization aborted!</source> <target>Synkronisering afbrudt!</target> <source>Synchronization completed with errors!</source> -<target>Synkronisering afsluttet med fejl!</target> +<target>Synkronisering gennemført med fejl!</target> <source>Synchronization completed with warnings.</source> -<target></target> +<target>Synkronisering gennemført med advarsel</target> <source>Nothing to synchronize!</source> -<target>Intet at synkronisere!</target> +<target>Alt er synkroniseret!</target> <source>Synchronization completed successfully.</source> -<target></target> +<target>Synkronisering gennemført</target> <source>Saving log file %x...</source> -<target></target> +<target>Gemmer rapport %x...</target> <source>Press "Switch" to resolve issues in FreeFileSync main dialog.</source> -<target>Tryk "Skift" for at løse problemer i hoved vinduet.</target> +<target>Klik "Skift" for at løse problemerne fra hovedvinduet.</target> <source>Switching to FreeFileSync main dialog...</source> -<target>Skift til hoved vinduet...</target> +<target>Skifter til hovedvindue...</target> <source>A new version of FreeFileSync is available:</source> -<target>En ny version af FreeFilesyns er tilgængelig:</target> +<target>Opdatering tilgængelig:</target> <source>Download now?</source> <target>Download nu?</target> @@ -358,16 +358,16 @@ Kommandoen udføres hvis: <target>Information</target> <source>Unable to connect to sourceforge.net!</source> -<target>Kan ikke forbinde til sourceforge.net!</target> +<target>Kan ikke kontakte sourceforge.net!</target> <source>Current FreeFileSync version number was not found online! Do you want to check manually?</source> -<target></target> +<target>Den aktuelle version blev ikke fundet! Vil du kontrollere manuelt</target> <source>Do you want FreeFileSync to automatically check for updates every week?</source> -<target>Skal FreeFileSync automatisk checke efter opdateringer hver uge?</target> +<target>Skal FreeFileSync søge efter opdateringer en gang om ugen?</target> <source>(Requires an Internet connection!)</source> -<target>(Kræver en Internet forbindelse!)</target> +<target>(kræver internetforbindelse!)</target> <source><Symlink></source> <target><Symlink></target> @@ -376,16 +376,16 @@ Kommandoen udføres hvis: <target><Mappe></target> <source>Full path</source> -<target>Fuld Sti</target> +<target>Fuld sti</target> <source>Name</source> <target>Navn</target> <source>Relative path</source> -<target>Filsti</target> +<target>Relativ sti</target> <source>Base folder</source> -<target>Grund mappe</target> +<target>Hovedmappe</target> <source>Size</source> <target>Størrelse</target> @@ -394,10 +394,10 @@ Kommandoen udføres hvis: <target>Dato</target> <source>Extension</source> -<target>Udvidelse</target> +<target>Filtype</target> <source>Size:</source> -<target>Størelse:</target> +<target>Størrelse:</target> <source>Date:</source> <target>Dato:</target> @@ -406,13 +406,13 @@ Kommandoen udføres hvis: <target>Handling</target> <source>Category</source> -<target>Kategori</target> +<target>Status</target> <source>Drag && drop</source> -<target>Træk && slip</target> +<target>Træk emner hertil</target> <source>Close progress dialog</source> -<target>Luk dialog boksen</target> +<target>Luk dialogen</target> <source>Standby</source> <target>Standby</target> @@ -427,43 +427,43 @@ Kommandoen udføres hvis: <target>Dvale</target> <source>&New</source> -<target>&Ny</target> +<target>Ny</target> <source>&Save</source> -<target>&Gem</target> +<target>Gem</target> <source>Save as &batch job...</source> -<target></target> +<target>Gem som batchfil</target> <source>1. &Compare</source> -<target>1. &Sammenlign</target> +<target>1. Analyser</target> <source>2. &Synchronize</source> -<target>2. &Synkroniser</target> +<target>2. Synkroniser</target> <source>&Language</source> -<target>&Sprog</target> +<target>Sprog</target> <source>&Global settings...</source> -<target>&Fælles indstillinger...</target> +<target>Programindstillinger...</target> <source>&Export file list...</source> -<target>&Eksporter fil liste...</target> +<target>Eksporter filliste...</target> <source>&Advanced</source> -<target>&Avanceret</target> +<target>Indstillinger</target> <source>&Check for new version</source> -<target>&Kig efter ny version</target> +<target>Søg efter opdatering</target> <source>Compare</source> -<target>Sammenlign</target> +<target>Analyser</target> <source>Compare both sides</source> -<target>Sammenlign begge sider</target> +<target>Analyser begge sider</target> <source>&Abort</source> -<target>&Afbryd</target> +<target>Afbryd</target> <source>Synchronize</source> <target>Synkroniser</target> @@ -472,103 +472,103 @@ Kommandoen udføres hvis: <target>Start synkronisering</target> <source>Add folder pair</source> -<target>Tilføj mappe par</target> +<target>Tilføj mappepar</target> <source>Remove folder pair</source> -<target>Fjern mappe par</target> +<target>Fjern mappepar</target> <source>Swap sides</source> -<target>Byt sider</target> +<target>Byt side</target> <source>Open</source> -<target></target> +<target>Åben</target> <source>Save</source> -<target></target> +<target>Gem job</target> <source>Last used configurations (press DEL to remove from list)</source> -<target>Senest brugte konfigurationer (tryk DEL for at fjerne fra listen)</target> +<target>Senest brugte konfigurationer (tast DEL for at slette)</target> <source>Hide excluded items</source> -<target></target> +<target>Skjul ekskluderede emner</target> <source>Show filtered or temporarily excluded files</source> -<target>Vis filtrerede eller midlertidigt afviste filer</target> +<target>Vis filtrerede eller midlertidigt ekskluderede filer</target> <source>Number of files and folders that will be created</source> -<target>Antallet af filer og mapper der vil blive oprettet</target> +<target>Antal filer og mapper der oprettes</target> <source>Number of files that will be overwritten</source> -<target>Antal filer der vil blive overskrevet</target> +<target>Antal filer der overskrives</target> <source>Number of files and folders that will be deleted</source> -<target>Antal filer og mapper der vil blive slettet</target> +<target>Antal filer og mapper der slettes</target> <source>Total bytes to copy</source> -<target>Samlet antal bytes der skal kopieres</target> +<target>Antal bytes der kopieres</target> <source>Items found:</source> -<target>Enheder fundet:</target> +<target>Emner fundet:</target> <source>Speed:</source> <target>Hastighed:</target> <source>Time remaining:</source> -<target>Tid tilbage:</target> +<target>Resterende tid:</target> <source>Time elapsed:</source> -<target>Tid forbrugt:</target> +<target>Brugt tid:</target> <source>Batch job</source> -<target>Batch job</target> +<target>Batchfil</target> <source>Create a batch file to automate synchronization. Double-click this file or schedule in your system's task planner: FreeFileSync.exe <job name>.ffs_batch</source> -<target>Opret en batch fil for at automatiserer synkronisering. Dobbelt-klik på denne fil eller planlæg den i dit systems opgavestyring: FreeFileSync.exe <opgave navn>.ffs_batch</target> +<target>Opret en batchfil til automatisk synkronisering. Dobbeltklik på filen eller brug systemets opgavestyring: FreeFileSync.exe <opgave navn>.ffs_batch</target> <source>Help</source> <target>Hjælp</target> <source>Error handling</source> -<target>Fejl håndtering</target> +<target>Fejlhåndtering</target> <source>Ignore</source> <target>Ignorer</target> <source>Hide all error and warning messages</source> -<target>Skjul beskeder om fejl og advarsler</target> +<target>Skjul fejlbeskeder og advarsler</target> <source>Pop-up</source> -<target>Pop-up</target> +<target>Besked</target> <source>Show pop-up on errors or warnings</source> -<target>Vis pop-up ved fejl eller advarsler</target> +<target>Vis fejlbeskeder og advarsler</target> <source>Exit</source> -<target>Afslut</target> +<target>Luk</target> <source>Abort synchronization on first error</source> <target>Stop synkronisering ved første fejl</target> <source>On completion</source> -<target></target> +<target>Ved gennemført</target> <source>Show progress dialog</source> -<target>Vis dialog boks</target> +<target>Vis fremskridt</target> <source>Save log</source> -<target></target> +<target>Gem rapport</target> <source>Select folder to save log files</source> -<target>Vælg destinations mappe til log filer</target> +<target>Vælg mappe til rapporter</target> <source>Limit</source> -<target>Grænse</target> +<target>Antal</target> <source>Limit maximum number of log files</source> -<target>Maksimale antal log filer</target> +<target>Begræns antal rapporter</target> <source>Select variant</source> -<target></target> +<target>Vælg type</target> <source> Files are found equal if @@ -577,14 +577,14 @@ Files are found equal if are the same </source> <target> -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 </target> <source>File time and size</source> -<target>Fil tid og størrelse</target> +<target>Størrelse og tid</target> <source> Files are found equal if @@ -592,46 +592,46 @@ Files are found equal if is the same </source> <target> -Filer er ens hvis - - fil indhold -er det samme +Filer betragtes som ens hvis + - filindhold +er identisk </target> <source>File content</source> -<target>Fil indhold</target> +<target>Indhold</target> <source>Symbolic Link handling</source> -<target>Link håndtering</target> +<target>Symlink håndtering</target> <source>OK</source> <target>OK</target> <source><- Two way -></source> -<target></target> +<target>< Tovejs ></target> <source>Identify and propagate changes on both sides using a database. Deletions, renaming and conflicts are detected automatically.</source> -<target>find ændringer på begge sider via. database. Sletninger, omdøbninger og konflikter bliver automatisk fundet.</target> +<target>Find og udbred ændringer på begge sider via en database. Sletninger, omdøbninger og konflikter opdages automatisk.</target> <source>Mirror ->></source> -<target>Spejl ->></target> +<target>Spejling >></target> <source>Mirror backup of left folder. Right folder is modified to exactly match left folder after synchronization.</source> -<target>Spejling af venstre mappe. Højre mappe tilpasses så den ligner den venstre efter synkronisering.</target> +<target>Spejl venstre mappe. Efter synkronisering er højre mappe en kopi af venstre.</target> <source>Update -></source> -<target>Opdater -></target> +<target>Opdater ></target> <source>Copy new or updated files to right folder.</source> -<target>Kopier nye eller opdaterede filer til højre mappe.</target> +<target>Kopier nye og opdaterede filer til højre mappe.</target> <source>Custom</source> -<target>Brugerdefineret</target> +<target>Tilpasset</target> <source>Configure your own synchronization rules.</source> -<target>Konfigurer dine egne synkroniserings regler.</target> +<target>Opret dine egne synkroniseringsregler.</target> <source>Deletion handling</source> -<target>Slette håndtering</target> +<target>Sletning</target> <source>Permanent</source> <target>Permanent</target> @@ -640,61 +640,61 @@ er det samme <target>Slet eller overskriv filer permanent</target> <source>Recycle Bin</source> -<target>Skralsespand</target> +<target>Papirkurv</target> <source>Use Recycle Bin for deleted and overwritten files</source> -<target></target> +<target>Slettede og overskrevne filer flyttes til papirkurv</target> <source>Versioning</source> <target>Versionering</target> <source>Move time-stamped files into specified folder</source> -<target>Flyt tids-stemplede filer til den valgte mappe</target> +<target>Flyt tidsstemplede filer til valgte mappe</target> <source>Naming convention:</source> -<target></target> +<target>Navneregler:</target> <source>Configuration</source> -<target>Konfiguration</target> +<target>Indstilling</target> <source>Item exists on left side only</source> -<target>Enheden findes kun på venstre side</target> +<target>Emnet findes kun på venstre side</target> <source>Item exists on right side only</source> -<target>Enheden findes kun på højre side</target> +<target>Emnet findes kun på højre side</target> <source>Left side is newer</source> -<target>Venstre side er nyest</target> +<target>Venstre er nyest</target> <source>Right side is newer</source> -<target>Højre side er nyest</target> +<target>Højre er nyest</target> <source>Items have different content</source> -<target>Enhederne har forskelligt indhold</target> +<target>Emnerne har forskelligt indhold</target> <source>Conflict/item cannot be categorized</source> -<target>Advarsel/enheden kan ikke kategoriseres</target> +<target>Konflikt/ukendt emne</target> <source>Synchronizing...</source> <target>Synkroniserer...</target> <source>&Pause</source> -<target>&Pause</target> +<target>Pause</target> -<source>Source code written in C++ utilizing:</source> -<target>Source code skrevet i C++:</target> +<source>Source code written in C++ using:</source> +<target>Kildekoden er skrevet i C++ med hjælp fra:</target> <source>If you like FreeFileSync</source> -<target>Hvis du kan lide FreeFileSync</target> +<target>Er du glad for FreeFileSync</target> <source>Donate with PayPal</source> -<target>Doner med PayPal</target> +<target>Donér med PayPal</target> <source>Many thanks for localization:</source> -<target>Mange tak for oversættelse til:</target> +<target>Tak for oversættelse:</target> <source>Feedback and suggestions are welcome</source> -<target>Feedback og forslag er velkomne</target> +<target>Kritik og forslag er meget velkomne</target> <source>Homepage</source> <target>Hjemmeside</target> @@ -703,7 +703,7 @@ er det samme <target>FreeFileSync på Sourceforge</target> <source>Email</source> -<target>E-mail</target> +<target>Email</target> <source>Published under the GNU General Public License</source> <target>Udgivet under GNU General Public Licence</target> @@ -712,28 +712,28 @@ er det samme <target>Slet på begge sider</target> <source>Delete on both sides even if the file is selected on one side only</source> -<target>Slet på begge sider selvom der kun er valgt en side</target> +<target>Slet på begge sider selvom selvom filen kun er valgt på en side</target> <source> Only files that match all filter settings will be synchronized. Note: File names must be relative to base directories! </source> <target> -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! </target> <source>Include</source> <target>Inkluder</target> <source>Exclude</source> -<target>Udeluk</target> +<target>Ekskluder</target> <source>Time span</source> -<target>Tidsrum</target> +<target>Tidsinterval</target> <source>File size</source> -<target>Fil Størrelse</target> +<target>Filstørrelse</target> <source>Minimum</source> <target>Minimum</target> @@ -742,25 +742,25 @@ Note: Fil navne må skal passe til grund bibliotekerne! <target>Maksimum</target> <source>&Default</source> -<target>&Standard</target> +<target>Standard</target> <source>Fail-safe file copy</source> -<target>Fejlsikret fil kopiering</target> +<target>Sikker filkopiering</target> <source>Write to a temporary file (*.ffs_tmp) first then rename it. This guarantees a consistent state even in case of fatal error.</source> -<target>Skriv til en midlertidig fil (*.ffs_tmp) først så omdøb den. Dette garanterer sikkerheden selv ved en kritisk fejl.</target> +<target>Skriv først til en midlertidig fil (*.ffs_tmp) og omdøb derefter. Dette garanterer sikkerheden selv ved fatale fejl.</target> <source>Copy locked files</source> <target>Kopier låste filer</target> <source>Copy shared or locked files using Volume Shadow Copy Service (Requires Administrator rights)</source> -<target>Kopier delte eller låste filer ved hjælp af Drev Kopi Servicen (Kræver Administrator rettigheder)</target> +<target>Kopier delte eller låste filer ved hjælp af VVS (kræver administrative rettigheder)</target> <source>Copy file access permissions</source> -<target>Kopier fil adgangs tilladelser</target> +<target>Kopier adgangstilladelser</target> <source>Transfer file and folder permissions (Requires Administrator rights)</source> -<target>Overfør fil og mappe tilladelser (Kræver Administrator rettigheder)</target> +<target>Overfør fil- og mappetilladelser (kræver administrative rettigheder)</target> <source>Restore hidden dialogs</source> <target>Gendan skjulte dialoger</target> @@ -772,43 +772,43 @@ Note: Fil navne må skal passe til grund bibliotekerne! <target>Beskrivelse</target> <source>Variant</source> -<target>Variation</target> +<target>Jobtype</target> <source>Statistics</source> -<target>Statestik</target> +<target>Statistik</target> <source>Don't show this dialog again</source> -<target>Vis ikke denne dialog boks igen</target> +<target>Vis ikke igen</target> <source>Find what:</source> -<target>Find:</target> +<target>Søg efter:</target> <source>Match case</source> -<target>Sammenlign stor og små bogstaver</target> +<target>Versalfølsom (a/A)</target> <source>&Find next</source> -<target>&Find næste</target> +<target>Find næste</target> <source>Operation aborted!</source> -<target>Operation afbrudt!</target> +<target>Handling afbrudt!</target> <source>Main bar</source> -<target>Hoved værktøjslinjen</target> +<target>Hovedlinie</target> <source>Folder pairs</source> -<target>Mappe par</target> +<target>Mappepar</target> <source>Overview</source> -<target>Overblik</target> +<target>Oversigt</target> <source>Filter files</source> -<target>Filter filer</target> +<target>Filtre</target> <source>Select view</source> -<target>Vælg udseende</target> +<target>Tilpas visning</target> <source>Set direction:</source> -<target>Sæt handlevejen:</target> +<target>Retning:</target> <source>Exclude temporarily</source> <target>Ekskluder midlertidigt</target> @@ -817,10 +817,10 @@ Note: Fil navne må skal passe til grund bibliotekerne! <target>Inkluder midlertidigt</target> <source>Exclude via filter:</source> -<target>Ekskluder via filter:</target> +<target>Ekskluder m. filter:</target> <source><multiple selection></source> -<target><flere valg></target> +<target><vælg flere></target> <source>Delete</source> <target>Slet</target> @@ -835,19 +835,19 @@ Note: Fil navne må skal passe til grund bibliotekerne! <target>Vis ikoner:</target> <source>Small</source> -<target>Lille</target> +<target>Små</target> <source>Medium</source> -<target>Mellem</target> +<target>Medium</target> <source>Large</source> -<target>Stor</target> +<target>Store</target> <source>Select time span...</source> -<target>Vælg tidsområde...</target> +<target>Vælg tidsinterval...</target> <source>Default view</source> -<target>Standard tilstand</target> +<target>Standardvisning</target> <source>Show "%x"</source> <target>Vis "%x"</target> @@ -856,28 +856,28 @@ Note: Fil navne må skal passe til grund bibliotekerne! <target><Sidste opgave></target> <source>Folder Comparison and Synchronization</source> -<target>Mappe sammenligning og synkronisering</target> +<target>Mappeanalyse og synkronisering</target> <source>Configuration saved!</source> -<target>Konfiguration gemt!</target> +<target>Indstillinger gemt!</target> <source>FreeFileSync batch</source> -<target>FreeFileSync batch</target> +<target>FreeFileSync batchfil</target> <source>Never save changes</source> -<target>Gem aldrig ændringer</target> +<target>Gem aldrig</target> <source>Do you want to save changes to %x?</source> -<target>Ønsker du at gemme ændringerne til %x?</target> +<target>Vil du gemme ændringer i %x?</target> <source>Do&n't save</source> <target>Gem ikke</target> <source>Configuration loaded!</source> -<target>Konfiguration hentet!</target> +<target>Indstillinger indlæst!</target> <source>Hide files that exist on left side only</source> -<target>Skjul filder der kun findes på venstre side</target> +<target>Skjul filer der kun findes på venstre side</target> <source>Show files that exist on left side only</source> <target>Vis filer der kun findes på venstre side</target> @@ -889,16 +889,16 @@ Note: Fil navne må skal passe til grund bibliotekerne! <target>Vis filer der kun findes på højre side</target> <source>Hide files that are newer on left</source> -<target>Skjul filer der er nyere til venstre</target> +<target>Skjul nyere filer på venstre side</target> <source>Show files that are newer on left</source> -<target>Vis filer der er nyere til venstre</target> +<target>Vis nyere filer på venstre side</target> <source>Hide files that are newer on right</source> -<target>Skjul filer der er nyere til højre</target> +<target>Skjul nyere filer på højre side</target> <source>Show files that are newer on right</source> -<target>Vis filer der er nyere til højre</target> +<target>Vis nyere filer på højre side</target> <source>Hide files that are equal</source> <target>Skjul ens filer</target> @@ -919,66 +919,66 @@ Note: Fil navne må skal passe til grund bibliotekerne! <target>Vis konflikter</target> <source>Hide files that will be created on the left side</source> -<target>Skjul filer der vil blive oprettet til venstre</target> +<target>Skjul filer der oprettes på venstre side</target> <source>Show files that will be created on the left side</source> -<target>Vis filer der vil blive oprettet til venstre</target> +<target>Vis filer der oprettes på venstre side</target> <source>Hide files that will be created on the right side</source> -<target>Skjul filer der vil blive oprettet til højre</target> +<target>Skjul filer der oprettes på højre side</target> <source>Show files that will be created on the right side</source> -<target>Vis filer der vil blive oprettet til højre</target> +<target>Vis filer der oprettes på højre side</target> <source>Hide files that will be deleted on the left side</source> -<target>Skjul filer der bliver slettet til venstre</target> +<target>Skjul filer der slettes på venstre side</target> <source>Show files that will be deleted on the left side</source> -<target>Vis filer der bliver slettet til venstre</target> +<target>Vis filer der slettes på venstre side</target> <source>Hide files that will be deleted on the right side</source> -<target>Skjul filer der bliver slettet til højre</target> +<target>Skjul filer der slettes på højre side</target> <source>Show files that will be deleted on the right side</source> -<target>Vis filer der bliver slettet til højre</target> +<target>Vis filer der slettes på højre side</target> <source>Hide files that will be overwritten on left side</source> -<target>Skjul filer der bliver overskrevet til venstre</target> +<target>Skjul filer der overskrives på venstre side</target> <source>Show files that will be overwritten on left side</source> -<target>Vis filer der bliver overskrevet til venstre</target> +<target>Vis filer der overskrives på venstre side</target> <source>Hide files that will be overwritten on right side</source> -<target>Skjul filer der bliver overskrevet til højre</target> +<target>Skjul filer der overskrives på højre side</target> <source>Show files that will be overwritten on right side</source> -<target>Vis filer der bliver overskrevet til højre</target> +<target>Vis filer der overskrives på højre side</target> <source>Hide files that won't be copied</source> -<target>Skjul filer der ikke bliver kopieret</target> +<target>Skjul filer der ikke kopieres</target> <source>Show files that won't be copied</source> -<target>Vis filer der ikke bliver kopieret</target> +<target>Vis filer der ikke kopieres</target> <source>All folders are in sync!</source> -<target>Alle mapper er synkroniserede</target> +<target>Alle mapper er synkrone</target> <source>Comma separated list</source> -<target>Komma separeret list</target> +<target>Kommaopdelt liste</target> <source>Legend</source> -<target>Legend</target> +<target>Definitioner</target> <source>File list exported!</source> -<target>Fil listen er eksporteret!</target> +<target>Fillisten blev eksporteret!</target> <source> <pluralform>Object deleted successfully!</pluralform> <pluralform>%x objects deleted successfully!</pluralform> </source> <target> -<pluralform>Emnet er slettet!</pluralform> -<pluralform>%x emner er slettet!</pluralform> +<pluralform>Emnet blev slettet!</pluralform> +<pluralform>%x emner blev slettet!</pluralform> </target> <source> @@ -986,8 +986,8 @@ Note: Fil navne må skal passe til grund bibliotekerne! <pluralform>%x directories</pluralform> </source> <target> -<pluralform>1 bibliotek</pluralform> -<pluralform>%x biblioteker</pluralform> +<pluralform>1 mappe</pluralform> +<pluralform>%x mapper</pluralform> </target> <source> @@ -1012,40 +1012,40 @@ Note: Fil navne må skal passe til grund bibliotekerne! <target>Ignorer fremtidige fejl</target> <source>&Ignore</source> -<target>&Ignorer</target> +<target>Ignorer</target> <source>&Switch</source> -<target>&Skift</target> +<target>Skift</target> <source>Question</source> <target>Spørgsmål</target> <source>&Yes</source> -<target>&Ja</target> +<target>Ja</target> <source>&No</source> -<target>&Nej</target> +<target>Nej</target> <source>Scanning...</source> <target>Skanner...</target> <source>Comparing content...</source> -<target>Sammenligner indhold...</target> +<target>Analyserer indhold...</target> <source>Copy</source> -<target></target> +<target>Kopier</target> <source>Paused</source> <target>Pauset</target> <source>Initializing...</source> -<target>Initialiserer...</target> +<target>Forbereder...</target> <source>Aborted</source> <target>Afbrudt</target> <source>Completed</source> -<target>Fuldført</target> +<target>Gennemført</target> <source>Continue</source> <target>Fortsæt</target> @@ -1054,7 +1054,7 @@ Note: Fil navne må skal passe til grund bibliotekerne! <target>Pause</target> <source>Logging</source> -<target>Logger</target> +<target>Rapporter</target> <source>Cannot find %x</source> <target>Kan ikke finde %x</target> @@ -1099,30 +1099,30 @@ Note: Fil navne må skal passe til grund bibliotekerne! <target>Kopier NTFS tilladelser</target> <source>Integrate external applications into context menu. The following macros are available:</source> -<target>Integrere eksterne programme. Følgende macroer er mulige:</target> +<target>Integrer eksterne programmer i kontekstmenu. Brug følgende macroer:</target> <source>- full file or folder name</source> -<target>- fulde fil eller mappe navn</target> +<target>Komplet fil- eller mappenavn</target> <source>- folder part only</source> -<target>- kun mappe delen</target> +<target>Kun mappedelen</target> <source>- Other side's counterpart to %item_path%</source> -<target></target> +<target>Modsat sides pendant til %item_path%</target> <source>- Other side's counterpart to %item_folder%</source> -<target></target> +<target>Modsat sides pendant til %item_folder%</target> <source>Make hidden warnings and dialogs visible again?</source> -<target></target> +<target>Vis skjulte advarsler og dialoger igen?</target> <source> <pluralform>Do you really want to move the following object to the Recycle Bin?</pluralform> <pluralform>Do you really want to move the following %x objects to the Recycle Bin?</pluralform> </source> <target> -<pluralform>Vil du flytte emnet til skraldespanden?</pluralform> -<pluralform>Vil du flytte følgende %x emner til skraldespanden?</pluralform> +<pluralform>Vil du flytte emnet til papirkurven?</pluralform> +<pluralform>Vil du flytte følgende %x emner til papirkurven?</pluralform> </target> <source> @@ -1135,25 +1135,25 @@ Note: Fil navne må skal passe til grund bibliotekerne! </target> <source>Leave as unresolved conflict</source> -<target>Efterlad som uløste konflikter</target> +<target>Efterlad som uløst konflikt</target> <source>Replace</source> -<target></target> +<target>Erstat</target> <source>Move files and replace if existing</source> -<target></target> +<target>Flyt fil og erstat eventuelt eksisterende</target> <source>Append a timestamp to each file name</source> -<target></target> +<target>Føj tidsstempel til hvert filnavn</target> <source>Folder</source> -<target></target> +<target>Mappe</target> <source>File</source> -<target></target> +<target>Fil</target> <source>YYYY-MM-DD hhmmss</source> -<target></target> +<target>YYYY-MM-DD hhmmss</target> <source>Files</source> <target>Filer</target> @@ -1162,10 +1162,10 @@ Note: Fil navne må skal passe til grund bibliotekerne! <target>Procent</target> <source>Cannot monitor directory %x.</source> -<target>Kan ikke overvåge biblioteket %x.</target> +<target>Kan ikke overvåge mappen %x.</target> <source>Conversion error:</source> -<target>Konverterings fejl:</target> +<target>Konverteringsfejl:</target> <source>Cannot delete file %x.</source> <target>Kan ikke slette filen %x.</target> @@ -1177,22 +1177,22 @@ Note: Fil navne må skal passe til grund bibliotekerne! <target>Kan ikke flytte filen %x til %y.</target> <source>Cannot delete directory %x.</source> -<target>Kan ikke slette biblioteket %x.</target> +<target>Kan ikke slette mappen %x.</target> <source>Cannot write file attributes of %x.</source> -<target>Kan ikke skrive fil attributter til %x.</target> +<target>Kan ikke skrive filattributter til %x.</target> <source>Cannot write modification time of %x.</source> -<target>Kan ikke opdatere ændringstiden på %x.</target> +<target>Kan ikke opdatere tidsændring på %x.</target> <source>Cannot find system function %x.</source> <target>Kan ikke finde systemfunktionen %x.</target> <source>Cannot read security context of %x.</source> -<target>Kan ikke læse sikkerhedsindstillingerne på %x.</target> +<target>Kan ikke læse sikkerhedsindstillinger på %x.</target> <source>Cannot write security context of %x.</source> -<target>Kan ikke skrive sikkerhedsindstillingerne på %x.</target> +<target>Kan ikke skrive sikkerhedsindstillinger til %x.</target> <source>Cannot read permissions of %x.</source> <target>Kan ikke læse tilladelserne på %x.</target> @@ -1201,25 +1201,25 @@ Note: Fil navne må skal passe til grund bibliotekerne! <target>Kan ikke skrive tilladelserne til %x.</target> <source>Cannot create directory %x.</source> -<target>Kan ikke oprette biblioteket %x.</target> +<target>Kan ikke oprette mappen %x.</target> <source>Cannot copy symbolic link %x to %y.</source> -<target>Kan ikke kopiere linket %x til %y.</target> +<target>Kan ikke kopiere symlinket %x til %y.</target> <source>Cannot copy file %x to %y.</source> <target>Kan ikke kopiere filen %x til %y.</target> <source>Type of item %x is not supported:</source> -<target></target> +<target>Filtypen %x understøttes ikke:</target> <source>Cannot open directory %x.</source> -<target>Kan ikke åbne bibliotek %x.</target> +<target>Kan ikke åbne mappen %x.</target> <source>Cannot enumerate directory %x.</source> -<target>Kan ikke optælle biblioteket %x.</target> +<target>Kan ikke optælle mappen %x.</target> <source>Detected endless directory recursion.</source> -<target>Opdaget en uendelig biblioteks løkke.</target> +<target>Uendelig mappegentagelse fundet.</target> <source>%x TB</source> <target>%x TB</target> @@ -1244,7 +1244,7 @@ Note: Fil navne må skal passe til grund bibliotekerne! <pluralform>%x hours</pluralform> </source> <target> -<pluralform>1 timer</pluralform> +<pluralform>1 time</pluralform> <pluralform>%x timer</pluralform> </target> @@ -1258,46 +1258,46 @@ Note: Fil navne må skal passe til grund bibliotekerne! </target> <source>Cannot set privilege %x.</source> -<target>Kan ikke sætte privilegier til %x.</target> +<target>Kan ikke sætte %x privilegier.</target> <source>Unable to move %x to the Recycle Bin!</source> -<target>Ude af stand til at flytte %x til skraldespanden!</target> +<target>Kan ikke flytte %x til papirkurv!</target> <source>Both sides have changed since last synchronization!</source> -<target>Begge sider er ændret siden sidste synkronisering!</target> +<target>Begge sider ændret siden sidste synkronisering!</target> <source>Cannot determine sync-direction:</source> -<target>Kan ikke bestemme synkroniseringsvej:</target> +<target>Kan ikke bestemme retning:</target> <source>No change since last synchronization!</source> <target>Ingen ændringer siden sidste synkronisering!</target> <source>The corresponding database entries are not in sync considering current settings.</source> -<target>Databasen er ikke synkroniseret i forhold til nuværende indstillinger.</target> +<target>Databasen er ikke synkroniseret i forhold til aktuelle indstillinger.</target> <source>Setting default synchronization directions: Old files will be overwritten with newer files.</source> -<target>Sætter standard synkroniseringsvej: Gamle filer bliver overskrevet med nyere.</target> +<target>Sætter standardretning: Gamle filer overskrives med nyere.</target> <source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> -<target>Papirkurven kan ikke bruges på følgende stier! Filerne vil blive slettet permanent i stedet for:</target> +<target>Papirkurven kan ikke bruges på følgende stier! Filer slettes permanent:</target> <source>You can ignore this error to consider the folder as empty.</source> -<target>Du kan ignorere denne fejl og betegne mappen som tom.</target> +<target>Du kan ignorere denne fejl og betragte mappen som tom.</target> <source>Cannot find folder %x.</source> <target>Kan ikke finde mappen %x.</target> <source>Directories are dependent! Be careful when setting up synchronization rules:</source> -<target>Biblioteker er afhængige! Vær forsigtig når du laver synkroniserings reglerne:</target> +<target>Afhængige mapper! Vær forsigtig når du laver synkroniseringsregler:</target> <source>Start comparison</source> -<target>Start sammenligning</target> +<target>Start analyse</target> <source>Preparing synchronization...</source> <target>Forbereder synkronisering...</target> <source>Conflict detected:</source> -<target>Konflik fundet:</target> +<target>Konflikt fundet:</target> <source>File %x has an invalid date!</source> <target>Filen %x har en ugyldig dato!</target> @@ -1306,28 +1306,28 @@ Note: Fil navne må skal passe til grund bibliotekerne! <target>Filerne %x har den samme dato men forskellig størrelse!</target> <source>Items differ in attributes only</source> -<target>Enheder der kun har attributter til forskel</target> +<target>Enhederne har kun attributter til forskel</target> <source>Symbolic links %x have the same date but a different target.</source> -<target>Linksene %x har samme dato men forskellige mål.</target> +<target>Symlinkene %x har samme dato men forskellige mål.</target> <source>Comparing content of files %x</source> <target>Sammenligner indhold af filer %x</target> <source>Comparing files by content failed.</source> -<target>Fejl i sammenligning af filernes indhold.</target> +<target>Fejl ved analyse af filindhold.</target> <source>Generating file list...</source> -<target>Laver fil liste...</target> +<target>Opretter filliste...</target> <source>Both sides are equal</source> <target>Begge sider er ens</target> <source>Copy new item to left</source> -<target>Kopiere nyt emne mod venstre</target> +<target>Kopier nyt emne mod venstre</target> <source>Copy new item to right</source> -<target>Kopiere nyt emne mod højre</target> +<target>Kopier nyt emne mod højre</target> <source>Delete left item</source> <target>Slet emne til venstre</target> @@ -1342,10 +1342,10 @@ Note: Fil navne må skal passe til grund bibliotekerne! <target>flyt filen til højre</target> <source>Overwrite left item</source> -<target>Overskriv emne til venstre</target> +<target>Overskriv venstre emne</target> <source>Overwrite right item</source> -<target>Overskriv emne til højre</target> +<target>Overskriv højre emne</target> <source>Do nothing</source> <target>Gør intet</target> @@ -1360,22 +1360,22 @@ Note: Fil navne må skal passe til grund bibliotekerne! <target>Flere...</target> <source>Deleting file %x</source> -<target>Sletter fil %x</target> +<target>Sletter filen %x</target> <source>Deleting folder %x</source> -<target>Sletter mappe %x</target> +<target>Sletter mappen %x</target> <source>Deleting symbolic link %x</source> -<target>Sletter symbolks link %x</target> +<target>Sletter symlinket %x</target> <source>Moving file %x to recycle bin</source> -<target>Flytter fil %x til papirkurven</target> +<target>Flytter filen %x til papirkurven</target> <source>Moving folder %x to recycle bin</source> <target>Flytter mappe %x til papirkurven</target> <source>Moving symbolic link %x to recycle bin</source> -<target>Flytter link %x til papirkurven</target> +<target>Flytter symlinket %x til papirkurven</target> <source>Moving file %x to %y</source> <target>Flytter filen %x til %y</target> @@ -1384,52 +1384,52 @@ Note: Fil navne må skal passe til grund bibliotekerne! <target>Flytter mappen %x til %y</target> <source>Moving symbolic link %x to %y</source> -<target>Flytter linket %x til %y</target> +<target>Flytter symlinket %x til %y</target> <source>Removing old versions...</source> -<target></target> +<target>Fjerner gamle udgaver...</target> <source>Creating file %x</source> <target>Opretter filen %x</target> <source>Creating symbolic link %x</source> -<target>Opretter linket %x</target> +<target>Opretter symlinket %x</target> <source>Creating folder %x</source> -<target>Opretter mappe %x</target> +<target>Opretter mappen %x</target> <source>Overwriting file %x</source> <target>Overskriver filen %x</target> <source>Overwriting symbolic link %x</source> -<target>Overskriver linket %x</target> +<target>Overskriver symlinket %x</target> <source>Verifying file %x</source> -<target>Verificerer fil %x</target> +<target>Verificerer filen %x</target> <source>Updating attributes of %x</source> -<target>Opdaterer attributter af %x</target> +<target>Opdaterer attributter for %x</target> <source>Target folder %x already existing.</source> -<target>Destinations mappen %x eksisterer allerede.</target> +<target>Destinationsmappen %x findes allerede.</target> <source>Target folder input field must not be empty.</source> -<target>Destinations mappe feltet må ikke være tomt.</target> +<target>Destinationsmappen skal angives.</target> <source>Folder input field for versioning must not be empty.</source> -<target>Mappe input feltet for versionering må ikke være tomt.</target> +<target>Mappe til versionering skal angives.</target> <source>Source folder %x not found.</source> -<target>Kilde mappen %x blev ikke fundet.</target> +<target>Kildemappen %x blev ikke fundet.</target> <source>The following items have unresolved conflicts and will not be synchronized:</source> -<target></target> +<target>Følgende emner har uløste konflikter og synkroniseres ikke:</target> <source>Significant difference detected:</source> -<target>Betydelig forskel fundet:</target> +<target>Markant forskel:</target> <source>More than 50% of the total number of files will be copied or deleted!</source> -<target>Mere end 50% af det samlede antal filer vil blive kopieret eller slettet!</target> +<target>Mere end 50% af af filerne kopieres eller slettes!</target> <source>Not enough free disk space available in:</source> <target>Ikke nok ledig diskplads på:</target> @@ -1441,7 +1441,7 @@ Note: Fil navne må skal passe til grund bibliotekerne! <target>Tilgængeligt:</target> <source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source> -<target>En mappe som er en del af flere mappe parringer vil blive ændret. Gennemse venligst synkroniserings indstillingerne.</target> +<target>Der ændres en mappe der tilhører flere mappepar. Kontroller dine synkroniseringsindstillinger.</target> <source>Left</source> <target>Venstre</target> @@ -1450,11 +1450,11 @@ Note: Fil navne må skal passe til grund bibliotekerne! <target>Højre</target> <source>Synchronizing folder pair:</source> -<target>Synkroniserer mappe par:</target> +<target>Synkroniserer mappepar:</target> <source>Generating database...</source> <target>Opretter database...</target> <source>Data verification error: Source and target file have different content!</source> -<target>Data godkendelses fejl: Kilde og destinations fil har forskelligt indhold!</target> +<target>Godkendelsesfejl: Kilde- og destinationsfil har forskelligt indhold!</target> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Bezig met afbreken: Wacht op beëindiging huidige bewerking...</target> -<source>Failure to create time stamp for versioning:</source> -<target></target> +<source>Failure to create timestamp for versioning:</source> +<target>Het creëren van een tijdsstempen voor versiebeheer is mislukt:</target> <source>RealtimeSync - Automated Synchronization</source> <target>RealtimeSync - Geautomatiseerde Synchronisatie</target> @@ -59,7 +59,7 @@ <target>Verwijder filterinstellingen</target> <source>Save as batch job</source> -<target></target> +<target>Opslaan als batch opdracht</target> <source>Comparison settings</source> <target>Vergelijksinstellingen</target> @@ -272,7 +272,7 @@ <target>Selecteer een map</target> <source>Idle time [seconds]</source> -<target></target> +<target>Inactiviteit [seconden]</target> <source>Idle time between last detected change and execution of command</source> <target>Tijd tussen de laatste gedetecteerde verandering en de uitvoering van het commando</target> @@ -328,16 +328,16 @@ De opdracht word geactiveerd als: <target>Synchronisatie is met fouten afgerond!</target> <source>Synchronization completed with warnings.</source> -<target></target> +<target>Synchronisatie afgerond met waarschuwingen.</target> <source>Nothing to synchronize!</source> <target>Niets om te synchroniseren!</target> <source>Synchronization completed successfully.</source> -<target></target> +<target>Synchronisatie succesvol.</target> <source>Saving log file %x...</source> -<target></target> +<target>Opslaan van logbestand %x...</target> <source>Press "Switch" to resolve issues in FreeFileSync main dialog.</source> <target>Druk op "Omschakelen" om problemen op te lossen in het hoofdscherm van FreeFileSync.</target> @@ -361,7 +361,7 @@ De opdracht word geactiveerd als: <target>Kan geen verbinding maken met sourceforge.net!</target> <source>Current FreeFileSync version number was not found online! Do you want to check manually?</source> -<target></target> +<target>Het huidige FreeFileSync versienummer is niet online gevonden! Wilt u handmatig checken?</target> <source>Do you want FreeFileSync to automatically check for updates every week?</source> <target>Wilt u FreeFileSync elke week automatisch laten controleren of er een nieuwe versie is?</target> @@ -433,7 +433,7 @@ De opdracht word geactiveerd als: <target>O&pslaan</target> <source>Save as &batch job...</source> -<target></target> +<target>Opslaan als &batch opdracht...</target> <source>1. &Compare</source> <target>1. &Vergelijk</target> @@ -481,16 +481,16 @@ De opdracht word geactiveerd als: <target>Wissel zijdes</target> <source>Open</source> -<target></target> +<target>Open</target> <source>Save</source> -<target></target> +<target>Opslaan</target> <source>Last used configurations (press DEL to remove from list)</source> <target>Laatst gebruikte instellingen (druk op DEL om iets te verwijderen)</target> <source>Hide excluded items</source> -<target></target> +<target>Verberg uitgesloten bestanden</target> <source>Show filtered or temporarily excluded files</source> <target>Laat gefilterde of tijdelijk uitgesloten bestanden zien</target> @@ -550,13 +550,13 @@ De opdracht word geactiveerd als: <target>Synchronisatie stoppen bij eerste foutmelding</target> <source>On completion</source> -<target></target> +<target>Na voltooiing</target> <source>Show progress dialog</source> <target>Toon voortgangsdialoogvenster</target> <source>Save log</source> -<target></target> +<target>Logbestand opslaan</target> <source>Select folder to save log files</source> <target>Selecteer map om log bestanden op te slaan</target> @@ -568,7 +568,7 @@ De opdracht word geactiveerd als: <target>Limiteer maximaal aantal log bestanden</target> <source>Select variant</source> -<target></target> +<target>Selecteer variant</target> <source> Files are found equal if @@ -607,7 +607,7 @@ overeenkomt <target>OK</target> <source><- Two way -></source> -<target></target> +<target><- Twee kanten op -></target> <source>Identify and propagate changes on both sides using a database. Deletions, renaming and conflicts are detected automatically.</source> <target>Identificeer en verspreid veranderingen aan beide kanten met behulp van een database. Verwijderingen, hernoemingen en conflicten worden automatisch gedetecteerd.</target> @@ -643,7 +643,7 @@ overeenkomt <target>Prullenbak</target> <source>Use Recycle Bin for deleted and overwritten files</source> -<target></target> +<target>Gebruik prullenbak voor verwijderde en overschreven bestanden</target> <source>Versioning</source> <target>Versiebeheer</target> @@ -652,7 +652,7 @@ overeenkomt <target>Verplaats bestanden met tijdstempel naar specifieke map</target> <source>Naming convention:</source> -<target></target> +<target>Naamgevingsconventie</target> <source>Configuration</source> <target>Configuratie</target> @@ -681,7 +681,7 @@ overeenkomt <source>&Pause</source> <target>&Pauze</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Broncode geschreven in C++ met behulp van:</target> <source>If you like FreeFileSync</source> @@ -960,6 +960,9 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! <source>Show files that won't be copied</source> <target>Toon bestanden die niet gekopiëerd zullen worden</target> +<source>Set as default</source> +<target></target> + <source>All folders are in sync!</source> <target>Alle mappen zijn gesynchroniseerd!</target> @@ -1033,7 +1036,7 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! <target>Inhoud vergelijken...</target> <source>Copy</source> -<target></target> +<target>Kopiëren</target> <source>Paused</source> <target>Gepauzeerd</target> @@ -1108,13 +1111,13 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! <target>- alleen het map gedeelte</target> <source>- Other side's counterpart to %item_path%</source> -<target></target> +<target>- Tegenhanger van de andere kant naar %item_path%</target> <source>- Other side's counterpart to %item_folder%</source> -<target></target> +<target>- Tegenhanger van de andere kant naar %item_folder%</target> <source>Make hidden warnings and dialogs visible again?</source> -<target></target> +<target>Verborgen waarschuwingen en dialogen zichtbaar maken?</target> <source> <pluralform>Do you really want to move the following object to the Recycle Bin?</pluralform> @@ -1138,22 +1141,22 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! <target>Beschouw als onopgelost conflict</target> <source>Replace</source> -<target></target> +<target>Vervangen</target> <source>Move files and replace if existing</source> -<target></target> +<target>Verplaats bestanden en overschrijf bestaande bestanden</target> <source>Append a timestamp to each file name</source> -<target></target> +<target>Voeg een timestamp aan elke bestandsnaam toe</target> <source>Folder</source> -<target></target> +<target>Map</target> <source>File</source> -<target></target> +<target>Bestand</target> <source>YYYY-MM-DD hhmmss</source> -<target></target> +<target>JJJJ-MM-DD hhmmss</target> <source>Files</source> <target>Bestanden</target> @@ -1210,7 +1213,7 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! <target>Kan bestand %x niet kopiëren naar %y.</target> <source>Type of item %x is not supported:</source> -<target></target> +<target>Type van bestand %x is niet ondersteund:</target> <source>Cannot open directory %x.</source> <target>Kan map %x niet openen.</target> @@ -1387,7 +1390,7 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! <target>Bezig met verplaatsen van snelkoppeling %x naar %y</target> <source>Removing old versions...</source> -<target></target> +<target>Bezig met verwijderen van oude versies...</target> <source>Creating file %x</source> <target>Bestand %x wordt aangemaakt</target> @@ -1423,7 +1426,7 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen! <target>Bronmap %x niet gevonden.</target> <source>The following items have unresolved conflicts and will not be synchronized:</source> -<target></target> +<target>De volgende items hebben onopgeloste conflicten en zullen niet worden gesynchroniseerd:</target> <source>Significant difference detected:</source> <target>Significant verschil gedetecteerd:</target> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Abort requested: Waiting for current operation to finish...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>Failure to create time stamp for versioning:</target> <source>RealtimeSync - Automated Synchronization</source> @@ -459,18 +459,12 @@ The command is triggered if: <source>Compare</source> <target>Compare</target> -<source>Compare both sides</source> -<target>Compare both sides</target> - <source>&Abort</source> <target>&Abort</target> <source>Synchronize</source> <target>Synchronise</target> -<source>Start synchronization</source> -<target>Start synchronisation</target> - <source>Add folder pair</source> <target>Add folder pair</target> @@ -480,15 +474,6 @@ The command is triggered if: <source>Swap sides</source> <target>Swap sides</target> -<source>Open</source> -<target>Open</target> - -<source>Save</source> -<target>Save</target> - -<source>Last used configurations (press DEL to remove from list)</source> -<target>Last used configurations (press DEL to remove from list)</target> - <source>Hide excluded items</source> <target>Hide excluded items</target> @@ -681,8 +666,8 @@ is the same <source>&Pause</source> <target>&Pause</target> -<source>Source code written in C++ utilizing:</source> -<target>Source code written in C++ utilising:</target> +<source>Source code written in C++ using:</source> +<target>Source code written in C++ using:</target> <source>If you like FreeFileSync</source> <target>If you like FreeFileSync</target> @@ -771,6 +756,9 @@ Note: File names must be relative to base directories! <source>Description</source> <target>Description</target> +<source>Start synchronization</source> +<target>Start synchronisation</target> + <source>Variant</source> <target>Variant</target> @@ -807,6 +795,15 @@ Note: File names must be relative to base directories! <source>Select view</source> <target>Select view</target> +<source>Open...</source> +<target>Open...</target> + +<source>Save</source> +<target>Save</target> + +<source>Compare both sides</source> +<target>Compare both sides</target> + <source>Set direction:</source> <target>Set direction:</target> @@ -960,6 +957,9 @@ Note: File names must be relative to base directories! <source>Show files that won't be copied</source> <target>Show files that won't be copied</target> +<source>Set as default</source> +<target>Set as default</target> + <source>All folders are in sync!</source> <target>All folders are in sync!</target> @@ -973,15 +973,6 @@ Note: File names must be relative to base directories! <target>File list exported!</target> <source> -<pluralform>Object deleted successfully!</pluralform> -<pluralform>%x objects deleted successfully!</pluralform> -</source> -<target> -<pluralform>Object deleted successfully!</pluralform> -<pluralform>%x objects deleted successfully!</pluralform> -</target> - -<source> <pluralform>1 directory</pluralform> <pluralform>%x directories</pluralform> </source> @@ -1137,15 +1128,15 @@ Note: File names must be relative to base directories! <source>Leave as unresolved conflict</source> <target>Leave as unresolved conflict</target> +<source>Append a timestamp to each file name</source> +<target>Append a timestamp to each file name</target> + <source>Replace</source> <target>Replace</target> <source>Move files and replace if existing</source> <target>Move files and replace if existing</target> -<source>Append a timestamp to each file name</source> -<target>Append a timestamp to each file name</target> - <source>Folder</source> <target>Folder</target> @@ -1278,6 +1269,27 @@ Note: File names must be relative to base directories! <source>Setting default synchronization directions: Old files will be overwritten with newer files.</source> <target>Setting default synchronisation directions: Old files will be overwritten with newer files.</target> +<source>Checking recycle bin availability for folder %x...</source> +<target>Checking recycle bin availability for folder %x...</target> + +<source>Moving file %x to recycle bin</source> +<target>Moving file %x to recycle bin</target> + +<source>Moving folder %x to recycle bin</source> +<target>Moving folder %x to recycle bin</target> + +<source>Moving symbolic link %x to recycle bin</source> +<target>Moving symbolic link %x to recycle bin</target> + +<source>Deleting file %x</source> +<target>Deleting file %x</target> + +<source>Deleting folder %x</source> +<target>Deleting folder %x</target> + +<source>Deleting symbolic link %x</source> +<target>Deleting symbolic link %x</target> + <source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> <target>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</target> @@ -1359,24 +1371,6 @@ Note: File names must be relative to base directories! <source>Multiple...</source> <target>Multiple...</target> -<source>Deleting file %x</source> -<target>Deleting file %x</target> - -<source>Deleting folder %x</source> -<target>Deleting folder %x</target> - -<source>Deleting symbolic link %x</source> -<target>Deleting symbolic link %x</target> - -<source>Moving file %x to recycle bin</source> -<target>Moving file %x to recycle bin</target> - -<source>Moving folder %x to recycle bin</source> -<target>Moving folder %x to recycle bin</target> - -<source>Moving symbolic link %x to recycle bin</source> -<target>Moving symbolic link %x to recycle bin</target> - <source>Moving file %x to %y</source> <target>Moving file %x to %y</target> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Keskeytys pyydetty: Odotetaan toiminnon loppumista...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>Ongelma luoda aikaleimaa versionnissa:</target> <source>RealtimeSync - Automated Synchronization</source> @@ -681,7 +681,7 @@ on sama <source>&Pause</source> <target>&Keskeytä</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Koodikieli on C++ käyttäen:</target> <source>If you like FreeFileSync</source> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Abandon demandé : En attente de la fin de l'opération en cours...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>Impossible de créer l'horodatage des versions</target> <source>RealtimeSync - Automated Synchronization</source> @@ -681,7 +681,7 @@ est identique <source>&Pause</source> <target>&Pause</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Code source écrit en C++ utilisant :</target> <source>If you like FreeFileSync</source> 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 @@ <target>Verzeichnis öffnen</target> <source>Abort requested: Waiting for current operation to finish...</source> -<target>Abbruch initiiert: Warte, bis aktuelle Operation beendet ist...</target> +<target>Abbruch initiiert: Warte bis die aktuelle Operation beendet ist...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>Der Zeitstempel für die Versionierung kann nicht erstellt werden:</target> <source>RealtimeSync - Automated Synchronization</source> @@ -459,18 +459,12 @@ Die Befehlszeile wird ausgelöst wenn: <source>Compare</source> <target>Vergleichen</target> -<source>Compare both sides</source> -<target>Beide Seiten vergleichen</target> - <source>&Abort</source> <target>&Abbrechen</target> <source>Synchronize</source> <target>Synchronisieren</target> -<source>Start synchronization</source> -<target>Synchronisation starten</target> - <source>Add folder pair</source> <target>Ordnerpaar hinzufügen</target> @@ -480,15 +474,6 @@ Die Befehlszeile wird ausgelöst wenn: <source>Swap sides</source> <target>Seiten vertauschen</target> -<source>Open</source> -<target>Öffnen</target> - -<source>Save</source> -<target>Speichern</target> - -<source>Last used configurations (press DEL to remove from list)</source> -<target>Zuletzt benutzte Konfigurationen (Entf-Taste löscht Einträge)</target> - <source>Hide excluded items</source> <target>Ausgeschlossene Elemente verstecken</target> @@ -681,7 +666,7 @@ gleich ist <source>&Pause</source> <target>&Pause</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Sourcecode in C++ geschrieben mit Hilfe von:</target> <source>If you like FreeFileSync</source> @@ -771,6 +756,9 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <source>Description</source> <target>Beschreibung</target> +<source>Start synchronization</source> +<target>Synchronisation starten</target> + <source>Variant</source> <target>Variante</target> @@ -807,6 +795,15 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <source>Select view</source> <target>Ansicht auswählen</target> +<source>Open...</source> +<target>Öffnen...</target> + +<source>Save</source> +<target>Speichern</target> + +<source>Compare both sides</source> +<target>Beide Seiten vergleichen</target> + <source>Set direction:</source> <target>Setze Richtung:</target> @@ -960,6 +957,9 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <source>Show files that won't be copied</source> <target>Dateien die nicht kopiert werden anzeigen</target> +<source>Set as default</source> +<target>Als Standard festlegen</target> + <source>All folders are in sync!</source> <target>Alle Ordner sind synchron!</target> @@ -973,15 +973,6 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <target>Dateiliste exportiert!</target> <source> -<pluralform>Object deleted successfully!</pluralform> -<pluralform>%x objects deleted successfully!</pluralform> -</source> -<target> -<pluralform>Element erfolgreich gelöscht!</pluralform> -<pluralform>%x Elemente erfolgreich gelöscht!</pluralform> -</target> - -<source> <pluralform>1 directory</pluralform> <pluralform>%x directories</pluralform> </source> @@ -1137,15 +1128,15 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <source>Leave as unresolved conflict</source> <target>Als unbehandelten Konflikt belassen</target> +<source>Append a timestamp to each file name</source> +<target>Einen Zeitstempel an jeden Dateinamen hinzufügen</target> + <source>Replace</source> <target>Ersetzen</target> <source>Move files and replace if existing</source> <target>Dateien verschieben und bereits vorhandene ersetzen</target> -<source>Append a timestamp to each file name</source> -<target>Einen Zeitstempel an jeden Dateinamen hinzufügen</target> - <source>Folder</source> <target>Ordner</target> @@ -1278,6 +1269,27 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <source>Setting default synchronization directions: Old files will be overwritten with newer files.</source> <target>Setze Standardwerte für Synchronisationsrichtungen: Alte Dateien werden durch neuere überschrieben.</target> +<source>Checking recycle bin availability for folder %x...</source> +<target>Prüfe Verfügbarkeit des Papierkorbs für Ordner %x...</target> + +<source>Moving file %x to recycle bin</source> +<target>Verschiebe Datei %x in den Papierkorb</target> + +<source>Moving folder %x to recycle bin</source> +<target>Verschiebe Ordner %x in den Papierkorb</target> + +<source>Moving symbolic link %x to recycle bin</source> +<target>Verschiebe Symbolischen Link %x in den Papierkorb</target> + +<source>Deleting file %x</source> +<target>Lösche Datei %x</target> + +<source>Deleting folder %x</source> +<target>Lösche Ordner %x</target> + +<source>Deleting symbolic link %x</source> +<target>Lösche Symbolischen Link %x</target> + <source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> <target>Der Papierkorb ist für die folgenden Pfade nicht verfügbar! Die Dateien werden stattdessen permanent gelöscht:</target> @@ -1359,24 +1371,6 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein! <source>Multiple...</source> <target>Verschiedene...</target> -<source>Deleting file %x</source> -<target>Lösche Datei %x</target> - -<source>Deleting folder %x</source> -<target>Lösche Ordner %x</target> - -<source>Deleting symbolic link %x</source> -<target>Lösche Symbolischen Link %x</target> - -<source>Moving file %x to recycle bin</source> -<target>Verschiebe Datei %x in den Papierkorb</target> - -<source>Moving folder %x to recycle bin</source> -<target>Verschiebe Ordner %x in den Papierkorb</target> - -<source>Moving symbolic link %x to recycle bin</source> -<target>Verschiebe Symbolischen Link %x in den Papierkorb</target> - <source>Moving file %x to %y</source> <target>Verschiebe Datei %x nach %y</target> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Ζητήθηκε ματαίωση: Αναμονή για την λήξη της τρέχουσας εργασίας...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target></target> <source>RealtimeSync - Automated Synchronization</source> @@ -59,7 +59,7 @@ <target>Διαγραφή όλων των ρυθμίσεων φίλτρου</target> <source>Save as batch job</source> -<target></target> +<target>Αποθήκευση ως δέσμη ενεργειών</target> <source>Comparison settings</source> <target>Ρυθμίσεις σύγκρισης</target> @@ -89,7 +89,7 @@ <target>Σφάλμα στη γραμμή εντολών:</target> <source>Info</source> -<target>Πληροφορία</target> +<target>Πληροφορίες</target> <source>Warning</source> <target>Προειδοποίηση</target> @@ -224,7 +224,7 @@ <target>Ά&νοιγμα...</target> <source>Save &as...</source> -<target>Απο&θήκευση ως...</target> +<target>Αποθή&κευση ως...</target> <source>&Quit</source> <target>Έ&ξοδος</target> @@ -254,7 +254,7 @@ <target>3. Πατήστε το 'Έναρξη'.</target> <source>To get started just import a .ffs_batch file.</source> -<target>Για να ξεκινήσετε μπορείτε απλά να εισάγεται ένα αρχείο .ffs_batch.</target> +<target>Για να ξεκινήσετε μπορείτε απλά να εισάγετε ένα αρχείο .ffs_batch.</target> <source>Folders to watch</source> <target>Υποκατάλογοι που θα παρακολουθούνται</target> @@ -272,7 +272,7 @@ <target>Επιλογή υποκαταλόγου</target> <source>Idle time [seconds]</source> -<target></target> +<target>Λανθάνων χρόνος [δευτερόλεπτα]</target> <source>Idle time between last detected change and execution of command</source> <target>Λανθάνων χρόνος μεταξύ τελευταίας αλλαγής που ανιχνεύθηκε και εκτέλεσης της εντολής</target> @@ -328,16 +328,16 @@ The command is triggered if: <target>Ο συγχρονισμός ολοκληρώθηκε με σφάλματα!</target> <source>Synchronization completed with warnings.</source> -<target></target> +<target>Ο συγχρονισμός ολοκληρώθηκε με προειδοποιήσεις.</target> <source>Nothing to synchronize!</source> <target>Δεν υπάρχει τίποτα προς συγχρονισμό!</target> <source>Synchronization completed successfully.</source> -<target></target> +<target>Ο συγχρονισμός ολοκληρώθηκε επιτυχώς.</target> <source>Saving log file %x...</source> -<target></target> +<target>Αποθήκευση του αρχείου καταγραφής %x...</target> <source>Press "Switch" to resolve issues in FreeFileSync main dialog.</source> <target>Πατήστε "Εναλλαγή" για να επιλύσετε τα θέματα στο κεντρικό παράθυρο του FreeFileSync.</target> @@ -361,7 +361,7 @@ The command is triggered if: <target>Δεν είναι δυνατή η σύνδεση με το sourceforge.net!</target> <source>Current FreeFileSync version number was not found online! Do you want to check manually?</source> -<target></target> +<target>Η τρέχουσα έκδοση του FreeFileSync δεν βρέθηκε στο δίκτυο! Θέλετε να την αναζητήσετε μόνος σας;</target> <source>Do you want FreeFileSync to automatically check for updates every week?</source> <target>Θέλετε το FreeFileSync να ελέγχει αυτόματα για ενημερώσεις κάθε εβδομάδα;</target> @@ -433,7 +433,7 @@ The command is triggered if: <target>Απο&θήκευση</target> <source>Save as &batch job...</source> -<target></target> +<target>Αποθήκευση ως δέσ&μη ενεργειών...</target> <source>1. &Compare</source> <target>1. &Σύγκριση</target> @@ -459,18 +459,12 @@ The command is triggered if: <source>Compare</source> <target>Σύγκριση</target> -<source>Compare both sides</source> -<target>Σύγκριση των δύο πλευρών</target> - <source>&Abort</source> <target>&Άκυρο</target> <source>Synchronize</source> <target>Συγχρονισμός</target> -<source>Start synchronization</source> -<target>Έναρξη του συγχρονισμού</target> - <source>Add folder pair</source> <target>Προσθήκη ζεύγους υποκαταλόγων</target> @@ -480,17 +474,8 @@ The command is triggered if: <source>Swap sides</source> <target>Ανταλλαγή πλευρών</target> -<source>Open</source> -<target></target> - -<source>Save</source> -<target></target> - -<source>Last used configurations (press DEL to remove from list)</source> -<target>Διατάξεις που χρησιμοποιήθηκαν τελευταία (πατήστε DEL για διαγραφή από τη λίστα)</target> - <source>Hide excluded items</source> -<target></target> +<target>Απόκρυψη στοιχείων που εξαιρέθηκαν</target> <source>Show filtered or temporarily excluded files</source> <target>Εμφάνιση φιλτραρισμένων ή προσωρινά εξαιρεθέντων αρχείων</target> @@ -550,25 +535,25 @@ The command is triggered if: <target>Ματαίωση του συγχρονισμού με το πρώτο σφάλμα</target> <source>On completion</source> -<target></target> +<target>Μετά την ολοκλήρωση</target> <source>Show progress dialog</source> <target>Εμφάνιση της αναφοράς προόδου</target> <source>Save log</source> -<target></target> +<target>Αποθήκευση αρχείου καταγραφής</target> <source>Select folder to save log files</source> <target>Επιλογή υποκαταλόγου για την αποθήκευση των αρχείων καταγραφής</target> <source>Limit</source> -<target>Αριθμός εκδόσεων</target> +<target>Μέγιστος αριθμός</target> <source>Limit maximum number of log files</source> <target>Μέγιστος αριθμός αρχείων καταγραφής</target> <source>Select variant</source> -<target></target> +<target>Επιλογή μεθόδου</target> <source> Files are found equal if @@ -607,7 +592,7 @@ is the same <target>OK</target> <source><- Two way -></source> -<target></target> +<target><- Διπλής κατεύθυνσης -></target> <source>Identify and propagate changes on both sides using a database. Deletions, renaming and conflicts are detected automatically.</source> <target>Αναγνώριση και αναπαραγωγή των αλλαγών και στις δύο πλευρές με τη χρήση μιας βάσης δεδομένων. Διαγραφές, μετονομασίες και διενέξεις ανιχνεύονται αυτόματα.</target> @@ -643,7 +628,7 @@ is the same <target>Κάδος Ανακύκλωσης</target> <source>Use Recycle Bin for deleted and overwritten files</source> -<target></target> +<target>Χρήση του Κάδου Ανακύκλωσης για αρχεία που διαγράφηκαν ή αντικαταστάθηκαν</target> <source>Versioning</source> <target>Διατήρηση παλιών εκδόσεων</target> @@ -652,7 +637,7 @@ is the same <target>Μεταφορά χρονοσημασμένων αρχείων σε συγκεκριμένο υποκατάλογο</target> <source>Naming convention:</source> -<target></target> +<target>Τρόπος ονομασίας:</target> <source>Configuration</source> <target>Διάταξη</target> @@ -681,7 +666,7 @@ is the same <source>&Pause</source> <target>&Παύση</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Ο πηγαίος κώδικας γράφτηκε σε C++ χρησιμοποιώντας τα:</target> <source>If you like FreeFileSync</source> @@ -771,6 +756,9 @@ Note: File names must be relative to base directories! <source>Description</source> <target>Περιγραφή</target> +<source>Start synchronization</source> +<target>Έναρξη του συγχρονισμού</target> + <source>Variant</source> <target>Μέθοδος</target> @@ -807,6 +795,15 @@ Note: File names must be relative to base directories! <source>Select view</source> <target>Επιλογή εμφάνισης</target> +<source>Open...</source> +<target></target> + +<source>Save</source> +<target>Αποθήκευση</target> + +<source>Compare both sides</source> +<target>Σύγκριση των δύο πλευρών</target> + <source>Set direction:</source> <target>Επιλογή κατεύθυνσης:</target> @@ -960,6 +957,9 @@ Note: File names must be relative to base directories! <source>Show files that won't be copied</source> <target>Εμφάνιση των αρχείων που δε θα αντιγραφούν</target> +<source>Set as default</source> +<target></target> + <source>All folders are in sync!</source> <target>Όλοι οι υποκατάλογοι είναι συγχρονισμένοι!</target> @@ -973,15 +973,6 @@ Note: File names must be relative to base directories! <target>Ο κατάλογος των αρχείων έχει εξαχθεί!</target> <source> -<pluralform>Object deleted successfully!</pluralform> -<pluralform>%x objects deleted successfully!</pluralform> -</source> -<target> -<pluralform>Το αντικείμενο διαγράφηκε επιτυχώς!</pluralform> -<pluralform>%x αντικείμενα διαγράφηκαν επιτυχώς!</pluralform> -</target> - -<source> <pluralform>1 directory</pluralform> <pluralform>%x directories</pluralform> </source> @@ -1033,7 +1024,7 @@ Note: File names must be relative to base directories! <target>Σύγκριση του περιεχομένου...</target> <source>Copy</source> -<target></target> +<target>Αντιγραφή</target> <source>Paused</source> <target>Σε παύση</target> @@ -1048,13 +1039,13 @@ Note: File names must be relative to base directories! <target>Ολοκληρώθηκε</target> <source>Continue</source> -<target>Συνέχεια</target> +<target>Συνέχιση</target> <source>Pause</source> <target>Παύση</target> <source>Logging</source> -<target>Καταγραφή μηνυμάτων</target> +<target>Καταγραφέντα μηνύματα</target> <source>Cannot find %x</source> <target>Δεν μπορεί να βρεθεί το %x</target> @@ -1102,19 +1093,19 @@ Note: File names must be relative to base directories! <target>Ένταξη εξωτερικών εφαρμογών στο μενού περιβάλλοντος. Οι ακόλουθες μακροεντολές είναι διαθέσιμες:</target> <source>- full file or folder name</source> -<target>- πλήρε όνομα αρχείου ή υποκαταλόγου</target> +<target>- πλήρες όνομα αρχείου ή υποκαταλόγου</target> <source>- folder part only</source> <target>- μέρος μόνο του υποκαταλόγου</target> <source>- Other side's counterpart to %item_path%</source> -<target></target> +<target>- Το αντίστοιχο του %item_path% της άλλης πλευράς</target> <source>- Other side's counterpart to %item_folder%</source> -<target></target> +<target>- Το αντίστοιχο του %item_folder% της άλλης πλευράς</target> <source>Make hidden warnings and dialogs visible again?</source> -<target></target> +<target>Να επανεμφανιστούν τα κρυμμένα μηνύματα και προειδοποιήσεις;</target> <source> <pluralform>Do you really want to move the following object to the Recycle Bin?</pluralform> @@ -1137,23 +1128,23 @@ Note: File names must be relative to base directories! <source>Leave as unresolved conflict</source> <target>Παράβλεψη ως ανεπίλυτη διένεξη</target> +<source>Append a timestamp to each file name</source> +<target>Προσάρτηση χρονικής σήμανσης σε κάθε όνομα αρχείου</target> + <source>Replace</source> -<target></target> +<target>Αντικατάσταση προηγούμενης</target> <source>Move files and replace if existing</source> -<target></target> - -<source>Append a timestamp to each file name</source> -<target></target> +<target>Μετακίνηση αρχείων και αντικατάσταση</target> <source>Folder</source> -<target></target> +<target>Υποκατάλογος</target> <source>File</source> -<target></target> +<target>Αρχείο</target> <source>YYYY-MM-DD hhmmss</source> -<target></target> +<target>ΕΕΕΕ-ΜΜ-ΗΗ ωωλλδδ</target> <source>Files</source> <target>Αρχεία</target> @@ -1210,7 +1201,7 @@ Note: File names must be relative to base directories! <target>Δεν μπορεί να αντιγραφεί το αρχείο %x στο %y.</target> <source>Type of item %x is not supported:</source> -<target></target> +<target>Ο τύπος του στοιχείου %x δεν υποστηρίζεται:</target> <source>Cannot open directory %x.</source> <target>Δεν είναι δυνατό το άνοιγμα του υποκαταλόγου %x.</target> @@ -1278,6 +1269,27 @@ Note: File names must be relative to base directories! <source>Setting default synchronization directions: Old files will be overwritten with newer files.</source> <target>Ρύθμιση προεπιλεγμένης κατεύθυνσης συγχρονισμού: Τα νεότερα αρχεία θα αντικαταστήσουν τα παλιότερα.</target> +<source>Checking recycle bin availability for folder %x...</source> +<target></target> + +<source>Moving file %x to recycle bin</source> +<target>Μεταφορά του αρχείου %x στον κάδο ανακύκλωσης</target> + +<source>Moving folder %x to recycle bin</source> +<target>Μεταφορά του υποκαταλόγου %x στον κάδο ανακύκλωσης</target> + +<source>Moving symbolic link %x to recycle bin</source> +<target>Μεταφορά του συμβολικού δεσμού %x στον κάδο ανακύκλωσης</target> + +<source>Deleting file %x</source> +<target>Διαγραφή του αρχείου %x</target> + +<source>Deleting folder %x</source> +<target>Διαγραφή του υποκαταλόγου %x</target> + +<source>Deleting symbolic link %x</source> +<target>Διαγραφή του συμβολικού δεσμού %x</target> + <source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> <target>Ο Κάδος Ανακύκλωσης δεν είναι διαθέσιμος για τις ακόλουθες διαδρομές! Τα αρχεία θα διαγραφούν μόνιμα:</target> @@ -1359,24 +1371,6 @@ Note: File names must be relative to base directories! <source>Multiple...</source> <target>Πολλαπλές ρυθμίσεις...</target> -<source>Deleting file %x</source> -<target>Διαγραφή του αρχείου %x</target> - -<source>Deleting folder %x</source> -<target>Διαγραφή του υποκαταλόγου %x</target> - -<source>Deleting symbolic link %x</source> -<target>Διαγραφή του συμβολικού δεσμού %x</target> - -<source>Moving file %x to recycle bin</source> -<target>Μεταφορά του αρχείου %x στον κάδο ανακύκλωσης</target> - -<source>Moving folder %x to recycle bin</source> -<target>Μεταφορά του υποκαταλόγου %x στον κάδο ανακύκλωσης</target> - -<source>Moving symbolic link %x to recycle bin</source> -<target>Μεταφορά του συμβολικού δεσμού %x στον κάδο ανακύκλωσης</target> - <source>Moving file %x to %y</source> <target>Μεταφορά του αρχείου %x στο %y</target> @@ -1387,7 +1381,7 @@ Note: File names must be relative to base directories! <target>Μεταφορά του συμβολικού δεσμού %x στο %y</target> <source>Removing old versions...</source> -<target></target> +<target>Διαγραφή παλιών εκδόσεων...</target> <source>Creating file %x</source> <target>Δημιουργία του αρχείου %x</target> @@ -1423,7 +1417,7 @@ Note: File names must be relative to base directories! <target>Ο υποκατάλογος %x δε βρέθηκε.</target> <source>The following items have unresolved conflicts and will not be synchronized:</source> -<target></target> +<target>Τα ακόλουθα στοιχεία έχουν ανεπίλυτες διενέξεις και δε θα συγχρονιστούν:</target> <source>Significant difference detected:</source> <target>Ανιχνεύθηκαν σημαντικές διαφορές:</target> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>התקבלה בקשת ביטול: מחכה לפעולה הנוכחית להסתיים...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>נכשל ייצור תג זמן עבור עדכון גרסאות</target> <source>RealtimeSync - Automated Synchronization</source> @@ -681,7 +681,7 @@ is the same <source>&Pause</source> <target>&עצור</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>קוד מקור נכתב ב- C++ באמצעות:</target> <source>If you like FreeFileSync</source> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Megszakítási kérelem: Várakozás a folyamatban lévő művelet befejezésére...</target> -<source>Failure to create time stamp for versioning:</source> -<target></target> +<source>Failure to create timestamp for versioning:</source> +<target>A verziókövetéshez szükséges időbélyeg létrehozása sikertelen:</target> <source>RealtimeSync - Automated Synchronization</source> <target>RealtimeSync - Automatikus szinkronizálás</target> @@ -59,7 +59,7 @@ <target>Szűrőbeállítások törlése</target> <source>Save as batch job</source> -<target></target> +<target>Mentés kötegelt feladatként</target> <source>Comparison settings</source> <target>Összehasonlítási beállítások</target> @@ -272,7 +272,7 @@ <target>Mappa kiválasztása</target> <source>Idle time [seconds]</source> -<target></target> +<target>Tétlenségi idő (másodpercekben)</target> <source>Idle time between last detected change and execution of command</source> <target>Tétlenség időtartama az utolsó változás észlelése és a parancs végrehajtása között</target> @@ -328,16 +328,16 @@ A parancs végrehajtódik, ha: <target>A szinkronizáció befejeződött, de akadtak hibák!</target> <source>Synchronization completed with warnings.</source> -<target></target> +<target>A szinkronizáció befejeződött figyelmeztetésekkel.</target> <source>Nothing to synchronize!</source> <target>Nincs mit szinkronizálni!</target> <source>Synchronization completed successfully.</source> -<target></target> +<target>A szinkronizáció sikeresen befejeződött.</target> <source>Saving log file %x...</source> -<target></target> +<target>Naplófájl mentése a következő fájlba: %x.</target> <source>Press "Switch" to resolve issues in FreeFileSync main dialog.</source> <target>Nyomg meg a "Váltás" gombot az események kezeléséhez a FreeFileSync fő párbeszédablakában.</target> @@ -361,7 +361,7 @@ A parancs végrehajtódik, ha: <target>A csatlakozás a sourceforge.net-hez sikertelen!</target> <source>Current FreeFileSync version number was not found online! Do you want to check manually?</source> -<target></target> +<target>A jelenlegi FreeFileSync verziószám nem található meg online. Akarod manuálisan ellenőrizni?</target> <source>Do you want FreeFileSync to automatically check for updates every week?</source> <target>Akarod, hogy a FreeFileSync automatikusan minden héten keressen frissítést?</target> @@ -433,7 +433,7 @@ A parancs végrehajtódik, ha: <target>&Mentés</target> <source>Save as &batch job...</source> -<target></target> +<target>Mentés &kötegelt feladatként...</target> <source>1. &Compare</source> <target>1. &Összehasonlítás</target> @@ -481,16 +481,16 @@ A parancs végrehajtódik, ha: <target>Oldalak felcserélése</target> <source>Open</source> -<target></target> +<target>Megnyitás</target> <source>Save</source> -<target></target> +<target>Mentés</target> <source>Last used configurations (press DEL to remove from list)</source> <target>Utoljára használt beállítások (DEL billentyűvel törölhető a listából)</target> <source>Hide excluded items</source> -<target></target> +<target>Kizárt elemek elrejtése</target> <source>Show filtered or temporarily excluded files</source> <target>A szűrt vagy ideiglenesen kizárt fájlok mutatása</target> @@ -550,13 +550,13 @@ A parancs végrehajtódik, ha: <target>Szinkronizáció leállítása az első hibánál</target> <source>On completion</source> -<target></target> +<target>Befejezés esetén</target> <source>Show progress dialog</source> <target>Folyamatjelző párbeszédablak mutatása</target> <source>Save log</source> -<target></target> +<target>Naplófájl mentése</target> <source>Select folder to save log files</source> <target>Válaszd ki a mappát a naplófájlok mentéséhez</target> @@ -568,7 +568,7 @@ A parancs végrehajtódik, ha: <target>Naplófájlok maximális számának korlátozása</target> <source>Select variant</source> -<target></target> +<target>Változat kiválasztása</target> <source> Files are found equal if @@ -605,7 +605,7 @@ A fájlok megegyeznek, ha megegyezik <target>OK</target> <source><- Two way -></source> -<target></target> +<target><- Kétirányú -></target> <source>Identify and propagate changes on both sides using a database. Deletions, renaming and conflicts are detected automatically.</source> <target>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.</target> @@ -641,7 +641,7 @@ A fájlok megegyeznek, ha megegyezik <target>Lomtár (Recycle Bin)</target> <source>Use Recycle Bin for deleted and overwritten files</source> -<target></target> +<target>Lomtár használata a törölt és felülírt fájlokhoz</target> <source>Versioning</source> <target>Verziókövetés</target> @@ -650,7 +650,7 @@ A fájlok megegyeznek, ha megegyezik <target>Időbélyeges fájlok mozgatása a megadott mappába</target> <source>Naming convention:</source> -<target></target> +<target>Elnevezési konvenció:</target> <source>Configuration</source> <target>Beállítás</target> @@ -679,7 +679,7 @@ A fájlok megegyeznek, ha megegyezik <source>&Pause</source> <target>&Szünet</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>A programot C++-ban fejlesztették a következők felhasználásával:</target> <source>If you like FreeFileSync</source> @@ -958,6 +958,9 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony <source>Show files that won't be copied</source> <target>A nem másolandó fájlok mutatása</target> +<source>Set as default</source> +<target></target> + <source>All folders are in sync!</source> <target>Minden mappa szinkronban!</target> @@ -971,15 +974,6 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony <target>A fájllista exportálása befejeződött!</target> <source> -<pluralform>Object deleted successfully!</pluralform> -<pluralform>%x objects deleted successfully!</pluralform> -</source> -<target> -<pluralform>Sikeresen töröltük az objektumot!</pluralform> -<pluralform>Sikeresen töröltünk %x objektumot!</pluralform> -</target> - -<source> <pluralform>1 directory</pluralform> <pluralform>%x directories</pluralform> </source> @@ -1031,7 +1025,7 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony <target>Tartalom összehasonlítása...</target> <source>Copy</source> -<target></target> +<target>Másolás</target> <source>Paused</source> <target>Szüneteltetve</target> @@ -1106,13 +1100,13 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony <target>- csak mappa rész</target> <source>- Other side's counterpart to %item_path%</source> -<target></target> +<target>- A másik oldal megfelelőjét a következő fájlba: %item_path%</target> <source>- Other side's counterpart to %item_folder%</source> -<target></target> +<target>- A másik oldal megfelelőjét a következő mappába: %item_path%</target> <source>Make hidden warnings and dialogs visible again?</source> -<target></target> +<target>Tegyük újra láthatóvá a rejtett figyelmeztetéseket és párbeszédablakokat?</target> <source> <pluralform>Do you really want to move the following object to the Recycle Bin?</pluralform> @@ -1135,23 +1129,23 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony <source>Leave as unresolved conflict</source> <target>Feloldatlan ütközésként hagyni</target> +<source>Append a timestamp to each file name</source> +<target>Időbélyeg hozzáadása a minden fájlnévhez</target> + <source>Replace</source> -<target></target> +<target>Felülírás</target> <source>Move files and replace if existing</source> -<target></target> - -<source>Append a timestamp to each file name</source> -<target></target> +<target>Fájlok mozgatása és felülírása létezés esetén</target> <source>Folder</source> -<target></target> +<target>Mappa</target> <source>File</source> -<target></target> +<target>Fájl</target> <source>YYYY-MM-DD hhmmss</source> -<target></target> +<target>ÉÉÉÉ-HH-NN óóppmm</target> <source>Files</source> <target>Fájlok</target> @@ -1208,7 +1202,7 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony <target>%x fájl másolása a(z) %y fájlba sikertelen.</target> <source>Type of item %x is not supported:</source> -<target></target> +<target>A(z) %x elem típusa nem támogatott:</target> <source>Cannot open directory %x.</source> <target>A következő mappa megnyitása sikertelen: %x.</target> @@ -1276,6 +1270,27 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony <source>Setting default synchronization directions: Old files will be overwritten with newer files.</source> <target>Alapértelmezett szinkronizációs irányok beállítása: a régebbi fájlok felülíródnak az újabbakkal.</target> +<source>Checking recycle bin availability for folder %x...</source> +<target></target> + +<source>Moving file %x to recycle bin</source> +<target>%x fájl mozgatása a Lomtárba (Recycle Bin)</target> + +<source>Moving folder %x to recycle bin</source> +<target>%x mappa mozgatása a Lomtárba (Recycle Bin)</target> + +<source>Moving symbolic link %x to recycle bin</source> +<target>%x symlink mozgatása a Lomtárba (Recycle Bin)</target> + +<source>Deleting file %x</source> +<target>Fájl törlése %x</target> + +<source>Deleting folder %x</source> +<target>Mappa törlése %x</target> + +<source>Deleting symbolic link %x</source> +<target>Symlink törlése: %x</target> + <source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> <target>A Lomtár (Recycle Bin) nem elérhető a következő útvonalakhoz! A fájlok azonnali törlésre kerülnek helyette:</target> @@ -1357,24 +1372,6 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony <source>Multiple...</source> <target>Sokszorosítás</target> -<source>Deleting file %x</source> -<target>Fájl törlése %x</target> - -<source>Deleting folder %x</source> -<target>Mappa törlése %x</target> - -<source>Deleting symbolic link %x</source> -<target>Symlink törlése: %x</target> - -<source>Moving file %x to recycle bin</source> -<target>%x fájl mozgatása a Lomtárba (Recycle Bin)</target> - -<source>Moving folder %x to recycle bin</source> -<target>%x mappa mozgatása a Lomtárba (Recycle Bin)</target> - -<source>Moving symbolic link %x to recycle bin</source> -<target>%x symlink mozgatása a Lomtárba (Recycle Bin)</target> - <source>Moving file %x to %y</source> <target>%x fájl mozgatása ide: %y</target> @@ -1385,7 +1382,7 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony <target>%x symlink mozgatása ide: %y</target> <source>Removing old versions...</source> -<target></target> +<target>Régi verziók eltávolítása...</target> <source>Creating file %x</source> <target>%x fájl létrehozása</target> @@ -1421,7 +1418,7 @@ Megjegyzés: A fájlneveknek relatívnak kell lenniük az alap mappához viszony <target>A következő forrásmappa nem található: %x.</target> <source>The following items have unresolved conflicts and will not be synchronized:</source> -<target></target> +<target>A következő elemek feloldatlan ütközésekkel rendelkeznek és nem lesznek szinkronizálva:</target> <source>Significant difference detected:</source> <target>Jelentős különbség érzékelve:</target> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Selezionata interruazione: conclusione dell'operazione...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>Errore nella creazione di data e ora per il controllo delle versioni:</target> <source>RealtimeSync - Automated Synchronization</source> @@ -681,7 +681,7 @@ I file sono considerati identici se <source>&Pause</source> <target>&Pausa</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Codice sorgente scritto in C++ utilizzando:</target> <source>If you like FreeFileSync</source> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>ユーザによる中断: 現在の処理を終了しています.. お待ちください...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>バージョン付けのタイムスタンプ作成に失敗:</target> <source>RealtimeSync - Automated Synchronization</source> @@ -678,7 +678,7 @@ is the same <source>&Pause</source> <target>一時停止(&P)</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>ソースコードは C++ で書かれています</target> <source>If you like FreeFileSync</source> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>사용자에 의한 작업 중단 : 현재 작업 종료 대기 중...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>버저닝을 위한 타임 스탬프 생성 실패 :</target> <source>RealtimeSync - Automated Synchronization</source> @@ -678,7 +678,7 @@ is the same <source>&Pause</source> <target>일시정지(&P)</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>소스코드는 C++ 언어로 아래 툴을 사용하여 작성되었습니다 :</target> <source>If you like FreeFileSync</source> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Nutraukti: laukiama kol baigsis esama operacija...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>Nepavyo sukurti laiko žymės versijavimui:</target> <source>RealtimeSync - Automated Synchronization</source> @@ -687,7 +687,7 @@ yra toks pats <source>&Pause</source> <target>&Pauzė</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Šaltinio kodas parašytas su C++ naudojant:</target> <source>If you like FreeFileSync</source> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Avbrytelse forespurt: Venter på at gjeldende handling avsluttes...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target></target> <source>RealtimeSync - Automated Synchronization</source> @@ -59,7 +59,7 @@ <target>Nullstill filterinnstillinger</target> <source>Save as batch job</source> -<target></target> +<target>Lagre som sammensatt oppgave</target> <source>Comparison settings</source> <target>Innstillinger for sammenligning</target> @@ -224,7 +224,7 @@ <target>&Åpne</target> <source>Save &as...</source> -<target></target> +<target>Lagre &som</target> <source>&Quit</source> <target>&Avslutt</target> @@ -272,7 +272,7 @@ <target>Velg en mappe</target> <source>Idle time [seconds]</source> -<target></target> +<target>Ventetid [sekunder]</target> <source>Idle time between last detected change and execution of command</source> <target>Ventetid mellom forrige endring og utførelse av kommando</target> @@ -328,16 +328,16 @@ Kommandoen utløses hvis: <target>Synkronisering fullført med feil!</target> <source>Synchronization completed with warnings.</source> -<target></target> +<target>Synkronisering fullført med advarsler.</target> <source>Nothing to synchronize!</source> <target>Ikke noe å synkronisere!</target> <source>Synchronization completed successfully.</source> -<target></target> +<target>Synkdonisering fullført.</target> <source>Saving log file %x...</source> -<target></target> +<target>Lagrer loggfil %x...</target> <source>Press "Switch" to resolve issues in FreeFileSync main dialog.</source> <target>Trykk "Skift" for å løse problemer i FreeFileSync hovedvindu.</target> @@ -361,7 +361,7 @@ Kommandoen utløses hvis: <target>Ikke i stand til å koble til sourceforge.net!</target> <source>Current FreeFileSync version number was not found online! Do you want to check manually?</source> -<target></target> +<target>Nyeste FreeFileSyncversjon ble ikke funnet online! Vil du sjekke manuelt?</target> <source>Do you want FreeFileSync to automatically check for updates every week?</source> <target>Skal FreeFileSync automatisk se etter oppdateringer hver uke?</target> @@ -433,7 +433,7 @@ Kommandoen utløses hvis: <target>&Lagre</target> <source>Save as &batch job...</source> -<target></target> +<target>Lagre som &sammensatt jobb...</target> <source>1. &Compare</source> <target>1. Sammen&lign</target> @@ -459,18 +459,12 @@ Kommandoen utløses hvis: <source>Compare</source> <target>Sammenlign</target> -<source>Compare both sides</source> -<target>Sammenlign begge sider</target> - <source>&Abort</source> <target>&Avbryt</target> <source>Synchronize</source> <target>Synkroniser</target> -<source>Start synchronization</source> -<target>Start synkronisering</target> - <source>Add folder pair</source> <target>Legg til mappepar</target> @@ -480,20 +474,11 @@ Kommandoen utløses hvis: <source>Swap sides</source> <target>Bytt sider</target> -<source>Open</source> -<target></target> - -<source>Save</source> -<target></target> - -<source>Last used configurations (press DEL to remove from list)</source> -<target>Sist brukte innstillinger (trykk DEL for å fjerne fra liste)</target> - <source>Hide excluded items</source> -<target></target> +<target>Gjem ekskluderte elementer</target> <source>Show filtered or temporarily excluded files</source> -<target></target> +<target>Vis filtrerte eller midlertidig ekskluderte filer</target> <source>Number of files and folders that will be created</source> <target>Antall filer og mapper som vil opprettes</target> @@ -532,43 +517,43 @@ Kommandoen utløses hvis: <target>Feilhåndtering</target> <source>Ignore</source> -<target></target> +<target>Ignorer</target> <source>Hide all error and warning messages</source> <target>Skjul feilmeldinger og advarsler</target> <source>Pop-up</source> -<target></target> +<target>Pop-up</target> <source>Show pop-up on errors or warnings</source> <target>Vis pop-upvindu ved feil eller advarsler</target> <source>Exit</source> -<target></target> +<target>Avslutt</target> <source>Abort synchronization on first error</source> -<target></target> +<target>Stopp synkronisering ved første feil</target> <source>On completion</source> -<target></target> +<target>Ved fullføring</target> <source>Show progress dialog</source> <target>Vis dialogboks</target> <source>Save log</source> -<target></target> +<target>Lagre logg</target> <source>Select folder to save log files</source> -<target></target> +<target>Velg mappe du vil lagre loggfiler i</target> <source>Limit</source> -<target></target> +<target>Grense</target> <source>Limit maximum number of log files</source> -<target></target> +<target>Begrens maks antall loggfiler</target> <source>Select variant</source> -<target></target> +<target>Velg variant</target> <source> Files are found equal if @@ -607,7 +592,7 @@ er det samme <target>OK</target> <source><- Two way -></source> -<target></target> +<target><- Toveis -></target> <source>Identify and propagate changes on both sides using a database. Deletions, renaming and conflicts are detected automatically.</source> <target>Identifiser og spre endringer på begge sider ved å bruke en database. Slettinger, navneendringer og konflikter blir automatisk oppdaget.</target> @@ -634,25 +619,25 @@ er det samme <target>Slette-håndtering</target> <source>Permanent</source> -<target></target> +<target>Permanent</target> <source>Delete or overwrite files permanently</source> <target>Slett eller overskriv filer permanent</target> <source>Recycle Bin</source> -<target></target> +<target>Papirkurv</target> <source>Use Recycle Bin for deleted and overwritten files</source> -<target></target> +<target>Bruk papirkurv for slettede og overskrevne filer</target> <source>Versioning</source> <target>Versjonshåndtering</target> <source>Move time-stamped files into specified folder</source> -<target></target> +<target>Flytt tidsstemplede filer den valgte mappen</target> <source>Naming convention:</source> -<target></target> +<target>Navnekonvensjon:</target> <source>Configuration</source> <target>Innstilling</target> @@ -681,7 +666,7 @@ er det samme <source>&Pause</source> <target>&Pause</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Kildekode skrevet i C++ med hjelp fra:</target> <source>If you like FreeFileSync</source> @@ -691,7 +676,7 @@ er det samme <target>Doner med PayPal</target> <source>Many thanks for localization:</source> -<target></target> +<target>Mange takk for lokalisering:</target> <source>Feedback and suggestions are welcome</source> <target>Tilbakemelding og forslag er velkomne</target> @@ -771,6 +756,9 @@ Merk: Filnavn må være relative til basismapper! <source>Description</source> <target>Beskrivelse</target> +<source>Start synchronization</source> +<target>Start synkronisering</target> + <source>Variant</source> <target>Variant</target> @@ -778,7 +766,7 @@ Merk: Filnavn må være relative til basismapper! <target>Statistikk</target> <source>Don't show this dialog again</source> -<target></target> +<target>Ikke vis dette vinduet igjen</target> <source>Find what:</source> <target>Søk hva:</target> @@ -807,6 +795,15 @@ Merk: Filnavn må være relative til basismapper! <source>Select view</source> <target>Velg visning</target> +<source>Open...</source> +<target></target> + +<source>Save</source> +<target>Lagre</target> + +<source>Compare both sides</source> +<target>Sammenlign begge sider</target> + <source>Set direction:</source> <target>Still inn retningen:</target> @@ -871,7 +868,7 @@ Merk: Filnavn må være relative til basismapper! <target>Vil du lagre endringene til %x?</target> <source>Do&n't save</source> -<target></target> +<target>&Ikke lagre</target> <source>Configuration loaded!</source> <target>Innstilling lastet!</target> @@ -960,9 +957,12 @@ Merk: Filnavn må være relative til basismapper! <source>Show files that won't be copied</source> <target>Vis filer som ikke blir kopiert</target> -<source>All folders are in sync!</source> +<source>Set as default</source> <target></target> +<source>All folders are in sync!</source> +<target>Alle mapper er synkroniserte!</target> + <source>Comma separated list</source> <target>Komma-separert liste</target> @@ -973,15 +973,6 @@ Merk: Filnavn må være relative til basismapper! <target>Filliste eksportert!</target> <source> -<pluralform>Object deleted successfully!</pluralform> -<pluralform>%x objects deleted successfully!</pluralform> -</source> -<target> -<pluralform>Objekt vellykket slettet!</pluralform> -<pluralform>%x objekter vellykket slettet!</pluralform> -</target> - -<source> <pluralform>1 directory</pluralform> <pluralform>%x directories</pluralform> </source> @@ -1033,7 +1024,7 @@ Merk: Filnavn må være relative til basismapper! <target>Sammenligner innhold...</target> <source>Copy</source> -<target></target> +<target>Kopier</target> <source>Paused</source> <target>Pauset</target> @@ -1108,13 +1099,13 @@ Merk: Filnavn må være relative til basismapper! <target>- kun mapper</target> <source>- Other side's counterpart to %item_path%</source> -<target></target> +<target>- Den andre sidens motpart til %item_path%</target> <source>- Other side's counterpart to %item_folder%</source> -<target></target> +<target>- Den andre sidens motpart til %item_folder%</target> <source>Make hidden warnings and dialogs visible again?</source> -<target></target> +<target>Gjør skjylte advarsler og dialoger synlige igjen?</target> <source> <pluralform>Do you really want to move the following object to the Recycle Bin?</pluralform> @@ -1137,23 +1128,23 @@ Merk: Filnavn må være relative til basismapper! <source>Leave as unresolved conflict</source> <target>Etterlat som uløste konflikter</target> +<source>Append a timestamp to each file name</source> +<target>Legg til et tidsstempel til hvert filnavn</target> + <source>Replace</source> -<target></target> +<target>Erstatt</target> <source>Move files and replace if existing</source> -<target></target> - -<source>Append a timestamp to each file name</source> -<target></target> +<target>Flytt filer og erstatt hvis eksisterer</target> <source>Folder</source> -<target></target> +<target>Mappe</target> <source>File</source> -<target></target> +<target>Fil</target> <source>YYYY-MM-DD hhmmss</source> -<target></target> +<target>ÅÅÅÅ-MM-DD ttmmss</target> <source>Files</source> <target>Filer</target> @@ -1210,13 +1201,13 @@ Merk: Filnavn må være relative til basismapper! <target>Kan ikke kopiere filen %x til %y.</target> <source>Type of item %x is not supported:</source> -<target></target> +<target>Type element %x er ikke støttet:</target> <source>Cannot open directory %x.</source> -<target></target> +<target>Kan ikke åpne mappe %x.</target> <source>Cannot enumerate directory %x.</source> -<target></target> +<target>Kan ikke gjennomgå mappe %x.</target> <source>Detected endless directory recursion.</source> <target>Fant uendelig dyp mappestruktur (lenker til mapper høyere opp i strukturen)</target> @@ -1278,6 +1269,27 @@ Merk: Filnavn må være relative til basismapper! <source>Setting default synchronization directions: Old files will be overwritten with newer files.</source> <target>Stiller inn standard synkroniseringsretning: Gamle filer blir overskrevet med nyere filer.</target> +<source>Checking recycle bin availability for folder %x...</source> +<target></target> + +<source>Moving file %x to recycle bin</source> +<target>Flytter fil %x til papirkurv</target> + +<source>Moving folder %x to recycle bin</source> +<target>Flytter mappe %x til papirkurv</target> + +<source>Moving symbolic link %x to recycle bin</source> +<target>Flytter symbolsk lenke %x til papirkurv</target> + +<source>Deleting file %x</source> +<target>Sletter fil %x</target> + +<source>Deleting folder %x</source> +<target>Sletter mappe %x</target> + +<source>Deleting symbolic link %x</source> +<target>Sletter symbolsk lenke %x</target> + <source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> <target>Papirkurv er ikke tilgjengelig for de følgende baner! Filer blir isteden slettet permanent:</target> @@ -1291,7 +1303,7 @@ Merk: Filnavn må være relative til basismapper! <target>Mapper er avhengige av hverandre! Vær forsiktig når du setter opp synkroniseringsregler:</target> <source>Start comparison</source> -<target></target> +<target>Start sammenligning</target> <source>Preparing synchronization...</source> <target>Forbereder synkronisering...</target> @@ -1306,7 +1318,7 @@ Merk: Filnavn må være relative til basismapper! <target>Filer %x har den samme datoen, men forskjellig størrelse!</target> <source>Items differ in attributes only</source> -<target></target> +<target>Elementer har kun forskjellige attributter</target> <source>Symbolic links %x have the same date but a different target.</source> <target>Symbolske lenker %x har den samme datoen, men forskjellige mål.</target> @@ -1359,24 +1371,6 @@ Merk: Filnavn må være relative til basismapper! <source>Multiple...</source> <target>Flere...</target> -<source>Deleting file %x</source> -<target>Sletter fil %x</target> - -<source>Deleting folder %x</source> -<target>Sletter mappe %x</target> - -<source>Deleting symbolic link %x</source> -<target>Sletter symbolsk lenke %x</target> - -<source>Moving file %x to recycle bin</source> -<target>Flytter fil %x til papirkurv</target> - -<source>Moving folder %x to recycle bin</source> -<target>Flytter mappe %x til papirkurv</target> - -<source>Moving symbolic link %x to recycle bin</source> -<target>Flytter symbolsk lenke %x til papirkurv</target> - <source>Moving file %x to %y</source> <target>Flytter fil %x til %y</target> @@ -1387,7 +1381,7 @@ Merk: Filnavn må være relative til basismapper! <target>Flytter symbolsk lenke %x til %y</target> <source>Removing old versions...</source> -<target></target> +<target>Fjerner gamle versjoner...</target> <source>Creating file %x</source> <target>Oppretter fil %x</target> @@ -1423,7 +1417,7 @@ Merk: Filnavn må være relative til basismapper! <target>Kildemappe %x finnes ikke.</target> <source>The following items have unresolved conflicts and will not be synchronized:</source> -<target></target> +<target>De følgende elementene har uoppklarte konflikter og vil ikke bli synkroniserte:</target> <source>Significant difference detected:</source> <target>Betydelig forskjell oppdaget:</target> @@ -1450,7 +1444,7 @@ Merk: Filnavn må være relative til basismapper! <target>Høyre</target> <source>Synchronizing folder pair:</source> -<target></target> +<target>Synkroniserer mappepar:</target> <source>Generating database...</source> <target>Oppretter database...</target> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Żądanie przerwania: Oczekiwanie na koniec aktualnie wykonywanego zadania...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>Nie można utworzyć sygnatury czasu na potrzeby wersjonowania:</target> <source>RealtimeSync - Automated Synchronization</source> @@ -684,7 +684,7 @@ jest identyczna <source>&Pause</source> <target>&Pauza</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Kod stworzony w C++ z wykorzystaniem:</target> <source>If you like FreeFileSync</source> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Abortar pedido: À espera do fim da operação atual...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>Falha ao criar selo temporal para versões anteriores:</target> <source>RealtimeSync - Automated Synchronization</source> @@ -680,7 +680,7 @@ Os ficheiros são considerados iguais se <source>&Pause</source> <target>&Pausa</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Código fonte escrito em C++ utilizando:</target> <source>If you like FreeFileSync</source> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Cancelar solicitado: Esperando fim da operação...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>Erro ao criar estampa de tempo para controle de versão:</target> <source>RealtimeSync - Automated Synchronization</source> @@ -681,7 +681,7 @@ Os arquivos são considerados iguais se <source>&Pause</source> <target>&Pausar</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Código-fonte escrito em C++ utilizando:</target> <source>If you like FreeFileSync</source> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Abandonare solicitată: Se așteaptă terminarea operației în curs...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>Crearea marcajului temporal pentru versionare a eșuat</target> <source>RealtimeSync - Automated Synchronization</source> @@ -684,7 +684,7 @@ este același <source>&Pause</source> <target>&Pauzează</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Cod sursă scris în C++ folosind:</target> <source>If you like FreeFileSync</source> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Запрос отмены: Ожидайте, пока текущая операция завершится...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>Неспособность создать отметку времени для архивации файлов:</target> <source>RealtimeSync - Automated Synchronization</source> @@ -682,7 +682,7 @@ is the same <source>&Pause</source> <target>&Пауза</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Исходный код написан на C++ с использованием:</target> <source>If you like FreeFileSync</source> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Tha thu airson sgur dheth: A' feitheamh gus an crìochnaich an gnìomh làithreach...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>Cha b' urrainn dhuinn stampa-ama a chruthachadh airson versioning:</target> <source>RealtimeSync - Automated Synchronization</source> @@ -686,7 +686,7 @@ Bidh dà fhaidhle co-ionnann 'nar beachd-sa <source>&Pause</source> <target>&Cuir 'na stad</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Chaidh an còd tùsail a sgrìobhadh ann an C++ le taic:</target> <source>If you like FreeFileSync</source> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Zahtevana je bila prekinitev: čakam, da se zaključi trenutna operacija...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>Napaka pri ustvarjanju časovne oznake pri ustvarjanju različic:</target> <source>RealtimeSync - Automated Synchronization</source> @@ -687,7 +687,7 @@ enaka <source>&Pause</source> <target>&Premor</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Izvorna koda napisana v C++ z uporabo:</target> <source>If you like FreeFileSync</source> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Solicitud de aborto: Esperando a que la operación actual finalice...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>Fallo al crear fecha y hora para el versionado:</target> <source>RealtimeSync - Automated Synchronization</source> @@ -681,7 +681,7 @@ es el mismo <source>&Pause</source> <target>&Pausa</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Código fuente escrito en C++ utilizando:</target> <source>If you like FreeFileSync</source> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Avbryter: Väntar på att aktuell process skall slutföras...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>Kunde inte skapa tidsstämpel för versionshantering</target> <source>RealtimeSync - Automated Synchronization</source> @@ -681,7 +681,7 @@ Filerna betecknas som lika om, <source>&Pause</source> <target>&Paus</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Källkod skriven i C++ med hjälp av:</target> <source>If you like FreeFileSync</source> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Vazgeçildi: Yürürlükteki işlemin bitmesi bekleniyor...</target> -<source>Failure to create time stamp for versioning:</source> +<source>Failure to create timestamp for versioning:</source> <target>Eski sürüm için zaman damgası oluşturulamadı:</target> <source>RealtimeSync - Automated Synchronization</source> @@ -681,7 +681,7 @@ aynı olmalıdır <source>&Pause</source> <target>&Duraklat</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Kaynak kodu C++ kullanılarak yazılmıştır:</target> <source>If you like FreeFileSync</source> 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 @@ <source>Abort requested: Waiting for current operation to finish...</source> <target>Запит переривання: В очікуванні завершення поточної операції...</target> -<source>Failure to create time stamp for versioning:</source> -<target></target> +<source>Failure to create timestamp for versioning:</source> +<target>Не вдається створити часової мітки для версій:</target> <source>RealtimeSync - Automated Synchronization</source> <target>RealtimeSync - Автоматична синхронізація</target> @@ -59,7 +59,7 @@ <target>Очистити налаштування фільтра</target> <source>Save as batch job</source> -<target></target> +<target>Зберегти як пакетне завдання</target> <source>Comparison settings</source> <target>Налаштування порівнювання</target> @@ -275,7 +275,7 @@ <target>Вибрати папку</target> <source>Idle time [seconds]</source> -<target></target> +<target>Час простою [секунд]</target> <source>Idle time between last detected change and execution of command</source> <target>Час простою між виявленням останньої зміни та виконанням команди</target> @@ -331,16 +331,16 @@ The command is triggered if: <target>Синхронізація закінчилася з помилками!</target> <source>Synchronization completed with warnings.</source> -<target></target> +<target>Синхронізація завершена з попередженнями.</target> <source>Nothing to synchronize!</source> <target>Нічого синхронізувати!</target> <source>Synchronization completed successfully.</source> -<target></target> +<target>Синхронізація успішно завершена.</target> <source>Saving log file %x...</source> -<target></target> +<target>Збереження файла журналу %x...</target> <source>Press "Switch" to resolve issues in FreeFileSync main dialog.</source> <target>Натисніть "Змінити" для вирішення питань у головному вікні FreeFileSync.</target> @@ -364,7 +364,7 @@ The command is triggered if: <target>Не можна з’єднатися з sourceforge.net!</target> <source>Current FreeFileSync version number was not found online! Do you want to check manually?</source> -<target></target> +<target>Номер поточної версії FreeFileSync не знайдений онлайн! Хочете перевірити вручну?</target> <source>Do you want FreeFileSync to automatically check for updates every week?</source> <target>Ви хочете, щоб FreeFileSync автоматично перевіряв наявність оновлень щотижня?</target> @@ -436,7 +436,7 @@ The command is triggered if: <target>Зберегти</target> <source>Save as &batch job...</source> -<target></target> +<target>Зберегти як пакетне завдання</target> <source>1. &Compare</source> <target>1. &Порівняти</target> @@ -484,10 +484,10 @@ The command is triggered if: <target>Поміняти місцями</target> <source>Open</source> -<target></target> +<target>Відкрити</target> <source>Save</source> -<target></target> +<target>Зберегти</target> <source>Last used configurations (press DEL to remove from list)</source> <target> @@ -496,7 +496,7 @@ The command is triggered if: </target> <source>Hide excluded items</source> -<target></target> +<target>Приховати виключені елементи</target> <source>Show filtered or temporarily excluded files</source> <target>Показати відфільтровані чи тимчасово виключені елементи</target> @@ -556,13 +556,13 @@ The command is triggered if: <target>Перервати синхронізацію при першій помилці</target> <source>On completion</source> -<target></target> +<target>Після завершення</target> <source>Show progress dialog</source> <target>Показувати вікно прогресу</target> <source>Save log</source> -<target></target> +<target>Зберегти журнал</target> <source>Select folder to save log files</source> <target>Виберіть папку для файлів журналу</target> @@ -574,7 +574,7 @@ The command is triggered if: <target>Обмежити максимальну кількість файлів журналу</target> <source>Select variant</source> -<target></target> +<target>Виберіть варіант</target> <source> Files are found equal if @@ -609,7 +609,7 @@ is the same <target>OK</target> <source><- Two way -></source> -<target></target> +<target><- Обидва напрямки -></target> <source>Identify and propagate changes on both sides using a database. Deletions, renaming and conflicts are detected automatically.</source> <target>Виявити та поширити зміни на обидві сторони використовуючи базу даних. Видалення, перейменування та конфлікти визначаються автоматично.</target> @@ -645,7 +645,7 @@ is the same <target>Корзина</target> <source>Use Recycle Bin for deleted and overwritten files</source> -<target></target> +<target>Використати Корзину для видалених і перезаписаних файлів</target> <source>Versioning</source> <target>Запис версій</target> @@ -654,7 +654,7 @@ is the same <target>Перемістити файли з часовою міткою у вказану папку</target> <source>Naming convention:</source> -<target></target> +<target>Метод іменування:</target> <source>Configuration</source> <target>Налаштування</target> @@ -683,7 +683,7 @@ is the same <source>&Pause</source> <target>&Пауза</target> -<source>Source code written in C++ utilizing:</source> +<source>Source code written in C++ using:</source> <target>Код програми написаний на C++ з використанням:</target> <source>If you like FreeFileSync</source> @@ -809,6 +809,12 @@ Note: File names must be relative to base directories! <source>Select view</source> <target>Список файлів</target> +<source>Deleting file %x</source> +<target>Вилучення файлу %x</target> + +<source>Deleting %x</source> +<target></target> + <source>Set direction:</source> <target>Виберіть напрям:</target> @@ -962,6 +968,9 @@ Note: File names must be relative to base directories! <source>Show files that won't be copied</source> <target>Показати файли, які не будуть зкопійовані</target> +<source>Set as default</source> +<target></target> + <source>All folders are in sync!</source> <target>Всі папки синхронізовано!</target> @@ -975,16 +984,6 @@ Note: File names must be relative to base directories! <target>Список файлів експортовано!</target> <source> -<pluralform>Object deleted successfully!</pluralform> -<pluralform>%x objects deleted successfully!</pluralform> -</source> -<target> -<pluralform>%x об'єкт успішно вилучено!</pluralform> -<pluralform>%x об'єкти успішно вилучено!</pluralform> -<pluralform>%x об'єктів успішно вилучено!</pluralform> -</target> - -<source> <pluralform>1 directory</pluralform> <pluralform>%x directories</pluralform> </source> @@ -1039,7 +1038,7 @@ Note: File names must be relative to base directories! <target>Порівнювання вмісту...</target> <source>Copy</source> -<target></target> +<target>Копія</target> <source>Paused</source> <target>Призупинено</target> @@ -1114,13 +1113,13 @@ Note: File names must be relative to base directories! <target>- тільки папка</target> <source>- Other side's counterpart to %item_path%</source> -<target></target> +<target>- Елемент з протилежної сторони до %item_path%</target> <source>- Other side's counterpart to %item_folder%</source> -<target></target> +<target>- Елемент з протилежної сторони до %item_folder%</target> <source>Make hidden warnings and dialogs visible again?</source> -<target></target> +<target>Зробити приховані попередження та діалоги знову видимими?</target> <source> <pluralform>Do you really want to move the following object to the Recycle Bin?</pluralform> @@ -1146,22 +1145,22 @@ Note: File names must be relative to base directories! <target>Залишити як невирішений конфлікт</target> <source>Replace</source> -<target></target> +<target>Замінити</target> <source>Move files and replace if existing</source> -<target></target> +<target>Перемістити файли замінюючи існуючі</target> <source>Append a timestamp to each file name</source> -<target></target> +<target>Додати часову мітку до кожного імені файлу</target> <source>Folder</source> -<target></target> +<target>Папка</target> <source>File</source> -<target></target> +<target>Файл</target> <source>YYYY-MM-DD hhmmss</source> -<target></target> +<target>YYYY-MM-DD hhmmss</target> <source>Files</source> <target>Файли</target> @@ -1218,7 +1217,7 @@ Note: File names must be relative to base directories! <target>Не вдається зкопіювати файл %х до %y.</target> <source>Type of item %x is not supported:</source> -<target></target> +<target>Тип елемента %х не підтримується:</target> <source>Cannot open directory %x.</source> <target>Не вдається відкрити каталогу %x.</target> @@ -1292,6 +1291,9 @@ Note: File names must be relative to base directories! Старі файли будуть замінені новішими файлами. </target> +<source>Checking recycle bin availability for folder %x...</source> +<target></target> + <source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> <target>Корзина для цього шляху недоступна. Файли будуть вилучені назавжди:</target> @@ -1373,9 +1375,6 @@ Note: File names must be relative to base directories! <source>Multiple...</source> <target>Різні варіанти...</target> -<source>Deleting file %x</source> -<target>Вилучення файлу %x</target> - <source>Deleting folder %x</source> <target>Вилучення папки %x</target> @@ -1401,7 +1400,7 @@ Note: File names must be relative to base directories! <target>Переміщення символьного посилання %x до %y</target> <source>Removing old versions...</source> -<target></target> +<target>Видалення старих версій...</target> <source>Creating file %x</source> <target>Створення файлу %x</target> @@ -1437,7 +1436,7 @@ Note: File names must be relative to base directories! <target>Вихідний каталог %x не знайдено.</target> <source>The following items have unresolved conflicts and will not be synchronized:</source> -<target></target> +<target>Наступні елементи мають невирішені конфлікти і не будуть синхронізовані:</target> <source>Significant difference detected:</source> <target>Виявлена істотна різниця:</target> diff --git a/BUILD/Resources.zip b/BUILD/Resources.zip Binary files differindex 3b646713..79efed0d 100644 --- a/BUILD/Resources.zip +++ b/BUILD/Resources.zip diff --git a/FreeFileSync.cbp b/FreeFileSync.cbp deleted file mode 100644 index f2abfcc6..00000000 --- a/FreeFileSync.cbp +++ /dev/null @@ -1,538 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> -<CodeBlocks_project_file> - <FileVersion major="1" minor="6" /> - <Project> - <Option title="FreeFileSync" /> - <Option makefile="makefile" /> - <Option pch_mode="2" /> - <Option compiler="gcc" /> - <Build> - <Target title="Release"> - <Option output="BUILD/Bin/FreeFileSync_Win32" prefix_auto="1" extension_auto="1" /> - <Option working_dir="BUILD/" /> - <Option object_output="OBJ/FFS_Release_32_MinGW/" /> - <Option type="0" /> - <Option compiler="gcc" /> - <Option projectCompilerOptionsRelation="2" /> - <Option projectLinkerOptionsRelation="2" /> - <Compiler> - <Add option="-O3" /> - <Add option="-DNDEBUG" /> - <Add directory="C:/Programme/C++/wxWidgets/lib/mingw_release_lib/mswu" /> - </Compiler> - <Linker> - <Add option="-s" /> - <Add option="-static" /> - <Add library="libboost_thread-mgw47-mt-s-1_52.a" /> - <Add library="libboost_system-mgw47-mt-s-1_52.a" /> - <Add library="libwxmsw28u_aui.a" /> - <Add library="libwxmsw28u_adv.a" /> - <Add library="libwxmsw28u_core.a" /> - <Add library="libwxbase28u.a" /> - <Add library="libwxbase28u_net.a" /> - <Add library="libwxpng.a" /> - <Add library="libwxzlib.a" /> - <Add directory="C:/Programme/C++/wxWidgets/lib/mingw_release_lib" /> - </Linker> - <ExtraCommands> - <Add after='"C:\Program Files\C++\CodeSigning\SignCode.cmd" "$(PROJECT_DIR)$(TARGET_OUTPUT_FILE)"' /> - </ExtraCommands> - </Target> - <Target title="Debug-DLL"> - <Option output="BUILD/Bin/FreeFileSync_Debug" prefix_auto="1" extension_auto="1" /> - <Option working_dir="BUILD" /> - <Option object_output="OBJ/FFS_Debug_32_MinGW/" /> - <Option type="0" /> - <Option compiler="gcc" /> - <Option projectCompilerOptionsRelation="2" /> - <Option projectLinkerOptionsRelation="2" /> - <Compiler> - <Add option="-g" /> - <Add option="-Winvalid-pch" /> - <Add option='-include "wx+/pch.h"' /> - <Add option="-D__WXDEBUG__" /> - <Add directory="C:/Programme/C++/wxWidgets/lib/mingw_debug_dll/mswud" /> - </Compiler> - <Linker> - <Add library="libboost_thread-mgw47-mt-sd-1_52.a" /> - <Add library="libboost_system-mgw47-mt-sd-1_52.a" /> - <Add library="libwxmsw28ud_aui.a" /> - <Add library="libwxmsw28ud_adv.a" /> - <Add library="libwxmsw28ud_core.a" /> - <Add library="libwxbase28ud.a" /> - <Add library="libwxbase28ud_net.a" /> - <Add library="libwxzlibd.a" /> - <Add directory="C:/Programme/C++/wxWidgets/lib/mingw_debug_dll" /> - </Linker> - </Target> - <Target title="Unit Test"> - <Option output="OBJ/Unit_Test_MinGW/Unit Test" prefix_auto="1" extension_auto="1" /> - <Option working_dir="OBJ/Unit_Test_MinGW/" /> - <Option object_output="OBJ/Unit_Test_MinGW/" /> - <Option type="1" /> - <Option compiler="gcc" /> - <Option projectCompilerOptionsRelation="2" /> - <Option projectLinkerOptionsRelation="2" /> - <Compiler> - <Add option="-g" /> - <Add option="-Winvalid-pch" /> - <Add option='-include "wx+/pch.h"' /> - <Add option="-D__WXDEBUG__" /> - <Add directory="C:/Programme/C++/wxWidgets/lib/mingw_debug_dll/mswud" /> - <Add directory="lib/gtest" /> - <Add directory="lib/gtest/include" /> - </Compiler> - <Linker> - <Add library="libboost_thread-mgw47-mt-sd-1_52.a" /> - <Add library="libboost_system-mgw47-mt-sd-1_52.a" /> - <Add library="libwxmsw28ud_adv.a" /> - <Add library="libwxmsw28ud_core.a" /> - <Add library="libwxbase28ud.a" /> - <Add library="libwxpngd.a" /> - <Add library="libwxzlibd.a" /> - <Add directory="C:/Programme/C++/wxWidgets/lib/mingw_debug_dll" /> - </Linker> - </Target> - </Build> - <Compiler> - <Add option="-Wshadow" /> - <Add option="-Winit-self" /> - <Add option="-Wredundant-decls" /> - <Add option="-Wcast-align" /> - <Add option="-Wunreachable-code" /> - <Add option="-Wmissing-include-dirs" /> - <Add option="-Wswitch-enum" /> - <Add option="-Wmain" /> - <Add option="-std=c++11" /> - <Add option="-Wall" /> - <Add option="-pipe" /> - <Add option="-mthreads" /> - <Add option="-fno-omit-frame-pointer" /> - <Add option='-include "zen/warn_static.h"' /> - <Add option="-std=gnu++0x" /> - <Add option="-D__GNUWIN32__" /> - <Add option="-D__WXMSW__" /> - <Add option="-DFFS_WIN" /> - <Add option="-DwxUSE_UNICODE" /> - <Add option="-DBOOST_THREAD_USE_LIB" /> - <Add option="-DWXINTL_NO_GETTEXT_MACRO" /> - <Add option="-DZEN_PLATFORM_WINDOWS" /> - <Add directory="C:/Programme/C++/wxWidgets/include" /> - <Add directory="C:/Program Files/C++/Boost" /> - <Add directory="." /> - </Compiler> - <ResourceCompiler> - <Add directory="C:/Programme/C++/wxWidgets/include" /> - </ResourceCompiler> - <Linker> - <Add option="-mthreads" /> - <Add library="libkernel32.a" /> - <Add library="libuser32.a" /> - <Add library="libuuid.a" /> - <Add library="libcomctl32.a" /> - <Add library="libole32.a" /> - <Add library="liboleaut32.a" /> - <Add library="libgdi32.a" /> - <Add library="libcomdlg32.a" /> - <Add library="libws2_32.a" /> - <Add library="libwinspool.a" /> - <Add library="libwinmm.a" /> - <Add library="libmpr.a" /> - <Add library="libuxtheme.a" /> - <Add library="libwininet.a" /> - <Add directory="C:/Program Files/C++/Boost/stage/lib" /> - </Linker> - <Unit filename="WxWizFrame.fbp"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="algorithm.cpp" /> - <Unit filename="algorithm.h" /> - <Unit filename="application.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="application.h" /> - <Unit filename="comparison.cpp" /> - <Unit filename="comparison.h" /> - <Unit filename="file_hierarchy.cpp" /> - <Unit filename="file_hierarchy.h" /> - <Unit filename="lib/binary.cpp" /> - <Unit filename="lib/binary.h" /> - <Unit filename="lib/cmp_filetime.h" /> - <Unit filename="lib/db_file.cpp" /> - <Unit filename="lib/db_file.h" /> - <Unit filename="lib/dir_exist_async.h" /> - <Unit filename="lib/dir_lock.cpp" /> - <Unit filename="lib/dir_lock.h" /> - <Unit filename="lib/error_log.h" /> - <Unit filename="lib/ffs_paths.h" /> - <Unit filename="lib/generate_logfile.h" /> - <Unit filename="lib/gtest/main.cpp"> - <Option target="Unit Test" /> - </Unit> - <Unit filename="lib/gtest/src/gtest-all.cc"> - <Option target="Unit Test" /> - </Unit> - <Unit filename="lib/gtest/unittest.h"> - <Option target="Unit Test" /> - </Unit> - <Unit filename="lib/gtest/unittest1.cpp"> - <Option target="Unit Test" /> - </Unit> - <Unit filename="lib/gtest/unittest2.cpp"> - <Option target="Unit Test" /> - </Unit> - <Unit filename="lib/gtest/unittest3.cpp"> - <Option target="Unit Test" /> - </Unit> - <Unit filename="lib/hard_filter.cpp" /> - <Unit filename="lib/hard_filter.h" /> - <Unit filename="lib/help_provider.h" /> - <Unit filename="lib/icon_buffer.cpp" /> - <Unit filename="lib/icon_buffer.h" /> - <Unit filename="lib/localization.cpp" /> - <Unit filename="lib/localization.h" /> - <Unit filename="lib/lock_holder.h" /> - <Unit filename="lib/norm_filter.h" /> - <Unit filename="lib/parallel_scan.cpp" /> - <Unit filename="lib/parallel_scan.h" /> - <Unit filename="lib/parse_lng.h" /> - <Unit filename="lib/parse_plural.h" /> - <Unit filename="lib/perf_check.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="lib/perf_check.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="lib/process_xml.cpp" /> - <Unit filename="lib/process_xml.h" /> - <Unit filename="lib/resolve_path.cpp" /> - <Unit filename="lib/resolve_path.h" /> - <Unit filename="lib/resources.cpp" /> - <Unit filename="lib/resources.h" /> - <Unit filename="lib/return_codes.h" /> - <Unit filename="lib/shadow.cpp" /> - <Unit filename="lib/shadow.h" /> - <Unit filename="lib/soft_filter.h" /> - <Unit filename="lib/status_handler.cpp" /> - <Unit filename="lib/status_handler.h" /> - <Unit filename="lib/versioning.cpp" /> - <Unit filename="lib/versioning.h" /> - <Unit filename="lib/xml_base.cpp" /> - <Unit filename="lib/xml_base.h" /> - <Unit filename="resource.rc"> - <Option compilerVar="WINDRES" /> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="structures.cpp" /> - <Unit filename="structures.h" /> - <Unit filename="synchronization.cpp" /> - <Unit filename="synchronization.h" /> - <Unit filename="ui/batch_config.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/batch_config.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/batch_status_handler.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/batch_status_handler.h" /> - <Unit filename="ui/check_version.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/check_version.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/custom_grid.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/custom_grid.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/dir_name.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/dir_name.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/exec_finished_box.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/exec_finished_box.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/folder_history_box.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/folder_history_box.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/folder_pair.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/grid_view.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/grid_view.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/gui_generated.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/gui_generated.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/gui_status_handler.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/gui_status_handler.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/main_dlg.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/main_dlg.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/msg_popup.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/msg_popup.h" /> - <Unit filename="ui/progress_indicator.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/progress_indicator.h" /> - <Unit filename="ui/search.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/search.h" /> - <Unit filename="ui/small_dlgs.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/small_dlgs.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/sorting.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/switch_to_gui.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/sync_cfg.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/sync_cfg.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/taskbar.cpp" /> - <Unit filename="ui/taskbar.h" /> - <Unit filename="ui/tray_icon.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/tray_icon.h" /> - <Unit filename="ui/tree_view.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/tree_view.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/triple_splitter.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/triple_splitter.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="ui/wx_form_build_hide_warnings.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="wx+/app_main.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="wx+/button.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="wx+/button.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="wx+/choice_enum.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="wx+/dir_picker.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="wx+/file_drop.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="wx+/graph.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="wx+/graph.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="wx+/grid.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="wx+/grid.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="wx+/image_tools.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="wx+/mouse_move_dlg.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="wx+/mouse_move_dlg.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="wx+/pch.h"> - <Option compile="1" /> - <Option weight="0" /> - <Option target="Debug-DLL" /> - <Option target="Unit Test" /> - </Unit> - <Unit filename="wx+/shell_execute.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="wx+/string_conv.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="wx+/toggle_button.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="wx+/tooltip.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="wx+/tooltip.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="wx+/zlib_wrap.cpp" /> - <Unit filename="wx+/zlib_wrap.h" /> - <Unit filename="zen/assert_static.h" /> - <Unit filename="zen/base64.h" /> - <Unit filename="zen/build_info.h" /> - <Unit filename="zen/com_error.h" /> - <Unit filename="zen/com_ptr.h" /> - <Unit filename="zen/com_util.h" /> - <Unit filename="zen/debug_log.h" /> - <Unit filename="zen/dir_watcher.h" /> - <Unit filename="zen/dll.h" /> - <Unit filename="zen/dst_hack.cpp" /> - <Unit filename="zen/dst_hack.h" /> - <Unit filename="zen/file_error.h" /> - <Unit filename="zen/file_handling.cpp" /> - <Unit filename="zen/file_handling.h" /> - <Unit filename="zen/file_id.cpp" /> - <Unit filename="zen/file_id.h" /> - <Unit filename="zen/file_io.cpp" /> - <Unit filename="zen/file_io.h" /> - <Unit filename="zen/file_traverser.cpp" /> - <Unit filename="zen/file_traverser.h" /> - <Unit filename="zen/fixed_list.h" /> - <Unit filename="zen/format_unit.cpp" /> - <Unit filename="zen/format_unit.h"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="zen/guid.h" /> - <Unit filename="zen/i18n.h" /> - <Unit filename="zen/int64.h" /> - <Unit filename="zen/last_error.h" /> - <Unit filename="zen/long_path_prefix.h" /> - <Unit filename="zen/notify_removal.h" /> - <Unit filename="zen/perf.h" /> - <Unit filename="zen/privilege.cpp" /> - <Unit filename="zen/privilege.h" /> - <Unit filename="zen/read_txt.h" /> - <Unit filename="zen/recycler.cpp" /> - <Unit filename="zen/recycler.h" /> - <Unit filename="zen/scroll_window_under_cursor.cpp"> - <Option target="Release" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="zen/serialize.h" /> - <Unit filename="zen/stl_tools.h" /> - <Unit filename="zen/string_base.h" /> - <Unit filename="zen/string_tools.h" /> - <Unit filename="zen/string_traits.h" /> - <Unit filename="zen/symlink_target.h" /> - <Unit filename="zen/thread.h" /> - <Unit filename="zen/tick_count.h" /> - <Unit filename="zen/warn_static.h" /> - <Unit filename="zen/win.h" /> - <Unit filename="zen/zstring.cpp" /> - <Unit filename="zen/zstring.h" /> - <Extensions> - <code_completion /> - <envvars /> - <debugger /> - <DoxyBlocks> - <comment_style block="0" line="0" /> - <doxyfile_project /> - <doxyfile_build /> - <doxyfile_warnings /> - <doxyfile_output /> - <doxyfile_dot /> - <general /> - </DoxyBlocks> - </Extensions> - </Project> -</CodeBlocks_project_file> diff --git a/FreeFileSync.vcxproj b/FreeFileSync.vcxproj index 60c622f6..5bc30a85 100644 --- a/FreeFileSync.vcxproj +++ b/FreeFileSync.vcxproj @@ -141,6 +141,7 @@ <SuppressStartupBanner>true</SuppressStartupBanner> <MinimalRebuild>false</MinimalRebuild> <SmallerTypeCheck>true</SmallerTypeCheck> + <ShowIncludes>false</ShowIncludes> </ClCompile> <Link> <SubSystem>Windows</SubSystem> 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 @@ -<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> -<CodeBlocks_project_file> - <FileVersion major="1" minor="6" /> - <Project> - <Option title="RealtimeSync" /> - <Option makefile="makefile" /> - <Option pch_mode="2" /> - <Option compiler="gcc" /> - <Build> - <Target title="Release"> - <Option output="../BUILD/Bin/RealtimeSync_Win32" prefix_auto="1" extension_auto="1" /> - <Option working_dir="../BUILD" /> - <Option object_output="../OBJ/RTS_Release_32_MinGW" /> - <Option type="0" /> - <Option compiler="gcc" /> - <Option projectLinkerOptionsRelation="2" /> - <Compiler> - <Add option="-O3" /> - <Add option="-DNDEBUG" /> - <Add directory="C:/Programme/C++/wxWidgets/lib/mingw_release_lib/mswu" /> - </Compiler> - <Linker> - <Add option="-s" /> - <Add option="-static" /> - <Add library="libwxmsw28u_core.a" /> - <Add library="libwxmsw28u_adv.a" /> - <Add library="libwxbase28u.a" /> - <Add library="libwxpng.a" /> - <Add library="libwxzlib.a" /> - <Add library="libboost_thread-mgw47-mt-s-1_52.a" /> - <Add library="libboost_system-mgw47-mt-s-1_52.a" /> - <Add directory="C:/Programme/C++/wxWidgets/lib/mingw_release_lib" /> - </Linker> - <ExtraCommands> - <Add after='"C:\Program Files\C++\CodeSigning\SignCode.cmd" "$(PROJECT_DIR)$(TARGET_OUTPUT_FILE)"' /> - </ExtraCommands> - </Target> - <Target title="Debug-DLL"> - <Option output="../BUILD/Bin/RealtimeSync_Debug" prefix_auto="1" extension_auto="1" /> - <Option working_dir="../BUILD" /> - <Option object_output="../OBJ/RTS_Debug_32_MinGW" /> - <Option type="0" /> - <Option compiler="gcc" /> - <Option projectLinkerOptionsRelation="2" /> - <Compiler> - <Add option="-g" /> - <Add option="-Winvalid-pch" /> - <Add option='-include "../wx+/pch.h"' /> - <Add option="-D__WXDEBUG__" /> - <Add directory="C:/Program Files/C++/wxWidgets/lib/mingw_debug_dll/mswud" /> - </Compiler> - <Linker> - <Add library="libwxmsw28ud_core.a" /> - <Add library="libwxmsw28ud_adv.a" /> - <Add library="libwxbase28ud.a" /> - <Add library="libwxpngd.a" /> - <Add library="libwxzlibd.a" /> - <Add library="libboost_thread-mgw47-mt-sd-1_52.a" /> - <Add library="libboost_system-mgw47-mt-sd-1_52.a" /> - <Add directory="C:/Program Files/C++/wxWidgets/lib/mingw_debug_dll" /> - </Linker> - </Target> - </Build> - <Compiler> - <Add option="-Wshadow" /> - <Add option="-Winit-self" /> - <Add option="-Wswitch-enum" /> - <Add option="-Wmain" /> - <Add option="-std=c++0x" /> - <Add option="-Wall" /> - <Add option="-pipe" /> - <Add option="-mthreads" /> - <Add option="-fno-omit-frame-pointer" /> - <Add option="-std=gnu++0x" /> - <Add option='-include "../zen/warn_static.h"' /> - <Add option="-D__GNUWIN32__" /> - <Add option="-D__WXMSW__" /> - <Add option="-DwxUSE_UNICODE" /> - <Add option="-DFFS_WIN" /> - <Add option="-DZEN_PLATFORM_WINDOWS" /> - <Add option="-DBOOST_THREAD_NO_LIB" /> - <Add option="-DBOOST_THREAD_USE_LIB" /> - <Add option="-DWXINTL_NO_GETTEXT_MACRO" /> - <Add directory="C:/Programme/C++/wxWidgets/include" /> - <Add directory="C:/Program Files/C++/Boost" /> - <Add directory=".." /> - </Compiler> - <ResourceCompiler> - <Add directory="C:/Programme/C++/wxWidgets/include" /> - </ResourceCompiler> - <Linker> - <Add option="-mthreads" /> - <Add library="libkernel32.a" /> - <Add library="libuser32.a" /> - <Add library="libuuid.a" /> - <Add library="libcomctl32.a" /> - <Add library="libgdi32.a" /> - <Add library="libole32.a" /> - <Add library="liboleaut32.a" /> - <Add library="libcomdlg32.a" /> - <Add library="libws2_32.a" /> - <Add library="libwinspool.a" /> - <Add library="libmpr.a" /> - <Add library="libuxtheme.a" /> - <Add directory="C:/Program Files/C++/Boost/stage/lib" /> - </Linker> - <Unit filename="../lib/localization.cpp" /> - <Unit filename="../lib/process_xml.cpp" /> - <Unit filename="../lib/resolve_path.cpp" /> - <Unit filename="../lib/xml_base.cpp" /> - <Unit filename="../structures.cpp" /> - <Unit filename="../ui/dir_name.cpp" /> - <Unit filename="../ui/dir_name.h" /> - <Unit filename="../ui/folder_history_box.cpp" /> - <Unit filename="../ui/folder_history_box.h" /> - <Unit filename="../wx+/button.cpp" /> - <Unit filename="../wx+/mouse_move_dlg.cpp" /> - <Unit filename="../wx+/pch.h"> - <Option compile="1" /> - <Option weight="0" /> - <Option target="Debug-DLL" /> - </Unit> - <Unit filename="../zen/dir_watcher.cpp" /> - <Unit filename="../zen/dst_hack.cpp" /> - <Unit filename="../zen/file_handling.cpp" /> - <Unit filename="../zen/file_io.cpp" /> - <Unit filename="../zen/file_traverser.cpp" /> - <Unit filename="../zen/notify_removal.cpp" /> - <Unit filename="../zen/privilege.cpp" /> - <Unit filename="../zen/scroll_window_under_cursor.cpp" /> - <Unit filename="../zen/zstring.cpp" /> - <Unit filename="WxWizFrame.fbp" /> - <Unit filename="application.cpp" /> - <Unit filename="application.h" /> - <Unit filename="gui_generated.cpp" /> - <Unit filename="gui_generated.h" /> - <Unit filename="main_dlg.cpp" /> - <Unit filename="main_dlg.h" /> - <Unit filename="resource.rc"> - <Option compilerVar="WINDRES" /> - </Unit> - <Unit filename="resources.cpp" /> - <Unit filename="resources.h" /> - <Unit filename="tray_menu.cpp" /> - <Unit filename="tray_menu.h" /> - <Unit filename="watcher.cpp" /> - <Unit filename="watcher.h" /> - <Unit filename="xml_ffs.cpp" /> - <Unit filename="xml_ffs.h" /> - <Unit filename="xml_proc.cpp" /> - <Unit filename="xml_proc.h" /> - <Extensions> - <code_completion /> - <envvars /> - <debugger /> - </Extensions> - </Project> -</CodeBlocks_project_file> 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<wxString>(expandMacros(utfCvrtTo<Zstring>(cmdLine))); + auto cmdLineExp = expandMacros(utfCvrtTo<Zstring>(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<Zstring>& 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<Zstring>& 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<Zstring>& 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<Zstring>& 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 <zen/recycler.h> #include <zen/stl_tools.h> #include <zen/scope_guard.h> -//#include <wx/msgdlg.h> +#include <zen/thread.h> #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<LEFT_SIDE>(), TEMP_FILE_ENDING)) + return fileObj.setSyncDir(SYNC_DIR_LEFT); + else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(fileObj.getShortName<RIGHT_SIDE>(), TEMP_FILE_ENDING)) + return fileObj.setSyncDir(SYNC_DIR_RIGHT); + //#################################################################################### + + switch (cat) { case FILE_LEFT_SIDE_ONLY: - if (endsWith(fileObj.getShortName<LEFT_SIDE>(), 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<RIGHT_SIDE>(), 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<LEFT_SIDE>(), TEMP_FILE_ENDING)) + return setSyncDirectionRec(SYNC_DIR_LEFT, dirObj); // + else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(dirObj.getShortName<RIGHT_SIDE>(), 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<LEFT_SIDE>(), 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<RIGHT_SIDE>(), 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<LEFT_SIDE>(), TEMP_FILE_ENDING)) + return setSyncDirectionRec(SYNC_DIR_LEFT, dirObj); // + else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(dirObj.getShortName<RIGHT_SIDE>(), 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 <bool include> @@ -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 <SelectedSide side> +void categorize(const std::set<FileSystemObject*>& rowsIn, + std::vector<FileSystemObject*>& deletePermanent, + std::vector<FileSystemObject*>& deleteRecyler, + bool useRecycleBin, + std::map<Zstring, bool, LessFilename>& hasRecyclerBuffer, + DeleteFilesHandler& callback) { - RemoveCallbackImpl(DeleteFilesHandler& handler) : handler_(handler) {} + auto hasRecycler = [&](const FileSystemObject& fsObj) -> bool + { +#ifdef FFS_WIN + const Zstring& baseDirPf = fsObj.root().getBaseDirPf<side>(); - 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<side>()) + { + 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 <SelectedSide side> -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<side>())) //throw FileError - remCallback.notifyFileDeletion(fileObj.getFullName<side>()); + notifyFileDeletion(fileObj.getFullName<side>()); + + if (useRecycleBin_) + zen::recycleOrDelete(fileObj.getFullName<side>()); //throw FileError + else + zen::removeFile(fileObj.getFullName<side>()); //throw FileError } virtual void visit(const SymLinkMapping& linkObj) { - switch (linkObj.getLinkType<side>()) - { - case LinkDescriptor::TYPE_DIR: - zen::removeDirectory(linkObj.getFullName<side>(), &remCallback); //throw FileError - break; - case LinkDescriptor::TYPE_FILE: - if (zen::removeFile(linkObj.getFullName<side>())) //throw FileError - remCallback.notifyFileDeletion(linkObj.getFullName<side>()); - break; - } + notifySymlinkDeletion(linkObj.getFullName<side>()); + + if (useRecycleBin_) + zen::recycleOrDelete(linkObj.getFullName<side>()); //throw FileError + else + switch (linkObj.getLinkType<side>()) + { + case LinkDescriptor::TYPE_DIR: + zen::removeDirectory(linkObj.getFullName<side>()); //throw FileError + break; + case LinkDescriptor::TYPE_FILE: + zen::removeFile(linkObj.getFullName<side>()); //throw FileError + break; + } } virtual void visit(const DirMapping& dirObj) { - zen::removeDirectory(dirObj.getFullName<side>(), &remCallback); //throw FileError + notifyDirectoryDeletion(dirObj.getFullName<side>()); //notfied twice! see RemoveCallbackImpl -> no big deal + + if (useRecycleBin_) + zen::recycleOrDelete(dirObj.getFullName<side>()); //throw FileError + else + zen::removeDirectory(dirObj.getFullName<side>(), &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<FileSystemObject*>& ptrList, bool useRecycleBin, DeleteFilesHandler& handler) { + ItemDeleter<side> 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<side>()) //element may be implicitly deleted, e.g. if parent folder was deleted first - continue; - - tryReportingError([&] + if (!fsObj.isEmpty<side>()) //element may be implicitly deleted, e.g. if parent folder was deleted first + tryReportingError([&] { - if (useRecycleBin) - { - if (zen::recycleOrDelete(fsObj.getFullName<side>())) //throw FileError - handler.notifyDeletion(fsObj.getFullName<side>()); - } - else - { - PermanentDeleter<side> delPerm(handler); //throw FileError - fsObj.accept(delPerm); - } - + fsObj.accept(deleter); //throw FileError fsObj.removeObject<side>(); //if directory: removes recursively! }, handler); } } - - -template <SelectedSide side> -void categorize(const std::set<FileSystemObject*>& rowsIn, - std::vector<FileSystemObject*>& deletePermanent, - std::vector<FileSystemObject*>& deleteRecyler, - bool useRecycleBin, - std::map<Zstring, bool, LessFilename>& hasRecyclerBuffer) -{ - auto hasRecycler = [&](const FileSystemObject& fsObj) -> bool - { -#ifdef FFS_WIN - const Zstring& baseDirPf = fsObj.root().getBaseDirPf<side>(); - - 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<side>()) - { - 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<FileSystemObject*>& rowsToDeleteOnLeft, //refresh GUI grid after deletion to remove invalid rows const std::vector<FileSystemObject*>& 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<FileSystemObject*>& rowsToDelete std::vector<FileSystemObject*> deleteRecylerRight; std::map<Zstring, bool, LessFilename> hasRecyclerBuffer; - categorize<LEFT_SIDE >(deleteLeft, deletePermanentLeft, deleteRecylerLeft, useRecycleBin, hasRecyclerBuffer); - categorize<RIGHT_SIDE>(deleteRight, deletePermanentRight, deleteRecylerRight, useRecycleBin, hasRecyclerBuffer); + categorize<LEFT_SIDE >(deleteLeft, deletePermanentLeft, deleteRecylerLeft, useRecycleBin, hasRecyclerBuffer, statusHandler); + categorize<RIGHT_SIDE>(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<Zstring, int> deleteFromGridAndHDPreview( //returns wxString with elements to be deleted and total count of selected(!) objects, NOT total files/dirs! +std::pair<Zstring, int> deleteFromGridAndHDPreview( //returns string with elements to be deleted and total count of selected(!) objects, NOT total files/dirs! const std::vector<FileSystemObject*>& selectionLeft, //all pointers need to be bound! const std::vector<FileSystemObject*>& 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<FileSystemObject*>& rowsToDeleteOnLeft, //refresh GUI grid after deletion to remove invalid rows const std::vector<FileSystemObject*>& 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<Zstring, LessFilename>& 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<FolderPairCfg>& 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<FolderPairCfg>& 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<Int64>(totalBytes) - to<Int64>(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<Int64>(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<Int64>(totalBytesToCmp) - to<Int64>(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<DirectoryKey>& 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<DirectoryKey>& 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<ScheduleForBackgroundProcessing> backgroundPrio; if (runWithBackgroundPriority) - backgroundPrio.reset(new ScheduleForBackgroundProcessing); + backgroundPrio = make_unique<ScheduleForBackgroundProcessing>(); //prevent operating system going into sleep state PreventStandby dummy2; @@ -699,7 +700,7 @@ void ComparisonBuffer::compareByContent(std::vector<std::pair<FolderPairCfg, Bas { if (filesHaveSameContentUpdating(fileObj->getFullName<LEFT_SIDE>(), //throw FileError fileObj->getFullName<RIGHT_SIDE>(), - fileObj->getFileSize<LEFT_SIDE >(), + to<Int64>(fileObj->getFileSize<LEFT_SIDE>()), callback_)) { //Caveat: @@ -715,7 +716,7 @@ void ComparisonBuffer::compareByContent(std::vector<std::pair<FolderPairCfg, Bas else fileObj->setCategory<FILE_DIFFERENT>(); - 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<RIGHT_SIDE>() const } -template <SelectedSide side> -inline +template <SelectedSide side> inline Zstring FileSystemObject::getRelativeName() const { return isEmpty<side>() ? Zstring() : parent_.getObjRelativeNamePf() + getShortName<side>(); @@ -743,8 +742,7 @@ Zstring FileSystemObject::getObjShortName() const } -template <SelectedSide side> -inline +template <SelectedSide side> inline Zstring FileSystemObject::getFullName() const { return isEmpty<side>() ? Zstring() : getBaseDirPf<side>() + parent_.getObjRelativeNamePf() + getShortName<side>(); 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<Int64>(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<std::wstring, std::wstring> Translation; + typedef hash_map<std::wstring, std::wstring> Translation; //hash_map is 15% faster than std::map on GCC typedef std::map<std::pair<std::wstring, std::wstring>, std::vector<std::wstring> > TranslationPlural; Translation transMapping; //map original text |-> translation TranslationPlural transMappingPl; - std::unique_ptr<PluralForm> pluralParser; + std::unique_ptr<parse_plural::PluralForm> 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<std::wstring>(i->first); const std::wstring translation = utfCvrtTo<std::wstring>(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<std::wstring>(*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_function<ExistingTranslations::Entry #endif } }; +} ExistingTranslations::ExistingTranslations() @@ -173,10 +169,10 @@ ExistingTranslations::ExistingTranslations() traverseFolder(zen::getResourceDir() + Zstr("Languages"), //throw(); traverseCallback); - for (auto i = lngFiles.begin(); i != lngFiles.end(); ++i) + for (auto it = lngFiles.begin(); it != lngFiles.end(); ++it) try { - std::string stream = loadStream(*i); //throw XmlFileError + std::string stream = loadStream(*it); //throw XmlFileError try { lngfile::TransHeader lngHeader; @@ -191,7 +187,7 @@ ExistingTranslations::ExistingTranslations() ExistingTranslations::Entry newEntry; newEntry.languageID = locInfo->Language; newEntry.languageName = utfCvrtTo<wxString>(lngHeader.languageName); - newEntry.languageFile = utfCvrtTo<wxString>(*i); + newEntry.languageFile = utfCvrtTo<wxString>(*it); newEntry.translatorName = utfCvrtTo<wxString>(lngHeader.translatorName); newEntry.languageFlag = utfCvrtTo<wxString>(lngHeader.flagFile); locMapping.push_back(newEntry); @@ -205,12 +201,59 @@ ExistingTranslations::ExistingTranslations() } +const std::vector<ExistingTranslations::Entry>& ExistingTranslations::get() +{ + static ExistingTranslations instance; + return instance.locMapping; +} + + namespace { wxLanguage mapLanguageDialect(wxLanguage language) { - switch (static_cast<int>(language)) //map language dialects + switch (static_cast<int>(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<wxLanguage>(language))); //throw lngfile::ParsingError, PluralForm::ParsingError + zen::setTranslator(new FFSLocale(languageFile, static_cast<wxLanguage>(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<std::wstring>(e.row + 1)), L"%z", numberTo<std::wstring>(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<wxLanguage>(wxLocale::GetSystemLanguage())); } - - -const std::vector<ExistingTranslations::Entry>& 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<std::string>(header.pluralCount); - fileStream += KnownTokens::text(Token::TK_PLURAL_COUNT_END) + LB; + out += '\t' + KnownTokens::text(Token::TK_PLURAL_COUNT_BEGIN); + out += zen::numberTo<std::string>(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<TranslationList::Item*>::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<const TranslationList::RegularItem*>(*i); - const TranslationList::PluralItem* plural = dynamic_cast<const TranslationList::PluralItem*>(*i); + const TranslationList::RegularItem* regular = dynamic_cast<const TranslationList::RegularItem*>(*it); + const TranslationList::PluralItem* plural = dynamic_cast<const TranslationList::PluralItem* >(*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 <list> #include <memory> #include <functional> #include <zen/string_base.h> +namespace parse_plural +{ +//expression interface +struct Expression { virtual ~Expression() {} }; + +template <class T> +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<int>> 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 <class T> -struct Expr : public Expression +namespace implementation { - typedef T ValueType; - virtual ValueType eval() const = 0; -}; - //specific binary expression based on STL function objects template <class StlOp> struct BinaryExp : public Expr<typename StlOp::result_type> { - typedef const Expr<typename StlOp::first_argument_type > ExpLhs; - typedef const Expr<typename StlOp::second_argument_type> ExpRhs; + typedef std::shared_ptr<Expr<typename StlOp::first_argument_type >> ExpLhs; + typedef std::shared_ptr<Expr<typename StlOp::second_argument_type>> 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 <class StlOp> inline -BinaryExp<StlOp> makeBiExp(const Expression& lhs, const Expression& rhs, StlOp biop) //throw std::bad_cast +std::shared_ptr<BinaryExp<StlOp>> makeBiExp(const std::shared_ptr<Expression>& lhs, const std::shared_ptr<Expression>& rhs, StlOp biop) //throw ParsingError { - return BinaryExp<StlOp>(dynamic_cast<const Expr<typename StlOp::first_argument_type >&>(lhs), //throw std::bad_cast - dynamic_cast<const Expr<typename StlOp::second_argument_type>&>(rhs), biop); // + auto exLeft = std::dynamic_pointer_cast<Expr<typename StlOp::first_argument_type >>(lhs); + auto exRight = std::dynamic_pointer_cast<Expr<typename StlOp::second_argument_type>>(rhs); + if (!exLeft || !exRight) + throw ParsingError(); + return std::make_shared<BinaryExp<StlOp>>(exLeft, exRight, biop); } template <class T> -struct TernaryExp : public Expr<T> +struct ConditionalExp : public Expr<T> { - TernaryExp(const Expr<bool>& ifExp, const Expr<T>& thenExp, const Expr<T>& elseExp) : ifExp_(ifExp), thenExp_(thenExp), elseExp_(elseExp) {} - virtual typename Expr<T>::ValueType eval() const { return ifExp_.eval() ? thenExp_.eval() : elseExp_.eval(); } - const Expr<bool>& ifExp_; - const Expr<T>& thenExp_; - const Expr<T>& elseExp_; + ConditionalExp(const std::shared_ptr<Expr<bool>>& ifExp, + const std::shared_ptr<Expr<T>>& thenExp, + const std::shared_ptr<Expr<T>>& elseExp) : ifExp_(ifExp), thenExp_(thenExp), elseExp_(elseExp) { assert(ifExp && thenExp && elseExp); } + + virtual typename Expr<T>::ValueType eval() const { return ifExp_->eval() ? thenExp_->eval() : elseExp_->eval(); } +private: + std::shared_ptr<Expr<bool>> ifExp_; + std::shared_ptr<Expr<T>> thenExp_; + std::shared_ptr<Expr<T>> elseExp_; }; struct ConstNumberExp : public Expr<int> { ConstNumberExp(int n) : n_(n) {} virtual int eval() const { return n_; } +private: int n_; }; @@ -108,296 +144,276 @@ struct VariableNumberNExp : public Expr<int> { 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<std::shared_ptr<Expression> > 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<int>(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<ptrdiff_t>(prefix.size())) - return false; - return std::equal(prefix.begin(), prefix.end(), pos); + int number = zen::stringTo<int>(std::string(pos, digitEnd)); + pos = digitEnd; + return number; } - typedef std::vector<std::pair<std::string, Token::Type> > 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<ptrdiff_t>(prefix.size())) + return false; + return std::equal(prefix.begin(), prefix.end(), pos); + } + typedef std::vector<std::pair<std::string, Token::Type> > 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<Expr<int>> parse() //throw ParsingError; return value always bound! { - public: - Parser(const std::string& phrase, //in - const Expr<int>*& 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<const Expr<int>&>(e); // - } - catch (std::bad_cast&) { throw ParsingError(); } + auto e = std::dynamic_pointer_cast<Expr<int>>(parseExpression()); //throw ParsingError + if (!e) + throw ParsingError(); + expectToken(Token::TK_END); + return e; + } - consumeToken(Token::TK_END); - } +private: + std::shared_ptr<Expression> parseExpression() { return parseConditional(); }//throw ParsingError - private: - void nextToken() { tk = scn.nextToken(); } - const Token& token() const { return tk; } + std::shared_ptr<Expression> parseConditional() //throw ParsingError + { + std::shared_ptr<Expression> 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<Expr<bool>>(e); + auto thenExp = std::dynamic_pointer_cast<Expr<int>>(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<int>(dynamic_cast<const Expr<bool>&>(e), // - dynamic_cast<const Expr<int>&>(thenEx), //throw std::bad_cast - dynamic_cast<const Expr<int>&>(elseEx))); // - } - return e; + auto elseExp = std::dynamic_pointer_cast<Expr<int>>(parseExpression()); // + if (!ifExp || !thenExp || !elseExp) + throw ParsingError(); + return std::make_shared<ConditionalExp<int>>(ifExp, thenExp, elseExp); } + return e; + } - const Expression& parseLogicalOr() + std::shared_ptr<Expression> parseLogicalOr() + { + std::shared_ptr<Expression> 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<bool>())); //throw std::bad_cast - } - return *e; + nextToken(); + + std::shared_ptr<Expression> rhs = parseLogicalAnd(); + e = makeBiExp(e, rhs, std::logical_or<bool>()); //throw ParsingError } + return e; + } - const Expression& parseLogicalAnd() + std::shared_ptr<Expression> parseLogicalAnd() + { + std::shared_ptr<Expression> 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<Expression> rhs = parseEquality(); - e = &manageObj(makeBiExp(*e, rhs, std::logical_and<bool>())); //throw std::bad_cast - } - return *e; + e = makeBiExp(e, rhs, std::logical_and<bool>()); //throw ParsingError } + return e; + } - const Expression& parseEquality() - { - const Expression& e = parseRelational(); + std::shared_ptr<Expression> parseEquality() + { + std::shared_ptr<Expression> 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<Expression> rhs = parseRelational(); - if (t == Token::TK_EQUAL) return manageObj(makeBiExp(e, rhs, std::equal_to <int>())); //throw std::bad_cast - if (t == Token::TK_NOT_EQUAL) return manageObj(makeBiExp(e, rhs, std::not_equal_to<int>())); // - } - return e; + if (t == Token::TK_EQUAL) return makeBiExp(e, rhs, std::equal_to <int>()); //throw ParsingError + if (t == Token::TK_NOT_EQUAL) return makeBiExp(e, rhs, std::not_equal_to<int>()); // } + return e; + } - const Expression& parseRelational() - { - const Expression& e = parseMultiplicative(); + std::shared_ptr<Expression> parseRelational() + { + std::shared_ptr<Expression> 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<Expression> rhs = parseMultiplicative(); - if (t == Token::TK_LESS) return manageObj(makeBiExp(e, rhs, std::less <int>())); // - if (t == Token::TK_LESS_EQUAL) return manageObj(makeBiExp(e, rhs, std::less_equal <int>())); //throw std::bad_cast - if (t == Token::TK_GREATER) return manageObj(makeBiExp(e, rhs, std::greater <int>())); // - if (t == Token::TK_GREATER_EQUAL) return manageObj(makeBiExp(e, rhs, std::greater_equal<int>())); // - } - return e; + if (t == Token::TK_LESS) return makeBiExp(e, rhs, std::less <int>()); // + if (t == Token::TK_LESS_EQUAL) return makeBiExp(e, rhs, std::less_equal <int>()); //throw ParsingError + if (t == Token::TK_GREATER) return makeBiExp(e, rhs, std::greater <int>()); // + if (t == Token::TK_GREATER_EQUAL) return makeBiExp(e, rhs, std::greater_equal<int>()); // } + return e; + } - const Expression& parseMultiplicative() - { - const Expression* e = &parsePrimary(); + std::shared_ptr<Expression> parseMultiplicative() + { + std::shared_ptr<Expression> 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<Expression> rhs = parsePrimary(); - //"compile-time" check: n % 0 - if (auto literal = dynamic_cast<const ConstNumberExp*>(&rhs)) - if (literal->eval() == 0) - throw ParsingError(); + //"compile-time" check: n % 0 + if (auto literal = std::dynamic_pointer_cast<ConstNumberExp>(rhs)) + if (literal->eval() == 0) + throw ParsingError(); - e = &manageObj(makeBiExp(*e, rhs, std::modulus<int>())); //throw std::bad_cast - } - return *e; + e = makeBiExp(e, rhs, std::modulus<int>()); //throw ParsingError } + return e; + } - const Expression& parsePrimary() + std::shared_ptr<Expression> 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<VariableNumberNExp>(n_); } - - template <class T> - const T& manageObj(const T& obj) + else if (token().type == Token::TK_CONST_NUMBER) { - dump_.push_back(std::make_shared<T>(obj)); - return static_cast<T&>(*dump_.back()); + const int number = token().number; + nextToken(); + return std::make_shared<ConstNumberExp>(number); } + else if (token().type == Token::TK_BRACKET_LEFT) + { + nextToken(); + std::shared_ptr<Expression> 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<int>* 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<std::wstring> 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<Zstring> fileList; //list of *short* names std::vector<Zstring> 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<Zstring>(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<void()> 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 <memory> +#include <random> #include <deque> #include <stdexcept> #include <wx/file.h> //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::FolderPairSyncCfg> zen::extractSyncCfg(const MainConfiguration& std::vector<FolderPairSyncCfg> 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::FolderPairSyncCfg> 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<Int64>(fileObj.getFileSize<RIGHT_SIDE>()); - break; - - case SO_CREATE_NEW_RIGHT: - ++items; - bytes += to<Int64>(fileObj.getFileSize<LEFT_SIDE>()); - 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<Int64>(fileObj.getFileSize<RIGHT_SIDE>()); - break; - - case SO_OVERWRITE_RIGHT: - //todo: delete - items += 2; //read + write - bytes += to<Int64>(fileObj.getFileSize<LEFT_SIDE>()); - 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 <class Function> void removeFileUpdating(const Zstring& relativeName, Int64 bytesExpected, Function notifyItemDeletion); //throw FileError + template <class Function> void removeDirUpdating (const Zstring& relativeName, Int64 bytesExpected, Function notifyItemDeletion); //reports ONLY data delta via updateProcessedData()! + template <class Function> void removeLinkUpdating(const Zstring& relativeName, Int64 bytesExpected, Function notifyItemDeletion, LinkDescriptor::LinkType lt); // 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<Zstring> 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<Zstring> 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<Zstring>(Zstr("%Y-%m-%d %H%M%S"), timeStamp); - if (folderIndex > 0) - tempDir += Zstr(" [") + numberTo<Zstring>(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<size_t> 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 <class Function> +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 Function> +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 <class Function> +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<Function> 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<Function> 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 <class Function> +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 <class Function> 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 <SelectedSide side, class DelTargetCommand> - void copyFileUpdatingTo(const FileMapping& fileObj, const DelTargetCommand& cmd, FileAttrib& newAttr) const; + template <SelectedSide sideTrg, class Function> + 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 <SelectedSide side> @@ -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<Zstring>(i) + zen::TEMP_FILE_ENDING; @@ -1437,8 +1393,8 @@ void SynchronizeFolderPair::runPass(HierarchyObject& hierObj) this->runPass<pass>(dirObj); //recurse }); } -//--------------------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------------------- namespace { @@ -1503,26 +1459,25 @@ void SynchronizeFolderPair::synchronizeFileInt(FileMapping& fileObj, SyncOperati case SO_CREATE_NEW_RIGHT: { const Zstring& target = fileObj.getBaseDirPf<sideTrg>() + fileObj.getRelativeName<sideSrc>(); //can't use "getFullName" as target is not yet existing - reportInfo(txtCreatingFile, target); try { - FileAttrib newAttr; - copyFileUpdatingTo<sideTrg>(fileObj, [] {}, /*no target to delete*/ newAttr); //throw FileError - procCallback_.updateProcessedData(1, 0); //processed data is communicated in copyFileUpdatingTo()! + const FileAttrib newAttr = copyFileUpdatingTo<sideTrg>(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<sideTrg>(descrTarget, &descrSource); //update FileMapping + + procCallback_.updateProcessedData(1, 0); //processed bytes are reported in copyFileUpdatingTo()! } catch (FileError&) { - if (somethingExists(fileObj.getFullName<sideSrc>())) //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<sideSrc>())) //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<zen::Int64>(fileObj.getFileSize<sideSrc>())); fileObj.removeObject<sideSrc>(); + procCallback_.updateTotalData(-1, -to<zen::Int64>(fileObj.getFileSize<sideSrc>())); } } break; @@ -1530,11 +1485,23 @@ void SynchronizeFolderPair::synchronizeFileInt(FileMapping& fileObj, SyncOperati case SO_DELETE_LEFT: case SO_DELETE_RIGHT: reportInfo(getDelHandling<sideTrg>().getTxtRemovingFile(), fileObj.getFullName<sideTrg>()); + { + 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<sideTrg>().removeFile(fileObj.getObjRelativeName()); //throw FileError - fileObj.removeObject<sideTrg>(); //update FileMapping + getDelHandling<sideTrg>().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<sideTrg>(); //update FileMapping break; case SO_MOVE_LEFT_SOURCE: @@ -1562,8 +1529,8 @@ void SynchronizeFolderPair::synchronizeFileInt(FileMapping& fileObj, SyncOperati sourceObj->getFileSize <sideTrg>(), sourceObj->getFileId <sideTrg>()); - sourceObj->removeObject<sideTrg>(); // - targetObj->syncTo<sideTrg>(descrTarget); //update FileMapping + sourceObj->removeObject<sideTrg>(); //update FileMapping + targetObj->syncTo<sideTrg>(descrTarget); // procCallback_.updateProcessedData(1, 0); } @@ -1573,25 +1540,24 @@ void SynchronizeFolderPair::synchronizeFileInt(FileMapping& fileObj, SyncOperati case SO_OVERWRITE_RIGHT: { const Zstring& target = fileObj.getBaseDirPf<sideTrg>() + fileObj.getRelativeName<sideSrc>(); //respect differences in case of source object - reportInfo(txtOverwritingFile, target); - FileAttrib newAttr; - copyFileUpdatingTo<sideTrg>(fileObj, [&] //delete target at appropriate time + const FileAttrib newAttr = copyFileUpdatingTo<sideTrg>(fileObj, [&] //delete target at appropriate time { reportStatus(this->getDelHandling<sideTrg>().getTxtRemovingFile(), fileObj.getFullName<sideTrg>()); - this->getDelHandling<sideTrg>().removeFile(fileObj.getObjRelativeName()); //throw FileError - fileObj.removeObject<sideTrg>(); //remove file from FileMapping, to keep in sync (if subsequent copying fails!!) + this->getDelHandling<sideTrg>().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<sideTrg>(); //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<sideTrg>(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<sideTrg>::result; - auto deleteSymlink = [&] - { - switch (linkObj.getLinkType<sideTrg>()) - { - case LinkDescriptor::TYPE_DIR: - this->getDelHandling<sideTrg>().removeFolder(linkObj.getObjRelativeName()); //throw FileError - break; - - case LinkDescriptor::TYPE_FILE: //Windows: true file symlink; Linux: file-link or broken link - this->getDelHandling<sideTrg>().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<sideSrc>(), target, copyFilePermissions_); //throw FileError - procCallback_.updateProcessedData(1, 0); - linkObj.copyTo<sideTrg>(); //update SymLinkMapping + + procCallback_.updateProcessedData(1, 0); } catch (FileError&) { if (somethingExists(linkObj.getFullName<sideSrc>())) //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<sideSrc>(); + 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<sideTrg>().getTxtRemovingSymLink(), linkObj.getFullName<sideTrg>()); + { + 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<sideTrg>().removeLinkUpdating(linkObj.getObjRelativeName(), bytesExpected, [&] //throw FileError + { + procCallback_.updateProcessedData(1, 0); //noexcept + ++objectsReported; + }, linkObj.getLinkType<sideTrg>()); + guardStatistics.dismiss(); //update statistics to consider the real amount of data + if (objectsReported != objectsExpected) + procCallback_.updateTotalData(objectsReported - objectsExpected, 0); //noexcept! + } linkObj.removeObject<sideTrg>(); //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<sideTrg>().getTxtRemovingSymLink(), linkObj.getFullName<sideTrg>()); - deleteSymlink(); //throw FileError + getDelHandling<sideTrg>().removeLinkUpdating(linkObj.getObjRelativeName(), 0, []{}, linkObj.getLinkType<sideTrg>()); //throw FileError linkObj.removeObject<sideTrg>(); //remove file from FileMapping, to keep in sync (if subsequent copying fails!!) reportStatus(txtOverwritingLink, target); //restore status text zen::copySymlink(linkObj.getFullName<sideSrc>(), target, copyFilePermissions_); //throw FileError linkObj.copyTo<sideTrg>(); //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<sideTrg>(), linkObj.getLastWriteTime<sideSrc>(), 2)) //respect 2 second FAT/FAT32 precision setFileTime(linkObj.getFullName<sideTrg>(), linkObj.getLastWriteTime<sideSrc>(), SYMLINK_DIRECT); //throw FileError - linkObj.copyTo<sideTrg>(); //-> 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<sideSrc>())) //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<sideSrc>())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should error out! { const Zstring& target = dirObj.getBaseDirPf<sideTrg>() + dirObj.getRelativeName<sideSrc>(); reportInfo(txtCreatingFolder, target); try { - makeNewDirectory(target, dirObj.getFullName<sideSrc>(), copyFilePermissions_); //no symlink copying! + makeNewDirectory(target, dirObj.getFullName<sideSrc>(), 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<sideTrg>(); //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<sideSrc>(); // + 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<sideSrc>(); + case SO_DELETE_LEFT: + case SO_DELETE_RIGHT: + reportInfo(getDelHandling<sideTrg>().getTxtRemovingDir(), dirObj.getFullName<sideTrg>()); + { + 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<sideTrg>().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<sideTrg>(); // break; case SO_COPY_METADATA_TO_LEFT: @@ -1812,29 +1799,12 @@ void SynchronizeFolderPair::synchronizeFolderInt(DirMapping& dirObj, SyncOperati if (dirObj.getShortName<sideTrg>() != dirObj.getShortName<sideSrc>()) //adapt difference in case (windows only) renameFile(dirObj.getFullName<sideTrg>(), beforeLast(dirObj.getFullName<sideTrg>(), FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + dirObj.getShortName<sideSrc>()); //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<sideTrg>(); //-> both sides *should* be completely equal now... procCallback_.updateProcessedData(1, 0); break; - case SO_DELETE_LEFT: - case SO_DELETE_RIGHT: - { - reportInfo(getDelHandling<sideTrg>().getTxtRemovingDir(), dirObj.getFullName<sideTrg>()); - - const SyncStatistics subStats(dirObj); //counts sub-objects only! - getDelHandling<sideTrg>().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<sideTrg>(); // - } - 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<ScheduleForBackgroundProcessing> backgroundPrio; if (runWithBackgroundPriority) - backgroundPrio.reset(new ScheduleForBackgroundProcessing); + backgroundPrio = make_unique<ScheduleForBackgroundProcessing>(); //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<LEFT_SIDE>(), callback); @@ -1979,7 +1948,6 @@ void zen::synchronize(const TimeComp& timeStamp, folderPairCfg.versioningFolder, folderPairCfg.versioningStyle_, timeStamp, - folderIndex, j->getBaseDirPf<RIGHT_SIDE>(), 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 <class DelTargetCommand> +template <class Function> 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 <SelectedSide side, class DelTargetCommand> -void SynchronizeFolderPair::copyFileUpdatingTo(const FileMapping& fileObj, const DelTargetCommand& cmd, FileAttrib& newAttr) const +//throw FileError; reports data delta via updateProcessedData() +template <SelectedSide sideTrg, class Function> +FileAttrib SynchronizeFolderPair::copyFileUpdatingTo(const FileMapping& fileObj, Function delTargetCommand) const //returns current attributes of source file { - const Int64 expectedBytesToCpy = to<Int64>(fileObj.getFileSize<OtherSide<side>::result>()); - Zstring source = fileObj.getFullName<OtherSide<side>::result>(); - const Zstring& target = fileObj.getBaseDirPf<side>() + fileObj.getRelativeName<OtherSide<side>::result>(); + static const SelectedSide sideSrc = OtherSide<sideTrg>::result; + + FileAttrib newAttr; + const Int64 bytesExpected = to<Int64>(fileObj.getFileSize<sideSrc>()); + Zstring source = fileObj.getFullName<sideSrc>(); + const Zstring& target = fileObj.getBaseDirPf<sideTrg>() + fileObj.getRelativeName<sideSrc>(); 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<DelTargetCommand> callback(bytesReported, procCallback_, cmd); + WhileCopying<Function> 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<wxString>(expandMacros(utfCvrtTo<Zstring>(finalCommand))); + auto cmdexp = expandMacros(utfCvrtTo<Zstring>(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<std::pair<size_t, BlockPosition>> 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<wxString>(expandMacros(utfCvrtTo<Zstring>(finalCommand))); + auto cmdexp = expandMacros(utfCvrtTo<Zstring>(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 <iterator> -#include <stdexcept> #include <wx/clipbrd.h> -#include <wx/dataobj.h> -#include <wx/imaglist.h> #include <wx/wupdlock.h> #include <wx/msgdlg.h> #include <wx/sound.h> -#include <wx/display.h> -#include <wx/app.h> -#include <wx/dcmemory.h> #include <wx/filedlg.h> #include <zen/format_unit.h> +#include <zen/file_handling.h> +#include <zen/serialize.h> +#include <zen/file_id.h> +#include <zen/thread.h> #include <wx+/context_menu.h> -#include "folder_history_box.h" #include <wx+/button.h> -#include "../comparison.h" -#include "../synchronization.h" -#include "../algorithm.h" +#include <wx+/shell_execute.h> #include <wx+/app_main.h> +#include <wx+/toggle_button.h> +#include <wx+/mouse_move_dlg.h> +#include <wx+/no_flicker.h> +#include <wx+/rtl.h> #include "check_version.h" #include "gui_status_handler.h" #include "sync_cfg.h" #include "small_dlgs.h" -#include <wx+/mouse_move_dlg.h> #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 <zen/file_handling.h> -#include <zen/serialize.h> -#include <zen/file_id.h> -#include <zen/recycler.h> #include "../lib/resolve_path.h" #include "../lib/ffs_paths.h" -#include <wx+/toggle_button.h> -#include "folder_pair.h" -#include <wx+/rtl.h> -#include "search.h" #include "../lib/help_provider.h" -#include "batch_config.h" -#include <zen/thread.h> #include "../lib/lock_holder.h" -#include <wx+/shell_execute.h> #include "../lib/localization.h" -#include <wx+/image_tools.h> -#include <wx+/no_flicker.h> -#include <wx+/grid.h> -#include "../lib/error_log.h" -#include "triple_splitter.h" +#include <zen/perf.h> using namespace zen; using namespace std::rel_ops; @@ -281,12 +268,13 @@ public: virtual bool allowMove(const wxMouseEvent& event) { - wxPanel* panel = dynamic_cast<wxPanel*>(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<wxPanel*>(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<GridView>(); + treeDataView = std::make_shared<TreeView>(); 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<FileSystemObject*> 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<FileSystemObject*>& 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 <SelectedSide side> -wxString extractLastValidDir(const FileSystemObject& fsObj) +Zstring getExistingParentFolder(const FileSystemObject& fsObj) { - Zstring fullname = fsObj.getBaseDirPf<side>() + fsObj.getObjRelativeName(); //full name even if FileSystemObject::isEmpty<side>() == true + const DirMapping* dirObj = dynamic_cast<const DirMapping*>(&fsObj); + if (!dirObj) + dirObj = dynamic_cast<const DirMapping*>(&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<side>()) + return dirObj->getFullName<side>(); - return toWx(fullname); + dirObj = dynamic_cast<const DirMapping*>(&dirObj->parent()); + } + return fsObj.getBaseDirPf<side>(); +} } - -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<FileSystemObject*>& 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<LEFT_SIDE>()); //empty if obj not existing - dir = toWx(beforeLast(fsObj->getFullName<LEFT_SIDE>(), 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<RIGHT_SIDE>()); - dirCo = toWx(beforeLast(fsObj->getFullName<RIGHT_SIDE>(), 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<LEFT_SIDE >()) || + (!leftSide && selectionTmp[0]->isEmpty<RIGHT_SIDE>())) + { + Zstring fallbackDir; + if (selectionTmp.empty()) + fallbackDir = leftSide ? + getFormattedDirectoryName(toZ(firstFolderPair->getLeftDir())) : + getFormattedDirectoryName(toZ(firstFolderPair->getRightDir())); + + else + fallbackDir = leftSide ? + getExistingParentFolder<LEFT_SIDE >(*selectionTmp[0]) : + getExistingParentFolder<RIGHT_SIDE>(*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<LEFT_SIDE>() + 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<RIGHT_SIDE>() + 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<Zstring>(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<Int64>(filesizeLeftView))); + + wxString statusMiddleNew; + if (gridDataView->rowsTotal() > 0) { - auto cmdExp = utfCvrtTo<wxString>(expandMacros(utfCvrtTo<Zstring>(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<LEFT_SIDE >(*fsObj) : - extractLastValidDir<RIGHT_SIDE>(*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<Int64>(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<wxString>(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<wxString>& 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<int> 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<int>(m_listBoxHistory->GetCount())) + newSelection = m_listBoxHistory->GetCount() - 1; + m_listBoxHistory->SetSelection(newSelection); + } +} - std::set<int> 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<int>(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<ToggleButton*>(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<FolderPairCfg>& 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<FileSystemObject*> 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<Int64>(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<Int64>(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<DirectoryPair*>::const_iterator i = additionalFolderPairs.begin(); i != additionalFolderPairs.end(); ++i) - if (eventObj == (*i)->m_bpButtonRemovePair) + for (std::vector<DirectoryPair*>::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<FolderPairEnh>& 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<MenuItemID, LanguageID>::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<wxString> 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<wxString>& filenames); //= update/insert + apply selection @@ -119,14 +121,15 @@ private: void deleteSelectedFiles(const std::vector<zen::FileSystemObject*>& selectionLeft, const std::vector<zen::FileSystemObject*>& selectionRight); - void openExternalApplication(const wxString& commandline, const zen::FileSystemObject* fsObj, bool leftSide); //fsObj may be nullptr + void openExternalApplication(const wxString& commandline, const std::vector<zen::FileSystemObject*>& 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<zen::ColumnAttributeRim>& 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<wxString> statusMsgStack; + std::unique_ptr<wxString> oldStatusMsg; //compare status panel (hidden on start, shown when comparing) std::unique_ptr<CompareStatus> 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<PerfCheck>(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<PerfCheck> 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> graphDataBytes; std::shared_ptr<GraphDataConstLine> 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<GraphDataConstLine>(); m_panelGraph->setAttributes(Graph2D::MainAttributes(). - setLabelX(Graph2D::X_LABEL_BOTTOM, 20, std::make_shared<LabelFormatterTimeElapsed>()). - setLabelY(Graph2D::Y_LABEL_RIGHT, 70, std::make_shared<LabelFormatterBytes>())); + setLabelX(Graph2D::X_LABEL_BOTTOM, 20, std::make_shared<LabelFormatterTimeElapsed>()). + setLabelY(Graph2D::Y_LABEL_RIGHT, 70, std::make_shared<LabelFormatterBytes>())); 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<PerfCheck>(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<double>(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<double>(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<TreeLine> 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<LabelFormatter> Graph2D::MainAttributes::defaultFormat = std::make_shared<DecimalNumberFormatter>(); //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<ptrdiff_t>(factor); iterIn += factor) //don't even let iterator point out of range! - *iterOut++ = std::accumulate(iterIn, iterIn + factor, 0.0) / static_cast<double>(factor); + auto itOut = cont.begin(); + for (auto itIn = cont.begin(); cont.end() - itIn >= static_cast<ptrdiff_t>(factor); itIn += factor) //don't even let iterator point out of range! + *itOut++ = std::accumulate(itIn, itIn + factor, 0.0) / static_cast<double>(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<GraphData>& data, const CurveAttributes& la) +void Graph2D::setData(const std::shared_ptr<GraphData>& data, const CurveAttributes& ca) { curves_.clear(); - addData(data, la); + addData(data, ca); } -void Graph2D::addData(const std::shared_ptr<GraphData>& data, const CurveAttributes& la) +void Graph2D::addData(const std::shared_ptr<GraphData>& 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<double> 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<const wxRect&>(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<std::pair<std::vector<double>, int>> yValuesList(curves_.size()); + std::vector<CurveSamples> yValuesList(curves_.size()); double minY = attr.minYauto ? std::numeric_limits<double>::infinity() : attr.minY; //automatic: ensure values are initialized by first curve double maxY = attr.maxYauto ? -std::numeric_limits<double>::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<double>& 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<double>& 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<int>(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<double> data; }; - +/* //reference data implementation class VectorData : public GraphData { @@ -75,6 +74,7 @@ private: std::vector<double> 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<GraphData>& data, const CurveAttributes& attr = CurveAttributes()); - void addData(const std::shared_ptr<GraphData>& data, const CurveAttributes& attr = CurveAttributes()); + void setData(const std::shared_ptr<GraphData>& data, const CurveAttributes& ca = CurveAttributes()); + void addData(const std::shared_ptr<GraphData>& data, const CurveAttributes& ca = CurveAttributes()); enum PosLabelY { @@ -214,14 +213,13 @@ public: labelFmtY(std::make_shared<DecimalNumberFormatter>()), 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<LabelFormatter> defaultFormat; @@ -268,7 +266,6 @@ public: void setAttributes(const MainAttributes& newAttr) { attr = newAttr; Refresh(); } MainAttributes getAttributes() const { return attr; } - std::vector<SelectionBlock> getSelections() const { return oldSel; } void setSelections(const std::vector<SelectionBlock>& 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 : @@ -13,11 +13,11 @@ #endif //##################################################### + // basic wxWidgets headers #ifndef WX_PRECOMP #define WX_PRECOMP #endif - #include <wx/wxprec.h> //includes <wx/msw/wrapwin.h> //other wxWidgets headers @@ -37,7 +37,8 @@ #include <wx/config.h> #include <wx/dc.h> #include <wx/dialog.h> -#include <wx/dir.h> +//#include <wx/dir.h> -> MSVC: avoid annoying IntelliSense error: wxZipStreamLink +//#include <wx/zipstrm.h> #include <wx/dnd.h> #include <wx/file.h> #include <wx/filename.h> @@ -70,15 +71,11 @@ #include <wx/thread.h> #include <wx/utils.h> #include <wx/wfstream.h> -#include <wx/zipstrm.h> #include <wx/scrolwin.h> #include <wx/notebook.h> #include <wx/help.h> #include <wx/event.h> -//##################################################### -// #include other rarely changing headers here - //STL headers #include <string> #include <vector> 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<std::string>(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<wxString>(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<wxString>(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 <class T> 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 <stdlib.h> //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 <crtdbg.h> //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<std::string>(pathPrefix)); + throw std::runtime_error(std::string("100000000 files, one for each number, exist in this directory? You're kidding...") + utfCvrtTo<std::string>(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<Zstring>& files, std::vector<Zstring>& dirs) : + CollectFilesFlat(std::vector<Zstring>& files, std::vector<Zstring>& 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<Zstring>& m_files; std::vector<Zstring>& 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<Zstring> fileList; - std::vector<Zstring> 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<Zstring> fileList; + std::vector<Zstring> 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> 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<TraverseCallback> //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<UINT>(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); } @@ -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()); } @@ -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<Zstring>& 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 <typename Char, //Character Type void setLength(Char* ptr, size_t newLength) */ -template <typename Char, //Character Type - class AP> //Allocator Policy +template <class Char, //Character Type + class AP> //Allocator Policy class StorageDeepCopy : public AP { protected: @@ -112,8 +111,8 @@ private: }; -template <typename Char, //Character Type - class AP> //Allocator Policy +template <class Char, //Character Type + class AP> //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<std::uint32_t>(len)), + length (static_cast<std::uint32_t>(len)), capacity(static_cast<std::uint32_t>(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<const Descriptor*>(ptr) - 1; } }; -//################################################################################################################################################################ +//################################################################################################################################################################ //perf note: interstingly StorageDeepCopy and StorageRefCountThreadSafe show same performance in FFS comparison @@ -386,7 +385,7 @@ size_t Zbase<Char, SP, AP>::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<Char, SP, AP>::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<Char, SP, AP>::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 <class T, class Function> inline auto async2(Function fun) -> boost::unique_future<T> //support for workaround of VS2010 bug: bool (*fun)(); decltype(fun()) == int! { - boost::packaged_task<T> 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<T()> pt(std::move(fun)); //packaged task seems to even require r-value reference: https://sourceforge.net/p/freefilesync/bugs/234/ +#else + boost::packaged_task<T> 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??? } |